Coverage for models/ipt.py: 17%
30 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
1"""
2IPT Colourspace
3===============
5Define the *IPT* colourspace transformations and correlate computations.
7- :func:`colour.XYZ_to_IPT`
8- :func:`colour.IPT_to_XYZ`
9- :func:`colour.IPT_hue_angle`
11References
12----------
13- :cite:`Fairchild2013y` : Fairchild, M. D. (2013). IPT Colourspace. In
14 Color Appearance Models (3rd ed., pp. 6197-6223). Wiley. ISBN:B00DAYO8E2
15"""
17from __future__ import annotations
19from functools import partial
21import numpy as np
23from colour.algebra import spow
24from colour.hints import ( # noqa: TC001
25 Domain1,
26 NDArrayFloat,
27 Range1,
28 Range360,
29)
30from colour.models import Iab_to_XYZ, XYZ_to_Iab
31from colour.utilities import as_float, from_range_degrees, to_domain_1, tsplit
33__author__ = "Colour Developers"
34__copyright__ = "Copyright 2013 Colour Developers"
35__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
36__maintainer__ = "Colour Developers"
37__email__ = "colour-developers@colour-science.org"
38__status__ = "Production"
40__all__ = [
41 "MATRIX_IPT_XYZ_TO_LMS",
42 "MATRIX_IPT_LMS_TO_XYZ",
43 "MATRIX_IPT_LMS_P_TO_IPT",
44 "MATRIX_IPT_IPT_TO_LMS_P",
45 "XYZ_to_IPT",
46 "IPT_to_XYZ",
47 "IPT_hue_angle",
48]
50MATRIX_IPT_XYZ_TO_LMS: NDArrayFloat = np.array(
51 [
52 [0.4002, 0.7075, -0.0807],
53 [-0.2280, 1.1500, 0.0612],
54 [0.0000, 0.0000, 0.9184],
55 ]
56)
57"""*CIE XYZ* tristimulus values to normalised cone responses matrix."""
59MATRIX_IPT_LMS_TO_XYZ: NDArrayFloat = np.linalg.inv(MATRIX_IPT_XYZ_TO_LMS)
60"""Normalised cone responses to *CIE XYZ* tristimulus values matrix."""
62MATRIX_IPT_LMS_P_TO_IPT: NDArrayFloat = np.array(
63 [
64 [0.4000, 0.4000, 0.2000],
65 [4.4550, -4.8510, 0.3960],
66 [0.8056, 0.3572, -1.1628],
67 ]
68)
69"""Normalised non-linear cone responses to *IPT* colourspace matrix."""
71MATRIX_IPT_IPT_TO_LMS_P: NDArrayFloat = np.linalg.inv(MATRIX_IPT_LMS_P_TO_IPT)
72"""*IPT* colourspace to normalised non-linear cone responses matrix."""
75def XYZ_to_IPT(XYZ: Domain1) -> Range1:
76 """
77 Convert from *CIE XYZ* tristimulus values to *IPT* colourspace.
79 Parameters
80 ----------
81 XYZ
82 *CIE XYZ* tristimulus values.
84 Returns
85 -------
86 :class:`numpy.ndarray`
87 *IPT* colourspace array.
89 Notes
90 -----
91 +------------+-----------------------+-----------------+
92 | **Domain** | **Scale - Reference** | **Scale - 1** |
93 +============+=======================+=================+
94 | ``XYZ`` | 1 | 1 |
95 +------------+-----------------------+-----------------+
97 +------------+-----------------------+-----------------+
98 | **Range** | **Scale - Reference** | **Scale - 1** |
99 +============+=======================+=================+
100 | ``IPT`` | 1 | 1 |
101 +------------+-----------------------+-----------------+
103 - Input *CIE XYZ* tristimulus values must be adapted to
104 *CIE Standard Illuminant D Series* *D65*.
106 References
107 ----------
108 :cite:`Fairchild2013y`
110 Examples
111 --------
112 >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
113 >>> XYZ_to_IPT(XYZ) # doctest: +ELLIPSIS
114 array([ 0.3842619..., 0.3848730..., 0.1888683...])
115 """
117 return XYZ_to_Iab(
118 XYZ,
119 partial(spow, p=0.43),
120 MATRIX_IPT_XYZ_TO_LMS,
121 MATRIX_IPT_LMS_P_TO_IPT,
122 )
125def IPT_to_XYZ(IPT: Domain1) -> Range1:
126 """
127 Convert from *IPT* colourspace to *CIE XYZ* tristimulus values.
129 Parameters
130 ----------
131 IPT
132 *IPT* colourspace array.
134 Returns
135 -------
136 :class:`numpy.ndarray`
137 *CIE XYZ* tristimulus values.
139 Notes
140 -----
141 +------------+-----------------------+-----------------+
142 | **Domain** | **Scale - Reference** | **Scale - 1** |
143 +============+=======================+=================+
144 | ``IPT`` | 1 | 1 |
145 +------------+-----------------------+-----------------+
147 +------------+-----------------------+-----------------+
148 | **Range** | **Scale - Reference** | **Scale - 1** |
149 +============+=======================+=================+
150 | ``XYZ`` | 1 | 1 |
151 +------------+-----------------------+-----------------+
153 - Output *CIE XYZ* tristimulus values are adapted to
154 *CIE Standard Illuminant D Series* *D65*.
156 References
157 ----------
158 :cite:`Fairchild2013y`
160 Examples
161 --------
162 >>> IPT = np.array([0.38426191, 0.38487306, 0.18886838])
163 >>> IPT_to_XYZ(IPT) # doctest: +ELLIPSIS
164 array([ 0.2065400..., 0.1219722..., 0.0513695...])
165 """
167 return Iab_to_XYZ(
168 IPT,
169 partial(spow, p=1 / 0.43),
170 MATRIX_IPT_IPT_TO_LMS_P,
171 MATRIX_IPT_LMS_TO_XYZ,
172 )
175def IPT_hue_angle(IPT: Domain1) -> Range360:
176 """
177 Compute the hue angle in degrees from the *IPT* colourspace array.
179 Parameters
180 ----------
181 IPT
182 *IPT* colourspace array.
184 Returns
185 -------
186 :class:`numpy.ndarray`
187 Hue angle in degrees.
189 Notes
190 -----
191 +------------+-----------------------+-----------------+
192 | **Domain** | **Scale - Reference** | **Scale - 1** |
193 +============+=======================+=================+
194 | ``IPT`` | 1 | 1 |
195 +------------+-----------------------+-----------------+
197 +------------+-----------------------+-----------------+
198 | **Range** | **Scale - Reference** | **Scale - 1** |
199 +============+=======================+=================+
200 | ``hue`` | 360 | 1 |
201 +------------+-----------------------+-----------------+
203 References
204 ----------
205 :cite:`Fairchild2013y`
207 Examples
208 --------
209 >>> IPT = np.array([0.96907232, 1, 1.12179215])
210 >>> IPT_hue_angle(IPT) # doctest: +ELLIPSIS
211 48.2852074...
212 """
214 _I, P, T = tsplit(to_domain_1(IPT))
216 hue = np.degrees(np.arctan2(T, P)) % 360
218 return as_float(from_range_degrees(hue))