Coverage for models/rgb/transfer_functions/exponent.py: 81%

1"""

2 Basic and Monitor-Curve Exponent Transfer Functions

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

4

5 Define the exponent transfer functions.

6

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

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

9

10 References

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

19 from __future__ import annotations

20

21 import typing

22

23 from colour.algebra import sdiv, sdiv_mode

24

25 if typing.TYPE_CHECKING:

26 from colour.hints import ArrayLike, Literal, NDArrayFloat

27

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

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

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