Coverage for colour/colorimetry/illuminants.py: 100%

38 statements  

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

1""" 

2Illuminants 

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

4 

5Define objects for computing *CIE* standard illuminants and related daylight 

6calculations: 

7 

8- :func:`colour.sd_CIE_standard_illuminant_A` 

9- :func:`colour.sd_CIE_illuminant_D_series` 

10- :func:`colour.daylight_locus_function` 

11 

12References 

13---------- 

14- :cite:`CIETC1-482004` : CIE TC 1-48. (2004). EXPLANATORY COMMENTS - 5. In 

15 CIE 015:2004 Colorimetry, 3rd Edition (pp. 68-68). ISBN:978-3-901906-33-6 

16- :cite:`CIETC1-482004n` : CIE TC 1-48. (2004). 3.1 Recommendations 

17 concerning standard physical data of illuminants. In CIE 015:2004 

18 Colorimetry, 3rd Edition (pp. 12-13). ISBN:978-3-901906-33-6 

19- :cite:`Wyszecki2000a` : Wyszecki, Günther, & Stiles, W. S. (2000). 

20 Equation I(1.2.1). In Color Science: Concepts and Methods, Quantitative 

21 Data and Formulae (p. 8). Wiley. ISBN:978-0-471-39918-6 

22- :cite:`Wyszecki2000z` : Wyszecki, Günther, & Stiles, W. S. (2000). CIE 

23 Method of Calculating D-Illuminants. In Color Science: Concepts and 

24 Methods, Quantitative Data and Formulae (pp. 145-146). Wiley. 

25 ISBN:978-0-471-39918-6 

26""" 

27 

28from __future__ import annotations 

29 

30import typing 

31 

32import numpy as np 

33 

34from colour.algebra import LinearInterpolator 

35from colour.colorimetry import ( 

36 SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES, 

37 SPECTRAL_SHAPE_DEFAULT, 

38 SpectralDistribution, 

39 SpectralShape, 

40 reshape_sd, 

41) 

42 

43if typing.TYPE_CHECKING: 

44 from colour.hints import ArrayLike, NDArrayFloat 

45 

46from colour.utilities import as_float, as_float_array, tsplit 

47 

48__author__ = "Colour Developers" 

49__copyright__ = "Copyright 2013 Colour Developers" 

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

51__maintainer__ = "Colour Developers" 

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

53__status__ = "Production" 

54 

55__all__ = [ 

56 "sd_CIE_standard_illuminant_A", 

57 "sd_CIE_illuminant_D_series", 

58 "daylight_locus_function", 

59] 

60 

61 

62def sd_CIE_standard_illuminant_A( 

63 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, 

64) -> SpectralDistribution: 

65 """ 

66 Represent typical domestic tungsten-filament lighting according to 

67 *CIE Standard Illuminant A*. 

68 

69 Generate spectral distribution of *CIE Standard Illuminant A*, which 

70 represents the spectral characteristics of a Planckian radiator at a 

71 temperature of approximately 2856 K. This illuminant should be used in 

72 all colorimetry applications involving incandescent lighting, unless 

73 there are specific reasons for using a different illuminant. 

74 

75 Parameters 

76 ---------- 

77 shape 

78 Spectral shape used to create the spectral distribution of the 

79 *CIE Standard Illuminant A*. 

80 

81 Returns 

82 ------- 

83 :class:`colour.SpectralDistribution` 

84 *CIE Standard Illuminant A* spectral distribution. 

85 

86 References 

87 ---------- 

88 :cite:`CIETC1-482004n` 

89 

90 Examples 

91 -------- 

92 >>> from colour import SpectralShape 

93 >>> sd_CIE_standard_illuminant_A(SpectralShape(400, 700, 10)) 

94 ... # doctest: +ELLIPSIS 

95 SpectralDistribution([[ 400. , 14.7080384...], 

96 [ 410. , 17.6752521...], 

97 [ 420. , 20.9949572...], 

98 [ 430. , 24.6709226...], 

99 [ 440. , 28.7027304...], 

100 [ 450. , 33.0858929...], 

101 [ 460. , 37.8120566...], 

102 [ 470. , 42.8692762...], 

103 [ 480. , 48.2423431...], 

104 [ 490. , 53.9131532...], 

105 [ 500. , 59.8610989...], 

106 [ 510. , 66.0634727...], 

107 [ 520. , 72.4958719...], 

108 [ 530. , 79.1325945...], 

109 [ 540. , 85.9470183...], 

110 [ 550. , 92.9119589...], 

111 [ 560. , 100. ...], 

112 [ 570. , 107.1837952...], 

113 [ 580. , 114.4363383...], 

114 [ 590. , 121.7312009...], 

115 [ 600. , 129.0427389...], 

116 [ 610. , 136.3462674...], 

117 [ 620. , 143.6182057...], 

118 [ 630. , 150.8361944...], 

119 [ 640. , 157.9791857...], 

120 [ 650. , 165.0275098...], 

121 [ 660. , 171.9629200...], 

122 [ 670. , 178.7686175...], 

123 [ 680. , 185.4292591...], 

124 [ 690. , 191.9309499...], 

125 [ 700. , 198.2612232...]], 

126 SpragueInterpolator, 

127 {}, 

128 Extrapolator, 

129 {'method': 'Constant', 'left': None, 'right': None}) 

130 """ 

131 

132 values = ( 

133 100 

134 * (560 / shape.wavelengths) ** 5 

135 * ( 

136 np.expm1((1.435 * 10**7) / (2848 * 560)) 

137 / np.expm1((1.435 * 10**7) / (2848 * shape.wavelengths)) 

138 ) 

139 ) 

140 

141 return SpectralDistribution( 

142 values, shape.wavelengths, name="CIE Standard Illuminant A" 

143 ) 

144 

145 

146def sd_CIE_illuminant_D_series( 

147 xy: ArrayLike, 

148 M1_M2_rounding: bool = True, 

149 shape: SpectralShape | None = None, 

150) -> SpectralDistribution: 

151 """ 

152 Return the spectral distribution of the specified *CIE Illuminant D 

153 Series* using the specified *CIE xy* chromaticity coordinates. 

154 

155 Parameters 

156 ---------- 

157 xy 

158 *CIE xy* chromaticity coordinates. 

159 M1_M2_rounding 

160 Whether to round :math:`M1` and :math:`M2` variables to 3 decimal 

161 places in order to yield the internationally agreed values. 

162 shape 

163 Specifies the shape of the returned SpectralDistribution. Optional, 

164 default None. 

165 

166 Returns 

167 ------- 

168 :class:`colour.SpectralDistribution` 

169 *CIE Illuminant D Series* spectral distribution. 

170 

171 Notes 

172 ----- 

173 - The nominal *CIE xy* chromaticity coordinates which have been 

174 computed with :func:`colour.temperature.CCT_to_xy_CIE_D` must be 

175 specified according to *CIE 015:2004* recommendation and thus 

176 multiplied by 1.4388 / 1.4380. 

177 - :math:`M1` and :math:`M2` variables are rounded to 3 decimal places 

178 according to *CIE 015:2004* recommendation. 

179 

180 References 

181 ---------- 

182 :cite:`CIETC1-482004`, :cite:`Wyszecki2000z` 

183 

184 Examples 

185 -------- 

186 >>> from colour.utilities import numpy_print_options 

187 >>> from colour.temperature import CCT_to_xy_CIE_D 

188 >>> CCT_D65 = 6500 * 1.4388 / 1.4380 

189 >>> xy = CCT_to_xy_CIE_D(CCT_D65) 

190 >>> with numpy_print_options(suppress=True): 

191 ... sd_CIE_illuminant_D_series(xy) # doctest: +ELLIPSIS 

192 SpectralDistribution([[ 300. , 0.0341...], 

193 [ 305. , 1.6643...], 

194 [ 310. , 3.2945...], 

195 [ 315. , 11.7652...], 

196 [ 320. , 20.236 ...], 

197 [ 325. , 28.6447...], 

198 [ 330. , 37.0535...], 

199 [ 335. , 38.5011...], 

200 [ 340. , 39.9488...], 

201 [ 345. , 42.4302...], 

202 [ 350. , 44.9117...], 

203 [ 355. , 45.775 ...], 

204 [ 360. , 46.6383...], 

205 [ 365. , 49.3637...], 

206 [ 370. , 52.0891...], 

207 [ 375. , 51.0323...], 

208 [ 380. , 49.9755...], 

209 [ 385. , 52.3118...], 

210 [ 390. , 54.6482...], 

211 [ 395. , 68.7015...], 

212 [ 400. , 82.7549...], 

213 [ 405. , 87.1204...], 

214 [ 410. , 91.486 ...], 

215 [ 415. , 92.4589...], 

216 [ 420. , 93.4318...], 

217 [ 425. , 90.0570...], 

218 [ 430. , 86.6823...], 

219 [ 435. , 95.7736...], 

220 [ 440. , 104.8649...], 

221 [ 445. , 110.9362...], 

222 [ 450. , 117.0076...], 

223 [ 455. , 117.4099...], 

224 [ 460. , 117.8122...], 

225 [ 465. , 116.3365...], 

226 [ 470. , 114.8609...], 

227 [ 475. , 115.3919...], 

228 [ 480. , 115.9229...], 

229 [ 485. , 112.3668...], 

230 [ 490. , 108.8107...], 

231 [ 495. , 109.0826...], 

232 [ 500. , 109.3545...], 

233 [ 505. , 108.5781...], 

234 [ 510. , 107.8017...], 

235 [ 515. , 106.2957...], 

236 [ 520. , 104.7898...], 

237 [ 525. , 106.2396...], 

238 [ 530. , 107.6895...], 

239 [ 535. , 106.0475...], 

240 [ 540. , 104.4055...], 

241 [ 545. , 104.2258...], 

242 [ 550. , 104.0462...], 

243 [ 555. , 102.0231...], 

244 [ 560. , 100. ...], 

245 [ 565. , 98.1671...], 

246 [ 570. , 96.3342...], 

247 [ 575. , 96.0611...], 

248 [ 580. , 95.788 ...], 

249 [ 585. , 92.2368...], 

250 [ 590. , 88.6856...], 

251 [ 595. , 89.3459...], 

252 [ 600. , 90.0062...], 

253 [ 605. , 89.8026...], 

254 [ 610. , 89.5991...], 

255 [ 615. , 88.6489...], 

256 [ 620. , 87.6987...], 

257 [ 625. , 85.4936...], 

258 [ 630. , 83.2886...], 

259 [ 635. , 83.4939...], 

260 [ 640. , 83.6992...], 

261 [ 645. , 81.863 ...], 

262 [ 650. , 80.0268...], 

263 [ 655. , 80.1207...], 

264 [ 660. , 80.2146...], 

265 [ 665. , 81.2462...], 

266 [ 670. , 82.2778...], 

267 [ 675. , 80.281 ...], 

268 [ 680. , 78.2842...], 

269 [ 685. , 74.0027...], 

270 [ 690. , 69.7213...], 

271 [ 695. , 70.6652...], 

272 [ 700. , 71.6091...], 

273 [ 705. , 72.9790...], 

274 [ 710. , 74.349 ...], 

275 [ 715. , 67.9765...], 

276 [ 720. , 61.604 ...], 

277 [ 725. , 65.7448...], 

278 [ 730. , 69.8856...], 

279 [ 735. , 72.4863...], 

280 [ 740. , 75.087 ...], 

281 [ 745. , 69.3398...], 

282 [ 750. , 63.5927...], 

283 [ 755. , 55.0054...], 

284 [ 760. , 46.4182...], 

285 [ 765. , 56.6118...], 

286 [ 770. , 66.8054...], 

287 [ 775. , 65.0941...], 

288 [ 780. , 63.3828...], 

289 [ 785. , 63.8434...], 

290 [ 790. , 64.304 ...], 

291 [ 795. , 61.8779...], 

292 [ 800. , 59.4519...], 

293 [ 805. , 55.7054...], 

294 [ 810. , 51.959 ...], 

295 [ 815. , 54.6998...], 

296 [ 820. , 57.4406...], 

297 [ 825. , 58.8765...], 

298 [ 830. , 60.3125...]], 

299 LinearInterpolator, 

300 {}, 

301 Extrapolator, 

302 {'method': 'Constant', 'left': None, 'right': None}) 

303 """ 

304 

305 xy = as_float_array(xy) 

306 

307 x, y = tsplit(xy) 

308 

309 M = 0.0241 + 0.2562 * x - 0.7341 * y 

310 M1 = (-1.3515 - 1.7703 * x + 5.9114 * y) / M 

311 M2 = (0.0300 - 31.4424 * x + 30.0717 * y) / M 

312 

313 if M1_M2_rounding: 

314 M1 = np.around(M1, 3) 

315 M2 = np.around(M2, 3) 

316 

317 S0 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S0"] 

318 S1 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S1"] 

319 S2 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S2"] 

320 

321 if shape is not None: 

322 S0 = reshape_sd(S0, shape=shape, copy=False) 

323 S1 = reshape_sd(S1, shape=shape, copy=False) 

324 S2 = reshape_sd(S2, shape=shape, copy=False) 

325 

326 distribution = S0.values + M1 * S1.values + M2 * S2.values 

327 

328 return SpectralDistribution( 

329 distribution, 

330 S0.wavelengths, 

331 name=f"CIE xy ({xy[0]}, {xy[1]}) - CIE Illuminant D Series", 

332 interpolator=LinearInterpolator, 

333 ) 

334 

335 

336def daylight_locus_function(x_D: ArrayLike) -> NDArrayFloat: 

337 """ 

338 Compute the *CIE y* chromaticity coordinate on the daylight locus. 

339 

340 Calculate the *CIE y* chromaticity coordinate on the daylight locus 

341 for the specified *CIE x* chromaticity coordinate :math:`x_D` using 

342 the quadratic relationship defined by *Wyszecki and Stiles*. 

343 

344 Parameters 

345 ---------- 

346 x_D 

347 *CIE x* chromaticity coordinate :math:`x_D` for which to compute 

348 the corresponding *CIE y* coordinate on the daylight locus. 

349 

350 Returns 

351 ------- 

352 :class:`numpy.ndarray` 

353 *CIE y* chromaticity coordinate on the daylight locus 

354 corresponding to the specified :math:`x_D`. 

355 

356 References 

357 ---------- 

358 :cite:`Wyszecki2000a` 

359 

360 Examples 

361 -------- 

362 >>> daylight_locus_function(0.31270) # doctest: +ELLIPSIS 

363 0.3291051... 

364 """ 

365 

366 x_D = as_float_array(x_D) 

367 

368 y_D = -3.000 * x_D**2 + 2.870 * x_D - 0.275 

369 

370 return as_float(y_D)