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

30 statements  

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

1""" 

2IPT Colourspace 

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

4 

5Define the *IPT* colourspace transformations and correlate computations. 

6 

7- :func:`colour.XYZ_to_IPT` 

8- :func:`colour.IPT_to_XYZ` 

9- :func:`colour.IPT_hue_angle` 

10 

11References 

12---------- 

13- :cite:`Fairchild2013y` : Fairchild, M. D. (2013). IPT Colourspace. In 

14 Color Appearance Models (3rd ed., pp. 6197-6223). Wiley. ISBN:B00DAYO8E2 

15""" 

16 

17from __future__ import annotations 

18 

19from functools import partial 

20 

21import numpy as np 

22 

23from colour.algebra import spow 

24from colour.hints import ( # noqa: TC001 

25 Domain1, 

26 NDArrayFloat, 

27 Range1, 

28 Range360, 

29) 

30from colour.models import Iab_to_XYZ, XYZ_to_Iab 

31from colour.utilities import as_float, from_range_degrees, to_domain_1, tsplit 

32 

33__author__ = "Colour Developers" 

34__copyright__ = "Copyright 2013 Colour Developers" 

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

36__maintainer__ = "Colour Developers" 

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

38__status__ = "Production" 

39 

40__all__ = [ 

41 "MATRIX_IPT_XYZ_TO_LMS", 

42 "MATRIX_IPT_LMS_TO_XYZ", 

43 "MATRIX_IPT_LMS_P_TO_IPT", 

44 "MATRIX_IPT_IPT_TO_LMS_P", 

45 "XYZ_to_IPT", 

46 "IPT_to_XYZ", 

47 "IPT_hue_angle", 

48] 

49 

50MATRIX_IPT_XYZ_TO_LMS: NDArrayFloat = np.array( 

51 [ 

52 [0.4002, 0.7075, -0.0807], 

53 [-0.2280, 1.1500, 0.0612], 

54 [0.0000, 0.0000, 0.9184], 

55 ] 

56) 

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

58 

59MATRIX_IPT_LMS_TO_XYZ: NDArrayFloat = np.linalg.inv(MATRIX_IPT_XYZ_TO_LMS) 

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

61 

62MATRIX_IPT_LMS_P_TO_IPT: NDArrayFloat = np.array( 

63 [ 

64 [0.4000, 0.4000, 0.2000], 

65 [4.4550, -4.8510, 0.3960], 

66 [0.8056, 0.3572, -1.1628], 

67 ] 

68) 

69"""Normalised non-linear cone responses to *IPT* colourspace matrix.""" 

70 

71MATRIX_IPT_IPT_TO_LMS_P: NDArrayFloat = np.linalg.inv(MATRIX_IPT_LMS_P_TO_IPT) 

72"""*IPT* colourspace to normalised non-linear cone responses matrix.""" 

73 

74 

75def XYZ_to_IPT(XYZ: Domain1) -> Range1: 

76 """ 

77 Convert from *CIE XYZ* tristimulus values to *IPT* colourspace. 

78 

79 Parameters 

80 ---------- 

81 XYZ 

82 *CIE XYZ* tristimulus values. 

83 

84 Returns 

85 ------- 

86 :class:`numpy.ndarray` 

87 *IPT* colourspace array. 

88 

89 Notes 

90 ----- 

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

92 | **Domain** | **Scale - Reference** | **Scale - 1** | 

93 +============+=======================+=================+ 

94 | ``XYZ`` | 1 | 1 | 

95 +------------+-----------------------+-----------------+ 

96 

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

98 | **Range** | **Scale - Reference** | **Scale - 1** | 

99 +============+=======================+=================+ 

100 | ``IPT`` | 1 | 1 | 

101 +------------+-----------------------+-----------------+ 

102 

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

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

105 

106 References 

107 ---------- 

108 :cite:`Fairchild2013y` 

109 

110 Examples 

111 -------- 

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

113 >>> XYZ_to_IPT(XYZ) # doctest: +ELLIPSIS 

114 array([ 0.3842619..., 0.3848730..., 0.1888683...]) 

115 """ 

116 

117 return XYZ_to_Iab( 

118 XYZ, 

119 partial(spow, p=0.43), 

120 MATRIX_IPT_XYZ_TO_LMS, 

121 MATRIX_IPT_LMS_P_TO_IPT, 

122 ) 

123 

124 

125def IPT_to_XYZ(IPT: Domain1) -> Range1: 

126 """ 

127 Convert from *IPT* colourspace to *CIE XYZ* tristimulus values. 

128 

129 Parameters 

130 ---------- 

131 IPT 

132 *IPT* colourspace array. 

133 

134 Returns 

135 ------- 

136 :class:`numpy.ndarray` 

137 *CIE XYZ* tristimulus values. 

138 

139 Notes 

140 ----- 

141 +------------+-----------------------+-----------------+ 

142 | **Domain** | **Scale - Reference** | **Scale - 1** | 

143 +============+=======================+=================+ 

144 | ``IPT`` | 1 | 1 | 

145 +------------+-----------------------+-----------------+ 

146 

147 +------------+-----------------------+-----------------+ 

148 | **Range** | **Scale - Reference** | **Scale - 1** | 

149 +============+=======================+=================+ 

150 | ``XYZ`` | 1 | 1 | 

151 +------------+-----------------------+-----------------+ 

152 

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

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

155 

156 References 

157 ---------- 

158 :cite:`Fairchild2013y` 

159 

160 Examples 

161 -------- 

162 >>> IPT = np.array([0.38426191, 0.38487306, 0.18886838]) 

163 >>> IPT_to_XYZ(IPT) # doctest: +ELLIPSIS 

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

165 """ 

166 

167 return Iab_to_XYZ( 

168 IPT, 

169 partial(spow, p=1 / 0.43), 

170 MATRIX_IPT_IPT_TO_LMS_P, 

171 MATRIX_IPT_LMS_TO_XYZ, 

172 ) 

173 

174 

175def IPT_hue_angle(IPT: Domain1) -> Range360: 

176 """ 

177 Compute the hue angle in degrees from the *IPT* colourspace array. 

178 

179 Parameters 

180 ---------- 

181 IPT 

182 *IPT* colourspace array. 

183 

184 Returns 

185 ------- 

186 :class:`numpy.ndarray` 

187 Hue angle in degrees. 

188 

189 Notes 

190 ----- 

191 +------------+-----------------------+-----------------+ 

192 | **Domain** | **Scale - Reference** | **Scale - 1** | 

193 +============+=======================+=================+ 

194 | ``IPT`` | 1 | 1 | 

195 +------------+-----------------------+-----------------+ 

196 

197 +------------+-----------------------+-----------------+ 

198 | **Range** | **Scale - Reference** | **Scale - 1** | 

199 +============+=======================+=================+ 

200 | ``hue`` | 360 | 1 | 

201 +------------+-----------------------+-----------------+ 

202 

203 References 

204 ---------- 

205 :cite:`Fairchild2013y` 

206 

207 Examples 

208 -------- 

209 >>> IPT = np.array([0.96907232, 1, 1.12179215]) 

210 >>> IPT_hue_angle(IPT) # doctest: +ELLIPSIS 

211 48.2852074... 

212 """ 

213 

214 _I, P, T = tsplit(to_domain_1(IPT)) 

215 

216 hue = np.degrees(np.arctan2(T, P)) % 360 

217 

218 return as_float(from_range_degrees(hue))