"""
ProLab Colourspace
==================
Define the *ProLab* colourspace transformations.
- :func:`colour.XYZ_to_ProLab`
- :func:`colour.ProLab_to_XYZ`
References
----------
- :cite:`Konovalenko2021` : Ivan A. Konovalenko, Anna A. Smagina, Dmitry P.
Nikolaev, Petr P. Nikolaev. ProLab: perceptually uniform projective colour
coordinate system. doi:10.1109/ACCESS.2017
"""
from __future__ import annotations
import numpy as np
from colour.colorimetry import CCS_ILLUMINANTS
from colour.hints import ( # noqa: TC001
ArrayLike,
Domain1,
NDArrayFloat,
Range1,
)
from colour.models import xy_to_xyY, xyY_to_XYZ
from colour.utilities import as_float_array, from_range_1, ones, to_domain_1
__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__ = [
"MATRIX_Q",
"MATRIX_INVERSE_Q",
"ProLab_to_XYZ",
"XYZ_to_ProLab",
]
MATRIX_Q: NDArrayFloat = np.array(
[
[75.54, 486.66, 167.39, 0.0],
[617.72, -595.45, -22.27, 0.0],
[48.34, 194.94, -243.28, 0.0],
[0.7554, 3.8666, 1.6739, 1.0],
]
)
"""Normalised cone responses to *CIE XYZ* tristimulus values matrix."""
MATRIX_INVERSE_Q: NDArrayFloat = np.linalg.inv(MATRIX_Q)
"""Normalised cone responses to *ProLab* colourspace matrix."""
def projective_transformation(a: ArrayLike, Q: ArrayLike) -> NDArrayFloat:
"""
Apply the specified projective transformation matrix :math:`Q` to the
array :math:`a`.
Parameters
----------
a
Array :math:`a` to apply the projective transformation matrix onto.
Q
Projective transformation matrix :math:`Q`.
Returns
-------
:class:`numpy.ndarray`
Transformed array :math:`a`.
"""
a = as_float_array(a)
Q = as_float_array(Q)
# Concatenate array with ones along last axis for homogeneous coordinates
M = np.concatenate([a, ones((*a.shape[:-1], 1))], axis=-1)
homography = np.dot(M, np.transpose(Q))
return homography[..., 0:-1] / homography[..., -1][..., None]
[docs]
def XYZ_to_ProLab(
XYZ: Domain1,
illuminant: ArrayLike = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][
"D65"
],
) -> Range1:
"""
Convert from *CIE XYZ* tristimulus values to *ProLab* colourspace.
Parameters
----------
XYZ
*CIE XYZ* tristimulus values.
illuminant
Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY*
colourspace array.
Returns
-------
:class:`numpy.ndarray`
*ProLab* colourspace array.
Notes
-----
+------------+-----------------------+-----------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``XYZ`` | 1 | 1 |
+------------+-----------------------+-----------------+
+------------+-----------------------+-----------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``ProLab`` | 1 | 1 |
+------------+-----------------------+-----------------+
References
----------
:cite:`Konovalenko2021`
Examples
--------
>>> Lab = np.array([0.51634019, 0.15469500, 0.06289579])
>>> XYZ_to_ProLab(Lab) # doctest: +ELLIPSIS
array([ 59.8466286..., 115.0396354..., 20.1251035...])
"""
XYZ = to_domain_1(XYZ)
XYZ_n = xyY_to_XYZ(xy_to_xyY(illuminant))
ProLab = projective_transformation(XYZ / XYZ_n, MATRIX_Q)
return from_range_1(ProLab)
[docs]
def ProLab_to_XYZ(
ProLab: Domain1,
illuminant: ArrayLike = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][
"D65"
],
) -> Range1:
"""
Convert from *ProLab* colourspace to *CIE XYZ* tristimulus values.
Parameters
----------
ProLab
*ProLab* colourspace array.
illuminant
Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY*
colourspace array.
Returns
-------
:class:`numpy.ndarray`
*CIE XYZ* tristimulus values.
Notes
-----
+------------+-----------------------+-----------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``ProLab`` | 1 | 1 |
+------------+-----------------------+-----------------+
+------------+-----------------------+-----------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``XYZ`` | 1 | 1 |
+------------+-----------------------+-----------------+
References
----------
:cite:`Konovalenko2021`
Examples
--------
>>> ProLab = np.array([59.8466286, 115.0396354, 20.12510352])
>>> ProLab_to_XYZ(ProLab) # doctest: +ELLIPSIS
array([0.5163401..., 0.154695..., 0.0628957...])
"""
ProLab = to_domain_1(ProLab)
XYZ_n = xyY_to_XYZ(xy_to_xyY(illuminant))
XYZ = projective_transformation(ProLab, MATRIX_INVERSE_Q)
XYZ = XYZ * XYZ_n
return from_range_1(XYZ)