Coverage for models/icacb.py: 26%

1"""

2:math:`IC_AC_B` Colourspace

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

4

5 Define the :math:`IC_AC_B` colourspace transformations.

6

7- :func:`colour.XYZ_to_ICaCb`

8- :func:`colour.ICaCb_to_XYZ`

9

10 References

11----------

12- :cite:`Frohlich2017` : Frohlich, J. (2017). Encoding high dynamic range

13 and wide color gamut imagery. doi:10.18419/OPUS-9664

14"""

15

16 from __future__ import annotations

17

18 import numpy as np

19

20 from colour.hints import ( # noqa: TC001

21 ArrayLike,

22 Domain1,

23 NDArrayFloat,

24 Range1,

25)

26 from colour.models import Iab_to_XYZ, XYZ_to_Iab

27 from colour.models.rgb.transfer_functions import eotf_inverse_ST2084, eotf_ST2084

28 from colour.utilities import domain_range_scale

29

30 __author__ = "Colour Developers"

31 __copyright__ = "Copyright 2013 Colour Developers"

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

33 __maintainer__ = "Colour Developers"

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

35 __status__ = "Production"

36

37 __all__ = [

38 "MATRIX_ICACB_XYZ_TO_LMS",

39 "MATRIX_ICACB_LMS_TO_XYZ",

40 "MATRIX_ICACB_XYZ_TO_LMS_2",

41 "MATRIX_ICACB_LMS_TO_XYZ_2",

42 "XYZ_to_ICaCb",

43 "ICaCb_to_XYZ",

44]

45

46 MATRIX_ICACB_XYZ_TO_LMS: NDArrayFloat = np.array(

47 [

48 [0.37613, 0.70431, -0.05675],

49 [-0.21649, 1.14744, 0.05356],

50 [0.02567, 0.16713, 0.74235],

51 ]

52)

53"""*CIE XYZ* tristimulus values to normalised cone responses matrix."""

54

55 MATRIX_ICACB_LMS_TO_XYZ: NDArrayFloat = np.linalg.inv(MATRIX_ICACB_XYZ_TO_LMS)

56"""Normalised cone responses to *CIE XYZ* tristimulus values matrix."""

57

58 MATRIX_ICACB_XYZ_TO_LMS_2: NDArrayFloat = np.array(

59 [

60 [0.4949, 0.5037, 0.0015],

61 [4.2854, -4.5462, 0.2609],

62 [0.3605, 1.1499, -1.5105],

63 ]

64)

65"""Normalised non-linear cone responses to :math:`IC_AC_B` colourspace matrix."""

66

67 MATRIX_ICACB_LMS_TO_XYZ_2: NDArrayFloat = np.linalg.inv(MATRIX_ICACB_XYZ_TO_LMS_2)

68""":math:`IC_AC_B` to normalised non-linear cone responses colourspace matrix."""

69

70

71 def XYZ_to_ICaCb(XYZ: Domain1) -> Range1:

72 """

73 Convert from *CIE XYZ* tristimulus values to :math:`IC_AC_B` colourspace.

74

75 Parameters

76 ----------

77 XYZ

78 *CIE XYZ* tristimulus values.

79

80 Returns

81 -------

82 :class:`numpy.ndarray`

83 :math:`IC_AC_B` colourspace array.

84

85 Notes

86 -----

87 +------------+-----------------------+-----------------+

88 | **Domain** | **Scale - Reference** | **Scale - 1** |

89 +============+=======================+=================+

90 | ``XYZ`` | 1 | 1 |

91 +------------+-----------------------+-----------------+

92

93 +------------+-----------------------+-----------------+

94 | **Range** | **Scale - Reference** | **Scale - 1** |

95 +============+=======================+=================+

96 | ``ICaCb`` | 1 | 1 |

97 +------------+-----------------------+-----------------+

98

99 - Input *CIE XYZ* tristimulus values must be adapted to

100 *CIE Standard Illuminant D Series* *D65*.

101

102 References

103 ----------

104 :cite:`Frohlich2017`

105

106 Examples

107 --------

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

109 >>> XYZ_to_ICaCb(XYZ) # doctest: +ELLIPSIS

110 array([ 0.0687529..., 0.0575335..., 0.0208154...])

111 """

112

113 def LMS_to_LMS_p_callable(LMS: ArrayLike) -> NDArrayFloat:

114 """

115 Callable applying the forward non-linearity to the :math:`LMS`

116 colourspace array.

117 """

118

119 with domain_range_scale("ignore"):

120 return eotf_inverse_ST2084(LMS)

121

122 return XYZ_to_Iab(

123 XYZ,

124 LMS_to_LMS_p_callable,

125 MATRIX_ICACB_XYZ_TO_LMS,

126 MATRIX_ICACB_XYZ_TO_LMS_2,

127 )

128

129

130 def ICaCb_to_XYZ(ICaCb: Domain1) -> Range1:

131 """

132 Convert from :math:`IC_AC_B` colourspace to *CIE XYZ* tristimulus values.

133

134 Parameters

135 ----------

136 ICaCb

137 :math:`IC_AC_B` colourspace array.

138

139 Returns

140 -------

141 :class:`numpy.ndarray`

142 *CIE XYZ* tristimulus values.

143

144 Notes

145 -----

146 +------------+-----------------------+-----------------+

147 | **Domain** | **Scale - Reference** | **Scale - 1** |

148 +============+=======================+=================+

149 | ``ICaCb`` | 1 | 1 |

150 +------------+-----------------------+-----------------+

151

152 +------------+-----------------------+-----------------+

153 | **Range** | **Scale - Reference** | **Scale - 1** |

154 +============+=======================+=================+

155 | ``XYZ`` | 1 | 1 |

156 +------------+-----------------------+-----------------+

157

158 - Output *CIE XYZ* tristimulus values are adapted to

159 *CIE Standard Illuminant D Series* *D65*.

160

161 References

162 ----------

163 :cite:`Frohlich2017`

164

165 Examples

166 --------

167 >>> ICaCb = np.array([0.06875297, 0.05753352, 0.02081548])

168 >>> ICaCb_to_XYZ(ICaCb) # doctest: +ELLIPSIS

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

170 """

171

172 def LMS_p_to_LMS_callable(LMS_p: ArrayLike) -> NDArrayFloat:

173 """

174 Callable applying the reverse non-linearity to the :math:`LMS_p`

175 colourspace array.

176 """

177

178 with domain_range_scale("ignore"):

179 return eotf_ST2084(LMS_p)

180

181 return Iab_to_XYZ(

182 ICaCb,

183 LMS_p_to_LMS_callable,

184 MATRIX_ICACB_LMS_TO_XYZ_2,

185 MATRIX_ICACB_LMS_TO_XYZ,

186 )