Coverage for colorimetry/tests/test_dominant.py: 100%

204 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 22:49 +1300

1"""Define the unit tests for the :mod:`colour.colorimetry.dominant` module.""" 

2 

3from __future__ import annotations 

4 

5from itertools import product 

6 

7import numpy as np 

8 

9from colour.colorimetry import ( 

10 CCS_ILLUMINANTS, 

11 MSDS_CMFS, 

12 colorimetric_purity, 

13 complementary_wavelength, 

14 dominant_wavelength, 

15 excitation_purity, 

16) 

17from colour.colorimetry.dominant import closest_spectral_locus_wavelength 

18from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

19from colour.models import XYZ_to_xy 

20from colour.utilities import ignore_numpy_errors, is_scipy_installed 

21 

22__author__ = "Colour Developers" 

23__copyright__ = "Copyright 2013 Colour Developers" 

24__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

25__maintainer__ = "Colour Developers" 

26__email__ = "colour-developers@colour-science.org" 

27__status__ = "Production" 

28 

29__all__ = [ 

30 "TestClosestSpectralLocusWavelength", 

31 "TestDominantWavelength", 

32 "TestComplementaryWavelength", 

33 "TestExcitationPurity", 

34 "TestColorimetricPurity", 

35] 

36 

37 

38class TestClosestSpectralLocusWavelength: 

39 """ 

40 Define :func:`colour.colorimetry.dominant.\ 

41closest_spectral_locus_wavelength` definition unit tests methods. 

42 """ 

43 

44 def setup_method(self) -> None: 

45 """Initialise the common tests attributes.""" 

46 

47 self._xy_s = XYZ_to_xy(MSDS_CMFS["CIE 1931 2 Degree Standard Observer"].values) 

48 

49 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] 

50 

51 def test_closest_spectral_locus_wavelength(self) -> None: 

52 """ 

53 Test :func:`colour.colorimetry.dominant.\ 

54closest_spectral_locus_wavelength` definition. 

55 """ 

56 

57 if not is_scipy_installed(): # pragma: no cover 

58 return 

59 

60 xy = np.array([0.54369557, 0.32107944]) 

61 xy_n = self._xy_D65 

62 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) 

63 

64 assert i_wl == np.array(256) 

65 np.testing.assert_allclose( 

66 xy_wl, 

67 np.array([0.68354746, 0.31628409]), 

68 atol=TOLERANCE_ABSOLUTE_TESTS, 

69 ) 

70 

71 xy = np.array([0.37605506, 0.24452225]) 

72 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) 

73 

74 assert i_wl == np.array(248) 

75 np.testing.assert_allclose( 

76 xy_wl, 

77 np.array([0.45723147, 0.13628148]), 

78 atol=TOLERANCE_ABSOLUTE_TESTS, 

79 ) 

80 

81 def test_n_dimensional_closest_spectral_locus_wavelength(self) -> None: 

82 """ 

83 Test :func:`colour.colorimetry.dominant.\ 

84closest_spectral_locus_wavelength` definition n-dimensional arrays support. 

85 """ 

86 

87 if not is_scipy_installed(): # pragma: no cover 

88 return 

89 

90 xy = np.array([0.54369557, 0.32107944]) 

91 xy_n = self._xy_D65 

92 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) 

93 i_wl_r, xy_wl_r = np.array(256), np.array([0.68354746, 0.31628409]) 

94 np.testing.assert_allclose(i_wl, i_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

95 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

96 

97 xy = np.tile(xy, (6, 1)) 

98 xy_n = np.tile(xy_n, (6, 1)) 

99 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) 

100 i_wl_r = np.tile(i_wl_r, 6) 

101 xy_wl_r = np.tile(xy_wl_r, (6, 1)) 

102 np.testing.assert_allclose(i_wl, i_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

103 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

104 

105 xy = np.reshape(xy, (2, 3, 2)) 

106 xy_n = np.reshape(xy_n, (2, 3, 2)) 

107 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) 

108 i_wl_r = np.reshape(i_wl_r, (2, 3)) 

109 xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2)) 

110 np.testing.assert_allclose(i_wl, i_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

111 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

112 

113 @ignore_numpy_errors 

114 def test_nan_closest_spectral_locus_wavelength(self) -> None: 

115 """ 

116 Test :func:`colour.colorimetry.dominant.\ 

117closest_spectral_locus_wavelength` definition nan support. 

118 """ 

119 

120 if not is_scipy_installed(): # pragma: no cover 

121 return 

122 

123 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] 

124 cases = np.array(list(set(product(cases, repeat=2)))) 

125 for case in cases: 

126 closest_spectral_locus_wavelength(case, case, self._xy_s) 

127 

128 

129class TestDominantWavelength: 

130 """ 

131 Define :func:`colour.colorimetry.dominant.dominant_wavelength` definition 

132 unit tests methods. 

133 """ 

134 

135 def setup_method(self) -> None: 

136 """Initialise the common tests attributes.""" 

137 

138 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] 

139 

140 def test_dominant_wavelength(self) -> None: 

141 """ 

142 Test :func:`colour.colorimetry.dominant.dominant_wavelength` 

143 definition. 

144 """ 

145 

146 if not is_scipy_installed(): # pragma: no cover 

147 return 

148 

149 xy = np.array([0.54369557, 0.32107944]) 

150 xy_n = self._xy_D65 

151 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) 

152 

153 assert wl == np.array(616.0) 

154 np.testing.assert_allclose( 

155 xy_wl, 

156 np.array([0.68354746, 0.31628409]), 

157 atol=TOLERANCE_ABSOLUTE_TESTS, 

158 ) 

159 np.testing.assert_allclose( 

160 xy_cwl, 

161 np.array([0.68354746, 0.31628409]), 

162 atol=TOLERANCE_ABSOLUTE_TESTS, 

163 ) 

164 

165 xy = np.array([0.37605506, 0.24452225]) 

166 i_wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) 

167 

168 assert i_wl == np.array(-509.0) 

169 np.testing.assert_allclose( 

170 xy_wl, 

171 np.array([0.45723147, 0.13628148]), 

172 atol=TOLERANCE_ABSOLUTE_TESTS, 

173 ) 

174 np.testing.assert_allclose( 

175 xy_cwl, 

176 np.array([0.01040962, 0.73207453]), 

177 atol=TOLERANCE_ABSOLUTE_TESTS, 

178 ) 

179 

180 def test_n_dimensional_dominant_wavelength(self) -> None: 

181 """ 

182 Test :func:`colour.colorimetry.dominant.dominant_wavelength` 

183 definition n-dimensional arrays support. 

184 """ 

185 

186 if not is_scipy_installed(): # pragma: no cover 

187 return 

188 

189 xy = np.array([0.54369557, 0.32107944]) 

190 xy_n = self._xy_D65 

191 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) 

192 wl_r, xy_wl_r, xy_cwl_r = ( 

193 np.array(616.0), 

194 np.array([0.68354746, 0.31628409]), 

195 np.array([0.68354746, 0.31628409]), 

196 ) 

197 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

198 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

199 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

200 

201 xy = np.tile(xy, (6, 1)) 

202 xy_n = np.tile(xy_n, (6, 1)) 

203 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) 

204 wl_r = np.tile(wl_r, 6) 

205 xy_wl_r = np.tile(xy_wl_r, (6, 1)) 

206 xy_cwl_r = np.tile(xy_cwl_r, (6, 1)) 

207 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

208 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

209 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

210 

211 xy = np.reshape(xy, (2, 3, 2)) 

212 xy_n = np.reshape(xy_n, (2, 3, 2)) 

213 wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) 

214 wl_r = np.reshape(wl_r, (2, 3)) 

215 xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2)) 

216 xy_cwl_r = np.reshape(xy_cwl_r, (2, 3, 2)) 

217 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

218 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

219 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

220 

221 @ignore_numpy_errors 

222 def test_nan_dominant_wavelength(self) -> None: 

223 """ 

224 Test :func:`colour.colorimetry.dominant.dominant_wavelength` 

225 definition nan support. 

226 """ 

227 

228 if not is_scipy_installed(): # pragma: no cover 

229 return 

230 

231 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] 

232 cases = np.array(list(set(product(cases, repeat=2)))) 

233 for case in cases: 

234 dominant_wavelength(case, case) 

235 

236 

237class TestComplementaryWavelength: 

238 """ 

239 Define :func:`colour.colorimetry.dominant.complementary_wavelength` 

240 definition unit tests methods. 

241 """ 

242 

243 def setup_method(self) -> None: 

244 """Initialise the common tests attributes.""" 

245 

246 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] 

247 

248 def test_complementary_wavelength(self) -> None: 

249 """ 

250 Test :func:`colour.colorimetry.dominant.complementary_wavelength` 

251 definition. 

252 """ 

253 

254 if not is_scipy_installed(): # pragma: no cover 

255 return 

256 

257 xy = np.array([0.54369557, 0.32107944]) 

258 xy_n = self._xy_D65 

259 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) 

260 

261 assert wl == np.array(492.0) 

262 np.testing.assert_allclose( 

263 xy_wl, 

264 np.array([0.03647950, 0.33847127]), 

265 atol=TOLERANCE_ABSOLUTE_TESTS, 

266 ) 

267 np.testing.assert_allclose( 

268 xy_cwl, 

269 np.array([0.03647950, 0.33847127]), 

270 atol=TOLERANCE_ABSOLUTE_TESTS, 

271 ) 

272 

273 xy = np.array([0.37605506, 0.24452225]) 

274 i_wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) 

275 

276 assert i_wl == np.array(509.0) 

277 np.testing.assert_allclose( 

278 xy_wl, 

279 np.array([0.01040962, 0.73207453]), 

280 atol=TOLERANCE_ABSOLUTE_TESTS, 

281 ) 

282 np.testing.assert_allclose( 

283 xy_cwl, 

284 np.array([0.01040962, 0.73207453]), 

285 atol=TOLERANCE_ABSOLUTE_TESTS, 

286 ) 

287 

288 def test_n_dimensional_complementary_wavelength(self) -> None: 

289 """ 

290 Test :func:`colour.colorimetry.dominant.complementary_wavelength` 

291 definition n-dimensional arrays support. 

292 """ 

293 

294 if not is_scipy_installed(): # pragma: no cover 

295 return 

296 

297 xy = np.array([0.54369557, 0.32107944]) 

298 xy_n = self._xy_D65 

299 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) 

300 wl_r, xy_wl_r, xy_cwl_r = ( 

301 np.array(492.0), 

302 np.array([0.03647950, 0.33847127]), 

303 np.array([0.03647950, 0.33847127]), 

304 ) 

305 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

306 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

307 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

308 

309 xy = np.tile(xy, (6, 1)) 

310 xy_n = np.tile(xy_n, (6, 1)) 

311 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) 

312 wl_r = np.tile(wl_r, 6) 

313 xy_wl_r = np.tile(xy_wl_r, (6, 1)) 

314 xy_cwl_r = np.tile(xy_cwl_r, (6, 1)) 

315 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

316 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

317 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

318 

319 xy = np.reshape(xy, (2, 3, 2)) 

320 xy_n = np.reshape(xy_n, (2, 3, 2)) 

321 wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) 

322 wl_r = np.reshape(wl_r, (2, 3)) 

323 xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2)) 

324 xy_cwl_r = np.reshape(xy_cwl_r, (2, 3, 2)) 

325 np.testing.assert_allclose(wl, wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

326 np.testing.assert_allclose(xy_wl, xy_wl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

327 np.testing.assert_allclose(xy_cwl, xy_cwl_r, atol=TOLERANCE_ABSOLUTE_TESTS) 

328 

329 @ignore_numpy_errors 

330 def test_nan_complementary_wavelength(self) -> None: 

331 """ 

332 Test :func:`colour.colorimetry.dominant.complementary_wavelength` 

333 definition nan support. 

334 """ 

335 

336 if not is_scipy_installed(): # pragma: no cover 

337 return 

338 

339 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] 

340 cases = np.array(list(set(product(cases, repeat=2)))) 

341 for case in cases: 

342 complementary_wavelength(case, case) 

343 

344 

345class TestExcitationPurity: 

346 """ 

347 Define :func:`colour.colorimetry.dominant.excitation_purity` definition 

348 unit tests methods. 

349 """ 

350 

351 def setup_method(self) -> None: 

352 """Initialise the common tests attributes.""" 

353 

354 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] 

355 

356 def test_excitation_purity(self) -> None: 

357 """Test :func:`colour.colorimetry.dominant.excitation_purity` definition.""" 

358 

359 if not is_scipy_installed(): # pragma: no cover 

360 return 

361 

362 xy = np.array([0.54369557, 0.32107944]) 

363 xy_n = self._xy_D65 

364 

365 np.testing.assert_allclose( 

366 excitation_purity(xy, xy_n), 

367 0.622885671878446, 

368 atol=TOLERANCE_ABSOLUTE_TESTS, 

369 ) 

370 

371 xy = np.array([0.37605506, 0.24452225]) 

372 np.testing.assert_allclose( 

373 excitation_purity(xy, xy_n), 

374 0.438347859215887, 

375 atol=TOLERANCE_ABSOLUTE_TESTS, 

376 ) 

377 

378 def test_n_dimensional_excitation_purity(self) -> None: 

379 """ 

380 Test :func:`colour.colorimetry.dominant.excitation_purity` definition 

381 n-dimensional arrays support. 

382 """ 

383 

384 if not is_scipy_installed(): # pragma: no cover 

385 return 

386 

387 xy = np.array([0.54369557, 0.32107944]) 

388 xy_n = self._xy_D65 

389 P_e = excitation_purity(xy, xy_n) 

390 

391 xy = np.tile(xy, (6, 1)) 

392 xy_n = np.tile(xy_n, (6, 1)) 

393 P_e = np.tile(P_e, 6) 

394 np.testing.assert_allclose( 

395 excitation_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS 

396 ) 

397 

398 xy = np.reshape(xy, (2, 3, 2)) 

399 xy_n = np.reshape(xy_n, (2, 3, 2)) 

400 P_e = np.reshape(P_e, (2, 3)) 

401 np.testing.assert_allclose( 

402 excitation_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS 

403 ) 

404 

405 @ignore_numpy_errors 

406 def test_nan_excitation_purity(self) -> None: 

407 """ 

408 Test :func:`colour.colorimetry.dominant.excitation_purity` definition 

409 nan support. 

410 """ 

411 

412 if not is_scipy_installed(): # pragma: no cover 

413 return 

414 

415 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] 

416 cases = np.array(list(set(product(cases, repeat=2)))) 

417 for case in cases: 

418 excitation_purity(case, case) 

419 

420 

421class TestColorimetricPurity: 

422 """ 

423 Define :func:`colour.colorimetry.dominant.colorimetric_purity` definition 

424 unit tests methods. 

425 """ 

426 

427 def setup_method(self) -> None: 

428 """Initialise the common tests attributes.""" 

429 

430 self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] 

431 

432 def test_colorimetric_purity(self) -> None: 

433 """ 

434 Test :func:`colour.colorimetry.dominant.colorimetric_purity` 

435 definition. 

436 """ 

437 

438 if not is_scipy_installed(): # pragma: no cover 

439 return 

440 

441 xy = np.array([0.54369557, 0.32107944]) 

442 xy_n = self._xy_D65 

443 

444 np.testing.assert_allclose( 

445 colorimetric_purity(xy, xy_n), 

446 0.613582813175483, 

447 atol=TOLERANCE_ABSOLUTE_TESTS, 

448 ) 

449 

450 xy = np.array([0.37605506, 0.24452225]) 

451 np.testing.assert_allclose( 

452 colorimetric_purity(xy, xy_n), 

453 0.244307811178847, 

454 atol=TOLERANCE_ABSOLUTE_TESTS, 

455 ) 

456 

457 def test_n_dimensional_colorimetric_purity(self) -> None: 

458 """ 

459 Test :func:`colour.colorimetry.dominant.colorimetric_purity` 

460 definition n-dimensional arrays support. 

461 """ 

462 

463 if not is_scipy_installed(): # pragma: no cover 

464 return 

465 

466 xy = np.array([0.54369557, 0.32107944]) 

467 xy_n = self._xy_D65 

468 P_e = colorimetric_purity(xy, xy_n) 

469 

470 xy = np.tile(xy, (6, 1)) 

471 xy_n = np.tile(xy_n, (6, 1)) 

472 P_e = np.tile(P_e, 6) 

473 np.testing.assert_allclose( 

474 colorimetric_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS 

475 ) 

476 

477 xy = np.reshape(xy, (2, 3, 2)) 

478 xy_n = np.reshape(xy_n, (2, 3, 2)) 

479 P_e = np.reshape(P_e, (2, 3)) 

480 np.testing.assert_allclose( 

481 colorimetric_purity(xy, xy_n), P_e, atol=TOLERANCE_ABSOLUTE_TESTS 

482 ) 

483 

484 @ignore_numpy_errors 

485 def test_nan_colorimetric_purity(self) -> None: 

486 """ 

487 Test :func:`colour.colorimetry.dominant.colorimetric_purity` 

488 definition nan support. 

489 """ 

490 

491 if not is_scipy_installed(): # pragma: no cover 

492 return 

493 

494 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] 

495 cases = np.array(list(set(product(cases, repeat=2)))) 

496 for case in cases: 

497 colorimetric_purity(case, case)