changeset 31118:f8be3654caef

Tiff read: Support for tiled images with normal planar configuration * __tiff__.cc(read_tiled_image): Implemented support for images organized in tiles, only normal planar configuration is currently supported.
author magedrifaat <magedrifaat@gmail.com>
date Sun, 17 Jul 2022 17:59:38 +0200
parents 530dbd1d6b07
children dbca50246dfc
files libinterp/dldfcn/__tiff__.cc
diffstat 1 files changed, 127 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__tiff__.cc	Sat Jul 16 23:40:15 2022 +0200
+++ b/libinterp/dldfcn/__tiff__.cc	Sun Jul 17 17:59:38 2022 +0200
@@ -33,6 +33,18 @@
     uint16_t is_tiled;
   };
 
+  // Error if status is not 1 (success status for TIFFGetField)
+  void
+  validate_tiff_get_field (bool status, void *p_to_free=NULL)
+  {
+      if (status != 1)
+        {
+          if (p_to_free != NULL)
+            _TIFFfree (p_to_free);
+          error ("Failed to read tag");
+        }
+  }
+
   template <typename T>
   octave_value
   read_stripped_image (TIFF *tif, tiff_image_data *image_data)
@@ -63,7 +75,7 @@
         // Can't rely on StripByteCounts because in compressed images
         // the byte count reflect the actual number of bytes stored
         // in the file not the size of the uncompressed strip
-        uint32_t strip_bytes = TIFFReadEncodedStrip (tif, strip, buf, -1);
+        int64_t strip_bytes = TIFFReadEncodedStrip (tif, strip, buf, -1);
         if (strip_bytes == -1)
           error ("Failed to read strip data");
         
@@ -72,76 +84,124 @@
         for (uint32_t row_subindex = 0;
               row_subindex < rows_in_strip;
               row_subindex++)
-          {
-            for (uint32_t column = 0;
-                  column < image_data->width;
-                  column++)
-              {
-                uint32_t row_offset = row_size_in_bits / 8
-                                      * row_subindex;
-                // TODO(maged): support arbitrary BitsPerSample?
-                if (image_data->bits_per_sample >= 8
-                    && image_data->bits_per_sample % 8 == 0)
-                  {
-                    // TODO(maged): clean up the math for both cases
-                    if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
-                      for (uint16_t sample = 0;
-                            sample < image_data->samples_per_pixel; sample++)
-                        // The memory organization of fvec is inverted from
-                        // what would be expected for a normal C-like array.
-                        // It is treated as samples * columns * rows as
-                        // opposed to rows * columns * samples.
-                        img_fvec[sample * plane_size
+          for (uint32_t column = 0;
+                column < image_data->width;
+                column++)
+            {
+              uint32_t row_offset = row_size_in_bits / 8
+                                    * row_subindex;
+              // TODO(maged): support arbitrary BitsPerSample?
+              if (image_data->bits_per_sample >= 8
+                  && image_data->bits_per_sample % 8 == 0)
+                {
+                  // TODO(maged): clean up the math for both cases
+                  if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
+                    for (uint16_t sample = 0;
+                          sample < image_data->samples_per_pixel; sample++)
+                      // The memory organization of fvec is inverted from
+                      // what would be expected for a normal C-like array.
+                      // It is treated as samples * columns * rows as
+                      // opposed to rows * columns * samples.
+                      img_fvec[sample * plane_size
+                              + column * image_data->height
+                              + row_index + row_subindex]
+                        = ((P *)buf)[row_subindex * image_data->width
+                                    * image_data->samples_per_pixel
+                                    + column * image_data->samples_per_pixel
+                                    + sample];
+                  else
+                    {
+                      uint16_t channel_no = strip
+                                            * image_data->samples_per_pixel
+                                            / strip_count;
+                      uint16_t corrected_row = (row_index + row_subindex)
+                                                % image_data->height;
+                      img_fvec[channel_no * plane_size
                                 + column * image_data->height
-                                + row_index + row_subindex]
-                          = ((P *)buf)[row_subindex * image_data->width
-                                      * image_data->samples_per_pixel
-                                      + column * image_data->samples_per_pixel
-                                      + sample];
-                    else
-                      {
-                        uint16_t channel_no = strip
-                                              * image_data->samples_per_pixel
-                                              / strip_count;
-                        uint16_t corrected_row = (row_index + row_subindex)
-                                                 % image_data->height;
-                        img_fvec[channel_no * plane_size
-                                 + column * image_data->height
-                                 + corrected_row]
-                          = ((P *)buf)[row_subindex * image_data->width
-                                       + column];
-                      }
-                  }
-                else if (image_data->bits_per_sample == 4
-                          && image_data->samples_per_pixel == 1)
-                  {
-                    // TODO(maged): Check FillOrder for completeness
-                    uint8_t nibble
-                      = ((uint8_t *)buf)[row_offset + column / 2];
-                    // Extract the needed nibble from the byte
-                    nibble = (column % 2 == 0? nibble >> 4: nibble) & 0x0F;
-                    img_fvec[(row_index + row_subindex)
-                              + column * image_data->height] = nibble;
-                  }
-                else if (image_data->bits_per_sample == 1
-                          && image_data->samples_per_pixel == 1)
-                  {
-                    uint8_t byte
-                      = ((uint8_t *)buf)[row_offset + column / 8];
-                    // Extract the needed bit from the byte
-                    uint8_t bit = (byte >> (7 - column % 8)) & 0x01;
-                    img_fvec[(row_index + row_subindex)
-                              + column * image_data->height] = bit;
-                  }
-                else
-                  error ("Unsupported bit depth");
-              }
-          }
+                                + corrected_row]
+                        = ((P *)buf)[row_subindex * image_data->width
+                                      + column];
+                    }
+                }
+              else if (image_data->bits_per_sample == 4
+                        && image_data->samples_per_pixel == 1)
+                {
+                  // TODO(maged): Check FillOrder for completeness
+                  uint8_t nibble
+                    = ((uint8_t *)buf)[row_offset + column / 2];
+                  // Extract the needed nibble from the byte
+                  nibble = (column % 2 == 0? nibble >> 4: nibble) & 0x0F;
+                  img_fvec[(row_index + row_subindex)
+                            + column * image_data->height] = nibble;
+                }
+              else if (image_data->bits_per_sample == 1
+                        && image_data->samples_per_pixel == 1)
+                {
+                  uint8_t byte
+                    = ((uint8_t *)buf)[row_offset + column / 8];
+                  // Extract the needed bit from the byte
+                  uint8_t bit = (byte >> (7 - column % 8)) & 0x01;
+                  img_fvec[(row_index + row_subindex)
+                            + column * image_data->height] = bit;
+                }
+              else
+                error ("Unsupported bit depth");
+            }
         row_index += rows_in_strip;
       }
     _TIFFfree (buf);
 
-    return octave_value(img);
+    return octave_value (img);
+  }
+
+  template <typename T>
+  octave_value
+  read_tiled_image (TIFF *tif, tiff_image_data *image_data)
+  {
+    typedef typename T::element_type P;
+
+    T img = T (dim_vector (image_data->height, image_data->width,
+                            image_data->samples_per_pixel));
+    P *img_fvec = img.fortran_vec ();
+
+    // Obtain the necessary data for handling the tiles
+    uint32_t tile_width, tile_height;
+    validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_TILEWIDTH,
+                                           &tile_width));
+    validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_TILELENGTH,
+                                           &tile_height));
+
+    tdata_t buf = _TIFFmalloc (TIFFTileSize (tif));
+    if (! buf)
+      error ("Failed to allocate buffer for tile data");
+    
+    // TODO(maged): implement support for ImageDepth?
+    uint32_t tile_size;
+    for (uint32_t base_row = 0; base_row < image_data->height; base_row += tile_height)
+      for (uint32_t base_col = 0; base_col < image_data->width; base_col += tile_width)
+        if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
+          {
+            if ((tile_size = TIFFReadTile(tif, buf, base_col, base_row, 0, 0)) == -1)
+              error ("Failed to read tile data");
+            
+            // Boundary tiles are zero padded so we might need to stop earlier than the end of the tile
+            for (uint32_t sub_row = 0; sub_row < tile_height && base_row + sub_row < image_data->height; sub_row++)
+              for (uint32_t sub_col = 0; sub_col < tile_width && base_col + sub_col < image_data->width; sub_col++)
+                for (uint16_t sample = 0; sample < image_data->samples_per_pixel; sample++)
+                  img_fvec[sample * image_data->width * image_data->height
+                           + (base_col + sub_col) * image_data->height
+                           + base_row + sub_row]
+                    = ((P *)buf)[sub_row * tile_width * image_data->samples_per_pixel
+                                 + sub_col * image_data->samples_per_pixel
+                                 + sample];
+          }
+        else
+          // TODO(maged): Implement for separated planes
+          error ("Tiled images with separate planar configuration not supported");
+
+    _TIFFfree (buf);
+
+    return octave_value (img);
   }
 
   template <typename T>
@@ -149,24 +209,11 @@
   read_image (TIFF *tif, tiff_image_data *image_data)
   {
     if (image_data->is_tiled)
-      // TODO(maged): Implement tiled images
-      error ("Tiled images are not implemented yet");
+      return read_tiled_image<T> (tif, image_data);
     else
       return read_stripped_image<T> (tif, image_data);
   }
 
-  // Error if status is not 1 (success status for TIFFGetField)
-  void
-  validate_tiff_get_field (bool status, void *p_to_free=NULL)
-  {
-      if (status != 1)
-        {
-          if (p_to_free != NULL)
-            _TIFFfree (p_to_free);
-          error ("Failed to read tag");
-        }
-  }
-
   // Convert tag value to double
   octave_value
   interpret_scalar_tag_data (void *data, TIFFDataType tag_datatype)
@@ -566,7 +613,6 @@
       case TIFFTAG_DOTRANGE:
       case TIFFTAG_YCBCRSUBSAMPLING:
         {
-          // TODO(maged): fix bug where only first one is returned
           uint16_t tag_part1, tag_part2;
           validate_tiff_get_field (TIFFGetField (tif, tag_id,
                                                  &tag_part1, &tag_part2));