# HG changeset patch # User Rik # Date 1354063093 28800 # Node ID 806ea52af230726dd93a2a4c92970ecd343e4425 # Parent 48e3841a7510cb76df82cc1f8bb82aa11680f0ae Overhaul m-files in image directory to provide better support for images stored as integers. * NEWS: Add note about overhaul of image scripts to support integer classes. * brighten.m: Add demo. * colormap.m: Better input validation. * contrast.m: Re-position window in demo. * gray2ind.m: Redo docstring. Match variables in docstring to function prototype. Better input validation. Return integer class outputs as Matlab does. Add %!tests. * hsv2rgb.m: Add to docstring. Better input validation. Redo algorithm to get rido of obsolete matrix-fill methods like kron(). Add %!tests. * image.m: Redo docstring. Match variables in docstring to function prototype. Better input validation without using for loops. * imagesc.m: Redo docstring. Match variables in docstring to function prototype. Remove DEPRECATEDZOOM functionality. Add demos. * imfinfo.m: Redo docstring. * ind2gray.m: Redo docstring. Match variables in docstring to function prototype. Redo algorithm to directly calculate luminance value rather than getting it from rgb2ntsc. Add %!tests. * ind2rgb.m: Redo docstring. Better input validation. Add some %!tests. * ntsc2rgb.m: Redo docstring. Better input validation. Add %!tests. * rgb2hsv.m: Better input validation. Add %!tests. * rgb2ind.m: Better input validation. Code algorithm in cleaner method for ease of understanding. * rgb2ntsc.m: Redo docstring: Better input validation. Add some %!tests. diff -r 48e3841a7510 -r 806ea52af230 NEWS --- a/NEWS Mon Nov 26 12:09:09 2012 -0500 +++ b/NEWS Tue Nov 27 16:38:13 2012 -0800 @@ -88,6 +88,13 @@ functions. Packages that implement extra colormaps should use these commands with PKG_ADD and PKG_DEL statements. + ** The m-files in the image directory have been overhauled. + The principal benefit is that Octave will now no longer automatically + convert images stored with integers to doubles. Storing images as uint8 + or uint16 requires only 1/8 or 1/4 the memory of an image stored using + doubles. For certain operations, such as fft2, the image must still be + converted to double in order to work. + ** The datevec function has been extended for better Matlab compatibility. It now accepts string inputs in the following numerical formats: 12, 21, 22, 26, 29, 31. This is undocumented, but verifiable, Matlab behavior. diff -r 48e3841a7510 -r 806ea52af230 scripts/image/brighten.m --- a/scripts/image/brighten.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/brighten.m Tue Nov 27 16:38:13 2012 -0800 @@ -33,6 +33,7 @@ ## @end deftypefn function rmap = brighten (arg1, beta) + h = -1; if (nargin == 1) beta = arg1; @@ -72,3 +73,22 @@ endif endfunction + + +%!demo +%! ## First figure uses default grayscale colormap +%! figure; +%! colormap (gray (64)); +%! image (1:64, linspace (0, 1, 64), repmat ((1:64)', 1, 64)); +%! axis ([1, 64, 0, 1], "ticy", "xy"); +%! title ("default grayscale colormap"); +%! pos = get (gcf, "position"); +%! pos(1) += pos(3) + 25; +%! ## Second figure uses brightened grayscale colormap +%! figure ("position", pos); +%! colormap (gray (64)); +%! image (1:64, linspace (0, 1, 64), repmat ((1:64)', 1, 64)); +%! axis ([1, 64, 0, 1], "ticy", "xy"); +%! brighten (0.5); +%! title ("grayscale colormap brightened by 0.5"); + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/colormap.m --- a/scripts/image/colormap.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/colormap.m Tue Nov 27 16:38:13 2012 -0800 @@ -68,11 +68,11 @@ endif if (! isempty (map)) - if (columns (map) != 3) - error ("colormap: MAP must have 3 columns: [R,G,B]"); + if (! ismatrix (map) || ndims (map) != 2 || columns (map) != 3) + error ("colormap: MAP must be an N x 3 ([R,G,B]) matrix"); endif - if (min (min (map)) < 0 || max (max (map)) > 1) - error ("colormap: MAP must have values in [0,1]"); + if (any (map(:) < 0) || any (map(:) > 1)) + error ("colormap: all MAP values must be in the range [0,1]"); endif ## Set the new color map set (gcf (), "colormap", map); @@ -96,3 +96,6 @@ endif endfunction + + +%% diff -r 48e3841a7510 -r 806ea52af230 scripts/image/contrast.m --- a/scripts/image/contrast.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/contrast.m Tue Nov 27 16:38:13 2012 -0800 @@ -48,13 +48,13 @@ %!demo -%! clf; +%! figure; %! img = reshape (1:100, 10, 10); %! imagesc (img); %! colormap (gray (64)); %! title ("Image with default 64 gray levels"); %! pos = get (gcf, "position"); -%! pos(1) += pos(3) + 15; +%! pos(1) += pos(3) + 25; %! figure ("position", pos); %! colormap (contrast (img, 10)); %! imagesc (img); diff -r 48e3841a7510 -r 806ea52af230 scripts/image/gray2ind.m --- a/scripts/image/gray2ind.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/gray2ind.m Tue Nov 27 16:38:13 2012 -0800 @@ -17,12 +17,19 @@ ## . ## -*- texinfo -*- -## @deftypefn {Function File} {[@var{img} =} gray2ind (@var{I}) -## @deftypefnx {Function File} {[@var{img} =} gray2ind (@var{I}, @var{n}) -## @deftypefnx {Function File} {[@var{img}, @var{map} =} gray2ind (@dots{}) -## Convert a gray scale intensity image to an Octave indexed image. +## @deftypefn {Function File} {@var{img} =} gray2ind (@var{I}) +## @deftypefnx {Function File} {@var{img} =} gray2ind (@var{I}, @var{n}) +## @deftypefnx {Function File} {@var{img} =} gray2ind (@var{BW}) +## @deftypefnx {Function File} {@var{img} =} gray2ind (@var{BW}, @var{n}) +## @deftypefnx {Function File} {[@var{img}, @var{map}] =} gray2ind (@dots{}) +## Convert a grayscale or binary intensity image to an indexed image. +## ## The indexed image will consist of @var{n} different intensity values. -## If not given @var{n} defaults to 64. +## If not given @var{n} defaults to 64 for grayscale images or 2 for +## binary black and white images. +## +## The output @var{img} is of class uint8 if @var{n} is less than or equal to +## 256. Otherwise, the output is of class uint16. ## @seealso{ind2gray, rgb2ind} ## @end deftypefn @@ -30,35 +37,74 @@ ## Created: July 1994 ## Adapted-By: jwe -function [X, map] = gray2ind (I, n = 64) - ## Check input +function [img, map] = gray2ind (I, n = 64) + if (nargin < 1 || nargin > 2) print_usage (); - endif - C = class (I); - if (! ismatrix (I) || ndims (I) != 2) - error ("gray2ind: first input argument must be a gray scale image"); + elseif (! isreal (I) || issparse (I)) + error ("gray2ind: I must be a grayscale or binary image"); + elseif (! isscalar (n) || n < 1 || n > 65536) + error ("gray2ind: N must be a positive integer in the range [1, 65536]"); endif - if (! isscalar (n) || n < 0) - error ("gray2ind: second input argument must be a positive integer"); - endif - ints = {"uint8", "uint16", "int8", "int16"}; - floats = {"double", "single"}; - if (! ismember (C, {ints{:}, floats{:}})) - error ("gray2ind: invalid data type '%s'", C); - endif - if (ismember (C, floats) && (min (I(:)) < 0 || max (I(:)) > 1)) + cls = class (I); + if (! any (isa (I, {"logical", "uint8", "uint16", "int16", "single", "double"}))) + error ("gray2ind: invalid data type '%s'", cls); + elseif (isfloat (I) && (any (I(:) < 0) || any (I(:) > 1))) error ("gray2ind: floating point images may only contain values between 0 and 1"); endif - ## Convert data + ## Create grayscale colormap + if (nargin == 1 && islogical (I)) + n = 2; + endif map = gray (n); - ## If @var{I} is an integer matrix convert it to a double matrix with values in [0, 1] - if (ismember (C, ints)) - low = double (intmin (C)); - high = double (intmax (C)); - I = (double (I) - low) / (high - low); + + ## Set up scale factor + if (isinteger (I)) + low = intmin (cls); + if (low != 0) + I -= low; # shift to zero-based indexing + endif + scale = double (intmax (cls)) - double (low); + else + scale = 1; # floating point doesn't need scaling endif - X = round (I*(n-1)) + 1; + + ## Scale image + img = I * ((n-1)/scale); + + ## Round and convert to appropriate output type. + ## Note: no separate call to round () necessary because + ## type conversion does that automatically. + if (n <= 256) + img = uint8 (img) + 1; + else + img = uint16 (img) + 1; + endif endfunction + + +%!assert (gray2ind ([0 0.25 0.5 1]), uint8 ([1 17 33 64])) +%!assert (gray2ind ([0 0.25 0.5 1], 400), uint16 ([1 101 201 400])) +%!assert (gray2ind (logical ([1 0 0 1])), uint8 ([2 1 1 2])) +%!assert (gray2ind (uint8 ([0 64 128 192 255])), uint8 ([1 17 33 48 64])) + +%!test +%! i2g = ind2gray (1:100, gray (100)); +%! g2i = gray2ind (i2g, 100); +%! assert (g2i, uint8 (1:100)); + +%% Test input validation +%!error gray2ind () +%!error gray2ind (1,2,3) +%!error gray2ind ({1}) +%!error gray2ind ([1+i]) +%!error gray2ind (sparse ([1])) +%!error gray2ind (1, ones (2,2)) +%!error gray2ind (1, 0) +%!error gray2ind (1, 65537) +%!error gray2ind (uint32 (1)) +%!error gray2ind (-1) +%!error gray2ind (2) + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/hsv2rgb.m --- a/scripts/image/hsv2rgb.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/hsv2rgb.m Tue Nov 27 16:38:13 2012 -0800 @@ -21,6 +21,13 @@ ## @deftypefnx {Function File} {@var{rgb_img} =} hsv2rgb (@var{hsv_img}) ## Transform a colormap or image from hue-saturation-value (HSV) space to ## red-green-blue (RGB) space. +## +## A color in HSV space is represented by hue, saturation and value +## (brightness) levels. Value gives the amount of light in the color. Hue +## describes the dominant wavelength. Saturation is the amount of hue mixed +## into the color. +## +## A color in the RGB space consists of red, green, and blue intensities. ## @seealso{rgb2hsv, ind2rgb, ntsc2rgb} ## @end deftypefn @@ -38,52 +45,83 @@ print_usage (); endif + cls = class (hsv_map); + if (! any (isa (hsv_map, {"uint8", "uint16", "single", "double"}))) + error ("hsv2rgb: invalid data type '%s'", cls); + elseif (isfloat (hsv_map) && (any (hsv_map(:) < 0) || any (hsv_map(:) > 1))) + error ("hsv2rgb: floating point images may only contain values between 0 and 1"); + endif + ## If we have an image convert it into a color map. - if (ismatrix (hsv_map) && ndims (hsv_map) == 3) + if (isreal (hsv_map) && ndims (hsv_map) == 3) is_image = true; - Sz = size (hsv_map); + sz = size (hsv_map); hsv_map = [hsv_map(:,:,1)(:), hsv_map(:,:,2)(:), hsv_map(:,:,3)(:)]; ## Convert to a double image. if (isinteger (hsv_map)) - C = class (hsv_map); - low = double (intmin (C)); - high = double (intmax (C)); + low = double (intmin (cls)); + high = double (intmax (cls)); hsv_map = (double (hsv_map) - low) / (high - low); endif else is_image = false; endif - if (! ismatrix (hsv_map) || columns (hsv_map) != 3) - error ("hsv2rgb: argument must be a matrix of size nx3"); + if (! isreal (hsv_map) || columns (hsv_map) != 3 || issparse (hsv_map)) + error ("hsv2rgb: input must be a matrix of size Nx3 or MxNx3"); endif - ## set values <0 to 0 and >1 to 1 - hsv_map = (hsv_map >= 0 & hsv_map <= 1) .* hsv_map ... - + (hsv_map < 0) .* 0 + (hsv_map > 1); - - ## fill rgb map with v*(1-s) - rgb_map = kron ([1, 1, 1], hsv_map(:,3) .* (1 - hsv_map(:,2))); + ## FIXME: Currently input is validated and an error results if values + ## are outside range [0, 1]. We could also simply allow those values + ## and re-instate this code to produce saturating semantics. + ## Trim map to range [0, 1] + #hsv_map(hsv_map < 0) = 0; + #hsv_map(hsv_map > 1) = 1; - ## red(hue-2/3)=green(hue)=blue(hue-1/3) - ## apply modulo 1 for red and blue - t = hsv_map(:,1); - tp = t'; - hue = [(tp - 2/3 - floor (t - 2/3)'); - tp; - (tp - 1/3 - floor (t - 1/3)')]'; + h = hsv_map(:,1); + s = hsv_map(:,2); + v = hsv_map(:,3); + ## Prefill rgb map with v*(1-s) + rgb_map = repmat (v .* (1 - s), 1, 3); + + ## red = hue-2/3 : green = hue : blue = hue-1/3 + ## Apply modulo 1 for red and blue to keep within range [0, 1] + hue = [mod(h - 2/3, 1), h , mod(h - 1/3, 1)]; ## factor s*v -> f - f = kron ([1, 1, 1], hsv_map(:,2)) .* kron ([1, 1, 1], hsv_map(:,3)); + f = repmat (s .* v, 1, 3); - ## add s*v* hue-function to rgb map - rgb_map = rgb_map + f .* (6 * (hue < 1/6) .* hue - + (hue >= 1/6 & hue < 1/2) - + (hue >= 1/2 & hue < 2/3) .* (4 - 6 * hue)); + ## add s*v*hue-function to rgb map + rgb_map += f .* (6 * (hue < 1/6) .* hue + + (hue >= 1/6 & hue < 1/2) + + (hue >= 1/2 & hue < 2/3) .* (4 - 6 * hue)); ## If input was an image, convert it back into one. if (is_image) - rgb_map = reshape (rgb_map, Sz); + rgb_map = reshape (rgb_map, sz); endif endfunction + + +%% Test pure colors +%!assert (hsv2rgb ([0 1 1]), [1 0 0]) +%!assert (hsv2rgb ([1 1 1]), [1 0 0]) +%!assert (hsv2rgb ([1/3 1 1]), [0 1 0]) +%!assert (hsv2rgb ([2/3 1 1]), [0 0 1]) + +%!test +%! hsv_map = rand (64, 3); +%! assert (rgb2hsv (hsv2rgb (hsv_map)), hsv_map, 1e-6); + +%!test +%! hsv_img = rand (64, 64, 3); +%! assert (rgb2hsv (hsv2rgb (hsv_img)), hsv_img, 1e-6); + +%% Test input validation +%!error hsv2rgb () +%!error hsv2rgb (1,2) +%!error hsv2rgb ({1}) +%!error hsv2rgb (ones (2,2)) +%!error hsv2rgb (sparse (ones(1,3))) + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/image.m --- a/scripts/image/image.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/image.m Tue Nov 27 16:38:13 2012 -0800 @@ -19,13 +19,19 @@ ## -*- texinfo -*- ## @deftypefn {Function File} {} image (@var{img}) ## @deftypefnx {Function File} {} image (@var{x}, @var{y}, @var{img}) +## @deftypefnx {Function File} {} image (@dots{}, "@var{property}", @var{value}, @dots{}) ## @deftypefnx {Function File} {@var{h} =} image (@dots{}) -## Display a matrix as a color image. +## Display a matrix as an indexed color image. ## ## The elements of @var{img} are indices into the current colormap. -## The axis values corresponding to the matrix elements are specified in -## @var{x} and @var{y}. If you are using gnuplot 4.1 or earlier, these -## variables are ignored. +## @var{x} and @var{y} are optional 2-element vectors, @w{@code{[min, max]}}, +## which specify the range for the axis labels. If a range is specified as +## @w{@code{[max, min]}} then the image will be reversed along that axis. For +## convenience, @var{x} and @var{y} may be specified as N-element vectors +## matching the length of the data in @var{img}. However, only the first and +## last elements will be used to determine the axis limits. +## @strong{Warning:} @var{x} and @var{y} are ignored when using gnuplot 4.0 +## or earlier. ## ## The optional return value @var{h} is a graphics handle to the image. ## @@ -36,7 +42,7 @@ ## @code{ydir} property to "reverse". This has implications whenever ## an image and an ordinary plot need to be overlaid. The recommended ## solution is to display the image and then plot the reversed ydata -## using, for example, @code{flipud (ydata,1)}. +## using, for example, @code{flipud (ydata)}. ## ## @seealso{imshow, imagesc, colormap} ## @end deftypefn @@ -45,49 +51,38 @@ ## Created: July 1994 ## Adapted-By: jwe -function retval = image (varargin) +function h = image (varargin) [ax, varargin, nargin] = __plt_get_axis_arg__ ("image", varargin{:}); - firstnonnumeric = Inf; - for i = 1 : nargin - if (! isnumeric (varargin{i})) - firstnonnumeric = i; - break; - endif - endfor - - if (nargin == 0 || firstnonnumeric == 1) + chararg = find (cellfun ("isclass", varargin, "char"), 1, "first"); + + if (nargin == 0 || chararg == 1) img = imread ("default.img"); x = y = []; - elseif (nargin == 1 || firstnonnumeric == 2) + elseif (nargin == 1 || chararg == 2) img = varargin{1}; x = y = []; - elseif (nargin == 2 || firstnonnumeric == 3) + elseif (nargin == 2 || chararg == 3) print_usage (); else x = varargin{1}; y = varargin{2}; img = varargin{3}; - firstnonnumeric = 4; - endif - - if (iscomplex (img)) - warning ("image: only showing real part of complex image"); - img = real (img); + chararg = 4; endif oldax = gca (); unwind_protect axes (ax); - h = __img__ (x, y, img, varargin {firstnonnumeric:end}); + htmp = __img__ (x, y, img, varargin{chararg:end}); set (ax, "layer", "top"); unwind_protect_cleanup axes (oldax); end_unwind_protect if (nargout > 0) - retval = h; + h = htmp; endif endfunction @@ -118,32 +113,36 @@ y = [1, rows(img)]; endif - xdata = [x(1), x(end)]; - ydata = [y(1), y(end)]; + xdata = x([1, end]); + ydata = y([1, end]); - dx = diff (x); - dy = diff (y); - dx = std (dx) / mean (abs (dx)); - dy = std (dy) / mean (abs (dy)); - tol = 100*eps; - if (any (dx > tol) || any (dy > tol)) - warning ("Image does not map to non-linearly spaced coordinates"); + if (numel (x) > 2 && numel (y) > 2) + ## Test data for non-linear spacing which is unsupported + ## FIXME: Need a better check on linearity + tol = 100*eps; + dx = diff (x); + dy = diff (y); + dx = std (dx) / mean (abs (dx)); + dy = std (dy) / mean (abs (dy)); + if (any (dx > tol) || any (dy > tol)) + warning ("image: non-linear X, Y data is ignored. IMG will be shown with linear mapping"); + endif endif ca = gca (); - tmp = __go_image__ (ca, "cdata", img, "xdata", xdata, "ydata", ydata, - "cdatamapping", "direct", varargin {:}); + htmp = __go_image__ (ca, "cdata", img, "xdata", xdata, "ydata", ydata, + "cdatamapping", "direct", varargin {:}); - px = __image_pixel_size__ (tmp); + px = __image_pixel_size__ (htmp); if (xdata(2) < xdata(1)) - xdata = xdata(2:-1:1); + xdata = fliplr (xdata); elseif (xdata(2) == xdata(1)) xdata = xdata(1) + [0, columns(img)-1]; endif if (ydata(2) < ydata(1)) - ydata = ydata(2:-1:1); + ydata = fliplr (ydata); elseif (ydata(2) == ydata(1)) ydata = ydata(1) + [0, rows(img)-1]; endif @@ -160,9 +159,9 @@ if (ndims (img) == 3) if (isinteger (img)) - c = class (img); - mn = intmin (c); - mx = intmax (c); + cls = class (img); + mn = intmin (cls); + mx = intmax (cls); set (ca, "clim", double ([mn, mx])); endif endif @@ -175,7 +174,7 @@ endif if (nargout > 0) - h = tmp; + h = htmp; endif endfunction @@ -183,66 +182,21 @@ %!demo %! clf; -%! colormap ("default"); +%! colormap (jet (21)); %! img = 1 ./ hilb (11); -%! x = -5:5; -%! y = x; +%! x = y = -5:5; %! subplot (2,2,1); -%! h = image (abs(x), abs(y), img); -%! set (h, "cdatamapping", "scaled"); -%! ylabel ("limits = [4.5, 15.5]"); -%! title ("image (abs(x), abs(y), img)"); +%! h = image (x, y, img); +%! ylabel ("limits = [-5.5, 5.5]"); +%! title ("image (x, y, img)"); %! subplot (2,2,2); %! h = image (-x, y, img); -%! set (h, "cdatamapping", "scaled"); %! title ("image (-x, y, img)"); %! subplot (2,2,3); %! h = image (x, -y, img); -%! set (h, "cdatamapping", "scaled"); %! title ("image (x, -y, img)"); %! ylabel ("limits = [-5.5, 5.5]"); %! subplot (2,2,4); %! h = image (-x, -y, img); -%! set (h, "cdatamapping", "scaled"); %! title ("image (-x, -y, img)"); -%!demo -%! clf; -%! colormap ("default"); -%! g = 0.1:0.1:10; -%! h = g'*g; -%! imagesc (g, g, sin (h)); -%! hold on; -%! imagesc (g, g+12, cos (h/2)); -%! axis ([0 10 0 22]); -%! hold off; -%! title ("two consecutive images"); - -%!demo -%! clf; -%! colormap ("default"); -%! g = 0.1:0.1:10; -%! h = g'*g; -%! imagesc (g, g, sin (h)); -%! hold all; -%! plot (g, 11.0 * ones (size (g))); -%! imagesc (g, g+12, cos (h/2)); -%! axis ([0 10 0 22]); -%! hold off; -%! title ("image, line, image"); - -%!demo -%! clf; -%! colormap ("default"); -%! g = 0.1:0.1:10; -%! h = g'*g; -%! plot (g, 10.5 * ones (size (g))); -%! hold all; -%! imagesc (g, g, sin (h)); -%! plot (g, 11.0 * ones (size (g))); -%! imagesc (g, g+12, cos (h/2)); -%! plot (g, 11.5 * ones (size (g))); -%! axis ([0 10 0 22]); -%! hold off; -%! title ("line, image, line, image, line"); - diff -r 48e3841a7510 -r 806ea52af230 scripts/image/imagesc.m --- a/scripts/image/imagesc.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/imagesc.m Tue Nov 27 16:38:13 2012 -0800 @@ -17,20 +17,20 @@ ## . ## -*- texinfo -*- -## @deftypefn {Function File} {} imagesc (@var{A}) -## @deftypefnx {Function File} {} imagesc (@var{x}, @var{y}, @var{A}) -## @deftypefnx {Function File} {} imagesc (@dots{}, @var{limits}) +## @deftypefn {Function File} {} imagesc (@var{img}) +## @deftypefnx {Function File} {} imagesc (@var{x}, @var{y}, @var{img}) +## @deftypefnx {Function File} {} imagesc (@dots{}, @var{climits}) ## @deftypefnx {Function File} {} imagesc (@var{h}, @dots{}) ## @deftypefnx {Function File} {@var{h} =} imagesc (@dots{}) -## Display a scaled version of the matrix @var{A} as a color image. The +## Display a scaled version of the matrix @var{img} as a color image. The ## colormap is scaled so that the entries of the matrix occupy the entire -## colormap. If @var{limits} = [@var{lo}, @var{hi}] are given, then that +## colormap. If @code{@var{climits} = [@var{lo}, @var{hi}]} is given, then that ## range is set to the "clim" of the current axes. ## ## The axis values corresponding to the matrix elements are specified in ## @var{x} and @var{y}, either as pairs giving the minimum and maximum ## values for the respective axes, or as values for each row and column -## of the matrix @var{A}. +## of the matrix @var{img}. ## ## The optional return value @var{h} is a graphics handle to the image. ## @seealso{image, imshow, caxis} @@ -40,82 +40,115 @@ ## Created: July 1994 ## Adapted-By: jwe -function retval = imagesc (varargin) +function h = imagesc (varargin) - if (nargin < 1) + if (nargin < 1 || nargin > 4) print_usage (); elseif (isscalar (varargin{1}) && ishandle (varargin{1})) - h = varargin{1}; - if (! strcmp (get (h, "type"), "axes")) + harg = varargin{1}; + if (! strcmp (get (harg, "type"), "axes")) error ("imagesc: expecting first argument to be an axes object"); endif oldh = gca (); unwind_protect axes (h); - tmp = __imagesc__ (h, varargin{2:end}); + htmp = __imagesc__ (harg, varargin{2:end}); unwind_protect_cleanup axes (oldh); end_unwind_protect else - tmp = __imagesc__ (gca (), varargin{:}); + htmp = __imagesc__ (gca (), varargin{:}); endif if (nargout > 0) - retval = tmp; + h = htmp; + endif + +endfunction + +function h = __imagesc__ (ax, x, y, img, climits) + + if (nargin == 2) + img = x; + x = y = climits = []; + elseif (nargin == 3) + img = x; + climits = y; + x = y = []; + elseif (nargin == 4 && ! isscalar (x) && ! isscalar (y) && ! isscalar (img)) + climits = []; + endif + + h = image (ax, x, y, img); + set (h, "cdatamapping", "scaled"); + + ## use given climits or guess them from the matrix + if (numel (climits) == 2 && climits(1) <= climits(2)) + set (ax, "clim", climits); + elseif (! isempty (climits)) + error ("imagesc: CLIMITS must be in form [lo, hi]"); endif endfunction -function ret = __imagesc__ (ax, x, y, A, limits, DEPRECATEDZOOM) + +%!demo +%! clf; +%! colormap ("default"); +%! img = 1 ./ hilb (11); +%! x = y = -5:5; +%! subplot (2,2,1); +%! h = imagesc (x, y, img); +%! ylabel ("limits = [-5.5, 5.5]"); +%! title ("imagesc (x, y, img)"); +%! subplot (2,2,2); +%! h = imagesc (-x, y, img); +%! title ("imagesc (-x, y, img)"); +%! subplot (2,2,3); +%! h = imagesc (x, -y, img); +%! title ("imagesc (x, -y, img)"); +%! ylabel ("limits = [-5.5, 5.5]"); +%! subplot (2,2,4); +%! h = imagesc (-x, -y, img); +%! title ("imagesc (-x, -y, img)"); - ## Deprecated zoom. Remove this hunk of code if old zoom argument - ## is outmoded. - if ((nargin == 3 && isscalar (y)) - || (nargin == 4 && (isscalar (y) || isscalar (A))) - || (nargin == 5 && isscalar (limits)) - || nargin == 6) - warning ("image: zoom argument ignored -- use GUI features"); - endif - if (nargin == 6) - if (isscalar (limits)) - limits = DEPRECATEDZOOM; - endif - nargin = 5; - endif - if (nargin == 5 && isscalar (limits)) - nargin = 4; - endif - if (nargin == 4 && (isscalar (y) || isscalar (A))) - if (isscalar (y)) - y = A; - endif - nargin = 3; - endif - if (nargin == 3 && isscalar (y)) - nargin = 2; - endif +%!demo +%! clf; +%! colormap ("default"); +%! g = 0.1:0.1:10; +%! h = g'*g; +%! imagesc (g, g, sin (h)); +%! hold on; +%! imagesc (g, g+12, cos (h/2)); +%! axis ([0 10 0 22]); +%! hold off; +%! title ("two consecutive images"); - if (nargin < 2 || nargin > 5) - print_usage (); - elseif (nargin == 2) - A = x; - x = y = limits = []; - elseif (nargin == 3) - A = x; - limits = y; - x = y = []; - elseif (nargin == 4 && ! isscalar (x) && ! isscalar (y) && ! isscalar (A)) - limits = []; - endif +%!demo +%! clf; +%! colormap ("default"); +%! g = 0.1:0.1:10; +%! h = g'*g; +%! imagesc (g, g, sin (h)); +%! hold all; +%! plot (g, 11.0 * ones (size (g))); +%! imagesc (g, g+12, cos (h/2)); +%! axis ([0 10 0 22]); +%! hold off; +%! title ("image, line, image"); - ret = image (ax, x, y, A); - set (ret, "cdatamapping", "scaled"); +%!demo +%! clf; +%! colormap ("default"); +%! g = 0.1:0.1:10; +%! h = g'*g; +%! plot (g, 10.5 * ones (size (g))); +%! hold all; +%! imagesc (g, g, sin (h)); +%! plot (g, 11.0 * ones (size (g))); +%! imagesc (g, g+12, cos (h/2)); +%! plot (g, 11.5 * ones (size (g))); +%! axis ([0 10 0 22]); +%! hold off; +%! title ("line, image, line, image, line"); - ## use given limits or guess them from the matrix - if (length (limits) == 2 && limits(2) >= limits(1)) - set (ax, "clim", limits); - elseif (! isempty (limits)) - error ("imagesc: expected data LIMITS to be [lo, hi]"); - endif - -endfunction diff -r 48e3841a7510 -r 806ea52af230 scripts/image/imfinfo.m --- a/scripts/image/imfinfo.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/imfinfo.m Tue Nov 27 16:38:13 2012 -0800 @@ -71,7 +71,7 @@ ## for. ## ## @item ByteOrder -## Endian option for formats that support it. Is either @code{"little-endian"}, +## Endian option for formats that support it. Value is @code{"little-endian"}, ## @code{"big-endian"}, or @code{"undefined"}. ## ## @item Gamma @@ -83,8 +83,7 @@ ## ## @item ModulusDepth ## Image modulus depth (minimum number of bits required to support -## red/green/blue -## components without loss of accuracy). +## red/green/blue components without loss of accuracy). ## ## @item Quality ## JPEG/MIFF/PNG compression level. @@ -93,19 +92,18 @@ ## Preferred number of colors in the image. ## ## @item ResolutionUnits -## Units of image resolution. Is either @code{"pixels per inch"}, +## Units of image resolution. Value is @code{"pixels per inch"}, ## @code{"pixels per centimeter"}, or @code{"undefined"}. ## ## @item ColorType -## Image type. Is either @code{"grayscale"}, @code{"indexed"}, -## @code{"truecolor"}, -## or @code{"undefined"}. +## Image type. Value is @code{"grayscale"}, @code{"indexed"}, +## @code{"truecolor"}, or @code{"undefined"}. ## ## @item View ## FlashPix viewing parameters. ## @end table ## -## @seealso{imread, imwrite} +## @seealso{imread, imwrite, imshow} ## @end deftypefn function info = imfinfo (filename) @@ -114,7 +112,7 @@ print_usage (); endif - if (!ischar (filename)) + if (! ischar (filename)) error ("imfinfo: FILENAME must be a string"); endif diff -r 48e3841a7510 -r 806ea52af230 scripts/image/ind2gray.m --- a/scripts/image/ind2gray.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/ind2gray.m Tue Nov 27 16:38:13 2012 -0800 @@ -17,11 +17,20 @@ ## . ## -*- texinfo -*- -## @deftypefn {Function File} {} ind2gray (@var{x}) -## @deftypefnx {Function File} {} ind2gray (@var{x}, @var{map}) -## Convert a color indexed image to a gray scale intensity image. +## @deftypefn {Function File} {@var{I} =} ind2gray (@var{x}) +## @deftypefnx {Function File} {@var{I} =} ind2gray (@var{x}, @var{map}) +## Convert a color indexed image to a grayscale intensity image. ## If @var{map} is omitted, the current colormap is used to determine the ## intensities. +## +## The output @var{I} is of the same class as the input @var{x} and may be +## one of @code{uint8}, @code{uint16}, @code{single}, or @code{double}. +## +## Implementation Note: There are several ways of converting colors to +## grayscale intensities. This functions uses the luminance value obtained +## from @code{rgb2ntsc} which is @code{I = 0.299*R + 0.587*G + 0.114*B}. +## Other possibilities include the value component from @code{rgb2hsv} or +## using a single color channel from @code{ind2rgb}. ## @seealso{gray2ind, ind2rgb} ## @end deftypefn @@ -29,22 +38,62 @@ ## Created: July 1994 ## Adapted-By: jwe -function y = ind2gray (x, map) +function I = ind2gray (x, map) if (nargin < 1 || nargin > 2) print_usage (); - elseif (nargin == 1) + endif + + if (! isreal (x) || issparse (x) + || (isfloat (x) && (any (x(:) < 1 || any (x(:) != fix (x(:))))))) + error ("ind2gray: X must be an indexed image"); + endif + cls = class (x); + if (! any (isa (x, {"logical", "uint8", "uint16", "single", "double"}))) + error ("ind2gray: invalid data type '%s'", cls); + endif + + if (nargin == 1) map = colormap (); + elseif (! iscolormap (map)) + error ("ind2gray: MAP must be a valid colormap"); + endif + + ## Convert colormap to luminance intensity values + map *= [0.299; 0.587; 0.114]; + + ## Convert colormap to same class as that of input so that + ## indexing in colormap will produce output of the same type as the input. + if (isinteger (x)) + map *= intmax (cls); + elseif (strcmp (cls, "single")) + map = single (map); endif - [rows, cols] = size (x); - - ## Convert colormap to intensity values (the first column of the - ## result of the call to rgb2ntsc) and then replace indices in - ## the input matrix with indexed values in the output matrix (indexed - ## values are the result of indexing the intensity values by the - ## elements of x(:)). - - y = reshape (((rgb2ntsc (map))(:,1))(x(:)), rows, cols); + ## Replace indices in the input matrix with the indexed luminance value. + I = reshape (map(x(:)), size (x)); endfunction + + +%!test +%! i2g = ind2gray (1:100, gray (100)); +%! assert (i2g, 0:1/99:1, eps); +%! g2i = gray2ind (i2g, 100); +%! assert (g2i, uint8 (1:100)); + +%%test input validation +%!error ind2gray () +%!error ind2gray (1,2,3) +%!error ind2gray ({1}) +%!error ind2gray (1+i) +%!error ind2gray (sparse (1)) +%!error ind2gray (0) +%!error ind2gray (1.1) +%!error ind2gray (1, {1}) +%!error ind2gray (1, 1+i) +%!error ind2gray (1, ones (2,2,2)) +%!error ind2gray (1, ones (2,4)) +%!error ind2gray (1, [-1]) +%!error ind2gray (1, [2]) + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/ind2rgb.m --- a/scripts/image/ind2rgb.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/ind2rgb.m Tue Nov 27 16:38:13 2012 -0800 @@ -21,9 +21,14 @@ ## @deftypefnx {Function File} {@var{rgb} =} ind2rgb (@var{x}, @var{map}) ## @deftypefnx {Function File} {[@var{R}, @var{G}, @var{B}] =} ind2rgb (@dots{}) ## Convert an indexed image to red, green, and blue color components. -## If the colormap doesn't contain enough colors, pad it with the -## last color in the map. ## If @var{map} is omitted, the current colormap is used for the conversion. +## When the colormap does not contain enough colors it is padded to the +## required length using the last color in the map. +## +## The output may be a single MxNx3 matrix where M is the number of rows in +## @var{x} and N is the number of columns in @var{x}. Alternatively, +## individual red, green, and blue color matrices of size MxN may be +## returned. ## @seealso{rgb2ind, ind2gray, hsv2rgb, ntsc2rgb} ## @end deftypefn @@ -33,20 +38,22 @@ function [R, G, B] = ind2rgb (x, map) - ## Do we have the right number of inputs? if (nargin < 1 || nargin > 2) print_usage (); - elseif (nargin == 1) - map = colormap (); endif - ## Check if X is an indexed image. - if (ndims (x) != 2 || any (x(:) != fix (x(:))) || min (x(:)) < 1) + if (! isreal (x) || issparse (x) + || (isfloat (x) && (any (x(:) < 1 || any (x(:) != fix (x(:))))))) error ("ind2rgb: X must be an indexed image"); endif + cls = class (x); + if (! any (isa (x, {"logical", "uint8", "uint16", "single", "double"}))) + error ("ind2rgb: invalid data type '%s'", cls); + endif - ## Check the color map. - if (! iscolormap (map)) + if (nargin == 1) + map = colormap (); + elseif (! iscolormap (map)) error ("ind2rgb: MAP must be a valid colormap"); endif @@ -60,14 +67,34 @@ endif ## Compute result - [hi, wi] = size (x); - R = reshape (map (x(:), 1), hi, wi); - G = reshape (map (x(:), 2), hi, wi); - B = reshape (map (x(:), 3), hi, wi); + [row, col] = size (x); + R = reshape (map(x(:), 1), row, col); + G = reshape (map(x(:), 2), row, col); + B = reshape (map(x(:), 3), row, col); ## Use 3D array if only one output is requested. if (nargout <= 1) + R(:,:,2) = G; R(:,:,3) = B; - R(:,:,2) = G; endif + endfunction + + +%% FIXME: Need some functional tests or %!demo blocks + +%%test input validation +%!error ind2rgb () +%!error ind2rgb (1,2,3) +%!error ind2rgb ({1}) +%!error ind2rgb (1+i) +%!error ind2rgb (sparse (1)) +%!error ind2rgb (0) +%!error ind2rgb (1.1) +%!error ind2rgb (1, {1}) +%!error ind2rgb (1, 1+i) +%!error ind2rgb (1, ones (2,2,2)) +%!error ind2rgb (1, ones (2,4)) +%!error ind2rgb (1, [-1]) +%!error ind2rgb (1, [2]) + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/ntsc2rgb.m --- a/scripts/image/ntsc2rgb.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/ntsc2rgb.m Tue Nov 27 16:38:13 2012 -0800 @@ -20,7 +20,18 @@ ## @deftypefn {Function File} {@var{rgb_map} =} ntsc2rgb (@var{yiq_map}) ## @deftypefnx {Function File} {@var{rgb_img} =} ntsc2rgb (@var{yiq_img}) ## Transform a colormap or image from luminance-chrominance (NTSC) space to -## red-green-blue (RGB) space. +## red-green-blue (RGB) color space. +## +## Implementation Note: +## The conversion matrix is chosen to be the inverse of the +## matrix used for rgb2ntsc such that +## +## @example +## x == ntsc2rgb (rgb2ntsc (x)) +## @end example +## +## @sc{matlab} uses a slightly different matrix where rounding +## means the equality above does not hold. ## @seealso{rgb2ntsc, hsv2rgb, ind2rgb} ## @end deftypefn @@ -34,16 +45,21 @@ print_usage (); endif + cls = class (yiq); + if (! any (isa (yiq, {"uint8", "uint16", "single", "double"}))) + error ("ntsc2rgb: invalid data type '%s'", cls); + endif + ## If we have an image convert it into a color map. if (ismatrix (yiq) && ndims (yiq) == 3) is_image = true; - Sz = size (yiq); + sz = size (yiq); yiq = [yiq(:,:,1)(:), yiq(:,:,2)(:), yiq(:,:,3)(:)]; ## Convert to a double image. if (isinteger (yiq)) - C = class (yiq); - low = double (intmin (C)); - high = double (intmax (C)); + cls = class (yiq); + low = double (intmin (cls)); + high = double (intmax (cls)); yiq = (double (yiq) - low) / (high - low); endif else @@ -54,16 +70,44 @@ error ("ntsc2rgb: argument must be a matrix of size Nx3 or NxMx3"); endif - ## Convert data + ## Conversion matrix constructed from 'inv (rgb2ntsc matrix)'. See + ## programming notes in rgb2ntsc.m. Note: Matlab matrix for inverse + ## is slightly different. We prefer this matrix so that + ## x == ntsc2rgb (rgb2ntsc (x)) rather than maintaining strict compatibility + ## with Matlab. trans = [ 1.0, 1.0, 1.0; 0.95617, -0.27269, -1.10374; - 0.62143, -0.64681, 1.70062 ]; + 0.62143, -0.64681, 1.70062 ]; rgb = yiq * trans; ## If input was an image, convert it back into one. if (is_image) - rgb = reshape (rgb, Sz); + rgb = reshape (rgb, sz); endif endfunction + + +%!test +%! rgb_map = rand (64, 3); +%! assert (ntsc2rgb (rgb2ntsc (rgb_map)), rgb_map, 1e-3); + +%!test +%! rgb_img = rand (64, 64, 3); +%! assert (ntsc2rgb (rgb2ntsc (rgb_img)), rgb_img, 1e-3); + +%%!test +%%! ntsc_map = rand (64, 3); +%%! assert (rgb2ntsc (ntsc2rgb (ntsc_map)), ntsc_map, 1e-3); +% +%%!test +%%! ntsc_img = rand (64, 64, 3); +%%! assert (rgb2ntsc (ntsc2rgb (ntsc_img)), ntsc_img, 1e-3); + +%% Test input validation +%!error ntsc2rgb () +%!error ntsc2rgb (1,2) +%!error ntsc2rgb ({1}) +%!error ntsc2rgb (ones (2,2)) + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/rgb2hsv.m --- a/scripts/image/rgb2hsv.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/rgb2hsv.m Tue Nov 27 16:38:13 2012 -0800 @@ -40,32 +40,39 @@ print_usage (); endif + cls = class (rgb); + if (! any (isa (rgb, {"uint8", "uint16", "single", "double"}))) + error ("rgb2hsv: invalid data type '%s'", cls); + elseif (isfloat (rgb) && (any (rgb(:) < 0) || any (rgb(:) > 1))) + error ("rgb2hsv: floating point images may only contain values between 0 and 1"); + endif + ## If we have an image convert it into a color map. - if (ismatrix (rgb) && ndims (rgb) == 3) + if (isreal (rgb) && ndims (rgb) == 3) is_image = true; - Sz = size (rgb); + sz = size (rgb); rgb = [rgb(:,:,1)(:), rgb(:,:,2)(:), rgb(:,:,3)(:)]; ## Convert to a double image. if (isinteger (rgb)) - C = class (rgb); - low = double (intmin (C)); - high = double (intmax (C)); + cls = class (rgb); + low = double (intmin (cls)); + high = double (intmax (cls)); rgb = (double (rgb) - low) / (high - low); endif else is_image = false; endif - if (! ismatrix (rgb) || columns (rgb) != 3) - error ("rgb2hsv: RGB_MAP must be a matrix of size n x 3"); + if (! ismatrix (rgb) || columns (rgb) != 3 || issparse (rgb)) + error ("rgb2hsv: input must be a matrix of size Nx3"); endif - ## get the max and min - s = min (rgb')'; - v = max (rgb')'; + ## get the max and min for each row + s = min (rgb, [], 2); + v = max (rgb, [], 2); ## set hue to zero for undefined values (gray has no hue) - h = zeros (size (v)); + h = zeros (rows (rgb), 1); notgray = (s != v); ## blue hue @@ -85,10 +92,7 @@ if (any (idx)) h(idx) = 1/6 * (rgb(idx,2) - rgb(idx,3)) ./ (v(idx) - s(idx)); endif - - ## correct for negative red - idx = (h < 0); - h(idx) = 1+h(idx); + h(h < 0) += 1; # correct for negative red ## set the saturation s(! notgray) = 0; @@ -98,7 +102,29 @@ ## If input was an image, convert it back into one. if (is_image) - hsv_map = reshape (hsv_map, Sz); + hsv_map = reshape (hsv_map, sz); endif endfunction + + +%% Test pure colors and gray +%!assert (rgb2hsv ([1 0 0]), [0 1 1]) +%!assert (rgb2hsv ([0 1 0]), [1/3 1 1]) +%!assert (rgb2hsv ([0 0 1]), [2/3 1 1]) +%!assert (rgb2hsv ([0.5 0.5 0.5]), [0 0 0.5]) + +%!test +%! rgb_map = rand (64, 3); +%! assert (hsv2rgb (rgb2hsv (rgb_map)), rgb_map, 1e-6); + +%!test +%! rgb_img = rand (64, 64, 3); +%! assert (hsv2rgb (rgb2hsv (rgb_img)), rgb_img, 1e-6); + +%% Test input validation +%!error rgb2hsv () +%!error rgb2hsv (1,2) +%!error rgb2hsv ({1}) +%!error rgb2hsv (ones (2,2)) + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/rgb2ind.m --- a/scripts/image/rgb2ind.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/rgb2ind.m Tue Nov 27 16:38:13 2012 -0800 @@ -24,7 +24,8 @@ ## @end deftypefn ## Bugs: The color map may have duplicate entries. - +## FIXME: This function has a very different syntax than the Matlab one of the same name. +## Octave function does no support N, MAP, DITHER, or TOL arguments ## Author: Tony Richardson ## Created: July 1994 ## Adapted-By: jwe @@ -37,29 +38,28 @@ if (nargin == 1) rgb = R; - if (length (size (rgb)) == 3 && size (rgb, 3) == 3) + if (ndims (rgb) != 3 || size (rgb, 3) != 3) + error ("rgb2ind: argument is not an RGB image"); + else R = rgb(:,:,1); G = rgb(:,:,2); B = rgb(:,:,3); - else - error ("rgb2ind: argument is not an RGB image"); endif - endif - - if (! size_equal (R, G) || ! size_equal (R, B)) - error ("rgb2ind: arguments must all have the same size"); + elseif (! size_equal (R, G, B)) + error ("rgb2ind: R, G, and B must have the same size"); endif - [hi, wi] = size (R); - - x = zeros (hi, wi); - - map = zeros (hi*wi, 3); + x = reshape (1:numel (R), size (R)); - map(:,1) = R(:); - map(:,2) = G(:); - map(:,3) = B(:); - - x(:) = 1:(hi*wi); + map = [R(:), G(:), B(:)]; endfunction + + +%% FIXME: Need some functional tests or %!demo blocks + +%% Test input validation +%!error rgb2ind () +%!error rgb2ind (1,2) +%!error rgb2ind (1,2,3,4) + diff -r 48e3841a7510 -r 806ea52af230 scripts/image/rgb2ntsc.m --- a/scripts/image/rgb2ntsc.m Mon Nov 26 12:09:09 2012 -0500 +++ b/scripts/image/rgb2ntsc.m Tue Nov 27 16:38:13 2012 -0800 @@ -19,8 +19,24 @@ ## -*- texinfo -*- ## @deftypefn {Function File} {@var{yiq_map} =} rgb2ntsc (@var{rgb_map}) ## @deftypefnx {Function File} {@var{yiq_img} =} rgb2ntsc (@var{rgb_img}) -## Transform a colormap or image from red-green-blue (RGB) space to +## Transform a colormap or image from red-green-blue (RGB) color space to ## luminance-chrominance (NTSC) space. +## +## Implementation Note: +## The reference matrix for the transformation is +## +## @example +## @group +## /Y\ 0.299 0.587 0.114 /R\ +## |I| = 0.596 -0.274 -0.322 |G| +## \Q/ 0.211 -0.523 0.312 \B/ +## @end group +## @end example +## +## @noindent +## as documented in @url{http://en.wikipedia.org/wiki/YIQ} and truncated to 3 +## significant figures. Note: The FCC version of NTSC uses only 2 +## significant digits and is slightly different. ## @seealso{ntsc2rgb, rgb2hsv, rgb2ind} ## @end deftypefn @@ -34,36 +50,64 @@ print_usage (); endif + cls = class (rgb); + if (! any (isa (rgb, {"uint8", "uint16", "single", "double"}))) + error ("rgb2ntsc: invalid data type '%s'", cls); + elseif (isfloat (rgb) && (any (rgb(:) < 0) || any (rgb(:) > 1))) + error ("rgb2ntsc: floating point images may only contain values between 0 and 1"); + endif + ## If we have an image convert it into a color map. - if (ismatrix (rgb) && ndims (rgb) == 3) + if (isreal (rgb) && ndims (rgb) == 3) is_image = true; - Sz = size (rgb); + sz = size (rgb); rgb = [rgb(:,:,1)(:), rgb(:,:,2)(:), rgb(:,:,3)(:)]; ## Convert to a double image. if (isinteger (rgb)) - C = class (rgb); - low = double (intmin (C)); - high = double (intmax (C)); + low = double (intmin (cls)); + high = double (intmax (cls)); rgb = (double (rgb) - low) / (high - low); endif else is_image = false; endif - if (! ismatrix (rgb) || columns (rgb) != 3) + if (! ismatrix (rgb) || columns (rgb) != 3 || issparse (rgb)) error ("rgb2ntsc: argument must be a matrix of size Nx3 or NxMx3"); endif - ## Convert data + ## Reference matrix for transformation from http://en.wikipedia.org/wiki/YIQ + ## and truncated to 3 significant figures. Matlab uses this matrix for their + ## conversion. trans = [ 0.299, 0.596, 0.211; 0.587, -0.274, -0.523; 0.114, -0.322, 0.312 ]; + ## Convert data. yiq = rgb * trans; ## If input was an image, convert it back into one. if (is_image) - yiq = reshape (yiq, Sz); + yiq = reshape (yiq, sz); endif endfunction + + +%% Test RED conversion +%assert (rgb2ntsc ([1 0 0]), [0.299 0.587 0.114]) + +%!test +%! rgb_map = rand (64, 3); +%! assert (ntsc2rgb (rgb2ntsc (rgb_map)), rgb_map, 1e-3); + +%!test +%! rgb_img = rand (64, 64, 3); +%! assert (ntsc2rgb (rgb2ntsc (rgb_img)), rgb_img, 1e-3); + +%% Test input validation +%!error rgb2ntsc () +%!error rgb2ntsc (1,2) +%!error rgb2ntsc ({1}) +%!error rgb2ntsc (ones (2,2)) +