changeset 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 4976f66d469b
children d74f996e005d
files scripts/ChangeLog scripts/image/imread.m src/ChangeLog src/DLD-FUNCTIONS/__magick_read__.cc
diffstat 4 files changed, 564 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/ChangeLog	Fri Jul 11 17:59:28 2008 -0400
+++ b/scripts/ChangeLog	Sat Jul 12 10:11:30 2008 -0400
@@ -1,3 +1,7 @@
+2008-07-14  Thomas L. Scofield  <scofield@calvin.edu>
+
+	* image/imread.m: New file from Octave Forge.
+
 2008-07-10  Jaroslav Hajek <highegg@gmail.com>
 
 	* set/unique.m: Implement 'first' and 'last', some simplifications.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/image/imread.m	Sat Jul 12 10:11:30 2008 -0400
@@ -0,0 +1,192 @@
+## 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
--- a/src/ChangeLog	Fri Jul 11 17:59:28 2008 -0400
+++ b/src/ChangeLog	Sat Jul 12 10:11:30 2008 -0400
@@ -1,3 +1,7 @@
+2008-07-14  Thomas L. Scofield  <scofield@calvin.edu>
+
+	* DLD-FUNCTIONS/__magick_read__.cc: New file from Octave Forge.
+
 2008-07-11  John W. Eaton  <jwe@octave.org>
 
 	* syscalls.cc (const_value): Delete arg NM.  Change all uses.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DLD-FUNCTIONS/__magick_read__.cc	Sat Jul 12 10:11:30 2008 -0400
@@ -0,0 +1,364 @@
+/*
+
+Copyright (C) 2002 Andy Adler
+              2008 Thomas L. Scofield
+
+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/>.
+
+*/
+
+
+#include <octave/oct.h>
+#include <GraphicsMagick/Magick++.h>
+#include <iostream>
+using namespace std;
+using namespace Magick;
+
+unsigned int
+scaleQuantumToDepth (const Quantum &_quantum, unsigned int depth)
+{
+  return (static_cast<unsigned int> (static_cast<double>(_quantum) / 
+                                     MaxRGB * ((1 << depth) - 1)));
+}
+
+octave_value_list
+read_indexed_images( vector<Image> imvec, Array<int> frameidx, bool wantalpha )
+{
+  octave_value_list output;
+  int rows = imvec[0].baseRows ();
+  int columns = imvec[0].baseColumns ();
+  int nframes = frameidx.length ();
+  ImageType type = imvec[0].type ();
+    
+  unsigned int mapsize = imvec[0].colorMapSize ();
+  int i = mapsize;
+  unsigned int depth = 0;
+  while (i >>= 1) depth++;
+  i = 0;
+  depth--;
+  while (depth >>= 1) i++;
+  depth = 1 << i;
+
+  int x, y, frame;
+  const IndexPacket *pix;
+  switch (depth)
+    {
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+      {
+      uint8NDArray im = uint8NDArray(dim_vector ( rows, columns, nframes ));
+      for (frame=0; frame < nframes; frame++)
+        {
+        imvec[frameidx(frame)].getConstPixels ( 0, 0, columns, rows );
+        pix = imvec[frameidx(frame)].getConstIndexes ();
+        i = 0;      
+        for ( y=0; y < rows; y++ )
+          for ( x=0; x < columns; x++ )
+            im(y, x, frame) = static_cast<octave_uint8>(pix[i++]);
+        }
+      im.chop_trailing_singletons ();
+      output(0) = octave_value (im);
+      }
+      break;
+    case 16:
+      {
+      uint16NDArray im = uint16NDArray(dim_vector( rows, columns, nframes ));
+      for (frame=0; frame < nframes; frame++)
+        {
+        imvec[frameidx(frame)].getConstPixels ( 0, 0, columns, rows );
+        pix = imvec[frameidx(frame)].getConstIndexes ();        
+        i = 0;      
+        for ( y=0; y < rows; y++ )
+          for ( x=0; x < columns; x++ )
+            im(y, x, frame) = static_cast<octave_uint16>(pix[i++]);
+        }
+        im.chop_trailing_singletons ();
+        output(0) = octave_value (im);
+      }
+      break;
+    default:
+      error ("Index depths bigger than 16-bit not supported");
+      return octave_value_list ();
+    }
+
+  ColorRGB c;
+  Matrix map = Matrix ( mapsize, 3 );
+  Matrix alpha;
+  switch (type)
+    {
+    case PaletteMatteType:
+/*    warning ("palettematte");
+      map = Matrix ( mapsize, 3 );
+      alpha = Matrix ( mapsize, 1 );
+      for ( i = 0; i < mapsize; i++ )
+        {
+        warning ( "%d", i );
+        c = imvec[0].colorMap (i);
+        map(i, 0) = c.red ();
+        map(i, 1) = c.green ();
+        map(i, 2) = c.blue ();
+        alpha(i, 1) = c.alpha ();                
+        }
+      break;        */
+    case PaletteType:
+      alpha = Matrix ( 0, 0 );
+      for ( i = 0; i < mapsize; i++ )
+        {
+        c = imvec[0].colorMap (i);
+        map(i, 0) = c.red ();
+        map(i, 1) = c.green ();
+        map(i, 2) = c.blue ();        
+        }
+      break;        
+    default:
+      error ("Unsupported indexed image type");
+      return octave_value_list ();
+    }
+
+  output(1) = octave_value (map);
+  if (wantalpha)
+    output(2) = octave_value (alpha);
+  return output;
+}
+
+template <class T>
+octave_value_list read_images( vector<Image> imvec, Array<int> frameidx,
+                               unsigned int depth )
+{
+  int i;
+  T im;  
+  int rows = imvec[0].baseRows ();
+  int columns = imvec[0].baseColumns ();
+  int nframes = frameidx.length ();
+  ImageType type = imvec[0].type ();
+  int x, y, frame;
+  const PixelPacket *pix;
+  dim_vector idim = dim_vector ();
+  idim.resize (4);
+  idim(0) = rows;
+  idim(1) = columns;
+  idim(2) = 1;
+  idim(3) = nframes;
+  Array<int> idx (dim_vector (4));
+  switch (type)
+    {
+    case BilevelType:
+    //    break;
+    case GrayscaleType:
+      im = T(dim_vector ( rows, columns, nframes ));
+      for ( frame=0; frame < nframes; frame++ )
+        {
+        pix = imvec[frameidx(frame)].getConstPixels ( 0, 0, columns, rows );
+        i = 0;      
+        for ( y=0; y < rows; y++ )
+          for ( x=0; x < columns; x++ )
+            im(y, x, frame) = scaleQuantumToDepth ( pix[i++].red, depth );
+        }
+      break;
+    case GrayscaleMatteType:
+      idim(2) = 2;
+      im = T(idim);
+      for ( frame=0; frame < nframes; frame++ )
+        {
+        idx(3) = frame;
+        i = 0;
+        pix = imvec[frameidx(frame)].getConstPixels ( 0, 0, columns, rows );
+        for ( y=0; y < rows; y++ )
+          {
+          idx(0) = y;
+          for ( x=0; x < columns; x++ )
+            {
+            idx(1) = x;
+            idx(2) = 0;
+            im(idx) = scaleQuantumToDepth ( pix[i].red, depth );
+            idx(2) = 1;
+            im(idx) = scaleQuantumToDepth ( pix[i].opacity, depth );
+            i++;
+            }
+          }
+        }    
+      break;
+    case PaletteType:
+    case TrueColorType:
+      idim(2) = 3;
+      im = T(idim);      
+      for ( frame=0; frame < nframes; frame++ )
+        {
+        idx(3) = frame;
+        i = 0;
+        pix = imvec[frameidx(frame)].getConstPixels ( 0, 0, columns, rows );
+        for ( y=0; y < rows; y++ )
+          {
+          idx(0) = y;
+          for ( x=0; x < columns; x++ )
+            {
+            idx(1) = x;
+            idx(2) = 0;
+            im(idx) = scaleQuantumToDepth ( pix[i].red, depth );
+            idx(2) = 1;
+            im(idx) = scaleQuantumToDepth ( pix[i].green, depth );
+            idx(2) = 2;
+            im(idx) = scaleQuantumToDepth ( pix[i].blue, depth );
+            i++;
+            }
+          }
+        }      
+      break;
+    case PaletteMatteType:
+    case TrueColorMatteType:
+    case ColorSeparationType:
+      idim(2) = 4;
+      im = T(idim);            
+      for ( frame=0; frame < nframes; frame++ )
+        {
+        idx(3) = frame;
+        i = 0;
+        pix = imvec[frameidx(frame)].getConstPixels ( 0, 0, columns, rows );
+        for ( y=0; y < rows; y++ )
+          {
+          idx(0) = y;
+          for ( x=0; x < columns; x++ )
+            {
+            idx(1) = x;
+            idx(2) = 0;
+            im(idx) = scaleQuantumToDepth ( pix[i].red, depth );
+            idx(2) = 1;
+            im(idx) = scaleQuantumToDepth ( pix[i].green, depth );
+            idx(2) = 2;
+            im(idx) = scaleQuantumToDepth ( pix[i].blue, depth );
+            idx(2) = 3;
+            im(idx) = scaleQuantumToDepth ( pix[i].opacity, depth );
+            i++;
+            }
+          }
+        }      
+      break;
+    default:
+      error ("Undefined Imagemagick image type");
+      return octave_value_list ();
+    }
+
+  im.chop_trailing_singletons ();
+  return octave_value_list (octave_value (im));
+}
+
+// instantiate templates
+template octave_value_list
+read_images<boolNDArray> ( vector<Image>, Array<int>, unsigned int depth );
+template octave_value_list
+read_images<uint8NDArray> ( vector<Image>, Array<int>, unsigned int depth );
+template octave_value_list
+read_images<uint16NDArray> ( vector<Image>, Array<int>, unsigned int depth );
+
+DEFUN_DLD ( __magick_read__, args, nargout, "\
+-*- texinfo -*-\n\
+@deftypefn {Function File} {@var{m} =} __imagemagick_read__(@var{fname}, @var{index})\n\
+@deftypefnx{Function File} {[@var{m}, @var{colormap}] =} __imagemagick_read__(@var{fname}, @var{index})\n\
+@deftypefnx{Function File} {[@var{m}, @var{colormap}, @var{alpha}] =} __imagemagick_read__(@var{fname}, @var{index})\n\
+Read images with ImageMagick++. In general you should not be using this function.\n\
+Instead you should use @code{imread}.\n\
+@seealso{imread}\n\
+@end deftypefn\n\
+" )
+{
+  octave_value_list output;
+  int i;    
+  if( args.length() > 2 || args.length() < 1 || !args(0).is_string() \
+            || nargout > 3 )
+    {
+      print_usage ();
+      return octave_value_list ();
+    }
+  Array<int> frameidx;
+  if ( args.length() == 2 && args(1).is_real_type() )
+    frameidx = args(1).int_vector_value();
+  else
+    {
+    frameidx = Array<int> (1);
+    frameidx(0) = 1;
+    }
+
+  vector<Image> imvec;
+  try
+    {
+    // Read a file into vector of image objects
+    readImages(&imvec, args(0).string_value ());
+    }
+  catch (Warning &warning_)
+    { warning ( "Magick++ warning: %s", warning_.what () ); }
+  catch (ErrorCoder &error_)
+    { warning ( "Magick++ coder error: %s", error_.what () ); }
+  catch (Exception &error_)
+    {
+    error ( "Magick++ exception: %s", error_.what () );
+    imvec.clear ();
+    return octave_value_list ();
+    }
+
+  int nframes = imvec.size ();
+  for ( i = 0; i < frameidx.length(); i++ )
+    {
+    frameidx(i) = frameidx(i) - 1;
+    if ( frameidx(i) >= nframes || frameidx(i) < 0 )
+      {
+      error ("Invalid index vector");
+      imvec.clear ();
+      return output;
+      }
+    }
+    
+  ClassType klass = imvec[0].classType ();
+  if ( klass == PseudoClass && nargout > 1 )
+    output = read_indexed_images( imvec, frameidx, (nargout == 3) );
+  else
+    {
+    unsigned int depth = imvec[0].modulusDepth ();
+    i = 0;
+    while (depth >>= 1) i++;
+    depth = 1 << i;
+    
+    switch (depth)
+      {
+      case 1:
+        output = read_images<boolNDArray> ( imvec, frameidx, depth );
+        break;
+      case 2:
+      case 4:
+      case 8:
+        output = read_images<uint8NDArray> ( imvec, frameidx, depth) ;
+        break;
+      case 16:
+        output = read_images<uint16NDArray> ( imvec, frameidx, depth );
+        break;
+      case 32:
+      case 64:
+        default:
+        error ("Image depths bigger than 16-bit not supported");
+      }
+    if (nargout > 1)
+      {
+      output(1) = Matrix ( 0, 0 );
+      if (nargout > 2)
+        output(2) = Matrix ( 0, 0 );
+      }
+    }
+  imvec.clear ();
+
+  return output;
+}