Coverage for colour/models/cam02_ucs.py: 100%

90 statements  

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

1""" 

2CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) 

3========================================================================= 

4 

5Define the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, and *CAM02-UCS* 

6colourspaces transformations. 

7 

8- :func:`colour.JMh_CIECAM02_to_CAM02LCD` 

9- :func:`colour.CAM02LCD_to_JMh_CIECAM02` 

10- :func:`colour.JMh_CIECAM02_to_CAM02SCD` 

11- :func:`colour.CAM02SCD_to_JMh_CIECAM02` 

12- :func:`colour.JMh_CIECAM02_to_CAM02UCS` 

13- :func:`colour.CAM02UCS_to_JMh_CIECAM02` 

14- :func:`colour.XYZ_to_CAM02LCD` 

15- :func:`colour.CAM02LCD_to_XYZ` 

16- :func:`colour.XYZ_to_CAM02SCD` 

17- :func:`colour.CAM02SCD_to_XYZ` 

18- :func:`colour.XYZ_to_CAM02UCS` 

19- :func:`colour.CAM02UCS_to_XYZ` 

20 

21References 

22---------- 

23- :cite:`Luo2006b` : Luo, M. Ronnier, Cui, G., & Li, C. (2006). Uniform 

24 colour spaces based on CIECAM02 colour appearance model. Color Research & 

25 Application, 31(4), 320-330. doi:10.1002/col.20227 

26""" 

27 

28from __future__ import annotations 

29 

30import typing 

31from dataclasses import dataclass 

32 

33import numpy as np 

34 

35from colour.algebra import cartesian_to_polar, polar_to_cartesian 

36 

37if typing.TYPE_CHECKING: 

38 from colour.hints import ( 

39 Any, 

40 Domain1, 

41 Domain100, 

42 Domain100_100_360, 

43 Range1, 

44 Range100, 

45 Range100_100_360, 

46 ) 

47 

48from colour.hints import NDArrayFloat, cast 

49from colour.utilities import ( 

50 CanonicalMapping, 

51 MixinDataclassIterable, 

52 as_float_array, 

53 from_range_100, 

54 from_range_degrees, 

55 get_domain_range_scale, 

56 to_domain_100, 

57 to_domain_degrees, 

58 tsplit, 

59 tstack, 

60) 

61 

62__author__ = "Colour Developers" 

63__copyright__ = "Copyright 2013 Colour Developers" 

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

65__maintainer__ = "Colour Developers" 

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

67__status__ = "Production" 

68 

69__all__ = [ 

70 "Coefficients_UCS_Luo2006", 

71 "COEFFICIENTS_UCS_LUO2006", 

72 "JMh_CIECAM02_to_UCS_Luo2006", 

73 "UCS_Luo2006_to_JMh_CIECAM02", 

74 "JMh_CIECAM02_to_CAM02LCD", 

75 "CAM02LCD_to_JMh_CIECAM02", 

76 "JMh_CIECAM02_to_CAM02SCD", 

77 "CAM02SCD_to_JMh_CIECAM02", 

78 "JMh_CIECAM02_to_CAM02UCS", 

79 "CAM02UCS_to_JMh_CIECAM02", 

80 "XYZ_to_UCS_Luo2006", 

81 "UCS_Luo2006_to_XYZ", 

82 "XYZ_to_CAM02LCD", 

83 "CAM02LCD_to_XYZ", 

84 "XYZ_to_CAM02SCD", 

85 "CAM02SCD_to_XYZ", 

86 "XYZ_to_CAM02UCS", 

87 "CAM02UCS_to_XYZ", 

88] 

89 

90 

91@dataclass(frozen=True) 

92class Coefficients_UCS_Luo2006(MixinDataclassIterable): 

93 """ 

94 Store *Luo et al. (2006)* fitting coefficients for the *CAM02-LCD*, 

95 *CAM02-SCD*, and *CAM02-UCS* colourspaces. 

96 

97 Attributes 

98 ---------- 

99 K_L 

100 Lightness coefficient. 

101 c_1 

102 First chroma coefficient. 

103 c_2 

104 Second chroma coefficient. 

105 """ 

106 

107 K_L: float 

108 c_1: float 

109 c_2: float 

110 

111 

112COEFFICIENTS_UCS_LUO2006: CanonicalMapping = CanonicalMapping( 

113 { 

114 "CAM02-LCD": Coefficients_UCS_Luo2006(0.77, 0.007, 0.0053), 

115 "CAM02-SCD": Coefficients_UCS_Luo2006(1.24, 0.007, 0.0363), 

116 "CAM02-UCS": Coefficients_UCS_Luo2006(1.00, 0.007, 0.0228), 

117 } 

118) 

119""" 

120*Luo et al. (2006)* fitting coefficients for the *CAM02-LCD*, *CAM02-SCD*, and 

121*CAM02-UCS* colourspaces. 

122""" 

123 

124 

125def JMh_CIECAM02_to_UCS_Luo2006( 

126 JMh: Domain100_100_360, coefficients: Coefficients_UCS_Luo2006 

127) -> Range100: 

128 """ 

129 Convert from *CIECAM02* :math:`JMh` correlates array to one of the 

130 *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* 

131 colourspaces :math:`J'a'b'` array. 

132 

133 The :math:`JMh` correlates array is constructed using the *CIECAM02* 

134 correlate of *Lightness* :math:`J`, the *CIECAM02* correlate of 

135 *colourfulness* :math:`M` and the *CIECAM02* *Hue* angle :math:`h` 

136 in degrees. 

137 

138 Parameters 

139 ---------- 

140 JMh 

141 *CIECAM02* correlates array :math:`JMh`. 

142 coefficients 

143 Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, 

144 *CAM02-SCD*, or *CAM02-UCS* colourspaces. 

145 

146 Returns 

147 ------- 

148 :class:`numpy.ndarray` 

149 *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* 

150 colourspaces :math:`J'a'b'` array. 

151 

152 Notes 

153 ----- 

154 +------------+------------------------+------------------+ 

155 | **Domain** | **Scale - Reference** | **Scale - 1** | 

156 +============+========================+==================+ 

157 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

158 | | | | 

159 | | ``M`` : 100 | ``M`` : 1 | 

160 | | | | 

161 | | ``h`` : 360 | ``h`` : 1 | 

162 +------------+------------------------+------------------+ 

163 

164 +------------+------------------------+------------------+ 

165 | **Range** | **Scale - Reference** | **Scale - 1** | 

166 +============+========================+==================+ 

167 | ``Jpapbp`` | 100 | 1 | 

168 +------------+------------------------+------------------+ 

169 

170 Examples 

171 -------- 

172 >>> from colour.appearance import ( 

173 ... VIEWING_CONDITIONS_CIECAM02, 

174 ... XYZ_to_CIECAM02, 

175 ... ) 

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

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

178 >>> L_A = 318.31 

179 >>> Y_b = 20.0 

180 >>> surround = VIEWING_CONDITIONS_CIECAM02["Average"] 

181 >>> specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) 

182 >>> JMh = (specification.J, specification.M, specification.h) 

183 >>> JMh_CIECAM02_to_UCS_Luo2006(JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) 

184 ... # doctest: +ELLIPSIS 

185 array([ 54.9043313..., -0.0845039..., -0.0685483...]) 

186 """ 

187 

188 J, M, h = tsplit(JMh) 

189 J = to_domain_100(J) 

190 M = to_domain_100(M) 

191 h = to_domain_degrees(h) 

192 

193 _K_L, c_1, c_2 = coefficients.values 

194 

195 J_p = ((1 + 100 * c_1) * J) / (1 + c_1 * J) 

196 M_p = (1 / c_2) * np.log1p(c_2 * M) 

197 

198 a_p, b_p = tsplit(polar_to_cartesian(tstack([M_p, np.radians(h)]))) 

199 

200 Jpapbp = tstack([J_p, a_p, b_p]) 

201 

202 return from_range_100(Jpapbp) 

203 

204 

205def UCS_Luo2006_to_JMh_CIECAM02( 

206 Jpapbp: Domain100, coefficients: Coefficients_UCS_Luo2006 

207) -> Range100_100_360: 

208 """ 

209 Convert from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, 

210 or *CAM02-UCS* colourspaces :math:`J'a'b'` array to *CIECAM02* 

211 :math:`JMh` correlates array. 

212 

213 Parameters 

214 ---------- 

215 Jpapbp 

216 *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* 

217 colourspaces :math:`J'a'b'` array. 

218 coefficients 

219 Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, 

220 *CAM02-SCD*, or *CAM02-UCS* colourspaces. 

221 

222 Returns 

223 ------- 

224 :class:`numpy.ndarray` 

225 *CIECAM02* correlates array :math:`JMh`. 

226 

227 Notes 

228 ----- 

229 +------------+------------------------+------------------+ 

230 | **Domain** | **Scale - Reference** | **Scale - 1** | 

231 +============+========================+==================+ 

232 | ``Jpapbp`` | 100 | 1 | 

233 +------------+------------------------+------------------+ 

234 

235 +------------+------------------------+------------------+ 

236 | **Range** | **Scale - Reference** | **Scale - 1** | 

237 +============+========================+==================+ 

238 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

239 | | | | 

240 | | ``M`` : 100 | ``M`` : 1 | 

241 | | | | 

242 | | ``h`` : 360 | ``h`` : 1 | 

243 +------------+------------------------+------------------+ 

244 

245 Examples 

246 -------- 

247 >>> Jpapbp = np.array([54.90433134, -0.08450395, -0.06854831]) 

248 >>> UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) 

249 ... # doctest: +ELLIPSIS 

250 array([ 4.1731091...e+01, 1.0884217...e-01, 2.1904843...e+02]) 

251 """ 

252 

253 J_p, a_p, b_p = tsplit(to_domain_100(Jpapbp)) 

254 _K_L, c_1, c_2 = coefficients.values 

255 

256 J = -J_p / (c_1 * J_p - 1 - 100 * c_1) 

257 

258 M_p, h = tsplit(cartesian_to_polar(tstack([a_p, b_p]))) 

259 

260 M = np.expm1(M_p / (1 / c_2)) / c_2 

261 

262 return tstack( 

263 [ 

264 from_range_100(J), 

265 from_range_100(M), 

266 from_range_degrees(np.degrees(h) % 360), 

267 ] 

268 ) 

269 

270 

271def JMh_CIECAM02_to_CAM02LCD( 

272 JMh: Domain100_100_360, 

273) -> Range100: 

274 """ 

275 Convert from *CIECAM02* :math:`JMh` correlates array to *Luo et al. 

276 (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. 

277 

278 Parameters 

279 ---------- 

280 JMh 

281 *CIECAM02* correlates array :math:`JMh`. 

282 

283 Returns 

284 ------- 

285 :class:`numpy.ndarray` 

286 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. 

287 

288 Notes 

289 ----- 

290 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. 

291 

292 +------------+------------------------+------------------+ 

293 | **Domain** | **Scale - Reference** | **Scale - 1** | 

294 +============+========================+==================+ 

295 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

296 | | | | 

297 | | ``M`` : 100 | ``M`` : 1 | 

298 | | | | 

299 | | ``h`` : 360 | ``h`` : 1 | 

300 +------------+------------------------+------------------+ 

301 

302 +------------+------------------------+------------------+ 

303 | **Range** | **Scale - Reference** | **Scale - 1** | 

304 +============+========================+==================+ 

305 | ``Jpapbp`` | 100 | 1 | 

306 +------------+------------------------+------------------+ 

307 

308 References 

309 ---------- 

310 :cite:`Luo2006b` 

311 

312 Examples 

313 -------- 

314 >>> from colour.appearance import ( 

315 ... VIEWING_CONDITIONS_CIECAM02, 

316 ... XYZ_to_CIECAM02, 

317 ... ) 

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

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

320 >>> L_A = 318.31 

321 >>> Y_b = 20.0 

322 >>> surround = VIEWING_CONDITIONS_CIECAM02["Average"] 

323 >>> specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) 

324 >>> JMh = (specification.J, specification.M, specification.h) 

325 >>> JMh_CIECAM02_to_CAM02LCD(JMh) # doctest: +ELLIPSIS 

326 array([ 54.9043313..., -0.0845039..., -0.0685483...]) 

327 """ 

328 

329 return JMh_CIECAM02_to_UCS_Luo2006( 

330 JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] 

331 ) 

332 

333 

334def CAM02LCD_to_JMh_CIECAM02( 

335 Jpapbp: Domain100, 

336) -> Range100_100_360: 

337 """ 

338 Convert from *Luo et al. (2006)* *CAM02-LCD* colourspace 

339 :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. 

340 

341 Parameters 

342 ---------- 

343 Jpapbp 

344 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. 

345 

346 Returns 

347 ------- 

348 :class:`numpy.ndarray` 

349 *CIECAM02* correlates array :math:`JMh`. 

350 

351 Notes 

352 ----- 

353 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. 

354 

355 +------------+------------------------+------------------+ 

356 | **Domain** | **Scale - Reference** | **Scale - 1** | 

357 +============+========================+==================+ 

358 | ``Jpapbp`` | 100 | 1 | 

359 +------------+------------------------+------------------+ 

360 

361 +------------+------------------------+------------------+ 

362 | **Range** | **Scale - Reference** | **Scale - 1** | 

363 +============+========================+==================+ 

364 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

365 | | | | 

366 | | ``M`` : 100 | ``M`` : 1 | 

367 | | | | 

368 | | ``h`` : 360 | ``h`` : 1 | 

369 +------------+------------------------+------------------+ 

370 

371 References 

372 ---------- 

373 :cite:`Luo2006b` 

374 

375 Examples 

376 -------- 

377 >>> Jpapbp = np.array([54.90433134, -0.08450395, -0.06854831]) 

378 >>> CAM02LCD_to_JMh_CIECAM02(Jpapbp) # doctest: +ELLIPSIS 

379 array([ 4.1731091...e+01, 1.0884217...e-01, 2.1904843...e+02]) 

380 """ 

381 

382 return UCS_Luo2006_to_JMh_CIECAM02( 

383 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] 

384 ) 

385 

386 

387def JMh_CIECAM02_to_CAM02SCD( 

388 JMh: Domain100_100_360, 

389) -> Range100: 

390 """ 

391 Convert from *CIECAM02* :math:`JMh` correlates array to *Luo et al. 

392 (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. 

393 

394 Parameters 

395 ---------- 

396 JMh 

397 *CIECAM02* correlates array :math:`JMh`. 

398 

399 Returns 

400 ------- 

401 :class:`numpy.ndarray` 

402 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. 

403 

404 Notes 

405 ----- 

406 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. 

407 

408 +------------+------------------------+------------------+ 

409 | **Domain** | **Scale - Reference** | **Scale - 1** | 

410 +============+========================+==================+ 

411 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

412 | | | | 

413 | | ``M`` : 100 | ``M`` : 1 | 

414 | | | | 

415 | | ``h`` : 360 | ``h`` : 1 | 

416 +------------+------------------------+------------------+ 

417 

418 +------------+------------------------+------------------+ 

419 | **Range** | **Scale - Reference** | **Scale - 1** | 

420 +============+========================+==================+ 

421 | ``Jpapbp`` | 100 | 1 | 

422 +------------+------------------------+------------------+ 

423 

424 References 

425 ---------- 

426 :cite:`Luo2006b` 

427 

428 Examples 

429 -------- 

430 >>> from colour.appearance import ( 

431 ... VIEWING_CONDITIONS_CIECAM02, 

432 ... XYZ_to_CIECAM02, 

433 ... ) 

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

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

436 >>> L_A = 318.31 

437 >>> Y_b = 20.0 

438 >>> surround = VIEWING_CONDITIONS_CIECAM02["Average"] 

439 >>> specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) 

440 >>> JMh = (specification.J, specification.M, specification.h) 

441 >>> JMh_CIECAM02_to_CAM02SCD(JMh) # doctest: +ELLIPSIS 

442 array([ 54.9043313..., -0.0843617..., -0.0684329...]) 

443 """ 

444 

445 return JMh_CIECAM02_to_UCS_Luo2006( 

446 JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] 

447 ) 

448 

449 

450def CAM02SCD_to_JMh_CIECAM02( 

451 Jpapbp: Domain100, 

452) -> Range100_100_360: 

453 """ 

454 Convert from *Luo et al. (2006)* *CAM02-SCD* colourspace 

455 :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. 

456 

457 Parameters 

458 ---------- 

459 Jpapbp 

460 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` 

461 array. 

462 

463 Returns 

464 ------- 

465 :class:`numpy.ndarray` 

466 *CIECAM02* correlates array :math:`JMh`. 

467 

468 Notes 

469 ----- 

470 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. 

471 

472 +------------+------------------------+------------------+ 

473 | **Domain** | **Scale - Reference** | **Scale - 1** | 

474 +============+========================+==================+ 

475 | ``Jpapbp`` | 100 | 1 | 

476 +------------+------------------------+------------------+ 

477 

478 +------------+------------------------+------------------+ 

479 | **Range** | **Scale - Reference** | **Scale - 1** | 

480 +============+========================+==================+ 

481 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

482 | | | | 

483 | | ``M`` : 100 | ``M`` : 1 | 

484 | | | | 

485 | | ``h`` : 360 | ``h`` : 1 | 

486 +------------+------------------------+------------------+ 

487 

488 References 

489 ---------- 

490 :cite:`Luo2006b` 

491 

492 Examples 

493 -------- 

494 >>> Jpapbp = np.array([54.90433134, -0.08436178, -0.06843298]) 

495 >>> CAM02SCD_to_JMh_CIECAM02(Jpapbp) # doctest: +ELLIPSIS 

496 array([ 4.1731091...e+01, 1.0884217...e-01, 2.1904843...e+02]) 

497 """ 

498 

499 return UCS_Luo2006_to_JMh_CIECAM02( 

500 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] 

501 ) 

502 

503 

504def JMh_CIECAM02_to_CAM02UCS( 

505 JMh: Domain100_100_360, 

506) -> Range100: 

507 """ 

508 Convert from *CIECAM02* :math:`JMh` correlates array to *Luo et al. 

509 (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. 

510 

511 Parameters 

512 ---------- 

513 JMh 

514 *CIECAM02* correlates array :math:`JMh`. 

515 

516 Returns 

517 ------- 

518 :class:`numpy.ndarray` 

519 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. 

520 

521 Notes 

522 ----- 

523 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*. 

524 

525 +------------+------------------------+------------------+ 

526 | **Domain** | **Scale - Reference** | **Scale - 1** | 

527 +============+========================+==================+ 

528 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

529 | | | | 

530 | | ``M`` : 100 | ``M`` : 1 | 

531 | | | | 

532 | | ``h`` : 360 | ``h`` : 1 | 

533 +------------+------------------------+------------------+ 

534 

535 +------------+------------------------+------------------+ 

536 | **Range** | **Scale - Reference** | **Scale - 1** | 

537 +============+========================+==================+ 

538 | ``Jpapbp`` | 100 | 1 | 

539 +------------+------------------------+------------------+ 

540 

541 References 

542 ---------- 

543 :cite:`Luo2006b` 

544 

545 Examples 

546 -------- 

547 >>> from colour.appearance import ( 

548 ... VIEWING_CONDITIONS_CIECAM02, 

549 ... XYZ_to_CIECAM02, 

550 ... ) 

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

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

553 >>> L_A = 318.31 

554 >>> Y_b = 20.0 

555 >>> surround = VIEWING_CONDITIONS_CIECAM02["Average"] 

556 >>> specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) 

557 >>> JMh = (specification.J, specification.M, specification.h) 

558 >>> JMh_CIECAM02_to_CAM02UCS(JMh) # doctest: +ELLIPSIS 

559 array([ 54.9043313..., -0.0844236..., -0.0684831...]) 

560 """ 

561 

562 return JMh_CIECAM02_to_UCS_Luo2006( 

563 JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] 

564 ) 

565 

566 

567def CAM02UCS_to_JMh_CIECAM02( 

568 Jpapbp: Domain100, 

569) -> Range100_100_360: 

570 """ 

571 Convert from *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` 

572 array to *CIECAM02* :math:`JMh` correlates array. 

573 

574 Parameters 

575 ---------- 

576 Jpapbp 

577 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. 

578 

579 Returns 

580 ------- 

581 :class:`numpy.ndarray` 

582 *CIECAM02* correlates array :math:`JMh`. 

583 

584 Notes 

585 ----- 

586 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*. 

587 

588 +------------+------------------------+------------------+ 

589 | **Domain** | **Scale - Reference** | **Scale - 1** | 

590 +============+========================+==================+ 

591 | ``Jpapbp`` | 100 | 1 | 

592 +------------+------------------------+------------------+ 

593 

594 +------------+------------------------+------------------+ 

595 | **Range** | **Scale - Reference** | **Scale - 1** | 

596 +============+========================+==================+ 

597 | ``JMh`` | ``J`` : 100 | ``J`` : 1 | 

598 | | | | 

599 | | ``M`` : 100 | ``M`` : 1 | 

600 | | | | 

601 | | ``h`` : 360 | ``h`` : 1 | 

602 +------------+------------------------+------------------+ 

603 

604 References 

605 ---------- 

606 :cite:`Luo2006b` 

607 

608 Examples 

609 -------- 

610 >>> Jpapbp = np.array([54.90433134, -0.08442362, -0.06848314]) 

611 >>> CAM02UCS_to_JMh_CIECAM02(Jpapbp) # doctest: +ELLIPSIS 

612 array([ 4.1731091...e+01, 1.0884217...e-01, 2.1904843...e+02]) 

613 """ 

614 

615 return UCS_Luo2006_to_JMh_CIECAM02( 

616 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] 

617 ) 

618 

619 

620def XYZ_to_UCS_Luo2006( 

621 XYZ: Domain1, 

622 coefficients: Coefficients_UCS_Luo2006, 

623 **kwargs: Any, 

624) -> Range100: 

625 """ 

626 Convert from *CIE XYZ* tristimulus values to one of the 

627 *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* 

628 colourspaces :math:`J'a'b'` array. 

629 

630 Parameters 

631 ---------- 

632 XYZ 

633 *CIE XYZ* tristimulus values. 

634 coefficients 

635 Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, 

636 *CAM02-SCD*, or *CAM02-UCS* colourspaces. 

637 

638 Other Parameters 

639 ---------------- 

640 kwargs 

641 {:func:`colour.XYZ_to_CIECAM02`}, 

642 See the documentation of the previously listed definition. The 

643 default viewing conditions are those of *IEC 61966-2-1:1999*, 

644 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

645 adapting field luminance about 20% of a white object in the 

646 scene. 

647 

648 Returns 

649 ------- 

650 :class:`numpy.ndarray` 

651 *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* 

652 colourspaces :math:`J'a'b'` array. 

653 

654 Warnings 

655 -------- 

656 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must 

657 be specified in the same domain-range scale than the ``XYZ`` parameter. 

658 

659 Notes 

660 ----- 

661 +------------+------------------------+------------------+ 

662 | **Domain** | **Scale - Reference** | **Scale - 1** | 

663 +============+========================+==================+ 

664 | ``XYZ`` | 1 | 1 | 

665 +------------+------------------------+------------------+ 

666 

667 +------------+------------------------+------------------+ 

668 | **Range** | **Scale - Reference** | **Scale - 1** | 

669 +============+========================+==================+ 

670 | ``Jpapbp`` | 100 | 1 | 

671 +------------+------------------------+------------------+ 

672 

673 Examples 

674 -------- 

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

676 >>> XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) 

677 ... # doctest: +ELLIPSIS 

678 array([ 46.6138615..., 39.3576023..., 15.9673043...]) 

679 """ 

680 

681 from colour.appearance import ( # noqa: PLC0415 

682 CAM_KWARGS_CIECAM02_sRGB, 

683 XYZ_to_CIECAM02, 

684 ) 

685 

686 domain_range_reference = get_domain_range_scale() == "reference" 

687 

688 settings = CAM_KWARGS_CIECAM02_sRGB.copy() 

689 settings.update(**kwargs) 

690 XYZ_w = kwargs.get("XYZ_w") 

691 

692 if XYZ_w is not None and domain_range_reference: 

693 settings["XYZ_w"] = as_float_array(XYZ_w) * 100 

694 

695 if domain_range_reference: 

696 XYZ = as_float_array(XYZ) * 100 

697 

698 specification = XYZ_to_CIECAM02(XYZ, **settings) 

699 JMh = tstack( 

700 [ 

701 cast("NDArrayFloat", specification.J), 

702 cast("NDArrayFloat", specification.M), 

703 cast("NDArrayFloat", specification.h), 

704 ] 

705 ) 

706 

707 return JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients) 

708 

709 

710def UCS_Luo2006_to_XYZ( 

711 Jpapbp: Domain100, 

712 coefficients: Coefficients_UCS_Luo2006, 

713 **kwargs: Any, 

714) -> Range1: 

715 """ 

716 Convert from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or 

717 *CAM02-UCS* colourspaces :math:`J'a'b'` array to *CIE XYZ* tristimulus 

718 values. 

719 

720 Parameters 

721 ---------- 

722 Jpapbp 

723 *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* 

724 colourspaces :math:`J'a'b'` array. 

725 coefficients 

726 Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, 

727 *CAM02-SCD*, or *CAM02-UCS* colourspaces. 

728 

729 Other Parameters 

730 ---------------- 

731 kwargs 

732 {:func:`colour.CIECAM02_to_XYZ`}, 

733 See the documentation of the previously listed definition. The 

734 default viewing conditions are those of *IEC 61966-2-1:1999*, 

735 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

736 adapting field luminance about 20% of a white object in the 

737 scene. 

738 

739 Returns 

740 ------- 

741 :class:`numpy.ndarray` 

742 *CIE XYZ* tristimulus values. 

743 

744 Warnings 

745 -------- 

746 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must 

747 be specified in the same domain-range scale as the ``XYZ`` parameter. 

748 

749 Notes 

750 ----- 

751 +------------+------------------------+------------------+ 

752 | **Domain** | **Scale - Reference** | **Scale - 1** | 

753 +============+========================+==================+ 

754 | ``Jpapbp`` | 100 | 1 | 

755 +------------+------------------------+------------------+ 

756 

757 +------------+------------------------+------------------+ 

758 | **Range** | **Scale - Reference** | **Scale - 1** | 

759 +============+========================+==================+ 

760 | ``XYZ`` | 1 | 1 | 

761 +------------+------------------------+------------------+ 

762 

763 Examples 

764 -------- 

765 >>> Jpapbp = np.array([46.61386154, 39.35760236, 15.96730435]) 

766 >>> UCS_Luo2006_to_XYZ(Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) 

767 ... # doctest: +ELLIPSIS 

768 array([ 0.2065400..., 0.1219722..., 0.0513695...]) 

769 """ 

770 

771 from colour.appearance import ( # noqa: PLC0415 

772 CAM_KWARGS_CIECAM02_sRGB, 

773 CAM_Specification_CIECAM02, 

774 CIECAM02_to_XYZ, 

775 ) 

776 

777 domain_range_reference = get_domain_range_scale() == "reference" 

778 

779 settings = CAM_KWARGS_CIECAM02_sRGB.copy() 

780 settings.update(**kwargs) 

781 XYZ_w = kwargs.get("XYZ_w") 

782 

783 if XYZ_w is not None and domain_range_reference: 

784 settings["XYZ_w"] = as_float_array(XYZ_w) * 100 

785 

786 J, M, h = tsplit(UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients)) 

787 

788 specification = CAM_Specification_CIECAM02(J=J, M=M, h=h) 

789 

790 XYZ = CIECAM02_to_XYZ(specification, **settings) 

791 

792 if domain_range_reference: 

793 XYZ /= 100 

794 

795 return XYZ 

796 

797 

798def XYZ_to_CAM02LCD(XYZ: Domain1, **kwargs: Any) -> Range100: 

799 """ 

800 Convert from *CIE XYZ* tristimulus values to *Luo et al. (2006)* 

801 *CAM02-LCD* colourspace :math:`J'a'b'` array. 

802 

803 Parameters 

804 ---------- 

805 XYZ 

806 *CIE XYZ* tristimulus values. 

807 

808 Other Parameters 

809 ---------------- 

810 kwargs 

811 {:func:`colour.XYZ_to_CIECAM02`}, 

812 See the documentation of the previously listed definition. The 

813 default viewing conditions are those of *IEC 61966-2-1:1999*, 

814 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

815 adapting field luminance about 20% of a white object in the 

816 scene. 

817 

818 Returns 

819 ------- 

820 :class:`numpy.ndarray` 

821 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` 

822 array. 

823 

824 Warnings 

825 -------- 

826 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition 

827 must be specified in the same domain-range scale as the ``XYZ`` 

828 parameter. 

829 

830 Notes 

831 ----- 

832 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. 

833 

834 +------------+------------------------+------------------+ 

835 | **Domain** | **Scale - Reference** | **Scale - 1** | 

836 +============+========================+==================+ 

837 | ``XYZ`` | 1 | 1 | 

838 +------------+------------------------+------------------+ 

839 

840 +------------+------------------------+------------------+ 

841 | **Range** | **Scale - Reference** | **Scale - 1** | 

842 +============+========================+==================+ 

843 | ``Jpapbp`` | 100 | 1 | 

844 +------------+------------------------+------------------+ 

845 

846 References 

847 ---------- 

848 :cite:`Luo2006b` 

849 

850 Examples 

851 -------- 

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

853 >>> XYZ_to_CAM02LCD(XYZ) # doctest: +ELLIPSIS 

854 array([ 46.6138615..., 39.3576023..., 15.9673043...]) 

855 """ 

856 

857 return XYZ_to_UCS_Luo2006( 

858 XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], **kwargs 

859 ) 

860 

861 

862def CAM02LCD_to_XYZ(Jpapbp: Domain100, **kwargs: Any) -> Range1: 

863 """ 

864 Convert from *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` 

865 array to *CIE XYZ* tristimulus values. 

866 

867 Parameters 

868 ---------- 

869 Jpapbp 

870 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. 

871 

872 Other Parameters 

873 ---------------- 

874 kwargs 

875 {:func:`colour.CIECAM02_to_XYZ`}, 

876 See the documentation of the previously listed definition. The 

877 default viewing conditions are those of *IEC 61966-2-1:1999*, 

878 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

879 adapting field luminance about 20% of a white object in the 

880 scene. 

881 

882 Returns 

883 ------- 

884 :class:`numpy.ndarray` 

885 *CIE XYZ* tristimulus values. 

886 

887 Warnings 

888 -------- 

889 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must 

890 be specified in the same domain-range scale as the ``XYZ`` parameter. 

891 

892 Notes 

893 ----- 

894 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. 

895 

896 +------------+------------------------+------------------+ 

897 | **Domain** | **Scale - Reference** | **Scale - 1** | 

898 +============+========================+==================+ 

899 | ``Jpapbp`` | 100 | 1 | 

900 +------------+------------------------+------------------+ 

901 

902 +------------+------------------------+------------------+ 

903 | **Range** | **Scale - Reference** | **Scale - 1** | 

904 +============+========================+==================+ 

905 | ``XYZ`` | 1 | 1 | 

906 +------------+------------------------+------------------+ 

907 

908 References 

909 ---------- 

910 :cite:`Luo2006b` 

911 

912 Examples 

913 -------- 

914 >>> Jpapbp = np.array([46.61386154, 39.35760236, 15.96730435]) 

915 >>> CAM02LCD_to_XYZ(Jpapbp) # doctest: +ELLIPSIS 

916 array([ 0.2065400..., 0.1219722..., 0.0513695...]) 

917 """ 

918 

919 return UCS_Luo2006_to_XYZ( 

920 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], **kwargs 

921 ) 

922 

923 

924def XYZ_to_CAM02SCD(XYZ: Domain1, **kwargs: Any) -> Range100: 

925 """ 

926 Convert from *CIE XYZ* tristimulus values to *Luo et al. (2006)* 

927 *CAM02-SCD* colourspace :math:`J'a'b'` array. 

928 

929 Parameters 

930 ---------- 

931 XYZ 

932 *CIE XYZ* tristimulus values. 

933 

934 Other Parameters 

935 ---------------- 

936 kwargs 

937 {:func:`colour.XYZ_to_CIECAM02`}, 

938 See the documentation of the previously listed definition. The 

939 default viewing conditions are those of *IEC 61966-2-1:1999*, 

940 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

941 adapting field luminance about 20% of a white object in the 

942 scene. 

943 

944 Returns 

945 ------- 

946 :class:`numpy.ndarray` 

947 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` 

948 array. 

949 

950 Warnings 

951 -------- 

952 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition 

953 must be specified in the same domain-range scale as the ``XYZ`` 

954 parameter. 

955 

956 Notes 

957 ----- 

958 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. 

959 

960 +------------+------------------------+------------------+ 

961 | **Domain** | **Scale - Reference** | **Scale - 1** | 

962 +============+========================+==================+ 

963 | ``XYZ`` | 1 | 1 | 

964 +------------+------------------------+------------------+ 

965 

966 +------------+------------------------+------------------+ 

967 | **Range** | **Scale - Reference** | **Scale - 1** | 

968 +============+========================+==================+ 

969 | ``Jpapbp`` | 100 | 1 | 

970 +------------+------------------------+------------------+ 

971 

972 References 

973 ---------- 

974 :cite:`Luo2006b` 

975 

976 Examples 

977 -------- 

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

979 >>> XYZ_to_CAM02SCD(XYZ) # doctest: +ELLIPSIS 

980 array([ 46.6138615..., 25.6287988..., 10.3975548...]) 

981 """ 

982 

983 return XYZ_to_UCS_Luo2006( 

984 XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], **kwargs 

985 ) 

986 

987 

988def CAM02SCD_to_XYZ(Jpapbp: Domain100, **kwargs: Any) -> Range1: 

989 """ 

990 Convert from *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` 

991 array to *CIE XYZ* tristimulus values. 

992 

993 Parameters 

994 ---------- 

995 Jpapbp 

996 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. 

997 

998 Other Parameters 

999 ---------------- 

1000 kwargs 

1001 {:func:`colour.CIECAM02_to_XYZ`}, 

1002 See the documentation of the previously listed definition. The 

1003 default viewing conditions are those of *IEC 61966-2-1:1999*, 

1004 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

1005 adapting field luminance about 20% of a white object in the 

1006 scene. 

1007 

1008 Returns 

1009 ------- 

1010 :class:`numpy.ndarray` 

1011 *CIE XYZ* tristimulus values. 

1012 

1013 Warnings 

1014 -------- 

1015 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be 

1016 specified in the same domain-range scale as the ``XYZ`` parameter. 

1017 

1018 Notes 

1019 ----- 

1020 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. 

1021 

1022 +------------+------------------------+------------------+ 

1023 | **Domain** | **Scale - Reference** | **Scale - 1** | 

1024 +============+========================+==================+ 

1025 | ``Jpapbp`` | 100 | 1 | 

1026 +------------+------------------------+------------------+ 

1027 

1028 +------------+------------------------+------------------+ 

1029 | **Range** | **Scale - Reference** | **Scale - 1** | 

1030 +============+========================+==================+ 

1031 | ``XYZ`` | 1 | 1 | 

1032 +------------+------------------------+------------------+ 

1033 

1034 References 

1035 ---------- 

1036 :cite:`Luo2006b` 

1037 

1038 Examples 

1039 -------- 

1040 >>> Jpapbp = np.array([46.61386154, 25.62879882, 10.39755489]) 

1041 >>> CAM02SCD_to_XYZ(Jpapbp) # doctest: +ELLIPSIS 

1042 array([ 0.2065400..., 0.1219722..., 0.0513695...]) 

1043 """ 

1044 

1045 return UCS_Luo2006_to_XYZ( 

1046 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], **kwargs 

1047 ) 

1048 

1049 

1050def XYZ_to_CAM02UCS(XYZ: Domain1, **kwargs: Any) -> Range100: 

1051 """ 

1052 Convert from *CIE XYZ* tristimulus values to *Luo et al. (2006)* 

1053 *CAM02-UCS* colourspace :math:`J'a'b'` array. 

1054 

1055 Parameters 

1056 ---------- 

1057 XYZ 

1058 *CIE XYZ* tristimulus values. 

1059 

1060 Other Parameters 

1061 ---------------- 

1062 kwargs 

1063 {:func:`colour.XYZ_to_CIECAM02`}, 

1064 See the documentation of the previously listed definition. The 

1065 default viewing conditions are those of *IEC 61966-2-1:1999*, 

1066 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

1067 adapting field luminance about 20% of a white object in the 

1068 scene. 

1069 

1070 Returns 

1071 ------- 

1072 :class:`numpy.ndarray` 

1073 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` 

1074 array. 

1075 

1076 Warnings 

1077 -------- 

1078 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition 

1079 must be specified in the same domain-range scale as the ``XYZ`` 

1080 parameter. 

1081 

1082 Notes 

1083 ----- 

1084 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*. 

1085 

1086 +------------+------------------------+------------------+ 

1087 | **Domain** | **Scale - Reference** | **Scale - 1** | 

1088 +============+========================+==================+ 

1089 | ``XYZ`` | 1 | 1 | 

1090 +------------+------------------------+------------------+ 

1091 

1092 +------------+------------------------+------------------+ 

1093 | **Range** | **Scale - Reference** | **Scale - 1** | 

1094 +============+========================+==================+ 

1095 | ``Jpapbp`` | 100 | 1 | 

1096 +------------+------------------------+------------------+ 

1097 

1098 References 

1099 ---------- 

1100 :cite:`Luo2006b` 

1101 

1102 Examples 

1103 -------- 

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

1105 >>> XYZ_to_CAM02UCS(XYZ) # doctest: +ELLIPSIS 

1106 array([ 46.6138615..., 29.8831001..., 12.1235168...]) 

1107 """ 

1108 

1109 return XYZ_to_UCS_Luo2006( 

1110 XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], **kwargs 

1111 ) 

1112 

1113 

1114def CAM02UCS_to_XYZ(Jpapbp: Domain100, **kwargs: Any) -> Range1: 

1115 """ 

1116 Convert from *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` 

1117 array to *CIE XYZ* tristimulus values. 

1118 

1119 Parameters 

1120 ---------- 

1121 Jpapbp 

1122 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. 

1123 

1124 Other Parameters 

1125 ---------------- 

1126 kwargs 

1127 {:func:`colour.CIECAM02_to_XYZ`}, 

1128 See the documentation of the previously listed definition. The 

1129 default viewing conditions are those of *IEC 61966-2-1:1999*, 

1130 i.e., *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, 

1131 adapting field luminance about 20% of a white object in the 

1132 scene. 

1133 

1134 Returns 

1135 ------- 

1136 :class:`numpy.ndarray` 

1137 *CIE XYZ* tristimulus values. 

1138 

1139 Warnings 

1140 -------- 

1141 The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must 

1142 be specified in the same domain-range scale as the ``XYZ`` parameter. 

1143 

1144 Notes 

1145 ----- 

1146 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*. 

1147 

1148 +------------+------------------------+------------------+ 

1149 | **Domain** | **Scale - Reference** | **Scale - 1** | 

1150 +============+========================+==================+ 

1151 | ``Jpapbp`` | 100 | 1 | 

1152 +------------+------------------------+------------------+ 

1153 

1154 +------------+------------------------+------------------+ 

1155 | **Range** | **Scale - Reference** | **Scale - 1** | 

1156 +============+========================+==================+ 

1157 | ``XYZ`` | 1 | 1 | 

1158 +------------+------------------------+------------------+ 

1159 

1160 References 

1161 ---------- 

1162 :cite:`Luo2006b` 

1163 

1164 Examples 

1165 -------- 

1166 >>> Jpapbp = np.array([46.61386154, 29.88310013, 12.12351683]) 

1167 >>> CAM02UCS_to_XYZ(Jpapbp) # doctest: +ELLIPSIS 

1168 array([ 0.2065400..., 0.1219722..., 0.0513695...]) 

1169 """ 

1170 

1171 return UCS_Luo2006_to_XYZ( 

1172 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], **kwargs 

1173 )