changeset 15683:806ea52af230

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.
author Rik <rik@octave.org>
date Tue, 27 Nov 2012 16:38:13 -0800
parents 48e3841a7510
children 18f8f2987205 a1b634240352 2fac72a256ce
files NEWS scripts/image/brighten.m scripts/image/colormap.m scripts/image/contrast.m scripts/image/gray2ind.m scripts/image/hsv2rgb.m scripts/image/image.m scripts/image/imagesc.m scripts/image/imfinfo.m scripts/image/ind2gray.m scripts/image/ind2rgb.m scripts/image/ntsc2rgb.m scripts/image/rgb2hsv.m scripts/image/rgb2ind.m scripts/image/rgb2ntsc.m
diffstat 15 files changed, 591 insertions(+), 302 deletions(-) [+]
line wrap: on
line diff
--- 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.
--- 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");
+
--- 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
+
+
+%% 
--- 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);
--- 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 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- 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 <I must be a grayscale or binary image> gray2ind ({1})
+%!error <I must be a grayscale or binary image> gray2ind ([1+i])
+%!error <I must be a grayscale or binary image> gray2ind (sparse ([1]))
+%!error <N must be a positive integer> gray2ind (1, ones (2,2))
+%!error <N must be a positive integer> gray2ind (1, 0)
+%!error <N must be a positive integer> gray2ind (1, 65537)
+%!error <invalid data type> gray2ind (uint32 (1))
+%!error <values between 0 and 1> gray2ind (-1)
+%!error <values between 0 and 1> gray2ind (2)
+
--- 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 <invalid data type> hsv2rgb ({1})
+%!error <must be a matrix of size Nx3> hsv2rgb (ones (2,2))
+%!error <must be a matrix of size Nx3> hsv2rgb (sparse (ones(1,3)))
+
--- 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");
-
--- 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 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- 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
--- 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
 
--- 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 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- 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 <X must be an indexed image> ind2gray ({1})
+%!error <X must be an indexed image> ind2gray (1+i)
+%!error <X must be an indexed image> ind2gray (sparse (1))
+%!error <X must be an indexed image> ind2gray (0)
+%!error <X must be an indexed image> ind2gray (1.1)
+%!error <MAP must be a valid colormap> ind2gray (1, {1})
+%!error <MAP must be a valid colormap> ind2gray (1, 1+i)
+%!error <MAP must be a valid colormap> ind2gray (1, ones (2,2,2))
+%!error <MAP must be a valid colormap> ind2gray (1, ones (2,4))
+%!error <MAP must be a valid colormap> ind2gray (1, [-1])
+%!error <MAP must be a valid colormap> ind2gray (1, [2])
+
--- 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 <X must be an indexed image> ind2rgb ({1})
+%!error <X must be an indexed image> ind2rgb (1+i)
+%!error <X must be an indexed image> ind2rgb (sparse (1))
+%!error <X must be an indexed image> ind2rgb (0)
+%!error <X must be an indexed image> ind2rgb (1.1)
+%!error <MAP must be a valid colormap> ind2rgb (1, {1})
+%!error <MAP must be a valid colormap> ind2rgb (1, 1+i)
+%!error <MAP must be a valid colormap> ind2rgb (1, ones (2,2,2))
+%!error <MAP must be a valid colormap> ind2rgb (1, ones (2,4))
+%!error <MAP must be a valid colormap> ind2rgb (1, [-1])
+%!error <MAP must be a valid colormap> ind2rgb (1, [2])
+
--- 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 <invalid data type 'cell'> ntsc2rgb ({1})
+%!error <must be a matrix of size Nx3 or NxMx3> ntsc2rgb (ones (2,2))
+
--- 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 <invalid data type 'cell'> rgb2hsv ({1})
+%!error <must be a matrix of size Nx3> rgb2hsv (ones (2,2))
+
--- 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 <arichard@stark.cc.oh.us>
 ## 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)
+
--- 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 <invalid data type 'cell'> rgb2ntsc ({1})
+%!error <must be a matrix of size Nx3 or NxMx3> rgb2ntsc (ones (2,2))
+