Coverage for colour/models/igpgtg.py: 100%
28 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
1"""
2:math:`I_GP_GT_G` Colourspace
3=============================
5Define the :math:`I_GP_GT_G` colourspace transformations.
7- :func:`colour.XYZ_to_IgPgTg`
8- :func:`colour.IgPgTg_to_XYZ`
10References
11----------
12- :cite:`Hellwig2020` : Hellwig, L., & Fairchild, M. D. (2020). Using
13 Gaussian Spectra to Derive a Hue-linear Color Space. Journal of Perceptual
14 Imaging. doi:10.2352/J.Percept.Imaging.2020.3.2.020401
15"""
17from __future__ import annotations
19import numpy as np
21from colour.algebra import spow
22from colour.hints import ( # noqa: TC001
23 ArrayLike,
24 Domain1,
25 NDArrayFloat,
26 Range1,
27)
28from colour.models import Iab_to_XYZ, XYZ_to_Iab
30__author__ = "Colour Developers"
31__copyright__ = "Copyright 2013 Colour Developers"
32__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
33__maintainer__ = "Colour Developers"
34__email__ = "colour-developers@colour-science.org"
35__status__ = "Production"
37__all__ = [
38 "MATRIX_IGPGTG_XYZ_TO_LMS",
39 "MATRIX_IGPGTG_LMS_TO_XYZ",
40 "MATRIX_IGPGTG_LMS_P_TO_IGPGTG",
41 "MATRIX_IGPGTG_IGPGTG_TO_LMS_P",
42 "XYZ_to_IgPgTg",
43 "IgPgTg_to_XYZ",
44]
46MATRIX_IGPGTG_XYZ_TO_LMS: NDArrayFloat = np.array(
47 [
48 [2.968, 2.741, -0.649],
49 [1.237, 5.969, -0.173],
50 [-0.318, 0.387, 2.311],
51 ]
52)
53"""*CIE XYZ* tristimulus values to normalised cone responses matrix."""
55MATRIX_IGPGTG_LMS_TO_XYZ: NDArrayFloat = np.linalg.inv(MATRIX_IGPGTG_XYZ_TO_LMS)
56"""Normalised cone responses to *CIE XYZ* tristimulus values matrix."""
58MATRIX_IGPGTG_LMS_P_TO_IGPGTG: NDArrayFloat = np.array(
59 [
60 [0.117, 1.464, 0.130],
61 [8.285, -8.361, 21.400],
62 [-1.208, 2.412, -36.530],
63 ]
64)
65"""Normalised non-linear cone responses to :math:`I_GP_GT_G` colourspace matrix."""
67MATRIX_IGPGTG_IGPGTG_TO_LMS_P: NDArrayFloat = np.linalg.inv(
68 MATRIX_IGPGTG_LMS_P_TO_IGPGTG
69)
70""":math:`I_GP_GT_G` colourspace to normalised non-linear cone responses matrix."""
73def XYZ_to_IgPgTg(XYZ: Domain1) -> Range1:
74 """
75 Convert from *CIE XYZ* tristimulus values to :math:`I_GP_GT_G`
76 colourspace.
78 Parameters
79 ----------
80 XYZ
81 *CIE XYZ* tristimulus values.
83 Returns
84 -------
85 :class:`numpy.ndarray`
86 :math:`I_GP_GT_G` colourspace array.
88 Notes
89 -----
90 +------------+-----------------------+-----------------+
91 | **Domain** | **Scale - Reference** | **Scale - 1** |
92 +============+=======================+=================+
93 | ``XYZ`` | 1 | 1 |
94 +------------+-----------------------+-----------------+
96 +------------+-----------------------+-----------------+
97 | **Range** | **Scale - Reference** | **Scale - 1** |
98 +============+=======================+=================+
99 | ``IgPgTg`` | 1 | 1 |
100 +------------+-----------------------+-----------------+
102 - Input *CIE XYZ* tristimulus values must be adapted to
103 *CIE Standard Illuminant D Series* *D65*.
105 References
106 ----------
107 :cite:`Hellwig2020`
109 Examples
110 --------
111 >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
112 >>> XYZ_to_IgPgTg(XYZ) # doctest: +ELLIPSIS
113 array([ 0.4242125..., 0.1863249..., 0.1068922...])
114 """
116 def LMS_to_LMS_p_callable(LMS: ArrayLike) -> NDArrayFloat:
117 """
118 Callable applying the forward non-linearity to the :math:`LMS`
119 colourspace array.
120 """
122 return spow(LMS / np.array([18.36, 21.46, 19435]), 0.427)
124 return XYZ_to_Iab(
125 XYZ,
126 LMS_to_LMS_p_callable,
127 MATRIX_IGPGTG_XYZ_TO_LMS,
128 MATRIX_IGPGTG_LMS_P_TO_IGPGTG,
129 )
132def IgPgTg_to_XYZ(IgPgTg: Domain1) -> Range1:
133 """
134 Convert from :math:`I_GP_GT_G` colourspace to *CIE XYZ* tristimulus
135 values.
137 Parameters
138 ----------
139 IgPgTg
140 :math:`I_GP_GT_G` colourspace array.
142 Returns
143 -------
144 :class:`numpy.ndarray`
145 *CIE XYZ* tristimulus values.
147 Notes
148 -----
149 +------------+-----------------------+-----------------+
150 | **Domain** | **Scale - Reference** | **Scale - 1** |
151 +============+=======================+=================+
152 | ``IgPgTg`` | 1 | 1 |
153 +------------+-----------------------+-----------------+
155 +------------+-----------------------+-----------------+
156 | **Range** | **Scale - Reference** | **Scale - 1** |
157 +============+=======================+=================+
158 | ``XYZ`` | 1 | 1 |
159 +------------+-----------------------+-----------------+
161 - Output *CIE XYZ* tristimulus values are adapted to
162 *CIE Standard Illuminant D Series* *D65*.
164 References
165 ----------
166 :cite:`Hellwig2020`
168 Examples
169 --------
170 >>> IgPgTg = np.array([0.42421258, 0.18632491, 0.10689223])
171 >>> IgPgTg_to_XYZ(IgPgTg) # doctest: +ELLIPSIS
172 array([ 0.2065400..., 0.1219722..., 0.0513695...])
173 """
175 def LMS_p_to_LMS_callable(LMS_p: ArrayLike) -> NDArrayFloat:
176 """
177 Callable applying the reverse non-linearity to the :math:`LMS_p`
178 colourspace array.
179 """
181 return spow(LMS_p, 1 / 0.427) * np.array([18.36, 21.46, 19435])
183 return Iab_to_XYZ(
184 IgPgTg,
185 LMS_p_to_LMS_callable,
186 MATRIX_IGPGTG_IGPGTG_TO_LMS_P,
187 MATRIX_IGPGTG_LMS_TO_XYZ,
188 )