Coverage for models/rgb/transfer_functions/aces.py: 59%
64 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
1"""
2Academy Color Encoding System - Log Encodings
3=============================================
5Define the *Academy Color Encoding System* (ACES) log encodings.
7- :func:`colour.models.log_encoding_ACESproxy`
8- :func:`colour.models.log_decoding_ACESproxy`
9- :func:`colour.models.log_encoding_ACEScc`
10- :func:`colour.models.log_decoding_ACEScc`
11- :func:`colour.models.log_encoding_ACEScct`
12- :func:`colour.models.log_decoding_ACEScct`
14References
15----------
16- :cite:`TheAcademyofMotionPictureArtsandSciences2014q` : The Academy of
17 Motion Picture Arts and Sciences, Science and Technology Council, & Academy
18 Color Encoding System (ACES) Project Subcommittee. (2014). Technical
19 Bulletin TB-2014-004 - Informative Notes on SMPTE ST 2065-1 - Academy Color
20 Encoding Specification (ACES) (pp. 1-40). Retrieved December 19, 2014, from
21 http://j.mp/TB-2014-004
22- :cite:`TheAcademyofMotionPictureArtsandSciences2014r` : The Academy of
23 Motion Picture Arts and Sciences, Science and Technology Council, & Academy
24 Color Encoding System (ACES) Project Subcommittee. (2014). Technical
25 Bulletin TB-2014-012 - Academy Color Encoding System Version 1.0 Component
26 Names (pp. 1-8). Retrieved December 19, 2014, from http://j.mp/TB-2014-012
27- :cite:`TheAcademyofMotionPictureArtsandSciences2014s` : The Academy of
28 Motion Picture Arts and Sciences, Science and Technology Council, & Academy
29 Color Encoding System (ACES) Project Subcommittee. (2013). Specification
30 S-2013-001 - ACESproxy, an int Log Encoding of ACES Image Data.
31 Retrieved December 19, 2014, from http://j.mp/S-2013-001
32- :cite:`TheAcademyofMotionPictureArtsandSciences2014t` : The Academy of
33 Motion Picture Arts and Sciences, Science and Technology Council, & Academy
34 Color Encoding System (ACES) Project Subcommittee. (2014). Specification
35 S-2014-003 - ACEScc, A Logarithmic Encoding of ACES Data for use within
36 Color Grading Systems (pp. 1-12). Retrieved December 19, 2014, from
37 http://j.mp/S-2014-003
38- :cite:`TheAcademyofMotionPictureArtsandSciences2016c` : The Academy of
39 Motion Picture Arts and Sciences, Science and Technology Council, & Academy
40 Color Encoding System (ACES) Project. (2016). Specification S-2016-001 -
41 ACEScct, A Quasi-Logarithmic Encoding of ACES Data for use within Color
42 Grading Systems. Retrieved October 10, 2016, from http://j.mp/S-2016-001
43- :cite:`TheAcademyofMotionPictureArtsandSciencese` : The Academy of Motion
44 Picture Arts and Sciences, Science and Technology Council, & Academy Color
45 Encoding System (ACES) Project Subcommittee. (n.d.). Academy Color Encoding
46 System. Retrieved February 24, 2014, from
47 http://www.oscars.org/science-technology/council/projects/aces.html
48"""
50from __future__ import annotations
52import typing
54import numpy as np
56if typing.TYPE_CHECKING:
57 from colour.hints import Literal, NDArrayInt
59from colour.hints import ( # noqa: TC001
60 Annotated,
61 Domain1,
62 NDArrayFloat,
63 Range1,
64)
65from colour.utilities import (
66 Structure,
67 as_float,
68 as_int,
69 from_range_1,
70 optional,
71 to_domain_1,
72)
74__author__ = "Colour Developers"
75__copyright__ = "Copyright 2013 Colour Developers"
76__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
77__maintainer__ = "Colour Developers"
78__email__ = "colour-developers@colour-science.org"
79__status__ = "Production"
81__all__ = [
82 "CONSTANTS_ACES_PROXY_10",
83 "CONSTANTS_ACES_PROXY_12",
84 "CONSTANTS_ACES_PROXY",
85 "CONSTANTS_ACES_CCT",
86 "log_encoding_ACESproxy",
87 "log_decoding_ACESproxy",
88 "log_encoding_ACEScc",
89 "log_decoding_ACEScc",
90 "log_encoding_ACEScct",
91 "log_decoding_ACEScct",
92]
94CONSTANTS_ACES_PROXY_10: Structure = Structure(
95 CV_min=64,
96 CV_max=940,
97 steps_per_stop=50,
98 mid_CV_offset=425,
99 mid_log_offset=2.5,
100)
101"""*ACESproxy* 10 bit constants."""
103CONSTANTS_ACES_PROXY_12: Structure = Structure(
104 CV_min=256,
105 CV_max=3760,
106 steps_per_stop=200,
107 mid_CV_offset=1700,
108 mid_log_offset=2.5,
109)
110"""*ACESproxy* 12 bit constants."""
112CONSTANTS_ACES_PROXY: dict = {
113 10: CONSTANTS_ACES_PROXY_10,
114 12: CONSTANTS_ACES_PROXY_12,
115}
116"""Aggregated *ACESproxy* constants."""
118CONSTANTS_ACES_CCT: Structure = Structure(
119 X_BRK=0.0078125,
120 Y_BRK=0.155251141552511,
121 A=10.5402377416545,
122 B=0.0729055341958355,
123)
124"""*ACEScct* constants."""
127def log_encoding_ACESproxy(
128 lin_AP1: Domain1,
129 bit_depth: Literal[10, 12] = 10,
130 out_int: bool = False,
131 constants: dict | None = None,
132) -> Annotated[NDArrayFloat | NDArrayInt, 1]:
133 """
134 Apply the *ACESproxy* log encoding opto-electronic transfer function (OETF).
136 Parameters
137 ----------
138 lin_AP1
139 Linear *AP1* colourspace value.
140 bit_depth
141 *ACESproxy* bit-depth.
142 out_int
143 Whether to return value as int code value or float equivalent of a
144 code value at a specified bit-depth.
145 constants
146 *ACESproxy* constants.
148 Returns
149 -------
150 :class:`numpy.ndarray`
151 *ACESproxy* non-linear encoded value.
153 Notes
154 -----
155 +----------------+-----------------------+---------------+
156 | **Domain** | **Scale - Reference** | **Scale - 1** |
157 +================+=======================+===============+
158 | ``lin_AP1`` | 1 | 1 |
159 +----------------+-----------------------+---------------+
161 +----------------+-----------------------+---------------+
162 | **Range** | **Scale - Reference** | **Scale - 1** |
163 +================+=======================+===============+
164 | ``ACESproxy`` | 1 | 1 |
165 +----------------+-----------------------+---------------+
167 - This definition has an output int switch, thus the domain-range
168 scale information is only specified for the floating point mode.
170 References
171 ----------
172 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,
173 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,
174 :cite:`TheAcademyofMotionPictureArtsandSciences2014s`,
175 :cite:`TheAcademyofMotionPictureArtsandSciencese`
177 Examples
178 --------
179 >>> log_encoding_ACESproxy(0.18) # doctest: +ELLIPSIS
180 0.4164222...
181 >>> log_encoding_ACESproxy(0.18, out_int=True)
182 426
183 """
185 lin_AP1 = to_domain_1(lin_AP1)
186 constants = optional(constants, CONSTANTS_ACES_PROXY)
188 CV_min = constants[bit_depth].CV_min
189 CV_max = constants[bit_depth].CV_max
190 mid_CV_offset = constants[bit_depth].mid_CV_offset
191 mid_log_offset = constants[bit_depth].mid_log_offset
192 steps_per_stop = constants[bit_depth].steps_per_stop
194 def float_2_cv(x: float) -> float:
195 """Convert specified numeric to code value."""
197 return np.maximum(CV_min, np.minimum(CV_max, np.round(x)))
199 ACESproxy = np.where(
200 lin_AP1 > 2**-9.72,
201 float_2_cv(
202 (np.log2(lin_AP1) + mid_log_offset) * steps_per_stop + mid_CV_offset
203 ),
204 np.resize(CV_min, lin_AP1.shape),
205 )
207 if out_int:
208 return as_int(np.round(ACESproxy))
210 return as_float(from_range_1(ACESproxy / (2**bit_depth - 1)))
213def log_decoding_ACESproxy(
214 ACESproxy: Domain1,
215 bit_depth: Literal[10, 12] = 10,
216 in_int: bool = False,
217 constants: dict | None = None,
218) -> Range1:
219 """
220 Apply the *ACESproxy* log decoding inverse opto-electronic transfer function (OETF).
222 Parameters
223 ----------
224 ACESproxy
225 *ACESproxy* non-linear encoded value.
226 bit_depth
227 *ACESproxy* bit-depth.
228 in_int
229 Whether to treat the input value as integer code value or floating
230 point equivalent of a code value at specified bit-depth.
231 constants
232 *ACESproxy* constants.
234 Returns
235 -------
236 :class:`numpy.ndarray`
237 Linear *AP1* colourspace value.
239 Notes
240 -----
241 +----------------+-----------------------+---------------+
242 | **Domain** | **Scale - Reference** | **Scale - 1** |
243 +================+=======================+===============+
244 | ``ACESproxy`` | 1 | 1 |
245 +----------------+-----------------------+---------------+
247 +----------------+-----------------------+---------------+
248 | **Range** | **Scale - Reference** | **Scale - 1** |
249 +================+=======================+===============+
250 | ``lin_AP1`` | 1 | 1 |
251 +----------------+-----------------------+---------------+
253 - This definition has an input int switch, thus the domain-range
254 scale information is only specified for the floating point mode.
256 References
257 ----------
258 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,
259 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,
260 :cite:`TheAcademyofMotionPictureArtsandSciences2014s`,
261 :cite:`TheAcademyofMotionPictureArtsandSciencese`
263 Examples
264 --------
265 >>> log_decoding_ACESproxy(0.416422287390029) # doctest: +ELLIPSIS
266 0.1...
267 >>> log_decoding_ACESproxy(426, in_int=True) # doctest: +ELLIPSIS
268 0.1...
269 """
271 ACESproxy = to_domain_1(ACESproxy)
272 constants = optional(constants, CONSTANTS_ACES_PROXY)
274 mid_CV_offset = constants[bit_depth].mid_CV_offset
275 mid_log_offset = constants[bit_depth].mid_log_offset
276 steps_per_stop = constants[bit_depth].steps_per_stop
278 if not in_int:
279 ACESproxy = ACESproxy * (2**bit_depth - 1)
281 lin_AP1 = 2 ** ((ACESproxy - mid_CV_offset) / steps_per_stop - mid_log_offset)
283 return as_float(from_range_1(lin_AP1))
286def log_encoding_ACEScc(lin_AP1: Domain1) -> Range1:
287 """
288 Apply the *ACEScc* log encoding opto-electronic transfer function (OETF).
290 Parameters
291 ----------
292 lin_AP1
293 Linear *AP1* colourspace value.
295 Returns
296 -------
297 :class:`numpy.ndarray`
298 *ACEScc* non-linear encoded value.
300 Notes
301 -----
302 +-------------+-----------------------+---------------+
303 | **Domain** | **Scale - Reference** | **Scale - 1** |
304 +=============+=======================+===============+
305 | ``lin_AP1`` | 1 | 1 |
306 +-------------+-----------------------+---------------+
308 +-------------+-----------------------+---------------+
309 | **Range** | **Scale - Reference** | **Scale - 1** |
310 +=============+=======================+===============+
311 | ``ACEScc`` | 1 | 1 |
312 +-------------+-----------------------+---------------+
314 References
315 ----------
316 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,
317 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,
318 :cite:`TheAcademyofMotionPictureArtsandSciences2014t`,
319 :cite:`TheAcademyofMotionPictureArtsandSciencese`
321 Examples
322 --------
323 >>> log_encoding_ACEScc(0.18) # doctest: +ELLIPSIS
324 0.4135884...
325 """
327 lin_AP1 = to_domain_1(lin_AP1)
329 ACEScc = np.where(
330 lin_AP1 < 0,
331 (np.log2(2**-16) + 9.72) / 17.52,
332 (np.log2(2**-16 + lin_AP1 * 0.5) + 9.72) / 17.52,
333 )
334 ACEScc = np.where(
335 lin_AP1 >= 2**-15,
336 (np.log2(lin_AP1) + 9.72) / 17.52,
337 ACEScc,
338 )
340 return as_float(from_range_1(ACEScc))
343def log_decoding_ACEScc(ACEScc: Domain1) -> Range1:
344 """
345 Apply the *ACEScc* log decoding inverse opto-electronic transfer function (OETF).
347 Parameters
348 ----------
349 ACEScc
350 *ACEScc* non-linear encoded value.
352 Returns
353 -------
354 :class:`numpy.ndarray`
355 Linear *AP1* colourspace value.
357 Notes
358 -----
359 +-------------+-----------------------+---------------+
360 | **Domain** | **Scale - Reference** | **Scale - 1** |
361 +=============+=======================+===============+
362 | ``ACEScc`` | 1 | 1 |
363 +-------------+-----------------------+---------------+
365 +-------------+-----------------------+---------------+
366 | **Range** | **Scale - Reference** | **Scale - 1** |
367 +=============+=======================+===============+
368 | ``lin_AP1`` | 1 | 1 |
369 +-------------+-----------------------+---------------+
371 References
372 ----------
373 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,
374 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,
375 :cite:`TheAcademyofMotionPictureArtsandSciences2014t`,
376 :cite:`TheAcademyofMotionPictureArtsandSciencese`
378 Examples
379 --------
380 >>> log_decoding_ACEScc(0.413588402492442) # doctest: +ELLIPSIS
381 0.1799999...
382 """
384 ACEScc = to_domain_1(ACEScc)
386 lin_AP1 = np.where(
387 ACEScc < (9.72 - 15) / 17.52,
388 (2 ** (ACEScc * 17.52 - 9.72) - 2**-16) * 2,
389 2 ** (ACEScc * 17.52 - 9.72),
390 )
391 lin_AP1 = np.where(
392 ACEScc >= (np.log2(65504) + 9.72) / 17.52,
393 65504,
394 lin_AP1,
395 )
397 return as_float(from_range_1(lin_AP1))
400def log_encoding_ACEScct(
401 lin_AP1: Domain1, constants: Structure | None = None
402) -> Range1:
403 """
404 Apply the *ACEScct* log encoding opto-electronic transfer function (OETF).
406 Parameters
407 ----------
408 lin_AP1
409 Linear *AP1* colourspace value.
410 constants
411 *ACEScct* constants.
413 Returns
414 -------
415 :class:`numpy.ndarray`
416 *ACEScct* non-linear encoded value.
418 Notes
419 -----
420 +-------------+-----------------------+---------------+
421 | **Domain** | **Scale - Reference** | **Scale - 1** |
422 +=============+=======================+===============+
423 | ``lin_AP1`` | 1 | 1 |
424 +-------------+-----------------------+---------------+
426 +-------------+-----------------------+---------------+
427 | **Range** | **Scale - Reference** | **Scale - 1** |
428 +=============+=======================+===============+
429 | ``ACEScct`` | 1 | 1 |
430 +-------------+-----------------------+---------------+
432 References
433 ----------
434 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,
435 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,
436 :cite:`TheAcademyofMotionPictureArtsandSciences2016c`,
437 :cite:`TheAcademyofMotionPictureArtsandSciencese`
439 Examples
440 --------
441 >>> log_encoding_ACEScct(0.18) # doctest: +ELLIPSIS
442 0.4135884...
443 """
445 lin_AP1 = to_domain_1(lin_AP1)
446 constants = optional(constants, CONSTANTS_ACES_CCT)
448 ACEScct = np.where(
449 lin_AP1 <= constants.X_BRK,
450 constants.A * lin_AP1 + constants.B,
451 (np.log2(lin_AP1) + 9.72) / 17.52,
452 )
454 return as_float(from_range_1(ACEScct))
457def log_decoding_ACEScct(
458 ACEScct: Domain1, constants: Structure | None = None
459) -> Range1:
460 """
461 Apply the *ACEScct* log decoding inverse opto-electronic transfer function (OETF).
463 Parameters
464 ----------
465 ACEScct
466 *ACEScct* non-linear encoded value.
467 constants
468 *ACEScct* constants.
470 Returns
471 -------
472 :class:`numpy.ndarray`
473 Linear *AP1* colourspace value.
475 Notes
476 -----
477 +-------------+-----------------------+---------------+
478 | **Domain** | **Scale - Reference** | **Scale - 1** |
479 +=============+=======================+===============+
480 | ``ACEScct`` | 1 | 1 |
481 +-------------+-----------------------+---------------+
483 +-------------+-----------------------+---------------+
484 | **Range** | **Scale - Reference** | **Scale - 1** |
485 +=============+=======================+===============+
486 | ``lin_AP1`` | 1 | 1 |
487 +-------------+-----------------------+---------------+
489 References
490 ----------
491 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,
492 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,
493 :cite:`TheAcademyofMotionPictureArtsandSciences2016c`,
494 :cite:`TheAcademyofMotionPictureArtsandSciencese`
496 Examples
497 --------
498 >>> log_decoding_ACEScct(0.413588402492442) # doctest: +ELLIPSIS
499 0.1799999...
500 """
502 ACEScct = to_domain_1(ACEScct)
503 constants = optional(constants, CONSTANTS_ACES_CCT)
505 lin_AP1 = np.where(
506 ACEScct > constants.Y_BRK,
507 2 ** (ACEScct * 17.52 - 9.72),
508 (ACEScct - constants.B) / constants.A,
509 )
511 return as_float(from_range_1(lin_AP1))