diff inst/imcast.m @ 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 (2014-03-24)
parents 40269ff6760d
children
line wrap: on
line diff
--- 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")