Coverage for adaptation/fairchild2020.py: 43%

46 statements  

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

1""" 

2Von Kries 2020 (vK20) Chromatic Adaptation Model 

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

4 

5Define the *Von Kries 2020* (*vK20*) chromatic adaptation model for predicting 

6corresponding colours under different viewing conditions. 

7 

8- :attr:`colour.adaptation.CONDITIONS_DEGREE_OF_ADAPTATION_VK20` 

9- :func:`colour.adaptation.matrix_chromatic_adaptation_vk20` 

10- :func:`colour.adaptation.chromatic_adaptation_vK20` 

11 

12References 

13---------- 

14- :cite:`Fairchild2020` : Fairchild, M. D. (2020). Von Kries 2020: 

15 Evolution of degree of chromatic adaptation. Color and Imaging 

16 Conference, 28(1), 252-257. doi:10.2352/issn.2169-2629.2020.28.40 

17""" 

18 

19from __future__ import annotations 

20 

21import typing 

22from dataclasses import dataclass 

23 

24import numpy as np 

25 

26from colour.adaptation import CHROMATIC_ADAPTATION_TRANSFORMS 

27from colour.algebra import sdiv, sdiv_mode, vecmul 

28 

29if typing.TYPE_CHECKING: 

30 from colour.hints import Literal 

31 

32from colour.hints import ( # noqa: TC001 

33 ArrayLike, 

34 Domain1, 

35 NDArrayFloat, 

36 Range1, 

37) 

38from colour.utilities import ( 

39 CanonicalMapping, 

40 MixinDataclassIterable, 

41 as_float_array, 

42 from_range_1, 

43 get_domain_range_scale, 

44 optional, 

45 row_as_diagonal, 

46 to_domain_1, 

47 validate_method, 

48) 

49 

50__author__ = "Colour Developers" 

51__copyright__ = "Copyright 2013 Colour Developers" 

52__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" 

53__maintainer__ = "Colour Developers" 

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

55__status__ = "Production" 

56 

57__all__ = [ 

58 "Coefficients_DegreeOfAdaptation_vK20", 

59 "CONDITIONS_DEGREE_OF_ADAPTATION_VK20", 

60 "TVS_XYZ_R_VK20", 

61 "matrix_chromatic_adaptation_vk20", 

62 "chromatic_adaptation_vK20", 

63] 

64 

65 

66@dataclass(frozen=True) 

67class Coefficients_DegreeOfAdaptation_vK20(MixinDataclassIterable): 

68 """ 

69 Define the degree of adaptation coefficients for the *Von Kries 2020* 

70 (*vK20*) chromatic adaptation model. 

71 

72 Parameters 

73 ---------- 

74 D_n 

75 Degree of adaptation for the adapting illuminant. 

76 D_r 

77 Degree of adaptation for the reference illuminant. 

78 D_p 

79 Degree of adaptation for the previous illuminant. 

80 

81 References 

82 ---------- 

83 :cite:`Fairchild2020` 

84 """ 

85 

86 D_n: float 

87 D_r: float 

88 D_p: float 

89 

90 

91CONDITIONS_DEGREE_OF_ADAPTATION_VK20: CanonicalMapping = CanonicalMapping( 

92 { 

93 "Fairchild": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0), 

94 "Hands": Coefficients_DegreeOfAdaptation_vK20(0.95, 0.05, 0), 

95 "No Hands": Coefficients_DegreeOfAdaptation_vK20(0.85, 0.15, 0), 

96 "Ordinal 1st": Coefficients_DegreeOfAdaptation_vK20(0.9, 0.1, 0), 

97 "Ordinal 2nd": Coefficients_DegreeOfAdaptation_vK20(0.8, 0.1, 0.1), 

98 "Reversibility Trial 1st": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0.1), 

99 "Reversibility Trial 2nd": Coefficients_DegreeOfAdaptation_vK20(0.6, 0.3, 0.1), 

100 "Ma et al.": Coefficients_DegreeOfAdaptation_vK20(1 / 3, 1 / 3, 1 / 3), 

101 "Hunt & Winter": Coefficients_DegreeOfAdaptation_vK20(0.6, 0.2, 0.2), 

102 "Hurvich & Jameson": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0), 

103 "Simple von Kries": Coefficients_DegreeOfAdaptation_vK20(1, 0, 0), 

104 } 

105) 

106CONDITIONS_DEGREE_OF_ADAPTATION_VK20.__doc__ = """ 

107Define the degree of adaptation coefficient conditions for the *Von Kries 2020* 

108(*vK20*) chromatic adaptation model. 

109 

110References 

111---------- 

112:cite:`Fairchild2020` 

113""" 

114 

115TVS_XYZ_R_VK20 = np.array([0.97941176, 1.00000000, 1.73235294]) 

116""" 

117*Von Kries 2020* (*vK20*) reference illuminant (taken to be 

118u' = 0.185, v' = 0.425, approximately 15000K, sky blue). 

119 

120References 

121---------- 

122:cite:`Fairchild2020` 

123""" 

124 

125 

126def matrix_chromatic_adaptation_vk20( 

127 XYZ_p: ArrayLike, 

128 XYZ_n: ArrayLike, 

129 XYZ_r: ArrayLike = TVS_XYZ_R_VK20, 

130 transform: Literal[ 

131 "Bianco 2010", 

132 "Bianco PC 2010", 

133 "Bradford", 

134 "CAT02 Brill 2008", 

135 "CAT02", 

136 "CAT16", 

137 "CMCCAT2000", 

138 "CMCCAT97", 

139 "Fairchild", 

140 "Sharp", 

141 "Von Kries", 

142 "XYZ Scaling", 

143 ] 

144 | str = "CAT02", 

145 coefficients: Coefficients_DegreeOfAdaptation_vK20 = ( 

146 CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Fairchild"] 

147 ), 

148) -> NDArrayFloat: 

149 """ 

150 Compute the chromatic adaptation matrix from previous viewing conditions 

151 to adapting viewing conditions using the *Von Kries 2020* (*vK20*) 

152 method. 

153 

154 Parameters 

155 ---------- 

156 XYZ_p 

157 *CIE XYZ* tristimulus values of the whitepoint under previous viewing 

158 conditions. 

159 XYZ_n 

160 *CIE XYZ* tristimulus values of the whitepoint under adapting viewing 

161 conditions. 

162 XYZ_r 

163 *CIE XYZ* tristimulus values of the whitepoint under reference viewing 

164 conditions. 

165 transform 

166 Chromatic adaptation transform. 

167 coefficients 

168 *vK20* degree of adaptation coefficients. 

169 

170 Returns 

171 ------- 

172 :class:`numpy.ndarray` 

173 Chromatic adaptation matrix :math:`M_{cat}`. 

174 

175 Notes 

176 ----- 

177 +------------+-----------------------+---------------+ 

178 | **Domain** | **Scale - Reference** | **Scale - 1** | 

179 +============+=======================+===============+ 

180 | ``XYZ_p`` | 1 | 1 | 

181 +------------+-----------------------+---------------+ 

182 | ``XYZ_n`` | 1 | 1 | 

183 +------------+-----------------------+---------------+ 

184 | ``XYZ_r`` | 1 | 1 | 

185 +------------+-----------------------+---------------+ 

186 

187 References 

188 ---------- 

189 :cite:`Fairchild2020` 

190 

191 Examples 

192 -------- 

193 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) 

194 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) 

195 >>> matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n) 

196 ... # doctest: +ELLIPSIS 

197 array([[ 1.0279139...e+00, 2.9137117...e-02, -2.2794068...e-02], 

198 [ 2.0702840...e-02, 9.9005316...e-01, -9.2143464...e-03], 

199 [ -6.3758553...e-04, -1.1577319...e-03, 9.1296320...e-01]]) 

200 

201 Using *Bradford* transform: 

202 

203 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) 

204 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) 

205 >>> transform = "Bradford" 

206 >>> matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n, transform=transform) 

207 ... # doctest: +ELLIPSIS 

208 array([[ 1.0367230..., 0.0195580..., -0.0219321...], 

209 [ 0.0276321..., 0.9822296..., -0.0082419...], 

210 [-0.0029508..., 0.0040690..., 0.9102430...]]) 

211 """ 

212 

213 XYZ_n = as_float_array(XYZ_n) 

214 XYZ_r = as_float_array(XYZ_r) 

215 XYZ_p = as_float_array(XYZ_p) 

216 

217 transform = validate_method( 

218 transform, 

219 tuple(CHROMATIC_ADAPTATION_TRANSFORMS), 

220 '"{0}" chromatic adaptation transform is invalid, it must be one of {1}!', 

221 ) 

222 

223 M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] 

224 

225 D_n, D_r, D_p = coefficients.values 

226 

227 LMS_n = vecmul(M, XYZ_n) 

228 LMS_r = vecmul(M, XYZ_r) 

229 LMS_p = vecmul(M, XYZ_p) 

230 

231 with sdiv_mode(): 

232 D = row_as_diagonal(sdiv(1, (D_n * LMS_n + D_r * LMS_r + D_p * LMS_p))) 

233 

234 M_CAT = np.matmul(np.linalg.inv(M), D) 

235 

236 return np.matmul(M_CAT, M) 

237 

238 

239def chromatic_adaptation_vK20( 

240 XYZ: Domain1, 

241 XYZ_p: Domain1, 

242 XYZ_n: Domain1, 

243 XYZ_r: ArrayLike | None = None, 

244 transform: Literal[ 

245 "Bianco 2010", 

246 "Bianco PC 2010", 

247 "Bradford", 

248 "CAT02 Brill 2008", 

249 "CAT02", 

250 "CAT16", 

251 "CMCCAT2000", 

252 "CMCCAT97", 

253 "Fairchild", 

254 "Sharp", 

255 "Von Kries", 

256 "XYZ Scaling", 

257 ] 

258 | str = "CAT02", 

259 coefficients: Coefficients_DegreeOfAdaptation_vK20 = ( 

260 CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Fairchild"] 

261 ), 

262) -> Range1: 

263 """ 

264 Adapt the specified stimulus *CIE XYZ* tristimulus values from test 

265 viewing conditions to reference viewing conditions using the 

266 *Von Kries 2020* (*vK20*) chromatic adaptation model. 

267 

268 Parameters 

269 ---------- 

270 XYZ 

271 *CIE XYZ* tristimulus values of the stimulus to adapt. 

272 XYZ_p 

273 Previous viewing conditions *CIE XYZ* tristimulus values of the 

274 whitepoint. 

275 XYZ_n 

276 Adapting viewing conditions *CIE XYZ* tristimulus values of the 

277 whitepoint. 

278 XYZ_r 

279 Reference viewing conditions *CIE XYZ* tristimulus values of the 

280 whitepoint. 

281 transform 

282 Chromatic adaptation transform. 

283 coefficients 

284 *vK20* degree of adaptation coefficients. 

285 

286 Returns 

287 ------- 

288 :class:`numpy.ndarray` 

289 *CIE XYZ* tristimulus values of the stimulus corresponding colour. 

290 

291 Notes 

292 ----- 

293 +------------+-----------------------+---------------+ 

294 | **Domain** | **Scale - Reference** | **Scale - 1** | 

295 +============+=======================+===============+ 

296 | ``XYZ`` | 1 | 1 | 

297 +------------+-----------------------+---------------+ 

298 | ``XYZ_p`` | 1 | 1 | 

299 +------------+-----------------------+---------------+ 

300 | ``XYZ_n`` | 1 | 1 | 

301 +------------+-----------------------+---------------+ 

302 | ``XYZ_r`` | 1 | 1 | 

303 +------------+-----------------------+---------------+ 

304 

305 +------------+-----------------------+---------------+ 

306 | **Range** | **Scale - Reference** | **Scale - 1** | 

307 +============+=======================+===============+ 

308 | ``XYZ_a`` | 1 | 1 | 

309 +------------+-----------------------+---------------+ 

310 

311 References 

312 ---------- 

313 :cite:`Fairchild2020` 

314 

315 Examples 

316 -------- 

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

318 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) 

319 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) 

320 >>> chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n) 

321 ... # doctest: +ELLIPSIS 

322 array([ 0.2146884..., 0.1245616..., 0.0466255...]) 

323 

324 Using *Bradford* transform: 

325 

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

327 >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) 

328 >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) 

329 >>> transform = "Bradford" 

330 >>> chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n, transform=transform) 

331 ... # doctest: +ELLIPSIS 

332 array([ 0.2153837..., 0.1250885..., 0.0466455...]) 

333 """ 

334 

335 XYZ = to_domain_1(XYZ) 

336 XYZ_p = to_domain_1(XYZ_p) 

337 XYZ_n = to_domain_1(XYZ_n) 

338 XYZ_r = to_domain_1( 

339 optional( 

340 XYZ_r, 

341 TVS_XYZ_R_VK20 

342 if get_domain_range_scale() == "reference" 

343 else TVS_XYZ_R_VK20 / 100, 

344 ) 

345 ) 

346 

347 M_CAT = matrix_chromatic_adaptation_vk20( 

348 XYZ_p, XYZ_n, XYZ_r, transform, coefficients 

349 ) 

350 XYZ_a = vecmul(M_CAT, XYZ) 

351 

352 return from_range_1(XYZ_a)