Coverage for models/hdr_cie_lab.py: 65%

1"""

2 hdr-CIELAB Colourspace

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

4

5 Define 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

11 References

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

23 from __future__ import annotations

24

25 import typing

26

27 import numpy as np

28

29 from colour.colorimetry import (

30 CCS_ILLUMINANTS,

31 lightness_Fairchild2010,

32 lightness_Fairchild2011,

33 luminance_Fairchild2010,

34 luminance_Fairchild2011,

35)

36

37 if typing.TYPE_CHECKING:

38 from colour.hints import Literal

39

40 from colour.hints import ( # noqa: TC001

41 ArrayLike,

42 Domain1,

43 Domain100,

44 NDArrayFloat,

45 Range1,

46 Range100,

47)

48 from colour.models import xy_to_xyY, xyY_to_XYZ

49 from 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)

60 from 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

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

77 if is_documentation_building(): # pragma: no cover

78 HDR_CIELAB_METHODS = DocstringTuple(HDR_CIELAB_METHODS)

79 HDR_CIELAB_METHODS.__doc__ = """

80 Supported *hdr-CIELAB* colourspace computation methods.

81

82 References

83----------

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

85"""

86

87

88 def 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

146 def 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

240 def 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)