changeset 31126:7851c5b9c950

Tiff: implemented writeEncodedStrip function for writing a strip to an image * Tiff.m(writeEncodedStrip): added a method for writing strip to the image. * __tiff__.cc(F__tiff_write_encoded_strip): implemented an internal function to preprocess the wwriteEncodedStrip function call. * __tiff__.cc(write_strip): implemented the logic to write the strip data to the file.
author magedrifaat <magedrifaat@gmail.com>
date Sat, 23 Jul 2022 21:34:47 +0200
parents 3b775b939de4
children 0d9633ee715e
files libinterp/dldfcn/__tiff__.cc scripts/io/Tiff.m
diffstat 2 files changed, 190 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__tiff__.cc	Fri Jul 22 05:07:00 2022 +0200
+++ b/libinterp/dldfcn/__tiff__.cc	Sat Jul 23 21:34:47 2022 +0200
@@ -864,6 +864,84 @@
       error ("Failed to set tag value");
   }
 
+  template <typename T>
+  void
+  write_strip (TIFF *tif, uint32_t strip_no, octave_value strip_data_ov,
+               tiff_image_data *image_data)
+  {
+    T strip_data = T (strip_data_ov.array_value ());
+    
+    uint32_t rows_in_strip;
+    if (! TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rows_in_strip))
+      error ("Failed to obtain the RowsPerStrip tag");
+    
+    uint32_t strip_count = TIFFNumberOfStrips (tif);
+    uint32_t expected_numel;
+
+    // Calculate the expected number of elements in the strip data array
+    // All strips have equal number of rows excpet strips at the bottom
+    // of the image can have less number of rows
+    if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
+      {
+        // All strips have equal number of rows excpet strips at the bottom
+        // of the image can have less number of rows
+        if (strip_no == strip_count)
+          rows_in_strip = image_data->height - rows_in_strip * (strip_no - 1);
+        expected_numel = rows_in_strip * image_data->width
+                        * image_data->samples_per_pixel;
+      }
+    else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE)
+      {
+        // For separate planes, we should check the last strip of each channel
+        uint32_t strips_per_channel
+          = strip_count / image_data->samples_per_pixel;
+        for (uint32_t boundary_strip = strips_per_channel;
+             boundary_strip <= strip_count;
+             boundary_strip += strips_per_channel)
+          if (strip_no == boundary_strip)
+            rows_in_strip = image_data->height - rows_in_strip
+                                                 * ((strip_no - 1)
+                                                    % (strips_per_channel));
+        expected_numel = rows_in_strip * image_data->width;
+      }
+    else
+      error ("Planar configuration not supported");
+
+    if (strip_data.numel () != expected_numel)
+      error ("Size of strip data is different from the expected size of the strip");
+
+    
+    // Put the strip in a consistent shape for the subsequent permutation
+    if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
+      {
+        strip_data
+          = strip_data.reshape (dim_vector (rows_in_strip, image_data->width,
+                                            image_data->samples_per_pixel));
+      }
+    else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE)
+      {
+        strip_data = strip_data.reshape (dim_vector (rows_in_strip, 
+                                                     image_data->width, 1));
+      }
+
+    // Permute the dimesions of the strip to match the expected memory
+    // arrangement of LibTIFF (channel x width x height)
+    Array<octave_idx_type> perm (dim_vector (3, 1));
+    perm(0) = 2;
+    perm(1) = 1;
+    perm(2) = 0;
+    strip_data = strip_data.permute (perm);
+
+    // LibTIFF uses zero-based indexing as opposed to Octave's 1-based
+    strip_no--;
+    // Can't rely in LibTIFF's TIFFStripSize because boundary strips
+    // can be smaller in size
+    tsize_t strip_size = expected_numel * image_data->bits_per_sample / 8;
+    if (TIFFWriteEncodedStrip (tif, strip_no,
+                               strip_data.fortran_vec (), strip_size) == -1)
+      error ("Failed to write strip data to image");
+  }
+
 #endif
 
   DEFUN_DLD (__open_tiff__, args, nargout,
@@ -1025,12 +1103,7 @@
     
     TIFF *tif = (TIFF *)(args (0).uint64_value ());
 
-    // Check: Strips vs Tiles
-    //        Planar Configuration
-    //        SamplesPerPixel and bits_per_smaple
-    //        nargout and ycbcr
-    //        SampleFormat? ExtendedSamples? TransferFunction? GrayResponse? ColorMap?
-    //        What about floating point images?
+    // TODO(maged): nargout and ycbcr
 
     // Obtain all necessary tags
     // The main purpose of this struct is to hold all the necessary tags that
@@ -1065,7 +1138,6 @@
         retval(0) = read_image<uint16NDArray> (tif, &image_data);
         break;
       case 32:
-        // TODO(maged): do we need to check for Min and MaxSampleValue
         if (sample_format == 3)
           retval(0) = read_image<FloatNDArray> (tif, &image_data);
         else
@@ -1086,4 +1158,101 @@
     err_disabled_feature ("read", "Tiff");
 #endif
   }
+
+  DEFUN_DLD (__tiff_write_encoded_strip__, args, nargout,
+             "Write an encoded strip to the image")
+  {
+#if defined (HAVE_TIFF)
+    int nargin = args.length ();
+
+    if (nargin < 3)
+      error ("Too few arguments provided\n");
+    
+    TIFF *tif = (TIFF *)(args (0).uint64_value ());
+
+    // Obtain all necessary tags
+    tiff_image_data image_data (tif);
+
+    if (image_data.is_tiled)
+      error ("Can't rite strips to a tiled image");
+
+    uint32_t strip_no = args (1).uint_value ();
+    if (strip_no < 1 || strip_no > TIFFNumberOfStrips (tif))
+      error ("Strip number out of range");
+    
+    // SampleFormat tag is not a required field and has a default value of 1
+    // So we need to use TIFFGetFieldDefaulted in case it is not present in
+    // the file
+    uint16_t sample_format;
+    if (! TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format))
+      error ("Failed to obtain a value for sample format");
+
+    if (sample_format == 3)
+      {
+        if (image_data.bits_per_sample != 32 && image_data.bits_per_sample != 64)
+          error ("Floating point images are only supported for bit depths of 32 and 64");
+      }
+    // The standard specifies that a SampleFormat of 4 should be treated
+    // the same as 1 (unsigned integer)
+    else if (sample_format != 1 && sample_format != 4)
+      error ("Unsupported sample format");
+    
+    std::string type_name = args(2).type_name ();
+    switch (image_data.bits_per_sample)
+      {
+      case 1:
+        if (type_name == "bool matrix")
+          write_strip<boolNDArray> (tif, strip_no, args(2), &image_data);
+        else
+          error ("Expected logical matrix for BiLevel image");
+        break;
+      case 4:
+      case 8:
+        if (type_name == "uint8 matrix" || type_name == "int8 matrix")
+          write_strip<uint8NDArray> (tif, strip_no, args(2), &image_data);
+        else
+          error ("Only uint8 and int8 data are allowed for images with bit depth of 8");
+        break;
+      case 16:
+        // Type conversion from signed to unsigned is handled in the function
+        // TODO(maged): what is the behavior if the input matrix has
+        // negative numbers?
+        if (type_name == "uint16 matrix" || type_name == "int16 matrix")
+          write_strip<uint16NDArray> (tif, strip_no, args(2), &image_data);
+        else
+          error ("Only uint16 and int16 data are allowed for images with bit depth of 16");
+        break;
+      case 32:
+        if (sample_format == 3)  
+          if (type_name == "float matrix" || type_name == "matrix")
+            write_strip<FloatNDArray> (tif, strip_no, args(2), &image_data);
+          else
+            error ("Only single and double data are allowed for floating-point images");
+        else
+          if (type_name == "uint32 matrix" || type_name == "int32 matrix")
+            write_strip<uint32NDArray> (tif, strip_no, args(2), &image_data);
+          else
+            error ("Only uint32 and int32 data are allowed for images with bit depth of 32");
+        break;
+      case 64:
+        if (sample_format == 3)          
+          if (type_name == "float matrix" || type_name == "matrix")
+            write_strip<NDArray> (tif, strip_no, args(2), &image_data);
+          else
+            error ("Only single and double data are allowed for floating-point images");
+        else  
+          if (type_name == "uint64 matrix" || type_name == "int64 matrix")
+            write_strip<uint64NDArray> (tif, strip_no, args(2), &image_data);
+          else
+            error ("Only uint64 and int64 data are allowed for images with bit depth of 64");
+        break;
+      default:
+        error ("Unsupported bit depth");
+      }
+    
+    return octave_value_list ();
+#else
+    err_disabled_feature ("writeEncodedStrip", "Tiff");
+#endif
+  }
 }
--- a/scripts/io/Tiff.m	Fri Jul 22 05:07:00 2022 +0200
+++ b/scripts/io/Tiff.m	Sat Jul 23 21:34:47 2022 +0200
@@ -79,29 +79,33 @@
     endproperties
 
     methods
-        function t = Tiff(filename, mode="r")
+        function t = Tiff (filename, mode="r")
             if (nargin == 0 || nargin > 2)
                 % print_usage();
                 error("Usage: Tiff(filename[, mode])");
             endif
 
-            t.tiff_handle = __open_tiff__(filename, mode);
+            t.tiff_handle = __open_tiff__ (filename, mode);
         endfunction
 
-        function close(t)
-            __close_tiff__(t.tiff_handle);
+        function close (t)
+            __close_tiff__ (t.tiff_handle);
+        endfunction
+
+        function tag = getTag (t, tag_name)
+            tag = __tiff_get_tag__ (t.tiff_handle, tag_name);
         endfunction
 
-        function tag = getTag(t, tag_name)
-            tag = __tiff_get_tag__(t.tiff_handle, tag_name);
+        function setTag (t, tag_name, tag_value)
+            __tiff_set_tag__ (t.tiff_handle, tag_name, tag_value);
         endfunction
 
-        function setTag(t, tag_name, tag_value)
-            __tiff_set_tag__(t.tiff_handle, tag_name, tag_value);
+        function argout = read (t)
+            argout = __tiff_read__ (t.tiff_handle);
         endfunction
 
-        function argout = read(t)
-            argout = __tiff_read__(t.tiff_handle);
+        function writeEncodedStrip (t, stripNumber, imageData)
+            __tiff_write_encoded_strip__ (t.tiff_handle, stripNumber, imageData);
         endfunction
 
         % TODO(maged): add documentation and make print_usage work