Coverage for models/tests/test_common.py: 100%
125 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"""Define the unit tests for the :mod:`colour.models.common` module."""
3from __future__ import annotations
5from itertools import product
7import numpy as np
9from colour.constants import TOLERANCE_ABSOLUTE_TESTS
10from colour.models import Iab_to_XYZ, Jab_to_JCh, JCh_to_Jab, XYZ_to_Iab
11from colour.utilities import domain_range_scale, ignore_numpy_errors
13__author__ = "Colour Developers"
14__copyright__ = "Copyright 2013 Colour Developers"
15__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
16__maintainer__ = "Colour Developers"
17__email__ = "colour-developers@colour-science.org"
18__status__ = "Production"
20__all__ = [
21 "TestJab_to_JCh",
22 "TestJCh_to_Jab",
23 "TestXYZ_to_Iab",
24 "TestIab_to_XYZ",
25]
28class TestJab_to_JCh:
29 """
30 Define :func:`colour.models.common.Jab_to_JCh` definition unit tests
31 methods.
32 """
34 def test_Jab_to_JCh(self) -> None:
35 """Test :func:`colour.models.common.Jab_to_JCh` definition."""
37 np.testing.assert_allclose(
38 Jab_to_JCh(np.array([41.52787529, 52.63858304, 26.92317922])),
39 np.array([41.52787529, 59.12425901, 27.08848784]),
40 atol=TOLERANCE_ABSOLUTE_TESTS,
41 )
43 np.testing.assert_allclose(
44 Jab_to_JCh(np.array([55.11636304, -41.08791787, 30.91825778])),
45 np.array([55.11636304, 51.42135412, 143.03889556]),
46 atol=TOLERANCE_ABSOLUTE_TESTS,
47 )
49 np.testing.assert_allclose(
50 Jab_to_JCh(np.array([29.80565520, 20.01830466, -48.34913874])),
51 np.array([29.80565520, 52.32945383, 292.49133666]),
52 atol=TOLERANCE_ABSOLUTE_TESTS,
53 )
55 def test_n_dimensional_Jab_to_JCh(self) -> None:
56 """
57 Test :func:`colour.models.common.Jab_to_JCh` definition n-dimensional
58 arrays support.
59 """
61 Lab = np.array([41.52787529, 52.63858304, 26.92317922])
62 LCHab = Jab_to_JCh(Lab)
64 Lab = np.tile(Lab, (6, 1))
65 LCHab = np.tile(LCHab, (6, 1))
66 np.testing.assert_allclose(
67 Jab_to_JCh(Lab), LCHab, atol=TOLERANCE_ABSOLUTE_TESTS
68 )
70 Lab = np.reshape(Lab, (2, 3, 3))
71 LCHab = np.reshape(LCHab, (2, 3, 3))
72 np.testing.assert_allclose(
73 Jab_to_JCh(Lab), LCHab, atol=TOLERANCE_ABSOLUTE_TESTS
74 )
76 def test_domain_range_scale_Jab_to_JCh(self) -> None:
77 """
78 Test :func:`colour.models.common.Jab_to_JCh` definition domain and
79 range scale support.
80 """
82 Lab = np.array([41.52787529, 52.63858304, 26.92317922])
83 LCHab = Jab_to_JCh(Lab)
85 d_r = (
86 ("reference", 1, 1),
87 ("1", 0.01, np.array([0.01, 0.01, 1 / 360])),
88 ("100", 1, np.array([1, 1, 1 / 3.6])),
89 )
90 for scale, factor_a, factor_b in d_r:
91 with domain_range_scale(scale):
92 np.testing.assert_allclose(
93 Jab_to_JCh(Lab * factor_a),
94 LCHab * factor_b,
95 atol=TOLERANCE_ABSOLUTE_TESTS,
96 )
98 @ignore_numpy_errors
99 def test_nan_Jab_to_JCh(self) -> None:
100 """Test :func:`colour.models.common.Jab_to_JCh` definition nan support."""
102 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
103 cases = np.array(list(set(product(cases, repeat=3))))
104 Jab_to_JCh(cases)
107class TestJCh_to_Jab:
108 """
109 Define :func:`colour.models.common.JCh_to_Jab` definition unit tests
110 methods.
111 """
113 def test_JCh_to_Jab(self) -> None:
114 """Test :func:`colour.models.common.JCh_to_Jab` definition."""
116 np.testing.assert_allclose(
117 JCh_to_Jab(np.array([41.52787529, 59.12425901, 27.08848784])),
118 np.array([41.52787529, 52.63858304, 26.92317922]),
119 atol=TOLERANCE_ABSOLUTE_TESTS,
120 )
122 np.testing.assert_allclose(
123 JCh_to_Jab(np.array([55.11636304, 51.42135412, 143.03889556])),
124 np.array([55.11636304, -41.08791787, 30.91825778]),
125 atol=TOLERANCE_ABSOLUTE_TESTS,
126 )
128 np.testing.assert_allclose(
129 JCh_to_Jab(np.array([29.80565520, 52.32945383, 292.49133666])),
130 np.array([29.80565520, 20.01830466, -48.34913874]),
131 atol=TOLERANCE_ABSOLUTE_TESTS,
132 )
134 def test_n_dimensional_JCh_to_Jab(self) -> None:
135 """
136 Test :func:`colour.models.common.JCh_to_Jab` definition n-dimensional
137 arrays support.
138 """
140 LCHab = np.array([41.52787529, 59.12425901, 27.08848784])
141 Lab = JCh_to_Jab(LCHab)
143 LCHab = np.tile(LCHab, (6, 1))
144 Lab = np.tile(Lab, (6, 1))
145 np.testing.assert_allclose(
146 JCh_to_Jab(LCHab), Lab, atol=TOLERANCE_ABSOLUTE_TESTS
147 )
149 LCHab = np.reshape(LCHab, (2, 3, 3))
150 Lab = np.reshape(Lab, (2, 3, 3))
151 np.testing.assert_allclose(
152 JCh_to_Jab(LCHab), Lab, atol=TOLERANCE_ABSOLUTE_TESTS
153 )
155 def test_domain_range_scale_JCh_to_Jab(self) -> None:
156 """
157 Test :func:`colour.models.common.JCh_to_Jab` definition domain and
158 range scale support.
159 """
161 LCHab = np.array([41.52787529, 59.12425901, 27.08848784])
162 Lab = JCh_to_Jab(LCHab)
164 d_r = (
165 ("reference", 1, 1),
166 ("1", np.array([0.01, 0.01, 1 / 360]), 0.01),
167 ("100", np.array([1, 1, 1 / 3.6]), 1),
168 )
169 for scale, factor_a, factor_b in d_r:
170 with domain_range_scale(scale):
171 np.testing.assert_allclose(
172 JCh_to_Jab(LCHab * factor_a),
173 Lab * factor_b,
174 atol=TOLERANCE_ABSOLUTE_TESTS,
175 )
177 @ignore_numpy_errors
178 def test_nan_JCh_to_Jab(self) -> None:
179 """Test :func:`colour.models.common.JCh_to_Jab` definition nan support."""
181 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
182 cases = np.array(list(set(product(cases, repeat=3))))
183 JCh_to_Jab(cases)
186class TestXYZ_to_Iab:
187 """Define :func:`colour.models.common.XYZ_to_Iab` definition unit tests methods."""
189 def setup_method(self) -> None:
190 """Initialise the common tests attributes."""
192 self.LMS_to_LMS_p = lambda x: x**0.43
193 self.M_XYZ_to_LMS = np.array(
194 [
195 [0.4002, 0.7075, -0.0807],
196 [-0.2280, 1.1500, 0.0612],
197 [0.0000, 0.0000, 0.9184],
198 ]
199 )
200 self.M_LMS_p_to_Iab = np.array(
201 [
202 [0.4000, 0.4000, 0.2000],
203 [4.4550, -4.8510, 0.3960],
204 [0.8056, 0.3572, -1.1628],
205 ]
206 )
208 def test_XYZ_to_Iab(self) -> None:
209 """Test :func:`colour.models.common.XYZ_to_Iab` definition."""
211 np.testing.assert_allclose(
212 XYZ_to_Iab(
213 np.array([0.20654008, 0.12197225, 0.05136952]),
214 self.LMS_to_LMS_p,
215 self.M_XYZ_to_LMS,
216 self.M_LMS_p_to_Iab,
217 ),
218 np.array([0.38426191, 0.38487306, 0.18886838]),
219 atol=TOLERANCE_ABSOLUTE_TESTS,
220 )
222 np.testing.assert_allclose(
223 XYZ_to_Iab(
224 np.array([0.14222010, 0.23042768, 0.10495772]),
225 self.LMS_to_LMS_p,
226 self.M_XYZ_to_LMS,
227 self.M_LMS_p_to_Iab,
228 ),
229 np.array([0.49437481, -0.19251742, 0.18080304]),
230 atol=TOLERANCE_ABSOLUTE_TESTS,
231 )
233 np.testing.assert_allclose(
234 XYZ_to_Iab(
235 np.array([0.07818780, 0.06157201, 0.28099326]),
236 self.LMS_to_LMS_p,
237 self.M_XYZ_to_LMS,
238 self.M_LMS_p_to_Iab,
239 ),
240 np.array([0.35167774, -0.07525627, -0.30921279]),
241 atol=TOLERANCE_ABSOLUTE_TESTS,
242 )
244 def test_n_dimensional_XYZ_to_Iab(self) -> None:
245 """
246 Test :func:`colour.models.common.XYZ_to_Iab` definition n-dimensional
247 support.
248 """
250 XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
251 Iab = XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab)
253 XYZ = np.tile(XYZ, (6, 1))
254 Iab = np.tile(Iab, (6, 1))
255 np.testing.assert_allclose(
256 XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab),
257 Iab,
258 atol=TOLERANCE_ABSOLUTE_TESTS,
259 )
261 XYZ = np.reshape(XYZ, (2, 3, 3))
262 Iab = np.reshape(Iab, (2, 3, 3))
263 np.testing.assert_allclose(
264 XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab),
265 Iab,
266 atol=TOLERANCE_ABSOLUTE_TESTS,
267 )
269 def test_domain_range_scale_XYZ_to_Iab(self) -> None:
270 """
271 Test :func:`colour.models.common.XYZ_to_Iab` definition domain and
272 range scale support.
273 """
275 XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
276 Iab = XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab)
278 d_r = (("reference", 1), ("1", 1), ("100", 100))
279 for scale, factor in d_r:
280 with domain_range_scale(scale):
281 np.testing.assert_allclose(
282 XYZ_to_Iab(
283 XYZ * factor,
284 self.LMS_to_LMS_p,
285 self.M_XYZ_to_LMS,
286 self.M_LMS_p_to_Iab,
287 ),
288 Iab * factor,
289 atol=TOLERANCE_ABSOLUTE_TESTS,
290 )
292 @ignore_numpy_errors
293 def test_nan_XYZ_to_Iab(self) -> None:
294 """Test :func:`colour.models.common.XYZ_to_Iab` definition nan support."""
296 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
297 cases = np.array(list(set(product(cases, repeat=3))))
298 XYZ_to_Iab(cases, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab)
301class TestIab_to_XYZ:
302 """
303 Define :func:`colour.models.common.Iab_to_XYZ` definition unit tests
304 methods.
305 """
307 def setup_method(self) -> None:
308 """Initialise the common tests attributes."""
310 self.LMS_p_to_LMS = lambda x: x ** (1 / 0.43)
311 self.M_Iab_to_LMS_p = np.linalg.inv(
312 np.array(
313 [
314 [0.4000, 0.4000, 0.2000],
315 [4.4550, -4.8510, 0.3960],
316 [0.8056, 0.3572, -1.1628],
317 ]
318 )
319 )
320 self.M_LMS_to_XYZ = np.linalg.inv(
321 np.array(
322 [
323 [0.4002, 0.7075, -0.0807],
324 [-0.2280, 1.1500, 0.0612],
325 [0.0000, 0.0000, 0.9184],
326 ]
327 )
328 )
330 def test_Iab_to_XYZ(self) -> None:
331 """Test :func:`colour.models.common.Iab_to_XYZ` definition."""
333 np.testing.assert_allclose(
334 Iab_to_XYZ(
335 np.array([0.38426191, 0.38487306, 0.18886838]),
336 self.LMS_p_to_LMS,
337 self.M_Iab_to_LMS_p,
338 self.M_LMS_to_XYZ,
339 ),
340 np.array([0.20654008, 0.12197225, 0.05136952]),
341 atol=TOLERANCE_ABSOLUTE_TESTS,
342 )
344 np.testing.assert_allclose(
345 Iab_to_XYZ(
346 np.array([0.49437481, -0.19251742, 0.18080304]),
347 self.LMS_p_to_LMS,
348 self.M_Iab_to_LMS_p,
349 self.M_LMS_to_XYZ,
350 ),
351 np.array([0.14222010, 0.23042768, 0.10495772]),
352 atol=TOLERANCE_ABSOLUTE_TESTS,
353 )
355 np.testing.assert_allclose(
356 Iab_to_XYZ(
357 np.array([0.35167774, -0.07525627, -0.30921279]),
358 self.LMS_p_to_LMS,
359 self.M_Iab_to_LMS_p,
360 self.M_LMS_to_XYZ,
361 ),
362 np.array([0.07818780, 0.06157201, 0.28099326]),
363 atol=TOLERANCE_ABSOLUTE_TESTS,
364 )
366 def test_n_dimensional_Iab_to_XYZ(self) -> None:
367 """
368 Test :func:`colour.models.common.Iab_to_XYZ` definition n-dimensional
369 support.
370 """
372 Iab = np.array([0.38426191, 0.38487306, 0.18886838])
373 XYZ = Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ)
375 Iab = np.tile(Iab, (6, 1))
376 XYZ = np.tile(XYZ, (6, 1))
377 np.testing.assert_allclose(
378 Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ),
379 XYZ,
380 atol=TOLERANCE_ABSOLUTE_TESTS,
381 )
383 Iab = np.reshape(Iab, (2, 3, 3))
384 XYZ = np.reshape(XYZ, (2, 3, 3))
385 np.testing.assert_allclose(
386 Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ),
387 XYZ,
388 atol=TOLERANCE_ABSOLUTE_TESTS,
389 )
391 def test_domain_range_scale_Iab_to_XYZ(self) -> None:
392 """
393 Test :func:`colour.models.common.Iab_to_XYZ` definition domain and
394 range scale support.
395 """
397 Iab = np.array([0.38426191, 0.38487306, 0.18886838])
398 XYZ = Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ)
400 d_r = (("reference", 1), ("1", 1), ("100", 100))
401 for scale, factor in d_r:
402 with domain_range_scale(scale):
403 np.testing.assert_allclose(
404 Iab_to_XYZ(
405 Iab * factor,
406 self.LMS_p_to_LMS,
407 self.M_Iab_to_LMS_p,
408 self.M_LMS_to_XYZ,
409 ),
410 XYZ * factor,
411 atol=TOLERANCE_ABSOLUTE_TESTS,
412 )
414 @ignore_numpy_errors
415 def test_nan_Iab_to_XYZ(self) -> None:
416 """Test :func:`colour.models.common.Iab_to_XYZ` definition nan support."""
418 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
419 cases = np.array(list(set(product(cases, repeat=3))))
420 Iab_to_XYZ(cases, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ)