1## Copyright (C) 2014 Carnë Draug <carandraug+dev@gmail.com>
2##
3## This program is free software; you can redistribute it and/or modify it under
4## the terms of the GNU General Public License as published by the Free Software
5## Foundation; either version 3 of the License, or (at your option) any later
6## version.
7##
8## This program is distributed in the hope that it will be useful, but WITHOUT
9## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11## details.
12##
13## You should have received a copy of the GNU General Public License along with
14## this program; if not, see <http://www.gnu.org/licenses/>.
15
16## -*- texinfo -*-
17## @deftypefn  {Function File} {} imcast (@var{img}, @var{type})
18## @deftypefnx {Function File} {} imcast (@var{img}, @var{type}, "indexed")
19## Convert image to specific data type @var{type}.
20##
21## Converts a valid image @var{img} into another class @var{type}.  A valid
22## image must be of class logical, uint8, uint16, int16, single, or double.
23##
24## If the image is indexed, the last argument may be the string
25## @qcode{"indexed"}.  An indexed image may not be of class int16,
26## single, or logical (see @code{isind} for details).
27##
28## Details on how the conversion is performed is class dependent, and
29## can be seen on the help text of @code{im2double}, @code{im2single},
30## @code{im2uint8}, @code{im2uint16}, and @code{im2int16}.  For the
31## case of logical, conversion to logical is equivalent to simply
32## casting @var{img} to logical data type.  Conversion from logical,
33## true elements are converted to 1 in the case of floating point, or
34## the maximum value in the case of integer types.
35##
36## @seealso{im2bw, im2uint8, im2double, im2int16, im2single, im2uint16}
37## @end deftypefn
38
39function imout = imcast (img, outcls, varargin)
40
41  if (nargin < 2 || nargin > 3)
42    print_usage ();
43  elseif (nargin == 3 && ! strcmpi (varargin{1}, "indexed"))
44    error ("imcast: third argument must be the string \"indexed\"");
45  endif
46
47  incls = class (img);
48  if (strcmp (outcls, incls))
49    imout = img;
50    return
51  endif
52
53  ## we are dealing with indexed images
54  if (nargin == 3)
55    if (! isind (img))
56      error ("imcast: input should have been an indexed image but it is not.");
57    endif
58
59    ## Check that the new class is enough to hold all the previous indices
60    ## If we are converting to floating point, then we don't bother
61    ## check the range of indices. Also, note that indexed images of
62    ## integer class are always unsigned.
63
64    ## we will be converting a floating point image to integer class
65    if (strcmp (outcls, "single") || strcmp (outcls, "double"))
66      if (isinteger (img))
67        imout = cast (img, outcls) +1;
68      else
69        imout = cast (img, outcls);
70      endif
71
72    ## we will be converting an indexed image to integer class
73    else
74      if (isinteger (img) && intmax (incls) > intmax (outcls) && max (img(:)) > intmax (outcls))
75          error ("imcast: IMG has too many colours '%d' for the range of values in %s",
76            max (img(:)), outcls);
77      elseif (isfloat (img))
78        imax = max (img(:)) -1;
79        if (imax > intmax (outcls))
80          error ("imcast: IMG has too many colours '%d' for the range of values in %s",
81            imax, outcls);
82        endif
83        img -= 1;
84      endif
85      imout = cast (img, outcls);
86    endif
87
88  ## we are dealing with "normal" images
89  else
90    problem = false; # did we found a bad conversion?
91    switch (incls)
92
93      case {"double", "single"}
94        switch (outcls)
95          case "uint8",  imout = uint8  (img * 255);
96          case "uint16", imout = uint16 (img * 65535);
97          case "int16",  imout = int16 (double (img * uint16 (65535)) -32768);
98          case {"double", "single"}, imout = cast (img, outcls);
99          case "logical", imout = logical (img);
100          otherwise, problem = true;
101        endswitch
102
103      case {"uint8"}
104        switch (outcls)
105          case "double", imout = double (img) / 255;
106          case "single", imout = single (img) / 255;
107          case "uint16", imout = uint16 (img) * 257; # 257 comes from 65535/255
108          case "int16",  imout = int16 ((double (img) * 257) -32768); # 257 comes from 65535/255
109          case "logical", imout = logical (img);
110          otherwise, problem = true;
111        endswitch
112
113      case {"uint16"}
114        switch (outcls)
115          case "double", imout = double (img) / 65535;
116          case "single", imout = single (img) / 65535;
117          case "uint8",  imout = uint8 (img / 257); # 257 comes from 65535/255
118          case "int16",  imout = int16 (double (img) -32768);
119          case "logical", imout = logical (img);
120          otherwise, problem = true;
121        endswitch
122
123      case {"logical"}
124        switch (outcls)
125          case {"double", "single"}
126            imout = cast (img, outcls);
127          case {"uint8", "uint16", "int16"}
128            imout = repmat (intmin (outcls), size (img));
129            imout(img) = intmax (outcls);
130          otherwise
131            problem = true;
132        endswitch
133
134      case {"int16"}
135        switch (outcls)
136          case "double", imout = (double (img) + 32768) / 65535;
137          case "single", imout = (single (img) + 32768) / 65535;
138          case "uint8",  imout = uint8 ((double (img) + 32768) / 257); # 257 comes from 65535/255
139          case "uint16", imout = uint16 (double (img) + 32768);
140          case "logical", imout = logical (img);
141          otherwise, problem = true;
142        endswitch
143
144      otherwise
145        error ("imcast: unknown image of class \"%s\"", incls);
146
147    endswitch
148    if (problem)
149      error ("imcast: unsupported TYPE \"%s\"", outcls);
150    endif
151  endif
152
153endfunction
154
155%!test
156%! im = randi ([0 255], 40, "uint8");
157%! assert (imcast (im, "uint8"), im2uint8 (im))
158%! assert (imcast (im, "uint16"), im2uint16 (im))
159%! assert (imcast (im, "single"), im2single (im))
160%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed"))
161%! assert (imcast (im, "uint16", "indexed"), im2uint16 (im, "indexed"))
162%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
163
164%!test
165%! im = randi ([1 256], 40, "double");
166%! assert (imcast (im, "uint8"), im2uint8 (im))
167%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed"))
168%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
169
170%!test
171%! im = randi ([0 65535], 40, "uint16");
172%! assert (imcast (im, "uint8"), im2uint8 (im))
173%! assert (imcast (im, "single"), im2single (im))
174%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
175
176%!test
177%! im = randi ([1 255], 40, "double");
178%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed"))
179%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
180
181%!test
182%! im = rand (40);
183%! assert (imcast (im, "uint8"), im2uint8 (im))
184
185%!error <unknown image of class> imcast (randi (127, 40, "int8"), "uint8")
186%!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "uint32")
187%!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "not a class")
188%!error <range of values> imcast (randi ([0 65535], 40, "uint16"), "uint8", "indexed")
189
190%!assert (imcast ([0 1 .2; 2 -0 Inf], "logical"), logical ([0 1 1; 1 0 1]))
191