changeset 886:a0c42a32c6c4

Move conversion between each image type to imcast. * im2double.m, im2int16.m, im2single.m, im2uint16.m, im2uint8.m: move code into imcast. Fix several small bugs mainly dealing with indexed and logical images, small precision for Matlab compatibility, and increased performance. Expanded documentation. Added new tests. * imcast.m: implement the conversion between each image type instead of being a wrapper around functions for each conversion. This reduces code duplication, and places all very similar (but not equal) code together so that a bug fix in one can be easily notices that requires application in others. Expand documetation. Add more tests. * private/imconversion.m, private/im2float.m: remove no longer necessary function since this has all been moved to imcast. * COPYING: remove license for private/im2float.m and private/imconversion.m. * NEWS: make note of bug fixes for this functions.
author Carnë Draug <carandraug@octave.org>
date Mon, 24 Mar 2014 22:00:05 +0000
parents 40269ff6760d
children 33c44c229f74
files COPYING NEWS inst/im2double.m inst/im2int16.m inst/im2single.m inst/im2uint16.m inst/im2uint8.m inst/imcast.m inst/private/im2float.m inst/private/imconversion.m
diffstat 10 files changed, 361 insertions(+), 304 deletions(-) [+]
line wrap: on
line diff
--- a/COPYING
+++ b/COPYING
@@ -1,9 +1,7 @@
 inst/private/analyze75filename.m        GPLv3+
 inst/private/handle_colorspec.m         GPLv3+
 inst/private/im2col_check.m             GPLv3+
-inst/private/im2float.m                 GPLv3+
 inst/private/imarithmetics.m            GPLv3+
-inst/private/imconversion.m             GPLv3+
 inst/private/interp_method.m            GPLv3+
 inst/private/is_double_image.m          GPLv3+
 inst/private/isimage.m                  GPLv3+
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,14 @@
       impad                 iptcheckstrs
       imrotate_Fourier      uintlut
 
+ ** Other functions that have been changed for smaller bugfixes, increased
+    Matlab compatibility, or performance:
+
+      im2double
+      im2int16
+      im2single
+      im2uint8
+      im2uint16
 
  Summary of important user-visible changes for image 2.2.1 (2014/03/08):
 -------------------------------------------------------------------------
--- a/inst/im2double.m
+++ b/inst/im2double.m
@@ -1,5 +1,5 @@
 ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org>
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## This program is free software; you can redistribute it and/or modify it under
 ## the terms of the GNU General Public License as published by the Free Software
@@ -15,32 +15,58 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2double (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2double (@var{im1}, "indexed")
-## Convert input image @var{im1} to double precision.
+## @deftypefn  {Function File} {} im2double (@var{img})
+## @deftypefnx {Function File} {} im2double (@var{img}, "indexed")
+## Convert image to double precision.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to double precision, is dependent
+## on the type of input image.  The following input classes are supported:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint8, uint16, int16 - output will be rescaled for the interval [0 1]
-## with the limits of the class;
-## @item double - output will be the same as input;
-## @item single - output will have the same values as input but the class will
-## double;
-## @item indexed, logical - converted to double class.
+## @table @samp
+## @item uint8, uint16, and int16
+## The whole range of values from the class (see @code{getrangefromclass})
+## are scaled for the interval [0 1], e.g., if input image was uint8,
+## intensity values of 0, 127, and 255, are converted to intensity of
+## 0, 0.498, and 1.
+##
+## @item logical
+## True and false values are assigned a value of 0 and 1 respectively.
+##
+## @item single
+## Values are cast to single precision.
+##
+## @item double
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2int16, im2single, im2uint8, im2uint16}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to double precision, and a +1 offset is applied if input is
+## an integer class.
+##
+## @seealso{im2bw, imcast, im2uint8, im2int16, im2single, im2uint16}
 ## @end deftypefn
 
-function im = im2double (im, indexed = false)
-  im = im2float ("double", nargin, im, indexed);
+function imout = im2double (img, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2double: second input argument must be the string \"indexed\"");
+  endif
+  imout = imcast (img, "double", varargin{:});
 endfunction
 
-%!assert(im2double([1 2 3]), [1 2 3]);                  # double returns the same
-%!assert(im2double(uint8([0 255])), [0 1]);             # basic usage
-%!assert(im2double(uint8([1 25]), "indexed"), [2 26]);  # test indexed
-%!assert(im2double(int16([-32768 32768])), [0 1]);      # test signed integer
+%!assert (im2double ([1 2 3]), [1 2 3]);
+%!assert (im2double (single ([1 2 3])), [1 2 3]);
+%!assert (im2double (uint8 ([0 127 128 255])), [0 127/255 128/255 1]);
+%!assert (im2double (uint16 ([0 127 128 65535])), [0 127/65535 128/65535 1]);
+%!assert (im2double (int16 ([-32768 -32767 -32766 32767])), [0 1/65535 2/65535 1]);
+
+%!assert (im2double (uint8 ([0 1 255]), "indexed"), [1 2 256]);
+%!assert (im2double (uint16 ([0 1 2557]), "indexed"), [1 2 2558]);
+%!assert (im2double ([3 25], "indexed"), [3 25]);
+
+%!error <indexed> im2double (single ([0 1 2]), "indexed");
+%!error <indexed> im2double (int16 ([17 8]), "indexed");
+%!error <indexed> im2double (int16 ([-7 8]), "indexed");
+%!error <indexed> im2double ([false true], "indexed");
+
--- a/inst/im2int16.m
+++ b/inst/im2int16.m
@@ -1,4 +1,4 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## This program is free software; you can redistribute it and/or modify it under
 ## the terms of the GNU General Public License as published by the Free Software
@@ -14,55 +14,47 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2int16 (@var{im1})
-## Convert input image @var{im1} to int16 precision.
+## @deftypefn {Function File} {} im2int16 (@var{img})
+## Convert image to int16.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical).
+## The conversion of @var{img} to a 16-bit signed integer, is dependent
+## on the type of input image.  The following input classes are supported:
+##
+## @table @samp
+## @item uint8 or uint16
+## Values are rescaled to the range of the int16 class [-32768 32767].
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item int16 - returns the same as input
-## @item uint8, double, single, uint16, logical - output will be rescaled for the
-## interval of the uint16 class [0 65535]
+## @item logical
+## True and false values are assigned a value of 32767 and -32768 respectively.
+##
+## @item double or single
+## Values are truncated to the interval [0 1] and then rescaled to the range
+## of values of the int16 class [-32768 32767].
+##
+## @item int16
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2single, im2uint8, im2uint16}
+## @seealso{im2bw, imcast, im2uint8, im2double, im2single, im2uint16}
 ## @end deftypefn
 
-function im = im2int16 (im)
-
-  ## unlike the others for imconversion, this only accepts 1 argument so we check here
+function imout = im2int16 (img)
   if (nargin != 1)
-    print_usage;
+    print_usage ();
   endif
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (nargin, "im2int16", false, im);
-
-  ## for those who may wonder, 32767 = intmax ("int16")
-  ## for those who may wonder, 65535 = intmax ("uint16")
-    switch im_class
-    case "int16"
-      ## do nothing, return the same
-    case {"single", "double"}
-      im = int16 (double (im * uint16(65535)) - 32767 - 1);
-    case "logical"
-      im(im)  = intmax ("int16");
-      im(!im) = intmin ("int16");
-    case "uint8"
-        ## 257 is the ratio between the max of uint8 and uint16
-        ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257
-        im = int16 (double (257 * uint16 (im)) - 32767 - 1);
-    case "uint16"
-      im = int16 (double (im) - 32767 - 1);
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-
+  imout = imcast (img, "int16");
 endfunction
 
-%!assert(im2int16(int16([-2 2 3])), int16([-2 2 3]));           # uint16 returns the same
-%!assert(im2int16(uint8([0 255])), int16([-32768 32767]));      # basic usage with uint8
-%!assert(im2int16(uint16([0 65535])), int16([-32768 32767]));   # basic usage with uint16
-%!assert(im2int16([0 0.5 1]), int16([-32768 0 32767]));         # basic usage with double
-%!assert(im2int16([1 2]), int16([32767 32767]));                # for double, above 1 is same as 1
+%!assert (im2int16 (int16 ([-2 2 3])),    int16 ([-2 2 3]));
+%!assert (im2int16 (uint16 ([0 65535])),  int16 ([-32768 32767]));
+%!assert (im2int16 ([false true]),        int16 ([-32768 32767]));
+%!assert (im2int16 ([true false]),        int16 ([32767 -32768]));
+%!assert (im2int16 (uint8 ([0 127 128 255])), int16 ([-32768 -129 128 32767]));
+
+%!assert (im2int16 ([0 1.4/65535 1.5/65535 2/65535 1]), int16 ([-32768 -32767 -32766 -32766 32767]));
+
+%!assert (im2int16 ([0 0.5 1]),  int16 ([-32768 0 32767]));
+%!assert (im2int16 ([-1 0 1 2]), int16 ([-32768 -32768 32767 32767]));
+
+%!error im2int16 ([1 2], "indexed");
+
--- a/inst/im2single.m
+++ b/inst/im2single.m
@@ -1,4 +1,4 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## This program is free software; you can redistribute it and/or modify it under
 ## the terms of the GNU General Public License as published by the Free Software
@@ -14,32 +14,58 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2single (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2single (@var{im1}, "indexed")
-## Convert input image @var{im1} to single precision.
+## @deftypefn  {Function File} {} im2single (@var{img})
+## @deftypefnx {Function File} {} im2single (@var{img}, "indexed")
+## Convert image to single precision.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to single precision, is dependent
+## on the type of input image.  The following input classes are supported:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint8, uint16, int16 - output will be rescaled for the interval [0 1]
-## with the limits of the class;
-## @item single - output will be the same as input;
-## @item double - output will have the same values as input but the class will
-## single;
-## @item indexed, logical - converted to single class.
+## @table @samp
+## @item uint8, uint16, and int16
+## The whole range of values from the class (see @code{getrangefromclass})
+## are scaled for the interval [0 1], e.g., if input image was uint8,
+## intensity values of 0, 127, and 255, are converted to intensity of
+## 0, 0.498, and 1.
+##
+## @item logical
+## True and false values are assigned a value of 0 and 1 respectively.
+##
+## @item double
+## Values are cast to double precision.
+##
+## @item single
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2int16, im2uint8, im2uint16}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to single precision, and a +1 offset is applied if input is
+## an integer class.
+##
+## @seealso{im2bw, imcast, im2uint8, im2double, im2int16, im2uint16}
 ## @end deftypefn
 
-function im = im2single (im, indexed = false)
-  im = im2float ("single", nargin, im, indexed);
+function imout = im2single (img, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2single: second input argument must be the string \"indexed\"");
+  endif
+  imout = imcast (img, "single", varargin{:});
 endfunction
 
-%!assert(im2single([1 2 3]), single([1 2 3]));                  # double returns the same
-%!assert(im2single(uint8([0 255])), single([0 1]));             # basic usage
-%!assert(im2single(uint8([1 25]), "indexed"), single([2 26]));  # test indexed
-%!assert(im2single(int16([-32768 32768])), single([0 1]));      # test signed integer
+%!assert (im2single (single ([1 2 3])), single ([1 2 3]));
+%!assert (im2single ([1 2 3]), single ([1 2 3]));
+%!assert (im2single (uint8 ([0 127 128 255])), single ([0 127/255 128/255 1]));
+%!assert (im2single (uint16 ([0 127 128 65535])), single ([0 127/65535 128/65535 1]));
+%!assert (im2single (int16 ([-32768 -32767 -32766 32767])), single ([0 1/65535 2/65535 1]));
+
+%!assert (im2single (uint8 ([0 1 255]), "indexed"), single ([1 2 256]));
+%!assert (im2single (uint16 ([0 1 2557]), "indexed"), single ([1 2 2558]));
+%!assert (im2single ([3 25], "indexed"), single ([3 25]));
+
+%!error <indexed> im2single ([0 1 2], "indexed");
+%!error <indexed> im2single (int16 ([17 8]), "indexed");
+%!error <indexed> im2single (int16 ([-7 8]), "indexed");
+%!error <indexed> im2single ([false true], "indexed");
+
--- a/inst/im2uint16.m
+++ b/inst/im2uint16.m
@@ -1,5 +1,5 @@
 ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org>
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## This program is free software; you can redistribute it and/or modify it under
 ## the terms of the GNU General Public License as published by the Free Software
@@ -15,67 +15,63 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2uint16 (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2uint16 (@var{im1}, "indexed")
-## Convert input image @var{im1} to uint16 precision.
+## @deftypefn  {Function File} {} im2uint16 (@var{img})
+## @deftypefnx {Function File} {} im2uint16 (@var{img}, "indexed")
+## Convert image to uint16.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to a 16-bit unsigned integer, is dependent
+## on the type of input image.  The following input classes are supported
+## for non-indexed images:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint16 - returns the same as input
-## @item uint8, double, single, int16, logical - output will be rescaled for the
-## interval of the uint16 class [0 65535]
-## @item indexed - depends on the input class. If double, no value can be above
-## the max of the uint16 class (65535).
+## @table @samp
+## @item int16 or uint8
+## Values are rescaled to the range of the uint16 class [0 65535].
+##
+## @item logical
+## True and false values are assigned a value of 0 and 255 respectively.
+##
+## @item double or single
+## Values are truncated to the interval [0 1] and then rescaled to the range
+## of values of the int16 class [0 255].
+##
+## @item uint16
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2int16, im2single, im2uint8}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to uint16, and a -1 offset is applied if input is
+## a floating point class.  Input checking is performed and an error will
+## be throw is the range of values in uint16 is not enough for all the
+## image indices.
+##
+## @seealso{im2bw, imcast, im2uint8, im2double, im2int16, im2single}
 ## @end deftypefn
 
-function im = im2uint16 (im, indexed = false)
-
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (nargin, "im2uint16", indexed, im);
-
-  ## for those who may wonder, 65535 = intmax ("uint16")
-  switch im_class
-    case "uint16"
-      ## do nothing, return the same
-    case {"single", "double"}
-      if (indexed)
-        imax = max (im(:));
-        if ( imax > 65535)
-          error ("Too many colors '%d' for an indexed uint16 image", imax);
-        endif
-        im = uint16 (im) - 1;
-      else
-        im = uint16 (im * 65535);
-      endif
-    case "logical"
-      im = uint16 (im) * uint16 (65535);
-    case "uint8"
-      if (indexed)
-        im = uint16 (im);
-      else
-        ## 257 is the ratio between the max of uint8 and uint16
-        ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257
-        im = 257 * uint16 (im);
-      endif
-    case "int16"
-      im = uint16 (double (im) + double (intmax (im_class)) + 1);
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-
+function imout = im2uint16 (im, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2uint16: second input argument must be the string \"indexed\"");
+  endif
+  imout = imcast (im, "uint16", varargin{:});
 endfunction
 
-%!assert(im2uint16(uint16([1 2 3])), uint16([1 2 3]));          # uint16 returns the same
-%!assert(im2uint16(uint8([0 255])), uint16([0 65535]));         # basic usage with uint8
-%!assert(im2uint16([0 0.5 1]), uint16([0 32768 65535]));        # basic usage with double
-%!assert(im2uint16([1 2]), uint16([65535 65535]));              # for double, above 1 is same as 1
-%!assert(im2uint16(uint8([3 25]), "indexed"), uint16([3 25]));  # test indexed uint8
-%!assert(im2uint16([3 25], "indexed"), uint16([2 24]));         # test indexed double
-%!assert(im2uint16(int16([-32768 32768])), uint16([0 65535]));  # test signed integer
+%!assert (im2uint16 (uint16 ([1 2 3])), uint16 ([1 2 3]));
+%!assert (im2uint16 (uint8 ([0 127 128 255])), uint16 ([0 32639 32896 65535]));
+%!assert (im2uint16 ([0 0.5 1]), uint16 ([0 32768 65535]));
+%!assert (im2uint16 ([0 1/65535 1.4/65535 1.5/65535 1]), uint16 ([0 1 1 2 65535]));
+%!assert (im2uint16 ([1 2]), uint16 ([65535 65535]));
+%!assert (im2uint16 ([-1 0 0.5 1]), uint16 ([0 0 32768 65535]));
+%!assert (im2uint16 (int16 ([-32768 -1 0 32768])), uint16 ([0 32767 32768 65535]));
+%!assert (im2uint16 ([false true]), uint16 ([0 65535]));
+%!assert (im2uint16 ([true false]), uint16 ([65535 0]));
+
+%!assert (im2uint16 (uint8 ([3 25]), "indexed"), uint16 ([3 25]));
+%!assert (im2uint16 ([1 3 25], "indexed"), uint16 ([0 2 24]));
+
+%!error <indexed> im2uint16 ([0 1 2], "indexed");
+%!error <indexed> im2uint16 (int16 ([17 8]), "indexed");
+%!error <indexed> im2uint16 (int16 ([-7 8]), "indexed");
+%!error <indexed> im2uint16 ([false true], "indexed");
+%!error <range of values> im2uint16 (65537, "indexed");
+
--- a/inst/im2uint8.m
+++ b/inst/im2uint8.m
@@ -1,5 +1,5 @@
 ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org>
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## This program is free software; you can redistribute it and/or modify it under
 ## the terms of the GNU General Public License as published by the Free Software
@@ -15,70 +15,64 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2uint8 (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2uint8 (@var{im1}, "indexed")
-## Convert input image @var{im1} to uint16 precision.
+## @deftypefn  {Function File} {} im2uint8 (@var{img})
+## @deftypefnx {Function File} {} im2uint8 (@var{img}, "indexed")
+## Convert image to uint8.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to an 8-bit unsigned integer, is dependent
+## on the type of input image.  The following input classes are supported
+## for non-indexed images:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint8 - returns the same as input
-## @item uint16, double, single, int16, logical - output will be rescaled for the
-## interval of the uint8 class [0 255]
-## @item indexed - depends on the input class. No value can be above the max of
-## the uint8 class (255).
+## @table @samp
+## @item int16 or uint16
+## Values are rescaled to the range of the uint8 class [0 255].
+##
+## @item logical
+## True and false values are assigned a value of 0 and 255 respectively.
+##
+## @item double or single
+## Values are truncated to the interval [0 1] and then rescaled to the range
+## of values of the int16 class [0 255].
+##
+## @item uint8
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2int16, im2single, im2uint16}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to uint8, and a -1 offset is applied if input is
+## a floating point class.  Input checking is performed and an error will
+## be throw is the range of values in uint8 is not enough for all the
+## image indices.
+##
+## @seealso{im2bw, imcast, im2double, im2int16, im2single, im2uint16}
 ## @end deftypefn
 
-function im = im2uint8 (im, indexed = false)
-
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (nargin, "im2uint8", indexed, im);
-
-  if (indexed)
-    imax = max (im(:));
-    if (imax > 255)
-      error ("Too many colors '%d' for an indexed uint8 image", imax);
-    endif
+function imout = im2uint8 (img, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2uint8: second input argument must be the string \"indexed\"");
   endif
-
-  ## for those who may wonder, 255 = intmax ("uint8")
-  switch im_class
-    case "uint8"
-      ## do nothing, return the same
-    case {"single", "double"}
-      if (indexed)
-        im = uint8 (im) - 1;
-      else
-        im = uint8 (im * 255);
-      endif
-    case "logical"
-      im = uint8 (im) * uint8 (255);
-    case "uint16"
-      if (indexed)
-        im = uint8 (im);
-      else
-        ## 257 is the ratio between the max of uint8 and uint16
-        ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257
-        im = uint8 (im / 257);
-      endif
-    case "int16"
-      im = uint8 ((double (im) + double (intmax (im_class)) + 1) / 257);
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-
+  imout = imcast (img, "uint8", varargin{:});
 endfunction
 
-%!assert(im2uint8(uint8([1 2 3])), uint8([1 2 3]));               # uint16 returns the same
-%!assert(im2uint8(uint16([0 65535])), uint8([0 255]));            # basic usage with uint16
-%!assert(im2uint8([0 0.5 1]), uint8([0 128 255]));                # basic usage with double
-%!assert(im2uint8([1 2]), uint8([255 255]));                      # for double, above 1 is same as 1
-%!assert(im2uint8(uint16([3 25]), "indexed"), uint8([3 25]));     # test indexed uint8
-%!assert(im2uint8([3 25], "indexed"), uint8([2 24]));             # test indexed double
-%!assert(im2uint8(int16([-32768 0 32768])), uint8([0 128 255]));  # test signed integer
+%!assert (im2uint8 (uint8 ([1 2 3])), uint8 ([1 2 3]));
+%!assert (im2uint8 (uint16 ([0 65535])), uint8 ([0 255]));
+%!assert (im2uint8 ([0 0.5 1]), uint8 ([0 128 255]));
+%!assert (im2uint8 ([1 2]), uint8 ([255 255]));
+%!assert (im2uint8 ([-1 0 0.5 1 2]), uint8 ([0 0 128 255 255]));
+%!assert (im2uint8 (int16 ([-32768 0 32768])), uint8 ([0 128 255]));
+%!assert (im2uint8 ([false true]), uint8 ([0 255]));
+%!assert (im2uint8 ([true false]), uint8 ([255 0]));
+
+%!assert (im2uint8 ([1 256], "indexed"), uint8 ([0 255]));
+%!assert (im2uint8 ([3 25], "indexed"), uint8 ([2 24]));
+%!assert (im2uint8 (uint16 ([3 25]), "indexed"), uint8 ([3 25]));
+
+%!error <indexed> im2uint8 ([0 1 2], "indexed");
+%!error <indexed> im2uint8 (int16 ([17 8]), "indexed");
+%!error <indexed> im2uint8 (int16 ([-7 8]), "indexed");
+%!error <indexed> im2uint8 ([false true], "indexed");
+%!error <range of values> im2uint8 (uint16 (256), "indexed");
+%!error <range of values> im2uint8 (257, "indexed");
+
--- a/inst/imcast.m
+++ b/inst/imcast.m
@@ -18,20 +18,24 @@
 ## @deftypefnx {Function File} {} imcast (@var{img}, @var{type}, "indexed")
 ## Convert image to specific data type @var{type}.
 ##
-## This is the same as calling one of the following
+## Converts a valid image @var{img} into another class @var{type}.  A valid
+## image must be of class logical, uint8, uint16, int16, single, or double.
+## Conversion of images of class logical is valid, but not the inverse, i.e.,
+## conversion of images into logical class is not supported. Use of
+## @code{im2bw} is recommended for such cases.
 ##
-## @itemize @bullet
-## @item im2double
-## @item im2int16
-## @item im2single
-## @item im2uint8
-## @item im2uint16
-## @end itemize
+## If the image is indexed, the last argument may be the string
+## @qcode{"indexed"}.  An indexed image may not be of class int16 or
+## single (see @code{isind} for details).
 ##
-## @seealso{im2uint8, im2double, im2int16, im2single, im2uint16}
+## Details on how the conversion is performed is class dependent, and can
+## be seen on the help text of @code{im2double}, @code{im2single},
+## @code{im2uint8}, @code{im2uint16}, and @code{im2int16}.
+##
+## @seealso{im2bw, im2uint8, im2double, im2int16, im2single, im2uint16}
 ## @end deftypefn
 
-function img = imcast (img, itype, varargin)
+function imout = imcast (img, outcls, varargin)
 
   if (nargin < 2 || nargin > 3)
     print_usage ();
@@ -39,18 +43,107 @@
     error ("imcast: third argument must be the string \"indexed\"");
   endif
 
-  ## We could confirm that the image really is an indexed image in
-  ## case the user says so, but the functions im2xxx already do it.
+  incls = class (img);
+  if (strcmp (outcls, incls))
+    imout = img;
+    return
+  endif
+
+  ## we are dealing with indexed images
+  if (nargin == 3)
+    if (! isind (img))
+      error ("imcast: input should have been an indexed image but it is not.");
+    endif
+
+    ## Check that the new class is enough to hold all the previous indices
+    ## If we are converting to floating point, then we don't bother
+    ## check the range of indices. Also, note that indexed images of
+    ## integer class are always unsigned.
+
+    ## we will be converting a floating point image to integer class
+    if (strcmp (outcls, "single") || strcmp (outcls, "double"))
+      if (isinteger (img))
+        imout = cast (img, outcls) +1;
+      else
+        imout = cast (img, outcls);
+      endif
+
+    ## we will be converting an indexed image to integer class
+    else
+      if (isinteger (img) && intmax (incls) > intmax (outcls) && max (img(:)) > intmax (outcls))
+          error ("imcast: IMG has too many colours '%d' for the range of values in %s",
+            max (img(:)), outcls);
+      elseif (isfloat (img))
+        imax = max (img(:)) -1;
+        if (imax > intmax (outcls))
+          error ("imcast: IMG has too many colours '%d' for the range of values in %s",
+            imax, outcls);
+        endif
+        img -= 1;
+      endif
+      imout = cast (img, outcls);
+    endif
+
+  ## we are dealing with "normal" images
+  else
+    problem = false; # did we found a bad conversion?
+    switch (incls)
 
-  switch itype
-    case "double",  img = im2double (img, varargin{:});
-    case "uint8",   img = im2uint8  (img, varargin{:});
-    case "uint16",  img = im2uint16 (img, varargin{:});
-    case "single",  img = im2single (img, varargin{:});
-    case "int16",   img = im2int16  (img, varargin{:});
-    otherwise
-      error ("imcast: unsupported TYPE \"%s\"", itype);
-  endswitch
+      case {"double", "single"}
+        switch (outcls)
+          case "uint8",  imout = uint8  (img * 255);
+          case "uint16", imout = uint16 (img * 65535);
+          case "int16",  imout = int16 (double (img * uint16 (65535)) -32768);
+          case {"double", "single"}, imout = cast (img, outcls);
+          otherwise, problem = true;
+        endswitch
+
+      case {"uint8"}
+        switch (outcls)
+          case "double", imout = double (img) / 255;
+          case "single", imout = single (img) / 255;
+          case "uint16", imout = uint16 (img) * 257; # 257 comes from 65535/255
+          case "int16",  imout = int16 ((double (img) * 257) -32768); # 257 comes from 65535/255
+          otherwise, problem = true;
+        endswitch
+
+      case {"uint16"}
+        switch (outcls)
+          case "double", imout = double (img) / 65535;
+          case "single", imout = single (img) / 65535;
+          case "uint8",  imout = uint8 (img / 257); # 257 comes from 65535/255
+          case "int16",  imout = int16 (double (img) -32768);
+          otherwise, problem = true;
+        endswitch
+
+      case {"logical"}
+        switch (outcls)
+          case {"double", "single"}
+            imout = cast (img, outcls);
+          case {"uint8", "uint16", "int16"}
+            imout = repmat (intmin (outcls), size (img));
+            imout(img) = intmax (outcls);
+          otherwise
+            problem = true;
+        endswitch
+
+      case {"int16"}
+        switch (outcls)
+          case "double", imout = (double (img) + 32768) / 65535;
+          case "single", imout = (single (img) + 32768) / 65535;
+          case "uint8",  imout = uint8 ((double (img) + 32768) / 257); # 257 comes from 65535/255
+          case "uint16", imout = uint16 (double (img) + 32768);
+          otherwise, problem = true;
+        endswitch
+
+      otherwise
+        error ("imcast: unknown image of class \"%s\"", incls);
+
+    endswitch
+    if (problem)
+      error ("imcast: unsupported TYPE \"%s\"", outcls);
+    endif
+  endif
 
 endfunction
 
@@ -73,7 +166,6 @@
 %! im = randi ([0 65535], 40, "uint16");
 %! assert (imcast (im, "uint8"), im2uint8 (im))
 %! assert (imcast (im, "single"), im2single (im))
-%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed"))
 %! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
 
 %!test
@@ -85,6 +177,8 @@
 %! im = rand (40);
 %! assert (imcast (im, "uint8"), im2uint8 (im))
 
+%!error <unknown image of class> imcast (randi (127, 40, "int8"), "uint8")
 %!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "uint32")
 %!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "not a class")
+%!error <range of values> imcast (randi ([0 65535], 40, "uint16"), "uint8", "indexed")
 
deleted file mode 100644
--- a/inst/private/im2float.m
+++ /dev/null
@@ -1,43 +0,0 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 3 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-
-## im2double and im2single are very similar so here's the common code,
-## which is prety much all of it.
-
-function im = im2float (out_class, caller_nargin, im, indexed = false)
-
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (caller_nargin, ["im2" out_class], indexed, im);
-
-  converter = eval (["@" out_class]);
-  switch im_class
-    case {"single", "double", "logical"}
-      if (strcmp (im_class, out_class))
-        ## do nothing, return the same
-      else
-        im = converter (im);
-      endif
-    case {"uint8", "uint16"}
-      if (indexed)
-        im = converter (im) + 1;
-      else
-        im = converter (im) / converter (intmax (im_class));
-      endif
-    case "int16"
-      im = (converter (im) + converter (intmax (im_class)) + 1) / converter (intmax ("uint16"));
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-endfunction
deleted file mode 100644
--- a/inst/private/imconversion.m
+++ /dev/null
@@ -1,34 +0,0 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 3 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-
-## This is a private fucntion for the common code between the functions that
-## convert an input image into another class. Mostly does the input checking for
-## all of them and returns the class of the input image
-
-function im_class = imconversion (nargs, fname, ind, im1)
-  ## Input checking
-  if (nargs < 1 || nargs > 2)
-    print_usage (fname);
-  elseif (nargs == 2 && (!ischar (ind) || !strcmpi (ind, "indexed")))
-    error ("%s: second argument must be a string with the word `indexed'\n", fname);
-  endif
-
-  if (ind && !isind (im1))
-    error ("%s: input should have been an indexed image but it is not.\n", fname);
-  endif
-
-  im_class = class (im1);
-
-endfunction