changeset 31157:dc3d2744916d

Tiff: added readEncodedStrip method and corresponding unit tests * __tiff__.cc(F__tiff_read_encoded_strip__): added internal functino to process arguments of readEncodedStrip method. * __tiff__.cc(readStrip): implemented logic for reading the strip from the image. * Tiff.m: added the method readEncodedStrip to the Tiff class and added the necessary unit tests for the method.
author magedrifaat <magedrifaat@gmail.com>
date Fri, 05 Aug 2022 22:44:55 +0200
parents 1e633a093faa
children f2ae7763739a
files libinterp/dldfcn/__tiff__.cc scripts/io/Tiff.m
diffstat 2 files changed, 210 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__tiff__.cc	Thu Aug 04 19:29:36 2022 +0200
+++ b/libinterp/dldfcn/__tiff__.cc	Fri Aug 05 22:44:55 2022 +0200
@@ -76,6 +76,89 @@
         }
   }
 
+  uint32_t get_rows_in_strip (uint32_t strip_no, uint32_t strip_count,
+                              uint32_t rows_per_strip,
+                              tiff_image_data *image_data)
+  {
+    // Calculate the expected number of elements in the strip data array
+    // All strips have equal number of rows except strips at the bottom
+    // of the image can have less number of rows
+    uint32_t rows_in_strip = rows_per_strip;
+    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 - 1)
+          rows_in_strip = image_data->height - rows_in_strip * strip_no;
+      }
+    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 - 1;
+             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 % strips_per_channel);
+      }
+    else
+      error ("Planar Configuration not supported");
+    
+    return rows_in_strip;
+  }
+
+  template <typename T>
+  octave_value
+  read_strip (TIFF *tif, uint32_t strip_no, tiff_image_data * image_data)
+  {
+    // ASSUMES stripped image and strip_no is a valid zero-based strip
+    // index for the tif image
+    
+    uint32_t rows_in_strip;
+    if (! TIFFGetFieldDefaulted (tif, TIFFTAG_ROWSPERSTRIP, &rows_in_strip))
+      error ("Failed to obtain a value for RowsPerStrip");
+    
+    if (rows_in_strip > image_data->height)
+      rows_in_strip = image_data->height;
+    
+    uint32_t strip_count = TIFFNumberOfStrips (tif);
+    rows_in_strip = get_rows_in_strip (strip_no, strip_count,
+                                       rows_in_strip, image_data);
+    dim_vector strip_dims;
+    if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
+      strip_dims = dim_vector (image_data->samples_per_pixel,
+                               image_data->width, rows_in_strip);
+    else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE)
+      strip_dims = dim_vector (image_data->width, rows_in_strip, 1);
+    else
+      error ("Unsupported bit depth");
+    
+    T strip_data (strip_dims);
+
+    if (TIFFReadEncodedStrip (tif, strip_no,
+                              strip_data.fortran_vec (), -1) == -1)
+      error ("Failed to read strip data");
+    
+    Array<octave_idx_type> perm (dim_vector (3, 1));
+    if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
+      {
+        perm(0) = 2;
+        perm(1) = 1;
+        perm(2) = 0;
+      }
+    else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE)
+      {
+        perm(0) = 1;
+        perm(1) = 0;
+        perm(2) = 2;
+      }
+    
+    strip_data = strip_data.permute (perm);
+    return octave_value (strip_data);
+  }
+
   template <typename T>
   octave_value
   read_stripped_image (TIFF *tif, tiff_image_data *image_data)
@@ -885,39 +968,6 @@
       error ("Failed to set tag value");
   }
   
-  uint32_t get_rows_in_strip (uint32_t strip_no, uint32_t strip_count,
-                              uint32_t rows_per_strip,
-                              tiff_image_data *image_data)
-  {
-    // Calculate the expected number of elements in the strip data array
-    // All strips have equal number of rows except strips at the bottom
-    // of the image can have less number of rows
-    uint32_t rows_in_strip = rows_per_strip;
-    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 - 1)
-          rows_in_strip = image_data->height - rows_in_strip * strip_no;
-      }
-    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 - 1;
-             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 % strips_per_channel);
-      }
-    else
-      error ("Planar Configuration not supported");
-    
-    return rows_in_strip;
-  }
-
   template <typename T>
   void
   write_strip (TIFF *tif, uint32_t strip_no, T strip_data,
@@ -1686,6 +1736,85 @@
 #endif
   }
 
+  DEFUN_DLD (__tiff_read_encoded_strip__, args, nargout,
+             "Read the image in the current IFD")
+  {
+#if defined (HAVE_TIFF)
+    int nargin = args.length ();
+
+    if (nargin != 2)
+      error ("rong number of arguments");
+    
+    TIFF *tif = (TIFF *)(args(0).uint64_value ());
+
+    // TODO(maged): nargout and ycbcr
+    octave_unused_parameter (nargout);
+
+    if (TIFFIsTiled (tif))
+      error ("The image is tiled not stripped");
+    
+    // TODO(maged): what is the behavior in matlab
+    uint32_t strip_no;
+    if (args(1).is_scalar_type ())
+      strip_no = args(1).uint32_scalar_value ();
+    else
+      error ("Expected scalar for strip number");
+    
+    if (strip_no < 1 || strip_no > TIFFNumberOfStrips (tif))
+      error ("Strip number out of bounds");
+    
+    // Convert from Octave's 1-based indexing to zero-based indexing
+    strip_no--;
+
+    // Obtain all necessary tags
+    tiff_image_data image_data (tif);
+
+    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");
+      }
+    else if (sample_format != 1 && sample_format != 4)
+      error ("Unsupported sample format");
+    
+    octave_value_list retval;
+    switch (image_data.bits_per_sample)
+      {
+      case 1:
+        retval(0) = read_strip<boolNDArray> (tif, strip_no, &image_data);
+        break;
+      case 8:
+        retval(0) = read_strip<uint8NDArray> (tif, strip_no, &image_data);
+        break;
+      case 16:
+        retval(0) = read_strip<uint16NDArray> (tif, strip_no, &image_data);
+        break;
+      case 32:
+        if (sample_format == 3)
+          retval(0) = read_strip<FloatNDArray> (tif, strip_no, &image_data);
+        else
+          retval(0) = read_strip<uint32NDArray> (tif, strip_no, &image_data);
+        break;
+      case 64:
+        if (sample_format == 3)
+          retval(0) = read_strip<NDArray> (tif, strip_no, &image_data);
+        else
+          retval(0) = read_strip<uint64NDArray> (tif, strip_no, &image_data);
+        break;
+      default:
+        error ("Unsupported bit depth");
+      }
+    
+    return retval;
+#else
+    err_disabled_feature ("readEncodedStrip", "Tiff");
+#endif
+  }
+
   DEFUN_DLD (__tiff_write__, args, ,
              "Write the image data to the current IFD")
   {
--- a/scripts/io/Tiff.m	Thu Aug 04 19:29:36 2022 +0200
+++ b/scripts/io/Tiff.m	Fri Aug 05 22:44:55 2022 +0200
@@ -116,6 +116,13 @@
       argout = __tiff_read__ (t.tiff_handle);
     endfunction
 
+    function stripData = readEncodedStrip (t, stripNumber)
+      if (t.closed)
+        error ("Image file was closed");
+      endif
+      stripData = __tiff_read_encoded_strip__ (t.tiff_handle, stripNumber);
+    endfunction
+
     function write (t, imageData)
       if (t.closed)
         error ("Image file was closed");
@@ -1168,3 +1175,44 @@
 %!    verify_data (filename, data, [40, 40, 3]);
 %!  endfunction
 %!  file_wrapper (@test_fn);
+
+## test readEncodedStrip RGB Chunky
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
+%!                        "PhotometricInterpretation", 2, "RowsPerStrip", 6,
+%!                        "PlanarConfiguration", 1));
+%!    data = uint16 (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    s1 = readEncodedStrip (img, 1);
+%!    s2 = readEncodedStrip (img, 2);
+%!    assert ([s1;s2], data);
+%!    img.close();
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test readEncodedStrip RGB Separated
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 16, "SamplesPerPixel", 3,
+%!                        "PhotometricInterpretation", 2, "RowsPerStrip", 6,
+%!                        "PlanarConfiguration", 2));
+%!    data = uint16 (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    s1 = readEncodedStrip (img, 1);
+%!    s2 = readEncodedStrip (img, 2);
+%!    s3 = readEncodedStrip (img, 3);
+%!    s4 = readEncodedStrip (img, 4);
+%!    s5 = readEncodedStrip (img, 5);
+%!    s6 = readEncodedStrip (img, 6);
+%!    s = [s1;s2];
+%!    s(:, :, 2) = [s3;s4];
+%!    s(:, :, 3) = [s5;s6];
+%!    assert (s, data);
+%!    img.close();
+%!  endfunction
+%!  file_wrapper (@test_fn);