Source code for colour.models.icacb

"""
:math:`IC_AC_B` Colourspace
===========================

Defines the :math:`IC_AC_B` colourspace transformations:

-   :func:`colour.XYZ_to_ICaCb`
-   :func:`colour.ICaCb_to_XYZ`

References
----------
-   :cite:`Frohlich2017` : Fro╠łhlich, J. (2017). Encoding high dynamic range
    and wide color gamut imagery. doi:10.18419/OPUS-9664
"""

from __future__ import annotations

import numpy as np

from colour.hints import ArrayLike, NDArray, cast
from colour.models import Iab_to_XYZ, XYZ_to_Iab
from colour.models.rgb.transfer_functions import (
    eotf_ST2084,
    eotf_inverse_ST2084,
)
from colour.utilities import domain_range_scale

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

__all__ = [
    "MATRIX_ICACB_XYZ_TO_LMS",
    "MATRIX_ICACB_LMS_TO_XYZ",
    "MATRIX_ICACB_XYZ_TO_LMS_2",
    "MATRIX_ICACB_LMS_TO_XYZ_2",
    "XYZ_to_ICaCb",
    "ICaCb_to_XYZ",
]

MATRIX_ICACB_XYZ_TO_LMS: NDArray = np.array(
    [
        [0.37613, 0.70431, -0.05675],
        [-0.21649, 1.14744, 0.05356],
        [0.02567, 0.16713, 0.74235],
    ]
)
"""*CIE XYZ* tristimulus values to normalised cone responses matrix."""

MATRIX_ICACB_LMS_TO_XYZ: NDArray = np.linalg.inv(MATRIX_ICACB_XYZ_TO_LMS)
"""Normalised cone responses to *CIE XYZ* tristimulus values matrix."""

MATRIX_ICACB_XYZ_TO_LMS_2: NDArray = np.array(
    [
        [0.4949, 0.5037, 0.0015],
        [4.2854, -4.5462, 0.2609],
        [0.3605, 1.1499, -1.5105],
    ]
)
"""Normalised non-linear cone responses to :math:`IC_AC_B` colourspace matrix."""

MATRIX_ICACB_LMS_TO_XYZ_2: NDArray = np.linalg.inv(MATRIX_ICACB_XYZ_TO_LMS_2)
""":math:`IC_AC_B` to normalised non-linear cone responses colourspace matrix."""


[docs]def XYZ_to_ICaCb(XYZ: ArrayLike) -> NDArray: """ Convert from *CIE XYZ* tristimulus values to :math:`IC_AC_B` colourspace. Parameters ---------- XYZ *CIE XYZ* tristimulus values. Returns ------- :class:`numpy.ndarray` :math:`IC_AC_B` colourspace array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``ICaCb`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``Ca`` : [-1, 1] | ``Ca``: [-1, 1] | | | | | | | ``Cb`` : [-1, 1] | ``Cb``: [-1, 1] | +------------+-----------------------+-----------------+ - Input *CIE XYZ* tristimulus values must be adapted to *CIE Standard Illuminant D Series* *D65*. References ---------- :cite:`Frohlich2017` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_ICaCb(XYZ) array([ 0.06875297, 0.05753352, 0.02081548]) """ def LMS_to_LMS_p_callable(LMS: ArrayLike) -> NDArray: """ Callable applying the forward non-linearity to the :math:`LMS` colourspace array. """ with domain_range_scale("ignore"): return cast(NDArray, eotf_inverse_ST2084(LMS)) return XYZ_to_Iab( XYZ, LMS_to_LMS_p_callable, MATRIX_ICACB_XYZ_TO_LMS, MATRIX_ICACB_XYZ_TO_LMS_2, )
[docs]def ICaCb_to_XYZ(ICaCb: ArrayLike) -> NDArray: """ Convert from :math:`IC_AC_B` tristimulus values to *CIE XYZ* colourspace. Parameters ---------- ICaCb :math:`IC_AC_B` tristimulus values. Returns ------- :class:`numpy.ndarray` *CIE XYZ* colourspace array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``ICaCb`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``Ca`` : [-1, 1] | ``Ca``: [-1, 1] | | | | | | | ``Cb`` : [-1, 1] | ``Cb``: [-1, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`Frohlich2017` Examples -------- >>> XYZ = np.array([0.06875297, 0.05753352, 0.02081548]) >>> ICaCb_to_XYZ(XYZ) array([ 0.20654008, 0.12197225, 0.05136951]) """ def LMS_p_to_LMS_callable(LMS_p: ArrayLike) -> NDArray: """ Callable applying the reverse non-linearity to the :math:`LMS_p` colourspace array. """ with domain_range_scale("ignore"): return cast(NDArray, eotf_ST2084(LMS_p)) return Iab_to_XYZ( ICaCb, LMS_p_to_LMS_callable, MATRIX_ICACB_LMS_TO_XYZ_2, MATRIX_ICACB_LMS_TO_XYZ, )