# HG changeset patch
# User Adam H Aitkenhead <adamhaitkenhead@gmail.com>
# Date 1367661223 -3600
# Node ID 76d81e04be5a2b9b11d38d1f0818b932eb15803b
# Parent  a1f613e5066d4cd442732e1c24ccb38c6154480f
rgb2ind.m: Enable handling of ND images

* rgb2ind.m: Enable handling of ND images.
             Enable n,tol,map as inputs.
             Simplify the code which takes the unique rgb indices.

* private/rgbmap.m: New functino to generate an RGB colormap with a
                    defined tolerance.

diff -r a1f613e5066d -r 76d81e04be5a scripts/image/rgb2ind.m
--- a/scripts/image/rgb2ind.m	Sat May 04 09:37:28 2013 +0200
+++ b/scripts/image/rgb2ind.m	Sat May 04 10:53:43 2013 +0100
@@ -1,5 +1,6 @@
 ## Copyright (C) 1994-2012 John W. Eaton
 ## Copyright (C) 2012 Carnë Draug
+## Copyright (C) 2013 Adam H Aitkenhead
 ##
 ## This file is part of Octave.
 ##
@@ -19,43 +20,119 @@
 
 ## -*- texinfo -*-
 ## @deftypefn  {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb})
-## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{R}, @var{G}, @var{B})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{map})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{n})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{tol})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{map}, @var{dither_option})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{n}, @var{dither_option})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{tol}, @var{dither_option})
 ## Convert an image in red-green-blue (RGB) color space to an indexed image.
+##
+## The input image @var{rgb} must be an N-dimensional RGB image (MxNxO...x3 matrix) where M,N,O... are the
+## image dimensions, and the final dimension contains the values in the red, green and blue
+## channels.
+## @var{map} defines the colormap to be used.  Alternatively, @var{n} or @var{tol} may be used to define
+## the maximum number of colors to use in an automatically generated colormap.  @var{n} is related to @var{tol}
+## by:  @var{n} = (floor (1/@var{tol}) + 1)^3;
+## @var{tol} must be >0 and <=1.
+## @var{dither_option} is a string which enables or disables dithering: 'dither' or 'nodither' (default).
+##
+## Note: This function does not currently support dithering.  Please consider submitting a patch.
 ## @seealso{ind2rgb, rgb2hsv, rgb2ntsc}
 ## @end deftypefn
 
-## FIXME: This function has a very different syntax than the Matlab
-##        one of the same name.
-##        Octave function does not support N, MAP, DITHER, or TOL arguments.
+## FIXME: This function does not currently support dithering.
 
 ## Author: Tony Richardson <arichard@stark.cc.oh.us>
 ## Created: July 1994
 ## Adapted-By: jwe
 
-function [x, map] = rgb2ind (R, G, B)
+function [x, map] = rgb2ind (varargin)
 
-  if (nargin != 1 && nargin != 3)
+  if (nargin < 1 || nargin > 3)
     print_usage ();
+
+  else
+
+    rgb = varargin{1};
+
+    if (ndims (rgb) < 3 || size (rgb)(end) != 3)
+      error ("rgb2ind: The input image must be an RGB image (MxNxO...x3 array).");
+
+    elseif (max (rgb(:)) <0 || max (rgb(:)) > 1)
+      error ("rgb2ind: The input image contains values between 0 and 1.");
+
+    else
+
+      ## Initialise R,G,B:
+      sz = size (rgb);
+      R = zeros (sz(1:end-1));
+      G = zeros (sz(1:end-1));
+      B = zeros (sz(1:end-1));
+
+      ## Pick R,G,B from the final dimension of rgb:
+      pr = prod (sz(1:end-1));
+      R(:) = rgb(1:pr);
+      G(:) = rgb((1:pr)+pr);
+      B(:) = rgb((1:pr)+2*pr);
+
+    endif
+
+    if (nargin == 2)
+      option = varargin{2};
+    end
+
+    if (nargin == 3)
+      dither_option = varargin{3};
+      if strcmp (dither_option,"dither")
+        error ("rgb2ind: The input dither_option is not currently supported.  Please consider submitting a patch.")
+      end
+    end
+
   endif
 
-  if (nargin == 1)
-    rgb = R;
-    if (ndims (rgb) != 3 || size (rgb, 3) != 3)
-      error ("rgb2ind: argument is not an RGB image");
+  ## Initial conversion of image to x,map
+  ## The initial map contains all colors in the original image
+  x = zeros (size (R));
+  [map,~,x(:)] = unique ([R(:) G(:) B(:)], "rows");
+
+  ## Apply a limited colormap if required
+  if exist ('option','var')
+
+    if size (option,1)==1
+
+      if option>0 && option<=1
+        ## option: tol
+        tol = option;
+        n   = (floor (1/option) + 1)^3;
+      else
+        ## option: n
+        n   = option;
+        tol = 1/(n^(1/3)-1);
+      endif
+
+      ## Generate the new colormap
+      if size (map, 1) > n
+        mapnew = rgbmap (tol, [min(R(:)),  max(R(:))], [min(G(:)), max(G(:))], [min(B(:)), max(B(:))]);
+      end
+
     else
-      R = rgb(:,:,1);
-      G = rgb(:,:,2);
-      B = rgb(:,:,3);
+
+      ## option: map
+      mapnew = option;
+      if isequal(mapnew(:,1),mapnew(:,2)) || isequal(mapnew(:,1),mapnew(:,3)) || isequal(mapnew(:,2),mapnew(:,3))
+        error('rgb2ind: The colormap cannot contain matching R,G, or B channels.')
+      end
+
     endif
-  elseif (! size_equal (R, G, B))
-    error ("rgb2ind: R, G, and B must have the same size");
+
+    tri  = delaunayn (mapnew);
+    idx  = dsearchn (mapnew, tri, map);
+    x(:) = idx(x(:));
+    map  = mapnew;
+
   endif
 
-  x = reshape (1:numel (R), size (R));
-
-  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))
# HG changeset patch
# User Adam H Aitkenhead <adamhaitkenhead@gmail.com>
# Date 1367661223 -3600
# Node ID d04e4dff68719eb7274dbd3a8f4be64394aa23f4
# Parent  a1f613e5066d4cd442732e1c24ccb38c6154480f
rgb2ind.m: Enable handling of ND images

* rgb2ind.m: Enable handling of ND images.
             Enable n,tol,map as inputs.
             Simplify the code which takes the unique rgb indices.

* private/rgbmap.m: New function to generate an RGB colormap with a
                    defined tolerance.

diff -r a1f613e5066d -r d04e4dff6871 scripts/image/rgb2ind.m
--- a/scripts/image/rgb2ind.m	Sat May 04 09:37:28 2013 +0200
+++ b/scripts/image/rgb2ind.m	Sat May 04 10:53:43 2013 +0100
@@ -1,5 +1,6 @@
 ## Copyright (C) 1994-2012 John W. Eaton
 ## Copyright (C) 2012 Carnë Draug
+## Copyright (C) 2013 Adam H Aitkenhead
 ##
 ## This file is part of Octave.
 ##
@@ -19,43 +20,119 @@
 
 ## -*- texinfo -*-
 ## @deftypefn  {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb})
-## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{R}, @var{G}, @var{B})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{map})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{n})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{tol})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{map}, @var{dither_option})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{n}, @var{dither_option})
+## @deftypefnx {Function File} {[@var{x}, @var{map}] =} rgb2ind (@var{rgb}, @var{tol}, @var{dither_option})
 ## Convert an image in red-green-blue (RGB) color space to an indexed image.
+##
+## The input image @var{rgb} must be an N-dimensional RGB image (MxNxO...x3 matrix) where M,N,O... are the
+## image dimensions, and the final dimension contains the values in the red, green and blue
+## channels.
+## @var{map} defines the colormap to be used.  Alternatively, @var{n} or @var{tol} may be used to define
+## the maximum number of colors to use in an automatically generated colormap.  @var{n} is related to @var{tol}
+## by:  @var{n} = (floor (1/@var{tol}) + 1)^3;
+## @var{tol} must be >0 and <=1.
+## @var{dither_option} is a string which enables or disables dithering: 'dither' or 'nodither' (default).
+##
+## Note: This function does not currently support dithering.  Please consider submitting a patch.
 ## @seealso{ind2rgb, rgb2hsv, rgb2ntsc}
 ## @end deftypefn
 
-## FIXME: This function has a very different syntax than the Matlab
-##        one of the same name.
-##        Octave function does not support N, MAP, DITHER, or TOL arguments.
+## FIXME: This function does not currently support dithering.
 
 ## Author: Tony Richardson <arichard@stark.cc.oh.us>
 ## Created: July 1994
 ## Adapted-By: jwe
 
-function [x, map] = rgb2ind (R, G, B)
+function [x, map] = rgb2ind (varargin)
 
-  if (nargin != 1 && nargin != 3)
+  if (nargin < 1 || nargin > 3)
     print_usage ();
+
+  else
+
+    rgb = varargin{1};
+
+    if (ndims (rgb) < 3 || size (rgb)(end) != 3)
+      error ("rgb2ind: The input image must be an RGB image (MxNxO...x3 array).");
+
+    elseif (max (rgb(:)) <0 || max (rgb(:)) > 1)
+      error ("rgb2ind: The input image contains values between 0 and 1.");
+
+    else
+
+      ## Initialise R,G,B:
+      sz = size (rgb);
+      R = zeros (sz(1:end-1));
+      G = zeros (sz(1:end-1));
+      B = zeros (sz(1:end-1));
+
+      ## Pick R,G,B from the final dimension of rgb:
+      pr = prod (sz(1:end-1));
+      R(:) = rgb(1:pr);
+      G(:) = rgb((1:pr)+pr);
+      B(:) = rgb((1:pr)+2*pr);
+
+    endif
+
+    if (nargin == 2)
+      option = varargin{2};
+    end
+
+    if (nargin == 3)
+      dither_option = varargin{3};
+      if strcmp (dither_option,"dither")
+        error ("rgb2ind: The input dither_option is not currently supported.  Please consider submitting a patch.")
+      end
+    end
+
   endif
 
-  if (nargin == 1)
-    rgb = R;
-    if (ndims (rgb) != 3 || size (rgb, 3) != 3)
-      error ("rgb2ind: argument is not an RGB image");
+  ## Initial conversion of image to x,map
+  ## The initial map contains all colors in the original image
+  x = zeros (size (R));
+  [map,~,x(:)] = unique ([R(:) G(:) B(:)], "rows");
+
+  ## Apply a limited colormap if required
+  if exist ('option','var')
+
+    if size (option,1)==1
+
+      if option>0 && option<=1
+        ## option: tol
+        tol = option;
+        n   = (floor (1/option) + 1)^3;
+      else
+        ## option: n
+        n   = option;
+        tol = 1/(n^(1/3)-1);
+      endif
+
+      ## Generate the new colormap
+      if size (map, 1) > n
+        mapnew = rgbmap (tol, [min(R(:)),  max(R(:))], [min(G(:)), max(G(:))], [min(B(:)), max(B(:))]);
+      end
+
     else
-      R = rgb(:,:,1);
-      G = rgb(:,:,2);
-      B = rgb(:,:,3);
+
+      ## option: map
+      mapnew = option;
+      if isequal(mapnew(:,1),mapnew(:,2)) || isequal(mapnew(:,1),mapnew(:,3)) || isequal(mapnew(:,2),mapnew(:,3))
+        error('rgb2ind: The colormap cannot contain matching R,G, or B channels.')
+      end
+
     endif
-  elseif (! size_equal (R, G, B))
-    error ("rgb2ind: R, G, and B must have the same size");
+
+    tri  = delaunayn (mapnew);
+    idx  = dsearchn (mapnew, tri, map);
+    x(:) = idx(x(:));
+    map  = mapnew;
+
   endif
 
-  x = reshape (1:numel (R), size (R));
-
-  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))
