/*
Copyright (C) 2002, 2009 Andy Adler
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
.
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "defun-dld.h"
#include "error.h"
#include "ov-struct.h"
#ifdef HAVE_MAGICK
#include
unsigned int
scale_quantum_to_depth (const Magick::Quantum& quantum, unsigned int depth)
{
return (static_cast (static_cast (quantum)
/ MaxRGB * ((1 << depth) - 1)));
}
octave_value_list
read_indexed_images (std::vector& imvec,
const Array& frameidx, bool wantalpha)
{
octave_value_list output;
int rows = imvec[0].baseRows ();
int columns = imvec[0].baseColumns ();
int nframes = frameidx.length ();
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));
Magick::ImageType type = imvec[0].type ();
unsigned int mapsize = imvec[0].colorMapSize ();
unsigned int i = mapsize;
unsigned int depth = 0;
while (i >>= 1)
depth++;
i = 0;
depth--;
while (depth >>= 1)
i++;
depth = 1 << i;
switch (depth)
{
case 1:
case 2:
case 4:
case 8:
{
uint8NDArray im = uint8NDArray (idim);
idx(2) = 0;
for (int frame = 0; frame < nframes; frame++)
{
imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
const Magick::IndexPacket *pix
= imvec[frameidx(frame)].getConstIndexes ();
i = 0;
idx(3) = frame;
for (int y = 0; y < rows; y++)
{
idx(0) = y;
for (int x = 0; x < columns; x++)
{
idx(1) = x;
im(idx) = static_cast (pix[i++]);
}
}
}
im.chop_trailing_singletons ();
output(0) = octave_value (im);
}
break;
case 16:
{
uint16NDArray im = uint16NDArray (idim);
idx(2) = 0;
for (int frame = 0; frame < nframes; frame++)
{
imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
const Magick::IndexPacket *pix
= imvec[frameidx(frame)].getConstIndexes ();
i = 0;
idx(3) = frame;
for (int y = 0; y < rows; y++)
{
idx(0) = y;
for (int x = 0; x < columns; x++)
{
idx(1) = x;
im(idx) = static_cast (pix[i++]);
}
}
}
im.chop_trailing_singletons ();
output(0) = octave_value (im);
}
break;
default:
error ("__magic_read__: index depths bigger than 16-bit not supported");
return octave_value_list ();
}
Matrix map = Matrix (mapsize, 3);
Matrix alpha;
switch (type)
{
case Magick::PaletteMatteType:
#if 0
warning ("palettematte");
Matrix map (mapsize, 3);
Matrix alpha (mapsize, 1);
for (i = 0; i < mapsize; i++)
{
warning ("%d", i);
Magick::ColorRGB 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;
#endif
case Magick::PaletteType:
alpha = Matrix (0, 0);
for (i = 0; i < mapsize; i++)
{
Magick::ColorRGB c = imvec[0].colorMap (i);
map(i,0) = c.red ();
map(i,1) = c.green ();
map(i,2) = c.blue ();
}
break;
default:
error ("__magick_read__: unsupported indexed image type");
return octave_value_list ();
}
if (wantalpha)
output(2) = alpha;
output(1) = map;
return output;
}
template
octave_value_list
read_images (const std::vector& imvec,
const Array& frameidx, unsigned int depth)
{
octave_value_list retval (3, Matrix ());
T im;
int rows = imvec[0].baseRows ();
int columns = imvec[0].baseColumns ();
int nframes = frameidx.length ();
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));
Magick::ImageType type = imvec[0].type ();
switch (type)
{
case Magick::BilevelType:
case Magick::GrayscaleType:
im = T (idim);
for (int frame = 0; frame < nframes; frame++)
{
const Magick::PixelPacket *pix
= imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
int i = 0;
idx(2) = 0;
idx(3) = frame;
for (int y = 0; y < rows; y++)
{
idx(0) = y;
for (int x = 0; x < columns; x++)
{
idx(1) = x;
im(idx) = scale_quantum_to_depth (pix[i++].red, depth);
}
}
}
break;
case Magick::GrayscaleMatteType:
idim(2) = 2;
im = T (idim);
for (int frame = 0; frame < nframes; frame++)
{
const Magick::PixelPacket *pix
= imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
int i = 0;
idx(3) = frame;
for (int y = 0; y < rows; y++)
{
idx(0) = y;
for (int x = 0; x < columns; x++)
{
idx(1) = x;
idx(2) = 0;
im(idx) = scale_quantum_to_depth (pix[i].red, depth);
idx(2) = 1;
im(idx) = scale_quantum_to_depth (pix[i].opacity, depth);
i++;
}
}
}
break;
case Magick::PaletteType:
case Magick::TrueColorType:
idim(2) = 3;
im = T (idim);
for (int frame = 0; frame < nframes; frame++)
{
const Magick::PixelPacket *pix
= imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
int i = 0;
idx(3) = frame;
for (int y = 0; y < rows; y++)
{
idx(0) = y;
for (int x = 0; x < columns; x++)
{
idx(1) = x;
idx(2) = 0;
im(idx) = scale_quantum_to_depth (pix[i].red, depth);
idx(2) = 1;
im(idx) = scale_quantum_to_depth (pix[i].green, depth);
idx(2) = 2;
im(idx) = scale_quantum_to_depth (pix[i].blue, depth);
i++;
}
}
}
break;
case Magick::PaletteMatteType:
case Magick::TrueColorMatteType:
case Magick::ColorSeparationType:
idim(2) = 4;
im = T (idim);
for (int frame = 0; frame < nframes; frame++)
{
const Magick::PixelPacket *pix
= imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
int i = 0;
idx(3) = frame;
for (int y = 0; y < rows; y++)
{
idx(0) = y;
for (int x = 0; x < columns; x++)
{
idx(1) = x;
idx(2) = 0;
im(idx) = scale_quantum_to_depth (pix[i].red, depth);
idx(2) = 1;
im(idx) = scale_quantum_to_depth (pix[i].green, depth);
idx(2) = 2;
im(idx) = scale_quantum_to_depth (pix[i].blue, depth);
idx(2) = 3;
im(idx) = scale_quantum_to_depth (pix[i].opacity, depth);
i++;
}
}
}
break;
default:
error ("__magick_read__: undefined ImageMagick image type");
return retval;
}
im.chop_trailing_singletons ();
retval(0) = im;
return retval;
}
#endif
DEFUN_DLD (magick_read, args, nargout,
"-*- texinfo -*-\n\
@deftypefn {Function File} {@var{m} =} __magick_read__(@var{fname}, @var{index})\n\
@deftypefnx{Function File} {[@var{m}, @var{colormap}] =} __magick_read__(@var{fname}, @var{index})\n\
@deftypefnx{Function File} {[@var{m}, @var{colormap}, @var{alpha}] =} __magick_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")
{
octave_value_list output;
#ifdef HAVE_MAGICK
if (args.length () > 2 || args.length () < 1 || ! args(0).is_string ()
|| nargout > 3)
{
print_usage ();
return output;
}
Array frameidx;
if (args.length () == 2 && args(1).is_real_type ())
frameidx = args(1).int_vector_value();
else
{
frameidx = Array (1);
frameidx(0) = 1;
}
std::vector imvec;
try
{
// Read a file into vector of image objects
Magick::readImages (&imvec, args(0).string_value ());
}
catch (Magick::Warning& w)
{
warning ("Magick++ warning: %s", w.what ());
}
catch (Magick::ErrorCoder& e)
{
warning ("Magick++ coder error: %s", e.what ());
}
catch (Magick::Exception& e)
{
error ("Magick++ exception: %s", e.what ());
return output;
}
for (int i = 0; i < frameidx.length(); i++)
{
frameidx(i) = frameidx(i) - 1;
int nframes = imvec.size ();
if (frameidx(i) >= nframes || frameidx(i) < 0)
{
error ("__magick_read__: invalid index vector");
return output;
}
}
Magick::ClassType klass = imvec[0].classType ();
if (klass == Magick::PseudoClass && nargout > 1)
output = read_indexed_images (imvec, frameidx, (nargout == 3));
else
{
unsigned int depth = imvec[0].modulusDepth ();
if (depth > 1)
{
--depth;
int i = 1;
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 ("__magick_read__: image depths bigger than 16-bit not supported");
}
}
#else
error ("__magick_read__: not available in this version of Octave");
#endif
return output;
}
#ifdef HAVE_MAGICK
static void
jpg_settings (std::vector& imvec,
const Octave_map& options,
bool)
{
int nframes = static_cast(imvec.size ());
bool something_set = 0;
// Quality setting
octave_value result;
Octave_map::const_iterator p;
bool found_it = 0;
for (p = options.begin (); p != options.end (); p++)
if (options.key (p) == "Quality")
{
found_it = 1;
result = options.contents (p).elem (0);
break;
}
if (found_it && (! result.is_empty ()))
{
something_set = 1;
if (result.is_real_type ())
{
int qlev = static_cast(result.int_value ());
if (qlev < 0 || qlev > 100)
warning ("warning: Quality setting invalid--use default of 75");
else
for (int fnum = 0; fnum < nframes; fnum++)
imvec[fnum].quality (static_cast(qlev));
}
else
warning ("warning: Quality setting invalid--use default of 75");
}
// Other settings go here
if (! something_set)
warning ("__magick_write__ warning: All write parameters ignored.");
}
static void
encode_bool_image (std::vector& imvec, const octave_value& img)
{
unsigned int nframes = 1;
boolNDArray m = img.bool_array_value ();
dim_vector dsizes = m.dims ();
if (dsizes.length () == 4)
nframes = dsizes(3);
Array idx (dsizes.length ());
octave_idx_type rows = m.rows ();
octave_idx_type columns = m.columns ();
for (unsigned int ii = 0; ii < nframes; ii++)
{
Magick::Image im(Magick::Geometry (columns, rows), "black");
im.classType (Magick::DirectClass);
im.depth (1);
for (int y=0; y < columns; y++)
{
idx(1) = y;
for (int x=0; x < rows; x++)
{
if (nframes > 1)
{
idx(2) = 0;
idx(3) = ii;
}
idx(0) = x;
if (m(idx))
im.pixelColor (y, x, "white");
}
}
imvec.push_back (im);
}
}
template
static void
encode_uint_image (std::vector& imvec,
const octave_value& img,
bool has_map)
{
unsigned int bitdepth = 0;
T m;
if (img.is_uint8_type ())
{
bitdepth = 8;
m = img.uint8_array_value ();
}
else if (img.is_uint16_type ())
{
bitdepth = 16;
m = img.uint16_array_value ();
}
else
error ("__magick_write__: invalid image class");
dim_vector dsizes = m.dims ();
unsigned int nframes = 1;
if (dsizes.length () == 4)
nframes = dsizes(3);
bool is_color = ((dsizes.length () > 2) && (dsizes(2) > 2));
bool has_alpha = (dsizes.length () > 2 && (dsizes(2) == 2 || dsizes(2) == 4));
Array idx (dsizes.length ());
octave_idx_type rows = m.rows ();
octave_idx_type columns = m.columns ();
// FIXME -- maybe simply using bit shifting would be better?
unsigned int div_factor = pow (2.0, static_cast (bitdepth)) - 1;
for (unsigned int ii = 0; ii < nframes; ii++)
{
Magick::Image im(Magick::Geometry (columns, rows), "black");
im.depth (bitdepth);
if (has_map)
im.classType (Magick::PseudoClass);
else
im.classType (Magick::DirectClass);
if (is_color)
{
if (has_alpha)
im.type (Magick::TrueColorMatteType);
else
im.type (Magick::TrueColorType);
Magick::ColorRGB c;
for (int y=0; y < columns; y++)
{
idx(1) = y;
for (int x=0; x < rows; x++)
{
idx(0) = x;
if (nframes > 1)
idx(3) = ii;
idx(2) = 0;
c.red (static_cast(m(idx)) / div_factor);
idx(2) = 1;
c.green (static_cast(m(idx)) / div_factor);
idx(2) = 2;
c.blue (static_cast(m(idx)) / div_factor);
if (has_alpha)
{
idx(2) = 3;
c.alpha (static_cast(m(idx)) / div_factor);
}
im.pixelColor (y, x, c);
}
}
}
else
{
if (has_alpha)
im.type (Magick::GrayscaleMatteType);
else
im.type (Magick::GrayscaleType);
Magick::ColorGray c;
for (int y=0; y < columns; y++)
{
idx(1) = y;
for (int x=0; x < rows; x++)
{
idx(0) = x;
if (nframes > 1)
{
idx(2) = 0;
idx(3) = ii;
}
if (has_alpha)
{
idx(2) = 1;
c.alpha (static_cast(m(idx)) / div_factor);
idx(2) = 0;
}
c.shade (static_cast(m(idx)) / div_factor);
im.pixelColor (y, x, c);
}
}
}
imvec.push_back (im);
}
}
static void
encode_map (std::vector& imvec, const NDArray& cmap)
{
unsigned int mapsize = cmap.dim1 ();
int nframes = static_cast(imvec.size ());
for (int fnum = 0; fnum < nframes; fnum++)
{
imvec[fnum].colorMapSize (mapsize);
imvec[fnum].type (Magick::PaletteType);
}
for (unsigned int ii = 0; ii < mapsize; ii++)
{
Magick::ColorRGB c (cmap(ii,0), cmap(ii,1), cmap(ii,2));
// FIXME -- is this case needed?
if (cmap.dim2 () == 4)
c.alpha (cmap(ii,3));
try
{
for_each (imvec.begin (), imvec.end (),
Magick::colorMapImage (ii, c));
}
catch (Magick::Warning& w)
{
warning ("Magick++ warning: %s", w.what ());
}
catch (Magick::ErrorCoder& e)
{
warning ("Magick++ coder error: %s", e.what ());
}
catch (Magick::Exception& e)
{
error ("Magick++ exception: %s", e.what ());
}
}
}
static void
write_image (const std::string& filename, const std::string& fmt,
const octave_value& img,
const octave_value& map = octave_value (),
const octave_value& params = octave_value ())
{
std::vector imvec;
bool has_map = map.is_defined ();
if (has_map)
{
error ("__magick_write__: direct saving of indexed images not currently supported; use ind2rgb and save converted image");
return;
}
if (img.is_bool_type ())
encode_bool_image (imvec, img);
else if (img.is_uint8_type ())
encode_uint_image (imvec, img, has_map);
else if (img.is_uint16_type ())
encode_uint_image (imvec, img, has_map);
else
error ("__magick_write__: image type not supported");
if (! error_state && has_map)
{
NDArray cmap = map.array_value ();
if (! error_state)
encode_map (imvec, cmap);
}
if (! error_state && params.is_defined ())
{
Octave_map options = params.map_value ();
// Insert calls here to handle parameters for various image formats
if (fmt == "jpg" || fmt == "jpeg")
jpg_settings (imvec, options, has_map);
else
warning ("warning: your parameter(s) currently not supported");
}
try
{
Magick::writeImages (imvec.begin (), imvec.end (), filename);
}
catch (Magick::Warning& w)
{
warning ("Magick++ warning: %s", w.what ());
}
catch (Magick::ErrorCoder& e)
{
warning ("Magick++ coder error: %s", e.what ());
}
catch (Magick::Exception& e)
{
error ("Magick++ exception: %s", e.what ());
}
}
#endif
DEFUN_DLD (magick_write, args, ,
"-*- texinfo -*-\n\
@deftypefn {Function File} {} __magick_write__(@var{fname}, @var{fmt}, @var{img})\n\
@deftypefnx {Function File} {} __magick_write__(@var{fname}, @var{fmt}, @var{img}, @var{map})\n\
Write images with ImageMagick++. In general you should not be using this function.\n\
Instead you should use @code{imwrite}.\n\
@seealso{imread}\n\
@end deftypefn")
{
octave_value_list retval;
#ifdef HAVE_MAGICK
int nargin = args.length ();
if (nargin > 2)
{
std::string filename = args(0).string_value ();
if (! error_state)
{
std::string fmt = args(1).string_value ();
if (! error_state)
{
if (nargin > 4)
write_image (filename, fmt, args(2), args(3), args(4));
else if (nargin > 3)
if (args(3).is_real_type ())
write_image (filename, fmt, args(2), args(3));
else
write_image (filename, fmt, args(2), octave_value(), args(3));
else
write_image (filename, fmt, args(2));
}
else
error ("__magick_write__: expecting format as second argument");
}
else
error ("__magick_write__: expecting filename as first argument");
}
else
print_usage ();
#else
error ("__magick_write__: not available in this version of Octave");
#endif
return retval;
}
#ifdef HAVE_MAGICK
template
static octave_value
magick_to_octave_value (const T magick)
{
return octave_value (magick);
}
static octave_value
magick_to_octave_value (const Magick::EndianType magick)
{
switch (magick)
{
case Magick::LSBEndian:
return octave_value ("little-endian");
case Magick::MSBEndian:
return octave_value ("big-endian");
default:
return octave_value ("undefined");
}
}
static octave_value
magick_to_octave_value (const Magick::ResolutionType magick)
{
switch (magick)
{
case Magick::PixelsPerInchResolution:
return octave_value ("pixels per inch");
case Magick::PixelsPerCentimeterResolution:
return octave_value ("pixels per centimeter");
default:
return octave_value ("undefined");
}
}
static octave_value
magick_to_octave_value (const Magick::ImageType magick)
{
switch (magick)
{
case Magick::BilevelType:
case Magick::GrayscaleType:
case Magick::GrayscaleMatteType:
return octave_value ("grayscale");
case Magick::PaletteType:
case Magick::PaletteMatteType:
return octave_value ("indexed");
case Magick::TrueColorType:
case Magick::TrueColorMatteType:
case Magick::ColorSeparationType:
return octave_value ("truecolor");
default:
return octave_value ("undefined");
}
}
// We put this in a try-block because GraphicsMagick will throw
// exceptions if a parameter isn't present in the current image.
#define GET_PARAM(NAME, OUTNAME) \
try \
{ \
st.assign (OUTNAME, magick_to_octave_value (im.NAME ())); \
} \
catch (Magick::Warning& w) \
{ \
}
#endif
DEFUN_DLD (magick_finfo, args, ,
"-*- texinfo -*-\n\
@deftypefn {Loadable File} {} __magick_finfo__(@var{fname})\n\
Read image information with GraphicsMagick++. In general you should\n\
not be using this function. Instead you should use @code{imfinfo}.\n\
@seealso{imfinfo, imread}\n\
@end deftypefn")
{
octave_value_list output;
#ifdef HAVE_MAGICK
if (args.length () < 1 || ! args (0).is_string ())
{
print_usage ();
return output;
}
const std::string filename = args (0).string_value ();
try
{
// Read the file.
Magick::Image im;
im.read (filename);
// Read properties.
Octave_map st;
st.assign ("Filename", filename);
// Annoying CamelCase naming is for Matlab compatibility.
GET_PARAM (fileSize, "FileSize")
GET_PARAM (rows, "Height")
GET_PARAM (columns, "Width")
GET_PARAM (depth, "BitDepth")
GET_PARAM (magick, "Format")
GET_PARAM (format, "LongFormat")
GET_PARAM (xResolution, "XResolution")
GET_PARAM (yResolution, "YResolution")
GET_PARAM (totalColors, "TotalColors")
GET_PARAM (tileName, "TileName")
GET_PARAM (animationDelay, "AnimationDelay")
GET_PARAM (animationIterations, "AnimationIterations")
GET_PARAM (endian, "ByteOrder")
GET_PARAM (gamma, "Gamma")
GET_PARAM (matte, "Matte")
GET_PARAM (modulusDepth, "ModulusDepth")
GET_PARAM (quality, "Quality")
GET_PARAM (quantizeColors, "QuantizeColors")
GET_PARAM (resolutionUnits, "ResolutionUnits")
GET_PARAM (type, "ColorType")
GET_PARAM (view, "View")
output (0) = st;
}
catch (Magick::Warning& w)
{
warning ("Magick++ warning: %s", w.what ());
}
catch (Magick::ErrorCoder& e)
{
warning ("Magick++ coder error: %s", e.what ());
}
catch (Magick::Exception& e)
{
error ("Magick++ exception: %s", e.what ());
return output;
}
#else
error ("imfinfo: not available in this version of Octave");
#endif
return output;
}
#undef GET_PARAM
/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; indent-tabs-mode: nil ***
;;; End: ***
*/