Skip to content

Commit

Permalink
add class to encapsulate gamma_function
Browse files Browse the repository at this point in the history
  • Loading branch information
tjdcs committed Jun 20, 2024
1 parent 94f34ae commit f8c9032
Show file tree
Hide file tree
Showing 2 changed files with 269 additions and 4 deletions.
78 changes: 75 additions & 3 deletions colour/models/rgb/transfer_functions/gamma.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,87 @@

__all__ = [
"gamma_function",
"GammaFunction",
]

NegativeNumberHandlingType = (
Literal["Clamp", "Indeterminate", "Mirror", "Preserve"] | str
)


class GammaFunction:
"""Provides an object oriented interface to contain optional parameters for
an underlying :func:gamma_function call. Useful for providing both a simpler
and constructed api for gamma_function as well as allowing for control flow.
"""

def __init__(
self,
exponent: float = 1,
negative_number_handling: NegativeNumberHandlingType = "Indeterminate",
):
"""
Construct an object oriented interface to contain optional parameters for
an underlying :func:gamma_function call. Useful for providing both a simpler
and constructed api for gamma_function as well as allowing for control flow.
Parameters
----------
exponent : float, optional
The exponent value in a^b, by default 1
negative_number_handling : NegativeNumberHandlingType, optional
Defines the behavior for negative number handling, by default
"Indeterminate"
See Also
--------
:func:gamma_function
"""
self._exponent = exponent
self._negative_number_handling = negative_number_handling

@property
def exponent(self) -> float:
"""The exponent, b, in the function a^b
Returns
-------
float
"""
return self._exponent

@property
def negative_number_handling(self) -> NegativeNumberHandlingType:
"""How to treat negative numbers. See also :func:gamma_function
Returns
-------
NegativeNumberHandlingType
See also :func:gamma_function
"""
return self._negative_number_handling

def __call__(self, a: ArrayLike):
"""Calculate a typical encoding / decoding function on `a`. Representative
of the function a ^ b where b is determined by the instance value of
`exponent` and negative handling behavior is defined by the instance
value `negative_number_handling`. See also :func:gamma_function
Parameters
----------
a : ArrayLike
"""
return gamma_function(
a,
exponent=self.exponent,
negative_number_handling=self.negative_number_handling,
)


def gamma_function(
a: ArrayLike,
exponent: ArrayLike = 1,
negative_number_handling: (
Literal["Clamp", "Indeterminate", "Mirror", "Preserve"] | str
) = "Indeterminate",
negative_number_handling: NegativeNumberHandlingType = "Indeterminate",
) -> NDArrayFloat:
"""
Define a typical gamma encoding / decoding function.
Expand Down
195 changes: 194 additions & 1 deletion colour/models/rgb/transfer_functions/tests/test_gamma.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
:mod:`colour.models.rgb.transfer_functions.gamma` module.
"""


import numpy as np

from colour.constants import TOLERANCE_ABSOLUTE_TESTS
from colour.models.rgb.transfer_functions import gamma_function
from colour.models.rgb.transfer_functions.gamma import GammaFunction
from colour.utilities import ignore_numpy_errors

__author__ = "Colour Developers"
Expand All @@ -22,6 +22,199 @@
]


class TestGammaFunctionClass:
def test_gamma_function_class(self):
"""
Test :func:`colour.models.rgb.transfer_functions.gamma.\
gamma_function` definition.
"""

np.testing.assert_allclose(
GammaFunction(2.2)(0.0), 0.0, atol=TOLERANCE_ABSOLUTE_TESTS
)

np.testing.assert_allclose(
GammaFunction(2.2)(0.18),
0.022993204992707,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_allclose(
GammaFunction(1.0 / 2.2)(0.022993204992707),
0.18,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_allclose(
GammaFunction(2.0)(-0.18),
0.0323999999999998,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_array_equal(GammaFunction(2.2)(-0.18), np.nan)

np.testing.assert_allclose(
GammaFunction(2.2, "Mirror")(-0.18),
-0.022993204992707,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_allclose(
GammaFunction(2.2, "Preserve")(-0.18),
-0.18,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_allclose(
GammaFunction(2.2, "Clamp")(-0.18),
0,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_array_equal(GammaFunction(-2.2)(-0.18), np.nan)

np.testing.assert_allclose(
GammaFunction(-2.2, "Mirror")(0.0),
0.0,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_allclose(
GammaFunction(2.2, "Preserve")(0.0),
0.0,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_allclose(
GammaFunction(2.2, "Clamp")(0.0), 0, atol=TOLERANCE_ABSOLUTE_TESTS
)

def test_n_dimensional_gamma_function(self):
"""
Test :func:`colour.models.rgb.transfer_functions.gamma.\
gamma_function` definition n-dimensional arrays support.
"""

a = 0.18
a_p = GammaFunction(2.2)(a)

a = np.tile(a, 6)
a_p = np.tile(a_p, 6)
np.testing.assert_allclose(
GammaFunction(2.2)(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
)

a = np.reshape(a, (2, 3))
a_p = np.reshape(a_p, (2, 3))
np.testing.assert_allclose(
GammaFunction(2.2)(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
)

a = np.reshape(a, (2, 3, 1))
a_p = np.reshape(a_p, (2, 3, 1))
np.testing.assert_allclose(
GammaFunction(2.2)(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
)

a = -0.18
a_p = -0.022993204992707
np.testing.assert_allclose(
GammaFunction(2.2, "Mirror")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = np.tile(a, 6)
a_p = np.tile(a_p, 6)
np.testing.assert_allclose(
GammaFunction(2.2, "Mirror")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = np.reshape(a, (2, 3))
a_p = np.reshape(a_p, (2, 3))
np.testing.assert_allclose(
GammaFunction(2.2, "Mirror")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = np.reshape(a, (2, 3, 1))
a_p = np.reshape(a_p, (2, 3, 1))
np.testing.assert_allclose(
GammaFunction(2.2, "Mirror")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = -0.18
a_p = -0.18
np.testing.assert_allclose(
GammaFunction(2.2, "Preserve")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = np.tile(a, 6)
a_p = np.tile(a_p, 6)
np.testing.assert_allclose(
GammaFunction(2.2, "Preserve")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = np.reshape(a, (2, 3))
a_p = np.reshape(a_p, (2, 3))
np.testing.assert_allclose(
GammaFunction(2.2, "Preserve")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = np.reshape(a, (2, 3, 1))
a_p = np.reshape(a_p, (2, 3, 1))
np.testing.assert_allclose(
GammaFunction(2.2, "Preserve")(a),
a_p,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

a = -0.18
a_p = 0.0
np.testing.assert_allclose(
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
)

a = np.tile(a, 6)
a_p = np.tile(a_p, 6)
np.testing.assert_allclose(
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
)

a = np.reshape(a, (2, 3))
a_p = np.reshape(a_p, (2, 3))
np.testing.assert_allclose(
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
)

a = np.reshape(a, (2, 3, 1))
a_p = np.reshape(a_p, (2, 3, 1))
np.testing.assert_allclose(
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
)

@ignore_numpy_errors
def test_nan_gamma_function(self):
"""
Test :func:`colour.models.rgb.transfer_functions.gamma.\
gamma_function` definition nan support.
"""

cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
GammaFunction(cases)(cases)


class TestGammaFunction:
"""
Define :func:`colour.models.rgb.transfer_functions.gamma.gamma_function`
Expand Down

0 comments on commit f8c9032

Please sign in to comment.