Coverage for models/hdr_cie_lab.py: 65%

55 statements  

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

1""" 

2hdr-CIELAB Colourspace 

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

4 

5Define the *hdr-CIELAB* colourspace transformations. 

6 

7- :attr:`colour.HDR_CIELAB_METHODS` 

8- :func:`colour.XYZ_to_hdr_CIELab` 

9- :func:`colour.hdr_CIELab_to_XYZ` 

10 

11References 

12---------- 

13- :cite:`Fairchild2010` : Fairchild, M. D., & Wyble, D. R. (2010). 

14 hdr-CIELAB and hdr-IPT: Simple Models for Describing the Color of 

15 High-Dynamic-Range and Wide-Color-Gamut Images. Proc. of Color and 

16 Imaging Conference, 322-326. ISBN:978-1-62993-215-6 

17- :cite:`Fairchild2011` : Fairchild, M. D., & Chen, P. (2011). 

18 Brightness, lightness, and specifying color in high-dynamic-range scenes 

19 and images. In S. P. Farnand & F. Gaykema (Eds.), Proc. SPIE 7867, Image 

20 Quality and System Performance VIII (p. 78670O). doi:10.1117/12.872075 

21""" 

22 

23from __future__ import annotations 

24 

25import typing 

26 

27import numpy as np 

28 

29from colour.colorimetry import ( 

30 CCS_ILLUMINANTS, 

31 lightness_Fairchild2010, 

32 lightness_Fairchild2011, 

33 luminance_Fairchild2010, 

34 luminance_Fairchild2011, 

35) 

36 

37if typing.TYPE_CHECKING: 

38 from colour.hints import Literal 

39 

40from colour.hints import ( # noqa: TC001 

41 ArrayLike, 

42 Domain1, 

43 Domain100, 

44 NDArrayFloat, 

45 Range1, 

46 Range100, 

47) 

48from colour.models import xy_to_xyY, xyY_to_XYZ 

49from colour.utilities import ( 

50 as_float_array, 

51 domain_range_scale, 

52 from_range_1, 

53 from_range_100, 

54 to_domain_1, 

55 to_domain_100, 

56 tsplit, 

57 tstack, 

58 validate_method, 

59) 

60from colour.utilities.documentation import DocstringTuple, is_documentation_building 

61 

62__author__ = "Colour Developers" 

63__copyright__ = "Copyright 2013 Colour Developers" 

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

65__maintainer__ = "Colour Developers" 

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

67__status__ = "Production" 

68 

69__all__ = [ 

70 "HDR_CIELAB_METHODS", 

71 "exponent_hdr_CIELab", 

72 "XYZ_to_hdr_CIELab", 

73 "hdr_CIELab_to_XYZ", 

74] 

75 

76HDR_CIELAB_METHODS: tuple = ("Fairchild 2010", "Fairchild 2011") 

77if is_documentation_building(): # pragma: no cover 

78 HDR_CIELAB_METHODS = DocstringTuple(HDR_CIELAB_METHODS) 

79 HDR_CIELAB_METHODS.__doc__ = """ 

80Supported *hdr-CIELAB* colourspace computation methods. 

81 

82References 

83---------- 

84:cite:`Fairchild2010`, :cite:`Fairchild2011` 

85""" 

86 

87 

88def exponent_hdr_CIELab( 

89 Y_s: Domain1, 

90 Y_abs: ArrayLike, 

91 method: (Literal["Fairchild 2011", "Fairchild 2010"] | str) = "Fairchild 2011", 

92) -> NDArrayFloat: 

93 """ 

94 Compute the *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` 

95 exponent using the *Fairchild and Wyble (2010)* or *Fairchild and Chen 

96 (2011)* methods. 

97 

98 Parameters 

99 ---------- 

100 Y_s 

101 Relative luminance :math:`Y_s` of the surround. 

102 Y_abs 

103 Absolute luminance :math:`Y_{abs}` of the scene diffuse white in 

104 :math:`cd/m^2`. 

105 method 

106 Computation method. 

107 

108 Returns 

109 ------- 

110 :class:`numpy.ndarray` 

111 *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` exponent. 

112 

113 Notes 

114 ----- 

115 +------------+-----------------------+---------------+ 

116 | **Domain** | **Scale - Reference** | **Scale - 1** | 

117 +============+=======================+===============+ 

118 | ``Y_s`` | 1 | 1 | 

119 +------------+-----------------------+---------------+ 

120 

121 Examples 

122 -------- 

123 >>> exponent_hdr_CIELab(0.2, 100) # doctest: +ELLIPSIS 

124 0.4738510... 

125 >>> exponent_hdr_CIELab(0.2, 100, method="Fairchild 2010") 

126 ... # doctest: +ELLIPSIS 

127 1.8360198... 

128 """ 

129 

130 Y_s = to_domain_1(Y_s) 

131 Y_abs = as_float_array(Y_abs) 

132 method = validate_method(method, HDR_CIELAB_METHODS) 

133 

134 epsilon = 1.5 if method == "fairchild 2010" else 0.58 

135 

136 sf = 1.25 - 0.25 * (Y_s / 0.184) 

137 lf = np.log(318) / np.log(Y_abs) 

138 if method == "fairchild 2010": 

139 epsilon *= sf * lf 

140 else: 

141 epsilon /= sf * lf 

142 

143 return epsilon 

144 

145 

146def XYZ_to_hdr_CIELab( 

147 XYZ: Domain1, 

148 illuminant: ArrayLike = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ 

149 "D65" 

150 ], 

151 Y_s: Domain1 = 0.2, 

152 Y_abs: ArrayLike = 100, 

153 method: (Literal["Fairchild 2011", "Fairchild 2010"] | str) = "Fairchild 2011", 

154) -> Range100: 

155 """ 

156 Convert from *CIE XYZ* tristimulus values to *hdr-CIELAB* colourspace. 

157 

158 Parameters 

159 ---------- 

160 XYZ 

161 *CIE XYZ* tristimulus values. 

162 illuminant 

163 Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* 

164 colourspace array. 

165 Y_s 

166 Relative luminance :math:`Y_s` of the surround. 

167 Y_abs 

168 Absolute luminance :math:`Y_{abs}` of the scene diffuse white in 

169 :math:`cd/m^2`. 

170 method 

171 Computation method. 

172 

173 Returns 

174 ------- 

175 :class:`numpy.ndarray` 

176 *hdr-CIELAB* colourspace array. 

177 

178 Notes 

179 ----- 

180 +----------------+-------------------------+---------------------+ 

181 | **Domain** | **Scale - Reference** | **Scale - 1** | 

182 +================+=========================+=====================+ 

183 | ``XYZ`` | 1 | 1 | 

184 +----------------+-------------------------+---------------------+ 

185 | ``illuminant`` | 1 | 1 | 

186 +----------------+-------------------------+---------------------+ 

187 | ``Y_s`` | 1 | 1 | 

188 +----------------+-------------------------+---------------------+ 

189 

190 +----------------+-------------------------+---------------------+ 

191 | **Range** | **Scale - Reference** | **Scale - 1** | 

192 +================+=========================+=====================+ 

193 | ``Lab_hdr`` | 100 | 1 | 

194 +----------------+-------------------------+---------------------+ 

195 

196 - Conversion to polar coordinates to compute the *chroma* 

197 :math:`C_{hdr}` and *hue* :math:`h_{hdr}` correlates can be 

198 safely performed with :func:`colour.Lab_to_LCHab` definition. 

199 - Conversion to cartesian coordinates from the *Lightness* 

200 :math:`L_{hdr}`, *chroma* :math:`C_{hdr}` and *hue* 

201 :math:`h_{hdr}` correlates can be safely performed with 

202 :func:`colour.LCHab_to_Lab` definition. 

203 

204 References 

205 ---------- 

206 :cite:`Fairchild2010`, :cite:`Fairchild2011` 

207 

208 Examples 

209 -------- 

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

211 >>> XYZ_to_hdr_CIELab(XYZ) # doctest: +ELLIPSIS 

212 array([ 51.8700206..., 60.4763385..., 32.1455191...]) 

213 >>> XYZ_to_hdr_CIELab(XYZ, method="Fairchild 2010") # doctest: +ELLIPSIS 

214 array([ 31.9962111..., 128.0076303..., 48.7695230...]) 

215 """ 

216 

217 X, Y, Z = tsplit(to_domain_1(XYZ)) 

218 method = validate_method(method, HDR_CIELAB_METHODS) 

219 

220 X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) 

221 

222 if method == "fairchild 2010": 

223 lightness_callable = lightness_Fairchild2010 

224 else: 

225 lightness_callable = lightness_Fairchild2011 

226 

227 e = exponent_hdr_CIELab(Y_s, Y_abs, method) 

228 

229 # Domain and range scaling has already been handled. 

230 with domain_range_scale("ignore"): 

231 L_hdr = lightness_callable(Y / Y_n, e) 

232 a_hdr = 5 * (lightness_callable(X / X_n, e) - L_hdr) 

233 b_hdr = 2 * (L_hdr - lightness_callable(Z / Z_n, e)) 

234 

235 Lab_hdr = tstack([L_hdr, a_hdr, b_hdr]) 

236 

237 return from_range_100(Lab_hdr) 

238 

239 

240def hdr_CIELab_to_XYZ( 

241 Lab_hdr: Domain100, 

242 illuminant: ArrayLike = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ 

243 "D65" 

244 ], 

245 Y_s: Domain1 = 0.2, 

246 Y_abs: ArrayLike = 100, 

247 method: (Literal["Fairchild 2011", "Fairchild 2010"] | str) = "Fairchild 2011", 

248) -> Range1: 

249 """ 

250 Convert from *hdr-CIELAB* colourspace to *CIE XYZ* tristimulus values. 

251 

252 Parameters 

253 ---------- 

254 Lab_hdr 

255 *hdr-CIELAB* colourspace array. 

256 illuminant 

257 Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* 

258 colourspace array. 

259 Y_s 

260 Relative luminance :math:`Y_s` of the surround. 

261 Y_abs 

262 Absolute luminance :math:`Y_{abs}` of the scene diffuse white in 

263 :math:`cd/m^2`. 

264 method 

265 Computation method. 

266 

267 Returns 

268 ------- 

269 :class:`numpy.ndarray` 

270 *CIE XYZ* tristimulus values. 

271 

272 Notes 

273 ----- 

274 +----------------+-------------------------+---------------------+ 

275 | **Domain** | **Scale - Reference** | **Scale - 1** | 

276 +================+=========================+=====================+ 

277 | ``Lab_hdr`` | 100 | 1 | 

278 +----------------+-------------------------+---------------------+ 

279 | ``illuminant`` | 1 | 1 | 

280 +----------------+-------------------------+---------------------+ 

281 | ``Y_s`` | 1 | 1 | 

282 +----------------+-------------------------+---------------------+ 

283 

284 +----------------+-------------------------+---------------------+ 

285 | **Range** | **Scale - Reference** | **Scale - 1** | 

286 +================+=========================+=====================+ 

287 | ``XYZ`` | 1 | 1 | 

288 +----------------+-------------------------+---------------------+ 

289 

290 References 

291 ---------- 

292 :cite:`Fairchild2010`, :cite:`Fairchild2011` 

293 

294 Examples 

295 -------- 

296 >>> Lab_hdr = np.array([51.87002062, 60.4763385, 32.14551912]) 

297 >>> hdr_CIELab_to_XYZ(Lab_hdr) # doctest: +ELLIPSIS 

298 array([ 0.2065400..., 0.1219722..., 0.0513695...]) 

299 >>> Lab_hdr = np.array([31.99621114, 128.00763036, 48.76952309]) 

300 >>> hdr_CIELab_to_XYZ(Lab_hdr, method="Fairchild 2010") 

301 ... # doctest: +ELLIPSIS 

302 array([ 0.2065400..., 0.1219722..., 0.0513695...]) 

303 """ 

304 

305 L_hdr, a_hdr, b_hdr = tsplit(to_domain_100(Lab_hdr)) 

306 method = validate_method(method, HDR_CIELAB_METHODS) 

307 

308 X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) 

309 

310 if method == "fairchild 2010": 

311 luminance_callable = luminance_Fairchild2010 

312 else: 

313 luminance_callable = luminance_Fairchild2011 

314 

315 e = exponent_hdr_CIELab(Y_s, Y_abs, method) 

316 

317 # Domain and range scaling has already been handled. 

318 with domain_range_scale("ignore"): 

319 Y = luminance_callable(L_hdr, e) * Y_n 

320 X = luminance_callable((a_hdr + 5 * L_hdr) / 5, e) * X_n 

321 Z = luminance_callable((-b_hdr + 2 * L_hdr) / 2, e) * Z_n 

322 

323 XYZ = tstack([X, Y, Z]) 

324 

325 return from_range_1(XYZ)