changeset 15693:a1b634240352

maint: merge default into image-overhaul head, specially changeset 806ea52. Resolving many conflicts and fixing bugs related to correctly indexing on image class (float vs integer have different offset)
author Carnë Draug <carandraug+dev@gmail.com>
date Wed, 28 Nov 2012 06:01:09 +0100
parents 37a21f615d67 (diff) 806ea52af230 (current diff)
children 0f924f0b0be3
files scripts/image/gray2ind.m scripts/image/ind2gray.m scripts/image/ind2rgb.m scripts/image/private/ind2x.m scripts/image/rgb2ind.m
diffstat 5 files changed, 152 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/image/gray2ind.m	Tue Nov 27 16:38:13 2012 -0800
+++ b/scripts/image/gray2ind.m	Wed Nov 28 06:01:09 2012 +0100
@@ -28,16 +28,16 @@
 ## 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} 
+## The output @var{img} is of class uint8 or uint 16 if @var{n} is less than or
+## equal to 256 or 65536 respectively.  Otherwise, the output is of class double.
+## @seealso{ind2gray, rgb2ind}
 ## @end deftypefn
 
 ## Author: Tony Richardson <arichard@stark.cc.oh.us>
 ## Created: July 1994
 ## Adapted-By: jwe
 
-function [img, map] = gray2ind (I, n = 64)
+function [I, map] = gray2ind (I, n = 64)
 
   if (nargin < 1 || nargin > 2)
     print_usage ();
@@ -45,55 +45,59 @@
     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]");
+  elseif (! ismatrix (I) || ndims (I) != 2)
+    error ("gray2ind: first input argument must be a gray scale image");
   endif
+
+  ## default n is different if image is logical
+  if (nargin == 1 && islogical (I))
+    n = 2;
+  endif
+
+  if (! isscalar (n) || n < 0)
+    error ("gray2ind: second input argument must be a positive integer");
+  endif
+
   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)))
+  elseif (isfloat (I) && (min (I(:) < 0) || max (I(:) > 1)))
     error ("gray2ind: floating point images may only contain values between 0 and 1");
   endif
 
-  ## Create grayscale colormap
-  if (nargin == 1 && islogical (I))
-    n = 2;
-  endif
   map = gray (n);
 
   ## 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);
+    low   = double (intmin (cls));
+    scale = double (intmax (cls)) - low;
+    I = double (I) - low;
   else
-    scale = 1;  # floating point doesn't need scaling
+    scale = 1;
   endif
-
-  ## 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;
+  I = I * ((n-1)/scale);
+  if (n < 256)
+    I = uint8 (I);
+  elseif (n < 65536)
+    I = uint16 (I);
   else
-    img = uint16 (img) + 1;
+    ## if uint16 is not enough, we return double in which case index
+    ## values should start at 1.
+    I = round (I) + 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]))
+%!assert (gray2ind ([0 0.25 0.5 1]), uint8 ([0 16 32 63]))
+%!assert (gray2ind ([0 0.25 0.5 1], 400), uint16 ([0 100 200 399]))
+%!assert (gray2ind (logical ([1 0 0 1])), uint8 ([1 0 0 1]))
+%!assert (gray2ind (uint8 ([0 64 128 192 255])), uint8 ([0 16 32 47 63]))
 
 %!test
 %! i2g = ind2gray (1:100, gray (100));
 %! g2i = gray2ind (i2g, 100);
-%! assert (g2i, uint8 (1:100));
+%! assert (g2i, uint8 (0:99));
 
 %% Test input validation
 %!error gray2ind ()
--- a/scripts/image/ind2gray.m	Tue Nov 27 16:38:13 2012 -0800
+++ b/scripts/image/ind2gray.m	Wed Nov 28 06:01:09 2012 +0100
@@ -20,8 +20,10 @@
 ## @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.
+## intensities.  If it doesn't contain enough colors, it is padded it with the
+## last color in the map.
 ##
 ## 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}.
@@ -38,32 +40,19 @@
 ## Created: July 1994
 ## Adapted-By: jwe
 
-function I = ind2gray (x, map)
+function I = ind2gray (x, map = colormap ())
 
   if (nargin < 1 || nargin > 2)
     print_usage ();
   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
+  [x, map] = ind2x ("ind2rgb", x, map);
 
   ## Convert colormap to luminance intensity values 
-  map *= [0.299; 0.587; 0.114];
+  map *= [0.29894; 0.58704; 0.11402];
 
-  ## Convert colormap to same class as that of input so that
-  ## indexing in colormap will produce output of the same type as the input.
+  ## Convert colormap to same class as that of input so that when reshape
+  ## later, produces output of the same type as the input
+  cls = class (x);
   if (isinteger (x)) 
     map *= intmax (cls);
   elseif (strcmp (cls, "single"))
@@ -75,12 +64,10 @@
 
 endfunction
 
-
-%!test
+%!shared i2g
 %! i2g = ind2gray (1:100, gray (100));
-%! assert (i2g, 0:1/99:1, eps);
-%! g2i = gray2ind (i2g, 100);
-%! assert (g2i, uint8 (1:100));
+%!assert (i2g, 0:1/99:1, eps);
+%!assert (gray2ind (i2g, 100), uint8 (0:99));
 
 %%test input validation
 %!error ind2gray ()
--- a/scripts/image/ind2rgb.m	Tue Nov 27 16:38:13 2012 -0800
+++ b/scripts/image/ind2rgb.m	Wed Nov 28 06:01:09 2012 +0100
@@ -36,35 +36,12 @@
 ## Created: July 1994
 ## Adapted-By: jwe
 
-function [R, G, B] = ind2rgb (x, map)
+function [R, G, B] = ind2rgb (x, map = colormap ())
 
   if (nargin < 1 || nargin > 2)
     print_usage ();
   endif
-
-  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
-
-  if (nargin == 1)
-    map = colormap ();
-  elseif (! iscolormap (map))
-    error ("ind2rgb: MAP must be a valid colormap");
-  endif
-
-  ## Do we have enough colors in the color map?
-  maxidx = max (x(:));
-  rm = rows (map);
-  if (rm < maxidx)
-    ## Pad with the last color in the map.
-    pad = repmat (map(end,:), maxidx-rm, 1);
-    map(end+1:maxidx, :) = pad;
-  endif
+  [x, map] = ind2x ("ind2rgb", x, map);
 
   ## Compute result
   [row, col] = size (x);
@@ -80,8 +57,27 @@
 
 endfunction
 
-
-%% FIXME: Need some functional tests or %!demo blocks
+%!shared img, map, ergb, rgb, r, g, b
+%! img = [2 4 5; 3 2 5; 1 2 4];
+%! map = [0.0  0.0  0.0
+%!        0.2  0.4  0.6
+%!        0.4  0.4  0.5
+%!        0.3  0.7  1.0
+%!        0.1  0.5  0.8];
+%! ergb(:,:,1) = [0.2 0.3 0.1; 0.4 0.2 0.1; 0.0 0.2 0.3];
+%! ergb(:,:,2) = [0.4 0.7 0.5; 0.4 0.4 0.5; 0.0 0.4 0.7];
+%! ergb(:,:,3) = [0.6 1.0 0.8; 0.5 0.6 0.8; 0.0 0.6 1.0];
+%! ## test basic usage with 1 and 3 outputs
+%! [rgb] = ind2rgb (img, map);
+%! [r, g, b] = ind2rgb (img, map);
+%!assert (ergb, rgb);
+%!assert (ergb, reshape ([r(:) g(:) b(:)], [size(img) 3]));
+%! ## test correction for integers
+%! img = uint8 (img -1);
+%! [rgb] = ind2rgb (img, map);
+%!assert (ergb, rgb);
+%! ## check it fails when image is a float with an index value of 0
+%!fail("[rgb] = ind2rgb (double(img), map)")
 
 %%test input validation
 %!error ind2rgb ()
@@ -97,4 +93,3 @@
 %!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])
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/image/private/ind2x.m	Wed Nov 28 06:01:09 2012 +0100
@@ -0,0 +1,56 @@
+## Copyright (C) 1994-2012 John W. Eaton
+## Copyright (C) 2012 Carnë Draug
+##
+## This file is part of Octave.
+##
+## Octave 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.
+##
+## Octave 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 Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## private function for the ind2something functions which have a lot of code
+## in common
+
+function [x, map] = ind2x (name, x, map)
+
+  ## Check if X is an indexed image.
+  if (ndims (x) != 2 || issparse (x) || (isfloat (x) && ! isindex (x)) ||
+      ! ismember (class (x), {"double", "single", "uint8", "uint16"}))
+    error ("%s: X must be an indexed image", name);
+  endif
+
+  ## Check the color map.
+  if (! iscolormap (map))
+    error ("%s: MAP must be a valid colormap", name);
+  endif
+
+  ## Do we have enough colors in the color map?
+  ## there's an offset of 1 when the indexed image is an integer class so we fix
+  ## it now and convert it to float only if really necessary and even then only
+  ## to single precision since its enough for both uint8 and uint16
+  maxidx = max (x(:));
+  if (isinteger (x))
+    if (maxidx == intmax (class (x)))
+      x = single (x);
+    endif
+    x      += 1;
+    maxidx += 1;
+  endif
+
+  rm = rows (map);
+  if (rm < maxidx)
+    ## Pad with the last color in the map.
+    pad = repmat (map(end,:), maxidx-rm, 1);
+    map(end+1:maxidx, :) = pad;
+  endif
+
+endfunction
--- a/scripts/image/rgb2ind.m	Tue Nov 27 16:38:13 2012 -0800
+++ b/scripts/image/rgb2ind.m	Wed Nov 28 06:01:09 2012 +0100
@@ -1,4 +1,5 @@
 ## Copyright (C) 1994-2012 John W. Eaton
+## Copyright (C) 2012 Carnë Draug
 ##
 ## This file is part of Octave.
 ##
@@ -23,7 +24,6 @@
 ## @seealso{ind2rgb, rgb2hsv, rgb2ntsc}
 ## @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>
@@ -51,15 +51,38 @@
 
   x = reshape (1:numel (R), size (R));
 
-  map = [R(:), G(:), B(:)];
+  map    = unique([R(:) G(:) B(:)], "rows");
+  [~, x] = ismember ([R(:) G(:) B(:)], map, "rows");
+  x      = reshape (x, size (R));
 
+  ## a colormap is of class double and values between 0 and 1
+  switch class (R)
+    case {"single", "double", "logical"}
+      ## do nothing, return the same
+    case {"uint8", "uint16"}
+      map = double (map) / double (intmax (class (R)));
+    case "int16"
+      map = (double (im) + 32768) / 65535;
+    otherwise
+      error ("unsupported image class %s", im_class);
+  endswitch
+
+  ## we convert to the smallest class necessary to encode the image. Matlab
+  ## documentation does not mention what it does when uint16 is not enough...
+  ## When an indexed image is of integer class, there's a -1 offset to the
+  ## colormap, hence the adjustment
+  if (rows (map) < 256)
+    x = uint8 (x - 1);
+  elseif (rows (map) < 65536)
+    x = uint16 (x - 1);
+  else
+    ## leave it as double
+  endif
 endfunction
 
-
 %% FIXME: Need some functional tests or %!demo blocks
 
 %% Test input validation
 %!error rgb2ind ()
 %!error rgb2ind (1,2)
 %!error rgb2ind (1,2,3,4)
-