Coverage for colour/adaptation/fairchild2020.py: 100%
46 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"""
2Von Kries 2020 (vK20) Chromatic Adaptation Model
3================================================
5Define the *Von Kries 2020* (*vK20*) chromatic adaptation model for predicting
6corresponding colours under different viewing conditions.
8- :attr:`colour.adaptation.CONDITIONS_DEGREE_OF_ADAPTATION_VK20`
9- :func:`colour.adaptation.matrix_chromatic_adaptation_vk20`
10- :func:`colour.adaptation.chromatic_adaptation_vK20`
12References
13----------
14- :cite:`Fairchild2020` : Fairchild, M. D. (2020). Von Kries 2020:
15 Evolution of degree of chromatic adaptation. Color and Imaging
16 Conference, 28(1), 252-257. doi:10.2352/issn.2169-2629.2020.28.40
17"""
19from __future__ import annotations
21import typing
22from dataclasses import dataclass
24import numpy as np
26from colour.adaptation import CHROMATIC_ADAPTATION_TRANSFORMS
27from colour.algebra import sdiv, sdiv_mode, vecmul
29if typing.TYPE_CHECKING:
30 from colour.hints import Literal
32from colour.hints import ( # noqa: TC001
33 ArrayLike,
34 Domain1,
35 NDArrayFloat,
36 Range1,
37)
38from colour.utilities import (
39 CanonicalMapping,
40 MixinDataclassIterable,
41 as_float_array,
42 from_range_1,
43 get_domain_range_scale,
44 optional,
45 row_as_diagonal,
46 to_domain_1,
47 validate_method,
48)
50__author__ = "Colour Developers"
51__copyright__ = "Copyright 2013 Colour Developers"
52__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause"
53__maintainer__ = "Colour Developers"
54__email__ = "colour-developers@colour-science.org"
55__status__ = "Production"
57__all__ = [
58 "Coefficients_DegreeOfAdaptation_vK20",
59 "CONDITIONS_DEGREE_OF_ADAPTATION_VK20",
60 "TVS_XYZ_R_VK20",
61 "matrix_chromatic_adaptation_vk20",
62 "chromatic_adaptation_vK20",
63]
66@dataclass(frozen=True)
67class Coefficients_DegreeOfAdaptation_vK20(MixinDataclassIterable):
68 """
69 Define the degree of adaptation coefficients for the *Von Kries 2020*
70 (*vK20*) chromatic adaptation model.
72 Parameters
73 ----------
74 D_n
75 Degree of adaptation for the adapting illuminant.
76 D_r
77 Degree of adaptation for the reference illuminant.
78 D_p
79 Degree of adaptation for the previous illuminant.
81 References
82 ----------
83 :cite:`Fairchild2020`
84 """
86 D_n: float
87 D_r: float
88 D_p: float
91CONDITIONS_DEGREE_OF_ADAPTATION_VK20: CanonicalMapping = CanonicalMapping(
92 {
93 "Fairchild": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0),
94 "Hands": Coefficients_DegreeOfAdaptation_vK20(0.95, 0.05, 0),
95 "No Hands": Coefficients_DegreeOfAdaptation_vK20(0.85, 0.15, 0),
96 "Ordinal 1st": Coefficients_DegreeOfAdaptation_vK20(0.9, 0.1, 0),
97 "Ordinal 2nd": Coefficients_DegreeOfAdaptation_vK20(0.8, 0.1, 0.1),
98 "Reversibility Trial 1st": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0.1),
99 "Reversibility Trial 2nd": Coefficients_DegreeOfAdaptation_vK20(0.6, 0.3, 0.1),
100 "Ma et al.": Coefficients_DegreeOfAdaptation_vK20(1 / 3, 1 / 3, 1 / 3),
101 "Hunt & Winter": Coefficients_DegreeOfAdaptation_vK20(0.6, 0.2, 0.2),
102 "Hurvich & Jameson": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0),
103 "Simple von Kries": Coefficients_DegreeOfAdaptation_vK20(1, 0, 0),
104 }
105)
106CONDITIONS_DEGREE_OF_ADAPTATION_VK20.__doc__ = """
107Define the degree of adaptation coefficient conditions for the *Von Kries 2020*
108(*vK20*) chromatic adaptation model.
110References
111----------
112:cite:`Fairchild2020`
113"""
115TVS_XYZ_R_VK20 = np.array([0.97941176, 1.00000000, 1.73235294])
116"""
117*Von Kries 2020* (*vK20*) reference illuminant (taken to be
118u' = 0.185, v' = 0.425, approximately 15000K, sky blue).
120References
121----------
122:cite:`Fairchild2020`
123"""
126def matrix_chromatic_adaptation_vk20(
127 XYZ_p: ArrayLike,
128 XYZ_n: ArrayLike,
129 XYZ_r: ArrayLike = TVS_XYZ_R_VK20,
130 transform: Literal[
131 "Bianco 2010",
132 "Bianco PC 2010",
133 "Bradford",
134 "CAT02 Brill 2008",
135 "CAT02",
136 "CAT16",
137 "CMCCAT2000",
138 "CMCCAT97",
139 "Fairchild",
140 "Sharp",
141 "Von Kries",
142 "XYZ Scaling",
143 ]
144 | str = "CAT02",
145 coefficients: Coefficients_DegreeOfAdaptation_vK20 = (
146 CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Fairchild"]
147 ),
148) -> NDArrayFloat:
149 """
150 Compute the chromatic adaptation matrix from previous viewing conditions
151 to adapting viewing conditions using the *Von Kries 2020* (*vK20*)
152 method.
154 Parameters
155 ----------
156 XYZ_p
157 *CIE XYZ* tristimulus values of the whitepoint under previous viewing
158 conditions.
159 XYZ_n
160 *CIE XYZ* tristimulus values of the whitepoint under adapting viewing
161 conditions.
162 XYZ_r
163 *CIE XYZ* tristimulus values of the whitepoint under reference viewing
164 conditions.
165 transform
166 Chromatic adaptation transform.
167 coefficients
168 *vK20* degree of adaptation coefficients.
170 Returns
171 -------
172 :class:`numpy.ndarray`
173 Chromatic adaptation matrix :math:`M_{cat}`.
175 Notes
176 -----
177 +------------+-----------------------+---------------+
178 | **Domain** | **Scale - Reference** | **Scale - 1** |
179 +============+=======================+===============+
180 | ``XYZ_p`` | 1 | 1 |
181 +------------+-----------------------+---------------+
182 | ``XYZ_n`` | 1 | 1 |
183 +------------+-----------------------+---------------+
184 | ``XYZ_r`` | 1 | 1 |
185 +------------+-----------------------+---------------+
187 References
188 ----------
189 :cite:`Fairchild2020`
191 Examples
192 --------
193 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775])
194 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460])
195 >>> matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n)
196 ... # doctest: +ELLIPSIS
197 array([[ 1.0279139...e+00, 2.9137117...e-02, -2.2794068...e-02],
198 [ 2.0702840...e-02, 9.9005316...e-01, -9.2143464...e-03],
199 [ -6.3758553...e-04, -1.1577319...e-03, 9.1296320...e-01]])
201 Using *Bradford* transform:
203 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775])
204 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460])
205 >>> transform = "Bradford"
206 >>> matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n, transform=transform)
207 ... # doctest: +ELLIPSIS
208 array([[ 1.0367230..., 0.0195580..., -0.0219321...],
209 [ 0.0276321..., 0.9822296..., -0.0082419...],
210 [-0.0029508..., 0.0040690..., 0.9102430...]])
211 """
213 XYZ_n = as_float_array(XYZ_n)
214 XYZ_r = as_float_array(XYZ_r)
215 XYZ_p = as_float_array(XYZ_p)
217 transform = validate_method(
218 transform,
219 tuple(CHROMATIC_ADAPTATION_TRANSFORMS),
220 '"{0}" chromatic adaptation transform is invalid, it must be one of {1}!',
221 )
223 M = CHROMATIC_ADAPTATION_TRANSFORMS[transform]
225 D_n, D_r, D_p = coefficients.values
227 LMS_n = vecmul(M, XYZ_n)
228 LMS_r = vecmul(M, XYZ_r)
229 LMS_p = vecmul(M, XYZ_p)
231 with sdiv_mode():
232 D = row_as_diagonal(sdiv(1, (D_n * LMS_n + D_r * LMS_r + D_p * LMS_p)))
234 M_CAT = np.matmul(np.linalg.inv(M), D)
236 return np.matmul(M_CAT, M)
239def chromatic_adaptation_vK20(
240 XYZ: Domain1,
241 XYZ_p: Domain1,
242 XYZ_n: Domain1,
243 XYZ_r: ArrayLike | None = None,
244 transform: Literal[
245 "Bianco 2010",
246 "Bianco PC 2010",
247 "Bradford",
248 "CAT02 Brill 2008",
249 "CAT02",
250 "CAT16",
251 "CMCCAT2000",
252 "CMCCAT97",
253 "Fairchild",
254 "Sharp",
255 "Von Kries",
256 "XYZ Scaling",
257 ]
258 | str = "CAT02",
259 coefficients: Coefficients_DegreeOfAdaptation_vK20 = (
260 CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Fairchild"]
261 ),
262) -> Range1:
263 """
264 Adapt the specified stimulus *CIE XYZ* tristimulus values from test
265 viewing conditions to reference viewing conditions using the
266 *Von Kries 2020* (*vK20*) chromatic adaptation model.
268 Parameters
269 ----------
270 XYZ
271 *CIE XYZ* tristimulus values of the stimulus to adapt.
272 XYZ_p
273 Previous viewing conditions *CIE XYZ* tristimulus values of the
274 whitepoint.
275 XYZ_n
276 Adapting viewing conditions *CIE XYZ* tristimulus values of the
277 whitepoint.
278 XYZ_r
279 Reference viewing conditions *CIE XYZ* tristimulus values of the
280 whitepoint.
281 transform
282 Chromatic adaptation transform.
283 coefficients
284 *vK20* degree of adaptation coefficients.
286 Returns
287 -------
288 :class:`numpy.ndarray`
289 *CIE XYZ* tristimulus values of the stimulus corresponding colour.
291 Notes
292 -----
293 +------------+-----------------------+---------------+
294 | **Domain** | **Scale - Reference** | **Scale - 1** |
295 +============+=======================+===============+
296 | ``XYZ`` | 1 | 1 |
297 +------------+-----------------------+---------------+
298 | ``XYZ_p`` | 1 | 1 |
299 +------------+-----------------------+---------------+
300 | ``XYZ_n`` | 1 | 1 |
301 +------------+-----------------------+---------------+
302 | ``XYZ_r`` | 1 | 1 |
303 +------------+-----------------------+---------------+
305 +------------+-----------------------+---------------+
306 | **Range** | **Scale - Reference** | **Scale - 1** |
307 +============+=======================+===============+
308 | ``XYZ_a`` | 1 | 1 |
309 +------------+-----------------------+---------------+
311 References
312 ----------
313 :cite:`Fairchild2020`
315 Examples
316 --------
317 >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
318 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775])
319 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460])
320 >>> chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n)
321 ... # doctest: +ELLIPSIS
322 array([ 0.2146884..., 0.1245616..., 0.0466255...])
324 Using *Bradford* transform:
326 >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
327 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775])
328 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460])
329 >>> transform = "Bradford"
330 >>> chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n, transform=transform)
331 ... # doctest: +ELLIPSIS
332 array([ 0.2153837..., 0.1250885..., 0.0466455...])
333 """
335 XYZ = to_domain_1(XYZ)
336 XYZ_p = to_domain_1(XYZ_p)
337 XYZ_n = to_domain_1(XYZ_n)
338 XYZ_r = to_domain_1(
339 optional(
340 XYZ_r,
341 TVS_XYZ_R_VK20
342 if get_domain_range_scale() == "reference"
343 else TVS_XYZ_R_VK20 / 100,
344 )
345 )
347 M_CAT = matrix_chromatic_adaptation_vk20(
348 XYZ_p, XYZ_n, XYZ_r, transform, coefficients
349 )
350 XYZ_a = vecmul(M_CAT, XYZ)
352 return from_range_1(XYZ_a)