Coverage for models/rgb/transfer_functions/leica_l_log.py: 62%

1"""

2 Leica L-Log Log Encoding

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

4

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

6

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

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

9

10 References

11----------

12- :cite:`LeicaCameraAG2022` : Leica Camera AG. (2022). Leica L-Log Reference

13 Manual. https://leica-camera.com/sites/default/files/\

14 pm-65976-210914__L-Log_Reference_Manual_EN.pdf

15"""

16

17 from __future__ import annotations

18

19 import numpy as np

20

21 from colour.algebra import spow

22 from colour.hints import ( # noqa: TC001

23 Domain1,

24 Range1,

25)

26 from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full

27 from 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

42 CONSTANTS_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

55 def 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

133 def 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))