Coverage for models/hdr_ipt.py: 62%

1"""

2 hdr-IPT Colourspace

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

4

5 Define the *hdr-IPT* colourspace transformations.

6

7- :attr:`colour.HDR_IPT_METHODS`

8- :func:`colour.XYZ_to_hdr_IPT`

9- :func:`colour.hdr_IPT_to_XYZ`

10

11 References

12----------

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

14 and hdr-IPT: Simple Models for Describing the Color of High-Dynamic-Range

15 and Wide-Color-Gamut Images. Proc. of Color and Imaging Conference,

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

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

18 lightness, and specifying color in high-dynamic-range scenes and images. In

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

20 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.algebra import vecmul

30 from colour.colorimetry import (

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.ipt import (

49 MATRIX_IPT_IPT_TO_LMS_P,

50 MATRIX_IPT_LMS_P_TO_IPT,

51 MATRIX_IPT_LMS_TO_XYZ,

52 MATRIX_IPT_XYZ_TO_LMS,

53)

54 from colour.utilities import (

55 as_float_array,

56 domain_range_scale,

57 from_range_1,

58 from_range_100,

59 to_domain_1,

60 to_domain_100,

61 validate_method,

62)

63 from colour.utilities.documentation import DocstringTuple, is_documentation_building

64

65 __author__ = "Colour Developers"

66 __copyright__ = "Copyright 2013 Colour Developers"

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

68 __maintainer__ = "Colour Developers"

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

70 __status__ = "Production"

71

72 __all__ = [

73 "HDR_IPT_METHODS",

74 "exponent_hdr_IPT",

75 "XYZ_to_hdr_IPT",

76 "hdr_IPT_to_XYZ",

77]

78

79 HDR_IPT_METHODS: tuple = ("Fairchild 2010", "Fairchild 2011")

80 if is_documentation_building(): # pragma: no cover

81 HDR_IPT_METHODS = DocstringTuple(HDR_IPT_METHODS)

82 HDR_IPT_METHODS.__doc__ = """

83 Supported *hdr-IPT* colourspace computation methods.

84

85 References

86----------

87:cite:`Fairchild2010`, :cite:`Fairchild2011`

88"""

89

90

91 def exponent_hdr_IPT(

92 Y_s: Domain1,

93 Y_abs: ArrayLike,

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

95) -> NDArrayFloat:

96 """

97 Compute the *hdr-IPT* colourspace *Lightness* :math:`\\epsilon` exponent

98 using the *Fairchild and Wyble (2010)* or *Fairchild and Chen (2011)*

99 methods.

100

101 Parameters

102 ----------

103 Y_s

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

105 Y_abs

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

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

108 method

109 Computation method.

110

111 Returns

112 -------

113 :class:`numpy.ndarray`

114 *hdr-IPT* colourspace *Lightness* :math:`\\epsilon` exponent.

115

116 Notes

117 -----

118 +------------+-----------------------+---------------+

119 | **Domain** | **Scale - Reference** | **Scale - 1** |

120 +============+=======================+===============+

121 | ``Y_s`` | 1 | 1 |

122 +------------+-----------------------+---------------+

123

124 Examples

125 --------

126 >>> exponent_hdr_IPT(0.2, 100) # doctest: +ELLIPSIS

127 0.4820209...

128 >>> exponent_hdr_IPT(0.2, 100, method="Fairchild 2010")

129 ... # doctest: +ELLIPSIS

130 1.6891383...

131 """

132

133 Y_s = to_domain_1(Y_s)

134 Y_abs = as_float_array(Y_abs)

135 method = validate_method(method, HDR_IPT_METHODS)

136

137 epsilon = 1.38 if method == "fairchild 2010" else 0.59

138

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

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

141 if method == "fairchild 2010":

142 epsilon *= sf * lf

143 else:

144 epsilon /= sf * lf

145

146 return epsilon

147

148

149 def XYZ_to_hdr_IPT(

150 XYZ: Domain1,

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-IPT* colourspace.

157

158 Parameters

159 ----------

160 XYZ

161 *CIE XYZ* tristimulus values.

162 Y_s

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

164 Y_abs

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

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

167 method

168 Computation method.

169

170 Returns

171 -------

172 :class:`numpy.ndarray`

173 *hdr-IPT* colourspace array.

174

175 Notes

176 -----

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

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

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

180 | ``XYZ`` | 1 | 1 |

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

182 | ``Y_s`` | 1 | 1 |

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

184

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

186 | **Range** | **Scale - Reference** | **Scale - 1** |

187 +=============+=========================+=====================+

188 | ``IPT_hdr`` | 100 | 1 |

189 +-------------+-------------------------+---------------------+

190

191 - Input *CIE XYZ* tristimulus values must be adapted to

192 *CIE Standard Illuminant D Series* *D65*.

193

194 References

195 ----------

196 :cite:`Fairchild2010`, :cite:`Fairchild2011`

197

198 Examples

199 --------

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

201 >>> XYZ_to_hdr_IPT(XYZ) # doctest: +ELLIPSIS

202 array([ 48.3937634..., 42.4499020..., 22.0195403...])

203 >>> XYZ_to_hdr_IPT(XYZ, method="Fairchild 2010") # doctest: +ELLIPSIS

204 array([ 30.0287314..., 83.9384506..., 34.9028738...])

205 """

206

207 XYZ = to_domain_1(XYZ)

208 method = validate_method(method, HDR_IPT_METHODS)

209

210 if method == "fairchild 2010":

211 lightness_callable = lightness_Fairchild2010

212 else:

213 lightness_callable = lightness_Fairchild2011

214

215 e = exponent_hdr_IPT(Y_s, Y_abs, method)[..., None]

216

217 LMS = vecmul(MATRIX_IPT_XYZ_TO_LMS, XYZ)

218

219 # Domain and range scaling has already been handled.

220 with domain_range_scale("ignore"):

221 LMS_prime = np.sign(LMS) * np.abs(lightness_callable(LMS, e))

222

223 IPT_hdr = vecmul(MATRIX_IPT_LMS_P_TO_IPT, LMS_prime)

224

225 return from_range_100(IPT_hdr)

226

227

228 def hdr_IPT_to_XYZ(

229 IPT_hdr: Domain100,

230 Y_s: Domain1 = 0.2,

231 Y_abs: ArrayLike = 100,

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

233) -> Range1:

234 """

235 Convert from *hdr-IPT* colourspace to *CIE XYZ* tristimulus values.

236

237 Parameters

238 ----------

239 IPT_hdr

240 *hdr-IPT* colourspace array.

241 Y_s

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

243 Y_abs

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

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

246 method

247 Computation method.

248

249 Returns

250 -------

251 :class:`numpy.ndarray`

252 *CIE XYZ* tristimulus values.

253

254 Notes

255 -----

256 +-------------+-------------------------+---------------------+

257 | **Domain** | **Scale - Reference** | **Scale - 1** |

258 +=============+=========================+=====================+

259 | ``IPT_hdr`` | 100 | 1 |

260 +-------------+-------------------------+---------------------+

261 | ``Y_s`` | 1 | 1 |

262 +-------------+-------------------------+---------------------+

263

264 +-------------+-------------------------+---------------------+

265 | **Range** | **Scale - Reference** | **Scale - 1** |

266 +=============+=========================+=====================+

267 | ``XYZ`` | 1 | 1 |

268 +-------------+-------------------------+---------------------+

269

270 References

271 ----------

272 :cite:`Fairchild2010`, :cite:`Fairchild2011`

273

274 Examples

275 --------

276 >>> IPT_hdr = np.array([48.39376346, 42.44990202, 22.01954033])

277 >>> hdr_IPT_to_XYZ(IPT_hdr) # doctest: +ELLIPSIS

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

279 >>> IPT_hdr = np.array([30.02873147, 83.93845061, 34.90287382])

280 >>> hdr_IPT_to_XYZ(IPT_hdr, method="Fairchild 2010")

281 ... # doctest: +ELLIPSIS

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

283 """

284

285 IPT_hdr = to_domain_100(IPT_hdr)

286 method = validate_method(method, HDR_IPT_METHODS)

287

288 if method == "fairchild 2010":

289 luminance_callable = luminance_Fairchild2010

290 else:

291 luminance_callable = luminance_Fairchild2011

292

293 e = exponent_hdr_IPT(Y_s, Y_abs, method)[..., None]

294

295 LMS = vecmul(MATRIX_IPT_IPT_TO_LMS_P, IPT_hdr)

296

297 # Domain and range scaling has already be handled.

298 with domain_range_scale("ignore"):

299 LMS_prime = np.sign(LMS) * np.abs(luminance_callable(LMS, e))

300

301 XYZ = vecmul(MATRIX_IPT_LMS_TO_XYZ, LMS_prime)

302

303 return from_range_1(XYZ)