Mercurial > octave-libtiff
comparison libinterp/corefcn/__tiff__.cc @ 31193:c142c153034c
Tiff: implemented imwrite handler that uses the Tiff interface
* __tiff__.cc (F__tiff_imwrite__): implemented internal function to
act as imwrite handler for tiff images but uses the new Tiff interface.
author | magedrifaat <magedrifaat@gmail.com> |
---|---|
date | Sat, 27 Aug 2022 23:06:54 +0200 |
parents | 6a2bb6f4e41e |
children | 0cdb7f35641e |
comparison
equal
deleted
inserted
replaced
31192:6a2bb6f4e41e | 31193:c142c153034c |
---|---|
11 #include "error.h" | 11 #include "error.h" |
12 | 12 |
13 #include "errwarn.h" | 13 #include "errwarn.h" |
14 | 14 |
15 #include "fcntl-wrappers.h" | 15 #include "fcntl-wrappers.h" |
16 #include "file-ops.h" | |
16 | 17 |
17 #if defined (HAVE_TIFF) | 18 #if defined (HAVE_TIFF) |
18 # include <tiffio.h> | 19 # include <tiffio.h> |
19 bool handlers_set = true; | 20 bool handlers_set = true; |
20 #endif | 21 #endif |
122 | 123 |
123 bool | 124 bool |
124 is_numeric_scalar (octave_value ov) | 125 is_numeric_scalar (octave_value ov) |
125 { | 126 { |
126 return ov.isnumeric () && ov.isreal () && ov.is_scalar_type (); | 127 return ov.isnumeric () && ov.isreal () && ov.is_scalar_type (); |
128 } | |
129 | |
130 bool | |
131 is_colormap (octave_value ov) | |
132 { | |
133 return ov.isnumeric () && ov.isreal () && ov.isfloat () | |
134 && ov.ndims () == 2 && ov.columns () == 3; | |
127 } | 135 } |
128 | 136 |
129 // A map of tag names supported by matlab, there are some differences | 137 // A map of tag names supported by matlab, there are some differences |
130 // than LibTIFF's names (e.g. Photometric vs PhotometricInerpretation) | 138 // than LibTIFF's names (e.g. Photometric vs PhotometricInerpretation) |
131 static const std::map<std::string, ttag_t> tag_name_map = { | 139 static const std::map<std::string, ttag_t> tag_name_map = { |
2739 uint8NDArray img (img_dims); | 2747 uint8NDArray img (img_dims); |
2740 uint32_t *img_ptr = reinterpret_cast <uint32_t *> (img.fortran_vec ()); | 2748 uint32_t *img_ptr = reinterpret_cast <uint32_t *> (img.fortran_vec ()); |
2741 | 2749 |
2742 // The TIFFRGBAImageGet interface is used instead of TIFFReadRGBAImage | 2750 // The TIFFRGBAImageGet interface is used instead of TIFFReadRGBAImage |
2743 // to have control on the requested orientation of the image | 2751 // to have control on the requested orientation of the image |
2752 // Matlab R2022a handles the orientation for each individual strip | |
2753 // or tile on its own, this results in a distorted image which is | |
2754 // not useful, and is probably a bug. So this deviated from matlab | |
2755 // by applying the orientation to the entire image to produce more | |
2756 // useful results. | |
2744 TIFFRGBAImage img_config; | 2757 TIFFRGBAImage img_config; |
2745 char emsg[1024]; | 2758 char emsg[1024]; |
2746 if (! TIFFRGBAImageOK (tif, emsg) | 2759 if (! TIFFRGBAImageOK (tif, emsg) |
2747 || ! TIFFRGBAImageBegin (&img_config, tif, 0, emsg)) | 2760 || ! TIFFRGBAImageBegin (&img_config, tif, 0, emsg)) |
2748 error ("Failed to read image"); | 2761 error ("Failed to read image"); |
3597 found_index = true; | 3610 found_index = true; |
3598 octave_value val = args(arg_idx + 1); | 3611 octave_value val = args(arg_idx + 1); |
3599 if (is_numeric_scalar (val)) | 3612 if (is_numeric_scalar (val)) |
3600 page = val.uint16_scalar_value (); | 3613 page = val.uint16_scalar_value (); |
3601 else | 3614 else |
3602 error ("imread: %s must be a scalar", param_cstr); | 3615 error ("imread: %s must be a numeric scalar", param_cstr); |
3603 } | 3616 } |
3604 } | 3617 } |
3605 | 3618 |
3606 // validate frame numbers | 3619 // validate frame numbers |
3607 if (page < 1 || page > dir_count) | 3620 if (page < 1 || page > dir_count) |
3734 return retval; | 3747 return retval; |
3735 #else | 3748 #else |
3736 err_disabled_feature ("imread", "Tiff"); | 3749 err_disabled_feature ("imread", "Tiff"); |
3737 #endif | 3750 #endif |
3738 } | 3751 } |
3752 | |
3753 DEFUN (__tiff_imwrite__, args, , | |
3754 "Handler for imwrite that uses Tiff interface") | |
3755 { | |
3756 #if defined (HAVE_TIFF) | |
3757 int nargin = args.length (); | |
3758 | |
3759 if (nargin < 2) | |
3760 error ("imwrite: Wrong number of arguments"); | |
3761 | |
3762 if (! (args(0).isnumeric () || args(0).is_bool_matrix () | |
3763 || args(0).is_bool_scalar ())) | |
3764 error ("imwrite: Expected image matrix as first argument"); | |
3765 | |
3766 uint16_t offset = 1; | |
3767 std::string filename; | |
3768 octave_value cmap = octave_value (NDArray ()); | |
3769 if (args(1).is_string ()) | |
3770 { | |
3771 filename = args(1).string_value (); | |
3772 offset++; | |
3773 } | |
3774 else if (nargin > 2 && is_colormap (args(1)) && args(2).is_string ()) | |
3775 { | |
3776 filename = args(2).string_value (); | |
3777 cmap = args(1); | |
3778 offset += 2; | |
3779 } | |
3780 else | |
3781 error ("imwrite: no FILENAME specified"); | |
3782 | |
3783 filename = sys::file_ops::tilde_expand (filename); | |
3784 | |
3785 if (nargin > offset && (nargin - offset) % 2 != 0 | |
3786 && args(offset).is_string ()) | |
3787 offset++; | |
3788 | |
3789 if ((nargin - offset) % 2 != 0) | |
3790 error ("imwrite: no pair for all arguments (odd number left)"); | |
3791 | |
3792 bool append = false; | |
3793 uint16_t compression = COMPRESSION_NONE; | |
3794 uint32_t rows_per_strip = UINT32_MAX; | |
3795 float x_res = 72, y_res = 72; | |
3796 std::string description = ""; | |
3797 | |
3798 for (uint16_t arg_idx = offset; arg_idx < nargin; arg_idx+=2) | |
3799 { | |
3800 if (! args(arg_idx).is_string ()) | |
3801 error ("imwrite: PARAM in PARAM/VALUE pair must be string"); | |
3802 | |
3803 const char *param_cstr = args(arg_idx).string_value ().c_str (); | |
3804 if (strcasecmp (param_cstr, "colorspace") == 0) | |
3805 { | |
3806 // Matlab specifies three colorspaces: RGB, CIELAB and ICCLAB. | |
3807 // Of the three, only RGB is currently supported so this tag | |
3808 // can be ignored. | |
3809 } | |
3810 else if (strcasecmp (param_cstr, "compression") == 0) | |
3811 { | |
3812 if (! args(arg_idx + 1).is_string ()) | |
3813 error ("imwrite: value for %s option must be a string", | |
3814 param_cstr); | |
3815 std::string comp = args(arg_idx + 1).string_value (); | |
3816 if (comp == "packbits") | |
3817 compression = COMPRESSION_PACKBITS; | |
3818 else if (comp == "lzw") | |
3819 compression = COMPRESSION_LZW; | |
3820 else if (comp == "deflate") | |
3821 // FIXME: check matlab if this is adobe deflate | |
3822 compression = COMPRESSION_DEFLATE; | |
3823 else if (comp == "jpeg") | |
3824 compression = COMPRESSION_JPEG; | |
3825 else if (comp == "ccitt") | |
3826 // FIXME: check matlab which one this is | |
3827 compression = COMPRESSION_CCITT_T4; | |
3828 else if (comp == "fax3") | |
3829 compression = COMPRESSION_CCITTFAX3; | |
3830 else if (comp == "fax4") | |
3831 compression = COMPRESSION_CCITTFAX4; | |
3832 else | |
3833 error ("imwrite: invalid compression '%s'", comp.c_str ()); | |
3834 } | |
3835 else if (strcasecmp (param_cstr, "Description") == 0) | |
3836 { | |
3837 if (! args(arg_idx + 1).is_string ()) | |
3838 error ("imwrite: value for %s option must be a string", | |
3839 param_cstr); | |
3840 description = args(arg_idx + 1).string_value (); | |
3841 } | |
3842 else if (strcasecmp (param_cstr, "resolution") == 0) | |
3843 { | |
3844 if (! args(arg_idx + 1).isnumeric ()) | |
3845 error ("imwrite: value for %s option must be numeric", | |
3846 param_cstr); | |
3847 NDArray res = args(arg_idx + 1).array_value (); | |
3848 if (res.numel () == 1) | |
3849 { | |
3850 x_res = res(0); | |
3851 y_res = res(0); | |
3852 } | |
3853 else if (res.numel () == 2) | |
3854 { | |
3855 x_res = res(0); | |
3856 y_res = res(1); | |
3857 } | |
3858 else | |
3859 error ("imwrite: value for %s option must be either a scalar value or a two-element vector", | |
3860 param_cstr); | |
3861 } | |
3862 else if (strcasecmp (param_cstr, "RowsPerStrip") == 0) | |
3863 { | |
3864 if (! args(arg_idx + 1).isnumeric () | |
3865 || ! args(arg_idx + 1).is_scalar_type ()) | |
3866 error ("imwrite: value for %s option must be a scalar value", | |
3867 param_cstr); | |
3868 rows_per_strip = args(arg_idx + 1).uint32_scalar_value (); | |
3869 } | |
3870 else if (strcasecmp (param_cstr, "WriteMode") == 0) | |
3871 { | |
3872 if (! args(arg_idx + 1).is_string ()) | |
3873 error ("imwrite: value for %s option must be a string", | |
3874 param_cstr); | |
3875 std::string wmode = args(arg_idx + 1).string_value (); | |
3876 if (wmode == "overwrite") | |
3877 append = false; | |
3878 else if (wmode == "append") | |
3879 append = true; | |
3880 else | |
3881 error ("imwrite: value for %s option must be either 'overwrite' or 'append'", | |
3882 param_cstr); | |
3883 } | |
3884 else | |
3885 error ("imread: invalid PARAMETER '%s'", param_cstr); | |
3886 } | |
3887 | |
3888 // FIXME: does matlab error if appending to non existant file? | |
3889 if (compression == COMPRESSION_JPEG | |
3890 && (rows_per_strip == UINT32_MAX || rows_per_strip % 8 != 0)) | |
3891 error ("imwrite: RowsPerStrip option must be specified for jpeg compression and must be divisible by 8"); | |
3892 | |
3893 if (! (args(0).is_bool_matrix () || args(0).is_bool_scalar ()) | |
3894 && (compression == COMPRESSION_CCITT_T4 | |
3895 || compression == COMPRESSION_CCITTFAX3 | |
3896 || compression == COMPRESSION_CCITTFAX4)) | |
3897 error ("imwrite: the specified compression scheme is for binary images only"); | |
3898 | |
3899 dim_vector img_dims = args(0).dims (); | |
3900 if (args(0).ndims () != 2 && args(0).ndims () != 3) | |
3901 error ("imwrite: expected 2-dimensional or 3-dimensional image matrix"); | |
3902 | |
3903 if (args(0).ndims () > 2 && img_dims(2) > 1 && cmap.numel () > 0) | |
3904 error ("imwrite: palette images must be 1 channel only"); | |
3905 | |
3906 TIFF *tif; | |
3907 if (append) | |
3908 tif = TIFFOpen (filename.c_str (), "a"); | |
3909 else | |
3910 tif = TIFFOpen (filename.c_str (), "w"); | |
3911 | |
3912 if (! tif) | |
3913 error ("Failed to open file %s", filename.c_str ()); | |
3914 | |
3915 // A simple way to make sure the file will be closed when the function | |
3916 // returns or when an error occurs as the destructor will always be called | |
3917 octave_tiff_handle tiff_handle (tif); | |
3918 | |
3919 // Set all necessary tags | |
3920 if (! TIFFSetField (tif, TIFFTAG_IMAGELENGTH, | |
3921 static_cast<uint32_t>(img_dims(0))) | |
3922 || ! TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, | |
3923 static_cast<uint32_t>(img_dims(1)))) | |
3924 error ("Failed to set image dimensions"); | |
3925 | |
3926 if (img_dims.ndims () > 2) | |
3927 if (! TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, img_dims(2))) | |
3928 error ("Failed to set the number of samples"); | |
3929 | |
3930 if (! TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) | |
3931 error ("Failed to set the planar configuration"); | |
3932 | |
3933 if (! TIFFSetField (tif, TIFFTAG_COMPRESSION, compression)) | |
3934 error ("Failed to set the compressoin tag"); | |
3935 | |
3936 if (! TIFFSetField (tif, TIFFTAG_XRESOLUTION, x_res) | |
3937 || ! TIFFSetField (tif, TIFFTAG_YRESOLUTION, y_res)) | |
3938 error ("Failed to set resolution tag"); | |
3939 | |
3940 if (! TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, description.c_str ())) | |
3941 error ("Failed to set description tag"); | |
3942 | |
3943 if (rows_per_strip == UINT32_MAX) | |
3944 rows_per_strip = TIFFDefaultStripSize (tif, 0); | |
3945 | |
3946 if (! TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rows_per_strip)) | |
3947 error ("Failed to set the RowsPerStrip tag"); | |
3948 | |
3949 uint16_t bits_per_sample; | |
3950 if (args(0).is_bool_matrix () || args(0).is_bool_scalar ()) | |
3951 bits_per_sample = 1; | |
3952 else if (args(0).is_uint8_type ()) | |
3953 bits_per_sample = 8; | |
3954 else if (args(0).is_uint16_type ()) | |
3955 bits_per_sample = 16; | |
3956 else if (args(0).is_uint32_type () || args(0).is_single_type ()) | |
3957 bits_per_sample = 32; | |
3958 else if (args(0).is_double_type ()) | |
3959 bits_per_sample = 64; | |
3960 else | |
3961 error ("imwrite: unsupported image data type"); | |
3962 | |
3963 if (! TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample)) | |
3964 error ("Failed to set BitsPerSample tag"); | |
3965 | |
3966 uint16_t photometric = PHOTOMETRIC_MINISBLACK; | |
3967 if (cmap.numel () > 0) | |
3968 { | |
3969 photometric = PHOTOMETRIC_PALETTE; | |
3970 set_field_data (tif, TIFFFieldWithTag (tif, TIFFTAG_COLORMAP), | |
3971 cmap); | |
3972 } | |
3973 else if (img_dims(2) == 3) | |
3974 photometric = PHOTOMETRIC_RGB; | |
3975 else if (img_dims(2) == 4) | |
3976 photometric = PHOTOMETRIC_SEPARATED; | |
3977 | |
3978 if (! TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric)) | |
3979 error ("Failed to set the photometric tag"); | |
3980 | |
3981 tiff_image_data image_data (tif); | |
3982 if (args(0).is_bool_scalar () || args(0).is_bool_matrix () | |
3983 || args(0).is_uint8_type () || args(0).is_uint16_type () | |
3984 || args(0).is_uint32_type ()) | |
3985 write_unsigned_image (tif, args(0), &image_data); | |
3986 else | |
3987 { | |
3988 if (!TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)) | |
3989 error ("Failed to set SampleFormat tag"); | |
3990 write_float_image (tif, args(0).as_double (), &image_data); | |
3991 } | |
3992 | |
3993 return octave_value_list (); | |
3994 #else | |
3995 err_disabled_feature ("imwrite", "Tiff"); | |
3996 #endif | |
3997 } | |
3739 } | 3998 } |