Source code for colour.models.rgb.transfer_functions.exponent

"""
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: """ Apply a *basic* exponent transfer function to the specified array. Parameters ---------- x Exponentially encoded data :math:`x`. exponent Exponent value used for the conversion. style Specifies the behaviour for the exponentiation 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: """ Apply the *Monitor Curve* exponent transfer function to the specified array. Parameters ---------- x Exponentially encoded data :math:`x`. exponent Exponent value used for the conversion. offset Offset value used for the conversion. style Specifies the behaviour for the exponentiation 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)