view scripts/image/imread.m @ 7925:9316b59903c9

Add original imread() files (from octave-forge) to core octave. * * * Modify imread() for partial reliance on GraphicsMagick libs; clean up code.
author Thomas L. Scofield <scofield AT calvin DOT edu>
date Sat, 12 Jul 2008 10:11:30 -0400
parents
children de26beacb20f
line wrap: on
line source

## Copyright (C) 2002 Andy Adler
## Copyright (C) 2005 Stefan van der Walt <stefan@sun.ac.za>
## Copyright (C) 2006 Thomas Weber <thomas.weber.mail@gmail.com>
## Copyright (C) 2008 Kristian Rumberg <kristianrumberg@gmail.com>
## Copyright (C) 2008 Thomas L. Scofield <scofield@calvin.edu>
##
## 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/>.

## -*- texinfo -*-
## @deftypefn {Function File} {@var{I} =} imread(@var{filename})
## Read images from various file formats.
##
## The size and numeric class of the output depends on the
## format of the image.  A colour image is returned as an
## MxNx3 matrix.  Grey-level and black-and-white images are
## of size MxN.
## The colour depth of the image determines the numeric
## class of the output: 'uint8' or 'uint16' for grey
## and colour, and 'logical' for black and white.
##
## Note: For image formats other than jpeg and png, the
## ImageMagick "convert" and "identify" utilities
## are needed. ImageMagick can be found at www.imagemagick.org
## @end deftypefn

## Author: Andy Adler
##
## Modified: Stefan van der Walt <stefan@sun.ac.za>
## Date: 24 January 2005
##
## Modified: Thomas Weber <thomas.weber.mail@gmail.com>
## Date: 20 December 2006
## Change parsing of imagemagick's output to get the 'color' depth for grayscale
## images
##
## Modified Kristian Rumberg <kristianrumberg@gmail.com>
## Date 2 April 2008
## Imread now works with BMP's created with "convert inputimage out.bmp"
## (tested with stable release Octave 3.0 in GNU/Linux and Windows XP),
## modified the calling parameters to identify and convert
##
## Modified Thomas Scofield <scofield 'at' calvin.edu
## Date 1 July 2008
## Imread now uses Magick++ API to GraphicsMagick libraries instead
## of ImageMagick libraries (tested with stable release Octave 3.0
## in GNU/Linux).  All images are read by GraphicsMagick routines;
## there is no longer exceptional handling of .png and .jpg files.

function varargout = imread ( filename, varargin )
  if (nargin < 1)
    usage ("I = imread(filename)")
  endif

  if (!ischar (filename))
    error ("imread: filename must be a string")
  endif

  filename = tilde_expand (filename);
  fn = file_in_path ( IMAGE_PATH, filename );
  if (isempty (fn))
    error ( "imread: cannot find %s", filename );
  endif

  [ig, ig, ext] = fileparts (fn);
  ext = upper (ext);    
    
  ## real work uses GraphicsMagick
  if ( file_in_loadpath ("__magick_read__.oct") )
    [varargout{1:nargout}] = __magick_read__ ( fn, varargin{:} );
    ## color .PNG formats (any others?) have 4 levels in the 3rd dimension
    if ( length (size (varargout{1}))==3 && length (varargout{1}(1,1,:)) > 3 )
      varargout{1} = varargout{1}(:,:,1:3);
    endif
    break
  endif
    
  ## The next line could be altered from "identify ..." to "gm identify ..."
  ## If we continue to carry this out with a system call, my guess is that
  ## more people will have ImageMagick than have GraphicsMagick.  But if
  ## we want this to work for people who have neither, then the system call
  ## needs to be scrapped and replaced with a call to a dynamically-linked
  ## routine that again employs the C++ API to GraphicsMagick.
  ## Note also the system call to ImageMagick's "convert" farther down.
  cmd = sprintf ('identify -verbose \"%s\" | grep -e "bit" -e Type', fn);
  [sys, ident] = system (cmd);
  if (sys != 0)
    error ( "imread: error running ImageMagick's 'identify' on %s", fn );
  endif
  depth = re_grab ( "([[:digit:]]{1,2})-bit", ident );
  imtype = re_grab ( "Type: ([[:alpha:]]*)", ident );

  depth = str2num (depth);
  if ( isempty (depth) || ( pow2 (nextpow2 (depth)) != depth ) )
    error ( "imread: invalid image depth %s", depth );
  endif

  if !( strcmp ( imtype, "Bilevel" )   || strcmp ( imtype, "Grayscale" )
        || strcmp ( imtype, "TrueColor" ) || strcmp ( imtype, "TrueColorMatte" )
        || strcmp ( imtype, "Palette" ) || strcmp ( imtype, "PaletteMatte" ) )
    # The 'PaletteMatte' option added by TLS to accomodate ImageMagick
    # on .png images.  It appears GraphicsMagick returns a different
    # string, so this will likely be only a temporary change.
    error ( "imread: unknown image type '%s'", imtype );
  endif

  switch (imtype)
    case {"Bilevel"} fmt = "pgm";
    case {"Grayscale"} fmt = "pgm";
    case {"TrueColor", "TrueColorMatte", "Palette", "PaletteMatte"}
      fmt = "ppm";
  endswitch
    
  ## Why are pipes so slow?
  ##    cmd = sprintf ( "convert -flatten -strip %s %s:-", fn, fmt );
    
  tmpf = [tmpnam(), ".", fmt];
  ##cmd = sprintf ( "convert -flatten -strip +compress '%s' '%s' 2>/dev/null",
  ##                fn, tmpf );
  cmd = sprintf ( "convert -strip \"%s\" \"%s\"", fn, tmpf );

  sys = system (cmd);    
  if (sys != 0)
    error ("imread: error running ImageMagick's 'convert'");
    unlink (tmpf);
  endif

  try
    fid = fopen ( tmpf, "rb" );
  catch
    unlink (tmpf);
    error ( "imread: could not open temporary file %s", tmpf )
  end_try_catch

  fgetl (fid); # P5 or P6 (pgm or ppm)
  [width, height] = sscanf ( fgetl (fid), "%d %d", "C" );
  fgetl (fid); # ignore max components

  if (depth == 16)
    ## PGM format has MSB first, i.e. big endian
    [data, count] = fread ( fid, "uint16", 0, "ieee-be" );
  else
    [data, count] = fread ( fid, "uint8" );
  endif
    
  fclose (fid);
  unlink (tmpf);

  if (any (strcmp ( imtype, {"TrueColor", "TrueColorMatte", ...
                             "Palette", "PaletteMatte"} ) ) )
    channels = 3;
  else
    channels = 1;
  endif
  if (count != width*height*channels)
    error ( "imread: image data chunk has invalid size %i != %i*%i*%i == %i",
            count, width, height, channels, width*height*channels );
  endif

  varargout = {};
  switch (imtype)
    case {"Bilevel"} varargout{1} = logical ( reshape (data, width, height)' );
    case {"Grayscale"} varargout{1} = uint8 ( reshape (data, width, height)' );
    case {"TrueColor", "TrueColorMatte", "Palette", "PaletteMatte"}
      varargout{1} = cat(3, reshape ( data(1:3:end), width, height )',
                         reshape ( data(2:3:end), width, height )',
                         reshape ( data(3:3:end), width, height )');
      eval( sprintf( "varargout{1} = uint%d(varargout{1});", depth ) );
  endswitch
endfunction

function value = re_grab ( re, str )
  T = regexp ( str, re, 'tokens' );
  if ( isempty (T) )
    value = "";
  else
    value = T{1}{1};
  endif
endfunction