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

import numpy as np

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

__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 np.float64(0.0229932...) >>> exponent_function_basic(-0.18, 2.2) np.float64(0.0) >>> exponent_function_basic(0.18, 2.2, "basicRev") # doctest: +ELLIPSIS np.float64(0.4586564...) >>> exponent_function_basic(-0.18, 2.2, "basicRev") np.float64(0.0) >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, "basicMirrorFwd" ... ) np.float64(0.0229932...) >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, "basicMirrorFwd" ... ) np.float64(-0.0229932...) >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, "basicMirrorRev" ... ) np.float64(0.4586564...) >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, "basicMirrorRev" ... ) np.float64(-0.4586564...) >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, "basicPassThruFwd" ... ) np.float64(0.0229932...) >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, "basicPassThruFwd" ... ) np.float64(-0.18) >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, "basicPassThruRev" ... ) np.float64(0.4586564...) >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, "basicPassThruRev" ... ) np.float64(-0.18) """ 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) m_x = x >= 0 if style == "basicfwd": y = np.where(m_x, exponent_forward(x), 0) elif style == "basicrev": y = np.where(m_x, exponent_reverse(x), 0) elif style == "basicmirrorfwd": y = np.where(m_x, exponent_forward(x), -exponent_forward(-x)) elif style == "basicmirrorrev": y = np.where(m_x, exponent_reverse(x), -exponent_reverse(-x)) elif style == "basicpassthrufwd": y = np.where(m_x, exponent_forward(x), x) else: # style == 'basicpassthrurev' y = np.where(m_x, exponent_reverse(x), 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 np.float64(0.0232240...) >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001 ... ) np.float64(-0.0002054...) >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... 0.18, 2.2, 0.001, "monCurveRev" ... ) np.float64(0.4581151...) >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001, "monCurveRev" ... ) np.float64(-157.7302795...) >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... 0.18, 2.2, 2, "monCurveMirrorFwd" ... ) np.float64(0.1679399...) >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001, "monCurveMirrorFwd" ... ) np.float64(-0.0232240...) >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... 0.18, 2.2, 0.001, "monCurveMirrorRev" ... ) np.float64(0.4581151...) >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001, "monCurveMirrorRev" ... ) np.float64(-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) return np.where(x >= x_break, ((x + offset) / (1 + offset)) ** exponent, 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) return np.where( y >= y_break, ((1 + offset) * (y ** (1 / exponent))) - offset, x ) 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 = np.where( m_x, monitor_curve_forward(x, offset, exponent), -monitor_curve_forward(-x, offset, exponent), ) else: # style == 'moncurvemirrorrev' y = np.where( m_x, monitor_curve_reverse(x, offset, exponent), -monitor_curve_reverse(-x, offset, exponent), ) return as_float(y)