Mercurial > octave-dspies
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 } |