Coverage for colour/models/rgb/transfer_functions/leica_l_log.py: 100%

45 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-15 19:01 +1300

1""" 

2Leica L-Log Log Encoding 

3======================== 

4 

5Define the *Leica L-Log* log encoding. 

6 

7- :func:`colour.models.log_encoding_LLog` 

8- :func:`colour.models.log_decoding_LLog` 

9 

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""" 

16 

17from __future__ import annotations 

18 

19import numpy as np 

20 

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 

28 

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" 

35 

36__all__ = [ 

37 "CONSTANTS_LLOG", 

38 "log_encoding_LLog", 

39 "log_decoding_LLog", 

40] 

41 

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.""" 

53 

54 

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). 

64 

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. 

78 

79 Returns 

80 ------- 

81 :class:`numpy.ndarray` 

82 *L-Log* 10-bit equivalent code value :math:`L-Log`. 

83 

84 Notes 

85 ----- 

86 +------------+-----------------------+---------------+ 

87 | **Domain** | **Scale - Reference** | **Scale - 1** | 

88 +============+=======================+===============+ 

89 | ``LSR`` | 1 | 1 | 

90 +------------+-----------------------+---------------+ 

91 

92 +------------+-----------------------+---------------+ 

93 | **Range** | **Scale - Reference** | **Scale - 1** | 

94 +============+=======================+===============+ 

95 | ``LLog`` | 1 | 1 | 

96 +------------+-----------------------+---------------+ 

97 

98 References 

99 ---------- 

100 :cite:`LeicaCameraAG2022` 

101 

102 Examples 

103 -------- 

104 >>> log_encoding_LLog(0.18) # doctest: +ELLIPSIS 

105 0.4353139... 

106 """ 

107 

108 LSR = to_domain_1(LSR) 

109 constants = optional(constants, CONSTANTS_LLOG) 

110 

111 if not in_reflection: 

112 LSR = LSR * 0.9 

113 

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 

121 

122 LLog = np.where( 

123 cut1 >= LSR, 

124 a * LSR + b, 

125 c * np.log10(d * LSR + e) + f, 

126 ) 

127 

128 LLog_cv = LLog if out_normalised_code_value else legal_to_full(LLog, bit_depth) 

129 

130 return as_float(from_range_1(LLog_cv)) 

131 

132 

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). 

143 

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. 

157 

158 Returns 

159 ------- 

160 :class:`numpy.ndarray` 

161 Linear scene reflection :math:`LSR` values. 

162 

163 Notes 

164 ----- 

165 +------------+-----------------------+---------------+ 

166 | **Domain** | **Scale - Reference** | **Scale - 1** | 

167 +============+=======================+===============+ 

168 | ``LLog`` | 1 | 1 | 

169 +------------+-----------------------+---------------+ 

170 

171 +------------+-----------------------+---------------+ 

172 | **Range** | **Scale - Reference** | **Scale - 1** | 

173 +============+=======================+===============+ 

174 | ``LSR`` | 1 | 1 | 

175 +------------+-----------------------+---------------+ 

176 

177 References 

178 ---------- 

179 :cite:`LeicaCameraAG2022` 

180 

181 Examples 

182 -------- 

183 >>> log_decoding_LLog(0.43531390404392656) # doctest: +ELLIPSIS 

184 0.1800000... 

185 """ 

186 

187 LLog = to_domain_1(LLog) 

188 constants = optional(constants, CONSTANTS_LLOG) 

189 

190 LLog = LLog if in_normalised_code_value else full_to_legal(LLog, bit_depth) 

191 

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 

199 

200 LSR = np.where( 

201 LLog <= cut2, 

202 (LLog - b) / a, 

203 (spow(10, (LLog - f) / c) - e) / d, 

204 ) 

205 

206 if not out_reflection: 

207 LSR = LSR / 0.9 

208 

209 return as_float(from_range_1(LSR))