Coverage for colour/io/luts/tests/test_operator.py: 90%

61 statements  

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

1"""Define the unit tests for the :mod:`colour.io.luts.operator` module.""" 

2 

3from __future__ import annotations 

4 

5import textwrap 

6 

7import numpy as np 

8 

9from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

10from colour.io.luts import AbstractLUTSequenceOperator, LUTOperatorMatrix 

11from colour.utilities import tstack, zeros 

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 "AbstractTestLUTSequenceOperator", 

22 "TestLUTOperatorMatrix", 

23] 

24 

25 

26class AbstractTestLUTSequenceOperator: 

27 """ 

28 Define :class:`colour.io.luts.operator.AbstractLUTSequenceOperator` class 

29 unit tests methods. 

30 """ 

31 

32 def test_required_attributes(self) -> None: 

33 """Test the presence of required attributes.""" 

34 

35 required_attributes = ("name", "comments") 

36 

37 for method in required_attributes: 

38 assert method in dir(AbstractLUTSequenceOperator) 

39 

40 def test_required_methods(self) -> None: 

41 """Test the presence of required methods.""" 

42 

43 required_methods = ("apply",) 

44 

45 for method in required_methods: 

46 assert method in dir(AbstractLUTSequenceOperator) 

47 

48 

49class TestLUTOperatorMatrix: 

50 """ 

51 Define :class:`colour.io.luts.operator.LUTOperatorMatrix` class unit tests 

52 methods. 

53 """ 

54 

55 def setup_method(self) -> None: 

56 """Initialise the common tests attributes.""" 

57 

58 self._lut_operator_matrix = LUTOperatorMatrix( 

59 np.reshape(np.linspace(0, 1, 16), (4, 4)), 

60 offset=np.array([0.25, 0.5, 0.75, 1.0]), 

61 name="Nemo Matrix", 

62 comments=["A first comment.", "A second comment."], 

63 ) 

64 

65 def test_required_attributes(self) -> None: 

66 """Test the presence of required attributes.""" 

67 

68 required_attributes = ("matrix", "offset") 

69 

70 for method in required_attributes: 

71 assert method in dir(LUTOperatorMatrix) 

72 

73 def test_required_methods(self) -> None: 

74 """Test the presence of required methods.""" 

75 

76 required_methods = ("__str__", "__repr__", "__eq__", "__ne__", "apply") 

77 

78 for method in required_methods: 

79 assert method in dir(LUTOperatorMatrix) 

80 

81 def test_matrix(self) -> None: 

82 """ 

83 Test :class:`colour.io.luts.operator.LUTOperatorMatrix.matrix` 

84 property. 

85 """ 

86 

87 M = np.identity(3) 

88 

89 lut_operator_matrix = LUTOperatorMatrix(M) 

90 np.testing.assert_array_equal(lut_operator_matrix.matrix, np.identity(4)) 

91 

92 def test_offset(self) -> None: 

93 """ 

94 Test :class:`colour.io.luts.operator.LUTOperatorMatrix.offset` 

95 property. 

96 """ 

97 

98 offset = zeros(3) 

99 

100 lut_operator_matrix = LUTOperatorMatrix(np.identity(3), offset) 

101 np.testing.assert_array_equal(lut_operator_matrix.offset, zeros(4)) 

102 

103 def test__str__(self) -> None: 

104 """ 

105 Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__str__` 

106 method. 

107 """ 

108 

109 assert ( 

110 str(self._lut_operator_matrix) 

111 == ( 

112 textwrap.dedent( 

113 """ 

114 LUTOperatorMatrix - Nemo Matrix 

115 ------------------------------- 

116 

117 Matrix : [[ 0. 0.06666667 0.13333333 0.2 ] 

118 [ 0.26666667 0.33333333 0.4 0.46666667] 

119 [ 0.53333333 0.6 0.66666667 0.73333333] 

120 [ 0.8 0.86666667 0.93333333 1. ]] 

121 Offset : [ 0.25 0.5 0.75 1. ] 

122 

123 A first comment. 

124 A second comment.""" 

125 )[1:] 

126 ) 

127 ) 

128 

129 def test__repr__(self) -> None: 

130 """ 

131 Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__repr__` 

132 method. 

133 """ 

134 

135 assert repr(self._lut_operator_matrix) == ( 

136 textwrap.dedent( 

137 """ 

138LUTOperatorMatrix([[ 0. , 0.06666667, 0.13333333, 0.2 ], 

139 [ 0.26666667, 0.33333333, 0.4 , 0.46666667], 

140 [ 0.53333333, 0.6 , 0.66666667, 0.73333333], 

141 [ 0.8 , 0.86666667, 0.93333333, 1. ]], 

142 [ 0.25, 0.5 , 0.75, 1. ], 

143 name='Nemo Matrix', 

144 comments=['A first comment.', 'A second comment.'])"""[1:] 

145 ) 

146 ) 

147 

148 def test__eq__(self) -> None: 

149 """Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__eq__` method.""" 

150 

151 matrix = LUTOperatorMatrix( 

152 np.reshape(np.linspace(0, 1, 16), (4, 4)), 

153 np.array([0.25, 0.5, 0.75, 1.0]), 

154 ) 

155 

156 assert self._lut_operator_matrix == matrix 

157 

158 def test__neq__(self) -> None: 

159 """ 

160 Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__neq__` 

161 method. 

162 """ 

163 

164 matrix = LUTOperatorMatrix(np.reshape(np.linspace(0, 1, 16), (4, 4)) * 0.75) 

165 

166 assert self._lut_operator_matrix != matrix 

167 

168 def test_apply(self) -> None: 

169 """Test :class:`colour.io.luts.operator.LUTOperatorMatrix.apply` method.""" 

170 

171 samples = np.linspace(0, 1, 5) 

172 RGB = tstack([samples, samples, samples]) 

173 

174 np.testing.assert_array_equal(LUTOperatorMatrix().apply(RGB), RGB) 

175 

176 np.testing.assert_allclose( 

177 self._lut_operator_matrix.apply(RGB), 

178 np.array( 

179 [ 

180 [0.25000000, 0.50000000, 0.75000000], 

181 [0.30000000, 0.75000000, 1.20000000], 

182 [0.35000000, 1.00000000, 1.65000000], 

183 [0.40000000, 1.25000000, 2.10000000], 

184 [0.45000000, 1.50000000, 2.55000000], 

185 ] 

186 ), 

187 atol=TOLERANCE_ABSOLUTE_TESTS, 

188 ) 

189 

190 np.testing.assert_allclose( 

191 self._lut_operator_matrix.apply(RGB, apply_offset_first=True), 

192 np.array( 

193 [ 

194 [0.13333333, 0.53333333, 0.93333333], 

195 [0.18333333, 0.78333333, 1.38333333], 

196 [0.23333333, 1.03333333, 1.83333333], 

197 [0.28333333, 1.28333333, 2.28333333], 

198 [0.33333333, 1.53333333, 2.73333333], 

199 ] 

200 ), 

201 atol=TOLERANCE_ABSOLUTE_TESTS, 

202 ) 

203 

204 RGBA = tstack([samples, samples, samples, samples]) 

205 

206 np.testing.assert_array_equal(LUTOperatorMatrix().apply(RGBA), RGBA) 

207 

208 np.testing.assert_allclose( 

209 self._lut_operator_matrix.apply(RGBA), 

210 np.array( 

211 [ 

212 [0.25000000, 0.50000000, 0.75000000, 1.00000000], 

213 [0.35000000, 0.86666667, 1.38333333, 1.90000000], 

214 [0.45000000, 1.23333333, 2.01666667, 2.80000000], 

215 [0.55000000, 1.60000000, 2.65000000, 3.70000000], 

216 [0.65000000, 1.96666667, 3.28333333, 4.60000000], 

217 ] 

218 ), 

219 atol=TOLERANCE_ABSOLUTE_TESTS, 

220 ) 

221 

222 np.testing.assert_allclose( 

223 self._lut_operator_matrix.apply(RGBA, apply_offset_first=True), 

224 np.array( 

225 [ 

226 [0.33333333, 1.00000000, 1.66666667, 2.33333333], 

227 [0.43333333, 1.36666667, 2.30000000, 3.23333333], 

228 [0.53333333, 1.73333333, 2.93333333, 4.13333333], 

229 [0.63333333, 2.10000000, 3.56666667, 5.03333333], 

230 [0.73333333, 2.46666667, 4.20000000, 5.93333333], 

231 ], 

232 ), 

233 atol=TOLERANCE_ABSOLUTE_TESTS, 

234 )