Coverage for colour/colorimetry/tests/test_tristimulus_values.py: 100%

246 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-15 19:01 +1300

1""" 

2Define the unit tests for the :mod:`colour.colorimetry.tristimulus_values` 

3module. 

4 

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""" 

11 

12from __future__ import annotations 

13 

14import typing 

15 

16import numpy as np 

17import pytest 

18 

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 

47 

48if typing.TYPE_CHECKING: 

49 from colour.hints import NDArrayFloat 

50 

51from colour.utilities import domain_range_scale 

52 

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" 

59 

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] 

88 

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) 

192 

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) 

206 

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) 

220 

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) 

273 

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) 

302 

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) 

331 

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) 

360 

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) 

381 

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) 

486 

487MSDS_TWO: MultiSpectralDistributions = MultiSpectralDistributions( 

488 np.transpose(np.reshape(DATA_TWO, [-1, 6])), 

489 SpectralShape(400, 700, 60).wavelengths, 

490) 

491 

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) 

508 

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) 

529 

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) 

550 

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) 

567 

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) 

584 

585 

586class TestHandleSpectralArguments: 

587 """ 

588 Define :func:`colour.colorimetry.tristimulus_values.\ 

589handle_spectral_arguments` definition unit tests methods. 

590 """ 

591 

592 def test_handle_spectral_arguments(self) -> None: 

593 """ 

594 Test :func:`colour.colorimetry.tristimulus_values.\ 

595handle_spectral_arguments` definition. 

596 """ 

597 

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"]) 

601 

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 

606 

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 

616 

617 

618class TestLagrangeCoefficientsASTME2022: 

619 """ 

620 Define :func:`colour.colorimetry.tristimulus_values.\ 

621lagrange_coefficients_ASTME2022` definition unit tests methods. 

622 """ 

623 

624 def test_lagrange_coefficients_ASTME2022(self) -> None: 

625 """ 

626 Test :func:`colour.colorimetry.tristimulus_values.\ 

627lagrange_coefficients_ASTME2022` definition. 

628 """ 

629 

630 np.testing.assert_allclose( 

631 lagrange_coefficients_ASTME2022(10, "inner"), 

632 LAGRANGE_COEFFICIENTS_A, 

633 atol=TOLERANCE_ABSOLUTE_TESTS, 

634 ) 

635 

636 np.testing.assert_allclose( 

637 lagrange_coefficients_ASTME2022(10, "boundary"), 

638 LAGRANGE_COEFFICIENTS_B, 

639 atol=TOLERANCE_ABSOLUTE_TESTS, 

640 ) 

641 

642 # Testing that the cache returns a copy of the data. 

643 lagrange_coefficients = lagrange_coefficients_ASTME2022(10) 

644 

645 np.testing.assert_allclose( 

646 lagrange_coefficients, 

647 LAGRANGE_COEFFICIENTS_A, 

648 atol=TOLERANCE_ABSOLUTE_TESTS, 

649 ) 

650 

651 lagrange_coefficients *= 10 

652 

653 np.testing.assert_allclose( 

654 lagrange_coefficients_ASTME2022(10), 

655 LAGRANGE_COEFFICIENTS_A, 

656 atol=TOLERANCE_ABSOLUTE_TESTS, 

657 ) 

658 

659 

660class TestTristimulusWeightingFactorsASTME2022: 

661 """ 

662 Define :func:`colour.colorimetry.tristimulus_values.\ 

663tristimulus_weighting_factors_ASTME2022` definition unit tests methods. 

664 """ 

665 

666 def test_tristimulus_weighting_factors_ASTME2022(self) -> None: 

667 """ 

668 Test :func:`colour.colorimetry.tristimulus_values.\ 

669tristimulus_weighting_factors_ASTME2022` definition. 

670 

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`. 

676 

677 References 

678 ---------- 

679 :cite:`ASTMInternational2015b` 

680 """ 

681 

682 cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"] 

683 A = sd_CIE_standard_illuminant_A(cmfs.shape) 

684 

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) 

689 

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) 

694 

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 ) 

707 

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 ) 

714 

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 ) 

725 

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 ) 

736 

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 """ 

742 

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) 

748 

749 pytest.raises( 

750 ValueError, 

751 tristimulus_weighting_factors_ASTME2022, 

752 cmfs_1, 

753 A_2, 

754 shape, 

755 ) 

756 

757 pytest.raises( 

758 ValueError, 

759 tristimulus_weighting_factors_ASTME2022, 

760 cmfs_2, 

761 A_1, 

762 shape, 

763 ) 

764 

765 

766class TestAdjustTristimulusWeightingFactorsASTME308: 

767 """ 

768 Define :func:`colour.colorimetry.tristimulus_values.\ 

769adjust_tristimulus_weighting_factors_ASTME308` definition unit tests methods. 

770 """ 

771 

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 """ 

777 

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 ) 

787 

788 

789class TestSd_to_XYZ_integration: 

790 """ 

791 Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_integration` 

792 definition unit tests methods. 

793 """ 

794 

795 def test_sd_to_XYZ_integration(self) -> None: 

796 """ 

797 Test :func:`colour.colorimetry.tristimulus_values.\ 

798sd_to_XYZ_integration` definition. 

799 """ 

800 

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 ) 

807 

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 ) 

818 

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 ) 

825 

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 ) 

831 

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 ) 

837 

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 ) 

848 

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 """ 

854 

855 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] 

856 XYZ = sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]) 

857 

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 ) 

866 

867 

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 """ 

874 

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 """ 

881 

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 ) 

890 

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 ) 

899 

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 ) 

907 

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 ) 

917 

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 ) 

927 

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 ) 

937 

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 ) 

948 

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 """ 

955 

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 ) 

960 

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 ) 

971 

972 

973class TestSd_to_XYZ_ASTME308: 

974 """ 

975 Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308` 

976 definition unit tests methods. 

977 """ 

978 

979 def setup_method(self) -> None: 

980 """Initialise the common tests attributes.""" 

981 

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) 

985 

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 """ 

991 

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 ) 

999 

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 ) 

1010 

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 ) 

1020 

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 ) 

1031 

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 ) 

1042 

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 """ 

1048 

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 ) 

1058 

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 ) 

1069 

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 ) 

1080 

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 ) 

1090 

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 ) 

1101 

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 ) 

1112 

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 ) 

1124 

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 ) 

1136 

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 ) 

1147 

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 """ 

1153 

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 ) 

1163 

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 ) 

1174 

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 ) 

1184 

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 ) 

1195 

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 ) 

1206 

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 ) 

1217 

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 """ 

1223 

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 ) 

1233 

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 ) 

1244 

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 ) 

1255 

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 ) 

1265 

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 ) 

1276 

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 ) 

1287 

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 ) 

1299 

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 ) 

1311 

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 ) 

1322 

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 ) 

1332 

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 """ 

1338 

1339 pytest.raises( 

1340 ValueError, 

1341 sd_to_XYZ_ASTME308, 

1342 reshape_sd(self._sd, SpectralShape(360, 820, 2)), 

1343 ) 

1344 

1345 

1346class TestSd_to_XYZ: 

1347 """ 

1348 Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ` definition 

1349 unit tests methods. 

1350 """ 

1351 

1352 def setup_method(self) -> None: 

1353 """Initialise the common tests attributes.""" 

1354 

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) 

1358 

1359 def test_sd_to_XYZ(self) -> None: 

1360 """ 

1361 Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ` 

1362 definition. 

1363 """ 

1364 

1365 # Testing that the cache returns a copy of the data. 

1366 XYZ = sd_to_XYZ(self._sd, self._cmfs, self._A) 

1367 

1368 np.testing.assert_allclose( 

1369 XYZ, 

1370 np.array([14.46372680, 10.85832950, 2.04663200]), 

1371 atol=TOLERANCE_ABSOLUTE_TESTS, 

1372 ) 

1373 

1374 XYZ *= 10 

1375 

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 ) 

1381 

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 ) 

1393 

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 ) 

1428 

1429 

1430class TestMsds_to_XYZ_integration: 

1431 """ 

1432 Define :func:`colour.colorimetry.tristimulus_values.\ 

1433msds_to_XYZ_integration` definition unit tests methods. 

1434 """ 

1435 

1436 def test_msds_to_XYZ_integration(self) -> None: 

1437 """ 

1438 Test :func:`colour.colorimetry.tristimulus_values.\ 

1439msds_to_XYZ_integration` definition. 

1440 """ 

1441 

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 ) 

1448 

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 ) 

1459 

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 ) 

1471 

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 """ 

1477 

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 ) 

1492 

1493 

1494class TestMsds_to_XYZ_ASTME308: 

1495 """ 

1496 Define :func:`colour.colorimetry.tristimulus_values.msds_to_XYZ_ASTME308` 

1497 definition unit tests methods. 

1498 """ 

1499 

1500 def test_msds_to_XYZ_ASTME308(self) -> None: 

1501 """ 

1502 Test :func:`colour.colorimetry.tristimulus_values.\ 

1503msds_to_XYZ_ASTME308` definition. 

1504 """ 

1505 

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 ) 

1513 

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 ) 

1519 

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 """ 

1525 

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 ) 

1539 

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 """ 

1545 

1546 pytest.raises(TypeError, msds_to_XYZ_ASTME308, DATA_TWO) 

1547 

1548 

1549class TestAbsoluteIntegrationToXYZ: 

1550 """ 

1551 Test the absolute integration to tristimulus values for :math:`k = 683` 

1552 """ 

1553 

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 """ 

1559 

1560 sd = sd_zeros(SpectralShape(380, 780, 1)) 

1561 

1562 k = 683 

1563 sd[555] = 1 # 1 watt at 555nm, 0 watt everywhere else. 

1564 

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 ] 

1573 

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 ) 

1585 

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 ) 

1599 

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 """ 

1606 

1607 sd = sd_zeros(SpectralShape(380, 780, 5), interpolator=SpragueInterpolator) 

1608 

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 

1613 

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 ] 

1622 

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 ) 

1634 

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 ) 

1648 

1649 

1650class TestWavelength_to_XYZ: 

1651 """ 

1652 Define :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` 

1653 definition unit tests methods. 

1654 """ 

1655 

1656 def test_wavelength_to_XYZ(self) -> None: 

1657 """ 

1658 Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` 

1659 definition. 

1660 """ 

1661 

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 ) 

1667 

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 ) 

1673 

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 ) 

1679 

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 """ 

1685 

1686 pytest.raises(ValueError, wavelength_to_XYZ, 1) 

1687 

1688 pytest.raises(ValueError, wavelength_to_XYZ, 1000) 

1689 

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 """ 

1695 

1696 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] 

1697 wl = 480 

1698 XYZ = wavelength_to_XYZ(wl, cmfs) 

1699 

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 ) 

1705 

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 ) 

1711 

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 )