Skip to content

Commit e72f266

Browse files
committed
add class to encapsulate gamma_function
1 parent b2da242 commit e72f266

File tree

2 files changed

+269
-4
lines changed

2 files changed

+269
-4
lines changed

colour/models/rgb/transfer_functions/gamma.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,87 @@
2525

2626
__all__ = [
2727
"gamma_function",
28+
"GammaFunction",
2829
]
2930

31+
NegativeNumberHandlingType = (
32+
Literal["Clamp", "Indeterminate", "Mirror", "Preserve"] | str
33+
)
34+
35+
36+
class GammaFunction:
37+
"""Provides an object oriented interface to contain optional parameters for
38+
an underlying :func:gamma_function call. Useful for providing both a simpler
39+
and constructed api for gamma_function as well as allowing for control flow.
40+
"""
41+
42+
def __init__(
43+
self,
44+
exponent: float = 1,
45+
negative_number_handling: NegativeNumberHandlingType = "Indeterminate",
46+
):
47+
"""
48+
Construct an object oriented interface to contain optional parameters for
49+
an underlying :func:gamma_function call. Useful for providing both a simpler
50+
and constructed api for gamma_function as well as allowing for control flow.
51+
52+
Parameters
53+
----------
54+
exponent : float, optional
55+
The exponent value in a^b, by default 1
56+
negative_number_handling : NegativeNumberHandlingType, optional
57+
Defines the behavior for negative number handling, by default
58+
"Indeterminate"
59+
60+
See Also
61+
--------
62+
:func:gamma_function
63+
"""
64+
self._exponent = exponent
65+
self._negative_number_handling = negative_number_handling
66+
67+
@property
68+
def exponent(self) -> float:
69+
"""The exponent, b, in the function a^b
70+
71+
Returns
72+
-------
73+
float
74+
"""
75+
return self._exponent
76+
77+
@property
78+
def negative_number_handling(self) -> NegativeNumberHandlingType:
79+
"""How to treat negative numbers. See also :func:gamma_function
80+
81+
Returns
82+
-------
83+
NegativeNumberHandlingType
84+
See also :func:gamma_function
85+
"""
86+
return self._negative_number_handling
87+
88+
def __call__(self, a: ArrayLike):
89+
"""Calculate a typical encoding / decoding function on `a`. Representative
90+
of the function a ^ b where b is determined by the instance value of
91+
`exponent` and negative handling behavior is defined by the instance
92+
value `negative_number_handling`. See also :func:gamma_function
93+
94+
Parameters
95+
----------
96+
a : ArrayLike
97+
"""
98+
return gamma_function(
99+
a,
100+
exponent=self.exponent,
101+
negative_number_handling=self.negative_number_handling,
102+
)
103+
30104

31105
def gamma_function(
32106
a: ArrayLike,
33107
exponent: ArrayLike = 1,
34-
negative_number_handling: (
35-
Literal["Clamp", "Indeterminate", "Mirror", "Preserve"] | str
36-
) = "Indeterminate",
108+
negative_number_handling: NegativeNumberHandlingType = "Indeterminate",
37109
) -> NDArrayFloat:
38110
"""
39111
Define a typical gamma encoding / decoding function.

colour/models/rgb/transfer_functions/tests/test_gamma.py

Lines changed: 194 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
:mod:`colour.models.rgb.transfer_functions.gamma` module.
44
"""
55

6-
76
import numpy as np
87

98
from colour.constants import TOLERANCE_ABSOLUTE_TESTS
109
from colour.models.rgb.transfer_functions import gamma_function
10+
from colour.models.rgb.transfer_functions.gamma import GammaFunction
1111
from colour.utilities import ignore_numpy_errors
1212

1313
__author__ = "Colour Developers"
@@ -22,6 +22,199 @@
2222
]
2323

2424

25+
class TestGammaFunctionClass:
26+
def test_gamma_function_class(self):
27+
"""
28+
Test :func:`colour.models.rgb.transfer_functions.gamma.\
29+
gamma_function` definition.
30+
"""
31+
32+
np.testing.assert_allclose(
33+
GammaFunction(2.2)(0.0), 0.0, atol=TOLERANCE_ABSOLUTE_TESTS
34+
)
35+
36+
np.testing.assert_allclose(
37+
GammaFunction(2.2)(0.18),
38+
0.022993204992707,
39+
atol=TOLERANCE_ABSOLUTE_TESTS,
40+
)
41+
42+
np.testing.assert_allclose(
43+
GammaFunction(1.0 / 2.2)(0.022993204992707),
44+
0.18,
45+
atol=TOLERANCE_ABSOLUTE_TESTS,
46+
)
47+
48+
np.testing.assert_allclose(
49+
GammaFunction(2.0)(-0.18),
50+
0.0323999999999998,
51+
atol=TOLERANCE_ABSOLUTE_TESTS,
52+
)
53+
54+
np.testing.assert_array_equal(GammaFunction(2.2)(-0.18), np.nan)
55+
56+
np.testing.assert_allclose(
57+
GammaFunction(2.2, "Mirror")(-0.18),
58+
-0.022993204992707,
59+
atol=TOLERANCE_ABSOLUTE_TESTS,
60+
)
61+
62+
np.testing.assert_allclose(
63+
GammaFunction(2.2, "Preserve")(-0.18),
64+
-0.18,
65+
atol=TOLERANCE_ABSOLUTE_TESTS,
66+
)
67+
68+
np.testing.assert_allclose(
69+
GammaFunction(2.2, "Clamp")(-0.18),
70+
0,
71+
atol=TOLERANCE_ABSOLUTE_TESTS,
72+
)
73+
74+
np.testing.assert_array_equal(GammaFunction(-2.2)(-0.18), np.nan)
75+
76+
np.testing.assert_allclose(
77+
GammaFunction(-2.2, "Mirror")(0.0),
78+
0.0,
79+
atol=TOLERANCE_ABSOLUTE_TESTS,
80+
)
81+
82+
np.testing.assert_allclose(
83+
GammaFunction(2.2, "Preserve")(0.0),
84+
0.0,
85+
atol=TOLERANCE_ABSOLUTE_TESTS,
86+
)
87+
88+
np.testing.assert_allclose(
89+
GammaFunction(2.2, "Clamp")(0.0), 0, atol=TOLERANCE_ABSOLUTE_TESTS
90+
)
91+
92+
def test_n_dimensional_gamma_function(self):
93+
"""
94+
Test :func:`colour.models.rgb.transfer_functions.gamma.\
95+
gamma_function` definition n-dimensional arrays support.
96+
"""
97+
98+
a = 0.18
99+
a_p = GammaFunction(2.2)(a)
100+
101+
a = np.tile(a, 6)
102+
a_p = np.tile(a_p, 6)
103+
np.testing.assert_allclose(
104+
GammaFunction(2.2)(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
105+
)
106+
107+
a = np.reshape(a, (2, 3))
108+
a_p = np.reshape(a_p, (2, 3))
109+
np.testing.assert_allclose(
110+
GammaFunction(2.2)(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
111+
)
112+
113+
a = np.reshape(a, (2, 3, 1))
114+
a_p = np.reshape(a_p, (2, 3, 1))
115+
np.testing.assert_allclose(
116+
GammaFunction(2.2)(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
117+
)
118+
119+
a = -0.18
120+
a_p = -0.022993204992707
121+
np.testing.assert_allclose(
122+
GammaFunction(2.2, "Mirror")(a),
123+
a_p,
124+
atol=TOLERANCE_ABSOLUTE_TESTS,
125+
)
126+
127+
a = np.tile(a, 6)
128+
a_p = np.tile(a_p, 6)
129+
np.testing.assert_allclose(
130+
GammaFunction(2.2, "Mirror")(a),
131+
a_p,
132+
atol=TOLERANCE_ABSOLUTE_TESTS,
133+
)
134+
135+
a = np.reshape(a, (2, 3))
136+
a_p = np.reshape(a_p, (2, 3))
137+
np.testing.assert_allclose(
138+
GammaFunction(2.2, "Mirror")(a),
139+
a_p,
140+
atol=TOLERANCE_ABSOLUTE_TESTS,
141+
)
142+
143+
a = np.reshape(a, (2, 3, 1))
144+
a_p = np.reshape(a_p, (2, 3, 1))
145+
np.testing.assert_allclose(
146+
GammaFunction(2.2, "Mirror")(a),
147+
a_p,
148+
atol=TOLERANCE_ABSOLUTE_TESTS,
149+
)
150+
151+
a = -0.18
152+
a_p = -0.18
153+
np.testing.assert_allclose(
154+
GammaFunction(2.2, "Preserve")(a),
155+
a_p,
156+
atol=TOLERANCE_ABSOLUTE_TESTS,
157+
)
158+
159+
a = np.tile(a, 6)
160+
a_p = np.tile(a_p, 6)
161+
np.testing.assert_allclose(
162+
GammaFunction(2.2, "Preserve")(a),
163+
a_p,
164+
atol=TOLERANCE_ABSOLUTE_TESTS,
165+
)
166+
167+
a = np.reshape(a, (2, 3))
168+
a_p = np.reshape(a_p, (2, 3))
169+
np.testing.assert_allclose(
170+
GammaFunction(2.2, "Preserve")(a),
171+
a_p,
172+
atol=TOLERANCE_ABSOLUTE_TESTS,
173+
)
174+
175+
a = np.reshape(a, (2, 3, 1))
176+
a_p = np.reshape(a_p, (2, 3, 1))
177+
np.testing.assert_allclose(
178+
GammaFunction(2.2, "Preserve")(a),
179+
a_p,
180+
atol=TOLERANCE_ABSOLUTE_TESTS,
181+
)
182+
183+
a = -0.18
184+
a_p = 0.0
185+
np.testing.assert_allclose(
186+
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
187+
)
188+
189+
a = np.tile(a, 6)
190+
a_p = np.tile(a_p, 6)
191+
np.testing.assert_allclose(
192+
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
193+
)
194+
195+
a = np.reshape(a, (2, 3))
196+
a_p = np.reshape(a_p, (2, 3))
197+
np.testing.assert_allclose(
198+
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
199+
)
200+
201+
a = np.reshape(a, (2, 3, 1))
202+
a_p = np.reshape(a_p, (2, 3, 1))
203+
np.testing.assert_allclose(
204+
GammaFunction(2.2, "Clamp")(a), a_p, atol=TOLERANCE_ABSOLUTE_TESTS
205+
)
206+
207+
@ignore_numpy_errors
208+
def test_nan_gamma_function(self):
209+
"""
210+
Test :func:`colour.models.rgb.transfer_functions.gamma.\
211+
gamma_function` definition nan support.
212+
"""
213+
214+
cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
215+
GammaFunction(cases)(cases)
216+
217+
25218
class TestGammaFunction:
26219
"""
27220
Define :func:`colour.models.rgb.transfer_functions.gamma.gamma_function`

0 commit comments

Comments
 (0)