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

91 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 22:49 +1300

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

2 

3from __future__ import annotations 

4 

5from itertools import product 

6 

7import numpy as np 

8 

9from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

10from colour.models import IPT_hue_angle, IPT_to_XYZ, XYZ_to_IPT 

11from 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 "TestXYZ_to_IPT", 

22 "TestIPT_to_XYZ", 

23 "TestIPTHueAngle", 

24] 

25 

26 

27class TestXYZ_to_IPT: 

28 """Define :func:`colour.models.ipt.XYZ_to_IPT` definition unit tests methods.""" 

29 

30 def test_XYZ_to_IPT(self) -> None: 

31 """Test :func:`colour.models.ipt.XYZ_to_IPT` definition.""" 

32 

33 np.testing.assert_allclose( 

34 XYZ_to_IPT(np.array([0.20654008, 0.12197225, 0.05136952])), 

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

36 atol=TOLERANCE_ABSOLUTE_TESTS, 

37 ) 

38 

39 np.testing.assert_allclose( 

40 XYZ_to_IPT(np.array([0.14222010, 0.23042768, 0.10495772])), 

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

42 atol=TOLERANCE_ABSOLUTE_TESTS, 

43 ) 

44 

45 np.testing.assert_allclose( 

46 XYZ_to_IPT(np.array([0.07818780, 0.06157201, 0.28099326])), 

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

48 atol=TOLERANCE_ABSOLUTE_TESTS, 

49 ) 

50 

51 def test_n_dimensional_XYZ_to_IPT(self) -> None: 

52 """ 

53 Test :func:`colour.models.ipt.XYZ_to_IPT` definition n-dimensional 

54 support. 

55 """ 

56 

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

58 IPT = XYZ_to_IPT(XYZ) 

59 

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

61 IPT = np.tile(IPT, (6, 1)) 

62 np.testing.assert_allclose(XYZ_to_IPT(XYZ), IPT, atol=TOLERANCE_ABSOLUTE_TESTS) 

63 

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

65 IPT = np.reshape(IPT, (2, 3, 3)) 

66 np.testing.assert_allclose(XYZ_to_IPT(XYZ), IPT, atol=TOLERANCE_ABSOLUTE_TESTS) 

67 

68 def test_domain_range_scale_XYZ_to_IPT(self) -> None: 

69 """ 

70 Test :func:`colour.models.ipt.XYZ_to_IPT` definition domain and 

71 range scale support. 

72 """ 

73 

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

75 IPT = XYZ_to_IPT(XYZ) 

76 

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

78 for scale, factor in d_r: 

79 with domain_range_scale(scale): 

80 np.testing.assert_allclose( 

81 XYZ_to_IPT(XYZ * factor), 

82 IPT * factor, 

83 atol=TOLERANCE_ABSOLUTE_TESTS, 

84 ) 

85 

86 @ignore_numpy_errors 

87 def test_nan_XYZ_to_IPT(self) -> None: 

88 """Test :func:`colour.models.ipt.XYZ_to_IPT` definition nan support.""" 

89 

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

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

92 XYZ_to_IPT(cases) 

93 

94 

95class TestIPT_to_XYZ: 

96 """ 

97 Define :func:`colour.models.ipt.IPT_to_XYZ` definition unit tests 

98 methods. 

99 """ 

100 

101 def test_IPT_to_XYZ(self) -> None: 

102 """Test :func:`colour.models.ipt.IPT_to_XYZ` definition.""" 

103 

104 np.testing.assert_allclose( 

105 IPT_to_XYZ(np.array([0.38426191, 0.38487306, 0.18886838])), 

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

107 atol=TOLERANCE_ABSOLUTE_TESTS, 

108 ) 

109 

110 np.testing.assert_allclose( 

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

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

113 atol=TOLERANCE_ABSOLUTE_TESTS, 

114 ) 

115 

116 np.testing.assert_allclose( 

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

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

119 atol=TOLERANCE_ABSOLUTE_TESTS, 

120 ) 

121 

122 def test_n_dimensional_IPT_to_XYZ(self) -> None: 

123 """ 

124 Test :func:`colour.models.ipt.IPT_to_XYZ` definition n-dimensional 

125 support. 

126 """ 

127 

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

129 XYZ = IPT_to_XYZ(IPT) 

130 

131 IPT = np.tile(IPT, (6, 1)) 

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

133 np.testing.assert_allclose(IPT_to_XYZ(IPT), XYZ, atol=TOLERANCE_ABSOLUTE_TESTS) 

134 

135 IPT = np.reshape(IPT, (2, 3, 3)) 

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

137 np.testing.assert_allclose(IPT_to_XYZ(IPT), XYZ, atol=TOLERANCE_ABSOLUTE_TESTS) 

138 

139 def test_domain_range_scale_IPT_to_XYZ(self) -> None: 

140 """ 

141 Test :func:`colour.models.ipt.IPT_to_XYZ` definition domain and 

142 range scale support. 

143 """ 

144 

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

146 XYZ = IPT_to_XYZ(IPT) 

147 

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

149 for scale, factor in d_r: 

150 with domain_range_scale(scale): 

151 np.testing.assert_allclose( 

152 IPT_to_XYZ(IPT * factor), 

153 XYZ * factor, 

154 atol=TOLERANCE_ABSOLUTE_TESTS, 

155 ) 

156 

157 @ignore_numpy_errors 

158 def test_nan_IPT_to_XYZ(self) -> None: 

159 """Test :func:`colour.models.ipt.IPT_to_XYZ` definition nan support.""" 

160 

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

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

163 IPT_to_XYZ(cases) 

164 

165 

166class TestIPTHueAngle: 

167 """ 

168 Define :func:`colour.models.ipt.IPT_hue_angle` definition unit tests 

169 methods. 

170 """ 

171 

172 def test_IPT_hue_angle(self) -> None: 

173 """Test :func:`colour.models.ipt.IPT_hue_angle` definition.""" 

174 

175 np.testing.assert_allclose( 

176 IPT_hue_angle(np.array([0.38426191, 0.38487306, 0.18886838])), 

177 26.138526939899490, 

178 atol=TOLERANCE_ABSOLUTE_TESTS, 

179 ) 

180 

181 np.testing.assert_allclose( 

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

183 136.797287973958500, 

184 atol=TOLERANCE_ABSOLUTE_TESTS, 

185 ) 

186 

187 np.testing.assert_allclose( 

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

189 256.321284526533300, 

190 atol=TOLERANCE_ABSOLUTE_TESTS, 

191 ) 

192 

193 def test_n_dimensional_IPT_hue_angle(self) -> None: 

194 """ 

195 Test :func:`colour.models.ipt.IPT_hue_angle` definition n-dimensional 

196 support. 

197 """ 

198 

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

200 hue = IPT_hue_angle(IPT) 

201 

202 IPT = np.tile(IPT, (6, 1)) 

203 hue = np.tile(hue, 6) 

204 np.testing.assert_allclose( 

205 IPT_hue_angle(IPT), hue, atol=TOLERANCE_ABSOLUTE_TESTS 

206 ) 

207 

208 IPT = np.reshape(IPT, (2, 3, 3)) 

209 hue = np.reshape(hue, (2, 3)) 

210 np.testing.assert_allclose( 

211 IPT_hue_angle(IPT), hue, atol=TOLERANCE_ABSOLUTE_TESTS 

212 ) 

213 

214 def test_domain_range_scale_IPT_hue_angle(self) -> None: 

215 """ 

216 Test :func:`colour.models.ipt.IPT_hue_angle` definition domain and 

217 range scale support. 

218 """ 

219 

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

221 hue = IPT_hue_angle(IPT) 

222 

223 d_r = (("reference", 1, 1), ("1", 1, 1 / 360), ("100", 100, 1 / 3.6)) 

224 for scale, factor_a, factor_b in d_r: 

225 with domain_range_scale(scale): 

226 np.testing.assert_allclose( 

227 IPT_hue_angle(IPT * factor_a), 

228 hue * factor_b, 

229 atol=TOLERANCE_ABSOLUTE_TESTS, 

230 ) 

231 

232 @ignore_numpy_errors 

233 def test_nan_IPT_hue_angle(self) -> None: 

234 """Test :func:`colour.models.ipt.IPT_hue_angle` definition nan support.""" 

235 

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

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

238 IPT_hue_angle(cases)