Coverage for colour/colorimetry/tests/test_dominant.py: 100%
204 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"""Define the unit tests for the :mod:`colour.colorimetry.dominant` module."""
3from __future__ import annotations
5from itertools import product
7import numpy as np
9from colour.colorimetry import (
10 CCS_ILLUMINANTS,
11 MSDS_CMFS,
12 colorimetric_purity,
13 complementary_wavelength,
14 dominant_wavelength,
15 excitation_purity,
16)
17from colour.colorimetry.dominant import closest_spectral_locus_wavelength
18from colour.constants import TOLERANCE_ABSOLUTE_TESTS
19from colour.models import XYZ_to_xy
20from colour.utilities import ignore_numpy_errors, is_scipy_installed
22__author__ = "Colour Developers"
23__copyright__ = "Copyright 2013 Colour Developers"
24__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
25__maintainer__ = "Colour Developers"
26__email__ = "colour-developers@colour-science.org"
27__status__ = "Production"
29__all__ = [
30 "TestClosestSpectralLocusWavelength",
31 "TestDominantWavelength",
32 "TestComplementaryWavelength",
33 "TestExcitationPurity",
34 "TestColorimetricPurity",
35]
38class TestClosestSpectralLocusWavelength:
39 """
40 Define :func:`colour.colorimetry.dominant.\
41closest_spectral_locus_wavelength` definition unit tests methods.
42 """
44 def setup_method(self) -> None:
45 """Initialise the common tests attributes."""
47 self._xy_s = XYZ_to_xy(MSDS_CMFS["CIE 1931 2 Degree Standard Observer"].values)
49 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
51 def test_closest_spectral_locus_wavelength(self) -> None:
52 """
53 Test :func:`colour.colorimetry.dominant.\
54closest_spectral_locus_wavelength` definition.
55 """
57 if not is_scipy_installed(): # pragma: no cover
58 return
60 xy = np.array([0.54369557, 0.32107944])
61 xy_n = self._xy_D65
62 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s)
64 assert i_wl == np.array(256)
65 np.testing.assert_allclose(
66 xy_wl,
67 np.array([0.68354746, 0.31628409]),
68 atol=TOLERANCE_ABSOLUTE_TESTS,
69 )
71 xy = np.array([0.37605506, 0.24452225])
72 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s)
74 assert i_wl == np.array(248)
75 np.testing.assert_allclose(
76 xy_wl,
77 np.array([0.45723147, 0.13628148]),
78 atol=TOLERANCE_ABSOLUTE_TESTS,
79 )
81 def test_n_dimensional_closest_spectral_locus_wavelength(self) -> None:
82 """
83 Test :func:`colour.colorimetry.dominant.\
84closest_spectral_locus_wavelength` definition n-dimensional arrays support.
85 """
87 if not is_scipy_installed(): # pragma: no cover
88 return
90 xy = np.array([0.54369557, 0.32107944])
91 xy_n = self._xy_D65
92 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s)
93 i_wl_r, xy_wl_r = np.array(256), np.array([0.68354746, 0.31628409])
94 np.testing.assert_allclose(i_wl, i_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
95 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
97 xy = np.tile(xy, (6, 1))
98 xy_n = np.tile(xy_n, (6, 1))
99 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s)
100 i_wl_r = np.tile(i_wl_r, 6)
101 xy_wl_r = np.tile(xy_wl_r, (6, 1))
102 np.testing.assert_allclose(i_wl, i_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
103 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
105 xy = np.reshape(xy, (2, 3, 2))
106 xy_n = np.reshape(xy_n, (2, 3, 2))
107 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s)
108 i_wl_r = np.reshape(i_wl_r, (2, 3))
109 xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2))
110 np.testing.assert_allclose(i_wl, i_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
111 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
113 @ignore_numpy_errors
114 def test_nan_closest_spectral_locus_wavelength(self) -> None:
115 """
116 Test :func:`colour.colorimetry.dominant.\
117closest_spectral_locus_wavelength` definition nan support.
118 """
120 if not is_scipy_installed(): # pragma: no cover
121 return
123 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
124 cases = np.array(list(set(product(cases, repeat=2))))
125 for case in cases:
126 closest_spectral_locus_wavelength(case, case, self._xy_s)
129class TestDominantWavelength:
130 """
131 Define :func:`colour.colorimetry.dominant.dominant_wavelength` definition
132 unit tests methods.
133 """
135 def setup_method(self) -> None:
136 """Initialise the common tests attributes."""
138 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
140 def test_dominant_wavelength(self) -> None:
141 """
142 Test :func:`colour.colorimetry.dominant.dominant_wavelength`
143 definition.
144 """
146 if not is_scipy_installed(): # pragma: no cover
147 return
149 xy = np.array([0.54369557, 0.32107944])
150 xy_n = self._xy_D65
151 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n)
153 assert wl == np.array(616.0)
154 np.testing.assert_allclose(
155 xy_wl,
156 np.array([0.68354746, 0.31628409]),
157 atol=TOLERANCE_ABSOLUTE_TESTS,
158 )
159 np.testing.assert_allclose(
160 xy_cwl,
161 np.array([0.68354746, 0.31628409]),
162 atol=TOLERANCE_ABSOLUTE_TESTS,
163 )
165 xy = np.array([0.37605506, 0.24452225])
166 i_wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n)
168 assert i_wl == np.array(-509.0)
169 np.testing.assert_allclose(
170 xy_wl,
171 np.array([0.45723147, 0.13628148]),
172 atol=TOLERANCE_ABSOLUTE_TESTS,
173 )
174 np.testing.assert_allclose(
175 xy_cwl,
176 np.array([0.01040962, 0.73207453]),
177 atol=TOLERANCE_ABSOLUTE_TESTS,
178 )
180 def test_n_dimensional_dominant_wavelength(self) -> None:
181 """
182 Test :func:`colour.colorimetry.dominant.dominant_wavelength`
183 definition n-dimensional arrays support.
184 """
186 if not is_scipy_installed(): # pragma: no cover
187 return
189 xy = np.array([0.54369557, 0.32107944])
190 xy_n = self._xy_D65
191 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n)
192 wl_r, xy_wl_r, xy_cwl_r = (
193 np.array(616.0),
194 np.array([0.68354746, 0.31628409]),
195 np.array([0.68354746, 0.31628409]),
196 )
197 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
198 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
199 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
201 xy = np.tile(xy, (6, 1))
202 xy_n = np.tile(xy_n, (6, 1))
203 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n)
204 wl_r = np.tile(wl_r, 6)
205 xy_wl_r = np.tile(xy_wl_r, (6, 1))
206 xy_cwl_r = np.tile(xy_cwl_r, (6, 1))
207 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
208 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
209 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
211 xy = np.reshape(xy, (2, 3, 2))
212 xy_n = np.reshape(xy_n, (2, 3, 2))
213 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n)
214 wl_r = np.reshape(wl_r, (2, 3))
215 xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2))
216 xy_cwl_r = np.reshape(xy_cwl_r, (2, 3, 2))
217 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
218 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
219 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
221 @ignore_numpy_errors
222 def test_nan_dominant_wavelength(self) -> None:
223 """
224 Test :func:`colour.colorimetry.dominant.dominant_wavelength`
225 definition nan support.
226 """
228 if not is_scipy_installed(): # pragma: no cover
229 return
231 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
232 cases = np.array(list(set(product(cases, repeat=2))))
233 for case in cases:
234 dominant_wavelength(case, case)
237class TestComplementaryWavelength:
238 """
239 Define :func:`colour.colorimetry.dominant.complementary_wavelength`
240 definition unit tests methods.
241 """
243 def setup_method(self) -> None:
244 """Initialise the common tests attributes."""
246 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
248 def test_complementary_wavelength(self) -> None:
249 """
250 Test :func:`colour.colorimetry.dominant.complementary_wavelength`
251 definition.
252 """
254 if not is_scipy_installed(): # pragma: no cover
255 return
257 xy = np.array([0.54369557, 0.32107944])
258 xy_n = self._xy_D65
259 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n)
261 assert wl == np.array(492.0)
262 np.testing.assert_allclose(
263 xy_wl,
264 np.array([0.03647950, 0.33847127]),
265 atol=TOLERANCE_ABSOLUTE_TESTS,
266 )
267 np.testing.assert_allclose(
268 xy_cwl,
269 np.array([0.03647950, 0.33847127]),
270 atol=TOLERANCE_ABSOLUTE_TESTS,
271 )
273 xy = np.array([0.37605506, 0.24452225])
274 i_wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n)
276 assert i_wl == np.array(509.0)
277 np.testing.assert_allclose(
278 xy_wl,
279 np.array([0.01040962, 0.73207453]),
280 atol=TOLERANCE_ABSOLUTE_TESTS,
281 )
282 np.testing.assert_allclose(
283 xy_cwl,
284 np.array([0.01040962, 0.73207453]),
285 atol=TOLERANCE_ABSOLUTE_TESTS,
286 )
288 def test_n_dimensional_complementary_wavelength(self) -> None:
289 """
290 Test :func:`colour.colorimetry.dominant.complementary_wavelength`
291 definition n-dimensional arrays support.
292 """
294 if not is_scipy_installed(): # pragma: no cover
295 return
297 xy = np.array([0.54369557, 0.32107944])
298 xy_n = self._xy_D65
299 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n)
300 wl_r, xy_wl_r, xy_cwl_r = (
301 np.array(492.0),
302 np.array([0.03647950, 0.33847127]),
303 np.array([0.03647950, 0.33847127]),
304 )
305 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
306 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
307 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
309 xy = np.tile(xy, (6, 1))
310 xy_n = np.tile(xy_n, (6, 1))
311 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n)
312 wl_r = np.tile(wl_r, 6)
313 xy_wl_r = np.tile(xy_wl_r, (6, 1))
314 xy_cwl_r = np.tile(xy_cwl_r, (6, 1))
315 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
316 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
317 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
319 xy = np.reshape(xy, (2, 3, 2))
320 xy_n = np.reshape(xy_n, (2, 3, 2))
321 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n)
322 wl_r = np.reshape(wl_r, (2, 3))
323 xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2))
324 xy_cwl_r = np.reshape(xy_cwl_r, (2, 3, 2))
325 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
326 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
327 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS)
329 @ignore_numpy_errors
330 def test_nan_complementary_wavelength(self) -> None:
331 """
332 Test :func:`colour.colorimetry.dominant.complementary_wavelength`
333 definition nan support.
334 """
336 if not is_scipy_installed(): # pragma: no cover
337 return
339 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
340 cases = np.array(list(set(product(cases, repeat=2))))
341 for case in cases:
342 complementary_wavelength(case, case)
345class TestExcitationPurity:
346 """
347 Define :func:`colour.colorimetry.dominant.excitation_purity` definition
348 unit tests methods.
349 """
351 def setup_method(self) -> None:
352 """Initialise the common tests attributes."""
354 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
356 def test_excitation_purity(self) -> None:
357 """Test :func:`colour.colorimetry.dominant.excitation_purity` definition."""
359 if not is_scipy_installed(): # pragma: no cover
360 return
362 xy = np.array([0.54369557, 0.32107944])
363 xy_n = self._xy_D65
365 np.testing.assert_allclose(
366 excitation_purity(xy, xy_n),
367 0.622885671878446,
368 atol=TOLERANCE_ABSOLUTE_TESTS,
369 )
371 xy = np.array([0.37605506, 0.24452225])
372 np.testing.assert_allclose(
373 excitation_purity(xy, xy_n),
374 0.438347859215887,
375 atol=TOLERANCE_ABSOLUTE_TESTS,
376 )
378 def test_n_dimensional_excitation_purity(self) -> None:
379 """
380 Test :func:`colour.colorimetry.dominant.excitation_purity` definition
381 n-dimensional arrays support.
382 """
384 if not is_scipy_installed(): # pragma: no cover
385 return
387 xy = np.array([0.54369557, 0.32107944])
388 xy_n = self._xy_D65
389 P_e = excitation_purity(xy, xy_n)
391 xy = np.tile(xy, (6, 1))
392 xy_n = np.tile(xy_n, (6, 1))
393 P_e = np.tile(P_e, 6)
394 np.testing.assert_allclose(
395 excitation_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS
396 )
398 xy = np.reshape(xy, (2, 3, 2))
399 xy_n = np.reshape(xy_n, (2, 3, 2))
400 P_e = np.reshape(P_e, (2, 3))
401 np.testing.assert_allclose(
402 excitation_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS
403 )
405 @ignore_numpy_errors
406 def test_nan_excitation_purity(self) -> None:
407 """
408 Test :func:`colour.colorimetry.dominant.excitation_purity` definition
409 nan support.
410 """
412 if not is_scipy_installed(): # pragma: no cover
413 return
415 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
416 cases = np.array(list(set(product(cases, repeat=2))))
417 for case in cases:
418 excitation_purity(case, case)
421class TestColorimetricPurity:
422 """
423 Define :func:`colour.colorimetry.dominant.colorimetric_purity` definition
424 unit tests methods.
425 """
427 def setup_method(self) -> None:
428 """Initialise the common tests attributes."""
430 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
432 def test_colorimetric_purity(self) -> None:
433 """
434 Test :func:`colour.colorimetry.dominant.colorimetric_purity`
435 definition.
436 """
438 if not is_scipy_installed(): # pragma: no cover
439 return
441 xy = np.array([0.54369557, 0.32107944])
442 xy_n = self._xy_D65
444 np.testing.assert_allclose(
445 colorimetric_purity(xy, xy_n),
446 0.613582813175483,
447 atol=TOLERANCE_ABSOLUTE_TESTS,
448 )
450 xy = np.array([0.37605506, 0.24452225])
451 np.testing.assert_allclose(
452 colorimetric_purity(xy, xy_n),
453 0.244307811178847,
454 atol=TOLERANCE_ABSOLUTE_TESTS,
455 )
457 def test_n_dimensional_colorimetric_purity(self) -> None:
458 """
459 Test :func:`colour.colorimetry.dominant.colorimetric_purity`
460 definition n-dimensional arrays support.
461 """
463 if not is_scipy_installed(): # pragma: no cover
464 return
466 xy = np.array([0.54369557, 0.32107944])
467 xy_n = self._xy_D65
468 P_e = colorimetric_purity(xy, xy_n)
470 xy = np.tile(xy, (6, 1))
471 xy_n = np.tile(xy_n, (6, 1))
472 P_e = np.tile(P_e, 6)
473 np.testing.assert_allclose(
474 colorimetric_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS
475 )
477 xy = np.reshape(xy, (2, 3, 2))
478 xy_n = np.reshape(xy_n, (2, 3, 2))
479 P_e = np.reshape(P_e, (2, 3))
480 np.testing.assert_allclose(
481 colorimetric_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS
482 )
484 @ignore_numpy_errors
485 def test_nan_colorimetric_purity(self) -> None:
486 """
487 Test :func:`colour.colorimetry.dominant.colorimetric_purity`
488 definition nan support.
489 """
491 if not is_scipy_installed(): # pragma: no cover
492 return
494 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
495 cases = np.array(list(set(product(cases, repeat=2))))
496 for case in cases:
497 colorimetric_purity(case, case)