Mercurial > hg > octave-image
changeset 767:e2da2625ad8e
impixel: New function.
* impixel.m: New function.
* INDEX: update list with impixel.
* NEWS: reference new function impixel.
author | Carnë Draug <carandraug@octave.org> |
---|---|
date | Mon, 15 Jul 2013 05:39:38 +0100 |
parents | 95334351461e |
children | 8f13dc2ccfd4 |
files | INDEX NEWS inst/impixel.m |
diffstat | 3 files changed, 191 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/INDEX +++ b/INDEX @@ -105,6 +105,7 @@ col2im colfilt im2col + impixel nlfilter poly2mask roicolor
--- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ checkerboard cp2tform findbounds + impixel imtransform maketform montage
new file mode 100644 --- /dev/null +++ b/inst/impixel.m @@ -0,0 +1,189 @@ +## Copyright (C) 2013 Carnë Draug <carandraug@octave.org> +## +## This program is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free Software +## Foundation; either version 3 of the License, or (at your option) any later +## version. +## +## This program is distributed in the hope that it will be useful, but WITHOUT +## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +## details. +## +## You should have received a copy of the GNU General Public License along with +## this program; if not, see <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} {} impixel () +## @deftypefnx {Function File} {} impixel (@var{img}, @var{x}, @var{y}) +## @deftypefnx {Function File} {} impixel (@var{ind}, @var{map}, @var{x}, @var{y}) +## @deftypefnx {Function File} {} impixel (@var{xdata}, @var{ydata}, @var{img}, @var{x}, @var{y}) +## @deftypefnx {Function File} {} impixel (@var{xdata}, @var{ydata}, @var{ind}, @var{map}, @var{x}, @var{y}) +## @deftypefnx {Function File} {[@var{x}, @var{y}, @var{p}] =} impixel (@dots{}) +## Get pixel values. +## +## For any image @var{img}, or indexed image @var{ind} with colormap @var{map}, +## returns the pixel values at the image coordinates @var{x} and @var{y}. +## +## The 2 element vectors @var{xdata} and @var{ydata} can be used to set an +## alternative coordinate system. +## +## If more than one output argument is requested, also returns the @var{x} and +## @var{y} coordinates for the image. +## +## @itemize @bullet +## @item +## The pixel values are always returned in RGB style triples, even when +## @var{img} is a grayscale image. +## +## @item +## The value for pixel coordinates outside the image limits is NaN. +## +## @item +## Because a floating-point is required to represent a NaN, the pixel +## values will be of class double if input is double, and single otherwise. +## @end itemize +## +## @end deftypefn + +function varargout = impixel (varargin) + + if (nargin > 6) + print_usage (); + + ## interactive usage + elseif (nargin <= 2) + ## FIXME not yet implemented + print_usage (); + if (nargin == 0) + ## If using the current image, it is possible that xData and yData + ## were changed? We will confirm later is they were tampered with. + xData = get (gcf (), "xData"); + yData = get (gcf (), "yData"); + else + ## with given image, otherwise we will use current image + [img, map, is_indexed] = get_image (varargin{:}); + endif + + ## If only 2 output arguments are requested in interactive mode, then + ## only the coordinates are required, no need to do anything else. + if (nargout <= 2) + varargout(1:2) = {x y}; + return + endif + + ## non-interactive usage + else + x = varargin{end-1}; + y = varargin{end}; + if (! isnumeric (x) || ! isreal (x) || ! isnumeric (y) || ! isreal (y)) + error ("impixel: X and Y must be real numbers"); + endif + x = x(:); + y = y(:); + + if (nargin >= 5) + [img, map, is_indexed] = get_image (varargin{3:end-2}); + xData = varargin{1}; + yData = varargin{2}; + if (! isnumeric (xData) || ! isnumeric (yData)) + ## For Matlab compatibility we do not check if there's + ## only 2 elements, or if they are real numbers + error ("impixel: XDATA and YDATA must be numeric"); + endif + else + [img, map, is_indexed] = get_image (varargin{1:end-2}); + xData = 1:columns (img); + yData = 1:rows (img); + endif + + endif + + ## We need to return NaN if the requested pixels are outside the image + ## limits. interp2() will respect the input class, which means it will + ## return a 0 instead of NaN if the image is an integer class. Because + ## of that, we convert it to single. If the input image was double, then + ## we let it be. + if (isinteger (img)) + img = single (img); + if (is_indexed) + ## There's an offset in indexed images depending on their class. An + ## indexed image from integer class, matches the value 0 to row 1 of the + ## colormap. An indexed image from a float class, matches value 1 to + ## row 1. Since we are changing the class, we need to readjust it. + img++; + endif + endif + + xx = linspace (min (xData), max (xData), columns (img)); + yy = linspace (min (yData), max (yData), rows (img)); + data = interp2 (xx, yy, img(:,:,1), x, y, "nearest"); + if (ndims (img) == 3 && size (img, 3) == 3) + ## We can't use interp3() because XI and YI will be used to select entire + ## columns and vectors instead of matched coordinates + for ch = 2:3 + data(:,ch) = interp2 (xx, yy, img(:,:,ch), x, y, "nearest"); + endfor + endif + + if (is_indexed) + bad = isnan (data); + data(bad) = 1; + data = map(data(:),:); + data([bad bad bad]) = NA; + elseif (isvector (data)) + ## If we have a vector but the image was not indexed, it must have + ## been a grayscale image. We need to repeat the values into a Nx3 + ## matrix as if they were RGB values. + data = [data(:) data(:) data(:)]; + endif + + if (nargout > 1) + varargout(1:3) = {x y data} + else + varargout(1) = {data}; + endif + +endfunction + +function [img, map, is_indexed] = get_image (img, map = []) + + if (! isimage (img)) + error ("impixel: invalid image"); + endif + + is_indexed = false; + if (nargin > 2) + error ("impixel: too many input arguments"); + elseif (nargin == 2) + is_indexed = true; + if (! iscolormap (map)) + error ("impixel: invalid colormap"); + elseif (! isind (img)) + error ("impixel: invalid indexed image"); + endif + endif + +endfunction + +%!shared img2d, img3d +%! img2d = uint8 (magic (10)); +%! img3d(:,:,1) = img2d; +%! img3d(:,:,2) = img2d + 1; +%! img3d(:,:,3) = img2d + 2; +%! img3d = uint8 (img3d); +%! +%!assert (impixel (img2d, 2, 2), single ([80 80 80])); +%!assert (impixel (img2d, -2, 2), single ([NA NA NA])); +%! +%!assert (impixel (img2d, [1 10], [1 10]), single ([92 92 92; 59 59 59])); +%!assert (impixel (img3d, [1 10], [1 10]), single ([92 93 94; 59 60 61])); +%!assert (impixel (double (img2d), [1 10], [1 10]), [92 92 92; 59 59 59]); +%! +%!assert (impixel ([1 10], [1 10], img2d, [1 10], [1 10]), single ([92 92 92; 59 59 59])); +%!assert (impixel ([3 12], [-4 12], img2d, [1 10], [1 10]), single ([NA NA NA; 44 44 44])); +%!assert (impixel ([3 5], [-4 3], img2d, [1 10], [1 10]), single ([NA NA NA; NA NA NA])); +%! +%! ## the following returns double because it's an indexed image +%!assert (impixel ([3 12], [-4 12], img2d, gray (100), [1 10], [1 10]), [NA NA NA; 4/9 4/9 4/9]); +