changeset 17124:c97a26408ee0

Implement PixelRegion option for imread(). * imread.m: document new option. * private/__imread__.m: parse new option and set defaults. * __magick_read__.cc (calculate region): new option to calculate the region to be read, shifts in memory required, and output image size. (read_indexed_images, read_images): implement reading of only specific regions of an image. (__magick_read__): get octave_scalar_map at start for simplicity.
author Carnë Draug <carandraug@octave.org>
date Wed, 31 Jul 2013 21:28:48 +0100
parents 47b504503a3f
children bd50e0660545
files libinterp/dldfcn/__magick_read__.cc scripts/image/imread.m scripts/image/private/__imread__.m
diffstat 3 files changed, 126 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__magick_read__.cc
+++ b/libinterp/dldfcn/__magick_read__.cc
@@ -44,30 +44,69 @@
 #include <Magick++.h>
 #include <clocale>
 
+static std::map<std::string, octave_idx_type>
+calculate_region (const octave_scalar_map& options)
+{
+  std::map<std::string, octave_idx_type> region;
+  const Cell pixel_region = options.getfield ("region").cell_value ();
+
+  // Subtract 1 to account for 0 indexing.
+  const Range rows     = pixel_region (0).range_value ();
+  const Range cols     = pixel_region (1).range_value ();
+  region["row_start"]  = rows.base () -1;
+  region["col_start"]  = cols.base () -1;
+  region["row_end"]    = rows.limit () -1;
+  region["col_end"]    = cols.limit () -1;
+
+  // Length of the area to load into the Image Pixel Cache
+  region["row_cache"] = region["row_end"] - region["row_start"] +1;
+  region["col_cache"] = region["col_end"] - region["col_start"] +1;
+
+  // How much we have to shift in the memory when doing the loops.
+  region["row_shift"] = region["col_cache"] * rows.inc ();
+  region["col_shift"] = region["col_cache"] * region["row_cache"] - cols.inc ();
+
+  // The actual height and width of the output image
+  region["row_out"] = floor ((region["row_end"] - region["row_start"]) / rows.inc ()) + 1;
+  region["col_out"] = floor ((region["col_end"] - region["col_start"]) / cols.inc ()) + 1;
+
+  return region;
+}
+
 template <class T>
 static octave_value_list
 read_indexed_images (std::vector<Magick::Image>& imvec,
                      const Array<octave_idx_type>& frameidx,
-                     const octave_idx_type nargout)
+                     const octave_idx_type nargout,
+                     const octave_scalar_map& options)
 {
   typedef typename T::element_type P;
 
   octave_value_list retval (3, Matrix ());
 
-  const octave_idx_type nRows    = imvec[0].baseRows ();
-  const octave_idx_type nCols    = imvec[0].baseColumns ();
-  const octave_idx_type nFrames  = frameidx.length ();
+  std::map<std::string, octave_idx_type> region = calculate_region (options);
+  const octave_idx_type nFrames = frameidx.length ();
+  const octave_idx_type nRows = region["row_out"];
+  const octave_idx_type nCols = region["col_out"];
 
   T img       = T (dim_vector (nRows, nCols, 1, nFrames));
   P* img_fvec = img.fortran_vec ();
 
+  const octave_idx_type row_start  = region["row_start"];
+  const octave_idx_type col_start  = region["col_start"];
+  const octave_idx_type row_shift  = region["row_shift"];
+  const octave_idx_type col_shift  = region["col_shift"];
+  const octave_idx_type row_cache  = region["row_cache"];
+  const octave_idx_type col_cache  = region["col_cache"];
+
   // When reading PixelPackets from the Image Pixel Cache, they come in
   // row major order. So we keep moving back and forth there so we can
   // write the image in column major order.
   octave_idx_type idx = 0;
   for (octave_idx_type frame = 0; frame < nFrames; frame++)
     {
-      imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows);
+      imvec[frameidx(frame)].getConstPixels (col_start, row_start,
+                                             col_cache, row_cache);
 
       const Magick::IndexPacket *pix
         = imvec[frameidx(frame)].getConstIndexes ();
@@ -77,9 +116,9 @@
           for (octave_idx_type row = 0; row < nRows; row++)
             {
               img_fvec[idx++] = static_cast<P> (*pix);
-              pix += nCols;
+              pix += row_shift;
             }
-          pix -= nCols * nRows -1;
+          pix -= col_shift;
         }
     }
   retval(0) = octave_value (img);
@@ -148,17 +187,26 @@
 octave_value_list
 read_images (std::vector<Magick::Image>& imvec,
              const Array<octave_idx_type>& frameidx,
-             const octave_idx_type nargout)
+             const octave_idx_type nargout,
+             const octave_scalar_map& options)
 {
   typedef typename T::element_type P;
 
   octave_value_list retval (3, Matrix ());
 
-  const octave_idx_type nRows   = imvec[0].baseRows ();
-  const octave_idx_type nCols   = imvec[0].baseColumns ();
+  std::map<std::string, octave_idx_type> region = calculate_region (options);
   const octave_idx_type nFrames = frameidx.length ();
+  const octave_idx_type nRows = region["row_out"];
+  const octave_idx_type nCols = region["col_out"];
   T img;
 
+  const octave_idx_type row_start  = region["row_start"];
+  const octave_idx_type col_start  = region["col_start"];
+  const octave_idx_type row_shift  = region["row_shift"];
+  const octave_idx_type col_shift  = region["col_shift"];
+  const octave_idx_type row_cache  = region["row_cache"];
+  const octave_idx_type col_cache  = region["col_cache"];
+
   // GraphicsMagick (GM) keeps the image values in memory using whatever
   // QuantumDepth it was built with independently of the original image
   // bitdepth. Basically this means that if GM was built with quantum 16
@@ -237,16 +285,17 @@
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
-              = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows);
+              = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
+                                                       col_cache, row_cache);
 
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
                   {
                     img_fvec[idx++] = pix->red / divisor;
-                    pix += nCols;
+                    pix += row_shift;
                   }
-                pix -= nRows * nCols -1;
+                pix -= col_shift;
               }
           }
         break;
@@ -263,18 +312,19 @@
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
-              = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows);
+              = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
+                                                       col_cache, row_cache);
 
             for (octave_idx_type col = 0; col < nCols; col++)
               {
                 for (octave_idx_type row = 0; row < nRows; row++)
                   {
                     img_fvec[idx] = pix->red / divisor;
-                    a_fvec[idx] = pix->opacity / divisor;
-                    pix += nCols;
+                    a_fvec[idx]   = pix->opacity / divisor;
+                    pix += row_shift;
                     idx++;
                   }
-                pix -= nRows * nCols -1;
+                pix -= col_shift;
               }
           }
         retval(2) = alpha;
@@ -290,7 +340,8 @@
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
-              = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows);
+              = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
+                                                       col_cache, row_cache);
 
             octave_idx_type idx = 0;
             img_fvec += nRows * nCols * frame;
@@ -305,10 +356,10 @@
                     rbuf[idx] = pix->red   / divisor;
                     gbuf[idx] = pix->green / divisor;
                     bbuf[idx] = pix->blue  / divisor;
-                    pix += nCols;
+                    pix += row_shift;
                     idx++;
                   }
-                pix -= nRows * nCols -1;
+                pix -= col_shift;
               }
           }
         break;
@@ -328,7 +379,8 @@
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
-              = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows);
+              = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
+                                                       col_cache, row_cache);
 
             octave_idx_type idx = 0;
             img_fvec += nRows * nCols * frame;
@@ -344,10 +396,10 @@
                     gbuf[idx]     = pix->green   / divisor;
                     bbuf[idx]     = pix->blue    / divisor;
                     a_fvec[a_idx] = pix->opacity / divisor;
-                    pix += nCols;
+                    pix += row_shift;
                     idx++;
                   }
-                pix -= nRows * nCols -1;
+                pix -= col_shift;
               }
           }
         retval(2) = alpha;
@@ -362,7 +414,8 @@
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
-              = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows);
+              = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
+                                                       col_cache, row_cache);
 
             octave_idx_type idx = 0;
             img_fvec += nRows * nCols * frame;
@@ -379,10 +432,10 @@
                     mbuf[idx] = pix->green   / divisor;
                     ybuf[idx] = pix->blue    / divisor;
                     kbuf[idx] = pix->opacity / divisor;
-                    pix += nCols;
+                    pix += row_shift;
                     idx++;
                   }
-                pix -= nRows * nCols -1;
+                pix -= col_shift;
               }
           }
         break;
@@ -402,7 +455,8 @@
         for (octave_idx_type frame = 0; frame < nFrames; frame++)
           {
             const Magick::PixelPacket *pix
-              = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows);
+              = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
+                                                       col_cache, row_cache);
             // Note that for CMYKColorspace + matte (CMYKA), the opacity is
             // stored in the assocated IndexPacket.
             const Magick::IndexPacket *apix
@@ -424,11 +478,11 @@
                     ybuf[idx]     = pix->blue    / divisor;
                     kbuf[idx]     = pix->opacity / divisor;
                     a_fvec[a_idx] = *apix / divisor;
-                    pix += nCols;
+                    pix += row_shift;
                     idx++;
                     a_idx++;
                   }
-                pix -= nRows * nCols -1;
+                pix -= col_shift;
               }
           }
         retval(2) = alpha;
@@ -524,7 +578,7 @@
       return output;
     }
 
-  const octave_map options = args(1).map_value ();
+  const octave_scalar_map options = args(1).scalar_map_value ();
   if (error_state)
     {
       error ("__magick_read__: OPTIONS must be a struct");
@@ -540,7 +594,7 @@
   // Prepare an Array with the indexes for the requested frames.
   const octave_idx_type nFrames = imvec.size ();
   Array<octave_idx_type> frameidx;
-  const octave_value indexes = options.getfield ("index")(0);
+  const octave_value indexes = options.getfield ("index");
   if (indexes.is_string () && indexes.string_value () == "all")
     {
       frameidx.resize (dim_vector (1, nFrames));
@@ -594,15 +648,15 @@
     {
       if (depth <= 1)
         {
-          output = read_indexed_images <boolNDArray> (imvec, frameidx, nargout);
+          output = read_indexed_images <boolNDArray> (imvec, frameidx, nargout, options);
         }
       else if (depth <= 8)
         {
-          output = read_indexed_images <uint8NDArray> (imvec, frameidx, nargout);
+          output = read_indexed_images <uint8NDArray> (imvec, frameidx, nargout, options);
         }
       else if (depth <= 16)
         {
-          output = read_indexed_images <uint16NDArray> (imvec, frameidx, nargout);
+          output = read_indexed_images <uint16NDArray> (imvec, frameidx, nargout, options);
         }
       else
         {
@@ -615,19 +669,19 @@
     {
       if (depth <= 1)
         {
-          output = read_images<boolNDArray> (imvec, frameidx, nargout);
+          output = read_images<boolNDArray> (imvec, frameidx, nargout, options);
         }
       else if (depth <= 8)
         {
-          output = read_images<uint8NDArray> (imvec, frameidx, nargout);
+          output = read_images<uint8NDArray> (imvec, frameidx, nargout, options);
         }
       else if (depth <= 16)
         {
-          output = read_images<uint16NDArray> (imvec, frameidx, nargout);
+          output = read_images<uint16NDArray> (imvec, frameidx, nargout, options);
         }
       else if (depth <= 32)
         {
-          output = read_images<FloatNDArray> (imvec, frameidx, nargout);
+          output = read_images<FloatNDArray> (imvec, frameidx, nargout, options);
         }
       else
         {
--- a/scripts/image/imread.m
+++ b/scripts/image/imread.m
@@ -65,6 +65,12 @@
 ## This option exists for @sc{Matlab} compatibility and has no effect.  For
 ## maximum performance while reading multiple images from a single file,
 ## use the Index option.
+##
+## @item "PixelRegion"
+## Controls the image region that is read.  Takes as value a cell array
+## with two arrays of 3 elements @code{@{@var{rows} @var{cols}@}}.  The
+## elements in the array are the start, increment and end pixel to be
+## read.  If the increment value is ommited, defaults to 1.
 ## @end table
 ##
 ## @seealso{imwrite, imfinfo, imformats}
--- a/scripts/image/private/__imread__.m
+++ b/scripts/image/private/__imread__.m
@@ -56,8 +56,10 @@
     error ("imread: cannot find %s", filename);
   endif
 
+  info = imfinfo (fn)(1);
   ## set default for options
-  options = struct ("index", 1);
+  options = struct ("index",       1,
+                    "region", {{1:1:info.Height 1:1:info.Width}});
 
   ## Index is the only option that can be defined without the parameter/value
   ## pair style. When defining it here, the string "all" is invalid though.
@@ -84,27 +86,31 @@
           error ("imread: value for %s must be a vector or the string `all'");
         endif
 
-## FIXME: commented until it's implemented in __magick_read__
-##      case "pixelregion",
-##        options.region = varargin{idx+1};
-##        if (! iscell (options.region) || numel (options.region) != 2)
-##          error ("imread: value for %s must be a 2 element cell array",
-##                 varargin{idx});
-##        endif
-##        for reg_idx = 1:2
-##          if (numel (options.region{reg_idx}) == 3)
-##            ## do nothing
-##          elseif (numel (options.region{reg_idx}) == 2)
-##            options.region{reg_idx}(3) = options.region{reg_idx}(2);
-##            options.region{reg_idx}(2) = 1;
-##          else
-##            error ("imread: range for %s must be a 2 or 3 element vector",
-##                   varargin{idx});
-##          endif
-##          options.region{reg_idx} = floor (options.region{reg_idx}(1)): ...
-##                                    floor (options.region{reg_idx}(2)): ...
-##                                    floor (options.region{reg_idx}(3));
-##        endfor
+      case "pixelregion",
+        options.region = varargin{idx+1};
+        if (! iscell (options.region) || numel (options.region) != 2)
+          error ("imread: value for %s must be a 2 element cell array",
+                 varargin{idx});
+        endif
+        for reg_idx = 1:2
+          if (numel (options.region{reg_idx}) == 3)
+            ## do nothing
+          elseif (numel (options.region{reg_idx}) == 2)
+            options.region{reg_idx}(3) = options.region{reg_idx}(2);
+            options.region{reg_idx}(2) = 1;
+          else
+            error ("imread: range for %s must be a 2 or 3 element vector",
+                   varargin{idx});
+          endif
+          options.region{reg_idx} = floor (options.region{reg_idx}(1)): ...
+                                    floor (options.region{reg_idx}(2)): ...
+                                    floor (options.region{reg_idx}(3));
+        endfor
+        if (options.region{1}(end) > info.Height)
+          error ("imread: end ROWS for PixelRegions option is larger than image height");
+        elseif (options.region{2}(end) > info.Width)
+          error ("imread: end COLS for PixelRegions option is larger than image width");
+        endif
 
       case "info",
         ## We ignore this option. This parameter exists in Matlab to