Coverage for temperature/cie_d.py: 57%

1"""

2 CIE Illuminant D Series Correlated Colour Temperature

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

4

5 Define the *CIE Illuminant D Series* correlated colour temperature

6:math:`T_{cp}` computation objects.

7

8- :func:`colour.temperature.xy_to_CCT_CIE_D`: Compute correlated colour

9 temperature :math:`T_{cp}` of a *CIE Illuminant D Series* from its

10 *CIE xy* chromaticity coordinates.

11- :func:`colour.temperature.CCT_to_xy_CIE_D`: Compute *CIE xy*

12 chromaticity coordinates of a *CIE Illuminant D Series* from its

13 correlated colour temperature :math:`T_{cp}`.

14

15 References

16----------

17- :cite:`Wyszecki2000z` : Wyszecki, Günther, & Stiles, W. S. (2000). CIE

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

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

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

21"""

22

23 from __future__ import annotations

24

25 import typing

26

27 import numpy as np

28

29 from colour.colorimetry import daylight_locus_function

30

31 if typing.TYPE_CHECKING:

32 from colour.hints import ArrayLike, DTypeFloat, NDArrayFloat

33

34 from colour.utilities import as_float, as_float_array, required, tstack, usage_warning

35

36 __author__ = "Colour Developers"

37 __copyright__ = "Copyright 2013 Colour Developers"

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

39 __maintainer__ = "Colour Developers"

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

41 __status__ = "Production"

42

43 __all__ = [

44 "xy_to_CCT_CIE_D",

45 "CCT_to_xy_CIE_D",

46]

47

48

49@required("SciPy")

50 def xy_to_CCT_CIE_D(

51 xy: ArrayLike, optimisation_kwargs: dict | None = None

52) -> NDArrayFloat:

53 """

54 Compute the correlated colour temperature :math:`T_{cp}` of a

55 *CIE Illuminant D Series* from the specified *CIE xy* chromaticity

56 coordinates.

57

58 Parameters

59 ----------

60 xy

61 *CIE xy* chromaticity coordinates.

62 optimisation_kwargs

63 Parameters for :func:`scipy.optimize.minimize` definition.

64

65 Returns

66 -------

67 :class:`numpy.ndarray`

68 Correlated colour temperature :math:`T_{cp}`.

69

70 Warnings

71 --------

72 The *CIE Illuminant D Series* method does not provide an analytical inverse

73 transformation to compute the correlated colour temperature :math:`T_{cp}`

74 from the specified *CIE xy* chromaticity coordinates. The current

75 implementation relies on optimisation using :func:`scipy.optimize.minimize`

76 definition and thus has reduced precision and poor performance.

77

78 References

79 ----------

80 :cite:`Wyszecki2000z`

81

82 Examples

83 --------

84 >>> xy_to_CCT_CIE_D(np.array([0.31270775, 0.32911283]))

85 ... # doctest: +ELLIPSIS

86 6504.3895840...

87 """

88

89 from scipy.optimize import minimize # noqa: PLC0415

90

91 xy = as_float_array(xy)

92 shape = xy.shape

93 xy = np.atleast_1d(np.reshape(xy, (-1, 2)))

94

95 def objective_function(CCT: NDArrayFloat, xy: NDArrayFloat) -> DTypeFloat:

96 """Objective function."""

97

98 objective = np.linalg.norm(CCT_to_xy_CIE_D(CCT) - xy)

99

100 return as_float(objective)

101

102 optimisation_settings = {

103 "method": "Nelder-Mead",

104 "options": {

105 "fatol": 1e-10,

106 },

107 }

108 if optimisation_kwargs is not None:

109 optimisation_settings.update(optimisation_kwargs)

110

111 CCT = as_float_array(

112 [

113 minimize(

114 objective_function,

115 x0=[6500],

116 args=(xy_i,),

117 **optimisation_settings,

118 ).x

119 for xy_i in xy

120 ]

121 )

122

123 return as_float(np.reshape(CCT, shape[:-1]))

124

125

126 def CCT_to_xy_CIE_D(CCT: ArrayLike) -> NDArrayFloat:

127 """

128 Compute the *CIE xy* chromaticity coordinates of a

129 *CIE Illuminant D Series* from the specified correlated colour temperature

130 :math:`T_{cp}`.

131

132 Parameters

133 ----------

134 CCT

135 Correlated colour temperature :math:`T_{cp}`.

136

137 Returns

138 -------

139 :class:`numpy.ndarray`

140 *CIE xy* chromaticity coordinates.

141

142 Raises

143 ------

144 ValueError

145 If the correlated colour temperature is not in the appropriate

146 domain.

147

148 References

149 ----------

150 :cite:`Wyszecki2000z`

151

152 Examples

153 --------

154 >>> CCT_to_xy_CIE_D(6504.38938305) # doctest: +ELLIPSIS

155 array([ 0.3127077..., 0.3291128...])

156 """

157

158 CCT = as_float_array(CCT)

159

160 if np.any(CCT[np.asarray(np.logical_or(CCT < 4000, CCT > 25000))]):

161 usage_warning(

162 "Correlated colour temperature must be in domain "

163 "[4000, 25000], unpredictable results may occur!"

164 )

165

166 CCT_3 = CCT**3

167 CCT_2 = CCT**2

168

169 x = as_float(

170 np.where(

171 CCT <= 7000,

172 -4.607 * 10**9 / CCT_3

173 + 2.9678 * 10**6 / CCT_2

174 + 0.09911 * 10**3 / CCT

175 + 0.244063,

176 -2.0064 * 10**9 / CCT_3

177 + 1.9018 * 10**6 / CCT_2

178 + 0.24748 * 10**3 / CCT

179 + 0.23704,

180 )

181 )

182

183 y = daylight_locus_function(x)

184

185 return tstack([x, y])