comparison libinterp/dldfcn/__magick_read__.cc @ 17226:46805642048f

Implement writing of indexed images. * __magick_read__.cc (encode_indexed_images): new function to create a Magick::Image object from an indexed image and colormap. (encode_uint_image): remove section about indexed image which was unfinished and is now completely done by a separate function. (encode_map): remove commented function for dealing with colormap which was never finished. (__magick_write__): implement writing of indexed images (integers only); refactor the actual writing of the file and dealing of the WriteMode option to make flow simpler. * private/__imwrite__.m: remove hack with ind2rgb() since writing of indexed images is now implemented; conversion of floating point indexed images to integer since it looks ugly in C++; also validate size 4 in the 3rd dimension of an image for CMYK images.
author Carnë Draug <carandraug@octave.org>
date Thu, 08 Aug 2013 17:41:50 +0100
parents 73a3c1580974
children 2f1729cae08f
comparison
equal deleted inserted replaced
17225:06824c3b1ff3 17226:46805642048f
306 type = Magick::ColorSeparationType; 306 type = Magick::ColorSeparationType;
307 break; 307 break;
308 } 308 }
309 default: 309 default:
310 { 310 {
311 // do nothing, other than silencing warnings about enumeration 311 // Do nothing other than silencing warnings about enumeration
312 // values not being handled in switch. 312 // values not being handled in switch.
313 } 313 }
314 } 314 }
315 } 315 }
316 316
536 536
537 retval(0) = img; 537 retval(0) = img;
538 return retval; 538 return retval;
539 } 539 }
540 540
541
542 void static 541 void static
543 read_file (const std::string filename, std::vector<Magick::Image>& imvec) 542 read_file (const std::string filename, std::vector<Magick::Image>& imvec)
544 { 543 {
545 try 544 try
546 { 545 {
561 { 560 {
562 error ("Magick++ exception: %s", e.what ()); 561 error ("Magick++ exception: %s", e.what ());
563 error_state = 1; 562 error_state = 1;
564 } 563 }
565 } 564 }
566
567 565
568 static void 566 static void
569 maybe_initialize_magick (void) 567 maybe_initialize_magick (void)
570 { 568 {
571 static bool initialized = false; 569 static bool initialized = false;
746 %!assert (1) 744 %!assert (1)
747 */ 745 */
748 746
749 #ifdef HAVE_MAGICK 747 #ifdef HAVE_MAGICK
750 748
749 template <class T>
750 static void
751 encode_indexed_images (std::vector<Magick::Image>& imvec,
752 const T& img,
753 const Matrix cmap)
754 {
755 typedef typename T::element_type P;
756 const octave_idx_type nFrames = img.ndims () < 4 ? 1 : img.dims ()(3);
757 const octave_idx_type nRows = img.rows ();
758 const octave_idx_type nCols = img.columns ();
759 const octave_idx_type cmap_size = cmap.rows ();
760 const octave_idx_type bitdepth =
761 sizeof (P) * std::numeric_limits<unsigned char>::digits;
762
763 // There is no colormap object, we need to build a new one for each frame,
764 // even if it's always the same. We can least get a vector for the Colors.
765 std::vector<Magick::ColorRGB> colormap;
766 {
767 const double* cmap_fvec = cmap.fortran_vec ();
768 const octave_idx_type G_offset = cmap_size;
769 const octave_idx_type B_offset = cmap_size * 2;
770 for (octave_idx_type map_idx = 0; map_idx < cmap_size; map_idx++)
771 {
772 colormap.push_back (Magick::ColorRGB (cmap_fvec[map_idx],
773 cmap_fvec[map_idx + G_offset],
774 cmap_fvec[map_idx + B_offset]));
775 }
776 }
777
778 for (octave_idx_type frame = 0; frame < nFrames; frame++)
779 {
780 Magick::Image m_img (Magick::Geometry (nCols, nRows), "black");
781
782 // Ensure that there are no other references to this image.
783 m_img.modifyImage ();
784
785 m_img.classType (Magick::PseudoClass);
786 m_img.type (Magick::PaletteType);
787 // FIXME: for some reason, setting bitdepth doesn't seem to work for
788 // indexed images.
789 m_img.depth (bitdepth);
790
791 // Insert colormap.
792 m_img.colorMapSize (cmap_size);
793 for (octave_idx_type map_idx = 0; map_idx < cmap_size; map_idx++)
794 {
795 m_img.colorMap (map_idx, colormap[map_idx]);
796 }
797 // Why are we also setting the pixel values instead of only the
798 // index values? We don't know if a file format supports indexed
799 // images. If we only set the indexes and then try to save the
800 // image as JPEG for example, the indexed values get discarded,
801 // there is no conversion from the indexes, it's the initial values
802 // that get used. An alternative would be to only set the pixel
803 // values (no indexes), then set the image as PseudoClass and GM
804 // would create a colormap for us. However, we wouldn't have control
805 // over the order of that colormap. And that's why we set both.
806 Magick::PixelPacket* pix = m_img.getPixels (0, 0, nCols, nRows);
807 Magick::IndexPacket* ind = m_img.getIndexes ();
808 const P* img_fvec = img.fortran_vec ();
809
810 octave_idx_type GM_idx = 0;
811 for (octave_idx_type column = 0; column < nCols; column++)
812 {
813 for (octave_idx_type row = 0; row < nRows; row++)
814 {
815 ind[GM_idx] = double (*img_fvec);
816 pix[GM_idx] = m_img.colorMap (double (*img_fvec));
817 img_fvec++;
818 GM_idx += nCols;
819 }
820 GM_idx -= nCols * nRows - 1;
821 }
822
823 // Save changes to underlying image.
824 m_img.syncPixels ();
825 imvec.push_back (m_img);
826 }
827 }
828
751 static void 829 static void
752 encode_bool_image (std::vector<Magick::Image>& imvec, const octave_value& img) 830 encode_bool_image (std::vector<Magick::Image>& imvec, const octave_value& img)
753 { 831 {
754 unsigned int nframes = 1; 832 unsigned int nframes = 1;
755 boolNDArray m = img.bool_array_value (); 833 boolNDArray m = img.bool_array_value ();
797 } 875 }
798 876
799 template <class T> 877 template <class T>
800 static void 878 static void
801 encode_uint_image (std::vector<Magick::Image>& imvec, 879 encode_uint_image (std::vector<Magick::Image>& imvec,
802 const octave_value& img, 880 const octave_value& img)
803 const bool has_map)
804 { 881 {
805 unsigned int bitdepth = 0; 882 unsigned int bitdepth = 0;
806 T m; 883 T m;
807 884
808 if (img.is_uint8_type ()) 885 if (img.is_uint8_type ())
836 { 913 {
837 Magick::Image im (Magick::Geometry (columns, rows), "black"); 914 Magick::Image im (Magick::Geometry (columns, rows), "black");
838 915
839 im.depth (bitdepth); 916 im.depth (bitdepth);
840 917
841 if (has_map) 918 im.classType (Magick::DirectClass);
842 im.classType (Magick::PseudoClass);
843 else
844 im.classType (Magick::DirectClass);
845 919
846 if (is_color) 920 if (is_color)
847 { 921 {
848 if (has_alpha) 922 if (has_alpha)
849 im.type (Magick::TrueColorMatteType); 923 im.type (Magick::TrueColorMatteType);
924 } 998 }
925 999
926 imvec.push_back (im); 1000 imvec.push_back (im);
927 } 1001 }
928 } 1002 }
929
930 // FIXME: this will be needed to write indexed images
931 //static void
932 //encode_map (std::vector<Magick::Image>& imvec, const NDArray& cmap)
933 //{
934 // unsigned int mapsize = cmap.dim1 ();
935
936 // for (size_t fnum = 0; fnum < imvec.size (); fnum++)
937 // {
938 // imvec[fnum].colorMapSize (mapsize);
939 // imvec[fnum].type (Magick::PaletteType);
940 // }
941
942 // for (unsigned int ii = 0; ii < mapsize; ii++)
943 // {
944 // Magick::ColorRGB c (cmap(ii,0), cmap(ii,1), cmap(ii,2));
945
946 // // FIXME -- is this case needed?
947 // if (cmap.dim2 () == 4)
948 // c.alpha (cmap(ii,3));
949
950 // try
951 // {
952 // for_each (imvec.begin (), imvec.end (),
953 // Magick::colorMapImage (ii, c));
954 // }
955 // catch (Magick::Warning& w)
956 // {
957 // warning ("Magick++ warning: %s", w.what ());
958 // }
959 // catch (Magick::ErrorCoder& e)
960 // {
961 // warning ("Magick++ coder error: %s", e.what ());
962 // }
963 // catch (Magick::Exception& e)
964 // {
965 // error ("Magick++ exception: %s", e.what ());
966 // }
967 // }
968 //}
969 1003
970 void static 1004 void static
971 write_file (const std::string filename, 1005 write_file (const std::string filename,
972 const std::string ext, 1006 const std::string ext,
973 std::vector<Magick::Image>& imvec) 1007 std::vector<Magick::Image>& imvec)
1018 return retval; 1052 return retval;
1019 } 1053 }
1020 const std::string filename = args(0).string_value (); 1054 const std::string filename = args(0).string_value ();
1021 const std::string ext = args(1).string_value (); 1055 const std::string ext = args(1).string_value ();
1022 1056
1023 const octave_map options = args(4).map_value (); 1057 const octave_scalar_map options = args(4).scalar_map_value ();
1024 if (error_state) 1058 if (error_state)
1025 { 1059 {
1026 error ("__magick_write__: OPTIONS must be a struct"); 1060 error ("__magick_write__: OPTIONS must be a struct");
1061 return retval;
1027 } 1062 }
1028 1063
1029 const octave_value img = args(2); 1064 const octave_value img = args(2);
1030 const Matrix cmap = args(3).matrix_value (); 1065 const Matrix cmap = args(3).matrix_value ();
1031 if (error_state) 1066 if (error_state)
1032 { 1067 {
1033 error ("__magick_write__: invalid IMG or MAP"); 1068 error ("__magick_write__: invalid IMG or MAP");
1034 } 1069 return retval;
1035 const bool is_indexed = ! cmap.is_empty (); 1070 }
1036 1071 // Create vector for the images to be written.
1037 // Create vector with the images to write
1038 std::vector<Magick::Image> imvec; 1072 std::vector<Magick::Image> imvec;
1039 if (img.is_bool_type ()) 1073
1040 { 1074 if (cmap.is_empty ())
1041 encode_bool_image (imvec, img); 1075 {
1042 } 1076 if (img.is_bool_type ())
1043 else if (img.is_uint8_type ()) 1077 {
1044 { 1078 encode_bool_image (imvec, img);
1045 encode_uint_image<uint8NDArray> (imvec, img, is_indexed); 1079 }
1046 } 1080 else if (img.is_uint8_type ())
1047 else if (img.is_uint16_type ()) 1081 {
1048 { 1082 encode_uint_image<uint8NDArray> (imvec, img);
1049 encode_uint_image<uint16NDArray> (imvec, img, is_indexed); 1083 }
1084 else if (img.is_uint16_type ())
1085 {
1086 encode_uint_image<uint16NDArray> (imvec, img);
1087 }
1088 else
1089 {
1090 error ("__magick_write__: image type not supported");
1091 return retval;
1092 }
1050 } 1093 }
1051 else 1094 else
1052 { 1095 {
1053 error ("__magick_write__: image type not supported"); 1096 // We should not get floating point indexed images here because we
1054 return retval; 1097 // converted them in __imwrite__.m. We should probably do it here
1055 } 1098 // but it would look much messier.
1056 const int nframes = imvec.size (); 1099 if (img.is_uint8_type ())
1057 1100 {
1058 // Add colormap to image 1101 encode_indexed_images<uint8NDArray> (imvec, img.uint8_array_value (), cmap);
1059 if (is_indexed) 1102 }
1060 { 1103 else if (img.is_uint16_type ())
1061 // FIXME: this should be implemented. At the moment, imwrite is doing the 1104 {
1062 // conversion in case of indexed images. 1105 encode_indexed_images<uint16NDArray> (imvec, img.uint16_array_value (), cmap);
1063 error ("__magick_write__: direct saving of indexed images not currently supported; use ind2rgb and save converted image"); 1106 }
1064 // encode_map (imvec, cmap); 1107 else
1065 return retval; 1108 {
1066 } 1109 error ("__magick_write__: indexed image must be uint8, uint16 or float.");
1110 return retval;
1111 }
1112 }
1113
1114 const octave_idx_type nFrames = imvec.size ();
1067 1115
1068 // Set quality. 1116 // Set quality.
1069 // FIXME What happens when we try to set with formats that do not support it? 1117 // FIXME What happens when we try to set with formats that do not support it?
1070 const unsigned int quality = options.getfield ("quality")(0).int_value (); 1118 const octave_idx_type quality = options.getfield ("quality").int_value ();
1071 for (int i = 0; i < nframes; i++) 1119 for (octave_idx_type i = 0; i < nFrames; i++)
1072 { 1120 {
1073 imvec[i].quality (quality); 1121 imvec[i].quality (quality);
1074 } 1122 }
1075 1123
1076 // Finally, save the file. 1124 // If writemode is set to append, read the image and append to it. Even
1077 // If writemode is set to append, read the image first, append to it, 1125 // if set to append, make sure that something was read at all.
1078 // and then save it. But even if set to append, make sure anything was 1126 const std::string writemode = options.getfield ("writemode").string_value ();
1079 // read at all.
1080 const std::string writemode = options.getfield ("writemode")(0).string_value ();
1081 std::vector<Magick::Image> ini_imvec;
1082 if (writemode == "append" && file_stat (filename).exists ()) 1127 if (writemode == "append" && file_stat (filename).exists ())
1083 { 1128 {
1129 std::vector<Magick::Image> ini_imvec;
1084 read_file (filename, ini_imvec); 1130 read_file (filename, ini_imvec);
1085 if (error_state) 1131 if (error_state)
1086 { 1132 {
1087 return retval; 1133 return retval;
1088 } 1134 }
1089 } 1135 if (ini_imvec.size () > 0)
1090 1136 {
1091 if (ini_imvec.size () > 0) 1137 ini_imvec.insert (ini_imvec.end (), imvec.begin (), imvec.end ());
1092 { 1138 ini_imvec.swap (imvec);
1093 ini_imvec.insert (ini_imvec.end (), imvec.begin (), imvec.end ()); 1139 }
1094 write_file (filename, ext, ini_imvec); 1140 }
1095 if (error_state) 1141
1096 { 1142 // Finally, save the damn thing.
1097 return retval; 1143 write_file (filename, ext, imvec);
1098 } 1144 if (error_state)
1099 } 1145 {
1100 else 1146 return retval;
1101 {
1102 write_file (filename, ext, imvec);
1103 if (error_state)
1104 {
1105 return retval;
1106 }
1107 } 1147 }
1108 1148
1109 #endif 1149 #endif
1110 return retval; 1150 return retval;
1111 } 1151 }