Coverage for models/rgb/transfer_functions/leica_l_log.py: 62%
45 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"""
2Leica L-Log Log Encoding
3========================
5Define the *Leica L-Log* log encoding.
7- :func:`colour.models.log_encoding_LLog`
8- :func:`colour.models.log_decoding_LLog`
10References
11----------
12- :cite:`LeicaCameraAG2022` : Leica Camera AG. (2022). Leica L-Log Reference
13 Manual. https://leica-camera.com/sites/default/files/\
14pm-65976-210914__L-Log_Reference_Manual_EN.pdf
15"""
17from __future__ import annotations
19import numpy as np
21from colour.algebra import spow
22from colour.hints import ( # noqa: TC001
23 Domain1,
24 Range1,
25)
26from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full
27from colour.utilities import Structure, as_float, from_range_1, optional, to_domain_1
29__author__ = "Colour Developers"
30__copyright__ = "Copyright 2013 Colour Developers"
31__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
32__maintainer__ = "Colour Developers"
33__email__ = "colour-developers@colour-science.org"
34__status__ = "Production"
36__all__ = [
37 "CONSTANTS_LLOG",
38 "log_encoding_LLog",
39 "log_decoding_LLog",
40]
42CONSTANTS_LLOG: Structure = Structure(
43 cut1=0.006,
44 cut2=0.1380,
45 a=8,
46 b=0.09,
47 c=0.27,
48 d=1.3,
49 e=0.0115,
50 f=0.6,
51)
52"""*Leica L-Log* constants."""
55def log_encoding_LLog(
56 LSR: Domain1,
57 bit_depth: int = 10,
58 out_normalised_code_value: bool = True,
59 in_reflection: bool = True,
60 constants: Structure | None = None,
61) -> Range1:
62 """
63 Apply the *Leica L-Log* log encoding opto-electronic transfer function (OETF).
65 Parameters
66 ----------
67 LSR
68 Linear scene reflection :math:`LSR` values.
69 bit_depth
70 Bit-depth used for conversion.
71 out_normalised_code_value
72 Whether the *Leica L-Log* non-linear data :math:`L-Log` is encoded
73 as normalised code values.
74 in_reflection
75 Whether the light level :math:`in` to a camera is reflection.
76 constants
77 *Leica L-Log* constants.
79 Returns
80 -------
81 :class:`numpy.ndarray`
82 *L-Log* 10-bit equivalent code value :math:`L-Log`.
84 Notes
85 -----
86 +------------+-----------------------+---------------+
87 | **Domain** | **Scale - Reference** | **Scale - 1** |
88 +============+=======================+===============+
89 | ``LSR`` | 1 | 1 |
90 +------------+-----------------------+---------------+
92 +------------+-----------------------+---------------+
93 | **Range** | **Scale - Reference** | **Scale - 1** |
94 +============+=======================+===============+
95 | ``LLog`` | 1 | 1 |
96 +------------+-----------------------+---------------+
98 References
99 ----------
100 :cite:`LeicaCameraAG2022`
102 Examples
103 --------
104 >>> log_encoding_LLog(0.18) # doctest: +ELLIPSIS
105 0.4353139...
106 """
108 LSR = to_domain_1(LSR)
109 constants = optional(constants, CONSTANTS_LLOG)
111 if not in_reflection:
112 LSR = LSR * 0.9
114 cut1 = constants.cut1
115 a = constants.a
116 b = constants.b
117 c = constants.c
118 d = constants.d
119 e = constants.e
120 f = constants.f
122 LLog = np.where(
123 cut1 >= LSR,
124 a * LSR + b,
125 c * np.log10(d * LSR + e) + f,
126 )
128 LLog_cv = LLog if out_normalised_code_value else legal_to_full(LLog, bit_depth)
130 return as_float(from_range_1(LLog_cv))
133def log_decoding_LLog(
134 LLog: Domain1,
135 bit_depth: int = 10,
136 in_normalised_code_value: bool = True,
137 out_reflection: bool = True,
138 constants: Structure | None = None,
139) -> Range1:
140 """
141 Apply the *Leica L-Log* log decoding inverse opto-electronic transfer
142 function (OETF).
144 Parameters
145 ----------
146 LLog
147 *L-Log* 10-bit equivalent code value :math:`L-Log`.
148 bit_depth
149 Bit-depth used for conversion.
150 in_normalised_code_value
151 Whether the *Leica L-Log* non-linear data :math:`L-Log` is encoded
152 as normalised code values.
153 out_reflection
154 Whether the light level :math:`in` to a camera is reflection.
155 constants
156 *Leica L-Log* constants.
158 Returns
159 -------
160 :class:`numpy.ndarray`
161 Linear scene reflection :math:`LSR` values.
163 Notes
164 -----
165 +------------+-----------------------+---------------+
166 | **Domain** | **Scale - Reference** | **Scale - 1** |
167 +============+=======================+===============+
168 | ``LLog`` | 1 | 1 |
169 +------------+-----------------------+---------------+
171 +------------+-----------------------+---------------+
172 | **Range** | **Scale - Reference** | **Scale - 1** |
173 +============+=======================+===============+
174 | ``LSR`` | 1 | 1 |
175 +------------+-----------------------+---------------+
177 References
178 ----------
179 :cite:`LeicaCameraAG2022`
181 Examples
182 --------
183 >>> log_decoding_LLog(0.43531390404392656) # doctest: +ELLIPSIS
184 0.1800000...
185 """
187 LLog = to_domain_1(LLog)
188 constants = optional(constants, CONSTANTS_LLOG)
190 LLog = LLog if in_normalised_code_value else full_to_legal(LLog, bit_depth)
192 cut2 = constants.cut2
193 a = constants.a
194 b = constants.b
195 c = constants.c
196 d = constants.d
197 e = constants.e
198 f = constants.f
200 LSR = np.where(
201 LLog <= cut2,
202 (LLog - b) / a,
203 (spow(10, (LLog - f) / c) - e) / d,
204 )
206 if not out_reflection:
207 LSR = LSR / 0.9
209 return as_float(from_range_1(LSR))