Coverage for models/rgb/transfer_functions/aces.py: 59%

1"""

2 Academy Color Encoding System - Log Encodings

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

4

5 Define the *Academy Color Encoding System* (ACES) log encodings.

6

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

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

9- :func:`colour.models.log_encoding_ACEScc`

10- :func:`colour.models.log_decoding_ACEScc`

11- :func:`colour.models.log_encoding_ACEScct`

12- :func:`colour.models.log_decoding_ACEScct`

13

14 References

15----------

16- :cite:`TheAcademyofMotionPictureArtsandSciences2014q` : The Academy of

17 Motion Picture Arts and Sciences, Science and Technology Council, & Academy

18 Color Encoding System (ACES) Project Subcommittee. (2014). Technical

19 Bulletin TB-2014-004 - Informative Notes on SMPTE ST 2065-1 - Academy Color

20 Encoding Specification (ACES) (pp. 1-40). Retrieved December 19, 2014, from

21 http://j.mp/TB-2014-004

22- :cite:`TheAcademyofMotionPictureArtsandSciences2014r` : The Academy of

23 Motion Picture Arts and Sciences, Science and Technology Council, & Academy

24 Color Encoding System (ACES) Project Subcommittee. (2014). Technical

25 Bulletin TB-2014-012 - Academy Color Encoding System Version 1.0 Component

26 Names (pp. 1-8). Retrieved December 19, 2014, from http://j.mp/TB-2014-012

27- :cite:`TheAcademyofMotionPictureArtsandSciences2014s` : The Academy of

28 Motion Picture Arts and Sciences, Science and Technology Council, & Academy

29 Color Encoding System (ACES) Project Subcommittee. (2013). Specification

30 S-2013-001 - ACESproxy, an int Log Encoding of ACES Image Data.

31 Retrieved December 19, 2014, from http://j.mp/S-2013-001

32- :cite:`TheAcademyofMotionPictureArtsandSciences2014t` : The Academy of

33 Motion Picture Arts and Sciences, Science and Technology Council, & Academy

34 Color Encoding System (ACES) Project Subcommittee. (2014). Specification

35 S-2014-003 - ACEScc, A Logarithmic Encoding of ACES Data for use within

36 Color Grading Systems (pp. 1-12). Retrieved December 19, 2014, from

37 http://j.mp/S-2014-003

38- :cite:`TheAcademyofMotionPictureArtsandSciences2016c` : The Academy of

39 Motion Picture Arts and Sciences, Science and Technology Council, & Academy

40 Color Encoding System (ACES) Project. (2016). Specification S-2016-001 -

41 ACEScct, A Quasi-Logarithmic Encoding of ACES Data for use within Color

42 Grading Systems. Retrieved October 10, 2016, from http://j.mp/S-2016-001

43- :cite:`TheAcademyofMotionPictureArtsandSciencese` : The Academy of Motion

44 Picture Arts and Sciences, Science and Technology Council, & Academy Color

45 Encoding System (ACES) Project Subcommittee. (n.d.). Academy Color Encoding

46 System. Retrieved February 24, 2014, from

47 http://www.oscars.org/science-technology/council/projects/aces.html

48"""

49

50 from __future__ import annotations

51

52 import typing

53

54 import numpy as np

55

56 if typing.TYPE_CHECKING:

57 from colour.hints import Literal, NDArrayInt

58

59 from colour.hints import ( # noqa: TC001

60 Annotated,

61 Domain1,

62 NDArrayFloat,

63 Range1,

64)

65 from colour.utilities import (

66 Structure,

67 as_float,

68 as_int,

69 from_range_1,

70 optional,

71 to_domain_1,

72)

73

74 __author__ = "Colour Developers"

75 __copyright__ = "Copyright 2013 Colour Developers"

76 __license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"

77 __maintainer__ = "Colour Developers"

78 __email__ = "colour-developers@colour-science.org"

79 __status__ = "Production"

80

81 __all__ = [

82 "CONSTANTS_ACES_PROXY_10",

83 "CONSTANTS_ACES_PROXY_12",

84 "CONSTANTS_ACES_PROXY",

85 "CONSTANTS_ACES_CCT",

86 "log_encoding_ACESproxy",

87 "log_decoding_ACESproxy",

88 "log_encoding_ACEScc",

89 "log_decoding_ACEScc",

90 "log_encoding_ACEScct",

91 "log_decoding_ACEScct",

92]

93

94 CONSTANTS_ACES_PROXY_10: Structure = Structure(

95 CV_min=64,

96 CV_max=940,

97 steps_per_stop=50,

98 mid_CV_offset=425,

99 mid_log_offset=2.5,

100)

101"""*ACESproxy* 10 bit constants."""

102

103 CONSTANTS_ACES_PROXY_12: Structure = Structure(

104 CV_min=256,

105 CV_max=3760,

106 steps_per_stop=200,

107 mid_CV_offset=1700,

108 mid_log_offset=2.5,

109)

110"""*ACESproxy* 12 bit constants."""

111

112 CONSTANTS_ACES_PROXY: dict = {

113 10: CONSTANTS_ACES_PROXY_10,

114 12: CONSTANTS_ACES_PROXY_12,

115}

116"""Aggregated *ACESproxy* constants."""

117

118 CONSTANTS_ACES_CCT: Structure = Structure(

119 X_BRK=0.0078125,

120 Y_BRK=0.155251141552511,

121 A=10.5402377416545,

122 B=0.0729055341958355,

123)

124"""*ACEScct* constants."""

125

126

127 def log_encoding_ACESproxy(

128 lin_AP1: Domain1,

129 bit_depth: Literal[10, 12] = 10,

130 out_int: bool = False,

131 constants: dict | None = None,

132) -> Annotated[NDArrayFloat | NDArrayInt, 1]:

133 """

134 Apply the *ACESproxy* log encoding opto-electronic transfer function (OETF).

135

136 Parameters

137 ----------

138 lin_AP1

139 Linear *AP1* colourspace value.

140 bit_depth

141 *ACESproxy* bit-depth.

142 out_int

143 Whether to return value as int code value or float equivalent of a

144 code value at a specified bit-depth.

145 constants

146 *ACESproxy* constants.

147

148 Returns

149 -------

150 :class:`numpy.ndarray`

151 *ACESproxy* non-linear encoded value.

152

153 Notes

154 -----

155 +----------------+-----------------------+---------------+

156 | **Domain** | **Scale - Reference** | **Scale - 1** |

157 +================+=======================+===============+

158 | ``lin_AP1`` | 1 | 1 |

159 +----------------+-----------------------+---------------+

160

161 +----------------+-----------------------+---------------+

162 | **Range** | **Scale - Reference** | **Scale - 1** |

163 +================+=======================+===============+

164 | ``ACESproxy`` | 1 | 1 |

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

166

167 - This definition has an output int switch, thus the domain-range

168 scale information is only specified for the floating point mode.

169

170 References

171 ----------

172 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,

173 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,

174 :cite:`TheAcademyofMotionPictureArtsandSciences2014s`,

175 :cite:`TheAcademyofMotionPictureArtsandSciencese`

176

177 Examples

178 --------

179 >>> log_encoding_ACESproxy(0.18) # doctest: +ELLIPSIS

180 0.4164222...

181 >>> log_encoding_ACESproxy(0.18, out_int=True)

182 426

183 """

184

185 lin_AP1 = to_domain_1(lin_AP1)

186 constants = optional(constants, CONSTANTS_ACES_PROXY)

187

188 CV_min = constants[bit_depth].CV_min

189 CV_max = constants[bit_depth].CV_max

190 mid_CV_offset = constants[bit_depth].mid_CV_offset

191 mid_log_offset = constants[bit_depth].mid_log_offset

192 steps_per_stop = constants[bit_depth].steps_per_stop

193

194 def float_2_cv(x: float) -> float:

195 """Convert specified numeric to code value."""

196

197 return np.maximum(CV_min, np.minimum(CV_max, np.round(x)))

198

199 ACESproxy = np.where(

200 lin_AP1 > 2**-9.72,

201 float_2_cv(

202 (np.log2(lin_AP1) + mid_log_offset) * steps_per_stop + mid_CV_offset

203 ),

204 np.resize(CV_min, lin_AP1.shape),

205 )

206

207 if out_int:

208 return as_int(np.round(ACESproxy))

209

210 return as_float(from_range_1(ACESproxy / (2**bit_depth - 1)))

211

212

213 def log_decoding_ACESproxy(

214 ACESproxy: Domain1,

215 bit_depth: Literal[10, 12] = 10,

216 in_int: bool = False,

217 constants: dict | None = None,

218) -> Range1:

219 """

220 Apply the *ACESproxy* log decoding inverse opto-electronic transfer function (OETF).

221

222 Parameters

223 ----------

224 ACESproxy

225 *ACESproxy* non-linear encoded value.

226 bit_depth

227 *ACESproxy* bit-depth.

228 in_int

229 Whether to treat the input value as integer code value or floating

230 point equivalent of a code value at specified bit-depth.

231 constants

232 *ACESproxy* constants.

233

234 Returns

235 -------

236 :class:`numpy.ndarray`

237 Linear *AP1* colourspace value.

238

239 Notes

240 -----

241 +----------------+-----------------------+---------------+

242 | **Domain** | **Scale - Reference** | **Scale - 1** |

243 +================+=======================+===============+

244 | ``ACESproxy`` | 1 | 1 |

245 +----------------+-----------------------+---------------+

246

247 +----------------+-----------------------+---------------+

248 | **Range** | **Scale - Reference** | **Scale - 1** |

249 +================+=======================+===============+

250 | ``lin_AP1`` | 1 | 1 |

251 +----------------+-----------------------+---------------+

252

253 - This definition has an input int switch, thus the domain-range

254 scale information is only specified for the floating point mode.

255

256 References

257 ----------

258 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,

259 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,

260 :cite:`TheAcademyofMotionPictureArtsandSciences2014s`,

261 :cite:`TheAcademyofMotionPictureArtsandSciencese`

262

263 Examples

264 --------

265 >>> log_decoding_ACESproxy(0.416422287390029) # doctest: +ELLIPSIS

266 0.1...

267 >>> log_decoding_ACESproxy(426, in_int=True) # doctest: +ELLIPSIS

268 0.1...

269 """

270

271 ACESproxy = to_domain_1(ACESproxy)

272 constants = optional(constants, CONSTANTS_ACES_PROXY)

273

274 mid_CV_offset = constants[bit_depth].mid_CV_offset

275 mid_log_offset = constants[bit_depth].mid_log_offset

276 steps_per_stop = constants[bit_depth].steps_per_stop

277

278 if not in_int:

279 ACESproxy = ACESproxy * (2**bit_depth - 1)

280

281 lin_AP1 = 2 ** ((ACESproxy - mid_CV_offset) / steps_per_stop - mid_log_offset)

282

283 return as_float(from_range_1(lin_AP1))

284

285

286 def log_encoding_ACEScc(lin_AP1: Domain1) -> Range1:

287 """

288 Apply the *ACEScc* log encoding opto-electronic transfer function (OETF).

289

290 Parameters

291 ----------

292 lin_AP1

293 Linear *AP1* colourspace value.

294

295 Returns

296 -------

297 :class:`numpy.ndarray`

298 *ACEScc* non-linear encoded value.

299

300 Notes

301 -----

302 +-------------+-----------------------+---------------+

303 | **Domain** | **Scale - Reference** | **Scale - 1** |

304 +=============+=======================+===============+

305 | ``lin_AP1`` | 1 | 1 |

306 +-------------+-----------------------+---------------+

307

308 +-------------+-----------------------+---------------+

309 | **Range** | **Scale - Reference** | **Scale - 1** |

310 +=============+=======================+===============+

311 | ``ACEScc`` | 1 | 1 |

312 +-------------+-----------------------+---------------+

313

314 References

315 ----------

316 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,

317 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,

318 :cite:`TheAcademyofMotionPictureArtsandSciences2014t`,

319 :cite:`TheAcademyofMotionPictureArtsandSciencese`

320

321 Examples

322 --------

323 >>> log_encoding_ACEScc(0.18) # doctest: +ELLIPSIS

324 0.4135884...

325 """

326

327 lin_AP1 = to_domain_1(lin_AP1)

328

329 ACEScc = np.where(

330 lin_AP1 < 0,

331 (np.log2(2**-16) + 9.72) / 17.52,

332 (np.log2(2**-16 + lin_AP1 * 0.5) + 9.72) / 17.52,

333 )

334 ACEScc = np.where(

335 lin_AP1 >= 2**-15,

336 (np.log2(lin_AP1) + 9.72) / 17.52,

337 ACEScc,

338 )

339

340 return as_float(from_range_1(ACEScc))

341

342

343 def log_decoding_ACEScc(ACEScc: Domain1) -> Range1:

344 """

345 Apply the *ACEScc* log decoding inverse opto-electronic transfer function (OETF).

346

347 Parameters

348 ----------

349 ACEScc

350 *ACEScc* non-linear encoded value.

351

352 Returns

353 -------

354 :class:`numpy.ndarray`

355 Linear *AP1* colourspace value.

356

357 Notes

358 -----

359 +-------------+-----------------------+---------------+

360 | **Domain** | **Scale - Reference** | **Scale - 1** |

361 +=============+=======================+===============+

362 | ``ACEScc`` | 1 | 1 |

363 +-------------+-----------------------+---------------+

364

365 +-------------+-----------------------+---------------+

366 | **Range** | **Scale - Reference** | **Scale - 1** |

367 +=============+=======================+===============+

368 | ``lin_AP1`` | 1 | 1 |

369 +-------------+-----------------------+---------------+

370

371 References

372 ----------

373 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,

374 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,

375 :cite:`TheAcademyofMotionPictureArtsandSciences2014t`,

376 :cite:`TheAcademyofMotionPictureArtsandSciencese`

377

378 Examples

379 --------

380 >>> log_decoding_ACEScc(0.413588402492442) # doctest: +ELLIPSIS

381 0.1799999...

382 """

383

384 ACEScc = to_domain_1(ACEScc)

385

386 lin_AP1 = np.where(

387 ACEScc < (9.72 - 15) / 17.52,

388 (2 ** (ACEScc * 17.52 - 9.72) - 2**-16) * 2,

389 2 ** (ACEScc * 17.52 - 9.72),

390 )

391 lin_AP1 = np.where(

392 ACEScc >= (np.log2(65504) + 9.72) / 17.52,

393 65504,

394 lin_AP1,

395 )

396

397 return as_float(from_range_1(lin_AP1))

398

399

400 def log_encoding_ACEScct(

401 lin_AP1: Domain1, constants: Structure | None = None

402) -> Range1:

403 """

404 Apply the *ACEScct* log encoding opto-electronic transfer function (OETF).

405

406 Parameters

407 ----------

408 lin_AP1

409 Linear *AP1* colourspace value.

410 constants

411 *ACEScct* constants.

412

413 Returns

414 -------

415 :class:`numpy.ndarray`

416 *ACEScct* non-linear encoded value.

417

418 Notes

419 -----

420 +-------------+-----------------------+---------------+

421 | **Domain** | **Scale - Reference** | **Scale - 1** |

422 +=============+=======================+===============+

423 | ``lin_AP1`` | 1 | 1 |

424 +-------------+-----------------------+---------------+

425

426 +-------------+-----------------------+---------------+

427 | **Range** | **Scale - Reference** | **Scale - 1** |

428 +=============+=======================+===============+

429 | ``ACEScct`` | 1 | 1 |

430 +-------------+-----------------------+---------------+

431

432 References

433 ----------

434 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,

435 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,

436 :cite:`TheAcademyofMotionPictureArtsandSciences2016c`,

437 :cite:`TheAcademyofMotionPictureArtsandSciencese`

438

439 Examples

440 --------

441 >>> log_encoding_ACEScct(0.18) # doctest: +ELLIPSIS

442 0.4135884...

443 """

444

445 lin_AP1 = to_domain_1(lin_AP1)

446 constants = optional(constants, CONSTANTS_ACES_CCT)

447

448 ACEScct = np.where(

449 lin_AP1 <= constants.X_BRK,

450 constants.A * lin_AP1 + constants.B,

451 (np.log2(lin_AP1) + 9.72) / 17.52,

452 )

453

454 return as_float(from_range_1(ACEScct))

455

456

457 def log_decoding_ACEScct(

458 ACEScct: Domain1, constants: Structure | None = None

459) -> Range1:

460 """

461 Apply the *ACEScct* log decoding inverse opto-electronic transfer function (OETF).

462

463 Parameters

464 ----------

465 ACEScct

466 *ACEScct* non-linear encoded value.

467 constants

468 *ACEScct* constants.

469

470 Returns

471 -------

472 :class:`numpy.ndarray`

473 Linear *AP1* colourspace value.

474

475 Notes

476 -----

477 +-------------+-----------------------+---------------+

478 | **Domain** | **Scale - Reference** | **Scale - 1** |

479 +=============+=======================+===============+

480 | ``ACEScct`` | 1 | 1 |

481 +-------------+-----------------------+---------------+

482

483 +-------------+-----------------------+---------------+

484 | **Range** | **Scale - Reference** | **Scale - 1** |

485 +=============+=======================+===============+

486 | ``lin_AP1`` | 1 | 1 |

487 +-------------+-----------------------+---------------+

488

489 References

490 ----------

491 :cite:`TheAcademyofMotionPictureArtsandSciences2014q`,

492 :cite:`TheAcademyofMotionPictureArtsandSciences2014r`,

493 :cite:`TheAcademyofMotionPictureArtsandSciences2016c`,

494 :cite:`TheAcademyofMotionPictureArtsandSciencese`

495

496 Examples

497 --------

498 >>> log_decoding_ACEScct(0.413588402492442) # doctest: +ELLIPSIS

499 0.1799999...

500 """

501

502 ACEScct = to_domain_1(ACEScct)

503 constants = optional(constants, CONSTANTS_ACES_CCT)

504

505 lin_AP1 = np.where(

506 ACEScct > constants.Y_BRK,

507 2 ** (ACEScct * 17.52 - 9.72),

508 (ACEScct - constants.B) / constants.A,

509 )

510

511 return as_float(from_range_1(lin_AP1))