Coverage for colour/models/tests/test_hunter_lab.py: 100%

123 statements  

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

1"""Define the unit tests for the :mod:`colour.models.hunter_lab` module.""" 

2 

3from __future__ import annotations 

4 

5from itertools import product 

6 

7import numpy as np 

8 

9from colour.colorimetry import TVS_ILLUMINANTS_HUNTERLAB 

10from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

11from colour.models import ( 

12 Hunter_Lab_to_XYZ, 

13 XYZ_to_Hunter_Lab, 

14 XYZ_to_K_ab_HunterLab1966, 

15) 

16from colour.utilities import domain_range_scale, ignore_numpy_errors 

17 

18__author__ = "Colour Developers" 

19__copyright__ = "Copyright 2013 Colour Developers" 

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

21__maintainer__ = "Colour Developers" 

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

23__status__ = "Production" 

24 

25__all__ = [ 

26 "TestXYZ_to_K_ab_HunterLab1966", 

27 "TestXYZ_to_Hunter_Lab", 

28 "TestHunter_Lab_to_XYZ", 

29] 

30 

31 

32class TestXYZ_to_K_ab_HunterLab1966: 

33 """ 

34 Define :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` 

35 definition unit tests methods. 

36 """ 

37 

38 def test_XYZ_to_K_ab_HunterLab1966(self) -> None: 

39 """ 

40 Test :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` 

41 definition. 

42 """ 

43 

44 np.testing.assert_allclose( 

45 XYZ_to_K_ab_HunterLab1966( 

46 np.array([0.20654008, 0.12197225, 0.05136952]) * 100 

47 ), 

48 np.array([80.32152090, 14.59816495]), 

49 atol=TOLERANCE_ABSOLUTE_TESTS, 

50 ) 

51 

52 np.testing.assert_allclose( 

53 XYZ_to_K_ab_HunterLab1966( 

54 np.array([0.14222010, 0.23042768, 0.10495772]) * 100 

55 ), 

56 np.array([66.65154834, 20.86664881]), 

57 atol=TOLERANCE_ABSOLUTE_TESTS, 

58 ) 

59 

60 np.testing.assert_allclose( 

61 XYZ_to_K_ab_HunterLab1966( 

62 np.array([0.07818780, 0.06157201, 0.28099326]) * 100 

63 ), 

64 np.array([49.41960269, 34.14235426]), 

65 atol=TOLERANCE_ABSOLUTE_TESTS, 

66 ) 

67 

68 def test_n_dimensional_XYZ_to_K_ab_HunterLab1966(self) -> None: 

69 """ 

70 Test :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` 

71 definition n-dimensional support. 

72 """ 

73 

74 XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 

75 K_ab = XYZ_to_K_ab_HunterLab1966(XYZ) 

76 

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

78 K_ab = np.tile(K_ab, (6, 1)) 

79 np.testing.assert_allclose( 

80 XYZ_to_K_ab_HunterLab1966(XYZ), K_ab, atol=TOLERANCE_ABSOLUTE_TESTS 

81 ) 

82 

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

84 K_ab = np.reshape(K_ab, (2, 3, 2)) 

85 np.testing.assert_allclose( 

86 XYZ_to_K_ab_HunterLab1966(XYZ), K_ab, atol=TOLERANCE_ABSOLUTE_TESTS 

87 ) 

88 

89 @ignore_numpy_errors 

90 def test_nan_XYZ_to_K_ab_HunterLab1966(self) -> None: 

91 """ 

92 Test :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` 

93 definition nan support. 

94 """ 

95 

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

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

98 XYZ_to_K_ab_HunterLab1966(cases) 

99 

100 

101class TestXYZ_to_Hunter_Lab: 

102 """ 

103 Define :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition unit 

104 tests methods. 

105 """ 

106 

107 def test_XYZ_to_Hunter_Lab(self) -> None: 

108 """Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition.""" 

109 

110 np.testing.assert_allclose( 

111 XYZ_to_Hunter_Lab(np.array([0.20654008, 0.12197225, 0.05136952]) * 100), 

112 np.array([34.92452577, 47.06189858, 14.38615107]), 

113 atol=TOLERANCE_ABSOLUTE_TESTS, 

114 ) 

115 

116 np.testing.assert_allclose( 

117 XYZ_to_Hunter_Lab(np.array([0.14222010, 0.23042768, 0.10495772]) * 100), 

118 np.array([48.00288325, -28.98551622, 18.75564181]), 

119 atol=TOLERANCE_ABSOLUTE_TESTS, 

120 ) 

121 

122 np.testing.assert_allclose( 

123 XYZ_to_Hunter_Lab(np.array([0.07818780, 0.06157201, 0.28099326]) * 100), 

124 np.array([24.81370791, 14.38300039, -53.25539126]), 

125 atol=TOLERANCE_ABSOLUTE_TESTS, 

126 ) 

127 

128 h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] 

129 A = h_i["A"] 

130 np.testing.assert_allclose( 

131 XYZ_to_Hunter_Lab( 

132 np.array([0.20654008, 0.12197225, 0.05136952]) * 100, 

133 A.XYZ_n, 

134 A.K_ab, 

135 ), 

136 np.array([34.92452577, 35.04243086, -2.47688619]), 

137 atol=TOLERANCE_ABSOLUTE_TESTS, 

138 ) 

139 

140 D65 = h_i["D65"] 

141 np.testing.assert_allclose( 

142 XYZ_to_Hunter_Lab( 

143 np.array([0.20654008, 0.12197225, 0.05136952]) * 100, 

144 D65.XYZ_n, 

145 D65.K_ab, 

146 ), 

147 np.array([34.92452577, 47.06189858, 14.38615107]), 

148 atol=TOLERANCE_ABSOLUTE_TESTS, 

149 ) 

150 

151 np.testing.assert_allclose( 

152 XYZ_to_Hunter_Lab( 

153 np.array([0.20654008, 0.12197225, 0.05136952]) * 100, 

154 D65.XYZ_n, 

155 K_ab=None, 

156 ), 

157 np.array([34.92452577, 47.05669614, 14.38385238]), 

158 atol=TOLERANCE_ABSOLUTE_TESTS, 

159 ) 

160 

161 def test_n_dimensional_XYZ_to_Hunter_Lab(self) -> None: 

162 """ 

163 Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition 

164 n-dimensional support. 

165 """ 

166 

167 h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] 

168 D65 = h_i["D65"] 

169 

170 XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 

171 XYZ_n = D65.XYZ_n 

172 K_ab = D65.K_ab 

173 Lab = XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab) 

174 

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

176 Lab = np.tile(Lab, (6, 1)) 

177 np.testing.assert_allclose( 

178 XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), 

179 Lab, 

180 atol=TOLERANCE_ABSOLUTE_TESTS, 

181 ) 

182 

183 XYZ_n = np.tile(XYZ_n, (6, 1)) 

184 K_ab = np.tile(K_ab, (6, 1)) 

185 np.testing.assert_allclose( 

186 XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), 

187 Lab, 

188 atol=TOLERANCE_ABSOLUTE_TESTS, 

189 ) 

190 

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

192 XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) 

193 K_ab = np.reshape(K_ab, (2, 3, 2)) 

194 Lab = np.reshape(Lab, (2, 3, 3)) 

195 np.testing.assert_allclose( 

196 XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), 

197 Lab, 

198 atol=TOLERANCE_ABSOLUTE_TESTS, 

199 ) 

200 

201 def test_domain_range_scale_XYZ_to_Hunter_Lab(self) -> None: 

202 """ 

203 Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition 

204 domain and range scale support. 

205 """ 

206 

207 h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] 

208 D65 = h_i["D65"] 

209 

210 XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 

211 XYZ_n = D65.XYZ_n 

212 K_ab = D65.K_ab 

213 Lab = XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab) 

214 

215 d_r = (("reference", 1), ("1", 0.01), ("100", 1)) 

216 for scale, factor in d_r: 

217 with domain_range_scale(scale): 

218 np.testing.assert_allclose( 

219 XYZ_to_Hunter_Lab(XYZ * factor, XYZ_n * factor, K_ab), 

220 Lab * factor, 

221 atol=TOLERANCE_ABSOLUTE_TESTS, 

222 ) 

223 

224 @ignore_numpy_errors 

225 def test_nan_XYZ_to_Hunter_Lab(self) -> None: 

226 """ 

227 Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition nan 

228 support. 

229 """ 

230 

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

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

233 XYZ_to_Hunter_Lab(cases, cases, cases[..., 0:2]) 

234 

235 

236class TestHunter_Lab_to_XYZ: 

237 """ 

238 Define :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition unit 

239 tests methods. 

240 """ 

241 

242 def test_Hunter_Lab_to_XYZ(self) -> None: 

243 """Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition.""" 

244 

245 np.testing.assert_allclose( 

246 Hunter_Lab_to_XYZ(np.array([34.92452577, 47.06189858, 14.38615107])), 

247 np.array([20.65400800, 12.19722500, 5.13695200]), 

248 atol=TOLERANCE_ABSOLUTE_TESTS, 

249 ) 

250 

251 np.testing.assert_allclose( 

252 Hunter_Lab_to_XYZ(np.array([48.00288325, -28.98551622, 18.75564181])), 

253 np.array([14.22201000, 23.04276800, 10.49577200]), 

254 atol=TOLERANCE_ABSOLUTE_TESTS, 

255 ) 

256 

257 np.testing.assert_allclose( 

258 Hunter_Lab_to_XYZ(np.array([24.81370791, 14.38300039, -53.25539126])), 

259 np.array([7.81878000, 6.15720100, 28.09932601]), 

260 atol=TOLERANCE_ABSOLUTE_TESTS, 

261 ) 

262 

263 h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] 

264 A = h_i["A"] 

265 np.testing.assert_allclose( 

266 Hunter_Lab_to_XYZ( 

267 np.array([34.92452577, 35.04243086, -2.47688619]), 

268 A.XYZ_n, 

269 A.K_ab, 

270 ), 

271 np.array([20.65400800, 12.19722500, 5.13695200]), 

272 atol=TOLERANCE_ABSOLUTE_TESTS, 

273 ) 

274 

275 D65 = h_i["D65"] 

276 np.testing.assert_allclose( 

277 Hunter_Lab_to_XYZ( 

278 np.array([34.92452577, 47.06189858, 14.38615107]), 

279 D65.XYZ_n, 

280 D65.K_ab, 

281 ), 

282 np.array([20.65400800, 12.19722500, 5.13695200]), 

283 atol=TOLERANCE_ABSOLUTE_TESTS, 

284 ) 

285 

286 np.testing.assert_allclose( 

287 Hunter_Lab_to_XYZ( 

288 np.array([34.92452577, 47.05669614, 14.38385238]), 

289 D65.XYZ_n, 

290 K_ab=None, 

291 ), 

292 np.array([20.65400800, 12.19722500, 5.13695200]), 

293 atol=TOLERANCE_ABSOLUTE_TESTS, 

294 ) 

295 

296 def test_n_dimensional_Hunter_Lab_to_XYZ(self) -> None: 

297 """ 

298 Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition 

299 n-dimensional support. 

300 """ 

301 

302 h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] 

303 D65 = h_i["D65"] 

304 

305 Lab = np.array([34.92452577, 47.06189858, 14.38615107]) 

306 XYZ_n = D65.XYZ_n 

307 K_ab = D65.K_ab 

308 XYZ = Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab) 

309 

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

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

312 np.testing.assert_allclose( 

313 Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), 

314 XYZ, 

315 atol=TOLERANCE_ABSOLUTE_TESTS, 

316 ) 

317 

318 K_ab = np.tile(K_ab, (6, 1)) 

319 XYZ_n = np.tile(XYZ_n, (6, 1)) 

320 np.testing.assert_allclose( 

321 Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), 

322 XYZ, 

323 atol=TOLERANCE_ABSOLUTE_TESTS, 

324 ) 

325 

326 Lab = np.reshape(Lab, (2, 3, 3)) 

327 XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) 

328 K_ab = np.reshape(K_ab, (2, 3, 2)) 

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

330 np.testing.assert_allclose( 

331 Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), 

332 XYZ, 

333 atol=TOLERANCE_ABSOLUTE_TESTS, 

334 ) 

335 

336 def test_domain_range_scale_Hunter_Lab_to_XYZ(self) -> None: 

337 """ 

338 Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition 

339 domain and range scale support. 

340 """ 

341 

342 h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] 

343 D65 = h_i["D65"] 

344 

345 Lab = np.array([34.92452577, 47.06189858, 14.38615107]) 

346 XYZ_n = D65.XYZ_n 

347 K_ab = D65.K_ab 

348 XYZ = Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab) 

349 

350 d_r = (("reference", 1), ("1", 0.01), ("100", 1)) 

351 for scale, factor in d_r: 

352 with domain_range_scale(scale): 

353 np.testing.assert_allclose( 

354 Hunter_Lab_to_XYZ(Lab * factor, XYZ_n * factor, K_ab), 

355 XYZ * factor, 

356 atol=TOLERANCE_ABSOLUTE_TESTS, 

357 ) 

358 

359 @ignore_numpy_errors 

360 def test_nan_Hunter_Lab_to_XYZ(self) -> None: 

361 """ 

362 Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition 

363 nan support. 

364 """ 

365 

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

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

368 Hunter_Lab_to_XYZ(cases, cases, cases[..., 0:2])