Mercurial > hg > octave-image
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