Coverage for colour/appearance/tests/test_hellwig2022.py: 100%

131 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.appearance.hellwig2022` module. 

3 

4References 

5---------- 

6- :cite:`Fairchild2022` : Fairchild, M. D., & Hellwig, L. (2022). Private 

7 Discussion with Mansencal, T. 

8""" 

9 

10from __future__ import annotations 

11 

12from itertools import product 

13 

14import numpy as np 

15import pytest 

16 

17from colour.appearance import ( 

18 VIEWING_CONDITIONS_HELLWIG2022, 

19 CAM_Specification_Hellwig2022, 

20 Hellwig2022_to_XYZ, 

21 InductionFactors_Hellwig2022, 

22 XYZ_to_Hellwig2022, 

23) 

24from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

25from colour.utilities import ( 

26 as_float_array, 

27 domain_range_scale, 

28 ignore_numpy_errors, 

29 tsplit, 

30) 

31 

32__author__ = "Colour Developers" 

33__copyright__ = "Copyright 2013 Colour Developers" 

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

35__maintainer__ = "Colour Developers" 

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

37__status__ = "Production" 

38 

39__all__ = [ 

40 "TestXYZ_to_Hellwig2022", 

41 "TestHellwig2022_to_XYZ", 

42] 

43 

44 

45class TestXYZ_to_Hellwig2022: 

46 """ 

47 Define :func:`colour.appearance.hellwig2022.XYZ_to_Hellwig2022` definition 

48 unit tests methods. 

49 """ 

50 

51 def test_XYZ_to_Hellwig2022(self) -> None: 

52 """ 

53 Test :func:`colour.appearance.hellwig2022.XYZ_to_Hellwig2022` 

54 definition. 

55 """ 

56 

57 XYZ = np.array([19.01, 20.00, 21.78]) 

58 XYZ_w = np.array([95.05, 100.00, 108.88]) 

59 L_A = 318.31 

60 Y_b = 20 

61 surround = VIEWING_CONDITIONS_HELLWIG2022["Average"] 

62 np.testing.assert_allclose( 

63 XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround), 

64 np.array( 

65 [ 

66 41.731, 

67 0.026, 

68 217.068, 

69 0.061, 

70 55.852, 

71 0.034, 

72 275.59498615, 

73 np.nan, 

74 41.88027828, 

75 56.05183586, 

76 ] 

77 ), 

78 atol=0.01, 

79 ) 

80 

81 XYZ = np.array([57.06, 43.06, 31.96]) 

82 L_A = 31.83 

83 np.testing.assert_allclose( 

84 XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround), 

85 np.array( 

86 [ 

87 65.428, 

88 31.330, 

89 17.487, 

90 47.200, 

91 64.077, 

92 30.245, 

93 398.03047943, 

94 np.nan, 

95 70.50187436, 

96 69.04574688, 

97 ] 

98 ), 

99 atol=0.01, 

100 ) 

101 

102 XYZ = np.array([3.53, 6.56, 2.14]) 

103 XYZ_w = np.array([109.85, 100, 35.58]) 

104 L_A = 318.31 

105 np.testing.assert_allclose( 

106 XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround), 

107 np.array( 

108 [ 

109 21.361, 

110 30.603, 

111 178.867, 

112 141.223, 

113 28.590, 

114 40.376, 

115 223.01823806, 

116 np.nan, 

117 29.35191711, 

118 39.28664523, 

119 ] 

120 ), 

121 atol=0.01, 

122 ) 

123 

124 XYZ = np.array([19.01, 20.00, 21.78]) 

125 XYZ_w = np.array([109.85, 100.00, 35.58]) 

126 L_A = 31.38 

127 np.testing.assert_allclose( 

128 XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround), 

129 np.array( 

130 [ 

131 41.064050542871215, 

132 31.939561618552826, 

133 259.034056616436715, 

134 76.668720573462167, 

135 40.196783565499423, 

136 30.818359671352116, 

137 311.329371306428470, 

138 np.nan, 

139 49.676917719967385, 

140 48.627748198047854, 

141 ] 

142 ), 

143 atol=0.01, 

144 ) 

145 

146 def test_n_dimensional_XYZ_to_Hellwig2022(self) -> None: 

147 """ 

148 Test :func:`colour.appearance.hellwig2022.XYZ_to_Hellwig2022` definition 

149 n-dimensional support. 

150 """ 

151 

152 XYZ = np.array([19.01, 20.00, 21.78]) 

153 XYZ_w = np.array([95.05, 100.00, 108.88]) 

154 L_A = 318.31 

155 Y_b = 20 

156 surround = VIEWING_CONDITIONS_HELLWIG2022["Average"] 

157 specification = XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround) 

158 

159 XYZ = np.tile(XYZ, (6, 1)) 

160 specification = np.tile(specification, (6, 1)) 

161 np.testing.assert_allclose( 

162 XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround), 

163 specification, 

164 atol=TOLERANCE_ABSOLUTE_TESTS, 

165 ) 

166 

167 XYZ_w = np.tile(XYZ_w, (6, 1)) 

168 np.testing.assert_allclose( 

169 XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround), 

170 specification, 

171 atol=TOLERANCE_ABSOLUTE_TESTS, 

172 ) 

173 

174 XYZ = np.reshape(XYZ, (2, 3, 3)) 

175 XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) 

176 specification = np.reshape(specification, (2, 3, 10)) 

177 np.testing.assert_allclose( 

178 XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround), 

179 specification, 

180 atol=TOLERANCE_ABSOLUTE_TESTS, 

181 ) 

182 

183 @ignore_numpy_errors 

184 def test_domain_range_scale_XYZ_to_Hellwig2022(self) -> None: 

185 """ 

186 Test :func:`colour.appearance.hellwig2022.XYZ_to_Hellwig2022` 

187 definition domain and range scale support. 

188 """ 

189 

190 XYZ = np.array([19.01, 20.00, 21.78]) 

191 XYZ_w = np.array([95.05, 100.00, 108.88]) 

192 L_A = 318.31 

193 Y_b = 20 

194 surround = VIEWING_CONDITIONS_HELLWIG2022["Average"] 

195 specification = XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround) 

196 

197 d_r = ( 

198 ("reference", 1, 1), 

199 ( 

200 "1", 

201 0.01, 

202 np.array( 

203 [ 

204 1 / 100, 

205 1 / 100, 

206 1 / 360, 

207 1 / 100, 

208 1 / 100, 

209 1 / 100, 

210 1 / 400, 

211 np.nan, 

212 1 / 100, 

213 1 / 100, 

214 ] 

215 ), 

216 ), 

217 ( 

218 "100", 

219 1, 

220 np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan, 1, 1]), 

221 ), 

222 ) 

223 for scale, factor_a, factor_b in d_r: 

224 with domain_range_scale(scale): 

225 np.testing.assert_allclose( 

226 XYZ_to_Hellwig2022( 

227 XYZ * factor_a, XYZ_w * factor_a, L_A, Y_b, surround 

228 ), 

229 as_float_array(specification) * factor_b, 

230 atol=TOLERANCE_ABSOLUTE_TESTS, 

231 ) 

232 

233 @ignore_numpy_errors 

234 def test_nan_XYZ_to_Hellwig2022(self) -> None: 

235 """ 

236 Test :func:`colour.appearance.hellwig2022.XYZ_to_Hellwig2022 

237 definition nan support. 

238 """ 

239 

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

241 cases = np.array(list(set(product(cases, repeat=3)))) 

242 surround = InductionFactors_Hellwig2022(cases[0, 0], cases[0, 0], cases[0, 0]) 

243 XYZ_to_Hellwig2022(cases, cases, cases[..., 0], cases[..., 0], surround) 

244 

245 

246class TestHellwig2022_to_XYZ: 

247 """ 

248 Define :func:`colour.appearance.hellwig2022.Hellwig2022_to_XYZ` definition 

249 unit tests methods. 

250 """ 

251 

252 def test_Hellwig2022_to_XYZ(self) -> None: 

253 """ 

254 Test :func:`colour.appearance.hellwig2022.Hellwig2022_to_XYZ` 

255 definition. 

256 """ 

257 

258 specification = CAM_Specification_Hellwig2022( 

259 41.731207905126638, 0.025763615829912909, 217.06795976739301 

260 ) 

261 XYZ_w = np.array([95.05, 100.00, 108.88]) 

262 L_A = 318.31 

263 Y_b = 20 

264 surround = VIEWING_CONDITIONS_HELLWIG2022["Average"] 

265 np.testing.assert_allclose( 

266 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

267 np.array([19.01, 20.00, 21.78]), 

268 atol=TOLERANCE_ABSOLUTE_TESTS, 

269 ) 

270 

271 specification = CAM_Specification_Hellwig2022( 

272 65.428280687118473, 31.330032520870901, 17.486592427576902 

273 ) 

274 L_A = 31.83 

275 np.testing.assert_allclose( 

276 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

277 np.array([57.06, 43.06, 31.96]), 

278 atol=TOLERANCE_ABSOLUTE_TESTS, 

279 ) 

280 

281 specification = CAM_Specification_Hellwig2022( 

282 21.360528925833027, 30.603219780800902, 178.8672426588991 

283 ) 

284 XYZ_w = np.array([109.85, 100, 35.58]) 

285 L_A = 318.31 

286 np.testing.assert_allclose( 

287 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

288 np.array([3.53, 6.56, 2.14]), 

289 atol=TOLERANCE_ABSOLUTE_TESTS, 

290 ) 

291 

292 specification = CAM_Specification_Hellwig2022( 

293 41.064050542871215, 31.939561618552826, 259.03405661643671 

294 ) 

295 L_A = 31.38 

296 np.testing.assert_allclose( 

297 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

298 np.array([19.01, 20.00, 21.78]), 

299 atol=TOLERANCE_ABSOLUTE_TESTS, 

300 ) 

301 

302 specification = CAM_Specification_Hellwig2022( 

303 J_HK=41.880278283880095, C=0.025763615829913, h=217.067959767393010 

304 ) 

305 XYZ_w = np.array([95.05, 100.00, 108.88]) 

306 L_A = 318.31 

307 Y_b = 20 

308 surround = VIEWING_CONDITIONS_HELLWIG2022["Average"] 

309 np.testing.assert_allclose( 

310 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

311 np.array([19.01, 20.00, 21.78]), 

312 atol=TOLERANCE_ABSOLUTE_TESTS, 

313 ) 

314 

315 def test_n_dimensional_Hellwig2022_to_XYZ(self) -> None: 

316 """ 

317 Test :func:`colour.appearance.hellwig2022.Hellwig2022_to_XYZ` 

318 definition n-dimensional support. 

319 """ 

320 

321 XYZ = np.array([19.01, 20.00, 21.78]) 

322 XYZ_w = np.array([95.05, 100.00, 108.88]) 

323 L_A = 318.31 

324 Y_b = 20 

325 surround = VIEWING_CONDITIONS_HELLWIG2022["Average"] 

326 specification = XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround) 

327 XYZ = Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround) 

328 

329 specification = CAM_Specification_Hellwig2022( 

330 *np.transpose(np.tile(tsplit(specification), (6, 1))).tolist() 

331 ) 

332 XYZ = np.tile(XYZ, (6, 1)) 

333 np.testing.assert_allclose( 

334 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

335 XYZ, 

336 atol=TOLERANCE_ABSOLUTE_TESTS, 

337 ) 

338 

339 XYZ_w = np.tile(XYZ_w, (6, 1)) 

340 np.testing.assert_allclose( 

341 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

342 XYZ, 

343 atol=TOLERANCE_ABSOLUTE_TESTS, 

344 ) 

345 

346 specification = CAM_Specification_Hellwig2022( 

347 *tsplit(np.reshape(specification, (2, 3, 10))).tolist() 

348 ) 

349 XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) 

350 XYZ = np.reshape(XYZ, (2, 3, 3)) 

351 np.testing.assert_allclose( 

352 Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), 

353 XYZ, 

354 atol=TOLERANCE_ABSOLUTE_TESTS, 

355 ) 

356 

357 @ignore_numpy_errors 

358 def test_domain_range_scale_Hellwig2022_to_XYZ(self) -> None: 

359 """ 

360 Test :func:`colour.appearance.hellwig2022.Hellwig2022_to_XYZ` 

361 definition domain and range scale support. 

362 """ 

363 

364 XYZ = np.array([19.01, 20.00, 21.78]) 

365 XYZ_w = np.array([95.05, 100.00, 108.88]) 

366 L_A = 318.31 

367 Y_b = 20 

368 surround = VIEWING_CONDITIONS_HELLWIG2022["Average"] 

369 specification = XYZ_to_Hellwig2022(XYZ, XYZ_w, L_A, Y_b, surround) 

370 XYZ = Hellwig2022_to_XYZ(specification, XYZ_w, L_A, Y_b, surround) 

371 

372 d_r = ( 

373 ("reference", 1, 1), 

374 ( 

375 "1", 

376 np.array( 

377 [ 

378 1 / 100, 

379 1 / 100, 

380 1 / 360, 

381 1 / 100, 

382 1 / 100, 

383 1 / 100, 

384 1 / 400, 

385 np.nan, 

386 1 / 100, 

387 1 / 100, 

388 ] 

389 ), 

390 0.01, 

391 ), 

392 ( 

393 "100", 

394 np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan, 1, 1]), 

395 1, 

396 ), 

397 ) 

398 for scale, factor_a, factor_b in d_r: 

399 with domain_range_scale(scale): 

400 np.testing.assert_allclose( 

401 Hellwig2022_to_XYZ( 

402 specification * factor_a, 

403 XYZ_w * factor_b, 

404 L_A, 

405 Y_b, 

406 surround, 

407 ), 

408 XYZ * factor_b, 

409 atol=TOLERANCE_ABSOLUTE_TESTS, 

410 ) 

411 

412 @ignore_numpy_errors 

413 def test_raise_exception_Hellwig2022_to_XYZ(self) -> None: 

414 """ 

415 Test :func:`colour.appearance.hellwig2022.Hellwig2022_to_XYZ` 

416 definition raised exception. 

417 """ 

418 pytest.raises( 

419 ValueError, 

420 Hellwig2022_to_XYZ, 

421 CAM_Specification_Hellwig2022( 

422 J_HK=None, C=0.025763615829912909, h=217.06795976739301 

423 ), 

424 np.array([95.05, 100.00, 108.88]), 

425 318.31, 

426 20.0, 

427 VIEWING_CONDITIONS_HELLWIG2022["Average"], 

428 ) 

429 

430 pytest.raises( 

431 ValueError, 

432 Hellwig2022_to_XYZ, 

433 CAM_Specification_Hellwig2022(41.731207905126638, None, 217.06795976739301), 

434 np.array([95.05, 100.00, 108.88]), 

435 318.31, 

436 20.0, 

437 VIEWING_CONDITIONS_HELLWIG2022["Average"], 

438 ) 

439 

440 @ignore_numpy_errors 

441 def test_nan_Hellwig2022_to_XYZ(self) -> None: 

442 """ 

443 Test :func:`colour.appearance.hellwig2022.Hellwig2022_to_XYZ` 

444 definition nan support. 

445 """ 

446 

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

448 cases = np.array(list(set(product(cases, repeat=3)))) 

449 surround = InductionFactors_Hellwig2022(cases[0, 0], cases[0, 0], cases[0, 0]) 

450 Hellwig2022_to_XYZ( 

451 CAM_Specification_Hellwig2022( 

452 cases[..., 0], cases[..., 0], cases[..., 0], M=50 

453 ), 

454 cases, 

455 cases[..., 0], 

456 cases[..., 0], 

457 surround, 

458 )