%COLORSPACE   Gets/sets/changes the color space.
%    IN = COLORSPACE(IN,COL), with IN a color image, changes the color
%    space of image IN to COL, converting the pixel values as
%    required. If COL is 'grey', a scalar image is returned with the
%    luminosity (Y).
%
%    IN = COLORSPACE(IN,COL), with IN a tensor image, sets the color
%    space of image IN to COL.
%
%    COL = COLORSPACE(IN) returns the name of the color space of the
%    image IN.
%
%    Converting to a color-space-less tensor image is done by
%    specifying the empty string as a color space. This action only
%    changes the color space information, and does not change any
%    pixel values. Thus, to change from one color space to another
%    without converting the pixel values themselves, change first to
%    a color-space-less tensor image, and then to the final color
%    space.
%
%    A color space is any string recognized by the system. It is
%    possible to specify any other string as color space, but no
%    conversion of pixel values can be made, since the system doesn't
%    know about that color space. Recognized color spaces are:
%       L*a*b* (or Lab, CIELAB)
%       L*u*v* (or Luv, CIELUV)
%       L*C*H* (or LCH)
%       RGB
%       R'G'B'
%       XYZ
%       Yxy
%       CMY
%       CMYK
%       HCV
%       HSV

% (C) Copyright 1999-2008               Pattern Recognition Group
%     All rights reserved               Faculty of Applied Physics
%                                       Delft University of Technology
%                                       Lorentzweg 1
%                                       2628 CJ Delft
%                                       The Netherlands
%
% Cris Luengo, October 2000.
% 20 April 2001: Known color spaces now always produce valid images
%                (i.e. adding blank images or removing them).
% 27 June 2001: Added color spaces: R'G'B' , L*u*v* .
%               Added aliases: rgb==RGB, lab==LAB==Lab==L*a*b*, etc.
%               Re-wrote function intelligence. We now don't need to
%               go through XYZ if it isn't necessary.
% 27 August 2001: Fixed stupid bug.
% 30 September 2001: Fixed another stupid bug.
% 8 March 2002: Added 'gray' to the alias list.
%    June 2002: (JD) Added CMY and CMYK
%  5 June 2002: (JD) added HCV and HSV
% 12 June 2002: (CL) Binary images are converted to 'uint8' before creating
%               the color image.
% 15 November 2002: Fixed binary images to work in MATLAB 6.5 (R13)
% 27 November 2003: Fixed removing of colorspace information.
% 17 July 2007: Added %#function pragma.
% 19 February 2008: RGB2GREY was never used, the table said to do RGB2XYZ and then XYZ2GREY.
% 14 July 2008: Added converion of grey-valued image to color.
% 1 August 2008: When no conversion is necessary, we now check for correctness.
% 28 August 2008: Added Piet's ART color space and L*C*H* color space.

% To add color spaces:
%   - To the TABLE in COLORSPACE_TRANSFORM_PATH, insert a row before the last
% one (2grey), and a column at the end.
%   - Add the name to the list in KNOWN_COLORSPACES.
%   - Add a CASE entry in COLORSPACE_CHANNELS.
%   - Optionally add aliases to COLOR_ALIAS.
%   - For each entry added to the TABLE in COLORSPACE_TRANSFORM_PATH, make a
% function in private/ for that color conversion, and add the function name
% to the "%#function" pragmas a few lines below this one.


function out = colorspace(in,newcol)
%#function cmy2cmyk cmy2rgb cmyk2cmy grey2lab grey2luv grey2rgb grey2xyz grey2yxy
%#function hcv2hsv hcv2rgb hsv2hcv lab2grey lab2xyz luv2xyz
%#function rgb2cmy rgb2grey rgb2hcv rgb2rgbnl rgb2xyz rgbnl2rgb
%#function xyz2grey xyz2lab xyz2luv xyz2rgb xyz2yxy yxy2grey yxy2xyz
%#function art2lab lab2art lch2lab lab2lch

if nargin < 1, error('Need an input argument.'); end
if nargin == 2
% Set/change color space
   if ~ischar(newcol), error('Color space name must be a string.'); end
      % (this assures IN is a dip_image)
   if ~istensor(in), error('Images in array are not of same size.'); end
   newcol = color_alias(newcol);
   if ~isempty(in(1).color)
      oldcol = in(1).color.space;
      if ~any(strcmp(oldcol,known_colorspaces))
         warning('Image has unknown color space. No conversion done.')
         oldcol = '';
      end
   else
      if isscalar(in)
         oldcol = 'grey';
      else
         oldcol = '';
      end
   end
   % Set or convert channels
   if isempty(newcol)
      out = in;
      [out.color] = deal([]);
   elseif isempty(oldcol) | strcmp(oldcol,newcol)
      % Make sure the image satisfies the requirements for the output type
      for ii=1:prod(imarsize(in))
         if strcmp(in(ii).dip_type,'bin')
            in(ii).dip_type = 'uint8';
         end
      end
      N = colorspace_channels(newcol);
      if N == 0
         out = in;
      else
         out = dip_image('array',[N,1]);
         M = min(N,prod(imarsize(in)));
         out(1:M) = in(1:M);
         out(M+1:N) = dip_image('zeros',size(in(1)));
      end
      out = di_setcolspace(out,out(1).color,newcol);
   else
      % Convert colors from old to new color space following tabled path
      path = colorspace_transform_path(oldcol,newcol);
      out = in;
      if ~iscell(path)
         warning('Destination color space is unknown. No conversion done.')
      elseif isempty(path)
         %!? shouldn't happen, but...
      else
         for ii=1:length(path)-1
            fn = [path{ii},'2',path{ii+1}];
            out = feval(fn,out);
         end
      end
   end
else
% Get color space
   if ~iscolor(in)
      out = '';
   else
      out = in(1).color.space;
   end
end


function col = color_alias(col)
switch lower(col)
   case {'lab','cielab','l*a*b*'}
      col = 'L*a*b*';
   case {'luv','cieluv','l*u*v*'}
      col = 'L*u*v*';
   case {'lch','l*c*h*'}
      col = 'L*C*H*';
   case 'rgb'
      col = 'RGB';
   case 'r''g''b'''
      col = 'R''G''B''';
   case 'xyz'
      col = 'XYZ';
   case 'yxy'
      col = 'Yxy';
   case {'cmy'}
      col = 'CMY';
   case {'cmyk'}
      col = 'CMYK';
   case {'hcv'}
      col = 'HCV';
   case {'hsv'}
      col = 'HSV';
   case {'gray'}
      col = 'grey';
   otherwise
      col = col;
end


function list = known_colorspaces
list = {'L*a*b*','L*u*v*','RGB','R''G''B''','XYZ','Yxy','CMY','CMYK','HCV','HSV','art','L*C*H*'};


function path = colorspace_transform_path(oldcol,newcol)
colorspaces = known_colorspaces;
if strcmp(newcol,'grey')
   newcol = length(colorspaces)+1;
else
   newcol = find(strcmp(newcol,colorspaces));
   if length(newcol) ~= 1
      newcol = [];
      path = [];
      return
   end
end
if strcmp(oldcol,'grey')
   oldcol = length(colorspaces)+1;
else
   oldcol = find(strcmp(oldcol,colorspaces));
   if length(oldcol) ~= 1
      error('Assertion failed: color space table is corrupt.')
   end
end
table = { {},                              {'luv','xyz','lab'},             {'rgb','xyz','lab'},      {'rgbnl','rgb','xyz','lab'},  {'xyz','lab'},             {'yxy','xyz','lab'},             {'cmy','rgb','xyz','lab'},      {'cmyk','cmy','rgb','xyz','lab'},      {'hcv','rgb','xyz','lab'},      {'hsv','hcv','rgb','xyz','lab'},      {'art','lab'},                         {'lch','lab'},                         {'grey','lab'};
          {'lab','xyz','luv'},             {},                              {'rgb','xyz','luv'},      {'rgbnl','rgb','xyz','luv'},  {'xyz','luv'},             {'yxy','xyz','luv'},             {'cmy','rgb','xyz','luv'},      {'cmyk','cmy','rgb','xyz','luv'},      {'hcv','rgb','xyz','luv'},      {'hsv','hcv','rgb','xyz','luv'},      {'art','lab','xyz','luv'},             {'lch','lab','xyz','luv'},             {'grey','luv'};
          {'lab','xyz','rgb'},             {'luv','xyz','rgb'},             {},                       {'rgbnl','rgb'},              {'xyz','rgb'},             {'yxy','xyz','rgb'},             {'cmy','rgb'},                  {'cmyk','cmy','rgb'},                  {'hcv','rgb'},                  {'hsv','hcv','rgb'},                  {'art','lab','xyz','rgb'},             {'lch','lab','xyz','rgb'},             {'grey','rgb'};
          {'lab','xyz','rgb','rgbnl'},     {'luv','xyz','rgb','rgbnl'},     {'rgb','rgbnl'},          {},                           {'xyz','rgb','rgbnl'},     {'yxy','xyz','rgb','rgbnl'},     {'cmy','rgb','rgbnl'},          {'cmyk','cmy','rgb','rgbnl'},          {'hcv','rgb','rgbnl'},          {'hsv','hcv','rgb','rgbnl'},          {'art','lab','xyz','rgb','rgbnl'},     {'lch','lab','xyz','rgb','rgbnl'},     {'grey','rgb','rgbnl'};
          {'lab','xyz'},                   {'luv','xyz'},                   {'rgb','xyz'},            {'rgbnl','rgb','xyz'},        {},                        {'yxy','xyz'},                   {'cmy','rgb','xyz'},            {'cmyk','cmy','rgb','xyz'},            {'hcv','rgb','xyz'},            {'hsv','hcv','rgb','xyz'},            {'art','lab','xyz'},                   {'lch','lab','xyz'},                   {'grey','xyz'};
          {'lab','xyz','yxy'},             {'luv','xyz','yxy'},             {'rgb','xyz','yxy'},      {'rgbnl','rgb','xyz','yxy'},  {'xyz','yxy'},             {} ,                             {'cmy','rgb','xyz','yxy'},      {'cmyk','cmy','rgb','xyz','yxy'},      {'hcv','rgb','xyz','yxy'},      {'hsv','hcv','rgb','xyz','yxy'},      {'art','lab','xyz','yxy'},             {'lch','lab','xyz','yxy'},             {'grey','xyz','yxy'};
          {'lab','xyz','rgb','cmy'},       {'luv','xyz','rgb','cmy'},       {'rgb','cmy'},            {'rgbnl','rgb','cmy'},        {'xyz','rgb','cmy'},       {'yxy','xyz','rgb','cmy'},       {},                             {'cmyk','cmy'},                        {'hcv','rgb','cmy'},            {'hsv','hcv','rgb','cmy'},            {'art','lab','xyz','rgb','cmy'},       {'lch','lab','xyz','rgb','cmy'},       {'grey','rgb','cmy'};
          {'lab','xyz','rgb','cmy','cmyk'},{'luv','xyz','rgb','cmy','cmyk'},{'rgb','cmy','cmyk'},     {'rgbnl','rgb','cmy','cmyk'}, {'xyz','rgb','cmy','cmyk'},{'yxy','xyz','rgb','cmy','cmyk'},{'cmy','cmyk'},                 {},                                    {'hcv','rgb','cmy','cmyk'},     {'hsv','hcv','rgb','cmy','cmyk'},     {'art','lab','xyz','rgb','cmy','cmyk'},{'lch','lab','xyz','rgb','cmy','cmyk'},{'grey','rgb','cmy','cmyk'};
          {'lab','xyz','rgb','hcv'},       {'luv','xyz','rgb','hcv'},       {'rgb','hcv'},            {'rgbnl','rgb','hcv'},        {'xyz','rgb','hcv'},       {'yxy','xyz','rgb','hcv'},       {'cmy','rgb','hcv'},            {'cmyk','cmy','rgb','hcv'},            {},                             {'hsv','hcv'},                        {'art','lab','xyz','rgb','hcv'},       {'lch','lab','xyz','rgb','hcv'},       {'grey','rgb','hcv'};
          {'lab','xyz','rgb','hcv','hsv'}, {'luv','xyz','rgb','hcv','hsv'}, {'rgb','hcv','hsv'},      {'rgbnl','rgb','hcv','hsv'},  {'xyz','rgb','hcv','hsv'}, {'yxy','xyz','rgb','hcv','hsv'}, {'cmy','rgb','hcv','hsv'},      {'cmyk','cmy','rgb','hcv','hsv'},      {'hcv','hsv'},                  {},                                   {'art','lab','xyz','rgb','hcv','hsv'}, {'lch','lab','xyz','rgb','hcv','hsv'}, {'grey','rgb','hcv','hsv'};
          {'lab','art'},                   {'luv','xyz','lab','art'},       {'rgb','xyz','lab','art'},{'rgbnl','rgb','xyz','lab','art'},{'xyz','lab','art'},   {'yxy','xyz','lab','art'},       {'cmy','rgb','xyz','lab','art'},{'cmyk','cmy','rgb','xyz','lab','art'},{'hcv','rgb','xyz','lab','art'},{'hsv','hcv','rgb','xyz','lab','art'},{},                                    {'lch','lab','art'},                   {'grey','lab','art'};
          {'lab','lch'},                   {'luv','xyz','lab','lch'},       {'rgb','xyz','lab','lch'},{'rgbnl','rgb','xyz','lab','lch'},{'xyz','lab','lch'},   {'yxy','xyz','lab','lch'},       {'cmy','rgb','xyz','lab','lch'},{'cmyk','cmy','rgb','xyz','lab','lch'},{'hcv','rgb','xyz','lab','lch'},{'hsv','hcv','rgb','xyz','lab','lch'},{'art','lab','lch'},                   {},                                    {'grey','lab','lch'};
          {'lab','grey'},                  {'lab','grey'},                  {'rgb','grey'},           {'rgbnl','rgb','grey'},       {'xyz','grey'},            {'yxy','grey'},                  {'cmy','rgb','grey'},           {'cmyk','cmy','rgb','grey'},           {'hcv','rgb','grey'},           {'hsv','hcv','rgb','grey'},           {'art','lab','grey'},                  {'lab','grey'},                        {}};
          %                                  lab2grey also works for L*u*v*                             rgbnl functions implement R'G'B' color space                                                                                                                                                                                                                                  lab2grey also works for L*C*H*
path = table{newcol,oldcol}; % Damn! I defined the table the wrong way around...

function N = colorspace_channels(newcol)
switch newcol
   case {'CMYK'}
      N = 4;
   case {'L*a*b*','L*u*v*','RGB','R''G''B''','XYZ','Yxy','CMY','CMYK','HCV','HSV','art','L*C*H*'}
      N = 3;
   case 'grey'
      N = 1;
   otherwise
      N = 0;
end
