Coverage for colour/models/rgb/transfer_functions/arri.py: 100%
57 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
1"""
2ARRI Log Encodings
3==================
5Define the *ARRI LogC3* and *ARRI LogC4* log encodings.
7- :func:`colour.models.log_encoding_ARRILogC3`
8- :func:`colour.models.log_decoding_ARRILogC3`
9- :func:`colour.models.log_encoding_ARRILogC4`
10- :func:`colour.models.log_decoding_ARRILogC4`
12References
13----------
14- :cite:`ARRI2012a` : ARRI. (2012). ALEXA - Log C Curve - Usage in VFX.
15 https://drive.google.com/open?id=1t73fAG_QpV7hJxoQPYZDWvOojYkYDgvn
16- :cite:`Cooper2022` : Cooper, S., & Brendel, H. (2022). ARRI LogC4
17 Logarithmic Color Space SPECIFICATION. Retrieved October 24, 2022, from
18 https://www.arri.com/resource/blob/278790/bea879ac0d041a925bed27a096ab3ec2/\
192022-05-arri-logc4-specification-data.pdf
20"""
22from __future__ import annotations
24import typing
26import numpy as np
28if typing.TYPE_CHECKING:
29 from colour.hints import Literal
31from colour.hints import ( # noqa: TC001
32 Domain1,
33 Range1,
34)
35from colour.utilities import (
36 CanonicalMapping,
37 Structure,
38 as_float,
39 from_range_1,
40 optional,
41 to_domain_1,
42 validate_method,
43)
45__author__ = "Colour Developers"
46__copyright__ = "Copyright 2013 Colour Developers"
47__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
48__maintainer__ = "Colour Developers"
49__email__ = "colour-developers@colour-science.org"
50__status__ = "Production"
52__all__ = [
53 "DATA_ALEXA_LOG_C_CURVE_BCL",
54 "DATA_ALEXA_LOG_C_CURVE_CONVERSION",
55 "log_encoding_ARRILogC3",
56 "log_decoding_ARRILogC3",
57 "CONSTANTS_ARRILOGC4",
58 "log_encoding_ARRILogC4",
59 "log_decoding_ARRILogC4",
60]
62DATA_ALEXA_LOG_C_CURVE_BCL: CanonicalMapping = CanonicalMapping(
63 {
64 "SUP 3.x": {
65 160: (0.0928, 0.8128),
66 200: (0.0928, 0.8341),
67 250: (0.0928, 0.8549),
68 320: (0.0928, 0.8773),
69 400: (0.0928, 0.8968),
70 500: (0.0928, 0.9158),
71 640: (0.0928, 0.9362),
72 800: (0.0928, 0.9539),
73 1000: (0.0928, 0.9711),
74 1280: (0.0928, 0.9895),
75 1600: (0.0928, 1.0000),
76 2000: (0.0928, 1.0000),
77 2560: (0.0928, 1.0000),
78 3200: (0.0928, 1.0000),
79 },
80 "SUP 2.x": {
81 160: (0.1083, 0.8110),
82 200: (0.1115, 0.8320),
83 250: (0.1146, 0.8524),
84 320: (0.1181, 0.8743),
85 400: (0.1213, 0.8935),
86 500: (0.1245, 0.9121),
87 640: (0.1280, 0.9320),
88 800: (0.1311, 0.9494),
89 1000: (0.1343, 0.9662),
90 1280: (0.1378, 0.9841),
91 1600: (0.1409, 0.9997),
92 },
93 }
94)
95"""*ARRI LogC3* curve *Ei, Black, Clipping Level* data."""
97DATA_ALEXA_LOG_C_CURVE_CONVERSION: CanonicalMapping = CanonicalMapping(
98 {
99 "SUP 3.x": CanonicalMapping(
100 {
101 "Normalised Sensor Signal": {
102 160: (
103 0.004680,
104 40.0,
105 -0.076072,
106 0.269036,
107 0.381991,
108 42.062665,
109 -0.071569,
110 0.125266,
111 ),
112 200: (
113 0.004597,
114 50.0,
115 -0.118740,
116 0.266007,
117 0.382478,
118 51.986387,
119 -0.110339,
120 0.128643,
121 ),
122 250: (
123 0.004518,
124 62.5,
125 -0.171260,
126 0.262978,
127 0.382966,
128 64.243053,
129 -0.158224,
130 0.132021,
131 ),
132 320: (
133 0.004436,
134 80.0,
135 -0.243808,
136 0.259627,
137 0.383508,
138 81.183335,
139 -0.224409,
140 0.135761,
141 ),
142 400: (
143 0.004369,
144 100.0,
145 -0.325820,
146 0.256598,
147 0.383999,
148 100.295280,
149 -0.299079,
150 0.139142,
151 ),
152 500: (
153 0.004309,
154 125.0,
155 -0.427461,
156 0.253569,
157 0.384493,
158 123.889239,
159 -0.391261,
160 0.142526,
161 ),
162 640: (
163 0.004249,
164 160.0,
165 -0.568709,
166 0.250219,
167 0.385040,
168 156.482680,
169 -0.518605,
170 0.146271,
171 ),
172 800: (
173 0.004201,
174 200.0,
175 -0.729169,
176 0.247190,
177 0.385537,
178 193.235573,
179 -0.662201,
180 0.149658,
181 ),
182 1000: (
183 0.004160,
184 250.0,
185 -0.928805,
186 0.244161,
187 0.386036,
188 238.584745,
189 -0.839385,
190 0.153047,
191 ),
192 1280: (
193 0.004120,
194 320.0,
195 -1.207168,
196 0.240810,
197 0.386590,
198 301.197380,
199 -1.084020,
200 0.156799,
201 ),
202 1600: (
203 0.004088,
204 400.0,
205 -1.524256,
206 0.237781,
207 0.387093,
208 371.761171,
209 -1.359723,
210 0.160192,
211 ),
212 },
213 "Linear Scene Exposure Factor": {
214 160: (
215 0.005561,
216 5.555556,
217 0.080216,
218 0.269036,
219 0.381991,
220 5.842037,
221 0.092778,
222 0.125266,
223 ),
224 200: (
225 0.006208,
226 5.555556,
227 0.076621,
228 0.266007,
229 0.382478,
230 5.776265,
231 0.092782,
232 0.128643,
233 ),
234 250: (
235 0.006871,
236 5.555556,
237 0.072941,
238 0.262978,
239 0.382966,
240 5.710494,
241 0.092786,
242 0.132021,
243 ),
244 320: (
245 0.007622,
246 5.555556,
247 0.068768,
248 0.259627,
249 0.383508,
250 5.637732,
251 0.092791,
252 0.135761,
253 ),
254 400: (
255 0.008318,
256 5.555556,
257 0.064901,
258 0.256598,
259 0.383999,
260 5.571960,
261 0.092795,
262 0.139142,
263 ),
264 500: (
265 0.009031,
266 5.555556,
267 0.060939,
268 0.253569,
269 0.384493,
270 5.506188,
271 0.092800,
272 0.142526,
273 ),
274 640: (
275 0.009840,
276 5.555556,
277 0.056443,
278 0.250219,
279 0.385040,
280 5.433426,
281 0.092805,
282 0.146271,
283 ),
284 800: (
285 0.010591,
286 5.555556,
287 0.052272,
288 0.247190,
289 0.385537,
290 5.367655,
291 0.092809,
292 0.149658,
293 ),
294 1000: (
295 0.011361,
296 5.555556,
297 0.047996,
298 0.244161,
299 0.386036,
300 5.301883,
301 0.092814,
302 0.153047,
303 ),
304 1280: (
305 0.012235,
306 5.555556,
307 0.043137,
308 0.240810,
309 0.386590,
310 5.229121,
311 0.092819,
312 0.156799,
313 ),
314 1600: (
315 0.013047,
316 5.555556,
317 0.038625,
318 0.237781,
319 0.387093,
320 5.163350,
321 0.092824,
322 0.16019,
323 ),
324 },
325 }
326 ),
327 "SUP 2.x": CanonicalMapping(
328 {
329 "Normalised Sensor Signal": {
330 160: (
331 0.003907,
332 36.439829,
333 -0.053366,
334 0.269035,
335 0.391007,
336 45.593473,
337 -0.069772,
338 0.10836,
339 ),
340 200: (
341 0.003907,
342 45.549786,
343 -0.088959,
344 0.266007,
345 0.391007,
346 55.709581,
347 -0.106114,
348 0.11154,
349 ),
350 250: (
351 0.003907,
352 56.937232,
353 -0.133449,
354 0.262978,
355 0.391007,
356 67.887153,
357 -0.150510,
358 0.11472,
359 ),
360 320: (
361 0.003907,
362 72.879657,
363 -0.195737,
364 0.259627,
365 0.391007,
366 84.167616,
367 -0.210597,
368 0.11824,
369 ),
370 400: (
371 0.003907,
372 91.099572,
373 -0.266922,
374 0.256598,
375 0.391007,
376 101.811426,
377 -0.276349,
378 0.12142,
379 ),
380 500: (
381 0.003907,
382 113.874465,
383 -0.355903,
384 0.253569,
385 0.391007,
386 122.608379,
387 -0.354421,
388 0.12461,
389 ),
390 640: (
391 0.003907,
392 145.759315,
393 -0.480477,
394 0.250218,
395 0.391007,
396 149.703304,
397 -0.456760,
398 0.12813,
399 ),
400 800: (
401 0.003907,
402 182.199144,
403 -0.622848,
404 0.247189,
405 0.391007,
406 178.216873,
407 -0.564981,
408 0.13131,
409 ),
410 1000: (
411 0.003907,
412 227.748930,
413 -0.800811,
414 0.244161,
415 0.391007,
416 210.785040,
417 -0.689043,
418 0.13449,
419 ),
420 1280: (
421 0.003907,
422 291.518630,
423 -1.049959,
424 0.240810,
425 0.391007,
426 251.689459,
427 -0.845336,
428 0.13801,
429 ),
430 1600: (
431 0.003907,
432 364.398287,
433 -1.334700,
434 0.237781,
435 0.391007,
436 293.073575,
437 -1.003841,
438 0.14119,
439 ),
440 },
441 "Linear Scene Exposure Factor": {
442 160: (
443 0.000000,
444 5.061087,
445 0.089004,
446 0.269035,
447 0.391007,
448 6.332427,
449 0.108361,
450 0.108361,
451 ),
452 200: (
453 0.000000,
454 5.061087,
455 0.089004,
456 0.266007,
457 0.391007,
458 6.189953,
459 0.111543,
460 0.111543,
461 ),
462 250: (
463 0.000000,
464 5.061087,
465 0.089004,
466 0.262978,
467 0.391007,
468 6.034414,
469 0.114725,
470 0.114725,
471 ),
472 320: (
473 0.000000,
474 5.061087,
475 0.089004,
476 0.259627,
477 0.391007,
478 5.844973,
479 0.118246,
480 0.118246,
481 ),
482 400: (
483 0.000000,
484 5.061087,
485 0.089004,
486 0.256598,
487 0.391007,
488 5.656190,
489 0.121428,
490 0.121428,
491 ),
492 500: (
493 0.000000,
494 5.061087,
495 0.089004,
496 0.253569,
497 0.391007,
498 5.449261,
499 0.124610,
500 0.124610,
501 ),
502 640: (
503 0.000000,
504 5.061087,
505 0.089004,
506 0.250218,
507 0.391007,
508 5.198031,
509 0.128130,
510 0.128130,
511 ),
512 800: (
513 0.000000,
514 5.061087,
515 0.089004,
516 0.247189,
517 0.391007,
518 4.950469,
519 0.131313,
520 0.131313,
521 ),
522 1000: (
523 0.000000,
524 5.061087,
525 0.089004,
526 0.244161,
527 0.391007,
528 4.684112,
529 0.134495,
530 0.134495,
531 ),
532 1280: (
533 0.000000,
534 5.061087,
535 0.089004,
536 0.240810,
537 0.391007,
538 4.369609,
539 0.138015,
540 0.138015,
541 ),
542 1600: (
543 0.000000,
544 5.061087,
545 0.089004,
546 0.237781,
547 0.391007,
548 4.070466,
549 0.141197,
550 0.14119,
551 ),
552 },
553 }
554 ),
555 }
556)
557"""
558*ARRI LogC3* curve conversion data between signal and linear scene
559exposure factor for *SUP 3.x* and signal and normalised sensor signal for
560*SUP 2.x*.
561"""
564def log_encoding_ARRILogC3(
565 x: Domain1,
566 firmware: Literal["SUP 2.x", "SUP 3.x"] | str = "SUP 3.x",
567 method: (
568 Literal["Linear Scene Exposure Factor", "Normalised Sensor Signal"] | str
569 ) = "Linear Scene Exposure Factor",
570 EI: Literal[160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600] = 800,
571) -> Range1:
572 """
573 Apply the *ARRI LogC3* log encoding opto-electronic transfer function (OETF).
575 Parameters
576 ----------
577 x
578 Linear data :math:`x`.
579 firmware
580 Alexa firmware version.
581 method
582 Conversion method.
583 EI
584 Exposure Index :math:`EI`.
586 Returns
587 -------
588 :class:`numpy.ndarray`
589 *ARRI LogC3* non-linear encoded data :math:`t`.
591 References
592 ----------
593 :cite:`ARRI2012a`
595 Notes
596 -----
597 +------------+-----------------------+---------------+
598 | **Domain** | **Scale - Reference** | **Scale - 1** |
599 +============+=======================+===============+
600 | ``x`` | 1 | 1 |
601 +------------+-----------------------+---------------+
603 +------------+-----------------------+---------------+
604 | **Range** | **Scale - Reference** | **Scale - 1** |
605 +============+=======================+===============+
606 | ``t`` | 1 | 1 |
607 +------------+-----------------------+---------------+
609 Examples
610 --------
611 >>> log_encoding_ARRILogC3(0.18) # doctest: +ELLIPSIS
612 0.3910068...
613 """
615 x = to_domain_1(x)
616 firmware = validate_method(firmware, ("SUP 3.x", "SUP 2.x"))
617 method = validate_method(
618 method, ("Linear Scene Exposure Factor", "Normalised Sensor Signal")
619 )
621 cut, a, b, c, d, e, f, _e_cut_f = DATA_ALEXA_LOG_C_CURVE_CONVERSION[firmware][
622 method
623 ][EI]
625 t = np.where(x > cut, c * np.log10(a * x + b) + d, e * x + f)
627 return as_float(from_range_1(t))
630def log_decoding_ARRILogC3(
631 t: Domain1,
632 firmware: Literal["SUP 2.x", "SUP 3.x"] | str = "SUP 3.x",
633 method: (
634 Literal["Linear Scene Exposure Factor", "Normalised Sensor Signal"] | str
635 ) = "Linear Scene Exposure Factor",
636 EI: Literal[160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600] = 800,
637) -> Range1:
638 """
639 Apply the *ARRI LogC3* log decoding inverse opto-electronic transfer
640 function (OETF).
642 Parameters
643 ----------
644 t
645 *ARRI LogC3* non-linear encoded data :math:`t`.
646 firmware
647 Alexa firmware version.
648 method
649 Conversion method.
650 EI
651 Exposure Index :math:`EI`.
653 Returns
654 -------
655 :class:`numpy.ndarray`
656 Linear data :math:`x`.
658 Notes
659 -----
660 +------------+-----------------------+---------------+
661 | **Domain** | **Scale - Reference** | **Scale - 1** |
662 +============+=======================+===============+
663 | ``t`` | 1 | 1 |
664 +------------+-----------------------+---------------+
666 +------------+-----------------------+---------------+
667 | **Range** | **Scale - Reference** | **Scale - 1** |
668 +============+=======================+===============+
669 | ``x`` | 1 | 1 |
670 +------------+-----------------------+---------------+
672 References
673 ----------
674 :cite:`ARRI2012a`
676 Examples
677 --------
678 >>> log_decoding_ARRILogC3(0.391006832034084) # doctest: +ELLIPSIS
679 0.18...
680 """
682 t = to_domain_1(t)
683 method = validate_method(
684 method, ("Linear Scene Exposure Factor", "Normalised Sensor Signal")
685 )
687 cut, a, b, c, d, e, f, _e_cut_f = DATA_ALEXA_LOG_C_CURVE_CONVERSION[firmware][
688 method
689 ][EI]
691 x = np.where(t > e * cut + f, (10 ** ((t - d) / c) - b) / a, (t - f) / e)
693 return as_float(from_range_1(x))
696CONSTANTS_ARRILOGC4: Structure = Structure(
697 a=(2**18 - 16) / 117.45,
698 b=(1023 - 95) / 1023,
699 c=95 / 1023,
700)
701"""*ARRI LogC4* constants."""
703_a = CONSTANTS_ARRILOGC4.a
704_b = CONSTANTS_ARRILOGC4.b
705_c = CONSTANTS_ARRILOGC4.c
707CONSTANTS_ARRILOGC4.s = (7 * np.log(2) * 2 ** (7 - 14 * _c / _b)) / (_a * _b)
708CONSTANTS_ARRILOGC4.t = (2 ** (14 * (-_c / _b) + 6) - 64) / _a
710del _a, _b, _c
713def log_encoding_ARRILogC4(
714 E_scene: Domain1,
715 constants: Structure | None = None,
716) -> Range1:
717 """
718 Apply the *ARRI LogC4* log encoding opto-electronic transfer function (OETF).
720 Parameters
721 ----------
722 E_scene
723 Relative scene linear signal :math:`E_{scene}`.
724 constants
725 *ARRI LogC4* constants.
727 Returns
728 -------
729 :class:`numpy.ndarray`
730 *ARRI LogC4* non-linear encoded signal :math:`E'`.
732 References
733 ----------
734 :cite:`Cooper2022`
736 Notes
737 -----
738 +-------------+-----------------------+---------------+
739 | **Domain** | **Scale - Reference** | **Scale - 1** |
740 +=============+=======================+===============+
741 | ``E_scene`` | 1 | 1 |
742 +-------------+-----------------------+---------------+
744 +------------+-----------------------+---------------+
745 | **Range** | **Scale - Reference** | **Scale - 1** |
746 +============+=======================+===============+
747 | ``E_p`` | 1 | 1 |
748 +------------+-----------------------+---------------+
750 Examples
751 --------
752 >>> log_encoding_ARRILogC4(0.18) # doctest: +ELLIPSIS
753 0.2783958...
754 """
756 E_scene = to_domain_1(E_scene)
757 constants = optional(constants, CONSTANTS_ARRILOGC4)
759 a = constants.a
760 b = constants.b
761 c = constants.c
762 s = constants.s
763 t = constants.t
765 E_p = np.where(
766 E_scene >= t,
767 (np.log2(a * E_scene + 64) - 6) / 14 * b + c,
768 (E_scene - t) / s,
769 )
771 return as_float(from_range_1(E_p))
774def log_decoding_ARRILogC4(
775 E_p: Domain1,
776 constants: Structure | None = None,
777) -> Range1:
778 """
779 Apply the *ARRI LogC4* log decoding inverse opto-electronic transfer
780 function (OETF).
782 Parameters
783 ----------
784 E_p
785 *ARRI LogC4* non-linear encoded signal :math:`E'`.
786 constants
787 *ARRI LogC4* constants.
789 Returns
790 -------
791 :class:`numpy.ndarray`
792 Relative scene linear signal :math:`E_{scene}`.
794 Notes
795 -----
796 +------------+-----------------------+---------------+
797 | **Domain** | **Scale - Reference** | **Scale - 1** |
798 +============+=======================+===============+
799 | ``E_p`` | 1 | 1 |
800 +------------+-----------------------+---------------+
802 +-------------+-----------------------+---------------+
803 | **Range** | **Scale - Reference** | **Scale - 1** |
804 +=============+=======================+===============+
805 | ``E_scene`` | 1 | 1 |
806 +-------------+-----------------------+---------------+
808 References
809 ----------
810 :cite:`Cooper2022`
812 Examples
813 --------
814 >>> log_decoding_ARRILogC4(0.27839583654826527) # doctest: +ELLIPSIS
815 0.18...
816 """
818 E_p = to_domain_1(E_p)
819 constants = optional(constants, CONSTANTS_ARRILOGC4)
821 a = constants.a
822 b = constants.b
823 c = constants.c
824 s = constants.s
825 t = constants.t
827 E_scene = np.where(
828 E_p >= 0,
829 (2 ** (14 * ((E_p - c) / b) + 6) - 64) / a,
830 E_p * s + t,
831 )
833 return as_float(from_range_1(E_scene))