view scripts/image/private/__imread__.m @ 17351:80bf005cdf8e

New function __magick_ping__ to speed reading of images. * __magick_read__.cc (__magick_ping__): New function that only pings one of the images in a file to obtain height, width and format information. This becomes an alternative to imfinfo for internal Octave functions. Because imfinfo requires reading of the whole file and all images, this has a large speed increase, specially for multipage images. * imformats.m: use __magick_ping__() to check image format. * private/__imread__.m: use __magick_ping__() to get rows and columns of an image which is used to set the reading defaults.
author Carnë Draug <carandraug@octave.org>
date Fri, 30 Aug 2013 07:19:13 +0100
parents 1c89599167a6
children 6dbc866379e2
line wrap: on
line source

## Copyright (C) 2013 Carnë Draug
## Copyright (C) 2008-2012 Thomas L. Scofield
## Copyright (C) 2008 Kristian Rumberg
## Copyright (C) 2006 Thomas Weber
## Copyright (C) 2005 Stefan van der Walt
## Copyright (C) 2002 Andy Adler
##
## This file is part of Octave.
##
## Octave is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or (at
## your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## This function does all the work of imread. It exists here as private
## function so that imread can use other functions if imformats is
## configured to. It is also needed so that imformats can create a
## function handle for it.

## Author: Carnë Draug <carandraug@octave.org>
## Author: Thomas L. Scofield <scofield@calvin.edu>
## Author: Kristian Rumberg <kristianrumberg@gmail.com>
## Author: Thomas Weber <thomas.weber.mail@gmail.com>
## Author: Stefan van der Walt <stefan@sun.ac.za>
## Author: Andy Adler

function varargout = __imread__ (filename, varargin)

  if (nargin < 1)
    print_usage ("imread");
  elseif (! ischar (filename))
    error ("imread: FILENAME must be a string");
  endif

  ## keep track of the varargin offset we're looking at each moment
  offset    = 1;

  filename  = tilde_expand (filename);
  fn        = file_in_path (IMAGE_PATH, filename);
  if (isempty (fn) && nargin >= offset + 1 && ischar (varargin{offset}))
    ## if we can't find the file, check if the next input is the file extension
    filename  = [filename "." varargin{offset}];
    fn        = file_in_path (IMAGE_PATH, filename);
    offset++;
  endif
  if (isempty (fn))
    error ("imread: cannot find %s", filename);
  endif

  ## It is possible for an file with multiple pages to have very different
  ## images on each page. Specifically, they may have different sizes. Because
  ## of this, we need to first find out the index of the images to read so
  ## we can set up defaults for things such as PixelRegion later on.
  options = struct ("index", 1);  # default image index

  ## Index is the only option that can be defined without the parameter/value
  ## pair style. When defining it here, the string "all" is invalid though.
  ## Also, for matlab compatibility, if index is defined both as an option here
  ## and parameter/value pair, silently ignore the first.
  if (nargin >= offset + 1 && ! ischar (varargin{offset}))
    if (! is_valid_index_option (options.index))
      error ("imread: IDX must be a numeric vector");
    endif
    options.index = varargin{offset};
    offset++;
  endif

  if (rem (numel (varargin) - offset + 1, 2) != 0)
    error ("imread: no pair for all arguments (odd number left over)");
  endif

  ## Check key/value options.
  indexes = find (cellfun (@(x) ischar (x) ...
                                && any (strcmpi (x, {"frames", "index"})),
                           varargin));
  if (indexes)
    options.index = varargin{indexes+1};
    if (! (is_valid_index_option (options.index)) &&
        ! (ischar (options.index) && strcmpi (options.index, "all")))
      error ("imread: value for %s must be a vector or the string `all'");
    endif
  endif

  try
    ## Use information from the first image to be read to set defaults.
    info = __magick_ping__ (fn, options.index(1));

    ## Set default for options.
    options.region = {1:1:info.rows 1:1:info.columns};

    for idx = offset:2:(numel (varargin) - offset + 1)
      switch (tolower (varargin{idx}))

        case "pixelregion",
          options.region = varargin{idx+1};
          if (! iscell (options.region) || numel (options.region) != 2)
            error ("imread: value for %s must be a 2 element cell array",
                   varargin{idx});
          endif
          for reg_idx = 1:2
            if (numel (options.region{reg_idx}) == 3)
              ## do nothing
            elseif (numel (options.region{reg_idx}) == 2)
              options.region{reg_idx}(3) = options.region{reg_idx}(2);
              options.region{reg_idx}(2) = 1;
            else
              error ("imread: range for %s must be a 2 or 3 element vector",
                     varargin{idx});
            endif
            options.region{reg_idx} = floor (options.region{reg_idx}(1)): ...
                                      floor (options.region{reg_idx}(2)): ...
                                      floor (options.region{reg_idx}(3));
          endfor
          if (options.region{1}(end) > info.rows)
            error ("imread: end ROWS for PixelRegions option is larger than image height");
          elseif (options.region{2}(end) > info.columns)
            error ("imread: end COLS for PixelRegions option is larger than image width");
          endif

        case "info",
          ## We ignore this option. This parameter exists in Matlab to
          ## speed up the reading of multipage TIFF by passing a structure
          ## that contains information about the start on the file of each
          ## page.  We can't control it through GraphicsMagic but at least
          ## we allow to load multiple pages with one command.

        otherwise
          error ("imread: invalid PARAMETER `%s'", varargin{idx});

      endswitch
    endfor

    [varargout{1:nargout}] = __magick_read__ (fn, options);

  catch
    ## If we can't read it with Magick, maybe the image is in Octave's
    ## native image format.  This is from back before Octave had 'imread'
    ## and 'imwrite'. Then we had the functions 'loadimage' and 'saveimage'.
    ##
    ## This "image format" seems to be any file that can be read with
    ## load() and contains 2 variables.  The variable named "map" is a
    ## colormap and must exist whether the image is indexed or not. The
    ## other variable must be named "img" or "X" for a "normal" or
    ## indexed image.
    ##
    ## FIXME: this has been deprecated for the next major release (3.8 or 4.0).
    ##        If someone wants to revive this as yet another image format, a
    ##        separate Octave package can be written for it, that register the
    ##        format through imformats.

    magick_error = lasterr ();

    img_field = false;
    x_field   = false;
    map_field = false;

    try
      vars = load (fn);
      if (isstruct (vars))
        img_field = isfield (vars, "img");
        x_field   = isfield (vars, "X");
        map_field = isfield (vars, "map");
      endif
    catch
      error ("imread: invalid image file: %s", magick_error);
    end_try_catch

    if (map_field && (img_field || x_field))
      varargout{2} = vars.map;
      if (img_field)
        varargout{1} = vars.img;
      else
        varargout{1} = vars.X;
      endif
      persistent warned = false;
      if (! warned)
        warning ("Octave's native image format has been deprecated.");
        warned = true;
      endif
    else
      error ("imread: invalid Octave image file format");
    endif

  end_try_catch

endfunction

## Tests if the value passed to the Index or Frames is valid. This option
## can be defined in two places, but only in one place can it also be the
## string "all"
function bool = is_valid_index_option (arg)
  ## is the index option
  bool = false;
  if (isvector (arg) && isnumeric (arg) && isreal (arg))
    bool = true;
  endif
endfunction