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

57 statements  

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

1""" 

2ARRI Log Encodings 

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

4 

5Define the *ARRI LogC3* and *ARRI LogC4* log encodings. 

6 

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

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

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

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

11 

12References 

13---------- 

14- :cite:`ARRI2012a` : ARRI. (2012). ALEXA - Log C Curve - Usage in VFX. 

15 https://drive.google.com/open?id=1t73fAG_QpV7hJxoQPYZDWvOojYkYDgvn 

16- :cite:`Cooper2022` : Cooper, S., & Brendel, H. (2022). ARRI LogC4 

17 Logarithmic Color Space SPECIFICATION. Retrieved October 24, 2022, from 

18 https://www.arri.com/resource/blob/278790/bea879ac0d041a925bed27a096ab3ec2/\ 

192022-05-arri-logc4-specification-data.pdf 

20""" 

21 

22from __future__ import annotations 

23 

24import typing 

25 

26import numpy as np 

27 

28if typing.TYPE_CHECKING: 

29 from colour.hints import Literal 

30 

31from colour.hints import ( # noqa: TC001 

32 Domain1, 

33 Range1, 

34) 

35from colour.utilities import ( 

36 CanonicalMapping, 

37 Structure, 

38 as_float, 

39 from_range_1, 

40 optional, 

41 to_domain_1, 

42 validate_method, 

43) 

44 

45__author__ = "Colour Developers" 

46__copyright__ = "Copyright 2013 Colour Developers" 

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

48__maintainer__ = "Colour Developers" 

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

50__status__ = "Production" 

51 

52__all__ = [ 

53 "DATA_ALEXA_LOG_C_CURVE_BCL", 

54 "DATA_ALEXA_LOG_C_CURVE_CONVERSION", 

55 "log_encoding_ARRILogC3", 

56 "log_decoding_ARRILogC3", 

57 "CONSTANTS_ARRILOGC4", 

58 "log_encoding_ARRILogC4", 

59 "log_decoding_ARRILogC4", 

60] 

61 

62DATA_ALEXA_LOG_C_CURVE_BCL: CanonicalMapping = CanonicalMapping( 

63 { 

64 "SUP 3.x": { 

65 160: (0.0928, 0.8128), 

66 200: (0.0928, 0.8341), 

67 250: (0.0928, 0.8549), 

68 320: (0.0928, 0.8773), 

69 400: (0.0928, 0.8968), 

70 500: (0.0928, 0.9158), 

71 640: (0.0928, 0.9362), 

72 800: (0.0928, 0.9539), 

73 1000: (0.0928, 0.9711), 

74 1280: (0.0928, 0.9895), 

75 1600: (0.0928, 1.0000), 

76 2000: (0.0928, 1.0000), 

77 2560: (0.0928, 1.0000), 

78 3200: (0.0928, 1.0000), 

79 }, 

80 "SUP 2.x": { 

81 160: (0.1083, 0.8110), 

82 200: (0.1115, 0.8320), 

83 250: (0.1146, 0.8524), 

84 320: (0.1181, 0.8743), 

85 400: (0.1213, 0.8935), 

86 500: (0.1245, 0.9121), 

87 640: (0.1280, 0.9320), 

88 800: (0.1311, 0.9494), 

89 1000: (0.1343, 0.9662), 

90 1280: (0.1378, 0.9841), 

91 1600: (0.1409, 0.9997), 

92 }, 

93 } 

94) 

95"""*ARRI LogC3* curve *Ei, Black, Clipping Level* data.""" 

96 

97DATA_ALEXA_LOG_C_CURVE_CONVERSION: CanonicalMapping = CanonicalMapping( 

98 { 

99 "SUP 3.x": CanonicalMapping( 

100 { 

101 "Normalised Sensor Signal": { 

102 160: ( 

103 0.004680, 

104 40.0, 

105 -0.076072, 

106 0.269036, 

107 0.381991, 

108 42.062665, 

109 -0.071569, 

110 0.125266, 

111 ), 

112 200: ( 

113 0.004597, 

114 50.0, 

115 -0.118740, 

116 0.266007, 

117 0.382478, 

118 51.986387, 

119 -0.110339, 

120 0.128643, 

121 ), 

122 250: ( 

123 0.004518, 

124 62.5, 

125 -0.171260, 

126 0.262978, 

127 0.382966, 

128 64.243053, 

129 -0.158224, 

130 0.132021, 

131 ), 

132 320: ( 

133 0.004436, 

134 80.0, 

135 -0.243808, 

136 0.259627, 

137 0.383508, 

138 81.183335, 

139 -0.224409, 

140 0.135761, 

141 ), 

142 400: ( 

143 0.004369, 

144 100.0, 

145 -0.325820, 

146 0.256598, 

147 0.383999, 

148 100.295280, 

149 -0.299079, 

150 0.139142, 

151 ), 

152 500: ( 

153 0.004309, 

154 125.0, 

155 -0.427461, 

156 0.253569, 

157 0.384493, 

158 123.889239, 

159 -0.391261, 

160 0.142526, 

161 ), 

162 640: ( 

163 0.004249, 

164 160.0, 

165 -0.568709, 

166 0.250219, 

167 0.385040, 

168 156.482680, 

169 -0.518605, 

170 0.146271, 

171 ), 

172 800: ( 

173 0.004201, 

174 200.0, 

175 -0.729169, 

176 0.247190, 

177 0.385537, 

178 193.235573, 

179 -0.662201, 

180 0.149658, 

181 ), 

182 1000: ( 

183 0.004160, 

184 250.0, 

185 -0.928805, 

186 0.244161, 

187 0.386036, 

188 238.584745, 

189 -0.839385, 

190 0.153047, 

191 ), 

192 1280: ( 

193 0.004120, 

194 320.0, 

195 -1.207168, 

196 0.240810, 

197 0.386590, 

198 301.197380, 

199 -1.084020, 

200 0.156799, 

201 ), 

202 1600: ( 

203 0.004088, 

204 400.0, 

205 -1.524256, 

206 0.237781, 

207 0.387093, 

208 371.761171, 

209 -1.359723, 

210 0.160192, 

211 ), 

212 }, 

213 "Linear Scene Exposure Factor": { 

214 160: ( 

215 0.005561, 

216 5.555556, 

217 0.080216, 

218 0.269036, 

219 0.381991, 

220 5.842037, 

221 0.092778, 

222 0.125266, 

223 ), 

224 200: ( 

225 0.006208, 

226 5.555556, 

227 0.076621, 

228 0.266007, 

229 0.382478, 

230 5.776265, 

231 0.092782, 

232 0.128643, 

233 ), 

234 250: ( 

235 0.006871, 

236 5.555556, 

237 0.072941, 

238 0.262978, 

239 0.382966, 

240 5.710494, 

241 0.092786, 

242 0.132021, 

243 ), 

244 320: ( 

245 0.007622, 

246 5.555556, 

247 0.068768, 

248 0.259627, 

249 0.383508, 

250 5.637732, 

251 0.092791, 

252 0.135761, 

253 ), 

254 400: ( 

255 0.008318, 

256 5.555556, 

257 0.064901, 

258 0.256598, 

259 0.383999, 

260 5.571960, 

261 0.092795, 

262 0.139142, 

263 ), 

264 500: ( 

265 0.009031, 

266 5.555556, 

267 0.060939, 

268 0.253569, 

269 0.384493, 

270 5.506188, 

271 0.092800, 

272 0.142526, 

273 ), 

274 640: ( 

275 0.009840, 

276 5.555556, 

277 0.056443, 

278 0.250219, 

279 0.385040, 

280 5.433426, 

281 0.092805, 

282 0.146271, 

283 ), 

284 800: ( 

285 0.010591, 

286 5.555556, 

287 0.052272, 

288 0.247190, 

289 0.385537, 

290 5.367655, 

291 0.092809, 

292 0.149658, 

293 ), 

294 1000: ( 

295 0.011361, 

296 5.555556, 

297 0.047996, 

298 0.244161, 

299 0.386036, 

300 5.301883, 

301 0.092814, 

302 0.153047, 

303 ), 

304 1280: ( 

305 0.012235, 

306 5.555556, 

307 0.043137, 

308 0.240810, 

309 0.386590, 

310 5.229121, 

311 0.092819, 

312 0.156799, 

313 ), 

314 1600: ( 

315 0.013047, 

316 5.555556, 

317 0.038625, 

318 0.237781, 

319 0.387093, 

320 5.163350, 

321 0.092824, 

322 0.16019, 

323 ), 

324 }, 

325 } 

326 ), 

327 "SUP 2.x": CanonicalMapping( 

328 { 

329 "Normalised Sensor Signal": { 

330 160: ( 

331 0.003907, 

332 36.439829, 

333 -0.053366, 

334 0.269035, 

335 0.391007, 

336 45.593473, 

337 -0.069772, 

338 0.10836, 

339 ), 

340 200: ( 

341 0.003907, 

342 45.549786, 

343 -0.088959, 

344 0.266007, 

345 0.391007, 

346 55.709581, 

347 -0.106114, 

348 0.11154, 

349 ), 

350 250: ( 

351 0.003907, 

352 56.937232, 

353 -0.133449, 

354 0.262978, 

355 0.391007, 

356 67.887153, 

357 -0.150510, 

358 0.11472, 

359 ), 

360 320: ( 

361 0.003907, 

362 72.879657, 

363 -0.195737, 

364 0.259627, 

365 0.391007, 

366 84.167616, 

367 -0.210597, 

368 0.11824, 

369 ), 

370 400: ( 

371 0.003907, 

372 91.099572, 

373 -0.266922, 

374 0.256598, 

375 0.391007, 

376 101.811426, 

377 -0.276349, 

378 0.12142, 

379 ), 

380 500: ( 

381 0.003907, 

382 113.874465, 

383 -0.355903, 

384 0.253569, 

385 0.391007, 

386 122.608379, 

387 -0.354421, 

388 0.12461, 

389 ), 

390 640: ( 

391 0.003907, 

392 145.759315, 

393 -0.480477, 

394 0.250218, 

395 0.391007, 

396 149.703304, 

397 -0.456760, 

398 0.12813, 

399 ), 

400 800: ( 

401 0.003907, 

402 182.199144, 

403 -0.622848, 

404 0.247189, 

405 0.391007, 

406 178.216873, 

407 -0.564981, 

408 0.13131, 

409 ), 

410 1000: ( 

411 0.003907, 

412 227.748930, 

413 -0.800811, 

414 0.244161, 

415 0.391007, 

416 210.785040, 

417 -0.689043, 

418 0.13449, 

419 ), 

420 1280: ( 

421 0.003907, 

422 291.518630, 

423 -1.049959, 

424 0.240810, 

425 0.391007, 

426 251.689459, 

427 -0.845336, 

428 0.13801, 

429 ), 

430 1600: ( 

431 0.003907, 

432 364.398287, 

433 -1.334700, 

434 0.237781, 

435 0.391007, 

436 293.073575, 

437 -1.003841, 

438 0.14119, 

439 ), 

440 }, 

441 "Linear Scene Exposure Factor": { 

442 160: ( 

443 0.000000, 

444 5.061087, 

445 0.089004, 

446 0.269035, 

447 0.391007, 

448 6.332427, 

449 0.108361, 

450 0.108361, 

451 ), 

452 200: ( 

453 0.000000, 

454 5.061087, 

455 0.089004, 

456 0.266007, 

457 0.391007, 

458 6.189953, 

459 0.111543, 

460 0.111543, 

461 ), 

462 250: ( 

463 0.000000, 

464 5.061087, 

465 0.089004, 

466 0.262978, 

467 0.391007, 

468 6.034414, 

469 0.114725, 

470 0.114725, 

471 ), 

472 320: ( 

473 0.000000, 

474 5.061087, 

475 0.089004, 

476 0.259627, 

477 0.391007, 

478 5.844973, 

479 0.118246, 

480 0.118246, 

481 ), 

482 400: ( 

483 0.000000, 

484 5.061087, 

485 0.089004, 

486 0.256598, 

487 0.391007, 

488 5.656190, 

489 0.121428, 

490 0.121428, 

491 ), 

492 500: ( 

493 0.000000, 

494 5.061087, 

495 0.089004, 

496 0.253569, 

497 0.391007, 

498 5.449261, 

499 0.124610, 

500 0.124610, 

501 ), 

502 640: ( 

503 0.000000, 

504 5.061087, 

505 0.089004, 

506 0.250218, 

507 0.391007, 

508 5.198031, 

509 0.128130, 

510 0.128130, 

511 ), 

512 800: ( 

513 0.000000, 

514 5.061087, 

515 0.089004, 

516 0.247189, 

517 0.391007, 

518 4.950469, 

519 0.131313, 

520 0.131313, 

521 ), 

522 1000: ( 

523 0.000000, 

524 5.061087, 

525 0.089004, 

526 0.244161, 

527 0.391007, 

528 4.684112, 

529 0.134495, 

530 0.134495, 

531 ), 

532 1280: ( 

533 0.000000, 

534 5.061087, 

535 0.089004, 

536 0.240810, 

537 0.391007, 

538 4.369609, 

539 0.138015, 

540 0.138015, 

541 ), 

542 1600: ( 

543 0.000000, 

544 5.061087, 

545 0.089004, 

546 0.237781, 

547 0.391007, 

548 4.070466, 

549 0.141197, 

550 0.14119, 

551 ), 

552 }, 

553 } 

554 ), 

555 } 

556) 

557""" 

558*ARRI LogC3* curve conversion data between signal and linear scene 

559exposure factor for *SUP 3.x* and signal and normalised sensor signal for 

560*SUP 2.x*. 

561""" 

562 

563 

564def log_encoding_ARRILogC3( 

565 x: Domain1, 

566 firmware: Literal["SUP 2.x", "SUP 3.x"] | str = "SUP 3.x", 

567 method: ( 

568 Literal["Linear Scene Exposure Factor", "Normalised Sensor Signal"] | str 

569 ) = "Linear Scene Exposure Factor", 

570 EI: Literal[160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600] = 800, 

571) -> Range1: 

572 """ 

573 Apply the *ARRI LogC3* log encoding opto-electronic transfer function (OETF). 

574 

575 Parameters 

576 ---------- 

577 x 

578 Linear data :math:`x`. 

579 firmware 

580 Alexa firmware version. 

581 method 

582 Conversion method. 

583 EI 

584 Exposure Index :math:`EI`. 

585 

586 Returns 

587 ------- 

588 :class:`numpy.ndarray` 

589 *ARRI LogC3* non-linear encoded data :math:`t`. 

590 

591 References 

592 ---------- 

593 :cite:`ARRI2012a` 

594 

595 Notes 

596 ----- 

597 +------------+-----------------------+---------------+ 

598 | **Domain** | **Scale - Reference** | **Scale - 1** | 

599 +============+=======================+===============+ 

600 | ``x`` | 1 | 1 | 

601 +------------+-----------------------+---------------+ 

602 

603 +------------+-----------------------+---------------+ 

604 | **Range** | **Scale - Reference** | **Scale - 1** | 

605 +============+=======================+===============+ 

606 | ``t`` | 1 | 1 | 

607 +------------+-----------------------+---------------+ 

608 

609 Examples 

610 -------- 

611 >>> log_encoding_ARRILogC3(0.18) # doctest: +ELLIPSIS 

612 0.3910068... 

613 """ 

614 

615 x = to_domain_1(x) 

616 firmware = validate_method(firmware, ("SUP 3.x", "SUP 2.x")) 

617 method = validate_method( 

618 method, ("Linear Scene Exposure Factor", "Normalised Sensor Signal") 

619 ) 

620 

621 cut, a, b, c, d, e, f, _e_cut_f = DATA_ALEXA_LOG_C_CURVE_CONVERSION[firmware][ 

622 method 

623 ][EI] 

624 

625 t = np.where(x > cut, c * np.log10(a * x + b) + d, e * x + f) 

626 

627 return as_float(from_range_1(t)) 

628 

629 

630def log_decoding_ARRILogC3( 

631 t: Domain1, 

632 firmware: Literal["SUP 2.x", "SUP 3.x"] | str = "SUP 3.x", 

633 method: ( 

634 Literal["Linear Scene Exposure Factor", "Normalised Sensor Signal"] | str 

635 ) = "Linear Scene Exposure Factor", 

636 EI: Literal[160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600] = 800, 

637) -> Range1: 

638 """ 

639 Apply the *ARRI LogC3* log decoding inverse opto-electronic transfer 

640 function (OETF). 

641 

642 Parameters 

643 ---------- 

644 t 

645 *ARRI LogC3* non-linear encoded data :math:`t`. 

646 firmware 

647 Alexa firmware version. 

648 method 

649 Conversion method. 

650 EI 

651 Exposure Index :math:`EI`. 

652 

653 Returns 

654 ------- 

655 :class:`numpy.ndarray` 

656 Linear data :math:`x`. 

657 

658 Notes 

659 ----- 

660 +------------+-----------------------+---------------+ 

661 | **Domain** | **Scale - Reference** | **Scale - 1** | 

662 +============+=======================+===============+ 

663 | ``t`` | 1 | 1 | 

664 +------------+-----------------------+---------------+ 

665 

666 +------------+-----------------------+---------------+ 

667 | **Range** | **Scale - Reference** | **Scale - 1** | 

668 +============+=======================+===============+ 

669 | ``x`` | 1 | 1 | 

670 +------------+-----------------------+---------------+ 

671 

672 References 

673 ---------- 

674 :cite:`ARRI2012a` 

675 

676 Examples 

677 -------- 

678 >>> log_decoding_ARRILogC3(0.391006832034084) # doctest: +ELLIPSIS 

679 0.18... 

680 """ 

681 

682 t = to_domain_1(t) 

683 method = validate_method( 

684 method, ("Linear Scene Exposure Factor", "Normalised Sensor Signal") 

685 ) 

686 

687 cut, a, b, c, d, e, f, _e_cut_f = DATA_ALEXA_LOG_C_CURVE_CONVERSION[firmware][ 

688 method 

689 ][EI] 

690 

691 x = np.where(t > e * cut + f, (10 ** ((t - d) / c) - b) / a, (t - f) / e) 

692 

693 return as_float(from_range_1(x)) 

694 

695 

696CONSTANTS_ARRILOGC4: Structure = Structure( 

697 a=(2**18 - 16) / 117.45, 

698 b=(1023 - 95) / 1023, 

699 c=95 / 1023, 

700) 

701"""*ARRI LogC4* constants.""" 

702 

703_a = CONSTANTS_ARRILOGC4.a 

704_b = CONSTANTS_ARRILOGC4.b 

705_c = CONSTANTS_ARRILOGC4.c 

706 

707CONSTANTS_ARRILOGC4.s = (7 * np.log(2) * 2 ** (7 - 14 * _c / _b)) / (_a * _b) 

708CONSTANTS_ARRILOGC4.t = (2 ** (14 * (-_c / _b) + 6) - 64) / _a 

709 

710del _a, _b, _c 

711 

712 

713def log_encoding_ARRILogC4( 

714 E_scene: Domain1, 

715 constants: Structure | None = None, 

716) -> Range1: 

717 """ 

718 Apply the *ARRI LogC4* log encoding opto-electronic transfer function (OETF). 

719 

720 Parameters 

721 ---------- 

722 E_scene 

723 Relative scene linear signal :math:`E_{scene}`. 

724 constants 

725 *ARRI LogC4* constants. 

726 

727 Returns 

728 ------- 

729 :class:`numpy.ndarray` 

730 *ARRI LogC4* non-linear encoded signal :math:`E'`. 

731 

732 References 

733 ---------- 

734 :cite:`Cooper2022` 

735 

736 Notes 

737 ----- 

738 +-------------+-----------------------+---------------+ 

739 | **Domain** | **Scale - Reference** | **Scale - 1** | 

740 +=============+=======================+===============+ 

741 | ``E_scene`` | 1 | 1 | 

742 +-------------+-----------------------+---------------+ 

743 

744 +------------+-----------------------+---------------+ 

745 | **Range** | **Scale - Reference** | **Scale - 1** | 

746 +============+=======================+===============+ 

747 | ``E_p`` | 1 | 1 | 

748 +------------+-----------------------+---------------+ 

749 

750 Examples 

751 -------- 

752 >>> log_encoding_ARRILogC4(0.18) # doctest: +ELLIPSIS 

753 0.2783958... 

754 """ 

755 

756 E_scene = to_domain_1(E_scene) 

757 constants = optional(constants, CONSTANTS_ARRILOGC4) 

758 

759 a = constants.a 

760 b = constants.b 

761 c = constants.c 

762 s = constants.s 

763 t = constants.t 

764 

765 E_p = np.where( 

766 E_scene >= t, 

767 (np.log2(a * E_scene + 64) - 6) / 14 * b + c, 

768 (E_scene - t) / s, 

769 ) 

770 

771 return as_float(from_range_1(E_p)) 

772 

773 

774def log_decoding_ARRILogC4( 

775 E_p: Domain1, 

776 constants: Structure | None = None, 

777) -> Range1: 

778 """ 

779 Apply the *ARRI LogC4* log decoding inverse opto-electronic transfer 

780 function (OETF). 

781 

782 Parameters 

783 ---------- 

784 E_p 

785 *ARRI LogC4* non-linear encoded signal :math:`E'`. 

786 constants 

787 *ARRI LogC4* constants. 

788 

789 Returns 

790 ------- 

791 :class:`numpy.ndarray` 

792 Relative scene linear signal :math:`E_{scene}`. 

793 

794 Notes 

795 ----- 

796 +------------+-----------------------+---------------+ 

797 | **Domain** | **Scale - Reference** | **Scale - 1** | 

798 +============+=======================+===============+ 

799 | ``E_p`` | 1 | 1 | 

800 +------------+-----------------------+---------------+ 

801 

802 +-------------+-----------------------+---------------+ 

803 | **Range** | **Scale - Reference** | **Scale - 1** | 

804 +=============+=======================+===============+ 

805 | ``E_scene`` | 1 | 1 | 

806 +-------------+-----------------------+---------------+ 

807 

808 References 

809 ---------- 

810 :cite:`Cooper2022` 

811 

812 Examples 

813 -------- 

814 >>> log_decoding_ARRILogC4(0.27839583654826527) # doctest: +ELLIPSIS 

815 0.18... 

816 """ 

817 

818 E_p = to_domain_1(E_p) 

819 constants = optional(constants, CONSTANTS_ARRILOGC4) 

820 

821 a = constants.a 

822 b = constants.b 

823 c = constants.c 

824 s = constants.s 

825 t = constants.t 

826 

827 E_scene = np.where( 

828 E_p >= 0, 

829 (2 ** (14 * ((E_p - c) / b) + 6) - 64) / a, 

830 E_p * s + t, 

831 ) 

832 

833 return as_float(from_range_1(E_scene))