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 }