# HG changeset patch # User Carnë Draug # Date 1375302528 -3600 # Node ID c97a26408ee0caee4359b99fb7467d911dadb369 # Parent 47b504503a3fd207f068361b7e7b1854a0b8a6b3 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. diff -r 47b504503a3f -r c97a26408ee0 libinterp/dldfcn/__magick_read__.cc --- a/libinterp/dldfcn/__magick_read__.cc Wed Jul 31 21:25:04 2013 +0200 +++ b/libinterp/dldfcn/__magick_read__.cc Wed Jul 31 21:28:48 2013 +0100 @@ -44,30 +44,69 @@ #include #include +static std::map +calculate_region (const octave_scalar_map& options) +{ + std::map 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 static octave_value_list read_indexed_images (std::vector& imvec, const Array& 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 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

(*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& imvec, const Array& 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 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 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 (imvec, frameidx, nargout); + output = read_indexed_images (imvec, frameidx, nargout, options); } else if (depth <= 8) { - output = read_indexed_images (imvec, frameidx, nargout); + output = read_indexed_images (imvec, frameidx, nargout, options); } else if (depth <= 16) { - output = read_indexed_images (imvec, frameidx, nargout); + output = read_indexed_images (imvec, frameidx, nargout, options); } else { @@ -615,19 +669,19 @@ { if (depth <= 1) { - output = read_images (imvec, frameidx, nargout); + output = read_images (imvec, frameidx, nargout, options); } else if (depth <= 8) { - output = read_images (imvec, frameidx, nargout); + output = read_images (imvec, frameidx, nargout, options); } else if (depth <= 16) { - output = read_images (imvec, frameidx, nargout); + output = read_images (imvec, frameidx, nargout, options); } else if (depth <= 32) { - output = read_images (imvec, frameidx, nargout); + output = read_images (imvec, frameidx, nargout, options); } else { diff -r 47b504503a3f -r c97a26408ee0 scripts/image/imread.m --- a/scripts/image/imread.m Wed Jul 31 21:25:04 2013 +0200 +++ b/scripts/image/imread.m Wed Jul 31 21:28:48 2013 +0100 @@ -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} diff -r 47b504503a3f -r c97a26408ee0 scripts/image/private/__imread__.m --- a/scripts/image/private/__imread__.m Wed Jul 31 21:25:04 2013 +0200 +++ b/scripts/image/private/__imread__.m Wed Jul 31 21:28:48 2013 +0100 @@ -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