Coverage for colour/models/rgb/transfer_functions/canon.py: 100%
133 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"""
2Canon Log Encodings
3===================
5Define the *Canon Log* encodings.
7- :attr:`colour.models.CANON_LOG_ENCODING_METHODS`
8- :func:`colour.models.log_encoding_CanonLog`
9- :attr:`colour.models.CANON_LOG_DECODING_METHODS`
10- :func:`colour.models.log_decoding_CanonLog`
11- :attr:`colour.models.CANON_LOG_2_ENCODING_METHODS`
12- :func:`colour.models.log_encoding_CanonLog2`
13- :attr:`colour.models.CANON_LOG_2_DECODING_METHODS`
14- :func:`colour.models.log_decoding_CanonLog2`
15- :attr:`colour.models.CANON_LOG_3_ENCODING_METHODS`
16- :func:`colour.models.log_encoding_CanonLog3`
17- :attr:`colour.models.CANON_LOG_3_DECODING_METHODS`
18- :func:`colour.models.log_decoding_CanonLog3`
20Notes
21-----
22- :cite:`Canon2016` is available as a *Drivers & Downloads* *Software* for
23 Windows 7 *Operating System*, a copy of the archive is hosted at
24 this url: https://drive.google.com/open?id=0B_IQZQdc4Vy8ZGYyY29pMEVwZU0
25- :cite:`Canon2020` is available as a *Drivers & Downloads* *Software* for
26 Windows 10 *Operating System*, a copy of the archive is hosted at
27 this url: https://drive.google.com/open?id=1Vcz8RVIXgXL54lhZsOwGUjjVZRObZSc5
29References
30----------
31- :cite:`Canon2016` : Canon. (2016). Input Transform Version 201612 for EOS
32 C300 Mark II. Retrieved August 23, 2016, from https://www.usa.canon.com/\
33internet/portal/us/home/support/details/cameras/cinema-eos/eos-c300-mark-ii
34- :cite:`Canon2020` : Canon. (2020). Input Transform Version 202007 for EOS
35 C300 Mark II. Retrieved July 16, 2023, from https://www.usa.canon.com/\
36internet/portal/us/home/support/details/cameras/cinema-eos/eos-c300-mark-ii
37- :cite:`Thorpe2012a` : Thorpe, L. (2012). CANON-LOG TRANSFER CHARACTERISTIC.
38 Retrieved September 25, 2014, from
39 http://downloads.canon.com/CDLC/Canon-Log_Transfer_Characteristic_6-20-2012.pdf
40"""
42from __future__ import annotations
44import typing
46import numpy as np
48if typing.TYPE_CHECKING:
49 from colour.hints import Literal
51from colour.hints import ( # noqa: TC001
52 Domain1,
53 Range1,
54)
55from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full
56from colour.utilities import (
57 CanonicalMapping,
58 as_float,
59 domain_range_scale,
60 from_range_1,
61 to_domain_1,
62 validate_method,
63)
65__author__ = "Colour Developers"
66__copyright__ = "Copyright 2013 Colour Developers"
67__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
68__maintainer__ = "Colour Developers"
69__email__ = "colour-developers@colour-science.org"
70__status__ = "Production"
72__all__ = [
73 "log_encoding_CanonLog_v1",
74 "log_decoding_CanonLog_v1",
75 "log_encoding_CanonLog_v1_2",
76 "log_decoding_CanonLog_v1_2",
77 "CANON_LOG_ENCODING_METHODS",
78 "log_encoding_CanonLog",
79 "CANON_LOG_DECODING_METHODS",
80 "log_decoding_CanonLog",
81 "log_encoding_CanonLog2_v1",
82 "log_decoding_CanonLog2_v1",
83 "log_encoding_CanonLog2_v1_2",
84 "log_decoding_CanonLog2_v1_2",
85 "CANON_LOG_2_ENCODING_METHODS",
86 "log_encoding_CanonLog2",
87 "CANON_LOG_2_DECODING_METHODS",
88 "log_decoding_CanonLog2",
89 "log_encoding_CanonLog3_v1",
90 "log_decoding_CanonLog3_v1",
91 "log_encoding_CanonLog3_v1_2",
92 "log_decoding_CanonLog3_v1_2",
93 "CANON_LOG_3_ENCODING_METHODS",
94 "log_encoding_CanonLog3",
95 "CANON_LOG_3_DECODING_METHODS",
96 "log_decoding_CanonLog3",
97]
100def log_encoding_CanonLog_v1(
101 x: Domain1,
102 bit_depth: int = 10,
103 out_normalised_code_value: bool = True,
104 in_reflection: bool = True,
105) -> Range1:
106 """
107 Apply the *Canon Log* v1 log encoding opto-electronic transfer function (OETF).
109 Parameters
110 ----------
111 x
112 Linear data :math:`x`.
113 bit_depth
114 Bit-depth used for conversion.
115 out_normalised_code_value
116 Whether the *Canon Log* v1 non-linear data is encoded as normalised
117 code values.
118 in_reflection
119 Whether the light level :math:`x` to a camera is reflection.
121 Returns
122 -------
123 :class:`numpy.ndarray`
124 *Canon Log* v1 non-linear encoded data.
126 References
127 ----------
128 :cite:`Canon2016`, :cite:`Thorpe2012a`
130 Notes
131 -----
132 +------------+-----------------------+---------------+
133 | **Domain** | **Scale - Reference** | **Scale - 1** |
134 +============+=======================+===============+
135 | ``x`` | 1 | 1 |
136 +------------+-----------------------+---------------+
138 +------------+-----------------------+---------------+
139 | **Range** | **Scale - Reference** | **Scale - 1** |
140 +============+=======================+===============+
141 | ``clog`` | 1 | 1 |
142 +------------+-----------------------+---------------+
144 Examples
145 --------
146 >>> log_encoding_CanonLog_v1(0.18) * 100 # doctest: +ELLIPSIS
147 34.3389651...
149 The values of *Table 2 Canon-Log Code Values* table in
150 :cite:`Thorpe2012a` are obtained as follows:
152 >>> x = np.array([0, 2, 18, 90, 720]) / 100
153 >>> np.around(log_encoding_CanonLog_v1(x) * (2**10 - 1)).astype(np.int_)
154 array([ 128, 169, 351, 614, 1016])
155 >>> np.around(log_encoding_CanonLog_v1(x, 10, False) * 100, 1)
156 array([ 7.3, 12. , 32.8, 62.7, 108.7])
157 """
159 x = to_domain_1(x)
161 if in_reflection:
162 x = x / 0.9
164 with domain_range_scale("ignore"):
165 clog = np.where(
166 x < log_decoding_CanonLog_v1(0.0730597, bit_depth, False),
167 -(0.529136 * (np.log10(-x * 10.1596 + 1)) - 0.0730597),
168 0.529136 * np.log10(10.1596 * x + 1) + 0.0730597,
169 )
171 clog_cv = full_to_legal(clog, bit_depth) if out_normalised_code_value else clog
173 return as_float(from_range_1(clog_cv))
176def log_decoding_CanonLog_v1(
177 clog: Domain1,
178 bit_depth: int = 10,
179 in_normalised_code_value: bool = True,
180 out_reflection: bool = True,
181) -> Range1:
182 """
183 Apply the *Canon Log* v1 log decoding inverse opto-electronic transfer
184 function (OETF).
186 Parameters
187 ----------
188 clog
189 *Canon Log* v1 non-linear encoded data.
190 bit_depth
191 Bit-depth used for conversion.
192 in_normalised_code_value
193 Whether the *Canon Log* v1 non-linear data is encoded with normalised
194 code values.
195 out_reflection
196 Whether the light level :math:`x` to a camera is reflection.
198 Returns
199 -------
200 :class:`numpy.ndarray`
201 Linear data :math:`x`.
203 Notes
204 -----
205 +------------+-----------------------+---------------+
206 | **Domain** | **Scale - Reference** | **Scale - 1** |
207 +============+=======================+===============+
208 | ``clog`` | 1 | 1 |
209 +------------+-----------------------+---------------+
211 +------------+-----------------------+---------------+
212 | **Range** | **Scale - Reference** | **Scale - 1** |
213 +============+=======================+===============+
214 | ``x`` | 1 | 1 |
215 +------------+-----------------------+---------------+
217 References
218 ----------
219 :cite:`Canon2016`, :cite:`Thorpe2012a`
221 Examples
222 --------
223 >>> log_decoding_CanonLog_v1(34.338965172606912 / 100) # doctest: +ELLIPSIS
224 0.17999999...
225 """
227 clog = to_domain_1(clog)
229 clog = legal_to_full(clog, bit_depth) if in_normalised_code_value else clog
231 x = np.where(
232 clog < 0.0730597,
233 -(10 ** ((0.0730597 - clog) / 0.529136) - 1) / 10.1596,
234 (10 ** ((clog - 0.0730597) / 0.529136) - 1) / 10.1596,
235 )
237 if out_reflection:
238 x = x * 0.9
240 return as_float(from_range_1(x))
243def log_encoding_CanonLog_v1_2(
244 x: Domain1,
245 bit_depth: int = 10,
246 out_normalised_code_value: bool = True,
247 in_reflection: bool = True,
248) -> Range1:
249 """
250 Apply the *Canon Log* v1.2 log encoding opto-electronic transfer function (OETF).
252 Parameters
253 ----------
254 x
255 Linear data :math:`x`.
256 bit_depth
257 Bit-depth used for conversion.
258 out_normalised_code_value
259 Whether the *Canon Log* v1.2 non-linear data is encoded as
260 normalised code values.
261 in_reflection
262 Whether the light level :math:`x` to a camera is reflection.
264 Returns
265 -------
266 :class:`numpy.ndarray`
267 *Canon Log* v1.2 non-linear encoded data.
269 References
270 ----------
271 :cite:`Canon2020`
273 Notes
274 -----
275 +------------+-----------------------+---------------+
276 | **Domain** | **Scale - Reference** | **Scale - 1** |
277 +============+=======================+===============+
278 | ``x`` | 1 | 1 |
279 +------------+-----------------------+---------------+
281 +------------+-----------------------+---------------+
282 | **Range** | **Scale - Reference** | **Scale - 1** |
283 +============+=======================+===============+
284 | ``clog`` | 1 | 1 |
285 +------------+-----------------------+---------------+
287 Examples
288 --------
289 >>> log_encoding_CanonLog_v1_2(0.18) * 100 # doctest: +ELLIPSIS
290 34.3389649...
291 """
293 x = to_domain_1(x)
295 if in_reflection:
296 x = x / 0.9
298 with domain_range_scale("ignore"):
299 clog = np.where(
300 x < (log_decoding_CanonLog_v1_2(0.12512248, bit_depth, True)),
301 -(0.45310179 * (np.log10(-x * 10.1596 + 1)) - 0.12512248),
302 0.45310179 * np.log10(10.1596 * x + 1) + 0.12512248,
303 )
305 # NOTE: *Canon Log* v1.2 constants are expressed in legal range
306 # (studio swing).
307 clog_cv = clog if out_normalised_code_value else legal_to_full(clog, bit_depth)
309 return as_float(from_range_1(clog_cv))
312def log_decoding_CanonLog_v1_2(
313 clog: Domain1,
314 bit_depth: int = 10,
315 in_normalised_code_value: bool = True,
316 out_reflection: bool = True,
317) -> Range1:
318 """
319 Apply the *Canon Log* v1.2 log decoding inverse opto-electronic transfer
321 function (OETF).
323 Parameters
324 ----------
325 clog
326 *Canon Log* v1.2 non-linear encoded data.
327 bit_depth
328 Bit-depth used for conversion.
329 in_normalised_code_value
330 Whether the *Canon Log* v1.2 non-linear data is encoded with normalised
331 code values.
332 out_reflection
333 Whether the light level :math:`x` to a camera is reflection.
335 Returns
336 -------
337 :class:`numpy.ndarray`
338 Linear data :math:`x`.
340 Notes
341 -----
342 +------------+-----------------------+---------------+
343 | **Domain** | **Scale - Reference** | **Scale - 1** |
344 +============+=======================+===============+
345 | ``clog`` | 1 | 1 |
346 +------------+-----------------------+---------------+
348 +------------+-----------------------+---------------+
349 | **Range** | **Scale - Reference** | **Scale - 1** |
350 +============+=======================+===============+
351 | ``x`` | 1 | 1 |
352 +------------+-----------------------+---------------+
354 References
355 ----------
356 :cite:`Canon2020`
358 Examples
359 --------
360 >>> log_decoding_CanonLog_v1_2(34.338964929528061 / 100)
361 ... # doctest: +ELLIPSIS
362 0.17999999...
363 """
365 clog = to_domain_1(clog)
367 # NOTE: *Canon Log* v1.2 constants are expressed in legal range
368 # (studio swing).
369 clog = clog if in_normalised_code_value else full_to_legal(clog, bit_depth)
371 x = np.where(
372 clog < 0.12512248,
373 -(10 ** ((0.12512248 - clog) / 0.45310179) - 1) / 10.1596,
374 (10 ** ((clog - 0.12512248) / 0.45310179) - 1) / 10.1596,
375 )
377 if out_reflection:
378 x = x * 0.9
380 return as_float(from_range_1(x))
383CANON_LOG_ENCODING_METHODS: CanonicalMapping = CanonicalMapping(
384 {
385 "v1": log_encoding_CanonLog_v1,
386 "v1.2": log_encoding_CanonLog_v1_2,
387 }
388)
389CANON_LOG_ENCODING_METHODS.__doc__ = """
390Supported *Canon Log* log encoding curve / opto-electronic transfer function
391(OETF) methods.
393References
394----------
395:cite:`Canon2016`, :cite:`Canon2020`
396"""
399def log_encoding_CanonLog(
400 x: Domain1,
401 bit_depth: int = 10,
402 out_normalised_code_value: bool = True,
403 in_reflection: bool = True,
404 method: Literal["v1", "v1.2"] | str = "v1.2",
405) -> Range1:
406 """
407 Apply the *Canon Log* log encoding opto-electronic transfer function (OETF).
409 Parameters
410 ----------
411 x
412 Linear data :math:`x`.
413 bit_depth
414 Bit-depth used for conversion.
415 out_normalised_code_value
416 Whether the *Canon Log* non-linear data is encoded as normalised
417 code values.
418 in_reflection
419 Whether the light level :math:`x` to a camera is reflection.
420 method
421 Computation method.
423 Returns
424 -------
425 :class:`numpy.ndarray`
426 *Canon Log* non-linear encoded data.
428 References
429 ----------
430 :cite:`Canon2016`, :cite:`Canon2020`, :cite:`Thorpe2012a`
432 Notes
433 -----
434 +------------+-----------------------+---------------+
435 | **Domain** | **Scale - Reference** | **Scale - 1** |
436 +============+=======================+===============+
437 | ``x`` | 1 | 1 |
438 +------------+-----------------------+---------------+
440 +------------+-----------------------+---------------+
441 | **Range** | **Scale - Reference** | **Scale - 1** |
442 +============+=======================+===============+
443 | ``clog`` | 1 | 1 |
444 +------------+-----------------------+---------------+
446 Examples
447 --------
448 >>> log_encoding_CanonLog(0.18) * 100 # doctest: +ELLIPSIS
449 34.3389649...
450 >>> log_encoding_CanonLog(0.18, method="v1") * 100 # doctest: +ELLIPSIS
451 34.3389651...
453 The values of *Table 2 Canon-Log Code Values* table in
454 :cite:`Thorpe2012a` are obtained as follows:
456 >>> x = np.array([0, 2, 18, 90, 720]) / 100
457 >>> np.around(log_encoding_CanonLog(x, method="v1") * (2**10 - 1)).astype(np.int_)
458 array([ 128, 169, 351, 614, 1016])
459 >>> np.around(log_encoding_CanonLog(x, 10, False, method="v1") * 100, 1)
460 array([ 7.3, 12. , 32.8, 62.7, 108.7])
461 """
463 method = validate_method(method, tuple(CANON_LOG_ENCODING_METHODS))
465 return CANON_LOG_ENCODING_METHODS[method](
466 x, bit_depth, out_normalised_code_value, in_reflection
467 )
470CANON_LOG_DECODING_METHODS: CanonicalMapping = CanonicalMapping(
471 {
472 "v1": log_decoding_CanonLog_v1,
473 "v1.2": log_decoding_CanonLog_v1_2,
474 }
475)
476CANON_LOG_DECODING_METHODS.__doc__ = """
477Supported *Canon Log* log decoding curve / electro-optical transfer function
478(EOTF) methods.
480References
481----------
482:cite:`Canon2016`, :cite:`Canon2020`
483"""
486def log_decoding_CanonLog(
487 clog: Domain1,
488 bit_depth: int = 10,
489 in_normalised_code_value: bool = True,
490 out_reflection: bool = True,
491 method: Literal["v1", "v1.2"] | str = "v1.2",
492) -> Range1:
493 """
494 Apply the *Canon Log* log decoding inverse opto-electronic transfer function (OETF).
496 Parameters
497 ----------
498 clog
499 *Canon Log* non-linear encoded data.
500 bit_depth
501 Bit-depth used for conversion.
502 in_normalised_code_value
503 Whether the *Canon Log* non-linear data is encoded with normalised
504 code values.
505 out_reflection
506 Whether the light level :math:`x` to a camera is reflection.
507 method
508 Computation method.
510 Returns
511 -------
512 :class:`numpy.ndarray`
513 Linear data :math:`x`.
515 Notes
516 -----
517 +------------+-----------------------+---------------+
518 | **Domain** | **Scale - Reference** | **Scale - 1** |
519 +============+=======================+===============+
520 | ``clog`` | 1 | 1 |
521 +------------+-----------------------+---------------+
523 +------------+-----------------------+---------------+
524 | **Range** | **Scale - Reference** | **Scale - 1** |
525 +============+=======================+===============+
526 | ``x`` | 1 | 1 |
527 +------------+-----------------------+---------------+
529 References
530 ----------
531 :cite:`Canon2016`, :cite:`Canon2020`, :cite:`Thorpe2012a`
533 Examples
534 --------
535 >>> log_decoding_CanonLog(34.338964929528061 / 100) # doctest: +ELLIPSIS
536 0.17999999...
537 >>> log_decoding_CanonLog(34.338965172606912 / 100, method="v1")
538 ... # doctest: +ELLIPSIS
539 0.17999999...
540 """
542 method = validate_method(method, tuple(CANON_LOG_DECODING_METHODS))
544 return CANON_LOG_DECODING_METHODS[method](
545 clog, bit_depth, in_normalised_code_value, out_reflection
546 )
549def log_encoding_CanonLog2_v1(
550 x: Domain1,
551 bit_depth: int = 10,
552 out_normalised_code_value: bool = True,
553 in_reflection: bool = True,
554) -> Range1:
555 """
556 Apply the *Canon Log 2* v1 log encoding opto-electronic transfer function (OETF).
558 Parameters
559 ----------
560 x
561 Linear data :math:`x`.
562 bit_depth
563 Bit-depth used for conversion.
564 out_normalised_code_value
565 Whether the *Canon Log 2* v1 non-linear data is encoded as normalised
566 code values.
567 in_reflection
568 Whether the light level :math:`x` to a camera is reflection.
570 Returns
571 -------
572 :class:`numpy.ndarray`
573 *Canon Log 2* v1 non-linear encoded data.
575 Notes
576 -----
577 +------------+-----------------------+---------------+
578 | **Domain** | **Scale - Reference** | **Scale - 1** |
579 +============+=======================+===============+
580 | ``x`` | 1 | 1 |
581 +------------+-----------------------+---------------+
583 +------------+-----------------------+---------------+
584 | **Range** | **Scale - Reference** | **Scale - 1** |
585 +============+=======================+===============+
586 | ``clog2`` | 1 | 1 |
587 +------------+-----------------------+---------------+
589 References
590 ----------
591 :cite:`Canon2016`
593 Examples
594 --------
595 >>> log_encoding_CanonLog2_v1(0.18) * 100 # doctest: +ELLIPSIS
596 39.8254694...
597 """
599 x = to_domain_1(x)
601 if in_reflection:
602 x = x / 0.9
604 with domain_range_scale("ignore"):
605 clog2 = np.where(
606 x < log_decoding_CanonLog2_v1(0.035388128, bit_depth, False),
607 -(0.281863093 * (np.log10(-x * 87.09937546 + 1)) - 0.035388128),
608 0.281863093 * np.log10(x * 87.09937546 + 1) + 0.035388128,
609 )
611 clog2_cv = full_to_legal(clog2, bit_depth) if out_normalised_code_value else clog2
613 return as_float(from_range_1(clog2_cv))
616def log_decoding_CanonLog2_v1(
617 clog2: Domain1,
618 bit_depth: int = 10,
619 in_normalised_code_value: bool = True,
620 out_reflection: bool = True,
621) -> Range1:
622 """
623 Apply the *Canon Log 2* v1 log decoding inverse opto-electronic transfer
624 function (OETF).
626 Parameters
627 ----------
628 clog2
629 *Canon Log 2* v1 non-linear encoded data.
630 bit_depth
631 Bit-depth used for conversion.
632 in_normalised_code_value
633 Whether the *Canon Log 2* v1 non-linear data is encoded with
634 normalised code values.
635 out_reflection
636 Whether the light level :math:`x` to a camera is reflection.
638 Returns
639 -------
640 :class:`numpy.ndarray`
641 Linear data :math:`x`.
643 Notes
644 -----
645 +------------+-----------------------+---------------+
646 | **Domain** | **Scale - Reference** | **Scale - 1** |
647 +============+=======================+===============+
648 | ``clog2`` | 1 | 1 |
649 +------------+-----------------------+---------------+
651 +------------+-----------------------+---------------+
652 | **Range** | **Scale - Reference** | **Scale - 1** |
653 +============+=======================+===============+
654 | ``x`` | 1 | 1 |
655 +------------+-----------------------+---------------+
657 References
658 ----------
659 :cite:`Canon2016`
661 Examples
662 --------
663 >>> log_decoding_CanonLog2_v1(39.825469498316735 / 100) # doctest: +ELLIPSIS
664 0.1799999...
665 """
667 clog2 = to_domain_1(clog2)
669 clog2 = legal_to_full(clog2, bit_depth) if in_normalised_code_value else clog2
671 x = np.where(
672 clog2 < 0.035388128,
673 -(10 ** ((0.035388128 - clog2) / 0.281863093) - 1) / 87.09937546,
674 (10 ** ((clog2 - 0.035388128) / 0.281863093) - 1) / 87.09937546,
675 )
677 if out_reflection:
678 x = x * 0.9
680 return as_float(from_range_1(x))
683def log_encoding_CanonLog2_v1_2(
684 x: Domain1,
685 bit_depth: int = 10,
686 out_normalised_code_value: bool = True,
687 in_reflection: bool = True,
688) -> Range1:
689 """
690 Apply the *Canon Log 2* v1.2 log encoding opto-electronic transfer function
691 (OETF).
693 Parameters
694 ----------
695 x
696 Linear data :math:`x`.
697 bit_depth
698 Bit-depth used for conversion.
699 out_normalised_code_value
700 Whether the *Canon Log 2* v1.2 non-linear data is encoded as
701 normalised code values.
702 in_reflection
703 Whether the light level :math:`x` to a camera is reflection.
705 Returns
706 -------
707 :class:`numpy.ndarray`
708 *Canon Log 2* v1.2 non-linear encoded data.
710 Notes
711 -----
712 +------------+-----------------------+---------------+
713 | **Domain** | **Scale - Reference** | **Scale - 1** |
714 +============+=======================+===============+
715 | ``x`` | 1 | 1 |
716 +------------+-----------------------+---------------+
718 +------------+-----------------------+---------------+
719 | **Range** | **Scale - Reference** | **Scale - 1** |
720 +============+=======================+===============+
721 | ``clog2`` | 1 | 1 |
722 +------------+-----------------------+---------------+
724 References
725 ----------
726 :cite:`Canon2020`
728 Examples
729 --------
730 >>> log_encoding_CanonLog2_v1_2(0.18) * 100 # doctest: +ELLIPSIS
731 39.8254692...
732 """
734 x = to_domain_1(x)
736 if in_reflection:
737 x = x / 0.9
739 with domain_range_scale("ignore"):
740 clog2 = np.where(
741 x < (log_decoding_CanonLog2_v1_2(0.092864125, bit_depth, True)),
742 -(0.24136077 * (np.log10(-x * 87.09937546 + 1)) - 0.092864125),
743 0.24136077 * np.log10(x * 87.09937546 + 1) + 0.092864125,
744 )
746 # NOTE: *Canon Log 2* v1.2 constants are expressed in legal range
747 # (studio swing).
748 clog2_cv = clog2 if out_normalised_code_value else legal_to_full(clog2, bit_depth)
750 return as_float(from_range_1(clog2_cv))
753def log_decoding_CanonLog2_v1_2(
754 clog2: Domain1,
755 bit_depth: int = 10,
756 in_normalised_code_value: bool = True,
757 out_reflection: bool = True,
758) -> Range1:
759 """
760 Apply the *Canon Log 2* v1.2 log decoding inverse opto-electronic transfer
761 function (OETF).
763 Parameters
764 ----------
765 clog2
766 *Canon Log 2* v1.2 non-linear encoded data.
767 bit_depth
768 Bit-depth used for conversion.
769 in_normalised_code_value
770 Whether the *Canon Log 2* v1.2 non-linear data is encoded with normalised
771 code values.
772 out_reflection
773 Whether the light level :math:`x` to a camera is reflection.
775 Returns
776 -------
777 :class:`numpy.ndarray`
778 Linear data :math:`x`.
780 Notes
781 -----
782 +------------+-----------------------+---------------+
783 | **Domain** | **Scale - Reference** | **Scale - 1** |
784 +============+=======================+===============+
785 | ``clog2`` | 1 | 1 |
786 +------------+-----------------------+---------------+
788 +------------+-----------------------+---------------+
789 | **Range** | **Scale - Reference** | **Scale - 1** |
790 +============+=======================+===============+
791 | ``x`` | 1 | 1 |
792 +------------+-----------------------+---------------+
794 References
795 ----------
796 :cite:`Canon2020`
798 Examples
799 --------
800 >>> log_decoding_CanonLog2_v1_2(39.825469256149191 / 100)
801 ... # doctest: +ELLIPSIS
802 0.1799999...
803 """
805 clog2 = to_domain_1(clog2)
807 # NOTE: *Canon Log 2* v1.2 constants are expressed in legal range
808 # (studio swing).
809 clog2 = clog2 if in_normalised_code_value else full_to_legal(clog2, bit_depth)
811 x = np.where(
812 clog2 < 0.092864125,
813 -(10 ** ((0.092864125 - clog2) / 0.24136077) - 1) / 87.09937546,
814 (10 ** ((clog2 - 0.092864125) / 0.24136077) - 1) / 87.09937546,
815 )
817 if out_reflection:
818 x = x * 0.9
820 return as_float(from_range_1(x))
823CANON_LOG_2_ENCODING_METHODS: CanonicalMapping = CanonicalMapping(
824 {
825 "v1": log_encoding_CanonLog2_v1,
826 "v1.2": log_encoding_CanonLog2_v1_2,
827 }
828)
829CANON_LOG_2_ENCODING_METHODS.__doc__ = """
830Supported *Canon Log 2* log encoding curve / opto-electronic transfer function
831(OETF) methods.
833References
834----------
835:cite:`Canon2016`, :cite:`Canon2020`
836"""
839def log_encoding_CanonLog2(
840 x: Domain1,
841 bit_depth: int = 10,
842 out_normalised_code_value: bool = True,
843 in_reflection: bool = True,
844 method: Literal["v1", "v1.2"] | str = "v1.2",
845) -> Range1:
846 """
847 Apply the *Canon Log 2* log encoding opto-electronic transfer function (OETF).
849 Parameters
850 ----------
851 x
852 Linear data :math:`x`.
853 bit_depth
854 Bit-depth used for conversion.
855 out_normalised_code_value
856 Whether the *Canon Log 2* non-linear data is encoded as normalised
857 code values.
858 in_reflection
859 Whether the light level :math:`x` to a camera is reflection.
860 method
861 Computation method.
863 Returns
864 -------
865 :class:`numpy.ndarray`
866 *Canon Log 2* non-linear encoded data.
868 Notes
869 -----
870 +------------+-----------------------+---------------+
871 | **Domain** | **Scale - Reference** | **Scale - 1** |
872 +============+=======================+===============+
873 | ``x`` | 1 | 1 |
874 +------------+-----------------------+---------------+
876 +------------+-----------------------+---------------+
877 | **Range** | **Scale - Reference** | **Scale - 1** |
878 +============+=======================+===============+
879 | ``clog2`` | 1 | 1 |
880 +------------+-----------------------+---------------+
882 References
883 ----------
884 :cite:`Canon2016`, :cite:`Canon2020`
886 Examples
887 --------
888 >>> log_encoding_CanonLog2(0.18) * 100 # doctest: +ELLIPSIS
889 39.8254692...
890 """
892 method = validate_method(method, tuple(CANON_LOG_2_ENCODING_METHODS))
894 return CANON_LOG_2_ENCODING_METHODS[method](
895 x, bit_depth, out_normalised_code_value, in_reflection
896 )
899CANON_LOG_2_DECODING_METHODS: CanonicalMapping = CanonicalMapping(
900 {
901 "v1": log_decoding_CanonLog2_v1,
902 "v1.2": log_decoding_CanonLog2_v1_2,
903 }
904)
905CANON_LOG_2_DECODING_METHODS.__doc__ = """
906Supported *Canon Log 2* log decoding curve / electro-optical transfer function
907(EOTF) methods.
909References
910----------
911:cite:`Canon2016`, :cite:`Canon2020`
912"""
915def log_decoding_CanonLog2(
916 clog2: Domain1,
917 bit_depth: int = 10,
918 in_normalised_code_value: bool = True,
919 out_reflection: bool = True,
920 method: Literal["v1", "v1.2"] | str = "v1.2",
921) -> Range1:
922 """
923 Apply the *Canon Log 2* log decoding inverse opto-electronic transfer
924 function (OETF).
926 Parameters
927 ----------
928 clog2
929 *Canon Log 2* non-linear encoded data.
930 bit_depth
931 Bit-depth used for conversion.
932 in_normalised_code_value
933 Whether the *Canon Log 2* non-linear data is encoded with normalised
934 code values.
935 out_reflection
936 Whether the light level :math:`x` to a camera is reflection.
937 method
938 Computation method.
940 Returns
941 -------
942 :class:`numpy.ndarray`
943 Linear data :math:`x`.
945 Notes
946 -----
947 +------------+-----------------------+---------------+
948 | **Domain** | **Scale - Reference** | **Scale - 1** |
949 +============+=======================+===============+
950 | ``clog2`` | 1 | 1 |
951 +------------+-----------------------+---------------+
953 +------------+-----------------------+---------------+
954 | **Range** | **Scale - Reference** | **Scale - 1** |
955 +============+=======================+===============+
956 | ``x`` | 1 | 1 |
957 +------------+-----------------------+---------------+
959 References
960 ----------
961 :cite:`Canon2016`, :cite:`Canon2020`
963 Examples
964 --------
965 >>> log_decoding_CanonLog2(39.825469256149191 / 100) # doctest: +ELLIPSIS
966 0.1799999...
967 """
969 method = validate_method(method, tuple(CANON_LOG_2_DECODING_METHODS))
971 return CANON_LOG_2_DECODING_METHODS[method](
972 clog2, bit_depth, in_normalised_code_value, out_reflection
973 )
976def log_encoding_CanonLog3_v1(
977 x: Domain1,
978 bit_depth: int = 10,
979 out_normalised_code_value: bool = True,
980 in_reflection: bool = True,
981) -> Range1:
982 """
983 Apply the *Canon Log 3* v1 log encoding opto-electronic transfer function
984 (OETF).
986 Parameters
987 ----------
988 x
989 Linear data :math:`x`.
990 bit_depth
991 Bit-depth used for conversion.
992 out_normalised_code_value
993 Whether the *Canon Log 3* v1 non-linear data is encoded as
994 normalised code values.
995 in_reflection
996 Whether the light level :math:`x` to a camera is reflection.
998 Returns
999 -------
1000 :class:`numpy.ndarray`
1001 *Canon Log 3* v1 non-linear encoded data.
1003 Notes
1004 -----
1005 - Introspection of the grafting points by Shaw, N. (2018) shows
1006 that the *Canon Log 3* v1 IDT was likely derived from its
1007 encoding curve as the latter is grafted at *+/-0.014*::
1009 >>> clog3 = 0.04076162
1010 >>> (clog3 - 0.073059361) / 2.3069815
1011 -0.014000000000000002
1012 >>> clog3 = 0.105357102
1013 >>> (clog3 - 0.073059361) / 2.3069815
1014 0.013999999999999997
1016 +------------+-----------------------+---------------+
1017 | **Domain** | **Scale - Reference** | **Scale - 1** |
1018 +============+=======================+===============+
1019 | ``x`` | 1 | 1 |
1020 +------------+-----------------------+---------------+
1022 +------------+-----------------------+---------------+
1023 | **Range** | **Scale - Reference** | **Scale - 1** |
1024 +============+=======================+===============+
1025 | ``clog3`` | 1 | 1 |
1026 +------------+-----------------------+---------------+
1028 References
1029 ----------
1030 :cite:`Canon2016`
1032 Examples
1033 --------
1034 >>> log_encoding_CanonLog3_v1(0.18) * 100 # doctest: +ELLIPSIS
1035 34.3389369...
1036 """
1038 x = to_domain_1(x)
1040 if in_reflection:
1041 x = x / 0.9
1043 with domain_range_scale("ignore"):
1044 clog3 = np.select(
1045 (
1046 x < log_decoding_CanonLog3_v1(0.04076162, bit_depth, False, False),
1047 x <= log_decoding_CanonLog3_v1(0.105357102, bit_depth, False, False),
1048 x > log_decoding_CanonLog3_v1(0.105357102, bit_depth, False, False),
1049 ),
1050 (
1051 -0.42889912 * np.log10(-x * 14.98325 + 1) + 0.07623209,
1052 2.3069815 * x + 0.073059361,
1053 0.42889912 * np.log10(x * 14.98325 + 1) + 0.069886632,
1054 ),
1055 )
1057 clog3_cv = full_to_legal(clog3, bit_depth) if out_normalised_code_value else clog3
1059 return as_float(from_range_1(clog3_cv))
1062def log_decoding_CanonLog3_v1(
1063 clog3: Domain1,
1064 bit_depth: int = 10,
1065 in_normalised_code_value: bool = True,
1066 out_reflection: bool = True,
1067) -> Range1:
1068 """
1069 Apply the *Canon Log 3* v1 log decoding inverse opto-electronic transfer
1070 function (OETF).
1072 Parameters
1073 ----------
1074 clog3
1075 *Canon Log 3* v1 non-linear encoded data.
1076 bit_depth
1077 Bit-depth used for conversion.
1078 in_normalised_code_value
1079 Whether the *Canon Log 3* v1 non-linear data is encoded with
1080 normalised code values.
1081 out_reflection
1082 Whether the light level :math:`x` to a camera is reflection.
1084 Returns
1085 -------
1086 :class:`numpy.ndarray`
1087 Linear data :math:`x`.
1089 Notes
1090 -----
1091 +------------+-----------------------+---------------+
1092 | **Domain** | **Scale - Reference** | **Scale - 1** |
1093 +============+=======================+===============+
1094 | ``clog3`` | 1 | 1 |
1095 +------------+-----------------------+---------------+
1097 +------------+-----------------------+---------------+
1098 | **Range** | **Scale - Reference** | **Scale - 1** |
1099 +============+=======================+===============+
1100 | ``x`` | 1 | 1 |
1101 +------------+-----------------------+---------------+
1103 References
1104 ----------
1105 :cite:`Canon2016`
1107 Examples
1108 --------
1109 >>> log_decoding_CanonLog3_v1(34.338936938868677 / 100) # doctest: +ELLIPSIS
1110 0.1800000...
1111 """
1113 clog3 = to_domain_1(clog3)
1115 clog3 = legal_to_full(clog3, bit_depth) if in_normalised_code_value else clog3
1117 x = np.select(
1118 (clog3 < 0.04076162, clog3 <= 0.105357102, clog3 > 0.105357102),
1119 (
1120 -(10 ** ((0.07623209 - clog3) / 0.42889912) - 1) / 14.98325,
1121 (clog3 - 0.073059361) / 2.3069815,
1122 (10 ** ((clog3 - 0.069886632) / 0.42889912) - 1) / 14.98325,
1123 ),
1124 )
1126 if out_reflection:
1127 x = x * 0.9
1129 return as_float(from_range_1(x))
1132def log_encoding_CanonLog3_v1_2(
1133 x: Domain1,
1134 bit_depth: int = 10,
1135 out_normalised_code_value: bool = True,
1136 in_reflection: bool = True,
1137) -> Range1:
1138 """
1139 Apply the *Canon Log 3* v1.2 log encoding opto-electronic transfer function
1140 (OETF).
1142 Parameters
1143 ----------
1144 x
1145 Linear data :math:`x`.
1146 bit_depth
1147 Bit-depth used for conversion.
1148 out_normalised_code_value
1149 Whether the *Canon Log 3* v1.2 non-linear data is encoded as normalised
1150 code values.
1151 in_reflection
1152 Whether the light level :math:`x` to a camera is reflection.
1154 Returns
1155 -------
1156 :class:`numpy.ndarray`
1157 *Canon Log 3* v1.2 non-linear encoded data.
1159 Notes
1160 -----
1161 +------------+-----------------------+---------------+
1162 | **Domain** | **Scale - Reference** | **Scale - 1** |
1163 +============+=======================+===============+
1164 | ``x`` | 1 | 1 |
1165 +------------+-----------------------+---------------+
1167 +------------+-----------------------+---------------+
1168 | **Range** | **Scale - Reference** | **Scale - 1** |
1169 +============+=======================+===============+
1170 | ``clog3`` | 1 | 1 |
1171 +------------+-----------------------+---------------+
1173 References
1174 ----------
1175 :cite:`Canon2020`
1177 Examples
1178 --------
1179 >>> log_encoding_CanonLog3_v1_2(0.18) * 100 # doctest: +ELLIPSIS
1180 34.3389370...
1181 """
1183 x = to_domain_1(x)
1185 if in_reflection:
1186 x = x / 0.9
1188 with domain_range_scale("ignore"):
1189 clog3 = np.select(
1190 (
1191 x < log_decoding_CanonLog3_v1_2(0.097465473, bit_depth, True, False),
1192 x <= log_decoding_CanonLog3_v1_2(0.15277891, bit_depth, True, False),
1193 x > log_decoding_CanonLog3_v1_2(0.15277891, bit_depth, True, False),
1194 ),
1195 (
1196 -0.36726845 * np.log10(-x * 14.98325 + 1) + 0.12783901,
1197 1.9754798 * x + 0.12512219,
1198 0.36726845 * np.log10(x * 14.98325 + 1) + 0.12240537,
1199 ),
1200 )
1202 # NOTE: *Canon Log 3* v1.2 constants are expressed in legal range
1203 # (studio swing).
1204 clog3_cv = clog3 if out_normalised_code_value else legal_to_full(clog3, bit_depth)
1206 return as_float(from_range_1(clog3_cv))
1209def log_decoding_CanonLog3_v1_2(
1210 clog3: Domain1,
1211 bit_depth: int = 10,
1212 in_normalised_code_value: bool = True,
1213 out_reflection: bool = True,
1214) -> Range1:
1215 """
1216 Apply the *Canon Log 3* v1.2 log decoding inverse opto-electronic transfer
1217 function (OETF).
1219 Parameters
1220 ----------
1221 clog3
1222 *Canon Log 3* v1.2 non-linear encoded data.
1223 bit_depth
1224 Bit-depth used for conversion.
1225 in_normalised_code_value
1226 Whether the *Canon Log 3* v1.2 non-linear data is encoded with
1227 normalised code values.
1228 out_reflection
1229 Whether the light level :math:`x` to a camera is reflection.
1231 Returns
1232 -------
1233 :class:`numpy.ndarray`
1234 Linear data :math:`x`.
1236 Notes
1237 -----
1238 +------------+-----------------------+---------------+
1239 | **Domain** | **Scale - Reference** | **Scale - 1** |
1240 +============+=======================+===============+
1241 | ``clog3`` | 1 | 1 |
1242 +------------+-----------------------+---------------+
1244 +------------+-----------------------+---------------+
1245 | **Range** | **Scale - Reference** | **Scale - 1** |
1246 +============+=======================+===============+
1247 | ``x`` | 1 | 1 |
1248 +------------+-----------------------+---------------+
1250 References
1251 ----------
1252 :cite:`Canon2020`
1254 Examples
1255 --------
1256 >>> log_decoding_CanonLog3_v1_2(34.338937037393549 / 100)
1257 ... # doctest: +ELLIPSIS
1258 0.1799999...
1259 """
1261 clog3 = to_domain_1(clog3)
1263 # NOTE: *Canon Log 3* v1.2 constants are expressed in legal range
1264 # (studio swing).
1265 clog3 = clog3 if in_normalised_code_value else full_to_legal(clog3, bit_depth)
1267 x = np.select(
1268 (clog3 < 0.097465473, clog3 <= 0.15277891, clog3 > 0.15277891),
1269 (
1270 -(10 ** ((0.12783901 - clog3) / 0.36726845) - 1) / 14.98325,
1271 (clog3 - 0.12512219) / 1.9754798,
1272 (10 ** ((clog3 - 0.12240537) / 0.36726845) - 1) / 14.98325,
1273 ),
1274 )
1276 if out_reflection:
1277 x = x * 0.9
1279 return as_float(from_range_1(x))
1282CANON_LOG_3_ENCODING_METHODS: CanonicalMapping = CanonicalMapping(
1283 {
1284 "v1": log_encoding_CanonLog3_v1,
1285 "v1.2": log_encoding_CanonLog3_v1_2,
1286 }
1287)
1288CANON_LOG_3_ENCODING_METHODS.__doc__ = """
1289Supported *Canon Log 3* log encoding curve / opto-electronic transfer function
1290(OETF) methods.
1292References
1293----------
1294:cite:`Canon2016`, :cite:`Canon2020`
1295"""
1298def log_encoding_CanonLog3(
1299 x: Domain1,
1300 bit_depth: int = 10,
1301 out_normalised_code_value: bool = True,
1302 in_reflection: bool = True,
1303 method: Literal["v1", "v1.2"] | str = "v1.2",
1304) -> Range1:
1305 """
1306 Apply the *Canon Log 3* log encoding opto-electronic transfer function (OETF).
1308 Parameters
1309 ----------
1310 x
1311 Linear data :math:`x`.
1312 bit_depth
1313 Bit-depth used for conversion.
1314 out_normalised_code_value
1315 Whether the *Canon Log 3* non-linear data is encoded as normalised
1316 code values.
1317 in_reflection
1318 Whether the light level :math:`x` to a camera is reflection.
1319 method
1320 Computation method.
1322 Returns
1323 -------
1324 :class:`numpy.ndarray`
1325 *Canon Log 3* non-linear encoded data.
1327 Notes
1328 -----
1329 - Introspection of the grafting points by Shaw, N. (2018) shows that
1330 the *Canon Log 3* v1 IDT was likely derived from its encoding curve
1331 as the latter is grafted at *+/-0.014*::
1333 >>> clog3 = 0.04076162
1334 >>> (clog3 - 0.073059361) / 2.3069815
1335 -0.014000000000000002
1336 >>> clog3 = 0.105357102
1337 >>> (clog3 - 0.073059361) / 2.3069815
1338 0.013999999999999997
1340 +------------+-----------------------+---------------+
1341 | **Domain** | **Scale - Reference** | **Scale - 1** |
1342 +============+=======================+===============+
1343 | ``x`` | 1 | 1 |
1344 +------------+-----------------------+---------------+
1346 +------------+-----------------------+---------------+
1347 | **Range** | **Scale - Reference** | **Scale - 1** |
1348 +============+=======================+===============+
1349 | ``clog3`` | 1 | 1 |
1350 +------------+-----------------------+---------------+
1352 References
1353 ----------
1354 :cite:`Canon2016`, :cite:`Canon2020`
1356 Examples
1357 --------
1358 >>> log_encoding_CanonLog3(0.18) * 100 # doctest: +ELLIPSIS
1359 34.3389370...
1360 """
1362 method = validate_method(method, tuple(CANON_LOG_3_ENCODING_METHODS))
1364 return CANON_LOG_3_ENCODING_METHODS[method](
1365 x, bit_depth, out_normalised_code_value, in_reflection
1366 )
1369CANON_LOG_3_DECODING_METHODS: CanonicalMapping = CanonicalMapping(
1370 {
1371 "v1": log_decoding_CanonLog3_v1,
1372 "v1.2": log_decoding_CanonLog3_v1_2,
1373 }
1374)
1375CANON_LOG_3_DECODING_METHODS.__doc__ = """
1376Supported *Canon Log 3* log decoding curve / electro-optical transfer function
1377(EOTF) methods.
1379References
1380----------
1381:cite:`Canon2016`, :cite:`Canon2020`
1382"""
1385def log_decoding_CanonLog3(
1386 clog3: Domain1,
1387 bit_depth: int = 10,
1388 in_normalised_code_value: bool = True,
1389 out_reflection: bool = True,
1390 method: Literal["v1", "v1.2"] | str = "v1.2",
1391) -> Range1:
1392 """
1393 Apply the *Canon Log 3* log decoding inverse opto-electronic transfer
1394 function (OETF).
1396 Parameters
1397 ----------
1398 clog3
1399 *Canon Log 3* non-linear encoded data.
1400 bit_depth
1401 Bit-depth used for conversion.
1402 in_normalised_code_value
1403 Whether the *Canon Log 3* non-linear data is encoded with normalised
1404 code values.
1405 out_reflection
1406 Whether the light level :math:`x` to a camera is reflection.
1407 method
1408 Computation method.
1410 Returns
1411 -------
1412 :class:`numpy.ndarray`
1413 Linear data :math:`x`.
1415 Notes
1416 -----
1417 +------------+-----------------------+---------------+
1418 | **Domain** | **Scale - Reference** | **Scale - 1** |
1419 +============+=======================+===============+
1420 | ``clog3`` | 1 | 1 |
1421 +------------+-----------------------+---------------+
1423 +------------+-----------------------+---------------+
1424 | **Range** | **Scale - Reference** | **Scale - 1** |
1425 +============+=======================+===============+
1426 | ``x`` | 1 | 1 |
1427 +------------+-----------------------+---------------+
1429 References
1430 ----------
1431 :cite:`Canon2016`, :cite:`Canon2020`
1433 Examples
1434 --------
1435 >>> log_decoding_CanonLog3(34.338937037393549 / 100) # doctest: +ELLIPSIS
1436 0.1799999...
1437 """
1439 method = validate_method(method, tuple(CANON_LOG_3_DECODING_METHODS))
1441 return CANON_LOG_3_DECODING_METHODS[method](
1442 clog3, bit_depth, in_normalised_code_value, out_reflection
1443 )