# HG changeset patch
# User rhendz <apatel79@vols.utk.edu>
# Date 1521349636 18000
#      Sun Mar 18 00:07:16 2018 -0500
# Node ID b1f0b18a42bb820dd9dee0b7a86c0419e23e0950
# Parent  54e7c88dd678012d5a985bbc93c1cc6fd3082822
New functions for Octave-Forge Image package

* inst/xyz2double.m: Converts XYZ iamges to double precision
* inst/xyz2single.m: Converts XYZ images to single precision
* inst/xyz2uint16.m: Converts XYZ images to uint16 precision
* inst/private/xyz2cls.m: Private function for xyz2cls conversion, supports colormaps, images,
and ND-images.

diff --git a/inst/private/xyz2cls.m b/inst/private/xyz2cls.m
new file mode 100644
--- /dev/null
+++ b/inst/private/xyz2cls.m
@@ -0,0 +1,78 @@
+## Copyright (C) 2018 Ankush Patel <apatel79@vols.utk.edu>
+##
+## 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/>.
+
+## Private function that is used by xyz2double and xyz2uint16
+## This implementation was based off of lab2cls.m
+
+function xyz = xyz2cls (xyz, cls)
+
+  nd = ndims (xyz);
+  sz = size (xyz);
+
+  ## ND Optimization - minimize changes on the input.
+  ## In order to do this and maintain the same code,
+  ## input must be transformed into a colormap
+  ## with additional images on the 3rd dim.
+  ## This allows for vectorization, speeding
+  ## up the process for conversion.
+  is_image = false; # Is input an image?
+  if (nd == 2 && sz(2) == 3)
+    ## Is a colormap
+  elseif (nd == 3 && sz(3) == 3) # MxNx3 image
+    is_image = true;
+    xyz = reshape (xyz, [sz(1)*sz(2) 3]);
+  elseif (nd == 4 && sz(3) == 3) # MxNx3xK images
+    is_image = true;
+    xyz = reshape (xyz, [sz(1)*sz(2) 3 sz(4)]);
+  else
+    error ("xyz2%s: XYZ must be Mx3, MxNx3, or MxNx3xK size", cls);
+  endif
+
+  im_cls = class (xyz); # Class of image
+  switch (cls)
+    case {"double", "single"}
+      xyz = cast (xyz, cls);
+      switch (im_cls)
+        case "uint16"
+          xyz .*= (2 / 65535);
+        case {"double", "single"}
+          ## cls is equal to im_cls
+        otherwise
+          error ("xyz2%s: invalid class '%s' for XYZ", cls, im_cls);
+      endswitch
+
+    case "uint16"
+      switch (im_cls)
+        case {"double", "single"}
+          xyz .*= ceil (65535 / 2);
+          ## Matlab Compatibility
+          xyz(xyz < 0) = 0; # Handles -Inf
+          xyz(isinf (xyz) | isnan (xyz)) = 65535; # Handles Inf and NaN
+          xyz = uint16 (xyz);
+        case {"uint16"}
+          ## cls is equal to im_cls
+        otherwise
+          error ("xyz2%s: invalid class '%s' for XYZ", cls, im_cls);
+      endswitch
+
+      otherwise
+        error ("xyz2%s: invalid class '%s' for XYZ", cls, im_cls);
+  endswitch
+
+  if (is_image)
+    xyz = reshape (xyz, sz);
+  endif
+
+endfunction
diff --git a/inst/xyz2double.m b/inst/xyz2double.m
new file mode 100644
--- /dev/null
+++ b/inst/xyz2double.m
@@ -0,0 +1,89 @@
+## Copyright (C) 2018 Ankush Patel <apatel79@vols.utk.edu>
+##
+## 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} xyz2double (@var{xyz})
+## Convert 1931 CIE XYZ values to double precision.
+##
+## @var{xyz} must be an XYZ image or colormap; therefore, its dimensions
+## must be MxNx3xK or Mx3. Its type must be double, single, or uint16.
+## There is no standard representation of XYZ values as uint8.
+##
+## When converted to double, XYZ values range from 0.0 to
+## 1.0 + (32767 / 32768). When converting from double or single, the
+## lower bound is 0 and the upper bound is 2. When converting from uint16
+## the lower bound is 0 and the upper bound is 65535.
+##
+## @seealso{xyz2single, xyz2double}
+## @end deftypefn
+
+function dxyz = xyz2double (xyz)
+  if (nargin () != 1)
+    print_usage();
+  endif
+  dxyz = xyz2cls (xyz, "double");
+endfunction
+
+## Edge Cases w/ colormap, image, nd-image
+%!test
+%! im = [ # uint16 values
+%!   -Inf
+%!   Inf
+%!   NaN
+%!   0
+%!   0.0
+%!   0.1
+%!   100
+%!   32768 - 32768 * 0.5
+%!   32768
+%!   32768 + 32768 * 0.5
+%!   65535 - 32768
+%!   65535];
+%! im = uint16 (im);
+%! im = repmat (im, [1 3]);
+%! im2d = reshape (im, [4 3 3]);
+%! imnd = permute (im2d, [1 4 3 2]);
+%!
+%! im_d = [ # correct values - rounded off to the 4th decimal place
+%!    0
+%!    2
+%!    0
+%!    0
+%!    0
+%!    0
+%!    0.0031
+%!    0.5
+%!    1
+%!    1.5
+%!    1
+%!    2];
+%!
+%! im_d = repmat (im_d, [1 3]);
+%! im2d_d = reshape (im_d, [4 3 3]);
+%! imnd_d = permute (im2d_d, [1 4 3 2]);
+%!
+%! assert (xyz2double (im), im_d, 2e-4)
+%! assert (xyz2double (im2d), im2d_d, 2e-4)
+%! assert (xyz2double (imnd), imnd_d, 2e-4)
+%!
+%! assert (xyz2uint16 (im), double (im), 2e-4)
+%! assert (xyz2uint16 (im2d), double (im2d), 2e-4)
+%! assert (xyz2uint16 (imnd), double (imnd), 2e-4)
+
+## Test input validation
+%!error xyz2double ()
+%!error xyz2double (1, 2)
+%!error xyz2double (ones (64, 64))
+%!error xyz2double (ones (64, 64, 2))
diff --git a/inst/xyz2single.m b/inst/xyz2single.m
new file mode 100644
--- /dev/null
+++ b/inst/xyz2single.m
@@ -0,0 +1,89 @@
+## Copyright (C) 2018 Ankush Patel <apatel79@vols.utk.edu>
+##
+## 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} xyz2single (@var{xyz})
+## Convert 1931 CIE XYZ values to single precision.
+##
+## @var{xyz} must be an XYZ image or colormap; therefore, its dimensions
+## must be MxNx3xK or Mx3. Its type must be double, single, or uint16.
+## There is no standard representation of XYZ values as uint8.
+##
+## When converted to single, XYZ values range from 0.0 to
+## 1.0 + (32767 / 32768). When converting from double or single, the
+## lower bound is 0 and the upper bound is 2. When converting from uint16
+## the lower bound is 0 and the upper bound is 65535.
+##
+## @seealso{xyz2double, xyz2uint16}
+## @end deftypefn
+
+function sxyz = xyz2single (xyz)
+  if (nargin () != 1)
+    print_usage();
+  endif
+  sxyz = xyz2cls (xyz, "single");
+endfunction
+
+## Edge Cases w/ colormap, image, nd-image
+%!test
+%! im = [ # uint16 values
+%!   -Inf
+%!   Inf
+%!   NaN
+%!   0
+%!   0.0
+%!   0.1
+%!   100
+%!   32768 - 32768 * 0.5
+%!   32768
+%!   32768 + 32768 * 0.5
+%!   65535 - 32768
+%!   65535];
+%! im = uint16 (im);
+%! im = repmat (im, [1 3]);
+%! im2d = reshape (im, [4 3 3]);
+%! imnd = permute (im2d, [1 4 3 2]);
+%!
+%! im_d = [ # correct values - rounded off to the 4th decimal place
+%!    0
+%!    2
+%!    0
+%!    0
+%!    0
+%!    0
+%!    0.0031
+%!    0.5
+%!    1
+%!    1.5
+%!    1
+%!    2];
+%!
+%! im_d = repmat (im_d, [1 3]);
+%! im2d_d = reshape (im_d, [4 3 3]);
+%! imnd_d = permute (im2d_d, [1 4 3 2]);
+%!
+%! assert (xyz2single (im), im_d, 2e-4)
+%! assert (xyz2single (im2d), im2d_d, 2e-4)
+%! assert (xyz2single (imnd), imnd_d, 2e-4)
+%!
+%! assert (xyz2uint16 (im), single (im), 2e-4)
+%! assert (xyz2uint16 (im2d), single (im2d), 2e-4)
+%! assert (xyz2uint16 (imnd), single (imnd), 2e-4)
+
+## Test input validation
+%!error xyz2single ()
+%!error xyz2single (1, 2)
+%!error xyz2single (ones (64, 64))
+%!error xyz2single (ones (64, 64, 2))
diff --git a/inst/xyz2uint16.m b/inst/xyz2uint16.m
new file mode 100644
--- /dev/null
+++ b/inst/xyz2uint16.m
@@ -0,0 +1,107 @@
+## Copyright (C) 2018 Ankush Patel <apatel79@vols.utk.edu>
+##
+## 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} xyz2uint16 (@var{xyz})
+## Convert 1931 CIE XYZ values to uint16 precision.
+##
+## @var{xyz} must be an XYZ image or colormap; therefore, its dimensions
+## must be MxNx3xK or Mx3. Its type must be double, single, or uint16.
+## There is no standard representation of XYZ values as uint8.
+##
+## When converted to uint16, XYZ values range from 0.0 to
+## 1.0 + (32767 / 32768). When converting from double or single, the
+## lower bound is 0 and the upper bound is 2. When converting from uint16
+## the lower bound is 0 and the upper bound is 65535.
+##
+## @seealso{xyz2double, xyz2single}
+## @end deftypefn
+
+function xyz16 = xyz2uint16 (xyz)
+  if (nargin () != 1)
+    print_usage();
+  endif
+  xyz16 = xyz2cls (xyz, "uint16");
+endfunction
+
+## Edge Cases w/ colormap, image, nd-image
+%!test
+%! im = [ # double vector
+%!   -Inf
+%!   Inf
+%!   NaN
+%!   0.0
+%!   0
+%!   0.1
+%!   0.5
+%!   0.99
+%!   1
+%!   1.01
+%!   1.0
+%!   1.5
+%!   1.99
+%!   2.0
+%!   2
+%!   0 + (32767 / 32768)
+%!   1 + (32767 / 32768)
+%!   1 + (32767 / 32768) * 0.5
+%!   1 - (32767 / 32768)
+%!   1 - (32767 / 32768) * 0.5
+%!   2 - (32767 / 32768)];
+%! im = repmat (im, [1 3]);
+%! im2d = reshape (im, [7 3 3]);
+%! imnd = permute (im2d, [1 4 3 2]);
+%!
+%! im_uint16 = [ # correct values
+%!   0
+%!   65535
+%!   65535
+%!   0
+%!   0
+%!   3277
+%!   16384
+%!   32440
+%!   32768
+%!   33096
+%!   32768
+%!   49152
+%!   65208
+%!   65535
+%!   65535
+%!   32767
+%!   65535
+%!   49152
+%!   1
+%!   16385
+%!   32769
+%! ];
+%! im_uint16 = uint16 (im_uint16);
+%! im_uint16 = repmat (im_uint16, [1 3]);
+%! im2d_uint16 = reshape (im_uint16, [7 3 3]);
+%! imnd_uint16 = permute (im2d_uint16, [1 4 3 2]);
+%!
+%! assert (xyz2uint16 (im), im_uint16)
+%! assert (xyz2uint16 (im2d), im2d_uint16)
+%! assert (xyz2uint16 (imnd), imnd_uint16)
+%!
+%! assert (xyz2double (im), double (im))
+%! assert (xyz2double (im2d), double (im2d))
+%! assert (xyz2double (imnd), double (imnd))
+
+## Test input validation
+%!error xyz2uint16 ()
+%!error xyz2uint16 (1, 2)
+%!error xyz2uint16 (ones (64, 64))
+%!error xyz2uint16 (ones (64, 64, 2))
