Source code for luxpy.color.cam.colorappearancemodels

# -*- coding: utf-8 -*-
########################################################################
# <LUXPY: a Python package for lighting and color science.>
# Copyright (C) <2017>  <Kevin A.G. Smet> (ksmet1977 at 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.
#
# 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/>.
#########################################################################
"""
cam: sub-package with color appearance models
=============================================

 : _AVAILABLE_MODELS: List with available color appearance models.

 :_UNIQUE_HUE_DATA: | database of unique hues with corresponding 
                    | Hue quadratures and eccentricity factors 
                    | for ciecam02, ciecam16, ciecam97s, cam15u, cam18sl)

 :_SURROUND_PARAMETERS: | database of surround param. c, Nc, F and FLL 
                        | for ciecam02, ciecam16, ciecam97s and cam15u.

 :_NAKA_RUSHTON_PARAMETERS: | database with parameters (n, sig, scaling and noise) 
                            | for the Naka-Rushton function: 
                            | NK(x) = sign(x) * scaling * ((abs(x)**n) / ((abs(x)**n) + (sig**n))) + noise

 :_CAM_UCS_PARAMETERS: | database with parameters specifying the conversion 
                       |  from ciecamX to:
                       |    camXucs (uniform color space), 
                       |    camXlcd (large color diff.), 
                       |    camXscd (small color diff).
                            
 :_CAM15U_PARAMETERS: database with CAM15u model parameters.
 
 :_CAM_SWW16_PARAMETERS: cam_sww16 model parameters.
 
 :_CAM18SL_PARAMETERS: database with CAM18sl model parameters

 :_CAM_DEFAULT_WHITE_POINT: Default internal reference white point (xyz)

 :_CAM_DEFAULT_CONDITIONS: Default CAM model parameters for model.
 
 :_CAM_DEFAULT_TYPE: Default CAM (string) [for use in other modules].

 :_CAM_AXES: dict with list[str,str,str] containing axis labels of defined cspaces.
                  
 :deltaH(): Compute a hue difference, dH = 2*C1*C2*sin(dh/2).

 :naka_rushton(): applies a Naka-Rushton function to the input
 
 :hue_angle(): calculates a positive hue angle

 :hue_quadrature(): calculates the Hue quadrature from the hue.

 
 :ciecam02(): | calculates ciecam02 output 
              | `N. Moroney, M. D. Fairchild, R. W. G. Hunt, C. Li, M. R. Luo, and T. Newman, 
                “The CIECAM02 color appearance model,” 
                IS&T/SID Tenth Color Imaging Conference. p. 23, 2002. <http://rit-mcsl.org/fairchild/PDFs/PRO19.pdf>`_

 :cam16(): | calculates cam16 output 
           | `C. Li, Z. Li, Z. Wang, Y. Xu, M. R. Luo, G. Cui, M. Melgosa, M. H. Brill, and M. Pointer, 
             “Comprehensive color solutions: CAM16, CAT16, and CAM16-UCS,” 
             Color Res. Appl., p. n/a–n/a. <http://onlinelibrary.wiley.com/doi/10.1002/col.22131/abstract>`_
           

 :cam02ucs(): | calculates ucs (or lcd, scd) output based on ciecam02 
              |  (forward + inverse available)
              |  `M. R. Luo, G. Cui, and C. Li, 
                 “Uniform colour spaces based on CIECAM02 colour appearance model,” 
                 Color Res. Appl., vol. 31, no. 4, pp. 320–330, 2006.
                 <http://onlinelibrary.wiley.com/doi/10.1002/col.20227/abstract>`_

 :cam16ucs(): | calculates ucs (or lcd, scd) output based on cam16 
              |  (forward + inverse available)
              | `C. Li, Z. Li, Z. Wang, Y. Xu, M. R. Luo, G. Cui, M. Melgosa, M. H. Brill, and M. Pointer, 
                “Comprehensive color solutions: CAM16, CAT16, and CAM16-UCS,” 
                Color Res. Appl., p. n/a–n/a. <http://onlinelibrary.wiley.com/doi/10.1002/col.22131/abstract>`_

 :cam15u(): | calculates the output for the CAM15u model for self-luminous unrelated stimuli. 
            | `M. Withouck, K. A. G. Smet, W. R. Ryckaert, and P. Hanselaer, 
              “Experimental driven modelling of the color appearance of 
              unrelated self-luminous stimuli: CAM15u,” 
              Opt. Express, vol. 23, no. 9, pp. 12045–12064, 2015.
              <https://www.osapublishing.org/oe/abstract.cfm?uri=oe-23-9-12045&origin=search>`_
            | `M. Withouck, K. A. G. Smet, and P. Hanselaer, (2015), 
            “Brightness prediction of different sized unrelated self-luminous stimuli,” 
            Opt. Express, vol. 23, no. 10, pp. 13455–13466. 
            <https://www.osapublishing.org/oe/abstract.cfm?uri=oe-23-10-13455&origin=search>`_
            
 :cam_sww16(): | A simple principled color appearance model based on a mapping 
                 of the Munsell color system.
               | `Smet, K. A. G., Webster, M. A., & Whitehead, L. A. (2016). 
                   A simple principled approach for modeling and understanding uniform color metrics. 
                   Journal of the Optical Society of America A, 33(3), A319–A331. 
                   <https://doi.org/10.1364/JOSAA.33.00A319>`_
               
 :cam18sl(): | calculates the output for the CAM18sl model for self-luminous related stimuli. 
             | `Hermans, S., Smet, K. A. G., & Hanselaer, P. (2018). 
               "Color appearance model for self-luminous stimuli."
               Journal of the Optical Society of America A, 35(12), 2000–2009. 
               <https://doi.org/10.1364/JOSAA.35.002000>`_       
               
 :camXucs(): Wraps ciecam02(), ciecam16(), cam02ucs(), cam16ucs().

 :specific_wrappers_in_the_'xyz_to_cspace()' and 'cpsace_to_xyz()' format:
      | 'xyz_to_jabM_ciecam02', 'jabM_ciecam02_to_xyz',
      | 'xyz_to_jabC_ciecam02', 'jabC_ciecam02_to_xyz',
      | 'xyz_to_jabM_ciecam16', 'jabM_ciecam16_to_xyz',
      | 'xyz_to_jabC_ciecam16', 'jabC_ciecam16_to_xyz',
      | 'xyz_to_jabz',          'jabz_to_xyz',
      | 'xyz_to_jabM_zcam',     'jabM_zcam_to_xyz', 
      | 'xyz_to_jabC_zcam',     'jabC_zcam_to_xyz']
      | 'xyz_to_jab_cam02ucs', 'jab_cam02ucs_to_xyz', 
      | 'xyz_to_jab_cam02lcd', 'jab_cam02lcd_to_xyz',
      | 'xyz_to_jab_cam02scd', 'jab_cam02scd_to_xyz', 
      | 'xyz_to_jab_cam16ucs', 'jab_cam16ucs_to_xyz',
      | 'xyz_to_jab_cam16lcd', 'jab_cam16lcd_to_xyz',
      | 'xyz_to_jab_cam16scd', 'jab_cam16scd_to_xyz',
      | 'xyz_to_qabW_cam15u', 'qabW_cam15u_to_xyz',
      | 'xyz_to_lab_cam_sww16', 'lab_cam_sww16_to_xyz',
      | 'xyz_to_qabM_cam18sl', 'qabM_cam18sl_to_xyz',
      | 'xyz_to_qabs_cam18sl', 'qabs_cam18sl_to_xyz',
      

 :_update_parameter_dict(): Get parameter dict and update with values in args dict

 :_setup_default_adaptation_field(): Setup a default illuminant adaptation field with Lw = 100 cd/m² for selected CIE observer.

 :_massage_input_and_init_output(): Redimension input data to ensure most they have the appropriate sizes for easy and efficient looping.

 :_massage_output_data_to_original_shape(): Massage output data to restore original shape of original CAM input.
 
 :_get_absolute_xyz_xyzw(): Calculate absolute xyz tristimulus values of stimulus and white point from spectral input or convert relative xyz values to absolute ones.
 
 :_simple_cam(): An example CAM illustration the usage of the functions in luxpy.cam.helpers 


.. codeauthor:: Kevin A.G. Smet (ksmet1977 at gmail.com)
"""
#------------------------------------------------------------------------------
# List available CAMs:
_AVAILABLE_MODELS = ['ciecam02','cam02ucs','ciecam16','cam16ucs',
                     'zcam','cam15u','cam18sl','cam_sww16']

__all__ = ['_AVAILABLE_MODELS']

#------------------------------------------------------------------------------
# Utility imports:
from .utils import hue_angle, naka_rushton, deltaH, hue_quadrature

__all__ += ['hue_angle', 'naka_rushton', 'deltaH', 'hue_quadrature']

#------------------------------------------------------------------------------
# Helper functions imports:
from .helpers import (_update_parameter_dict,_setup_default_adaptation_field,
                      _massage_input_and_init_output,_massage_output_data_to_original_shape,
                      _get_absolute_xyz_xyzw,_simple_cam)

__all__ += ['_update_parameter_dict','_setup_default_adaptation_field',
           '_massage_input_and_init_output','_massage_output_data_to_original_shape',
           '_get_absolute_xyz_xyzw','_simple_cam']

#------------------------------------------------------------------------------
# ciecam02 imports:
# import ciecam02 as _ciecam02
from .ciecam02 import run as ciecam02
from .ciecam02 import _AXES as _CIECAM02_AXES
from .ciecam02 import _UNIQUE_HUE_DATA as _CIECAM02_UNIQUE_HUE_DATA
from .ciecam02 import _SURROUND_PARAMETERS as _CIECAM02_SURROUND_PARAMETERS
from .ciecam02 import _NAKA_RUSHTON_PARAMETERS as _CIECAM02_NAKA_RUSHTON_PARAMETERS
from .ciecam02 import _DEFAULT_WHITE_POINT as _CIECAM02_DEFAULT_WHITE_POINT
from .ciecam02 import _DEFAULT_CONDITIONS as _CIECAM02_DEFAULT_CONDITIONS
from .ciecam02 import (xyz_to_jabM_ciecam02, jabM_ciecam02_to_xyz, 
                       xyz_to_jabC_ciecam02, jabC_ciecam02_to_xyz)

__all__ += ['ciecam02'] 

# __all__ += ['_CIECAM02_AXES',
#             '_CIECAM02_UNIQUE_HUE_DATA',
#             '_CIECAM02_DEFAULT_WHITE_POINT',
#             '_CIECAM02_SURROUND_PARAMETERS', 
#             '_CIECAM02_NAKA_RUSHTON_PARAMETERS']

__all__ += ['xyz_to_jabM_ciecam02', 'jabM_ciecam02_to_xyz', 
            'xyz_to_jabC_ciecam02', 'jabC_ciecam02_to_xyz']

#------------------------------------------------------------------------------
# cam02ucs imports:
# import cam02ucs as _cam02ucs
from .cam02ucs import run as cam02ucs
from .cam02ucs import _AXES as _CAM02UCS_AXES
from .cam02ucs import _CAM_UCS_PARAMETERS as _CAM02UCS_UCS_PARAMETERS
from .cam02ucs import _DEFAULT_WHITE_POINT as _CAM02UCS_DEFAULT_WHITE_POINT
from .cam02ucs import _DEFAULT_CONDITIONS as _CAM02UCS_DEFAULT_CONDITIONS
from .cam02ucs import (xyz_to_jab_cam02ucs, jab_cam02ucs_to_xyz, 
                       xyz_to_jab_cam02lcd, jab_cam02lcd_to_xyz,
                       xyz_to_jab_cam02scd, jab_cam02scd_to_xyz)

__all__  += ['cam02ucs'] 

# __all__ += ['_CAM02UCS_AXES',
#             '_CAM02UCS_UCS_PARAMETERS',
#             '_CAM02UCS_DEFAULT_WHITE_POINT']

__all__ += ['xyz_to_jab_cam02ucs', 'jab_cam02ucs_to_xyz', 
            'xyz_to_jab_cam02lcd', 'jab_cam02lcd_to_xyz',
            'xyz_to_jab_cam02scd', 'jab_cam02scd_to_xyz']


#------------------------------------------------------------------------------
# ciecam16 imports:
# import ciecam16 as _ciecam16
from .ciecam16 import run as ciecam16
from .ciecam16 import _AXES as _CIECAM16_AXES
from .ciecam16 import _UNIQUE_HUE_DATA as _CIECAM16_UNIQUE_HUE_DATA
from .ciecam16 import _SURROUND_PARAMETERS as _CIECAM16_SURROUND_PARAMETERS
from .ciecam16 import _NAKA_RUSHTON_PARAMETERS as _CIECAM16_NAKA_RUSHTON_PARAMETERS
from .ciecam16 import _DEFAULT_WHITE_POINT as _CIECAM16_DEFAULT_WHITE_POINT
from .ciecam16 import _DEFAULT_CONDITIONS as _CIECAM16_DEFAULT_CONDITIONS
from .ciecam16 import (xyz_to_jabM_ciecam16, jabM_ciecam16_to_xyz, 
                       xyz_to_jabC_ciecam16, jabC_ciecam16_to_xyz)

__all__ += ['ciecam16'] 

# __all__ += ['_CIECAM16_AXES',
#             '_CIECAM16_UNIQUE_HUE_DATA',
#             '_CIECAM16_DEFAULT_WHITE_POINT',
#             '_CIECAM16_SURROUND_PARAMETERS', 
#             '_CIECAM16_NAKA_RUSHTON_PARAMETERS']

__all__ += ['xyz_to_jabM_ciecam16', 'jabM_ciecam16_to_xyz', 
            'xyz_to_jabC_ciecam16', 'jabC_ciecam16_to_xyz']

#------------------------------------------------------------------------------
# cam16ucs imports:
# import cam16ucs as _cam16ucs
from .cam16ucs import run as cam16ucs
from .cam16ucs import _AXES as _CAM16UCS_AXES
from .cam16ucs import _CAM_UCS_PARAMETERS as _CAM16UCS_UCS_PARAMETERS
from .cam16ucs import _DEFAULT_WHITE_POINT as _CAM16UCS_DEFAULT_WHITE_POINT
from .cam16ucs import _DEFAULT_CONDITIONS as _CAM16UCS_DEFAULT_CONDITIONS
from .cam16ucs import (xyz_to_jab_cam16ucs, jab_cam16ucs_to_xyz, 
                       xyz_to_jab_cam16lcd, jab_cam16lcd_to_xyz,
                       xyz_to_jab_cam16scd, jab_cam16scd_to_xyz)

__all__  += ['cam16ucs'] 

# __all__ += ['_CAM16UCS_AXES',
#             '_CAM16UCS_UCS_PARAMETERS',
#             '_CAM16UCS_DEFAULT_WHITE_POINT']

__all__ += ['xyz_to_jab_cam16ucs', 'jab_cam16ucs_to_xyz', 
            'xyz_to_jab_cam16lcd', 'jab_cam16lcd_to_xyz',
            'xyz_to_jab_cam16scd', 'jab_cam16scd_to_xyz']  



#------------------------------------------------------------------------------
# zcam imports:
# import zcam as _zcam
from .zcam import run as zcam
from .zcam import _AXES as _ZCAM_AXES
from .zcam import _UNIQUE_HUE_DATA as _ZCAM_UNIQUE_HUE_DATA
from .zcam import _SURROUND_PARAMETERS as _ZCAM_SURROUND_PARAMETERS
from .zcam import _DEFAULT_WHITE_POINT as _ZCAM_DEFAULT_WHITE_POINT
from .zcam import _DEFAULT_CONDITIONS as _ZCAM_DEFAULT_CONDITIONS
from .zcam import (xyz_to_jabz, jabz_to_xyz,
                   xyz_to_jabM_zcam, jabM_zcam_to_xyz, 
                   xyz_to_jabC_zcam, jabC_zcam_to_xyz)

__all__ += ['zcam'] 


__all__ += ['xyz_to_jabz', 'jabz_to_xyz',
            'xyz_to_jabM_zcam', 'jabM_zcam_to_xyz', 
            'xyz_to_jabC_zcam', 'jabC_zcam_to_xyz']



#------------------------------------------------------------------------------
# cam15 imports:
from .cam15u import  (cam15u, _CAM15U_AXES, _CAM15U_UNIQUE_HUE_DATA, _CAM15U_PARAMETERS,
                      _CAM15U_NAKA_RUSHTON_PARAMETERS, _CAM15U_SURROUND_PARAMETERS,
                      xyz_to_qabW_cam15u, qabW_cam15u_to_xyz)

__all__ += ['cam15u', 'xyz_to_qabW_cam15u', 'qabW_cam15u_to_xyz']



#------------------------------------------------------------------------------
# sww2016 cam imports:
from .sww2016 import (cam_sww16, _CAM_SWW16_AXES, _CAM_SWW16_PARAMETERS,
                      xyz_to_lab_cam_sww16, lab_cam_sww16_to_xyz)

__all__ += ['cam_sww16', 'xyz_to_lab_cam_sww16', 'lab_cam_sww16_to_xyz']



#------------------------------------------------------------------------------
# cam18sl imports:
from .cam18sl import  (cam18sl, _CAM18SL_AXES, _CAM18SL_UNIQUE_HUE_DATA, 
                       _CAM18SL_PARAMETERS,_CAM18SL_NAKA_RUSHTON_PARAMETERS, 
                      xyz_to_qabM_cam18sl, qabM_cam18sl_to_xyz, 
                      xyz_to_qabS_cam18sl, qabS_cam18sl_to_xyz)

__all__ += ['cam18sl','xyz_to_qabM_cam18sl', 'qabM_cam18sl_to_xyz',
            'xyz_to_qabS_cam18sl', 'qabS_cam18sl_to_xyz']


__all__ += ['_CAM_AXES', '_UNIQUE_HUE_DATA','_SURROUND_PARAMETERS','_NAKA_RUSHTON_PARAMETERS']


__all__ += ['_CAM15U_PARAMETERS','_CAM_SWW16_PARAMETERS','_CAM18SL_PARAMETERS']



#------------------------------------------------------------------------------
# Create some dictionaries that group databases of different models: 
# --- cam axes list of strings for ploting ---
_CAM_AXES = {}
_CAM_AXES.update(_CIECAM02_AXES)
_CAM_AXES.update(_CAM02UCS_AXES)
_CAM_AXES.update(_CIECAM16_AXES)
_CAM_AXES.update(_CAM16UCS_AXES)
_CAM_AXES.update(_ZCAM_AXES)
_CAM_AXES['qabW_cam15u'] = _CAM15U_AXES 
_CAM_AXES['lab_cam_sww16'] = _CAM_SWW16_AXES
_CAM_AXES['qabS_cam18sl'] = _CAM18SL_AXES 

__all__ += ['_CAM_AXES']

# --- unique hue data ---
_UNIQUE_HUE_DATA = {'models' : ['ciecam02', 'cam02ucs', 
                                'ciecam16', 'cam16ucs',
                                'zcam'],
                    'ciecam02' : _CIECAM02_UNIQUE_HUE_DATA,
                    'cam02ucs' : _CIECAM02_UNIQUE_HUE_DATA,
                    'ciecam16' : _CIECAM16_UNIQUE_HUE_DATA,
                    'cam16ucs' : _CIECAM16_UNIQUE_HUE_DATA,
                    'zcam' : _ZCAM_UNIQUE_HUE_DATA,
                    }
_UNIQUE_HUE_DATA['cam15u'] = _CAM15U_UNIQUE_HUE_DATA
_UNIQUE_HUE_DATA['models'].append('cam15u')
_UNIQUE_HUE_DATA['cam18sl'] = _CAM18SL_UNIQUE_HUE_DATA
_UNIQUE_HUE_DATA['models'].append('cam18sl')

__all__ += ['_UNIQUE_HUE_DATA']

# --- surround parameter data ---
_SURROUND_PARAMETERS = {'ciecam02' : _CIECAM02_SURROUND_PARAMETERS,
                        'cam02ucs' : _CIECAM02_SURROUND_PARAMETERS,
                        'ciecam16' : _CIECAM16_SURROUND_PARAMETERS,
                        'cam16ucs' : _CIECAM16_SURROUND_PARAMETERS,
                        'zcam'  : _ZCAM_SURROUND_PARAMETERS
                        }
_SURROUND_PARAMETERS['cam15u'] = _CAM15U_SURROUND_PARAMETERS
_SURROUND_PARAMETERS['cam_sww16'] = {} 
_SURROUND_PARAMETERS['cam18sl'] = {}

__all__ += ['_SURROUND_PARAMETERS']

# --- Naka-Rushton function parameter data ---
_NAKA_RUSHTON_PARAMETERS = {'ciecam02' : _CIECAM02_NAKA_RUSHTON_PARAMETERS,
                            'cam02ucs' : _CIECAM02_NAKA_RUSHTON_PARAMETERS,
                            'ciecam16' : _CIECAM16_NAKA_RUSHTON_PARAMETERS,
                            'cam16ucs' : _CIECAM16_NAKA_RUSHTON_PARAMETERS,
                            }
_NAKA_RUSHTON_PARAMETERS['cam15u'] =  _CAM15U_NAKA_RUSHTON_PARAMETERS
_NAKA_RUSHTON_PARAMETERS['cam18sl'] =  _CAM18SL_NAKA_RUSHTON_PARAMETERS

__all__ += ['_NAKA_RUSHTON_PARAMETERS']


# ---- UCS parameters ---
_CAM_UCS_PARAMETERS = {'ciecam02' : _CAM02UCS_UCS_PARAMETERS,
                       'cam02ucs' : _CAM02UCS_UCS_PARAMETERS,
                       'ciecam16' : _CAM16UCS_UCS_PARAMETERS,
                       'cam16ucs' : _CAM16UCS_UCS_PARAMETERS}

__all__ += ['_CAM_UCS_PARAMETERS']

#------------------------------------------------------------------------------
#Set some defaults:
_CAM_DEFAULT_TYPE = 'ciecam02'
_CAM_DEFAULT_WHITE_POINT = _CIECAM02_DEFAULT_WHITE_POINT
_CAM_DEFAULT_CONDITIONS = _CIECAM02_DEFAULT_CONDITIONS
__all__ += ['_CAM_DEFAULT_TYPE', '_CAM_DEFAULT_WHITE_POINT','_CAM_DEFAULT_CONDITIONS']


#------------------------------------------------------------------------------
# Define some extra functions:

[docs] def camXucs(data, xyzw = _CAM_DEFAULT_WHITE_POINT, Yw = None, outin = 'J,aM,bM', conditions = None, forward = True, ucstype = 'ucs', yellowbluepurplecorrect = False, mcat = None, camtype = _CAM_DEFAULT_TYPE): """ Wraps ciecam02(), ciecam16(), cam02ucs(), cam16ucs(). Args: :camtype: | _DEFAULT_TYPE, optional | String specifying the cam-model. Notes: 1. To call ciecam02() or ciecam16(): set ucstype to None !!! 2. For more info on other input arguments, see doc-strings of those functions. """ if (camtype == 'ciecam02') & (ucstype is None): return ciecam02(data, xyzw = xyzw, Yw = Yw, outin = outin, conditions = conditions, forward = forward, mcat = mcat, yellowbluepurplecorrect = yellowbluepurplecorrect) elif ((camtype == 'cam02ucs') | (camtype == 'ciecam02')) & (ucstype is not None): return cam02ucs(data, xyzw = xyzw, Yw = Yw, ucstype = ucstype, conditions = conditions, forward = forward, mcat = mcat, yellowbluepurplecorrect = yellowbluepurplecorrect) elif (camtype == 'ciecam16') & (ucstype is None): return ciecam16(data, xyzw = xyzw, Yw = Yw, outin = outin, conditions = conditions, forward = forward, mcat = mcat) elif ((camtype == 'cam16ucs') | (camtype == 'ciecam16')) & (ucstype is not None): return cam16ucs(data, xyzw = xyzw, Yw = Yw, ucstype = ucstype, conditions = conditions, forward = forward, mcat = mcat) elif ((camtype == 'zcam')): return zcam(data, xyzw = xyzw, Yw = Yw, outin = outin, conditions = conditions, forward = forward, mcat = mcat)
__all__ +=['camXucs']