Coverage for models/rgb/transfer_functions/exponent.py: 81%
68 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
1"""
2Basic and Monitor-Curve Exponent Transfer Functions
3===================================================
5Define the exponent transfer functions.
7- :func:`colour.models.exponent_function_basic`
8- :func:`colour.models.exponent_function_monitor_curve`
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"""
19from __future__ import annotations
21import typing
23from colour.algebra import sdiv, sdiv_mode
25if typing.TYPE_CHECKING:
26 from colour.hints import ArrayLike, Literal, NDArrayFloat
28from colour.utilities import as_float, as_float_array, validate_method, zeros
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"
37__all__ = [
38 "exponent_function_basic",
39 "exponent_function_monitor_curve",
40]
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.
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:
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.
95 Returns
96 -------
97 :class:`numpy.ndarray`
98 Exponentially converted data.
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 """
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 )
159 def exponent_forward(x: NDArrayFloat) -> NDArrayFloat:
160 """Return the input raised to the exponent value."""
162 return x**exponent
164 def exponent_reverse(y: NDArrayFloat) -> NDArrayFloat:
165 """Return the input raised to the inverse exponent value."""
167 return y ** (as_float_array(1) / exponent)
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]
188 return as_float(y)
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.
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:
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).
236 Returns
237 -------
238 :class:`numpy.ndarray`
239 Exponentially converted data.
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 """
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 )
289 with sdiv_mode():
290 s = as_float_array(
291 sdiv(exponent - 1, offset)
292 * sdiv(exponent * offset, (exponent - 1) * (offset + 1)) ** exponent
293 )
295 def monitor_curve_forward(
296 x: NDArrayFloat, offset: NDArrayFloat, exponent: NDArrayFloat
297 ) -> NDArrayFloat:
298 """Define the *Monitor Curve Forward* function."""
300 with sdiv_mode():
301 x_break = sdiv(offset, exponent - 1)
303 y = as_float_array(x * s)
305 y[x >= x_break] = ((x[x >= x_break] + offset) / (1 + offset)) ** exponent
307 return y
309 def monitor_curve_reverse(
310 y: NDArrayFloat, offset: NDArrayFloat, exponent: NDArrayFloat
311 ) -> NDArrayFloat:
312 """Define the *Monitor Curve Reverse* function."""
314 with sdiv_mode():
315 y_break = (
316 sdiv(exponent * offset, (exponent - 1) * (1 + offset))
317 ) ** exponent
319 x = as_float_array(y / s)
321 x[y >= y_break] = ((1 + offset) * (y[y >= y_break] ** (1 / exponent))) - offset
323 return x
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)
338 return as_float(y)