"""
Basic and Monitor-Curve Exponent Transfer Functions
===================================================
Define the exponent transfer functions:
- :func:`colour.models.exponent_function_basic`
- :func:`colour.models.exponent_function_monitor_curve`
References
----------
- :cite: `TheAcademyofMotionPictureArtsandSciences2020` : The Academy of
Motion Picture Arts and Sciences, Science and Technology Council, & Academy
Color Encoding System (ACES) Project Subcommittee. (2020). Specification
S-2014-006 - Common LUT Format (CLF) - A Common File Format for Look-Up
Tables. Retrieved June 24, 2020, from http://j.mp/S-2014-006
"""
from __future__ import annotations
import typing
from colour.algebra import sdiv, sdiv_mode
if typing.TYPE_CHECKING:
from colour.hints import ArrayLike, Literal, NDArrayFloat
from colour.utilities import as_float, as_float_array, validate_method, zeros
__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"
__all__ = [
"exponent_function_basic",
"exponent_function_monitor_curve",
]
[docs]
def exponent_function_basic(
x: ArrayLike,
exponent: ArrayLike = 1,
style: (
Literal[
"basicFwd",
"basicRev",
"basicMirrorFwd",
"basicMirrorRev",
"basicPassThruFwd",
"basicPassThruRev",
]
| str
) = "basicFwd",
) -> NDArrayFloat:
"""
Define the *basic* exponent transfer function.
Parameters
----------
x
Data to undergo the basic exponent conversion.
exponent
Exponent value used for the conversion.
style
Defines the behaviour for the transfer function to operate:
- *basicFwd*: *Basic Forward* exponential behaviour where the
definition applies a basic power law using the exponent. Values
less than zero are clamped.
- *basicRev*: *Basic Reverse* exponential behaviour where the
definition applies a basic power law using the exponent. Values
less than zero are clamped.
- *basicMirrorFwd*: *Basic Mirror Forward* exponential behaviour
where the definition applies a basic power law using the exponent
for values greater than or equal to zero and mirrors the function
for values less than zero (i.e., rotationally symmetric
around the origin).
- *basicMirrorRev*: *Basic Mirror Reverse* exponential behaviour
where the definition applies a basic power law using the exponent
for values greater than or equal to zero and mirrors the function
for values less than zero (i.e., rotationally symmetric around the
origin).
- *basicPassThruFwd*: *Basic Pass Forward* exponential behaviour
where the definition applies a basic power law using the exponent
for values greater than or equal to zero and passes values less
than zero unchanged.
- *basicPassThruRev*: *Basic Pass Reverse* exponential behaviour
where the definition applies a basic power law using the exponent
for values greater than or equal to zero and passes values less
than zero unchanged.
Returns
-------
:class:`numpy.ndarray`
Exponentially converted data.
Examples
--------
>>> exponent_function_basic(0.18, 2.2) # doctest: +ELLIPSIS
0.0229932...
>>> exponent_function_basic(-0.18, 2.2)
0.0
>>> exponent_function_basic(0.18, 2.2, "basicRev") # doctest: +ELLIPSIS
0.4586564...
>>> exponent_function_basic(-0.18, 2.2, "basicRev")
0.0
>>> exponent_function_basic( # doctest: +ELLIPSIS
... 0.18, 2.2, "basicMirrorFwd"
... )
0.0229932...
>>> exponent_function_basic( # doctest: +ELLIPSIS
... -0.18, 2.2, "basicMirrorFwd"
... )
-0.0229932...
>>> exponent_function_basic( # doctest: +ELLIPSIS
... 0.18, 2.2, "basicMirrorRev"
... )
0.4586564...
>>> exponent_function_basic( # doctest: +ELLIPSIS
... -0.18, 2.2, "basicMirrorRev"
... )
-0.4586564...
>>> exponent_function_basic( # doctest: +ELLIPSIS
... 0.18, 2.2, "basicPassThruFwd"
... )
0.0229932...
>>> exponent_function_basic( # doctest: +ELLIPSIS
... -0.18, 2.2, "basicPassThruFwd"
... )
-0.1799999...
>>> exponent_function_basic( # doctest: +ELLIPSIS
... 0.18, 2.2, "basicPassThruRev"
... )
0.4586564...
>>> exponent_function_basic( # doctest: +ELLIPSIS
... -0.18, 2.2, "basicPassThruRev"
... )
-0.1799999...
"""
x = as_float_array(x)
exponent = as_float_array(exponent)
style = validate_method(
style,
(
"basicFwd",
"basicRev",
"basicMirrorFwd",
"basicMirrorRev",
"basicPassThruFwd",
"basicPassThruRev",
),
'"{0}" style is invalid, it must be one of {1}!',
)
def exponent_forward(x: NDArrayFloat) -> NDArrayFloat:
"""Return the input raised to the exponent value."""
return x**exponent
def exponent_reverse(y: NDArrayFloat) -> NDArrayFloat:
"""Return the input raised to the inverse exponent value."""
return y ** (as_float_array(1) / exponent)
y = zeros(x.shape)
m_x = x >= 0
if style == "basicfwd":
y[m_x] = exponent_forward(x[m_x])
elif style == "basicrev":
y[m_x] = exponent_reverse(x[m_x])
elif style == "basicmirrorfwd":
y[m_x] = exponent_forward(x[m_x])
y[~m_x] = -exponent_forward(-x[~m_x])
elif style == "basicmirrorrev":
y[m_x] = exponent_reverse(x[m_x])
y[~m_x] = -exponent_reverse(-x[~m_x])
elif style == "basicpassthrufwd":
y[m_x] = exponent_forward(x[m_x])
y[~m_x] = x[~m_x]
else: # style == 'basicpassthrurev'
y[m_x] = exponent_reverse(x[m_x])
y[~m_x] = x[~m_x]
return as_float(y)
[docs]
def exponent_function_monitor_curve(
x: ArrayLike,
exponent: ArrayLike = 1,
offset: ArrayLike = 0,
style: (
Literal[
"monCurveFwd",
"monCurveRev",
"monCurveMirrorFwd",
"monCurveMirrorRev",
]
| str
) = "monCurveFwd",
) -> NDArrayFloat:
"""
Define the *Monitor Curve* exponent transfer function.
Parameters
----------
x
Data to undergo the monitor curve exponential conversion.
exponent
Exponent value used for the conversion.
offset
Offset value used for the conversion.
style
Defines the behaviour for the transfer function to operate:
- *monCurveFwd*: *Monitor Curve Forward* exponential behaviour
where the definition applies a power law function with a linear
segment near the origin.
- *monCurveRev*: *Monitor Curve Reverse* exponential behaviour
where the definition applies a power law function with a linear
segment near the origin.
- *monCurveMirrorFwd*: *Monitor Curve Mirror Forward* exponential
behaviour where the definition applies a power law function with a
linear segment near the origin and mirrors the function for values
less than zero (i.e., rotationally symmetric around the origin).
- *monCurveMirrorRev*: *Monitor Curve Mirror Reverse* exponential
behaviour where the definition applies a power law function with a
linear segment near the origin and mirrors the function for values
less than zero (i.e., rotationally symmetric around the origin).
Returns
-------
:class:`numpy.ndarray`
Exponentially converted data.
Examples
--------
>>> exponent_function_monitor_curve(0.18, 2.2, 0.001) # doctest: +ELLIPSIS
0.0232240...
>>> exponent_function_monitor_curve( # doctest: +ELLIPSIS
... -0.18, 2.2, 0.001
... )
-0.0002054...
>>> exponent_function_monitor_curve( # doctest: +ELLIPSIS
... 0.18, 2.2, 0.001, "monCurveRev"
... )
0.4581151...
>>> exponent_function_monitor_curve( # doctest: +ELLIPSIS
... -0.18, 2.2, 0.001, "monCurveRev"
... )
-157.7302795...
>>> exponent_function_monitor_curve( # doctest: +ELLIPSIS
... 0.18, 2.2, 2, "monCurveMirrorFwd"
... )
0.1679399...
>>> exponent_function_monitor_curve( # doctest: +ELLIPSIS
... -0.18, 2.2, 0.001, "monCurveMirrorFwd"
... )
-0.0232240...
>>> exponent_function_monitor_curve( # doctest: +ELLIPSIS
... 0.18, 2.2, 0.001, "monCurveMirrorRev"
... )
0.4581151...
>>> exponent_function_monitor_curve( # doctest: +ELLIPSIS
... -0.18, 2.2, 0.001, "monCurveMirrorRev"
... )
-0.4581151...
"""
x = as_float_array(x)
exponent = as_float_array(exponent)
offset = as_float_array(offset)
style = validate_method(
style,
(
"monCurveFwd",
"monCurveRev",
"monCurveMirrorFwd",
"monCurveMirrorRev",
),
'"{0}" style is invalid, it must be one of {1}!',
)
with sdiv_mode():
s = as_float_array(
sdiv(exponent - 1, offset)
* sdiv(exponent * offset, (exponent - 1) * (offset + 1)) ** exponent
)
def monitor_curve_forward(
x: NDArrayFloat, offset: NDArrayFloat, exponent: NDArrayFloat
) -> NDArrayFloat:
"""Define the *Monitor Curve Forward* function."""
with sdiv_mode():
x_break = sdiv(offset, exponent - 1)
y = as_float_array(x * s)
y[x >= x_break] = ((x[x >= x_break] + offset) / (1 + offset)) ** exponent
return y
def monitor_curve_reverse(
y: NDArrayFloat, offset: NDArrayFloat, exponent: NDArrayFloat
) -> NDArrayFloat:
"""Define the *Monitor Curve Reverse* function."""
with sdiv_mode():
y_break = (
sdiv(exponent * offset, (exponent - 1) * (1 + offset))
) ** exponent
x = as_float_array(y / s)
x[y >= y_break] = ((1 + offset) * (y[y >= y_break] ** (1 / exponent))) - offset
return x
y = zeros(x.shape)
m_x = x >= 0
if style == "moncurvefwd":
y = monitor_curve_forward(x, offset, exponent)
elif style == "moncurverev":
y = monitor_curve_reverse(x, offset, exponent)
elif style == "moncurvemirrorfwd":
y[m_x] = monitor_curve_forward(x[m_x], offset, exponent)
y[~m_x] = -monitor_curve_forward(-x[~m_x], offset, exponent)
else: # style == 'moncurvemirrorrev'
y[m_x] = monitor_curve_reverse(x[m_x], offset, exponent)
y[~m_x] = -monitor_curve_reverse(-x[~m_x], offset, exponent)
return as_float(y)