# HG changeset patch # User Thomas L. Scofield # Date 1215871890 14400 # Node ID 9316b59903c9b3b1e6e41dfd95f3284157ce4362 # Parent 4976f66d469b51185e96b2df7f1f48eefb602331 Add original imread() files (from octave-forge) to core octave. * * * Modify imread() for partial reliance on GraphicsMagick libs; clean up code. diff -r 4976f66d469b -r 9316b59903c9 scripts/ChangeLog --- 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 + + * image/imread.m: New file from Octave Forge. + 2008-07-10 Jaroslav Hajek * set/unique.m: Implement 'first' and 'last', some simplifications. diff -r 4976f66d469b -r 9316b59903c9 scripts/image/imread.m --- /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 +## Copyright (C) 2006 Thomas Weber +## Copyright (C) 2008 Kristian Rumberg +## Copyright (C) 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 +## . + +## -*- 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 +## Date: 24 January 2005 +## +## Modified: Thomas Weber +## Date: 20 December 2006 +## Change parsing of imagemagick's output to get the 'color' depth for grayscale +## images +## +## Modified Kristian Rumberg +## 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 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 diff -r 4976f66d469b -r 9316b59903c9 src/ChangeLog --- 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 + + * DLD-FUNCTIONS/__magick_read__.cc: New file from Octave Forge. + 2008-07-11 John W. Eaton * syscalls.cc (const_value): Delete arg NM. Change all uses. diff -r 4976f66d469b -r 9316b59903c9 src/DLD-FUNCTIONS/__magick_read__.cc --- /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 +. + +*/ + + +#include +#include +#include +using namespace std; +using namespace Magick; + +unsigned int +scaleQuantumToDepth (const Quantum &_quantum, unsigned int depth) +{ + return (static_cast (static_cast(_quantum) / + MaxRGB * ((1 << depth) - 1))); +} + +octave_value_list +read_indexed_images( vector imvec, Array 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(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(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 +octave_value_list read_images( vector imvec, Array 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 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 ( vector, Array, unsigned int depth ); +template octave_value_list +read_images ( vector, Array, unsigned int depth ); +template octave_value_list +read_images ( vector, Array, 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 frameidx; + if ( args.length() == 2 && args(1).is_real_type() ) + frameidx = args(1).int_vector_value(); + else + { + frameidx = Array (1); + frameidx(0) = 1; + } + + vector 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 ( imvec, frameidx, depth ); + break; + case 2: + case 4: + case 8: + output = read_images ( imvec, frameidx, depth) ; + break; + case 16: + output = read_images ( 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; +}