changeset 31109:06814e8b5a29

add function to read an entire image * __tiff__.cc (F__tiff_read__): Implemented a simple layer to read stripped images using libtiff TIFFReadEncodedStrip API, implementation is still buggy. * Tiff.m: added read method to reference the new internal function.
author magedrifaat <magedrifaat@gmail.com>
date Wed, 06 Jul 2022 04:44:17 +0200
parents 99a301eb315e
children 2daeeff33980
files libinterp/dldfcn/__tiff__.cc scripts/io/Tiff.m
diffstat 2 files changed, 203 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__tiff__.cc	Tue Jul 05 04:11:37 2022 +0200
+++ b/libinterp/dldfcn/__tiff__.cc	Wed Jul 06 04:44:17 2022 +0200
@@ -34,7 +34,7 @@
   }
 
   octave_value_list
-  interpret_scalar(void *data, TIFFDataType tag_datatype)
+  interpret_scalar (void *data, TIFFDataType tag_datatype)
   {
     double retval;
 
@@ -109,7 +109,7 @@
         error ("Unsupported tag data type");
       }
 
-    return octave_value_list(octave_value(retval));
+    return octave_value_list (octave_value (retval));
   }
 
   // Convert memory buffer into suitable octave values
@@ -143,7 +143,7 @@
             }
           case TIFF_ASCII:
             {
-              ovl_data(0) = octave_value(*(char **)data);
+              ovl_data(0) = octave_value (*(char **)data);
               break;
             }
           case TIFF_SHORT:
@@ -456,7 +456,7 @@
 #endif
 
   DEFUN_DLD (__open_tiff__, args, nargout,
-            "Open a Tiff file and return its handle")
+             "Open a Tiff file and return its handle")
   {
 #if defined (HAVE_TIFF)
     int nargin = args.length ();
@@ -490,7 +490,7 @@
 
 
   DEFUN_DLD (__close_tiff__, args, nargout,
-            "Close a tiff file")
+             "Close a tiff file")
   {
 #if defined (HAVE_TIFF)
     int nargin = args.length ();
@@ -509,45 +509,212 @@
 
 
   DEFUN_DLD (__tiff_get_tag__, args, nargout,
-            "Get the value of a tag from a tiff image")
+             "Get the value of a tag from a tiff image")
   {
 #if defined (HAVE_TIFF)
-      int nargin = args.length ();
+    int nargin = args.length ();
 
-      if (nargin == 0)
-        error ("No handle provided\n");
-      
-      if (nargin < 2)
-        error ("No tag name provided\n");
-      
-      TIFF *tif = (TIFF *)(args (0).uint64_value ());
+    if (nargin == 0)
+      error ("No handle provided\n");
+    
+    if (nargin < 2)
+      error ("No tag name provided\n");
+    
+    TIFF *tif = (TIFF *)(args (0).uint64_value ());
 
-      uint32_t tag_id;
-      const TIFFField *fip;
-      if (args (1).type_name () == "string")
-        {
-          std::string tagName = args (1).string_value ();
-          fip = TIFFFieldWithName (tif, tagName.c_str ());
-          if (! fip)
-            error ("Tiff tag not found");
-          
-          tag_id = TIFFFieldTag (fip);
-        }
-      else
-        {
-          tag_id = args (1).int_value ();
-          fip = TIFFFieldWithTag (tif, tag_id);
-          // TODO(maged): Handle other types of errors (e.g. unsupported tags)
-          if (! fip)
-            error ("Tiff tag not found");
-        }
+    uint32_t tag_id;
+    const TIFFField *fip;
+    if (args (1).type_name () == "string")
+      {
+        std::string tagName = args (1).string_value ();
+        fip = TIFFFieldWithName (tif, tagName.c_str ());
+        if (! fip)
+          error ("Tiff tag not found");
+        
+        tag_id = TIFFFieldTag (fip);
+      }
+    else
+      {
+        tag_id = args (1).int_value ();
+        fip = TIFFFieldWithTag (tif, tag_id);
+        // TODO(maged): Handle other types of errors (e.g. unsupported tags)
+        if (! fip)
+          error ("Tiff tag not found");
+      }
 
 
-      octave_value_list tag_data_ovl = get_field_data (tif, fip);
+    octave_value_list tag_data_ovl = get_field_data (tif, fip);
 
-      return tag_data_ovl;
+    return tag_data_ovl;
 #else
     err_disabled_feature ("getTag", "Tiff");
 #endif
   }
+
+  DEFUN_DLD (__tiff_read__, args, nargout,
+             "Read the image in the current IFD")
+  {
+#if defined (HAVE_TIFF)
+    int nargin = args.length ();
+
+    if (nargin == 0)
+      error ("No handle provided\n");
+    
+    TIFF *tif = (TIFF *)(args (0).uint64_value ());
+
+    // Check: Strips vs Tiles
+    //        Planar Configuration
+    //        StripByteCounts
+    //        SamplesPerPixel and bits_per_smaple
+    //        nargout and ycbcr
+    //        ExtendedSamples? TransferFunction? GrayResponse? ColorMap?
+    //        What about floating point images?
+
+    // Obtain all necessary tags
+    uint32_t width, height;
+    if (! TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width))
+      error ("Failed to read image width");
+
+    if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &height))
+      error ("Failed to read image height");
+    
+    uint16_t samples_per_pixel;
+    if (! TIFFGetField (tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel))
+      error ("Failed to read the SamplesPerPixel tag");
+
+    uint16_t bits_per_sample;
+    if (! TIFFGetField (tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample))
+      error ("Failed to read the BitsPerSample tag");
+    
+    uint16_t planar_configuration;
+    if (! TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar_configuration))
+      error ("Failed to read the PlanarConfiguration tag");
+    
+    uint16_t is_tiled = TIFFIsTiled(tif);
+
+    // Create memory for storing the image data
+    // TODO(maged): replace malloc with a suitable C++ structure
+    void ***image = (void ***)malloc (sizeof(void ***) * height);
+    uint32_t pixel_size = samples_per_pixel * (bits_per_sample / 8);
+    uint32_t row_size = width * pixel_size;
+    for (uint32_t row = 0; row < height; row++)
+      {
+        image[row] = (void **)malloc (sizeof(void **) * width);
+        for (uint32_t column = 0; column < width; column++)
+          image[row][column] = malloc (pixel_size);
+      }
+
+    if (is_tiled)
+      // TODO(maged): Implement tiled images
+      error ("Tiled images are not implemented yet");
+    else
+      {
+        // Obtain the necessary data for handling the strips
+        uint32_t strip_count = TIFFNumberOfStrips (tif);
+        uint32_t *strip_byte_counts;
+        if (! TIFFGetField (tif, TIFFTAG_STRIPBYTECOUNTS, &strip_byte_counts))
+          error ("Failed to read StripByteCounts tag");
+        
+        uint32_t strip_size = strip_byte_counts[0];
+        tdata_t buf = _TIFFmalloc (strip_size);
+        uint32_t row_index = 0;
+        for (uint32_t strip = 0; strip < strip_count; strip++)
+          {
+            if (strip_size < strip_byte_counts[strip])
+              {
+                buf = _TIFFrealloc (buf, strip_byte_counts[strip]);
+                strip_size = strip_byte_counts[strip];
+              }
+            TIFFReadEncodedStrip (tif, strip, buf, -1);
+            if (planar_configuration == PLANARCONFIG_CONTIG)
+              {
+                uint32_t rows_in_strip = strip_byte_counts[strip] / row_size;
+                for (uint32_t row_subindex = 0;
+                     row_subindex < rows_in_strip;
+                     row_subindex++)
+                  {
+                    for (uint32_t column = 0; column < width; column++)
+                      memcpy (image[row_index + row_subindex][column],
+                              buf + row_size * row_subindex
+                              + column * pixel_size, pixel_size);
+                  }
+                row_index += rows_in_strip;
+              }
+            else
+              error ("Images with multiple planes are not implemented yet");
+          }
+        _TIFFfree (buf);
+      }
+    
+    octave_value_list retval;
+    dim_vector arr_dims (height, width, samples_per_pixel);
+    // TODO(maged): what about palette images? are they handled internally?
+    switch (bits_per_sample)
+      {
+      // TODO(maged): this is probably incorrect as bilevel has no BitsPerSample
+      case 1:
+        {
+          boolNDArray arr(arr_dims);
+          for (uint32_t row = 0; row < height; row++)
+            for (uint32_t column = 0; column < width; column++)
+              for (uint16_t sample = 0; sample < samples_per_pixel; sample++)
+                arr(row, column, sample) = ((uint8_t ***)image)[row][column][sample];
+          
+          retval(0) = arr;
+          break;
+        }
+      // TODO(maged): bits_per_sample of 4 will probably require more logic
+      case 4:
+      case 8:
+        {
+          uint8NDArray arr(arr_dims);
+          for (uint32_t row = 0; row < height; row++)
+            for (uint32_t column = 0; column < width; column++)
+              for (uint16_t sample = 0; sample < samples_per_pixel; sample++)
+                arr(row, column, sample) = ((uint8_t ***)image)[row][column][sample];
+          
+          retval(0) = arr;
+          break;
+        }
+      case 16:
+        {
+          uint16NDArray arr(arr_dims);
+          for (uint32_t row = 0; row < height; row++)
+            for (uint32_t column = 0; column < width; column++)
+              for (uint16_t sample = 0; sample < samples_per_pixel; sample++)
+                arr(row, column, sample) = ((uint16_t ***)image)[row][column][sample];
+          
+          retval(0) = arr;
+          break;
+        }
+      case 32:
+        {
+          uint32NDArray arr(arr_dims);
+          for (uint32_t row = 0; row < height; row++)
+            for (uint32_t column = 0; column < width; column++)
+              for (uint16_t sample = 0; sample < samples_per_pixel; sample++)
+                arr(row, column, sample) = ((uint32_t ***)image)[row][column][sample];
+          
+          retval(0) = arr;
+          break;
+        }
+      case 64:
+        {
+          uint64NDArray arr(arr_dims);
+          for (uint32_t row = 0; row < height; row++)
+            for (uint32_t column = 0; column < width; column++)
+              for (uint16_t sample = 0; sample < samples_per_pixel; sample++)
+                arr(row, column, sample) = ((uint64_t ***)image)[row][column][sample];
+          
+          retval(0) = arr;
+          break;
+        }
+      default:
+        error ("Unsupported bit depth");
+      }
+    return retval;
+#else
+    err_disabled_feature ("read", "Tiff");
+#endif
+  }
 }
--- a/scripts/io/Tiff.m	Tue Jul 05 04:11:37 2022 +0200
+++ b/scripts/io/Tiff.m	Wed Jul 06 04:44:17 2022 +0200
@@ -97,6 +97,7 @@
         endfunction
 
         function argout = read(t)
+            argout = __tiff_read__(t.tiff_handle)
         endfunction
 
         % TODO(maged): add documentation and make print_usage work