Coverage for models/tests/test_common.py: 100%

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

2

3 from __future__ import annotations

4

5 from itertools import product

6

7 import numpy as np

8

9 from colour.constants import TOLERANCE_ABSOLUTE_TESTS

10 from colour.models import Iab_to_XYZ, Jab_to_JCh, JCh_to_Jab, XYZ_to_Iab

11 from colour.utilities import domain_range_scale, ignore_numpy_errors

12

13 __author__ = "Colour Developers"

14 __copyright__ = "Copyright 2013 Colour Developers"

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

16 __maintainer__ = "Colour Developers"

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

18 __status__ = "Production"

19

20 __all__ = [

21 "TestJab_to_JCh",

22 "TestJCh_to_Jab",

23 "TestXYZ_to_Iab",

24 "TestIab_to_XYZ",

25]

26

27

28 class TestJab_to_JCh:

29 """

30 Define :func:`colour.models.common.Jab_to_JCh` definition unit tests

31 methods.

32 """

33

34 def test_Jab_to_JCh(self) -> None:

35 """Test :func:`colour.models.common.Jab_to_JCh` definition."""

36

37 np.testing.assert_allclose(

38 Jab_to_JCh(np.array([41.52787529, 52.63858304, 26.92317922])),

39 np.array([41.52787529, 59.12425901, 27.08848784]),

40 atol=TOLERANCE_ABSOLUTE_TESTS,

41 )

42

43 np.testing.assert_allclose(

44 Jab_to_JCh(np.array([55.11636304, -41.08791787, 30.91825778])),

45 np.array([55.11636304, 51.42135412, 143.03889556]),

46 atol=TOLERANCE_ABSOLUTE_TESTS,

47 )

48

49 np.testing.assert_allclose(

50 Jab_to_JCh(np.array([29.80565520, 20.01830466, -48.34913874])),

51 np.array([29.80565520, 52.32945383, 292.49133666]),

52 atol=TOLERANCE_ABSOLUTE_TESTS,

53 )

54

55 def test_n_dimensional_Jab_to_JCh(self) -> None:

56 """

57 Test :func:`colour.models.common.Jab_to_JCh` definition n-dimensional

58 arrays support.

59 """

60

61 Lab = np.array([41.52787529, 52.63858304, 26.92317922])

62 LCHab = Jab_to_JCh(Lab)

63

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

65 LCHab = np.tile(LCHab, (6, 1))

66 np.testing.assert_allclose(

67 Jab_to_JCh(Lab), LCHab, atol=TOLERANCE_ABSOLUTE_TESTS

68 )

69

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

71 LCHab = np.reshape(LCHab, (2, 3, 3))

72 np.testing.assert_allclose(

73 Jab_to_JCh(Lab), LCHab, atol=TOLERANCE_ABSOLUTE_TESTS

74 )

75

76 def test_domain_range_scale_Jab_to_JCh(self) -> None:

77 """

78 Test :func:`colour.models.common.Jab_to_JCh` definition domain and

79 range scale support.

80 """

81

82 Lab = np.array([41.52787529, 52.63858304, 26.92317922])

83 LCHab = Jab_to_JCh(Lab)

84

85 d_r = (

86 ("reference", 1, 1),

87 ("1", 0.01, np.array([0.01, 0.01, 1 / 360])),

88 ("100", 1, np.array([1, 1, 1 / 3.6])),

89 )

90 for scale, factor_a, factor_b in d_r:

91 with domain_range_scale(scale):

92 np.testing.assert_allclose(

93 Jab_to_JCh(Lab * factor_a),

94 LCHab * factor_b,

95 atol=TOLERANCE_ABSOLUTE_TESTS,

96 )

97

98 @ignore_numpy_errors

99 def test_nan_Jab_to_JCh(self) -> None:

100 """Test :func:`colour.models.common.Jab_to_JCh` definition nan support."""

101

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

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

104 Jab_to_JCh(cases)

105

106

107 class TestJCh_to_Jab:

108 """

109 Define :func:`colour.models.common.JCh_to_Jab` definition unit tests

110 methods.

111 """

112

113 def test_JCh_to_Jab(self) -> None:

114 """Test :func:`colour.models.common.JCh_to_Jab` definition."""

115

116 np.testing.assert_allclose(

117 JCh_to_Jab(np.array([41.52787529, 59.12425901, 27.08848784])),

118 np.array([41.52787529, 52.63858304, 26.92317922]),

119 atol=TOLERANCE_ABSOLUTE_TESTS,

120 )

121

122 np.testing.assert_allclose(

123 JCh_to_Jab(np.array([55.11636304, 51.42135412, 143.03889556])),

124 np.array([55.11636304, -41.08791787, 30.91825778]),

125 atol=TOLERANCE_ABSOLUTE_TESTS,

126 )

127

128 np.testing.assert_allclose(

129 JCh_to_Jab(np.array([29.80565520, 52.32945383, 292.49133666])),

130 np.array([29.80565520, 20.01830466, -48.34913874]),

131 atol=TOLERANCE_ABSOLUTE_TESTS,

132 )

133

134 def test_n_dimensional_JCh_to_Jab(self) -> None:

135 """

136 Test :func:`colour.models.common.JCh_to_Jab` definition n-dimensional

137 arrays support.

138 """

139

140 LCHab = np.array([41.52787529, 59.12425901, 27.08848784])

141 Lab = JCh_to_Jab(LCHab)

142

143 LCHab = np.tile(LCHab, (6, 1))

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

145 np.testing.assert_allclose(

146 JCh_to_Jab(LCHab), Lab, atol=TOLERANCE_ABSOLUTE_TESTS

147 )

148

149 LCHab = np.reshape(LCHab, (2, 3, 3))

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

151 np.testing.assert_allclose(

152 JCh_to_Jab(LCHab), Lab, atol=TOLERANCE_ABSOLUTE_TESTS

153 )

154

155 def test_domain_range_scale_JCh_to_Jab(self) -> None:

156 """

157 Test :func:`colour.models.common.JCh_to_Jab` definition domain and

158 range scale support.

159 """

160

161 LCHab = np.array([41.52787529, 59.12425901, 27.08848784])

162 Lab = JCh_to_Jab(LCHab)

163

164 d_r = (

165 ("reference", 1, 1),

166 ("1", np.array([0.01, 0.01, 1 / 360]), 0.01),

167 ("100", np.array([1, 1, 1 / 3.6]), 1),

168 )

169 for scale, factor_a, factor_b in d_r:

170 with domain_range_scale(scale):

171 np.testing.assert_allclose(

172 JCh_to_Jab(LCHab * factor_a),

173 Lab * factor_b,

174 atol=TOLERANCE_ABSOLUTE_TESTS,

175 )

176

177 @ignore_numpy_errors

178 def test_nan_JCh_to_Jab(self) -> None:

179 """Test :func:`colour.models.common.JCh_to_Jab` definition nan support."""

180

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

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

183 JCh_to_Jab(cases)

184

185

186 class TestXYZ_to_Iab:

187 """Define :func:`colour.models.common.XYZ_to_Iab` definition unit tests methods."""

188

189 def setup_method(self) -> None:

190 """Initialise the common tests attributes."""

191

192 self.LMS_to_LMS_p = lambda x: x**0.43

193 self.M_XYZ_to_LMS = np.array(

194 [

195 [0.4002, 0.7075, -0.0807],

196 [-0.2280, 1.1500, 0.0612],

197 [0.0000, 0.0000, 0.9184],

198 ]

199 )

200 self.M_LMS_p_to_Iab = np.array(

201 [

202 [0.4000, 0.4000, 0.2000],

203 [4.4550, -4.8510, 0.3960],

204 [0.8056, 0.3572, -1.1628],

205 ]

206 )

207

208 def test_XYZ_to_Iab(self) -> None:

209 """Test :func:`colour.models.common.XYZ_to_Iab` definition."""

210

211 np.testing.assert_allclose(

212 XYZ_to_Iab(

213 np.array([0.20654008, 0.12197225, 0.05136952]),

214 self.LMS_to_LMS_p,

215 self.M_XYZ_to_LMS,

216 self.M_LMS_p_to_Iab,

217 ),

218 np.array([0.38426191, 0.38487306, 0.18886838]),

219 atol=TOLERANCE_ABSOLUTE_TESTS,

220 )

221

222 np.testing.assert_allclose(

223 XYZ_to_Iab(

224 np.array([0.14222010, 0.23042768, 0.10495772]),

225 self.LMS_to_LMS_p,

226 self.M_XYZ_to_LMS,

227 self.M_LMS_p_to_Iab,

228 ),

229 np.array([0.49437481, -0.19251742, 0.18080304]),

230 atol=TOLERANCE_ABSOLUTE_TESTS,

231 )

232

233 np.testing.assert_allclose(

234 XYZ_to_Iab(

235 np.array([0.07818780, 0.06157201, 0.28099326]),

236 self.LMS_to_LMS_p,

237 self.M_XYZ_to_LMS,

238 self.M_LMS_p_to_Iab,

239 ),

240 np.array([0.35167774, -0.07525627, -0.30921279]),

241 atol=TOLERANCE_ABSOLUTE_TESTS,

242 )

243

244 def test_n_dimensional_XYZ_to_Iab(self) -> None:

245 """

246 Test :func:`colour.models.common.XYZ_to_Iab` definition n-dimensional

247 support.

248 """

249

250 XYZ = np.array([0.20654008, 0.12197225, 0.05136952])

251 Iab = XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab)

252

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

254 Iab = np.tile(Iab, (6, 1))

255 np.testing.assert_allclose(

256 XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab),

257 Iab,

258 atol=TOLERANCE_ABSOLUTE_TESTS,

259 )

260

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

262 Iab = np.reshape(Iab, (2, 3, 3))

263 np.testing.assert_allclose(

264 XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab),

265 Iab,

266 atol=TOLERANCE_ABSOLUTE_TESTS,

267 )

268

269 def test_domain_range_scale_XYZ_to_Iab(self) -> None:

270 """

271 Test :func:`colour.models.common.XYZ_to_Iab` definition domain and

272 range scale support.

273 """

274

275 XYZ = np.array([0.20654008, 0.12197225, 0.05136952])

276 Iab = XYZ_to_Iab(XYZ, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab)

277

278 d_r = (("reference", 1), ("1", 1), ("100", 100))

279 for scale, factor in d_r:

280 with domain_range_scale(scale):

281 np.testing.assert_allclose(

282 XYZ_to_Iab(

283 XYZ * factor,

284 self.LMS_to_LMS_p,

285 self.M_XYZ_to_LMS,

286 self.M_LMS_p_to_Iab,

287 ),

288 Iab * factor,

289 atol=TOLERANCE_ABSOLUTE_TESTS,

290 )

291

292 @ignore_numpy_errors

293 def test_nan_XYZ_to_Iab(self) -> None:

294 """Test :func:`colour.models.common.XYZ_to_Iab` definition nan support."""

295

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

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

298 XYZ_to_Iab(cases, self.LMS_to_LMS_p, self.M_XYZ_to_LMS, self.M_LMS_p_to_Iab)

299

300

301 class TestIab_to_XYZ:

302 """

303 Define :func:`colour.models.common.Iab_to_XYZ` definition unit tests

304 methods.

305 """

306

307 def setup_method(self) -> None:

308 """Initialise the common tests attributes."""

309

310 self.LMS_p_to_LMS = lambda x: x ** (1 / 0.43)

311 self.M_Iab_to_LMS_p = np.linalg.inv(

312 np.array(

313 [

314 [0.4000, 0.4000, 0.2000],

315 [4.4550, -4.8510, 0.3960],

316 [0.8056, 0.3572, -1.1628],

317 ]

318 )

319 )

320 self.M_LMS_to_XYZ = np.linalg.inv(

321 np.array(

322 [

323 [0.4002, 0.7075, -0.0807],

324 [-0.2280, 1.1500, 0.0612],

325 [0.0000, 0.0000, 0.9184],

326 ]

327 )

328 )

329

330 def test_Iab_to_XYZ(self) -> None:

331 """Test :func:`colour.models.common.Iab_to_XYZ` definition."""

332

333 np.testing.assert_allclose(

334 Iab_to_XYZ(

335 np.array([0.38426191, 0.38487306, 0.18886838]),

336 self.LMS_p_to_LMS,

337 self.M_Iab_to_LMS_p,

338 self.M_LMS_to_XYZ,

339 ),

340 np.array([0.20654008, 0.12197225, 0.05136952]),

341 atol=TOLERANCE_ABSOLUTE_TESTS,

342 )

343

344 np.testing.assert_allclose(

345 Iab_to_XYZ(

346 np.array([0.49437481, -0.19251742, 0.18080304]),

347 self.LMS_p_to_LMS,

348 self.M_Iab_to_LMS_p,

349 self.M_LMS_to_XYZ,

350 ),

351 np.array([0.14222010, 0.23042768, 0.10495772]),

352 atol=TOLERANCE_ABSOLUTE_TESTS,

353 )

354

355 np.testing.assert_allclose(

356 Iab_to_XYZ(

357 np.array([0.35167774, -0.07525627, -0.30921279]),

358 self.LMS_p_to_LMS,

359 self.M_Iab_to_LMS_p,

360 self.M_LMS_to_XYZ,

361 ),

362 np.array([0.07818780, 0.06157201, 0.28099326]),

363 atol=TOLERANCE_ABSOLUTE_TESTS,

364 )

365

366 def test_n_dimensional_Iab_to_XYZ(self) -> None:

367 """

368 Test :func:`colour.models.common.Iab_to_XYZ` definition n-dimensional

369 support.

370 """

371

372 Iab = np.array([0.38426191, 0.38487306, 0.18886838])

373 XYZ = Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ)

374

375 Iab = np.tile(Iab, (6, 1))

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

377 np.testing.assert_allclose(

378 Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ),

379 XYZ,

380 atol=TOLERANCE_ABSOLUTE_TESTS,

381 )

382

383 Iab = np.reshape(Iab, (2, 3, 3))

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

385 np.testing.assert_allclose(

386 Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ),

387 XYZ,

388 atol=TOLERANCE_ABSOLUTE_TESTS,

389 )

390

391 def test_domain_range_scale_Iab_to_XYZ(self) -> None:

392 """

393 Test :func:`colour.models.common.Iab_to_XYZ` definition domain and

394 range scale support.

395 """

396

397 Iab = np.array([0.38426191, 0.38487306, 0.18886838])

398 XYZ = Iab_to_XYZ(Iab, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ)

399

400 d_r = (("reference", 1), ("1", 1), ("100", 100))

401 for scale, factor in d_r:

402 with domain_range_scale(scale):

403 np.testing.assert_allclose(

404 Iab_to_XYZ(

405 Iab * factor,

406 self.LMS_p_to_LMS,

407 self.M_Iab_to_LMS_p,

408 self.M_LMS_to_XYZ,

409 ),

410 XYZ * factor,

411 atol=TOLERANCE_ABSOLUTE_TESTS,

412 )

413

414 @ignore_numpy_errors

415 def test_nan_Iab_to_XYZ(self) -> None:

416 """Test :func:`colour.models.common.Iab_to_XYZ` definition nan support."""

417

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

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

420 Iab_to_XYZ(cases, self.LMS_p_to_LMS, self.M_Iab_to_LMS_p, self.M_LMS_to_XYZ)