Coverage for colour/models/rgb/transfer_functions/exponent.py: 100%

63 statements  

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

1""" 

2Basic and Monitor-Curve Exponent Transfer Functions 

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

4 

5Define the exponent transfer functions. 

6 

7- :func:`colour.models.exponent_function_basic` 

8- :func:`colour.models.exponent_function_monitor_curve` 

9 

10References 

11---------- 

12- :cite: `TheAcademyofMotionPictureArtsandSciences2020` : The Academy of 

13 Motion Picture Arts and Sciences, Science and Technology Council, & Academy 

14 Color Encoding System (ACES) Project Subcommittee. (2020). Specification 

15 S-2014-006 - Common LUT Format (CLF) - A Common File Format for Look-Up 

16 Tables. Retrieved June 24, 2020, from http://j.mp/S-2014-006 

17""" 

18 

19from __future__ import annotations 

20 

21import typing 

22 

23from colour.algebra import sdiv, sdiv_mode 

24 

25if typing.TYPE_CHECKING: 

26 from colour.hints import ArrayLike, Literal, NDArrayFloat 

27 

28from colour.utilities import as_float, as_float_array, validate_method, zeros 

29 

30__author__ = "Colour Developers" 

31__copyright__ = "Copyright 2013 Colour Developers" 

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

33__maintainer__ = "Colour Developers" 

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

35__status__ = "Production" 

36 

37__all__ = [ 

38 "exponent_function_basic", 

39 "exponent_function_monitor_curve", 

40] 

41 

42 

43def exponent_function_basic( 

44 x: ArrayLike, 

45 exponent: ArrayLike = 1, 

46 style: ( 

47 Literal[ 

48 "basicFwd", 

49 "basicRev", 

50 "basicMirrorFwd", 

51 "basicMirrorRev", 

52 "basicPassThruFwd", 

53 "basicPassThruRev", 

54 ] 

55 | str 

56 ) = "basicFwd", 

57) -> NDArrayFloat: 

58 """ 

59 Apply a *basic* exponent transfer function to the specified array. 

60 

61 Parameters 

62 ---------- 

63 x 

64 Exponentially encoded data :math:`x`. 

65 exponent 

66 Exponent value used for the conversion. 

67 style 

68 Specifies the behaviour for the exponentiation function to operate: 

69 

70 - *basicFwd*: *Basic Forward* exponential behaviour where the 

71 definition applies a basic power law using the exponent. 

72 Values less than zero are clamped. 

73 - *basicRev*: *Basic Reverse* exponential behaviour where the 

74 definition applies a basic power law using the exponent. 

75 Values less than zero are clamped. 

76 - *basicMirrorFwd*: *Basic Mirror Forward* exponential 

77 behaviour where the definition applies a basic power law 

78 using the exponent for values greater than or equal to zero 

79 and mirrors the function for values less than zero (i.e., 

80 rotationally symmetric around the origin). 

81 - *basicMirrorRev*: *Basic Mirror Reverse* exponential 

82 behaviour where the definition applies a basic power law 

83 using the exponent for values greater than or equal to zero 

84 and mirrors the function for values less than zero (i.e., 

85 rotationally symmetric around the origin). 

86 - *basicPassThruFwd*: *Basic Pass Forward* exponential 

87 behaviour where the definition applies a basic power law 

88 using the exponent for values greater than or equal to zero 

89 and passes values less than zero unchanged. 

90 - *basicPassThruRev*: *Basic Pass Reverse* exponential 

91 behaviour where the definition applies a basic power law 

92 using the exponent for values greater than or equal to zero 

93 and passes values less than zero unchanged. 

94 

95 Returns 

96 ------- 

97 :class:`numpy.ndarray` 

98 Exponentially converted data. 

99 

100 Examples 

101 -------- 

102 >>> exponent_function_basic(0.18, 2.2) # doctest: +ELLIPSIS 

103 0.0229932... 

104 >>> exponent_function_basic(-0.18, 2.2) 

105 0.0 

106 >>> exponent_function_basic(0.18, 2.2, "basicRev") # doctest: +ELLIPSIS 

107 0.4586564... 

108 >>> exponent_function_basic(-0.18, 2.2, "basicRev") 

109 0.0 

110 >>> exponent_function_basic( # doctest: +ELLIPSIS 

111 ... 0.18, 2.2, "basicMirrorFwd" 

112 ... ) 

113 0.0229932... 

114 >>> exponent_function_basic( # doctest: +ELLIPSIS 

115 ... -0.18, 2.2, "basicMirrorFwd" 

116 ... ) 

117 -0.0229932... 

118 >>> exponent_function_basic( # doctest: +ELLIPSIS 

119 ... 0.18, 2.2, "basicMirrorRev" 

120 ... ) 

121 0.4586564... 

122 >>> exponent_function_basic( # doctest: +ELLIPSIS 

123 ... -0.18, 2.2, "basicMirrorRev" 

124 ... ) 

125 -0.4586564... 

126 >>> exponent_function_basic( # doctest: +ELLIPSIS 

127 ... 0.18, 2.2, "basicPassThruFwd" 

128 ... ) 

129 0.0229932... 

130 >>> exponent_function_basic( # doctest: +ELLIPSIS 

131 ... -0.18, 2.2, "basicPassThruFwd" 

132 ... ) 

133 -0.1799999... 

134 >>> exponent_function_basic( # doctest: +ELLIPSIS 

135 ... 0.18, 2.2, "basicPassThruRev" 

136 ... ) 

137 0.4586564... 

138 >>> exponent_function_basic( # doctest: +ELLIPSIS 

139 ... -0.18, 2.2, "basicPassThruRev" 

140 ... ) 

141 -0.1799999... 

142 """ 

143 

144 x = as_float_array(x) 

145 exponent = as_float_array(exponent) 

146 style = validate_method( 

147 style, 

148 ( 

149 "basicFwd", 

150 "basicRev", 

151 "basicMirrorFwd", 

152 "basicMirrorRev", 

153 "basicPassThruFwd", 

154 "basicPassThruRev", 

155 ), 

156 '"{0}" style is invalid, it must be one of {1}!', 

157 ) 

158 

159 def exponent_forward(x: NDArrayFloat) -> NDArrayFloat: 

160 """Return the input raised to the exponent value.""" 

161 

162 return x**exponent 

163 

164 def exponent_reverse(y: NDArrayFloat) -> NDArrayFloat: 

165 """Return the input raised to the inverse exponent value.""" 

166 

167 return y ** (as_float_array(1) / exponent) 

168 

169 y = zeros(x.shape) 

170 m_x = x >= 0 

171 if style == "basicfwd": 

172 y[m_x] = exponent_forward(x[m_x]) 

173 elif style == "basicrev": 

174 y[m_x] = exponent_reverse(x[m_x]) 

175 elif style == "basicmirrorfwd": 

176 y[m_x] = exponent_forward(x[m_x]) 

177 y[~m_x] = -exponent_forward(-x[~m_x]) 

178 elif style == "basicmirrorrev": 

179 y[m_x] = exponent_reverse(x[m_x]) 

180 y[~m_x] = -exponent_reverse(-x[~m_x]) 

181 elif style == "basicpassthrufwd": 

182 y[m_x] = exponent_forward(x[m_x]) 

183 y[~m_x] = x[~m_x] 

184 else: # style == 'basicpassthrurev' 

185 y[m_x] = exponent_reverse(x[m_x]) 

186 y[~m_x] = x[~m_x] 

187 

188 return as_float(y) 

189 

190 

191def exponent_function_monitor_curve( 

192 x: ArrayLike, 

193 exponent: ArrayLike = 1, 

194 offset: ArrayLike = 0, 

195 style: ( 

196 Literal[ 

197 "monCurveFwd", 

198 "monCurveRev", 

199 "monCurveMirrorFwd", 

200 "monCurveMirrorRev", 

201 ] 

202 | str 

203 ) = "monCurveFwd", 

204) -> NDArrayFloat: 

205 """ 

206 Apply the *Monitor Curve* exponent transfer function to the specified array. 

207 

208 Parameters 

209 ---------- 

210 x 

211 Exponentially encoded data :math:`x`. 

212 exponent 

213 Exponent value used for the conversion. 

214 offset 

215 Offset value used for the conversion. 

216 style 

217 Specifies the behaviour for the exponentiation function to operate: 

218 

219 - *monCurveFwd*: *Monitor Curve Forward* exponential behaviour 

220 where the definition applies a power law function with a 

221 linear segment near the origin. 

222 - *monCurveRev*: *Monitor Curve Reverse* exponential behaviour 

223 where the definition applies a power law function with a 

224 linear segment near the origin. 

225 - *monCurveMirrorFwd*: *Monitor Curve Mirror Forward* 

226 exponential behaviour where the definition applies a power law 

227 function with a linear segment near the origin and mirrors the 

228 function for values less than zero (i.e., rotationally 

229 symmetric around the origin). 

230 - *monCurveMirrorRev*: *Monitor Curve Mirror Reverse* 

231 exponential behaviour where the definition applies a power law 

232 function with a linear segment near the origin and mirrors the 

233 function for values less than zero (i.e., rotationally 

234 symmetric around the origin). 

235 

236 Returns 

237 ------- 

238 :class:`numpy.ndarray` 

239 Exponentially converted data. 

240 

241 Examples 

242 -------- 

243 >>> exponent_function_monitor_curve(0.18, 2.2, 0.001) # doctest: +ELLIPSIS 

244 0.0232240... 

245 >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS 

246 ... -0.18, 2.2, 0.001 

247 ... ) 

248 -0.0002054... 

249 >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS 

250 ... 0.18, 2.2, 0.001, "monCurveRev" 

251 ... ) 

252 0.4581151... 

253 >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS 

254 ... -0.18, 2.2, 0.001, "monCurveRev" 

255 ... ) 

256 -157.7302795... 

257 >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS 

258 ... 0.18, 2.2, 2, "monCurveMirrorFwd" 

259 ... ) 

260 0.1679399... 

261 >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS 

262 ... -0.18, 2.2, 0.001, "monCurveMirrorFwd" 

263 ... ) 

264 -0.0232240... 

265 >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS 

266 ... 0.18, 2.2, 0.001, "monCurveMirrorRev" 

267 ... ) 

268 0.4581151... 

269 >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS 

270 ... -0.18, 2.2, 0.001, "monCurveMirrorRev" 

271 ... ) 

272 -0.4581151... 

273 """ 

274 

275 x = as_float_array(x) 

276 exponent = as_float_array(exponent) 

277 offset = as_float_array(offset) 

278 style = validate_method( 

279 style, 

280 ( 

281 "monCurveFwd", 

282 "monCurveRev", 

283 "monCurveMirrorFwd", 

284 "monCurveMirrorRev", 

285 ), 

286 '"{0}" style is invalid, it must be one of {1}!', 

287 ) 

288 

289 with sdiv_mode(): 

290 s = as_float_array( 

291 sdiv(exponent - 1, offset) 

292 * sdiv(exponent * offset, (exponent - 1) * (offset + 1)) ** exponent 

293 ) 

294 

295 def monitor_curve_forward( 

296 x: NDArrayFloat, offset: NDArrayFloat, exponent: NDArrayFloat 

297 ) -> NDArrayFloat: 

298 """Define the *Monitor Curve Forward* function.""" 

299 

300 with sdiv_mode(): 

301 x_break = sdiv(offset, exponent - 1) 

302 

303 y = as_float_array(x * s) 

304 

305 y[x >= x_break] = ((x[x >= x_break] + offset) / (1 + offset)) ** exponent 

306 

307 return y 

308 

309 def monitor_curve_reverse( 

310 y: NDArrayFloat, offset: NDArrayFloat, exponent: NDArrayFloat 

311 ) -> NDArrayFloat: 

312 """Define the *Monitor Curve Reverse* function.""" 

313 

314 with sdiv_mode(): 

315 y_break = ( 

316 sdiv(exponent * offset, (exponent - 1) * (1 + offset)) 

317 ) ** exponent 

318 

319 x = as_float_array(y / s) 

320 

321 x[y >= y_break] = ((1 + offset) * (y[y >= y_break] ** (1 / exponent))) - offset 

322 

323 return x 

324 

325 y = zeros(x.shape) 

326 m_x = x >= 0 

327 if style == "moncurvefwd": 

328 y = monitor_curve_forward(x, offset, exponent) 

329 elif style == "moncurverev": 

330 y = monitor_curve_reverse(x, offset, exponent) 

331 elif style == "moncurvemirrorfwd": 

332 y[m_x] = monitor_curve_forward(x[m_x], offset, exponent) 

333 y[~m_x] = -monitor_curve_forward(-x[~m_x], offset, exponent) 

334 else: # style == 'moncurvemirrorrev' 

335 y[m_x] = monitor_curve_reverse(x[m_x], offset, exponent) 

336 y[~m_x] = -monitor_curve_reverse(-x[~m_x], offset, exponent) 

337 

338 return as_float(y)