3 Copyright (C) 2002, 2009 Andy Adler
4 Copyright (C) 2008 Thomas L. Scofield
6 This file is part of Octave.
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
30 #include "defun-dld.h"
32 #include "ov-struct.h"
36 #include <GraphicsMagick/Magick++.h>
39 scale_quantum_to_depth (const Magick::Quantum& quantum, unsigned int depth)
41 return (static_cast<unsigned int> (static_cast<double> (quantum)
42 / MaxRGB * ((1 << depth) - 1)));
46 read_indexed_images (std::vector<Magick::Image>& imvec,
47 const Array<int>& frameidx, bool wantalpha)
49 octave_value_list output;
51 int rows = imvec[0].baseRows ();
52 int columns = imvec[0].baseColumns ();
53 int nframes = frameidx.length ();
55 dim_vector idim = dim_vector ();
62 Array<int> idx (dim_vector (4));
64 Magick::ImageType type = imvec[0].type ();
66 unsigned int mapsize = imvec[0].colorMapSize ();
67 unsigned int i = mapsize;
68 unsigned int depth = 0;
84 uint8NDArray im = uint8NDArray (idim);
87 for (int frame = 0; frame < nframes; frame++)
89 imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
91 const Magick::IndexPacket *pix
92 = imvec[frameidx(frame)].getConstIndexes ();
97 for (int y = 0; y < rows; y++)
100 for (int x = 0; x < columns; x++)
103 im(idx) = static_cast<octave_uint8> (pix[i++]);
107 im.chop_trailing_singletons ();
108 output(0) = octave_value (im);
114 uint16NDArray im = uint16NDArray (idim);
117 for (int frame = 0; frame < nframes; frame++)
119 imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
121 const Magick::IndexPacket *pix
122 = imvec[frameidx(frame)].getConstIndexes ();
127 for (int y = 0; y < rows; y++)
130 for (int x = 0; x < columns; x++)
133 im(idx) = static_cast<octave_uint16> (pix[i++]);
137 im.chop_trailing_singletons ();
138 output(0) = octave_value (im);
143 error ("__magic_read__: index depths bigger than 16-bit not supported");
144 return octave_value_list ();
147 Matrix map = Matrix (mapsize, 3);
152 case Magick::PaletteMatteType:
154 warning ("palettematte");
155 Matrix map (mapsize, 3);
156 Matrix alpha (mapsize, 1);
157 for (i = 0; i < mapsize; i++)
160 Magick::ColorRGB c = imvec[0].colorMap (i);
162 map(i,1) = c.green ();
163 map(i,2) = c.blue ();
164 alpha(i,1) = c.alpha ();
169 case Magick::PaletteType:
170 alpha = Matrix (0, 0);
171 for (i = 0; i < mapsize; i++)
173 Magick::ColorRGB c = imvec[0].colorMap (i);
175 map(i,1) = c.green ();
176 map(i,2) = c.blue ();
181 error ("__magick_read__: unsupported indexed image type");
182 return octave_value_list ();
195 read_images (const std::vector<Magick::Image>& imvec,
196 const Array<int>& frameidx, unsigned int depth)
198 octave_value_list retval (3, Matrix ());
202 int rows = imvec[0].baseRows ();
203 int columns = imvec[0].baseColumns ();
204 int nframes = frameidx.length ();
206 dim_vector idim = dim_vector ();
213 Array<int> idx (dim_vector (4));
215 Magick::ImageType type = imvec[0].type ();
219 case Magick::BilevelType:
220 case Magick::GrayscaleType:
222 for (int frame = 0; frame < nframes; frame++)
224 const Magick::PixelPacket *pix
225 = imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
231 for (int y = 0; y < rows; y++)
234 for (int x = 0; x < columns; x++)
237 im(idx) = scale_quantum_to_depth (pix[i++].red, depth);
243 case Magick::GrayscaleMatteType:
246 for (int frame = 0; frame < nframes; frame++)
248 const Magick::PixelPacket *pix
249 = imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
254 for (int y = 0; y < rows; y++)
257 for (int x = 0; x < columns; x++)
261 im(idx) = scale_quantum_to_depth (pix[i].red, depth);
263 im(idx) = scale_quantum_to_depth (pix[i].opacity, depth);
270 case Magick::PaletteType:
271 case Magick::TrueColorType:
274 for (int frame = 0; frame < nframes; frame++)
276 const Magick::PixelPacket *pix
277 = imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
282 for (int y = 0; y < rows; y++)
285 for (int x = 0; x < columns; x++)
289 im(idx) = scale_quantum_to_depth (pix[i].red, depth);
291 im(idx) = scale_quantum_to_depth (pix[i].green, depth);
293 im(idx) = scale_quantum_to_depth (pix[i].blue, depth);
300 case Magick::PaletteMatteType:
301 case Magick::TrueColorMatteType:
302 case Magick::ColorSeparationType:
305 for (int frame = 0; frame < nframes; frame++)
307 const Magick::PixelPacket *pix
308 = imvec[frameidx(frame)].getConstPixels (0, 0, columns, rows);
313 for (int y = 0; y < rows; y++)
316 for (int x = 0; x < columns; x++)
320 im(idx) = scale_quantum_to_depth (pix[i].red, depth);
322 im(idx) = scale_quantum_to_depth (pix[i].green, depth);
324 im(idx) = scale_quantum_to_depth (pix[i].blue, depth);
326 im(idx) = scale_quantum_to_depth (pix[i].opacity, depth);
334 error ("__magick_read__: undefined ImageMagick image type");
338 im.chop_trailing_singletons ();
347 DEFUN_DLD (magick_read, args, nargout,
349 @deftypefn {Function File} {@var{m} =} __magick_read__(@var{fname}, @var{index})\n\
350 @deftypefnx{Function File} {[@var{m}, @var{colormap}] =} __magick_read__(@var{fname}, @var{index})\n\
351 @deftypefnx{Function File} {[@var{m}, @var{colormap}, @var{alpha}] =} __magick_read__(@var{fname}, @var{index})\n\
352 Read images with ImageMagick++. In general you should not be using this function.\n\
353 Instead you should use @code{imread}.\n\
357 octave_value_list output;
361 if (args.length () > 2 || args.length () < 1 || ! args(0).is_string ()
370 if (args.length () == 2 && args(1).is_real_type ())
371 frameidx = args(1).int_vector_value();
374 frameidx = Array<int> (1);
378 std::vector<Magick::Image> imvec;
382 // Read a file into vector of image objects
383 Magick::readImages (&imvec, args(0).string_value ());
385 catch (Magick::Warning& w)
387 warning ("Magick++ warning: %s", w.what ());
389 catch (Magick::ErrorCoder& e)
391 warning ("Magick++ coder error: %s", e.what ());
393 catch (Magick::Exception& e)
395 error ("Magick++ exception: %s", e.what ());
399 for (int i = 0; i < frameidx.length(); i++)
401 frameidx(i) = frameidx(i) - 1;
403 int nframes = imvec.size ();
405 if (frameidx(i) >= nframes || frameidx(i) < 0)
407 error ("__magick_read__: invalid index vector");
412 Magick::ClassType klass = imvec[0].classType ();
414 if (klass == Magick::PseudoClass && nargout > 1)
415 output = read_indexed_images (imvec, frameidx, (nargout == 3));
418 unsigned int depth = imvec[0].modulusDepth ();
427 output = read_images<boolNDArray> (imvec, frameidx, depth);
433 output = read_images<uint8NDArray> (imvec, frameidx, depth) ;
437 output = read_images<uint16NDArray> (imvec, frameidx, depth);
443 error ("__magick_read__: image depths bigger than 16-bit not supported");
448 error ("__magick_read__: not available in this version of Octave");
458 jpg_settings (std::vector<Magick::Image>& imvec,
459 const Octave_map& options,
462 int nframes = static_cast<int>(imvec.size ());
463 bool something_set = 0;
467 Octave_map::const_iterator p;
469 for (p = options.begin (); p != options.end (); p++)
470 if (options.key (p) == "Quality")
473 result = options.contents (p).elem (0);
476 if (found_it && (! result.is_empty ()))
479 if (result.is_real_type ())
481 int qlev = static_cast<int>(result.int_value ());
482 if (qlev < 0 || qlev > 100)
483 warning ("warning: Quality setting invalid--use default of 75");
485 for (int fnum = 0; fnum < nframes; fnum++)
486 imvec[fnum].quality (static_cast<unsigned int>(qlev));
489 warning ("warning: Quality setting invalid--use default of 75");
492 // Other settings go here
495 warning ("__magick_write__ warning: All write parameters ignored.");
499 encode_bool_image (std::vector<Magick::Image>& imvec, const octave_value& img)
501 unsigned int nframes = 1;
502 boolNDArray m = img.bool_array_value ();
504 dim_vector dsizes = m.dims ();
505 if (dsizes.length () == 4)
508 Array<octave_idx_type> idx (dsizes.length ());
510 octave_idx_type rows = m.rows ();
511 octave_idx_type columns = m.columns ();
513 for (unsigned int ii = 0; ii < nframes; ii++)
515 Magick::Image im(Magick::Geometry (columns, rows), "black");
516 im.classType (Magick::DirectClass);
519 for (int y=0; y < columns; y++)
522 for (int x=0; x < rows; x++)
531 im.pixelColor (y, x, "white");
534 imvec.push_back (im);
540 encode_uint_image (std::vector<Magick::Image>& imvec,
541 const octave_value& img,
544 unsigned int bitdepth = 0;
547 if (img.is_uint8_type ())
550 m = img.uint8_array_value ();
552 else if (img.is_uint16_type ())
555 m = img.uint16_array_value ();
558 error ("__magick_write__: invalid image class");
560 dim_vector dsizes = m.dims ();
561 unsigned int nframes = 1;
562 if (dsizes.length () == 4)
564 bool is_color = ((dsizes.length () > 2) && (dsizes(2) > 2));
565 bool has_alpha = (dsizes.length () > 2 && (dsizes(2) == 2 || dsizes(2) == 4));
567 Array<octave_idx_type> idx (dsizes.length ());
568 octave_idx_type rows = m.rows ();
569 octave_idx_type columns = m.columns ();
571 // FIXME -- maybe simply using bit shifting would be better?
572 unsigned int div_factor = pow (2.0, static_cast<int> (bitdepth)) - 1;
574 for (unsigned int ii = 0; ii < nframes; ii++)
576 Magick::Image im(Magick::Geometry (columns, rows), "black");
579 im.classType (Magick::PseudoClass);
581 im.classType (Magick::DirectClass);
586 im.type (Magick::TrueColorMatteType);
588 im.type (Magick::TrueColorType);
591 for (int y=0; y < columns; y++)
594 for (int x=0; x < rows; x++)
601 c.red (static_cast<double>(m(idx)) / div_factor);
603 c.green (static_cast<double>(m(idx)) / div_factor);
605 c.blue (static_cast<double>(m(idx)) / div_factor);
610 c.alpha (static_cast<double>(m(idx)) / div_factor);
612 im.pixelColor (y, x, c);
619 im.type (Magick::GrayscaleMatteType);
621 im.type (Magick::GrayscaleType);
625 for (int y=0; y < columns; y++)
628 for (int x=0; x < rows; x++)
639 c.alpha (static_cast<double>(m(idx)) / div_factor);
643 c.shade (static_cast<double>(m(idx)) / div_factor);
644 im.pixelColor (y, x, c);
648 imvec.push_back (im);
653 encode_map (std::vector<Magick::Image>& imvec, const NDArray& cmap)
655 unsigned int mapsize = cmap.dim1 ();
656 int nframes = static_cast<int>(imvec.size ());
658 for (int fnum = 0; fnum < nframes; fnum++)
660 imvec[fnum].colorMapSize (mapsize);
661 imvec[fnum].type (Magick::PaletteType);
664 for (unsigned int ii = 0; ii < mapsize; ii++)
666 Magick::ColorRGB c (cmap(ii,0), cmap(ii,1), cmap(ii,2));
668 // FIXME -- is this case needed?
669 if (cmap.dim2 () == 4)
670 c.alpha (cmap(ii,3));
674 for_each (imvec.begin (), imvec.end (),
675 Magick::colorMapImage (ii, c));
677 catch (Magick::Warning& w)
679 warning ("Magick++ warning: %s", w.what ());
681 catch (Magick::ErrorCoder& e)
683 warning ("Magick++ coder error: %s", e.what ());
685 catch (Magick::Exception& e)
687 error ("Magick++ exception: %s", e.what ());
693 write_image (const std::string& filename, const std::string& fmt,
694 const octave_value& img,
695 const octave_value& map = octave_value (),
696 const octave_value& params = octave_value ())
698 std::vector<Magick::Image> imvec;
700 bool has_map = map.is_defined ();
704 error ("__magick_write__: direct saving of indexed images not currently supported; use ind2rgb and save converted image");
708 if (img.is_bool_type ())
709 encode_bool_image (imvec, img);
710 else if (img.is_uint8_type ())
711 encode_uint_image<uint8NDArray> (imvec, img, has_map);
712 else if (img.is_uint16_type ())
713 encode_uint_image<uint16NDArray> (imvec, img, has_map);
715 error ("__magick_write__: image type not supported");
717 if (! error_state && has_map)
719 NDArray cmap = map.array_value ();
722 encode_map (imvec, cmap);
725 if (! error_state && params.is_defined ())
727 Octave_map options = params.map_value ();
729 // Insert calls here to handle parameters for various image formats
730 if (fmt == "jpg" || fmt == "jpeg")
731 jpg_settings (imvec, options, has_map);
733 warning ("warning: your parameter(s) currently not supported");
738 Magick::writeImages (imvec.begin (), imvec.end (), filename);
740 catch (Magick::Warning& w)
742 warning ("Magick++ warning: %s", w.what ());
744 catch (Magick::ErrorCoder& e)
746 warning ("Magick++ coder error: %s", e.what ());
748 catch (Magick::Exception& e)
750 error ("Magick++ exception: %s", e.what ());
756 DEFUN_DLD (magick_write, args, ,
758 @deftypefn {Function File} {} __magick_write__(@var{fname}, @var{fmt}, @var{img})\n\
759 @deftypefnx {Function File} {} __magick_write__(@var{fname}, @var{fmt}, @var{img}, @var{map})\n\
760 Write images with ImageMagick++. In general you should not be using this function.\n\
761 Instead you should use @code{imwrite}.\n\
765 octave_value_list retval;
768 int nargin = args.length ();
772 std::string filename = args(0).string_value ();
776 std::string fmt = args(1).string_value ();
781 write_image (filename, fmt, args(2), args(3), args(4));
783 if (args(3).is_real_type ())
784 write_image (filename, fmt, args(2), args(3));
786 write_image (filename, fmt, args(2), octave_value(), args(3));
788 write_image (filename, fmt, args(2));
791 error ("__magick_write__: expecting format as second argument");
794 error ("__magick_write__: expecting filename as first argument");
800 error ("__magick_write__: not available in this version of Octave");
811 magick_to_octave_value (const T magick)
813 return octave_value (magick);
817 magick_to_octave_value (const Magick::EndianType magick)
821 case Magick::LSBEndian:
822 return octave_value ("little-endian");
824 case Magick::MSBEndian:
825 return octave_value ("big-endian");
828 return octave_value ("undefined");
833 magick_to_octave_value (const Magick::ResolutionType magick)
837 case Magick::PixelsPerInchResolution:
838 return octave_value ("pixels per inch");
840 case Magick::PixelsPerCentimeterResolution:
841 return octave_value ("pixels per centimeter");
844 return octave_value ("undefined");
849 magick_to_octave_value (const Magick::ImageType magick)
853 case Magick::BilevelType:
854 case Magick::GrayscaleType:
855 case Magick::GrayscaleMatteType:
856 return octave_value ("grayscale");
858 case Magick::PaletteType:
859 case Magick::PaletteMatteType:
860 return octave_value ("indexed");
862 case Magick::TrueColorType:
863 case Magick::TrueColorMatteType:
864 case Magick::ColorSeparationType:
865 return octave_value ("truecolor");
868 return octave_value ("undefined");
872 // We put this in a try-block because GraphicsMagick will throw
873 // exceptions if a parameter isn't present in the current image.
874 #define GET_PARAM(NAME, OUTNAME) \
877 st.assign (OUTNAME, magick_to_octave_value (im.NAME ())); \
879 catch (Magick::Warning& w) \
885 DEFUN_DLD (magick_finfo, args, ,
887 @deftypefn {Loadable File} {} __magick_finfo__(@var{fname})\n\
888 Read image information with GraphicsMagick++. In general you should\n\
889 not be using this function. Instead you should use @code{imfinfo}.\n\
890 @seealso{imfinfo, imread}\n\
893 octave_value_list output;
897 if (args.length () < 1 || ! args (0).is_string ())
903 const std::string filename = args (0).string_value ();
913 st.assign ("Filename", filename);
915 // Annoying CamelCase naming is for Matlab compatibility.
916 GET_PARAM (fileSize, "FileSize")
917 GET_PARAM (rows, "Height")
918 GET_PARAM (columns, "Width")
919 GET_PARAM (depth, "BitDepth")
920 GET_PARAM (magick, "Format")
921 GET_PARAM (format, "LongFormat")
922 GET_PARAM (xResolution, "XResolution")
923 GET_PARAM (yResolution, "YResolution")
924 GET_PARAM (totalColors, "TotalColors")
925 GET_PARAM (tileName, "TileName")
926 GET_PARAM (animationDelay, "AnimationDelay")
927 GET_PARAM (animationIterations, "AnimationIterations")
928 GET_PARAM (endian, "ByteOrder")
929 GET_PARAM (gamma, "Gamma")
930 GET_PARAM (matte, "Matte")
931 GET_PARAM (modulusDepth, "ModulusDepth")
932 GET_PARAM (quality, "Quality")
933 GET_PARAM (quantizeColors, "QuantizeColors")
934 GET_PARAM (resolutionUnits, "ResolutionUnits")
935 GET_PARAM (type, "ColorType")
936 GET_PARAM (view, "View")
940 catch (Magick::Warning& w)
942 warning ("Magick++ warning: %s", w.what ());
944 catch (Magick::ErrorCoder& e)
946 warning ("Magick++ coder error: %s", e.what ());
948 catch (Magick::Exception& e)
950 error ("Magick++ exception: %s", e.what ());
956 error ("imfinfo: not available in this version of Octave");
967 ;;; Local Variables: ***
969 ;;; indent-tabs-mode: nil ***