Source code for colour.adaptation.cat

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

"""
Chromatic Adaptation Transforms
===============================

Defines various chromatic adaptation transforms (CAT) and objects to
calculate the chromatic adaptation matrix between two given *CIE XYZ*
colourspace matrices:

-   :attr:`XYZ_SCALING_CAT`: *XYZ Scaling* CAT [1]_
-   :attr:`BRADFORD_CAT`: *Bradford* CAT [1]_
-   :attr:`VON_KRIES_CAT`: *Von Kries* CAT [1]_
-   :attr:`FAIRCHILD_CAT`: *Fairchild* CAT [2]_
-   :attr:`CAT02_CAT`: *CAT02* CAT [3]_

See Also
--------
`Chromatic Adaptation Transforms IPython Notebook
<http://nbviewer.ipython.org/github/colour-science/colour-ipython/blob/master/notebooks/adaptation/cat.ipynb>`_  # noqa

References
----------
.. [1]  http://brucelindbloom.com/Eqn_ChromAdapt.html
.. [2]  http://rit-mcsl.org/fairchild//files/FairchildYSh.zip
.. [3]  http://en.wikipedia.org/wiki/CIECAM02#CAT02
"""

from __future__ import division, unicode_literals

import numpy as np

from colour.utilities import CaseInsensitiveMapping

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

__all__ = ['XYZ_SCALING_CAT',
           'BRADFORD_CAT',
           'VON_KRIES_CAT',
           'FAIRCHILD_CAT',
           'CAT02_CAT',
           'CAT02_INVERSE_CAT',
           'CHROMATIC_ADAPTATION_METHODS',
           'chromatic_adaptation_matrix']

XYZ_SCALING_CAT = np.array(np.identity(3)).reshape((3, 3))
"""
*XYZ Scaling* chromatic adaptation transform. [1]_

XYZ_SCALING_CAT : array_like, (3, 3)
"""

BRADFORD_CAT = np.array(
    [[0.8951000, 0.2664000, -0.1614000],
     [-0.7502000, 1.7135000, 0.0367000],
     [0.0389000, -0.0685000, 1.0296000]])
"""
*Bradford* chromatic adaptation transform. [1]_

BRADFORD_CAT : array_like, (3, 3)
"""

VON_KRIES_CAT = np.array(
    [[0.4002400, 0.7076000, -0.0808100],
     [-0.2263000, 1.1653200, 0.0457000],
     [0.0000000, 0.0000000, 0.9182200]])
"""
*Von Kries* chromatic adaptation transform. [1]_

VON_KRIES_CAT : array_like, (3, 3)
"""

FAIRCHILD_CAT = np.array(
    [[.8562, .3372, -.1934],
     [-.8360, 1.8327, .0033],
     [.0357, -.0469, 1.0112]])
"""
*Fairchild* chromatic adaptation transform. [2]_

FAIRCHILD_CAT : array_like, (3, 3)
"""

CAT02_CAT = np.array(
    [[0.7328, 0.4296, -0.1624],
     [-0.7036, 1.6975, 0.0061],
     [0.0030, 0.0136, 0.9834]])
"""
*CAT02* chromatic adaptation transform. [3]_

CAT02_CAT : array_like, (3, 3)
"""

CAT02_INVERSE_CAT = np.linalg.inv(CAT02_CAT)
"""
Inverse *CAT02* chromatic adaptation transform. [3]_

CAT02_INVERSE_CAT : array_like, (3, 3)
"""

CHROMATIC_ADAPTATION_METHODS = CaseInsensitiveMapping(
    {'XYZ Scaling': XYZ_SCALING_CAT,
     'Bradford': BRADFORD_CAT,
     'Von Kries': VON_KRIES_CAT,
     'Fairchild': FAIRCHILD_CAT,
     'CAT02': CAT02_CAT})
"""
Supported chromatic adaptation transform methods.

CHROMATIC_ADAPTATION_METHODS : dict
    ('XYZ Scaling', 'Bradford', 'Von Kries', 'Fairchild, 'CAT02')
"""


[docs]def chromatic_adaptation_matrix(XYZ1, XYZ2, method='CAT02'): """ Returns the *chromatic adaptation* matrix from given source and target *CIE XYZ* colourspace *array_like* variables. Parameters ---------- XYZ1 : array_like, (3,) *CIE XYZ* source *array_like* variable. XYZ2 : array_like, (3,) *CIE XYZ* target *array_like* variable. method : unicode, optional ('XYZ Scaling', 'Bradford', 'Von Kries', 'Fairchild, 'CAT02'), Chromatic adaptation method. Returns ------- ndarray, (3, 3) Chromatic adaptation matrix. Raises ------ KeyError If chromatic adaptation method is not defined. References ---------- .. [4] http://brucelindbloom.com/Eqn_ChromAdapt.html (Last accessed 24 February 2014) Examples -------- >>> XYZ1 = np.array([1.09923822, 1.000, 0.35445412]) >>> XYZ2 = np.array([0.96907232, 1.000, 1.121792157]) >>> chromatic_adaptation_matrix(XYZ1, XYZ2) # doctest: +ELLIPSIS array([[ 0.8714561..., -0.1320467..., 0.4039483...], [-0.0963880..., 1.0490978..., 0.160403... ], [ 0.0080207..., 0.0282636..., 3.0602319...]]) Using *Bradford* method: >>> XYZ1 = np.array([1.09923822, 1.000, 0.35445412]) >>> XYZ2 = np.array([0.96907232, 1.000, 1.121792157]) >>> method = 'Bradford' >>> chromatic_adaptation_matrix(XYZ1, XYZ2, method) # doctest: +ELLIPSIS array([[ 0.8518131..., -0.1134786..., 0.4124804...], [-0.1277659..., 1.0928930..., 0.1341559...], [ 0.0845323..., -0.1434969..., 3.3075309...]]) """ method_matrix = CHROMATIC_ADAPTATION_METHODS.get(method) if method_matrix is None: raise KeyError( '"{0}" chromatic adaptation method is not defined! Supported ' 'methods: "{1}".'.format(method, CHROMATIC_ADAPTATION_METHODS.keys())) XYZ1, XYZ2 = np.ravel(XYZ1), np.ravel(XYZ2) if (XYZ1 == XYZ2).all(): # Skip the chromatic adaptation computation if the two input matrices # are the same, because no adaptation is needed. return np.identity(3) rgb_source = np.ravel(np.dot(method_matrix, XYZ1)) rgb_target = np.ravel(np.dot(method_matrix, XYZ2)) crd = np.diagflat(np.array( [[rgb_target[0] / rgb_source[0], rgb_target[1] / rgb_source[1], rgb_target[2] / rgb_source[2]]])).reshape((3, 3)) cat = np.dot(np.dot(np.linalg.inv(method_matrix), crd), method_matrix) return cat