changeset 31207:22f63f446e79

__tiff_imread__: optimized reading PixelRegion for stripped images * scripts/image/private/__tiff_imread__.m: added logic to read only the strips that contain the requested PixelRegion to improve performance.
author magedrifaat <magedrifaat@gmail.com>
date Sat, 03 Sep 2022 23:51:06 +0200
parents df8ee95752aa
children 1a564892fef1
files scripts/image/private/__tiff_imread__.m
diffstat 1 files changed, 75 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/image/private/__tiff_imread__.m	Sat Sep 03 20:36:33 2022 +0200
+++ b/scripts/image/private/__tiff_imread__.m	Sat Sep 03 23:51:06 2022 +0200
@@ -122,11 +122,23 @@
   alpha = [];
   for page_idx = 1:numel(pages)
     img.setDirectory (pages(page_idx));
-    ## FIXME: This an ineffecient way to read pixel regions because it
-    ## always reads the entire image first. A better way is to figure
-    ## out which strips/tiles are needed for the region and read only those.
-    data = img.read ();
-    data = data (region{1}, region{2}, :);
+
+    tiled = img.isTiled ();
+    if (numel (region{1}) == info.height
+        && (! tiled || numel (region{2}) == info.width))
+      ## If we need the entire tiled image or all the rows of a stripped
+      ## image then the best approach is to read the entire image directly
+      data = img.read ();
+      if (numel (region{2}) != info.width)
+        data = data (:, region{2}, :);
+      endif
+    elseif (tiled)
+      ## Otherwise it should be better to read the specific strips and tiles
+      ## that the requested region falls in
+      data = read_tiled_data (img, region);
+    else
+      data = read_stripped_data (img, region);
+    endif
     A = cat (4, A, data);
     if (info.photometric == Tiff.Photometric.Palette)
       cmap = cat (3, cmap, img.getTag (Tiff.TagID.ColorMap));
@@ -136,6 +148,64 @@
   img.close ();
 endfunction
 
+function data = read_tiled_data (img, region)
+  info = get_image_info (img);
+  tile = img.readEncodedTile (1);
+  data = zeros (numel (region{1}), numel (region{2}),
+                info.nchannels, class (tile));
+
+endfunction
+
+function data = read_stripped_data (img, region)
+  ## Get all necessary image info
+  info = get_image_info (img);
+  rows_per_strip = img.getTag (Tiff.TagID.RowsPerStrip);
+  planar = img.getTag (Tiff.TagID.PlanarConfiguration);
+  
+  ## Create array to hold the output data
+  ## (we need a strip to infer the data type)
+  strip = img.readEncodedStrip (1);
+  data = zeros (numel (region{1}), numel (region{2}),
+                info.nchannels, class (strip));
+  last_strip_index = 1;
+  if (planar == Tiff.PlanarConfiguration.Chunky)
+    ## For chunky planes, we need to iterate over rows only
+    data_row = 1;
+    for row = region{1}
+      strip_index = img.computeStrip (row);
+      ## The index of the row in the strip
+      strip_row = rem (row - 1, rows_per_strip) + 1;
+      if (strip_index != last_strip_index)
+        ## If the required strip isn't cached, grab it
+        strip = img.readEncodedStrip (strip_index);
+        last_strip_index = strip_index;
+      endif
+      data(data_row, :, :) = strip (strip_row, region{2}, :);
+      data_row += 1;
+    endfor
+  else
+    ## For separate planes we need to loop over channels as well as rows
+    ## It's better to make the outer loop for channels so we can make use
+    ## of strip caching, because otherwise, we would need to read a different
+    ## strip each iteration of the inner loop
+    for ch = 1:info.nchannels
+      data_row = 1;
+      for row = region{1}
+        strip_index = img.computeStrip (row, ch);
+        ## The index of the row in the strip
+        strip_row = rem (row - 1, rows_per_strip) + 1;
+        if (strip_index != last_strip_index)
+          ## If the needed strip is not cached, grab it
+          strip = img.readEncodedStrip (strip_index);
+          last_strip_index = strip_index;
+        endif
+        data(data_row, :, ch) = strip (strip_row, region{2});
+        data_row += 1;
+      endfor
+    endfor
+  endif
+endfunction
+
 function bool = is_valid_index_option (arg)
   bool = isvector (arg) && isnumeric (arg) && isreal (arg);
 endfunction