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
« 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=========================================================================
5Define the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, and *CAM02-UCS*
6colourspaces transformations.
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`
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"""
28from __future__ import annotations
30import typing
31from dataclasses import dataclass
33import numpy as np
35from colour.algebra import cartesian_to_polar, polar_to_cartesian
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 )
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)
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"
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]
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.
97 Attributes
98 ----------
99 K_L
100 Lightness coefficient.
101 c_1
102 First chroma coefficient.
103 c_2
104 Second chroma coefficient.
105 """
107 K_L: float
108 c_1: float
109 c_2: float
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"""
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.
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.
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.
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.
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 +------------+------------------------+------------------+
164 +------------+------------------------+------------------+
165 | **Range** | **Scale - Reference** | **Scale - 1** |
166 +============+========================+==================+
167 | ``Jpapbp`` | 100 | 1 |
168 +------------+------------------------+------------------+
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 """
188 J, M, h = tsplit(JMh)
189 J = to_domain_100(J)
190 M = to_domain_100(M)
191 h = to_domain_degrees(h)
193 _K_L, c_1, c_2 = coefficients.values
195 J_p = ((1 + 100 * c_1) * J) / (1 + c_1 * J)
196 M_p = (1 / c_2) * np.log1p(c_2 * M)
198 a_p, b_p = tsplit(polar_to_cartesian(tstack([M_p, np.radians(h)])))
200 Jpapbp = tstack([J_p, a_p, b_p])
202 return from_range_100(Jpapbp)
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.
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.
222 Returns
223 -------
224 :class:`numpy.ndarray`
225 *CIECAM02* correlates array :math:`JMh`.
227 Notes
228 -----
229 +------------+------------------------+------------------+
230 | **Domain** | **Scale - Reference** | **Scale - 1** |
231 +============+========================+==================+
232 | ``Jpapbp`` | 100 | 1 |
233 +------------+------------------------+------------------+
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 +------------+------------------------+------------------+
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 """
253 J_p, a_p, b_p = tsplit(to_domain_100(Jpapbp))
254 _K_L, c_1, c_2 = coefficients.values
256 J = -J_p / (c_1 * J_p - 1 - 100 * c_1)
258 M_p, h = tsplit(cartesian_to_polar(tstack([a_p, b_p])))
260 M = np.expm1(M_p / (1 / c_2)) / c_2
262 return tstack(
263 [
264 from_range_100(J),
265 from_range_100(M),
266 from_range_degrees(np.degrees(h) % 360),
267 ]
268 )
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.
278 Parameters
279 ----------
280 JMh
281 *CIECAM02* correlates array :math:`JMh`.
283 Returns
284 -------
285 :class:`numpy.ndarray`
286 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array.
288 Notes
289 -----
290 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*.
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 +------------+------------------------+------------------+
302 +------------+------------------------+------------------+
303 | **Range** | **Scale - Reference** | **Scale - 1** |
304 +============+========================+==================+
305 | ``Jpapbp`` | 100 | 1 |
306 +------------+------------------------+------------------+
308 References
309 ----------
310 :cite:`Luo2006b`
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 """
329 return JMh_CIECAM02_to_UCS_Luo2006(
330 JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]
331 )
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.
341 Parameters
342 ----------
343 Jpapbp
344 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array.
346 Returns
347 -------
348 :class:`numpy.ndarray`
349 *CIECAM02* correlates array :math:`JMh`.
351 Notes
352 -----
353 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*.
355 +------------+------------------------+------------------+
356 | **Domain** | **Scale - Reference** | **Scale - 1** |
357 +============+========================+==================+
358 | ``Jpapbp`` | 100 | 1 |
359 +------------+------------------------+------------------+
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 +------------+------------------------+------------------+
371 References
372 ----------
373 :cite:`Luo2006b`
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 """
382 return UCS_Luo2006_to_JMh_CIECAM02(
383 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]
384 )
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.
394 Parameters
395 ----------
396 JMh
397 *CIECAM02* correlates array :math:`JMh`.
399 Returns
400 -------
401 :class:`numpy.ndarray`
402 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array.
404 Notes
405 -----
406 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*.
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 +------------+------------------------+------------------+
418 +------------+------------------------+------------------+
419 | **Range** | **Scale - Reference** | **Scale - 1** |
420 +============+========================+==================+
421 | ``Jpapbp`` | 100 | 1 |
422 +------------+------------------------+------------------+
424 References
425 ----------
426 :cite:`Luo2006b`
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 """
445 return JMh_CIECAM02_to_UCS_Luo2006(
446 JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"]
447 )
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.
457 Parameters
458 ----------
459 Jpapbp
460 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'`
461 array.
463 Returns
464 -------
465 :class:`numpy.ndarray`
466 *CIECAM02* correlates array :math:`JMh`.
468 Notes
469 -----
470 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*.
472 +------------+------------------------+------------------+
473 | **Domain** | **Scale - Reference** | **Scale - 1** |
474 +============+========================+==================+
475 | ``Jpapbp`` | 100 | 1 |
476 +------------+------------------------+------------------+
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 +------------+------------------------+------------------+
488 References
489 ----------
490 :cite:`Luo2006b`
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 """
499 return UCS_Luo2006_to_JMh_CIECAM02(
500 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"]
501 )
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.
511 Parameters
512 ----------
513 JMh
514 *CIECAM02* correlates array :math:`JMh`.
516 Returns
517 -------
518 :class:`numpy.ndarray`
519 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array.
521 Notes
522 -----
523 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*.
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 +------------+------------------------+------------------+
535 +------------+------------------------+------------------+
536 | **Range** | **Scale - Reference** | **Scale - 1** |
537 +============+========================+==================+
538 | ``Jpapbp`` | 100 | 1 |
539 +------------+------------------------+------------------+
541 References
542 ----------
543 :cite:`Luo2006b`
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 """
562 return JMh_CIECAM02_to_UCS_Luo2006(
563 JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"]
564 )
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.
574 Parameters
575 ----------
576 Jpapbp
577 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array.
579 Returns
580 -------
581 :class:`numpy.ndarray`
582 *CIECAM02* correlates array :math:`JMh`.
584 Notes
585 -----
586 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*.
588 +------------+------------------------+------------------+
589 | **Domain** | **Scale - Reference** | **Scale - 1** |
590 +============+========================+==================+
591 | ``Jpapbp`` | 100 | 1 |
592 +------------+------------------------+------------------+
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 +------------+------------------------+------------------+
604 References
605 ----------
606 :cite:`Luo2006b`
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 """
615 return UCS_Luo2006_to_JMh_CIECAM02(
616 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"]
617 )
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.
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.
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.
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.
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.
659 Notes
660 -----
661 +------------+------------------------+------------------+
662 | **Domain** | **Scale - Reference** | **Scale - 1** |
663 +============+========================+==================+
664 | ``XYZ`` | 1 | 1 |
665 +------------+------------------------+------------------+
667 +------------+------------------------+------------------+
668 | **Range** | **Scale - Reference** | **Scale - 1** |
669 +============+========================+==================+
670 | ``Jpapbp`` | 100 | 1 |
671 +------------+------------------------+------------------+
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 """
681 from colour.appearance import ( # noqa: PLC0415
682 CAM_KWARGS_CIECAM02_sRGB,
683 XYZ_to_CIECAM02,
684 )
686 domain_range_reference = get_domain_range_scale() == "reference"
688 settings = CAM_KWARGS_CIECAM02_sRGB.copy()
689 settings.update(**kwargs)
690 XYZ_w = kwargs.get("XYZ_w")
692 if XYZ_w is not None and domain_range_reference:
693 settings["XYZ_w"] = as_float_array(XYZ_w) * 100
695 if domain_range_reference:
696 XYZ = as_float_array(XYZ) * 100
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 )
707 return JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients)
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.
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.
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.
739 Returns
740 -------
741 :class:`numpy.ndarray`
742 *CIE XYZ* tristimulus values.
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.
749 Notes
750 -----
751 +------------+------------------------+------------------+
752 | **Domain** | **Scale - Reference** | **Scale - 1** |
753 +============+========================+==================+
754 | ``Jpapbp`` | 100 | 1 |
755 +------------+------------------------+------------------+
757 +------------+------------------------+------------------+
758 | **Range** | **Scale - Reference** | **Scale - 1** |
759 +============+========================+==================+
760 | ``XYZ`` | 1 | 1 |
761 +------------+------------------------+------------------+
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 """
771 from colour.appearance import ( # noqa: PLC0415
772 CAM_KWARGS_CIECAM02_sRGB,
773 CAM_Specification_CIECAM02,
774 CIECAM02_to_XYZ,
775 )
777 domain_range_reference = get_domain_range_scale() == "reference"
779 settings = CAM_KWARGS_CIECAM02_sRGB.copy()
780 settings.update(**kwargs)
781 XYZ_w = kwargs.get("XYZ_w")
783 if XYZ_w is not None and domain_range_reference:
784 settings["XYZ_w"] = as_float_array(XYZ_w) * 100
786 J, M, h = tsplit(UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients))
788 specification = CAM_Specification_CIECAM02(J=J, M=M, h=h)
790 XYZ = CIECAM02_to_XYZ(specification, **settings)
792 if domain_range_reference:
793 XYZ /= 100
795 return XYZ
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.
803 Parameters
804 ----------
805 XYZ
806 *CIE XYZ* tristimulus values.
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.
818 Returns
819 -------
820 :class:`numpy.ndarray`
821 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'`
822 array.
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.
830 Notes
831 -----
832 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*.
834 +------------+------------------------+------------------+
835 | **Domain** | **Scale - Reference** | **Scale - 1** |
836 +============+========================+==================+
837 | ``XYZ`` | 1 | 1 |
838 +------------+------------------------+------------------+
840 +------------+------------------------+------------------+
841 | **Range** | **Scale - Reference** | **Scale - 1** |
842 +============+========================+==================+
843 | ``Jpapbp`` | 100 | 1 |
844 +------------+------------------------+------------------+
846 References
847 ----------
848 :cite:`Luo2006b`
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 """
857 return XYZ_to_UCS_Luo2006(
858 XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], **kwargs
859 )
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.
867 Parameters
868 ----------
869 Jpapbp
870 *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array.
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.
882 Returns
883 -------
884 :class:`numpy.ndarray`
885 *CIE XYZ* tristimulus values.
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.
892 Notes
893 -----
894 - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*.
896 +------------+------------------------+------------------+
897 | **Domain** | **Scale - Reference** | **Scale - 1** |
898 +============+========================+==================+
899 | ``Jpapbp`` | 100 | 1 |
900 +------------+------------------------+------------------+
902 +------------+------------------------+------------------+
903 | **Range** | **Scale - Reference** | **Scale - 1** |
904 +============+========================+==================+
905 | ``XYZ`` | 1 | 1 |
906 +------------+------------------------+------------------+
908 References
909 ----------
910 :cite:`Luo2006b`
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 """
919 return UCS_Luo2006_to_XYZ(
920 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], **kwargs
921 )
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.
929 Parameters
930 ----------
931 XYZ
932 *CIE XYZ* tristimulus values.
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.
944 Returns
945 -------
946 :class:`numpy.ndarray`
947 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'`
948 array.
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.
956 Notes
957 -----
958 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*.
960 +------------+------------------------+------------------+
961 | **Domain** | **Scale - Reference** | **Scale - 1** |
962 +============+========================+==================+
963 | ``XYZ`` | 1 | 1 |
964 +------------+------------------------+------------------+
966 +------------+------------------------+------------------+
967 | **Range** | **Scale - Reference** | **Scale - 1** |
968 +============+========================+==================+
969 | ``Jpapbp`` | 100 | 1 |
970 +------------+------------------------+------------------+
972 References
973 ----------
974 :cite:`Luo2006b`
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 """
983 return XYZ_to_UCS_Luo2006(
984 XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], **kwargs
985 )
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.
993 Parameters
994 ----------
995 Jpapbp
996 *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array.
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.
1008 Returns
1009 -------
1010 :class:`numpy.ndarray`
1011 *CIE XYZ* tristimulus values.
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.
1018 Notes
1019 -----
1020 - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*.
1022 +------------+------------------------+------------------+
1023 | **Domain** | **Scale - Reference** | **Scale - 1** |
1024 +============+========================+==================+
1025 | ``Jpapbp`` | 100 | 1 |
1026 +------------+------------------------+------------------+
1028 +------------+------------------------+------------------+
1029 | **Range** | **Scale - Reference** | **Scale - 1** |
1030 +============+========================+==================+
1031 | ``XYZ`` | 1 | 1 |
1032 +------------+------------------------+------------------+
1034 References
1035 ----------
1036 :cite:`Luo2006b`
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 """
1045 return UCS_Luo2006_to_XYZ(
1046 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], **kwargs
1047 )
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.
1055 Parameters
1056 ----------
1057 XYZ
1058 *CIE XYZ* tristimulus values.
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.
1070 Returns
1071 -------
1072 :class:`numpy.ndarray`
1073 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'`
1074 array.
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.
1082 Notes
1083 -----
1084 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*.
1086 +------------+------------------------+------------------+
1087 | **Domain** | **Scale - Reference** | **Scale - 1** |
1088 +============+========================+==================+
1089 | ``XYZ`` | 1 | 1 |
1090 +------------+------------------------+------------------+
1092 +------------+------------------------+------------------+
1093 | **Range** | **Scale - Reference** | **Scale - 1** |
1094 +============+========================+==================+
1095 | ``Jpapbp`` | 100 | 1 |
1096 +------------+------------------------+------------------+
1098 References
1099 ----------
1100 :cite:`Luo2006b`
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 """
1109 return XYZ_to_UCS_Luo2006(
1110 XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], **kwargs
1111 )
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.
1119 Parameters
1120 ----------
1121 Jpapbp
1122 *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array.
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.
1134 Returns
1135 -------
1136 :class:`numpy.ndarray`
1137 *CIE XYZ* tristimulus values.
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.
1144 Notes
1145 -----
1146 - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*.
1148 +------------+------------------------+------------------+
1149 | **Domain** | **Scale - Reference** | **Scale - 1** |
1150 +============+========================+==================+
1151 | ``Jpapbp`` | 100 | 1 |
1152 +------------+------------------------+------------------+
1154 +------------+------------------------+------------------+
1155 | **Range** | **Scale - Reference** | **Scale - 1** |
1156 +============+========================+==================+
1157 | ``XYZ`` | 1 | 1 |
1158 +------------+------------------------+------------------+
1160 References
1161 ----------
1162 :cite:`Luo2006b`
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 """
1171 return UCS_Luo2006_to_XYZ(
1172 Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], **kwargs
1173 )