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

1"""

2 Define the unit tests for the :mod:`colour.appearance.hellwig2022` module.

3

4 References

5----------

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

7 Discussion with Mansencal, T.

8"""

9

10 from __future__ import annotations

11

12 from itertools import product

13

14 import numpy as np

15 import pytest

16

17 from colour.appearance import (

18 VIEWING_CONDITIONS_HELLWIG2022,

19 CAM_Specification_Hellwig2022,

20 Hellwig2022_to_XYZ,

21 InductionFactors_Hellwig2022,

22 XYZ_to_Hellwig2022,

23)

24 from colour.constants import TOLERANCE_ABSOLUTE_TESTS

25 from 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

45 class 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

246 class 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 )