Source code for colour.adaptation.fairchild1990

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Fairchild (1990) Chromatic Adaptation Model
===========================================

Defines Fairchild (1990) chromatic adaptation model objects:

-   :func:`chromatic_adaptation_Fairchild1990`

See Also
--------
`Fairchild (1990) Chromatic Adaptation Model IPython Notebook
<http://nbviewer.ipython.org/github/colour-science/colour-ipython/blob/master/notebooks/adaptation/fairchild1990.ipynb>`_  # noqa

References
----------
.. [1]  Fairchild, M. D. (1991). Formulation and testing of an
        incomplete-chromatic-adaptation model. Color Research & Application,
        16(4), 243–250. doi:10.1002/col.5080160406
.. [2]  Fairchild, M. D. (2013). FAIRCHILD’S 1990 MODEL. In Color Appearance
        Models (3rd ed., pp. 4418–4495). Wiley. ASIN:B00DAYO8E2
"""

from __future__ import division, unicode_literals

import numpy as np

from colour.adaptation import VON_KRIES_CAT

__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013 - 2015 - Colour Developers'
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-science@googlegroups.com'
__status__ = 'Production'

__all__ = ['FAIRCHILD1990_XYZ_TO_RGB_MATRIX',
           'FAIRCHILD1990_RGB_TO_XYZ_MATRIX',
           'chromatic_adaptation_Fairchild1990',
           'XYZ_to_RGB_fairchild1990',
           'RGB_to_XYZ_fairchild1990',
           'degrees_of_adaptation']

FAIRCHILD1990_XYZ_TO_RGB_MATRIX = VON_KRIES_CAT
"""
Fairchild (1990) colour appearance model *CIE XYZ* colourspace to cone
responses matrix.

FAIRCHILD1990_XYZ_TO_RGB_MATRIX : array_like, (3, 3)
"""

FAIRCHILD1990_RGB_TO_XYZ_MATRIX = np.linalg.inv(VON_KRIES_CAT)
"""
Fairchild (1990) colour appearance model cone responses to *CIE XYZ*
colourspace to  matrix.

FAIRCHILD1990_RGB_TO_XYZ_MATRIX : array_like, (3, 3)
"""


[docs]def chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n, discount_illuminant=False): """ Adapts given *CIE XYZ_1* colourspace stimulus from test viewing conditions to reference viewing conditions using Fairchild (1990) chromatic adaptation model. Parameters ---------- XYZ_1 : array_like, (3,) *CIE XYZ_1* colourspace matrix of test sample / stimulus in domain [0, 100]. XYZ_n : array_like, (3,) Test viewing condition *CIE XYZ_n* colourspace whitepoint matrix. XYZ_r : array_like, (3,) Reference viewing condition *CIE XYZ_r* colourspace whitepoint matrix. Y_n : numeric Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- ndarray, (3,) Adapted *CIE XYZ_2* colourspace test stimulus. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ_1*, *CIE XYZ_n* and *CIE XYZ_r* colourspace matrices are in domain [0, 100]. - Output *CIE XYZ_2* colourspace matrix is in domain [0, 100]. Examples -------- >>> XYZ_1 = np.array([19.53, 23.07, 24.97]) >>> XYZ_n = np.array([111.15, 100.00, 35.20]) >>> XYZ_r = np.array([94.81, 100.00, 107.30]) >>> Y_n = 200 >>> chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) # noqa # doctest: +ELLIPSIS array([ 23.3252634..., 23.3245581..., 76.1159375...]) """ XYZ_1, XYZ_n, XYZ_r = np.ravel(XYZ_1), np.ravel(XYZ_n), np.ravel(XYZ_r) LMS_1 = np.dot(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_1) LMS_n = np.dot(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_n) LMS_r = np.dot(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_r) p_LMS = degrees_of_adaptation(LMS_1, Y_n, discount_illuminant=discount_illuminant) a_LMS_1 = p_LMS / LMS_n a_LMS_2 = p_LMS / LMS_r diagonal = lambda x: np.diagflat(x).reshape((3, 3)) A_1 = diagonal(a_LMS_1) A_2 = diagonal(a_LMS_2) LMSp_1 = np.dot(A_1, LMS_1) c = 0.219 - 0.0784 * np.log10(Y_n) C = np.array([[c, 0, 0], [0, c, 0], [0, 0, c]]) LMS_a = np.dot(C, LMSp_1) LMSp_2 = np.dot(np.linalg.inv(C), LMS_a) LMS_c = np.dot(np.linalg.inv(A_2), LMSp_2) XYZ_c = np.dot(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, LMS_c) return XYZ_c
[docs]def XYZ_to_RGB_fairchild1990(XYZ): """ Converts from *CIE XYZ* colourspace to cone responses. Parameters ---------- XYZ : array_like, (3,) *CIE XYZ* colourspace matrix. Returns ------- ndarray, (3,) Cone responses. Examples -------- >>> XYZ = np.array([19.53, 23.07, 24.97]) >>> XYZ_to_RGB_fairchild1990(XYZ) # doctest: +ELLIPSIS array([ 22.1231935..., 23.6054224..., 22.9279534...]) """ return np.dot(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ)
[docs]def RGB_to_XYZ_fairchild1990(RGB): """ Converts from cone responses to *CIE XYZ* colourspace. Parameters ---------- RGB : array_like, (3,) Cone responses. Returns ------- ndarray, (3,) *CIE XYZ* colourspace matrix. Examples -------- >>> RGB = np.array([22.1231935, 23.6054224, 22.9279534]) >>> RGB_to_XYZ_fairchild1990(RGB) # doctest: +ELLIPSIS array([ 19.53, 23.07, 24.97]) """ return np.dot(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, RGB)
[docs]def degrees_of_adaptation(LMS, Y_n, v=1 / 3, discount_illuminant=False): """ Computes the degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Parameters ---------- LMS : array_like, (3,) Cone responses. Y_n : numeric Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. v : numeric, optional Exponent :math:`v`. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- ndarray, (3,) Degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Examples -------- >>> LMS = np.array([20.0005206, 19.999783, 19.9988316]) >>> Y_n = 31.83 >>> degrees_of_adaptation(LMS, Y_n) # doctest: +ELLIPSIS array([ 0.9799324..., 0.9960035..., 1.0233041...]) >>> degrees_of_adaptation(LMS, Y_n, 1 / 3, True) array([1, 1, 1]) """ if discount_illuminant: return np.array([1, 1, 1]) L, M, S = np.ravel(LMS) LMS_E = np.dot(VON_KRIES_CAT, np.array([1, 1, 1])) # E illuminant. L_E, M_E, S_E = np.ravel(LMS_E) Ye_n = Y_n ** v f_E = lambda x, y: (3 * (x / y)) / (L / L_E + M / M_E + S / S_E) f_P = lambda x: (1 + Ye_n + x) / (1 + Ye_n + 1 / x) p_L = f_P(f_E(L, L_E)) p_M = f_P(f_E(M, M_E)) p_S = f_P(f_E(S, S_E)) return np.array([p_L, p_M, p_S])