Source code for colour.adaptation.zhai2018

"""
Zhai and Luo (2018) Chromatic Adaptation Model
==============================================

Define the *Zhai and Luo (2018)* chromatic adaptation model object:

-   :func:`colour.adaptation.chromatic_adaptation_Zhai2018`

References
----------
-   :cite:`Zhai2018` : Zhai, Q., & Luo, M. R. (2018). Study of chromatic
    adaptation via neutral white matches on different viewing media. Optics
    Express, 26(6), 7724. doi:10.1364/OE.26.007724
"""

from __future__ import annotations

import numpy as np

from colour.adaptation import CHROMATIC_ADAPTATION_TRANSFORMS
from colour.algebra import vecmul
from colour.hints import ArrayLike, Literal, NDArrayFloat
from colour.utilities import (
    as_float_array,
    from_range_100,
    to_domain_100,
    validate_method,
)

__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"

__all__ = [
    "chromatic_adaptation_Zhai2018",
]


[docs] def chromatic_adaptation_Zhai2018( XYZ_b: ArrayLike, XYZ_wb: ArrayLike, XYZ_wd: ArrayLike, D_b: ArrayLike = 1, D_d: ArrayLike = 1, XYZ_wo: ArrayLike = np.array([1, 1, 1]), transform: Literal["CAT02", "CAT16"] | str = "CAT02", ) -> NDArrayFloat: """ Adapt given sample colour :math:`XYZ_{\\beta}` tristimulus values from input viewing conditions under :math:`\\beta` illuminant to output viewing conditions under :math:`\\delta` illuminant using *Zhai and Luo (2018)* chromatic adaptation model. According to the definition of :math:`D`, a one-step CAT such as CAT02 can only be used to transform colors from an incomplete adapted field into a complete adapted field. When CAT02 are used to transform an incomplete to incomplete case, :math:`D` has no baseline level to refer to. *Smet et al. (2017)* proposed a new concept of two-step CAT to replace the present CATs such as CAT02 with only one-step transform in order to define :math:`D` more clearly. A two-step CAT involves an illuminant representing the baseline states between the test and reference illuminants for the calculation. In the first step the test color is transformed from test illuminant to the baseline illuminant (:math:`BI`), and it is then transformed to the reference illuminant Degrees of adaptation under the other illuminants should be calculated relative to the adaptation under the :math:`BI`. When :math:`D` becomes lower towards zero, the adaptation point of the observer moves towards the :math:`BI`. Therefore, the chromaticity of the :math:`BI` should be an intrinsic property of the human vision system. Parameters ---------- XYZ_b Sample colour :math:`XYZ_{\\beta}` under input illuminant :math:`\\beta`. XYZ_wb Input illuminant :math:`\\beta`. XYZ_wd Output illuminant :math:`\\delta`. D_b Degree of adaptation :math:`D_{\\beta}` of input illuminant :math:`\\beta`. D_d Degree of adaptation :math:`D_{\\delta}` of output illuminant :math:`\\delta`. XYZ_wo Baseline illuminant (:math:`BI`) :math:`o`. transform Chromatic adaptation transform. Returns ------- :class:`numpy.ndarray` Sample corresponding colour :math:`XYZ_{\\delta}` tristimulus values under output illuminant :math:`D_{\\delta}`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_b`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wb`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wd`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wo`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_d`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Zhai2018` Examples -------- >>> XYZ_b = np.array([48.900, 43.620, 6.250]) >>> XYZ_wb = np.array([109.850, 100, 35.585]) >>> XYZ_wd = np.array([95.047, 100, 108.883]) >>> D_b = 0.9407 >>> D_d = 0.9800 >>> XYZ_wo = np.array([100, 100, 100]) >>> chromatic_adaptation_Zhai2018( ... XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d, XYZ_wo ... ) # doctest: +ELLIPSIS array([ 39.1856164..., 42.1546179..., 19.2367203...]) >>> XYZ_d = np.array([39.18561644, 42.15461798, 19.23672036]) >>> chromatic_adaptation_Zhai2018( ... XYZ_d, XYZ_wd, XYZ_wb, D_d, D_b, XYZ_wo ... ) # doctest: +ELLIPSIS array([ 48.9 , 43.62, 6.25]) """ XYZ_b = to_domain_100(XYZ_b) XYZ_wb = to_domain_100(XYZ_wb) XYZ_wd = to_domain_100(XYZ_wd) XYZ_wo = to_domain_100(XYZ_wo) D_b = as_float_array(D_b) D_d = as_float_array(D_d) Y_wb = XYZ_wb[..., 1][..., None] Y_wd = XYZ_wd[..., 1][..., None] Y_wo = XYZ_wo[..., 1][..., None] transform = validate_method(transform, ("CAT02", "CAT16")) M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] RGB_b = vecmul(M, XYZ_b) RGB_wb = vecmul(M, XYZ_wb) RGB_wd = vecmul(M, XYZ_wd) RGB_wo = vecmul(M, XYZ_wo) D_RGB_b = D_b * (Y_wb / Y_wo) * (RGB_wo / RGB_wb) + 1 - D_b D_RGB_d = D_d * (Y_wd / Y_wo) * (RGB_wo / RGB_wd) + 1 - D_d D_RGB = D_RGB_b / D_RGB_d RGB_d = D_RGB * RGB_b XYZ_d = vecmul(np.linalg.inv(M), RGB_d) return from_range_100(XYZ_d)