Coverage for colorimetry/tests/test_tristimulus_values.py: 100%
246 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"""
2Define the unit tests for the :mod:`colour.colorimetry.tristimulus_values`
3module.
5References
6----------
7- :cite:`ASTMInternational2015b` : ASTM International. (2015). ASTM E308-15 -
8 Standard Practice for Computing the Colors of Objects by Using the CIE
9 System (pp. 1-47). doi:10.1520/E0308-15
10"""
12from __future__ import annotations
14import typing
16import numpy as np
17import pytest
19from colour.algebra import LinearInterpolator, SpragueInterpolator
20from colour.characterisation import SDS_COLOURCHECKERS
21from colour.colorimetry import (
22 MSDS_CMFS,
23 SDS_ILLUMINANTS,
24 MultiSpectralDistributions,
25 SpectralDistribution,
26 SpectralShape,
27 adjust_tristimulus_weighting_factors_ASTME308,
28 handle_spectral_arguments,
29 lagrange_coefficients_ASTME2022,
30 msds_to_XYZ,
31 msds_to_XYZ_ASTME308,
32 msds_to_XYZ_integration,
33 reshape_msds,
34 reshape_sd,
35 sd_CIE_standard_illuminant_A,
36 sd_ones,
37 sd_to_XYZ,
38 sd_to_XYZ_ASTME308,
39 sd_to_XYZ_integration,
40 sd_to_XYZ_tristimulus_weighting_factors_ASTME308,
41 sd_zeros,
42 sds_and_msds_to_msds,
43 tristimulus_weighting_factors_ASTME2022,
44 wavelength_to_XYZ,
45)
46from colour.constants import TOLERANCE_ABSOLUTE_TESTS
48if typing.TYPE_CHECKING:
49 from colour.hints import NDArrayFloat
51from colour.utilities import domain_range_scale
53__author__ = "Colour Developers"
54__copyright__ = "Copyright 2013 Colour Developers"
55__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
56__maintainer__ = "Colour Developers"
57__email__ = "colour-developers@colour-science.org"
58__status__ = "Production"
60__all__ = [
61 "SD_SAMPLE",
62 "LAGRANGE_COEFFICIENTS_A",
63 "LAGRANGE_COEFFICIENTS_B",
64 "TWF_A_CIE_1964_10_10",
65 "TWF_A_CIE_1964_10_20",
66 "TWF_D65_CIE_1931_2_20",
67 "TWF_D65_CIE_1931_2_20_K1",
68 "TWF_D65_CIE_1931_2_20_A",
69 "DATA_TWO",
70 "MSDS_TWO",
71 "TVS_D65_INTEGRATION_MSDS",
72 "TVS_D65_ARRAY_INTEGRATION",
73 "TVS_D65_ARRAY_K1_INTEGRATION",
74 "TVS_D65_ASTME308_MSDS",
75 "TVS_D65_ASTME308_K1_MSDS",
76 "TestHandleSpectralArguments",
77 "TestLagrangeCoefficientsASTME2022",
78 "TestTristimulusWeightingFactorsASTME2022",
79 "TestAdjustTristimulusWeightingFactorsASTME308",
80 "TestSd_to_XYZ_integration",
81 "TestSd_to_XYZ_ASTME308",
82 "TestSd_to_XYZ",
83 "TestMsds_to_XYZ_integration",
84 "TestMsds_to_XYZ_ASTME308",
85 "TestAbsoluteIntegrationToXYZ",
86 "TestWavelength_to_XYZ",
87]
89SD_SAMPLE: SpectralDistribution = SpectralDistribution(
90 {
91 340: 0.0000,
92 345: 0.0000,
93 350: 0.0000,
94 355: 0.0000,
95 360: 0.0000,
96 365: 0.0000,
97 370: 0.0000,
98 375: 0.0000,
99 380: 0.0000,
100 385: 0.0000,
101 390: 0.0000,
102 395: 0.0000,
103 400: 0.0641,
104 405: 0.0650,
105 410: 0.0654,
106 415: 0.0652,
107 420: 0.0645,
108 425: 0.0629,
109 430: 0.0605,
110 435: 0.0581,
111 440: 0.0562,
112 445: 0.0551,
113 450: 0.0543,
114 455: 0.0539,
115 460: 0.0537,
116 465: 0.0538,
117 470: 0.0541,
118 475: 0.0547,
119 480: 0.0559,
120 485: 0.0578,
121 490: 0.0603,
122 495: 0.0629,
123 500: 0.0651,
124 505: 0.0667,
125 510: 0.0680,
126 515: 0.0691,
127 520: 0.0705,
128 525: 0.0720,
129 530: 0.0736,
130 535: 0.0753,
131 540: 0.0772,
132 545: 0.0791,
133 550: 0.0809,
134 555: 0.0833,
135 560: 0.0870,
136 565: 0.0924,
137 570: 0.0990,
138 575: 0.1061,
139 580: 0.1128,
140 585: 0.1190,
141 590: 0.1251,
142 595: 0.1308,
143 600: 0.1360,
144 605: 0.1403,
145 610: 0.1439,
146 615: 0.1473,
147 620: 0.1511,
148 625: 0.1550,
149 630: 0.1590,
150 635: 0.1634,
151 640: 0.1688,
152 645: 0.1753,
153 650: 0.1828,
154 655: 0.1909,
155 660: 0.1996,
156 665: 0.2088,
157 670: 0.2187,
158 675: 0.2291,
159 680: 0.2397,
160 685: 0.2505,
161 690: 0.2618,
162 695: 0.2733,
163 700: 0.2852,
164 705: 0.0000,
165 710: 0.0000,
166 715: 0.0000,
167 720: 0.0000,
168 725: 0.0000,
169 730: 0.0000,
170 735: 0.0000,
171 740: 0.0000,
172 745: 0.0000,
173 750: 0.0000,
174 755: 0.0000,
175 760: 0.0000,
176 765: 0.0000,
177 770: 0.0000,
178 775: 0.0000,
179 780: 0.0000,
180 785: 0.0000,
181 790: 0.0000,
182 795: 0.0000,
183 800: 0.0000,
184 805: 0.0000,
185 810: 0.0000,
186 815: 0.0000,
187 820: 0.0000,
188 825: 0.0000,
189 830: 0.0000,
190 }
191)
193LAGRANGE_COEFFICIENTS_A: NDArrayFloat = np.array(
194 [
195 [-0.0285, 0.9405, 0.1045, -0.0165],
196 [-0.0480, 0.8640, 0.2160, -0.0320],
197 [-0.0595, 0.7735, 0.3315, -0.0455],
198 [-0.0640, 0.6720, 0.4480, -0.0560],
199 [-0.0625, 0.5625, 0.5625, -0.0625],
200 [-0.0560, 0.4480, 0.6720, -0.0640],
201 [-0.0455, 0.3315, 0.7735, -0.0595],
202 [-0.0320, 0.2160, 0.8640, -0.0480],
203 [-0.0165, 0.1045, 0.9405, -0.0285],
204 ]
205)
207LAGRANGE_COEFFICIENTS_B: NDArrayFloat = np.array(
208 [
209 [0.8550, 0.1900, -0.0450],
210 [0.7200, 0.3600, -0.0800],
211 [0.5950, 0.5100, -0.1050],
212 [0.4800, 0.6400, -0.1200],
213 [0.3750, 0.7500, -0.1250],
214 [0.2800, 0.8400, -0.1200],
215 [0.1950, 0.9100, -0.1050],
216 [0.1200, 0.9600, -0.0800],
217 [0.0550, 0.9900, -0.0450],
218 ]
219)
221TWF_A_CIE_1964_10_10: NDArrayFloat = np.array(
222 [
223 [-0.000, -0.000, -0.000],
224 [-0.000, -0.000, -0.000],
225 [-0.000, -0.000, -0.000],
226 [0.002, 0.000, 0.008],
227 [0.025, 0.003, 0.110],
228 [0.134, 0.014, 0.615],
229 [0.377, 0.039, 1.792],
230 [0.686, 0.084, 3.386],
231 [0.964, 0.156, 4.944],
232 [1.080, 0.259, 5.806],
233 [1.006, 0.424, 5.812],
234 [0.731, 0.696, 4.919],
235 [0.343, 1.082, 3.300],
236 [0.078, 1.616, 1.973],
237 [0.022, 2.422, 1.152],
238 [0.218, 3.529, 0.658],
239 [0.750, 4.840, 0.382],
240 [1.642, 6.100, 0.211],
241 [2.842, 7.250, 0.102],
242 [4.336, 8.114, 0.032],
243 [6.200, 8.758, 0.001],
244 [8.262, 8.988, -0.000],
245 [10.227, 8.760, 0.000],
246 [11.945, 8.304, 0.000],
247 [12.746, 7.468, 0.000],
248 [12.337, 6.323, 0.000],
249 [10.817, 5.033, 0.000],
250 [8.560, 3.744, 0.000],
251 [6.014, 2.506, 0.000],
252 [3.887, 1.560, 0.000],
253 [2.309, 0.911, 0.000],
254 [1.276, 0.499, 0.000],
255 [0.666, 0.259, 0.000],
256 [0.336, 0.130, 0.000],
257 [0.166, 0.065, 0.000],
258 [0.082, 0.032, 0.000],
259 [0.040, 0.016, 0.000],
260 [0.020, 0.008, 0.000],
261 [0.010, 0.004, 0.000],
262 [0.005, 0.002, 0.000],
263 [0.003, 0.001, 0.000],
264 [0.001, 0.001, 0.000],
265 [0.001, 0.000, 0.000],
266 [0.000, 0.000, 0.000],
267 [0.000, 0.000, 0.000],
268 [0.000, 0.000, 0.000],
269 [0.000, 0.000, 0.000],
270 [0.000, 0.000, 0.000],
271 ]
272)
274TWF_A_CIE_1964_10_20: NDArrayFloat = np.array(
275 [
276 [-0.000, -0.000, -0.001],
277 [-0.009, -0.001, -0.041],
278 [0.060, 0.005, 0.257],
279 [0.773, 0.078, 3.697],
280 [1.900, 0.304, 9.755],
281 [1.971, 0.855, 11.487],
282 [0.718, 2.146, 6.785],
283 [0.043, 4.899, 2.321],
284 [1.522, 9.647, 0.743],
285 [5.677, 14.461, 0.196],
286 [12.445, 17.474, 0.005],
287 [20.554, 17.584, -0.003],
288 [25.332, 14.896, 0.000],
289 [21.571, 10.080, 0.000],
290 [12.179, 5.068, 0.000],
291 [4.668, 1.830, 0.000],
292 [1.324, 0.513, 0.000],
293 [0.318, 0.123, 0.000],
294 [0.075, 0.029, 0.000],
295 [0.018, 0.007, 0.000],
296 [0.005, 0.002, 0.000],
297 [0.001, 0.001, 0.000],
298 [0.000, 0.000, 0.000],
299 [0.000, 0.000, 0.000],
300 ]
301)
303TWF_D65_CIE_1931_2_20: NDArrayFloat = np.array(
304 [
305 [-0.001, -0.000, -0.005],
306 [-0.008, -0.000, -0.039],
307 [0.179, 0.002, 0.829],
308 [2.542, 0.071, 12.203],
309 [6.670, 0.453, 33.637],
310 [6.333, 1.316, 36.334],
311 [2.213, 2.933, 18.278],
312 [0.052, 6.866, 5.543],
313 [1.348, 14.106, 1.611],
314 [5.767, 18.981, 0.382],
315 [11.301, 18.863, 0.068],
316 [16.256, 15.455, 0.025],
317 [17.933, 10.699, 0.013],
318 [14.020, 6.277, 0.003],
319 [7.057, 2.743, 0.000],
320 [2.527, 0.927, -0.000],
321 [0.670, 0.242, -0.000],
322 [0.140, 0.050, 0.000],
323 [0.035, 0.013, 0.000],
324 [0.008, 0.003, 0.000],
325 [0.002, 0.001, 0.000],
326 [0.000, 0.000, 0.000],
327 [0.000, 0.000, 0.000],
328 [0.000, 0.000, 0.000],
329 ]
330)
332TWF_D65_CIE_1931_2_20_K1: NDArrayFloat = np.array(
333 [
334 [-0.10095678, -0.00265636, -0.48295051],
335 [-0.83484763, -0.02190274, -4.11563004],
336 [18.94315946, 0.22803520, 87.62930101],
337 [268.66426663, 7.45156533, 1289.46785306],
338 [704.84093814, 47.85119956, 3554.47729494],
339 [669.16372619, 139.09334752, 3839.47028848],
340 [233.89418975, 309.95075927, 1931.47489098],
341 [5.51347204, 725.55154566, 585.77542998],
342 [142.48116090, 1490.55009270, 170.27819443],
343 [609.43424752, 2005.73581058, 40.34233506],
344 [1194.21293134, 1993.32004423, 7.15981395],
345 [1717.79835378, 1633.12477710, 2.59651081],
346 [1895.00791740, 1130.54333854, 1.34461357],
347 [1481.55235852, 663.25632432, 0.29999368],
348 [745.76471129, 289.85683288, 0.01943154],
349 [267.01875994, 97.97358872, -0.00261658],
350 [70.75239887, 25.56445574, -0.00019929],
351 [14.78862574, 5.31713332, 0.00000000],
352 [3.67620064, 1.32650433, 0.00000000],
353 [0.89699648, 0.32392186, 0.00000000],
354 [0.16623785, 0.06003153, 0.00000000],
355 [0.04824448, 0.01742197, 0.00000000],
356 [0.01310759, 0.00473339, 0.00000000],
357 [0.00223616, 0.00080752, 0.00000000],
358 ]
359)
361TWF_D65_CIE_1931_2_20_A: NDArrayFloat = np.array(
362 [
363 [0.170, 0.002, 0.785],
364 [2.542, 0.071, 12.203],
365 [6.670, 0.453, 33.637],
366 [6.333, 1.316, 36.334],
367 [2.213, 2.933, 18.278],
368 [0.052, 6.866, 5.543],
369 [1.348, 14.106, 1.611],
370 [5.767, 18.981, 0.382],
371 [11.301, 18.863, 0.068],
372 [16.256, 15.455, 0.025],
373 [17.933, 10.699, 0.013],
374 [14.020, 6.277, 0.003],
375 [7.057, 2.743, 0.000],
376 [2.527, 0.927, -0.000],
377 [0.670, 0.242, -0.000],
378 [0.185, 0.067, 0.000],
379 ]
380)
382DATA_TWO: NDArrayFloat = np.array(
383 [
384 [
385 [
386 0.01367208,
387 0.09127947,
388 0.01524376,
389 0.02810712,
390 0.19176012,
391 0.04299992,
392 ],
393 [
394 0.01591516,
395 0.31454948,
396 0.08416876,
397 0.09071489,
398 0.71026170,
399 0.04374762,
400 ],
401 [
402 0.00959792,
403 0.25822842,
404 0.41388571,
405 0.22275120,
406 0.00407416,
407 0.37439537,
408 ],
409 [
410 0.01106279,
411 0.07090867,
412 0.02204929,
413 0.12487984,
414 0.18168917,
415 0.00202945,
416 ],
417 [
418 0.01791409,
419 0.29707789,
420 0.56295109,
421 0.23752193,
422 0.00236515,
423 0.58190280,
424 ],
425 [
426 0.10565346,
427 0.46204320,
428 0.19180590,
429 0.56250858,
430 0.42085907,
431 0.00270085,
432 ],
433 ],
434 [
435 [
436 0.04325933,
437 0.26825359,
438 0.23732357,
439 0.05175860,
440 0.01181048,
441 0.08233768,
442 ],
443 [
444 0.02577249,
445 0.08305486,
446 0.04303044,
447 0.32298771,
448 0.23022813,
449 0.00813306,
450 ],
451 [
452 0.02484169,
453 0.12027161,
454 0.00541695,
455 0.00654612,
456 0.18603799,
457 0.36247808,
458 ],
459 [
460 0.01861601,
461 0.12924391,
462 0.00785840,
463 0.40062562,
464 0.94044405,
465 0.32133976,
466 ],
467 [
468 0.03102159,
469 0.16815442,
470 0.37186235,
471 0.08610666,
472 0.00413520,
473 0.78492409,
474 ],
475 [
476 0.04727245,
477 0.32210270,
478 0.22679484,
479 0.31613642,
480 0.11242847,
481 0.00244144,
482 ],
483 ],
484 ]
485)
487MSDS_TWO: MultiSpectralDistributions = MultiSpectralDistributions(
488 np.transpose(np.reshape(DATA_TWO, [-1, 6])),
489 SpectralShape(400, 700, 60).wavelengths,
490)
492TVS_D65_INTEGRATION_MSDS: NDArrayFloat = np.array(
493 [
494 [7.50219602, 3.95048275, 8.40152163],
495 [26.92629005, 15.07170066, 28.71020457],
496 [16.70060700, 28.21421317, 25.64802044],
497 [11.57577260, 8.64108703, 6.57740493],
498 [18.73108262, 35.07369122, 30.14365007],
499 [45.16559608, 39.61411218, 43.68158810],
500 [8.17318743, 13.09236381, 25.93755134],
501 [22.46715798, 19.31066951, 7.95954422],
502 [6.58106180, 2.52865132, 11.09122159],
503 [43.91745731, 27.98043364, 11.73313699],
504 [8.53693599, 19.70195654, 17.70110118],
505 [23.91114755, 26.21471641, 30.67613685],
506 ]
507)
509TVS_D65_ARRAY_INTEGRATION: NDArrayFloat = np.array(
510 [
511 [
512 [7.19510558, 3.86227393, 10.09950719],
513 [25.57464912, 14.71934603, 34.84931928],
514 [17.58300551, 28.56388139, 30.18370150],
515 [11.32631694, 8.46087304, 7.90263107],
516 [19.65793587, 35.59047030, 35.14042633],
517 [45.82162927, 39.26057155, 51.79537877],
518 ],
519 [
520 [8.82617380, 13.38600040, 30.56510531],
521 [22.33167167, 18.95683859, 9.39034481],
522 [6.69130415, 2.57592352, 13.25898396],
523 [41.81950400, 27.11920225, 14.26746010],
524 [9.24148668, 20.20448258, 20.19416075],
525 [24.78545992, 26.22388193, 36.44325237],
526 ],
527 ]
528)
530TVS_D65_ARRAY_K1_INTEGRATION: NDArrayFloat = np.array(
531 [
532 [
533 [7.7611755347, 4.1661356647, 10.8940789347],
534 [27.5867169575, 15.8773804035, 37.5910653835],
535 [18.9663363103, 30.8111250154, 32.5583833518],
536 [12.2174071019, 9.1265263825, 8.5243651028],
537 [21.2045103874, 38.3905259546, 37.9050750799],
538 [49.4266142767, 42.3493698798, 55.8703443988],
539 ],
540 [
541 [9.5205669281, 14.4391347280, 32.9697938475],
542 [24.0886005021, 20.4482547598, 10.1291236929],
543 [7.2177378835, 2.7785825207, 14.3021253594],
544 [45.1096245706, 29.2527867434, 15.3899426670],
545 [9.9685542597, 21.7940562871, 21.7829223889],
546 [26.7354388456, 28.2870277157, 39.3104000571],
547 ],
548 ]
549)
551TVS_D65_ASTME308_MSDS: NDArrayFloat = np.array(
552 [
553 [7.50450425, 3.95744742, 8.38735462],
554 [26.94116124, 15.09801442, 28.66753115],
555 [16.70212538, 28.20596151, 25.65809190],
556 [11.57025728, 8.64549437, 6.55935421],
557 [18.74248163, 35.06128859, 30.17576781],
558 [45.12240306, 39.62432052, 43.58455883],
559 [8.17632546, 13.09396693, 25.92811880],
560 [22.44582614, 19.31227394, 7.92623840],
561 [6.57937576, 2.53370970, 11.07068448],
562 [43.91405117, 28.00039763, 11.68910584],
563 [8.54996478, 19.69029667, 17.73601959],
564 [23.88899194, 26.21653407, 30.62958339],
565 ]
566)
568TVS_D65_ASTME308_K1_MSDS: NDArrayFloat = np.array(
569 [
570 [7.9300584037, 4.1818604067, 8.8629721234],
571 [28.4689001419, 15.9541699464, 30.2931664392],
572 [17.6492444185, 29.8054228038, 27.1130724359],
573 [12.2263660519, 9.1357500818, 6.9313122104],
574 [19.8053021265, 37.0494914782, 31.8869299177],
575 [47.6811365133, 41.8712769685, 46.0560865221],
576 [8.6399762445, 13.8364799389, 27.3984116154],
577 [23.7186503263, 20.4074053594, 8.3757076242],
578 [6.9524691183, 2.6773875067, 11.6984642292],
579 [46.4042632139, 29.5882021193, 12.3519540951],
580 [9.0348033348, 20.8068644330, 18.7417671434],
581 [25.2436530136, 27.7031818974, 32.3664797963],
582 ]
583)
586class TestHandleSpectralArguments:
587 """
588 Define :func:`colour.colorimetry.tristimulus_values.\
589handle_spectral_arguments` definition unit tests methods.
590 """
592 def test_handle_spectral_arguments(self) -> None:
593 """
594 Test :func:`colour.colorimetry.tristimulus_values.\
595handle_spectral_arguments` definition.
596 """
598 cmfs, illuminant = handle_spectral_arguments()
599 assert cmfs == reshape_msds(MSDS_CMFS["CIE 1931 2 Degree Standard Observer"])
600 assert illuminant == reshape_sd(SDS_ILLUMINANTS["D65"])
602 shape = SpectralShape(400, 700, 20)
603 cmfs, illuminant = handle_spectral_arguments(shape_default=shape)
604 assert cmfs.shape == shape
605 assert illuminant.shape == shape
607 cmfs, illuminant = handle_spectral_arguments(
608 cmfs_default="CIE 2015 2 Degree Standard Observer",
609 illuminant_default="E",
610 shape_default=shape,
611 )
612 assert cmfs == reshape_msds(
613 MSDS_CMFS["CIE 2015 2 Degree Standard Observer"], shape=shape
614 )
615 assert illuminant == sd_ones(shape, interpolator=LinearInterpolator) * 100
618class TestLagrangeCoefficientsASTME2022:
619 """
620 Define :func:`colour.colorimetry.tristimulus_values.\
621lagrange_coefficients_ASTME2022` definition unit tests methods.
622 """
624 def test_lagrange_coefficients_ASTME2022(self) -> None:
625 """
626 Test :func:`colour.colorimetry.tristimulus_values.\
627lagrange_coefficients_ASTME2022` definition.
628 """
630 np.testing.assert_allclose(
631 lagrange_coefficients_ASTME2022(10, "inner"),
632 LAGRANGE_COEFFICIENTS_A,
633 atol=TOLERANCE_ABSOLUTE_TESTS,
634 )
636 np.testing.assert_allclose(
637 lagrange_coefficients_ASTME2022(10, "boundary"),
638 LAGRANGE_COEFFICIENTS_B,
639 atol=TOLERANCE_ABSOLUTE_TESTS,
640 )
642 # Testing that the cache returns a copy of the data.
643 lagrange_coefficients = lagrange_coefficients_ASTME2022(10)
645 np.testing.assert_allclose(
646 lagrange_coefficients,
647 LAGRANGE_COEFFICIENTS_A,
648 atol=TOLERANCE_ABSOLUTE_TESTS,
649 )
651 lagrange_coefficients *= 10
653 np.testing.assert_allclose(
654 lagrange_coefficients_ASTME2022(10),
655 LAGRANGE_COEFFICIENTS_A,
656 atol=TOLERANCE_ABSOLUTE_TESTS,
657 )
660class TestTristimulusWeightingFactorsASTME2022:
661 """
662 Define :func:`colour.colorimetry.tristimulus_values.\
663tristimulus_weighting_factors_ASTME2022` definition unit tests methods.
664 """
666 def test_tristimulus_weighting_factors_ASTME2022(self) -> None:
667 """
668 Test :func:`colour.colorimetry.tristimulus_values.\
669tristimulus_weighting_factors_ASTME2022` definition.
671 Notes
672 -----
673 - :attr:`TWF_A_CIE_1964_10_10`, :attr:`TWF_A_CIE_1964_10_20` and
674 :attr:`TWF_D65_CIE_1931_2_20` attributes data is matching
675 :cite:`ASTMInternational2015b`.
677 References
678 ----------
679 :cite:`ASTMInternational2015b`
680 """
682 cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"]
683 A = sd_CIE_standard_illuminant_A(cmfs.shape)
685 twf = tristimulus_weighting_factors_ASTME2022(
686 cmfs, A, SpectralShape(360, 830, 10)
687 )
688 np.testing.assert_allclose(np.round(twf, 3), TWF_A_CIE_1964_10_10, atol=1e-5)
690 twf = tristimulus_weighting_factors_ASTME2022(
691 cmfs, A, SpectralShape(360, 830, 20)
692 )
693 np.testing.assert_allclose(np.round(twf, 3), TWF_A_CIE_1964_10_20, atol=1e-5)
695 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
696 D65 = reshape_sd(
697 SDS_ILLUMINANTS["D65"], cmfs.shape, interpolator=LinearInterpolator
698 )
699 twf = tristimulus_weighting_factors_ASTME2022(
700 cmfs, D65, SpectralShape(360, 830, 20)
701 )
702 np.testing.assert_allclose(
703 np.round(twf, 3),
704 TWF_D65_CIE_1931_2_20,
705 atol=TOLERANCE_ABSOLUTE_TESTS,
706 )
708 twf = tristimulus_weighting_factors_ASTME2022(
709 cmfs, D65, SpectralShape(360, 830, 20), k=1
710 )
711 np.testing.assert_allclose(
712 twf, TWF_D65_CIE_1931_2_20_K1, atol=TOLERANCE_ABSOLUTE_TESTS
713 )
715 # Testing that the cache returns a copy of the data.
716 cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"]
717 twf = tristimulus_weighting_factors_ASTME2022(
718 cmfs, A, SpectralShape(360, 830, 10)
719 )
720 np.testing.assert_allclose(
721 np.round(twf, 3),
722 TWF_A_CIE_1964_10_10,
723 atol=TOLERANCE_ABSOLUTE_TESTS,
724 )
726 np.testing.assert_allclose(
727 np.round(
728 tristimulus_weighting_factors_ASTME2022(
729 cmfs, A, SpectralShape(360, 830, 10)
730 ),
731 3,
732 ),
733 TWF_A_CIE_1964_10_10,
734 atol=TOLERANCE_ABSOLUTE_TESTS,
735 )
737 def test_raise_exception_tristimulus_weighting_factors_ASTME2022(self) -> None:
738 """
739 Test :func:`colour.colorimetry.tristimulus_values.\
740tristimulus_weighting_factors_ASTME2022` definition raised exception.
741 """
743 shape = SpectralShape(360, 830, 10)
744 cmfs_1 = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"]
745 cmfs_2 = reshape_msds(cmfs_1, shape)
746 A_1 = sd_CIE_standard_illuminant_A(cmfs_1.shape)
747 A_2 = sd_CIE_standard_illuminant_A(cmfs_2.shape)
749 pytest.raises(
750 ValueError,
751 tristimulus_weighting_factors_ASTME2022,
752 cmfs_1,
753 A_2,
754 shape,
755 )
757 pytest.raises(
758 ValueError,
759 tristimulus_weighting_factors_ASTME2022,
760 cmfs_2,
761 A_1,
762 shape,
763 )
766class TestAdjustTristimulusWeightingFactorsASTME308:
767 """
768 Define :func:`colour.colorimetry.tristimulus_values.\
769adjust_tristimulus_weighting_factors_ASTME308` definition unit tests methods.
770 """
772 def test_adjust_tristimulus_weighting_factors_ASTME308(self) -> None:
773 """
774 Test :func:`colour.colorimetry.tristimulus_values.\
775adjust_tristimulus_weighting_factors_ASTME308` definition.
776 """
778 np.testing.assert_allclose(
779 adjust_tristimulus_weighting_factors_ASTME308(
780 TWF_D65_CIE_1931_2_20,
781 SpectralShape(360, 830, 20),
782 SpectralShape(400, 700, 20),
783 ),
784 TWF_D65_CIE_1931_2_20_A,
785 atol=TOLERANCE_ABSOLUTE_TESTS,
786 )
789class TestSd_to_XYZ_integration:
790 """
791 Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_integration`
792 definition unit tests methods.
793 """
795 def test_sd_to_XYZ_integration(self) -> None:
796 """
797 Test :func:`colour.colorimetry.tristimulus_values.\
798sd_to_XYZ_integration` definition.
799 """
801 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
802 np.testing.assert_allclose(
803 sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]),
804 np.array([14.46341147, 10.85819624, 2.04695585]),
805 atol=TOLERANCE_ABSOLUTE_TESTS,
806 )
808 np.testing.assert_allclose(
809 sd_to_XYZ_integration(
810 SD_SAMPLE.values,
811 cmfs,
812 SDS_ILLUMINANTS["A"],
813 shape=SD_SAMPLE.shape,
814 ),
815 np.array([14.46365947, 10.85828084, 2.04663993]),
816 atol=TOLERANCE_ABSOLUTE_TESTS,
817 )
819 cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"]
820 np.testing.assert_allclose(
821 sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["C"]),
822 np.array([10.77002699, 9.44876636, 6.62415290]),
823 atol=TOLERANCE_ABSOLUTE_TESTS,
824 )
826 np.testing.assert_allclose(
827 sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["FL2"]),
828 np.array([11.57540576, 9.98608874, 3.95242590]),
829 atol=TOLERANCE_ABSOLUTE_TESTS,
830 )
832 np.testing.assert_allclose(
833 sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["FL2"], k=683),
834 np.array([1223.7509261493, 1055.7284645912, 417.8501342332]),
835 atol=TOLERANCE_ABSOLUTE_TESTS,
836 )
838 np.testing.assert_allclose(
839 sd_to_XYZ_integration(
840 SD_SAMPLE,
841 cmfs,
842 SDS_ILLUMINANTS["FL2"],
843 shape=SpectralShape(400, 700, 20),
844 ),
845 np.array([11.98232967, 10.13543929, 3.66442524]),
846 atol=TOLERANCE_ABSOLUTE_TESTS,
847 )
849 def test_domain_range_scale_sd_to_XYZ_integration(self) -> None:
850 """
851 Test :func:`colour.colorimetry.tristimulus_values.\
852sd_to_XYZ_integration` definition domain and range scale support.
853 """
855 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
856 XYZ = sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"])
858 d_r = (("reference", 1), ("1", 0.01), ("100", 1))
859 for scale, factor in d_r:
860 with domain_range_scale(scale):
861 np.testing.assert_allclose(
862 sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]),
863 XYZ * factor,
864 atol=TOLERANCE_ABSOLUTE_TESTS,
865 )
868class TestSd_to_XYZ_tristimulus_weighting_factors_ASTME308:
869 """
870 Define :func:`colour.colorimetry.tristimulus_values.\
871sd_to_XYZ_tristimulus_weighting_factors_ASTME308`
872 definition unit tests methods.
873 """
875 def test_sd_to_XYZ_tristimulus_weighting_factors_ASTME308(self) -> None:
876 """
877 Test :func:`colour.colorimetry.tristimulus_values.\
878sd_to_XYZ_tristimulus_weighting_factors_ASTME308`
879 definition.
880 """
882 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
883 np.testing.assert_allclose(
884 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
885 SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]
886 ),
887 np.array([14.46341867, 10.85820227, 2.04697034]),
888 atol=TOLERANCE_ABSOLUTE_TESTS,
889 )
891 cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"]
892 np.testing.assert_allclose(
893 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
894 SD_SAMPLE, cmfs, SDS_ILLUMINANTS["C"]
895 ),
896 np.array([10.77005571, 9.44877491, 6.62428210]),
897 atol=TOLERANCE_ABSOLUTE_TESTS,
898 )
900 np.testing.assert_allclose(
901 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
902 SD_SAMPLE, cmfs, SDS_ILLUMINANTS["FL2"]
903 ),
904 np.array([11.57542759, 9.98605604, 3.95273304]),
905 atol=TOLERANCE_ABSOLUTE_TESTS,
906 )
908 np.testing.assert_allclose(
909 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
910 reshape_sd(SD_SAMPLE, SpectralShape(400, 700, 5), "Trim"),
911 cmfs,
912 SDS_ILLUMINANTS["A"],
913 ),
914 np.array([14.38153638, 10.74503131, 2.01613844]),
915 atol=TOLERANCE_ABSOLUTE_TESTS,
916 )
918 np.testing.assert_allclose(
919 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
920 reshape_sd(SD_SAMPLE, SpectralShape(400, 700, 10), "Interpolate"),
921 cmfs,
922 SDS_ILLUMINANTS["A"],
923 ),
924 np.array([14.38257202, 10.74568178, 2.01588427]),
925 atol=TOLERANCE_ABSOLUTE_TESTS,
926 )
928 np.testing.assert_allclose(
929 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
930 reshape_sd(SD_SAMPLE, SpectralShape(400, 700, 20), "Interpolate"),
931 cmfs,
932 SDS_ILLUMINANTS["A"],
933 ),
934 np.array([14.38329645, 10.74603515, 2.01561113]),
935 atol=TOLERANCE_ABSOLUTE_TESTS,
936 )
938 np.testing.assert_allclose(
939 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
940 reshape_sd(SD_SAMPLE, SpectralShape(400, 700, 20), "Interpolate"),
941 cmfs,
942 SDS_ILLUMINANTS["A"],
943 k=1,
944 ),
945 np.array([1636.74881983, 1222.84626486, 229.36669308]),
946 atol=TOLERANCE_ABSOLUTE_TESTS,
947 )
949 def test_domain_range_scale_sd_to_XYZ_twf_ASTME308(self) -> None:
950 """
951 Test :func:`colour.colorimetry.tristimulus_values.\
952sd_to_XYZ_tristimulus_weighting_factors_ASTME308` definition domain and
953 range scale support.
954 """
956 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
957 XYZ = sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
958 SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]
959 )
961 d_r = (("reference", 1), ("1", 0.01), ("100", 1))
962 for scale, factor in d_r:
963 with domain_range_scale(scale):
964 np.testing.assert_allclose(
965 sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
966 SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]
967 ),
968 XYZ * factor,
969 atol=TOLERANCE_ABSOLUTE_TESTS,
970 )
973class TestSd_to_XYZ_ASTME308:
974 """
975 Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308`
976 definition unit tests methods.
977 """
979 def setup_method(self) -> None:
980 """Initialise the common tests attributes."""
982 self._sd = SD_SAMPLE.copy()
983 self._cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
984 self._A = sd_CIE_standard_illuminant_A(self._cmfs.shape)
986 def test_sd_to_XYZ_ASTME308_mi_1nm(self) -> None:
987 """
988 Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308`
989 definition for 1 nm measurement intervals.
990 """
992 np.testing.assert_allclose(
993 sd_to_XYZ_ASTME308(
994 reshape_sd(self._sd, self._cmfs.shape), self._cmfs, self._A
995 ),
996 np.array([14.46372680, 10.85832950, 2.04663200]),
997 atol=TOLERANCE_ABSOLUTE_TESTS,
998 )
1000 np.testing.assert_allclose(
1001 sd_to_XYZ_ASTME308(
1002 reshape_sd(self._sd, self._cmfs.shape),
1003 self._cmfs,
1004 self._A,
1005 use_practice_range=False,
1006 ),
1007 np.array([14.46366018, 10.85827949, 2.04662258]),
1008 atol=TOLERANCE_ABSOLUTE_TESTS,
1009 )
1011 np.testing.assert_allclose(
1012 sd_to_XYZ_ASTME308(
1013 reshape_sd(self._sd, SpectralShape(400, 700, 1)),
1014 self._cmfs,
1015 self._A,
1016 ),
1017 np.array([14.54173397, 10.88628632, 2.04965822]),
1018 atol=TOLERANCE_ABSOLUTE_TESTS,
1019 )
1021 np.testing.assert_allclose(
1022 sd_to_XYZ_ASTME308(
1023 reshape_sd(self._sd, SpectralShape(400, 700, 1)),
1024 self._cmfs,
1025 self._A,
1026 use_practice_range=False,
1027 ),
1028 np.array([14.54203076, 10.88636754, 2.04964877]),
1029 atol=TOLERANCE_ABSOLUTE_TESTS,
1030 )
1032 np.testing.assert_allclose(
1033 sd_to_XYZ_ASTME308(
1034 reshape_sd(self._sd, SpectralShape(400, 700, 1)),
1035 self._cmfs,
1036 self._A,
1037 k=1,
1038 ),
1039 np.array([15.6898152997, 11.7457671769, 2.2114803420]),
1040 atol=TOLERANCE_ABSOLUTE_TESTS,
1041 )
1043 def test_sd_to_XYZ_ASTME308_mi_5nm(self) -> None:
1044 """
1045 Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308`
1046 definition for 5 nm measurement intervals.
1047 """
1049 np.testing.assert_allclose(
1050 sd_to_XYZ_ASTME308(
1051 reshape_sd(self._sd, SpectralShape(360, 830, 5)),
1052 self._cmfs,
1053 self._A,
1054 ),
1055 np.array([14.46372173, 10.85832502, 2.04664734]),
1056 atol=TOLERANCE_ABSOLUTE_TESTS,
1057 )
1059 np.testing.assert_allclose(
1060 sd_to_XYZ_ASTME308(
1061 reshape_sd(self._sd, SpectralShape(360, 830, 5)),
1062 self._cmfs,
1063 self._A,
1064 use_practice_range=False,
1065 ),
1066 np.array([14.46366388, 10.85828159, 2.04663915]),
1067 atol=TOLERANCE_ABSOLUTE_TESTS,
1068 )
1070 np.testing.assert_allclose(
1071 sd_to_XYZ_ASTME308(
1072 reshape_sd(self._sd, SpectralShape(360, 830, 5)),
1073 self._cmfs,
1074 self._A,
1075 mi_5nm_omission_method=False,
1076 ),
1077 np.array([14.46373399, 10.85833553, 2.0466465]),
1078 atol=TOLERANCE_ABSOLUTE_TESTS,
1079 )
1081 np.testing.assert_allclose(
1082 sd_to_XYZ_ASTME308(
1083 reshape_sd(self._sd, SpectralShape(400, 700, 5)),
1084 self._cmfs,
1085 self._A,
1086 ),
1087 np.array([14.54025742, 10.88576251, 2.04950226]),
1088 atol=TOLERANCE_ABSOLUTE_TESTS,
1089 )
1091 np.testing.assert_allclose(
1092 sd_to_XYZ_ASTME308(
1093 reshape_sd(self._sd, SpectralShape(400, 700, 5)),
1094 self._cmfs,
1095 self._A,
1096 use_practice_range=False,
1097 ),
1098 np.array([14.54051517, 10.88583304, 2.04949406]),
1099 atol=TOLERANCE_ABSOLUTE_TESTS,
1100 )
1102 np.testing.assert_allclose(
1103 sd_to_XYZ_ASTME308(
1104 reshape_sd(self._sd, SpectralShape(400, 700, 5)),
1105 self._cmfs,
1106 self._A,
1107 mi_5nm_omission_method=False,
1108 ),
1109 np.array([14.54022093, 10.88575468, 2.04951057]),
1110 atol=TOLERANCE_ABSOLUTE_TESTS,
1111 )
1113 np.testing.assert_allclose(
1114 sd_to_XYZ_ASTME308(
1115 reshape_sd(self._sd, SpectralShape(360, 830, 5)),
1116 self._cmfs,
1117 self._A,
1118 use_practice_range=False,
1119 mi_5nm_omission_method=False,
1120 ),
1121 np.array([14.46366737, 10.85828552, 2.04663707]),
1122 atol=TOLERANCE_ABSOLUTE_TESTS,
1123 )
1125 np.testing.assert_allclose(
1126 sd_to_XYZ_ASTME308(
1127 reshape_sd(self._sd, SpectralShape(400, 700, 5)),
1128 self._cmfs,
1129 self._A,
1130 use_practice_range=False,
1131 mi_5nm_omission_method=False,
1132 ),
1133 np.array([14.54051772, 10.88583590, 2.04950113]),
1134 atol=TOLERANCE_ABSOLUTE_TESTS,
1135 )
1137 np.testing.assert_allclose(
1138 sd_to_XYZ_ASTME308(
1139 reshape_sd(self._sd, SpectralShape(400, 700, 5)),
1140 self._cmfs,
1141 self._A,
1142 k=1,
1143 ),
1144 np.array([15.6882479013, 11.7452212708, 2.2113156963]),
1145 atol=TOLERANCE_ABSOLUTE_TESTS,
1146 )
1148 def test_sd_to_XYZ_ASTME308_mi_10nm(self) -> None:
1149 """
1150 Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308`
1151 definition for 10 nm measurement intervals.
1152 """
1154 np.testing.assert_allclose(
1155 sd_to_XYZ_ASTME308(
1156 reshape_sd(self._sd, SpectralShape(360, 830, 10)),
1157 self._cmfs,
1158 self._A,
1159 ),
1160 np.array([14.47779980, 10.86358645, 2.04751388]),
1161 atol=TOLERANCE_ABSOLUTE_TESTS,
1162 )
1164 np.testing.assert_allclose(
1165 sd_to_XYZ_ASTME308(
1166 reshape_sd(self._sd, SpectralShape(360, 830, 10)),
1167 self._cmfs,
1168 self._A,
1169 use_practice_range=False,
1170 ),
1171 np.array([14.47773312, 10.86353641, 2.04750445]),
1172 atol=TOLERANCE_ABSOLUTE_TESTS,
1173 )
1175 np.testing.assert_allclose(
1176 sd_to_XYZ_ASTME308(
1177 reshape_sd(self._sd, SpectralShape(400, 700, 10)),
1178 self._cmfs,
1179 self._A,
1180 ),
1181 np.array([14.54137532, 10.88641727, 2.04931318]),
1182 atol=TOLERANCE_ABSOLUTE_TESTS,
1183 )
1185 np.testing.assert_allclose(
1186 sd_to_XYZ_ASTME308(
1187 reshape_sd(self._sd, SpectralShape(400, 700, 10)),
1188 self._cmfs,
1189 self._A,
1190 use_practice_range=False,
1191 ),
1192 np.array([14.54167211, 10.88649849, 2.04930374]),
1193 atol=TOLERANCE_ABSOLUTE_TESTS,
1194 )
1196 np.testing.assert_allclose(
1197 sd_to_XYZ_ASTME308(
1198 reshape_sd(self._sd, SpectralShape(400, 700, 10)),
1199 self._cmfs,
1200 self._A,
1201 k=1,
1202 ),
1203 np.array([15.6894283333, 11.7459084705, 2.2111080639]),
1204 atol=TOLERANCE_ABSOLUTE_TESTS,
1205 )
1207 np.testing.assert_allclose(
1208 sd_to_XYZ_ASTME308(
1209 reshape_sd(self._sd, SpectralShape(401, 701, 10)),
1210 self._cmfs,
1211 self._A,
1212 k=1,
1213 ),
1214 np.array([15.6713226093, 11.7392254489, 2.2117708792]),
1215 atol=TOLERANCE_ABSOLUTE_TESTS,
1216 )
1218 def test_sd_to_XYZ_ASTME308_mi_20nm(self) -> None:
1219 """
1220 Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308`
1221 definition for 20 nm measurement intervals.
1222 """
1224 np.testing.assert_allclose(
1225 sd_to_XYZ_ASTME308(
1226 reshape_sd(self._sd, SpectralShape(360, 820, 20)),
1227 self._cmfs,
1228 self._A,
1229 ),
1230 np.array([14.50187464, 10.87217124, 2.04918305]),
1231 atol=TOLERANCE_ABSOLUTE_TESTS,
1232 )
1234 np.testing.assert_allclose(
1235 sd_to_XYZ_ASTME308(
1236 reshape_sd(self._sd, SpectralShape(360, 820, 20)),
1237 self._cmfs,
1238 self._A,
1239 use_practice_range=False,
1240 ),
1241 np.array([14.50180785, 10.87212116, 2.04917361]),
1242 atol=TOLERANCE_ABSOLUTE_TESTS,
1243 )
1245 np.testing.assert_allclose(
1246 sd_to_XYZ_ASTME308(
1247 reshape_sd(self._sd, SpectralShape(360, 820, 20)),
1248 self._cmfs,
1249 self._A,
1250 mi_20nm_interpolation_method=False,
1251 ),
1252 np.array([14.50216194, 10.87236873, 2.04977256]),
1253 atol=TOLERANCE_ABSOLUTE_TESTS,
1254 )
1256 np.testing.assert_allclose(
1257 sd_to_XYZ_ASTME308(
1258 reshape_sd(self._sd, SpectralShape(400, 700, 20)),
1259 self._cmfs,
1260 self._A,
1261 ),
1262 np.array([14.54114025, 10.88634755, 2.04916445]),
1263 atol=TOLERANCE_ABSOLUTE_TESTS,
1264 )
1266 np.testing.assert_allclose(
1267 sd_to_XYZ_ASTME308(
1268 reshape_sd(self._sd, SpectralShape(400, 700, 20)),
1269 self._cmfs,
1270 self._A,
1271 use_practice_range=False,
1272 ),
1273 np.array([14.54143704, 10.88642877, 2.04915501]),
1274 atol=TOLERANCE_ABSOLUTE_TESTS,
1275 )
1277 np.testing.assert_allclose(
1278 sd_to_XYZ_ASTME308(
1279 reshape_sd(self._sd, SpectralShape(400, 700, 20)),
1280 self._cmfs,
1281 self._A,
1282 mi_20nm_interpolation_method=False,
1283 ),
1284 np.array([14.54242562, 10.88694088, 2.04919645]),
1285 atol=TOLERANCE_ABSOLUTE_TESTS,
1286 )
1288 np.testing.assert_allclose(
1289 sd_to_XYZ_ASTME308(
1290 reshape_sd(self._sd, SpectralShape(360, 820, 20)),
1291 self._cmfs,
1292 self._A,
1293 use_practice_range=False,
1294 mi_20nm_interpolation_method=False,
1295 ),
1296 np.array([14.50209515, 10.87231865, 2.04976312]),
1297 atol=TOLERANCE_ABSOLUTE_TESTS,
1298 )
1300 np.testing.assert_allclose(
1301 sd_to_XYZ_ASTME308(
1302 reshape_sd(self._sd, SpectralShape(400, 700, 20)),
1303 self._cmfs,
1304 self._A,
1305 use_practice_range=False,
1306 mi_20nm_interpolation_method=False,
1307 ),
1308 np.array([14.54272240, 10.88702210, 2.04918701]),
1309 atol=TOLERANCE_ABSOLUTE_TESTS,
1310 )
1312 np.testing.assert_allclose(
1313 sd_to_XYZ_ASTME308(
1314 reshape_sd(self._sd, SpectralShape(400, 700, 20)),
1315 self._cmfs,
1316 self._A,
1317 k=1,
1318 ),
1319 np.array([15.6891747040, 11.7458332427, 2.2109475945]),
1320 atol=TOLERANCE_ABSOLUTE_TESTS,
1321 )
1323 np.testing.assert_allclose(
1324 sd_to_XYZ_ASTME308(
1325 reshape_sd(self._sd, SpectralShape(401, 701, 20)),
1326 self._cmfs,
1327 self._A,
1328 ),
1329 np.array([14.5220164311, 10.8790959535, 2.0490905325]),
1330 atol=TOLERANCE_ABSOLUTE_TESTS,
1331 )
1333 def test_raise_exception_sd_to_XYZ_ASTME308(self) -> None:
1334 """
1335 Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308`
1336 definition raised exception.
1337 """
1339 pytest.raises(
1340 ValueError,
1341 sd_to_XYZ_ASTME308,
1342 reshape_sd(self._sd, SpectralShape(360, 820, 2)),
1343 )
1346class TestSd_to_XYZ:
1347 """
1348 Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ` definition
1349 unit tests methods.
1350 """
1352 def setup_method(self) -> None:
1353 """Initialise the common tests attributes."""
1355 self._cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
1356 self._A = sd_CIE_standard_illuminant_A(self._cmfs.shape)
1357 self._sd = reshape_sd(SD_SAMPLE, self._cmfs.shape)
1359 def test_sd_to_XYZ(self) -> None:
1360 """
1361 Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ`
1362 definition.
1363 """
1365 # Testing that the cache returns a copy of the data.
1366 XYZ = sd_to_XYZ(self._sd, self._cmfs, self._A)
1368 np.testing.assert_allclose(
1369 XYZ,
1370 np.array([14.46372680, 10.85832950, 2.04663200]),
1371 atol=TOLERANCE_ABSOLUTE_TESTS,
1372 )
1374 XYZ *= 10
1376 np.testing.assert_allclose(
1377 sd_to_XYZ(self._sd, self._cmfs, self._A),
1378 np.array([14.46372680, 10.85832950, 2.04663200]),
1379 atol=TOLERANCE_ABSOLUTE_TESTS,
1380 )
1382 np.testing.assert_allclose(
1383 sd_to_XYZ(
1384 self._sd,
1385 self._cmfs,
1386 self._A,
1387 method="Integration",
1388 shape=SpectralShape(400, 700, 20),
1389 ),
1390 np.array([14.52005467, 10.88000966, 2.03888717]),
1391 atol=TOLERANCE_ABSOLUTE_TESTS,
1392 )
1394 np.testing.assert_allclose(
1395 sd_to_XYZ(
1396 sds_and_msds_to_msds(SDS_COLOURCHECKERS["babel_average"].values()),
1397 ),
1398 np.array(
1399 [
1400 [12.06344619, 10.33615020, 6.25100082],
1401 [40.27284790, 35.29615976, 23.01540616],
1402 [18.09306423, 18.50797244, 31.67770084],
1403 [11.16523793, 13.25122619, 6.36666038],
1404 [25.83420330, 23.32560917, 40.31232440],
1405 [31.64244829, 41.64149301, 40.76605171],
1406 [40.89999141, 31.22508083, 5.82002195],
1407 [13.60742370, 11.51655221, 35.39885724],
1408 [30.69032237, 19.87942885, 12.46562439],
1409 [8.93362430, 6.46162368, 13.07228804],
1410 [35.66114785, 44.08411919, 10.28724123],
1411 [49.24169657, 43.46327044, 7.13506647],
1412 [7.81888273, 5.89050934, 25.75773837],
1413 [15.18817149, 22.93975934, 8.94663877],
1414 [22.23187260, 12.73987267, 4.62599881],
1415 [60.68157753, 60.55780947, 8.43465082],
1416 [32.27071879, 20.22049576, 28.85354643],
1417 [14.49902213, 19.10377565, 35.60283004],
1418 [90.78293221, 91.26928233, 87.29499532],
1419 [58.53195263, 58.84257471, 58.34877604],
1420 [35.77113141, 35.94371650, 35.85712527],
1421 [18.98962045, 19.11651714, 19.15115933],
1422 [8.87518188, 8.93947283, 9.06486638],
1423 [3.21099614, 3.20073667, 3.25495104],
1424 ]
1425 ),
1426 atol=TOLERANCE_ABSOLUTE_TESTS,
1427 )
1430class TestMsds_to_XYZ_integration:
1431 """
1432 Define :func:`colour.colorimetry.tristimulus_values.\
1433msds_to_XYZ_integration` definition unit tests methods.
1434 """
1436 def test_msds_to_XYZ_integration(self) -> None:
1437 """
1438 Test :func:`colour.colorimetry.tristimulus_values.\
1439msds_to_XYZ_integration` definition.
1440 """
1442 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
1443 np.testing.assert_allclose(
1444 msds_to_XYZ_integration(MSDS_TWO, cmfs, SDS_ILLUMINANTS["D65"]),
1445 TVS_D65_INTEGRATION_MSDS,
1446 atol=TOLERANCE_ABSOLUTE_TESTS,
1447 )
1449 np.testing.assert_allclose(
1450 msds_to_XYZ_integration(
1451 DATA_TWO,
1452 cmfs,
1453 SDS_ILLUMINANTS["D65"],
1454 shape=SpectralShape(400, 700, 60),
1455 ),
1456 TVS_D65_ARRAY_INTEGRATION,
1457 atol=TOLERANCE_ABSOLUTE_TESTS,
1458 )
1460 np.testing.assert_allclose(
1461 msds_to_XYZ_integration(
1462 DATA_TWO,
1463 cmfs,
1464 SDS_ILLUMINANTS["D65"],
1465 1,
1466 shape=SpectralShape(400, 700, 60),
1467 ),
1468 TVS_D65_ARRAY_K1_INTEGRATION,
1469 atol=TOLERANCE_ABSOLUTE_TESTS,
1470 )
1472 def test_domain_range_scale_msds_to_XYZ_integration(self) -> None:
1473 """
1474 Test :func:`colour.colorimetry.tristimulus_values.\
1475msds_to_XYZ_integration` definition domain and range scale support.
1476 """
1478 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
1479 d_r = (("reference", 1), ("1", 0.01), ("100", 1))
1480 for scale, factor in d_r:
1481 with domain_range_scale(scale):
1482 np.testing.assert_allclose(
1483 msds_to_XYZ_integration(
1484 DATA_TWO,
1485 cmfs,
1486 SDS_ILLUMINANTS["D65"],
1487 shape=SpectralShape(400, 700, 60),
1488 ),
1489 TVS_D65_ARRAY_INTEGRATION * factor,
1490 atol=TOLERANCE_ABSOLUTE_TESTS,
1491 )
1494class TestMsds_to_XYZ_ASTME308:
1495 """
1496 Define :func:`colour.colorimetry.tristimulus_values.msds_to_XYZ_ASTME308`
1497 definition unit tests methods.
1498 """
1500 def test_msds_to_XYZ_ASTME308(self) -> None:
1501 """
1502 Test :func:`colour.colorimetry.tristimulus_values.\
1503msds_to_XYZ_ASTME308` definition.
1504 """
1506 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
1507 msds = reshape_msds(MSDS_TWO, SpectralShape(400, 700, 20))
1508 np.testing.assert_allclose(
1509 msds_to_XYZ_ASTME308(msds, cmfs, SDS_ILLUMINANTS["D65"]),
1510 TVS_D65_ASTME308_MSDS,
1511 atol=TOLERANCE_ABSOLUTE_TESTS,
1512 )
1514 np.testing.assert_allclose(
1515 msds_to_XYZ_ASTME308(msds, cmfs, SDS_ILLUMINANTS["D65"], k=1),
1516 TVS_D65_ASTME308_K1_MSDS,
1517 atol=TOLERANCE_ABSOLUTE_TESTS,
1518 )
1520 def test_domain_range_scale_msds_to_XYZ_ASTME308(self) -> None:
1521 """
1522 Test :func:`colour.colorimetry.tristimulus_values.\
1523msds_to_XYZ_ASTME308` definition domain and range scale support.
1524 """
1526 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
1527 d_r = (("reference", 1), ("1", 0.01), ("100", 1))
1528 for scale, factor in d_r:
1529 with domain_range_scale(scale):
1530 np.testing.assert_allclose(
1531 msds_to_XYZ_ASTME308(
1532 reshape_msds(MSDS_TWO, SpectralShape(400, 700, 20)),
1533 cmfs,
1534 SDS_ILLUMINANTS["D65"],
1535 ),
1536 TVS_D65_ASTME308_MSDS * factor,
1537 atol=TOLERANCE_ABSOLUTE_TESTS,
1538 )
1540 def test_raise_exception_msds_to_XYZ_ASTME308(self) -> None:
1541 """
1542 Test :func:`colour.colorimetry.tristimulus_values.\
1543msds_to_XYZ_ASTME308` definition raise exception.
1544 """
1546 pytest.raises(TypeError, msds_to_XYZ_ASTME308, DATA_TWO)
1549class TestAbsoluteIntegrationToXYZ:
1550 """
1551 Test the absolute integration to tristimulus values for :math:`k = 683`
1552 """
1554 def test_absolute_integration_to_TVS_1nm(self) -> None:
1555 """
1556 Test the absolute, i.e., user specified :math:`k` value, integration to
1557 tristimulus values for 1nm interval.
1558 """
1560 sd = sd_zeros(SpectralShape(380, 780, 1))
1562 k = 683
1563 sd[555] = 1 # 1 watt at 555nm, 0 watt everywhere else.
1565 methods = [
1566 sd_to_XYZ,
1567 sd_to_XYZ_ASTME308,
1568 sd_to_XYZ_integration,
1569 msds_to_XYZ,
1570 msds_to_XYZ_ASTME308,
1571 msds_to_XYZ_integration,
1572 ]
1574 # Test single spectral distribution integration methods.
1575 for method in methods[0:3]:
1576 XYZ = method(sd, k=k)
1577 XYZ = np.reshape(XYZ, 3) if len(XYZ.shape) > 1 else XYZ
1578 (
1579 np.testing.assert_allclose(XYZ[1], k, atol=5e-5),
1580 (
1581 "1 watt @ 555nm should be approximately 683 candela."
1582 f" Failed method: {method}"
1583 ),
1584 )
1586 # Test multi-spectral distributions integration methods.
1587 msds = MultiSpectralDistributions(sd)
1588 for method in methods[3:6]:
1589 XYZ: np.ndarray = method(msds, k=k)
1590 if len(XYZ.shape) > 1:
1591 XYZ = np.reshape(XYZ, 3)
1592 (
1593 np.testing.assert_allclose(XYZ[1], k, atol=5e-5),
1594 (
1595 "1 watt @ 555nm should be approximately 683 candela."
1596 f" Failed method: {method}"
1597 ),
1598 )
1600 def test_absolute_integration_to_TVS_5nm(self) -> None:
1601 """
1602 Test the absolute, i.e., user specified :math:`k` value, integration to
1603 tristimulus values for 5nm interval by ensuring that the *Riemann Sum*
1604 accounts for the :math:`\\delta w` term.
1605 """
1607 sd = sd_zeros(SpectralShape(380, 780, 5), interpolator=SpragueInterpolator)
1609 # 1 watt at 555nm, 0 watt everywhere else.
1610 # For 5nm average sampling, this corresponds to 0.2 watt at 555nm.
1611 k = 683
1612 sd[555] = 0.2
1614 methods = [
1615 sd_to_XYZ,
1616 sd_to_XYZ_ASTME308,
1617 sd_to_XYZ_integration,
1618 msds_to_XYZ,
1619 msds_to_XYZ_ASTME308,
1620 msds_to_XYZ_integration,
1621 ]
1623 # Test single spectral distribution integration methods.
1624 for method in methods[0:3]:
1625 XYZ: np.ndarray = method(sd, k=k)
1626 XYZ = np.reshape(XYZ, 3) if len(XYZ.shape) > 1 else XYZ
1627 (
1628 np.testing.assert_allclose(XYZ[1], k, atol=5e-2),
1629 (
1630 "1 watt @ 555nm should be approximately 683 candela. "
1631 f"Failed method: {method}"
1632 ),
1633 )
1635 # Test multi-spectral distributions integration methods.
1636 msds = MultiSpectralDistributions(sd)
1637 for method in methods[3:6]:
1638 XYZ: np.ndarray = method(msds, k=k)
1639 if len(XYZ.shape) > 1:
1640 XYZ = np.reshape(XYZ, 3)
1641 (
1642 np.testing.assert_allclose(XYZ[1], k, atol=5e-2),
1643 (
1644 "1 watt @ 555nm should be approximately 683 candela."
1645 f"Failed method: {method}"
1646 ),
1647 )
1650class TestWavelength_to_XYZ:
1651 """
1652 Define :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ`
1653 definition unit tests methods.
1654 """
1656 def test_wavelength_to_XYZ(self) -> None:
1657 """
1658 Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ`
1659 definition.
1660 """
1662 np.testing.assert_allclose(
1663 wavelength_to_XYZ(480, MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]),
1664 np.array([0.09564, 0.13902, 0.81295]),
1665 atol=TOLERANCE_ABSOLUTE_TESTS,
1666 )
1668 np.testing.assert_allclose(
1669 wavelength_to_XYZ(480, MSDS_CMFS["CIE 2015 2 Degree Standard Observer"]),
1670 np.array([0.08182895, 0.17880480, 0.75523790]),
1671 atol=TOLERANCE_ABSOLUTE_TESTS,
1672 )
1674 np.testing.assert_allclose(
1675 wavelength_to_XYZ(641.5, MSDS_CMFS["CIE 2015 2 Degree Standard Observer"]),
1676 np.array([0.44575583, 0.18184213, 0.00000000]),
1677 atol=TOLERANCE_ABSOLUTE_TESTS,
1678 )
1680 def test_raise_exception_wavelength_to_XYZ(self) -> None:
1681 """
1682 Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ`
1683 definition raised exception.
1684 """
1686 pytest.raises(ValueError, wavelength_to_XYZ, 1)
1688 pytest.raises(ValueError, wavelength_to_XYZ, 1000)
1690 def test_n_dimensional_wavelength_to_XYZ(self) -> None:
1691 """
1692 Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ`
1693 definition n-dimensional arrays support.
1694 """
1696 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
1697 wl = 480
1698 XYZ = wavelength_to_XYZ(wl, cmfs)
1700 wl = np.tile(wl, 6)
1701 XYZ = np.tile(XYZ, (6, 1))
1702 np.testing.assert_allclose(
1703 wavelength_to_XYZ(wl, cmfs), XYZ, atol=TOLERANCE_ABSOLUTE_TESTS
1704 )
1706 wl = np.reshape(wl, (2, 3))
1707 XYZ = np.reshape(XYZ, (2, 3, 3))
1708 np.testing.assert_allclose(
1709 wavelength_to_XYZ(wl, cmfs), XYZ, atol=TOLERANCE_ABSOLUTE_TESTS
1710 )
1712 wl = np.reshape(wl, (2, 3, 1))
1713 XYZ = np.reshape(XYZ, (2, 3, 1, 3))
1714 np.testing.assert_allclose(
1715 wavelength_to_XYZ(wl, cmfs), XYZ, atol=TOLERANCE_ABSOLUTE_TESTS
1716 )