"""
hdr-CIELAB Colourspace
======================
Define the *hdr-CIELAB* colourspace transformations.
- :attr:`colour.HDR_CIELAB_METHODS`
- :func:`colour.XYZ_to_hdr_CIELab`
- :func:`colour.hdr_CIELab_to_XYZ`
References
----------
- :cite:`Fairchild2010` : Fairchild, M. D., & Wyble, D. R. (2010).
hdr-CIELAB and hdr-IPT: Simple Models for Describing the Color of
High-Dynamic-Range and Wide-Color-Gamut Images. Proc. of Color and
Imaging Conference, 322-326. ISBN:978-1-62993-215-6
- :cite:`Fairchild2011` : Fairchild, M. D., & Chen, P. (2011).
Brightness, lightness, and specifying color in high-dynamic-range scenes
and images. In S. P. Farnand & F. Gaykema (Eds.), Proc. SPIE 7867, Image
Quality and System Performance VIII (p. 78670O). doi:10.1117/12.872075
"""
from __future__ import annotations
import typing
import numpy as np
from colour.colorimetry import (
CCS_ILLUMINANTS,
lightness_Fairchild2010,
lightness_Fairchild2011,
luminance_Fairchild2010,
luminance_Fairchild2011,
)
if typing.TYPE_CHECKING:
from colour.hints import Literal
from colour.hints import ( # noqa: TC001
ArrayLike,
Domain1,
Domain100,
NDArrayFloat,
Range1,
Range100,
)
from colour.models import xy_to_xyY, xyY_to_XYZ
from colour.utilities import (
as_float_array,
domain_range_scale,
from_range_1,
from_range_100,
to_domain_1,
to_domain_100,
tsplit,
tstack,
validate_method,
)
from colour.utilities.documentation import DocstringTuple, is_documentation_building
__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__ = [
"HDR_CIELAB_METHODS",
"exponent_hdr_CIELab",
"XYZ_to_hdr_CIELab",
"hdr_CIELab_to_XYZ",
]
HDR_CIELAB_METHODS: tuple = ("Fairchild 2010", "Fairchild 2011")
if is_documentation_building(): # pragma: no cover
HDR_CIELAB_METHODS = DocstringTuple(HDR_CIELAB_METHODS)
HDR_CIELAB_METHODS.__doc__ = """
Supported *hdr-CIELAB* colourspace computation methods.
References
----------
:cite:`Fairchild2010`, :cite:`Fairchild2011`
"""
def exponent_hdr_CIELab(
Y_s: Domain1,
Y_abs: ArrayLike,
method: (Literal["Fairchild 2011", "Fairchild 2010"] | str) = "Fairchild 2011",
) -> NDArrayFloat:
"""
Compute the *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon`
exponent using the *Fairchild and Wyble (2010)* or *Fairchild and Chen
(2011)* methods.
Parameters
----------
Y_s
Relative luminance :math:`Y_s` of the surround.
Y_abs
Absolute luminance :math:`Y_{abs}` of the scene diffuse white in
:math:`cd/m^2`.
method
Computation method.
Returns
-------
:class:`numpy.ndarray`
*hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` exponent.
Notes
-----
+------------+-----------------------+---------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``Y_s`` | 1 | 1 |
+------------+-----------------------+---------------+
Examples
--------
>>> exponent_hdr_CIELab(0.2, 100) # doctest: +ELLIPSIS
0.4738510...
>>> exponent_hdr_CIELab(0.2, 100, method="Fairchild 2010")
... # doctest: +ELLIPSIS
1.8360198...
"""
Y_s = to_domain_1(Y_s)
Y_abs = as_float_array(Y_abs)
method = validate_method(method, HDR_CIELAB_METHODS)
epsilon = 1.5 if method == "fairchild 2010" else 0.58
sf = 1.25 - 0.25 * (Y_s / 0.184)
lf = np.log(318) / np.log(Y_abs)
if method == "fairchild 2010":
epsilon *= sf * lf
else:
epsilon /= sf * lf
return epsilon
[docs]
def XYZ_to_hdr_CIELab(
XYZ: Domain1,
illuminant: ArrayLike = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][
"D65"
],
Y_s: Domain1 = 0.2,
Y_abs: ArrayLike = 100,
method: (Literal["Fairchild 2011", "Fairchild 2010"] | str) = "Fairchild 2011",
) -> Range100:
"""
Convert from *CIE XYZ* tristimulus values to *hdr-CIELAB* colourspace.
Parameters
----------
XYZ
*CIE XYZ* tristimulus values.
illuminant
Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY*
colourspace array.
Y_s
Relative luminance :math:`Y_s` of the surround.
Y_abs
Absolute luminance :math:`Y_{abs}` of the scene diffuse white in
:math:`cd/m^2`.
method
Computation method.
Returns
-------
:class:`numpy.ndarray`
*hdr-CIELAB* colourspace array.
Notes
-----
+----------------+-------------------------+---------------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+================+=========================+=====================+
| ``XYZ`` | 1 | 1 |
+----------------+-------------------------+---------------------+
| ``illuminant`` | 1 | 1 |
+----------------+-------------------------+---------------------+
| ``Y_s`` | 1 | 1 |
+----------------+-------------------------+---------------------+
+----------------+-------------------------+---------------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+================+=========================+=====================+
| ``Lab_hdr`` | 100 | 1 |
+----------------+-------------------------+---------------------+
- Conversion to polar coordinates to compute the *chroma*
:math:`C_{hdr}` and *hue* :math:`h_{hdr}` correlates can be
safely performed with :func:`colour.Lab_to_LCHab` definition.
- Conversion to cartesian coordinates from the *Lightness*
:math:`L_{hdr}`, *chroma* :math:`C_{hdr}` and *hue*
:math:`h_{hdr}` correlates can be safely performed with
:func:`colour.LCHab_to_Lab` definition.
References
----------
:cite:`Fairchild2010`, :cite:`Fairchild2011`
Examples
--------
>>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
>>> XYZ_to_hdr_CIELab(XYZ) # doctest: +ELLIPSIS
array([ 51.8700206..., 60.4763385..., 32.1455191...])
>>> XYZ_to_hdr_CIELab(XYZ, method="Fairchild 2010") # doctest: +ELLIPSIS
array([ 31.9962111..., 128.0076303..., 48.7695230...])
"""
X, Y, Z = tsplit(to_domain_1(XYZ))
method = validate_method(method, HDR_CIELAB_METHODS)
X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant)))
if method == "fairchild 2010":
lightness_callable = lightness_Fairchild2010
else:
lightness_callable = lightness_Fairchild2011
e = exponent_hdr_CIELab(Y_s, Y_abs, method)
# Domain and range scaling has already been handled.
with domain_range_scale("ignore"):
L_hdr = lightness_callable(Y / Y_n, e)
a_hdr = 5 * (lightness_callable(X / X_n, e) - L_hdr)
b_hdr = 2 * (L_hdr - lightness_callable(Z / Z_n, e))
Lab_hdr = tstack([L_hdr, a_hdr, b_hdr])
return from_range_100(Lab_hdr)
[docs]
def hdr_CIELab_to_XYZ(
Lab_hdr: Domain100,
illuminant: ArrayLike = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][
"D65"
],
Y_s: Domain1 = 0.2,
Y_abs: ArrayLike = 100,
method: (Literal["Fairchild 2011", "Fairchild 2010"] | str) = "Fairchild 2011",
) -> Range1:
"""
Convert from *hdr-CIELAB* colourspace to *CIE XYZ* tristimulus values.
Parameters
----------
Lab_hdr
*hdr-CIELAB* colourspace array.
illuminant
Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY*
colourspace array.
Y_s
Relative luminance :math:`Y_s` of the surround.
Y_abs
Absolute luminance :math:`Y_{abs}` of the scene diffuse white in
:math:`cd/m^2`.
method
Computation method.
Returns
-------
:class:`numpy.ndarray`
*CIE XYZ* tristimulus values.
Notes
-----
+----------------+-------------------------+---------------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+================+=========================+=====================+
| ``Lab_hdr`` | 100 | 1 |
+----------------+-------------------------+---------------------+
| ``illuminant`` | 1 | 1 |
+----------------+-------------------------+---------------------+
| ``Y_s`` | 1 | 1 |
+----------------+-------------------------+---------------------+
+----------------+-------------------------+---------------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+================+=========================+=====================+
| ``XYZ`` | 1 | 1 |
+----------------+-------------------------+---------------------+
References
----------
:cite:`Fairchild2010`, :cite:`Fairchild2011`
Examples
--------
>>> Lab_hdr = np.array([51.87002062, 60.4763385, 32.14551912])
>>> hdr_CIELab_to_XYZ(Lab_hdr) # doctest: +ELLIPSIS
array([ 0.2065400..., 0.1219722..., 0.0513695...])
>>> Lab_hdr = np.array([31.99621114, 128.00763036, 48.76952309])
>>> hdr_CIELab_to_XYZ(Lab_hdr, method="Fairchild 2010")
... # doctest: +ELLIPSIS
array([ 0.2065400..., 0.1219722..., 0.0513695...])
"""
L_hdr, a_hdr, b_hdr = tsplit(to_domain_100(Lab_hdr))
method = validate_method(method, HDR_CIELAB_METHODS)
X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant)))
if method == "fairchild 2010":
luminance_callable = luminance_Fairchild2010
else:
luminance_callable = luminance_Fairchild2011
e = exponent_hdr_CIELab(Y_s, Y_abs, method)
# Domain and range scaling has already been handled.
with domain_range_scale("ignore"):
Y = luminance_callable(L_hdr, e) * Y_n
X = luminance_callable((a_hdr + 5 * L_hdr) / 5, e) * X_n
Z = luminance_callable((-b_hdr + 2 * L_hdr) / 2, e) * Z_n
XYZ = tstack([X, Y, Z])
return from_range_1(XYZ)