Coverage for colour/io/luts/tests/test_lut.py: 99%

479 statements  

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

1"""Define the unit tests for the :mod:`colour.io.luts.lut` module.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import textwrap 

7import typing 

8 

9import numpy as np 

10import pytest 

11 

12from colour.algebra import ( 

13 CubicSplineInterpolator, 

14 LinearInterpolator, 

15 random_triplet_generator, 

16 spow, 

17 table_interpolation_tetrahedral, 

18 table_interpolation_trilinear, 

19) 

20from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

21 

22if typing.TYPE_CHECKING: 

23 from colour.hints import NDArrayFloat 

24 

25from colour.io.luts import LUT1D, LUT3D, LUT3x1D, LUT_to_LUT 

26from colour.io.luts.lut import AbstractLUT 

27from colour.utilities import as_float_array, is_scipy_installed, tsplit, tstack 

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

38 "RANDOM_TRIPLETS", 

39 "AbstractTestLUT", 

40 "TestLUT1D", 

41 "TestLUT3x1D", 

42 "TestLUT3D", 

43] 

44 

45ROOT_RESOURCES: str = os.path.join(os.path.dirname(__file__), "resources") 

46 

47RANDOM_TRIPLETS: NDArrayFloat = np.reshape( 

48 random_triplet_generator(8, random_state=np.random.RandomState(4)), 

49 (4, 2, 3), 

50) 

51 

52 

53class AbstractTestLUT: 

54 """Define :class:`colour.io.luts.lut.AbstractLUT` class unit tests methods.""" 

55 

56 def test_required_attributes(self) -> None: 

57 """Test the presence of required attributes.""" 

58 

59 required_attributes = ( 

60 "table", 

61 "name", 

62 "dimensions", 

63 "domain", 

64 "size", 

65 "comments", 

66 ) 

67 

68 for attribute in required_attributes: 

69 assert attribute in dir(AbstractLUT) 

70 

71 def test_required_methods(self) -> None: 

72 """Test the presence of required methods.""" 

73 

74 required_methods = ( 

75 "__init__", 

76 "__str__", 

77 "__repr__", 

78 "__eq__", 

79 "__ne__", 

80 "__add__", 

81 "__iadd__", 

82 "__sub__", 

83 "__isub__", 

84 "__mul__", 

85 "__imul__", 

86 "__div__", 

87 "__idiv__", 

88 "__pow__", 

89 "__ipow__", 

90 "arithmetical_operation", 

91 "is_domain_explicit", 

92 "linear_table", 

93 "copy", 

94 "invert", 

95 "apply", 

96 "convert", 

97 ) 

98 

99 for method in required_methods: 

100 assert method in dir(AbstractLUT) 

101 

102 

103class TestLUT1D: 

104 """Define :class:`colour.io.luts.lut.LUT1D` class unit tests methods.""" 

105 

106 def setup_method(self) -> None: 

107 """Initialise the common tests attributes.""" 

108 

109 self._size = 10 

110 self._dimensions = 1 

111 self._domain_1 = np.array([0, 1]) 

112 self._domain_2 = np.array([-0.1, 1.5]) 

113 self._domain_3 = np.linspace(-0.1, 1.5, 10) 

114 self._domain_4 = np.linspace(0, 1, 10) 

115 self._table_1 = self._domain_4 

116 self._table_2 = self._table_1 ** (1 / 2.2) 

117 self._table_3 = as_float_array( 

118 spow(np.linspace(-0.1, 1.5, self._size), (1 / 2.6)) 

119 ) 

120 self._table_1_kwargs = {"size": self._size, "domain": self._domain_1} 

121 self._table_2_kwargs = {"size": self._size, "domain": self._domain_2} 

122 self._table_3_kwargs = {"size": self._size, "domain": self._domain_3} 

123 self._interpolator_1 = LinearInterpolator 

124 self._interpolator_kwargs_1 = {} 

125 self._interpolator_2 = CubicSplineInterpolator 

126 self._interpolator_kwargs_2 = {} 

127 self._invert_kwargs_1 = {} 

128 self._invert_kwargs_2 = {} 

129 self._str = textwrap.dedent( 

130 """ 

131 LUT1D - Nemo 

132 ------------ 

133 

134 Dimensions : 1 

135 Domain : [ 0. 1.] 

136 Size : (10,) 

137 """ 

138 ).strip() 

139 self._repr = textwrap.dedent( 

140 """ 

141 LUT1D([ 0. , 0.11111111, 0.22222222, 0.33333333, 0.44444444, 

142 0.55555556, 0.66666667, 0.77777778, 0.88888889, 1. ], 

143 'Nemo', 

144 [ 0., 1.], 

145 10, 

146 ['A first comment.', 'A second comment.']) 

147 """ 

148 ).strip() 

149 self._inverted_apply_1 = np.array( 

150 [ 

151 [ 

152 [0.92972640, 0.07631226, 0.00271066], 

153 [0.26841861, 0.16523270, 0.12595735], 

154 ], 

155 [ 

156 [0.94177862, 0.57881126, 0.01332090], 

157 [0.47923027, 0.05963181, 0.90760882], 

158 ], 

159 [ 

160 [0.45351816, 0.72429553, 0.16633644], 

161 [0.06518351, 0.96461970, 0.89124869], 

162 ], 

163 [ 

164 [0.94943065, 0.04942310, 0.59044056], 

165 [0.00187936, 0.32291386, 0.73036245], 

166 ], 

167 ] 

168 ) 

169 self._inverted_apply_2 = self._inverted_apply_1 

170 self._applied_1 = np.array( 

171 [ 

172 [ 

173 [0.98453144, 0.53304051, 0.02978976], 

174 [0.76000720, 0.68433298, 0.64753760], 

175 ], 

176 [ 

177 [0.98718436, 0.89285575, 0.14639477], 

178 [0.85784314, 0.47463489, 0.97966294], 

179 ], 

180 [ 

181 [0.84855994, 0.93486051, 0.68536703], 

182 [0.49723089, 0.99221212, 0.97606176], 

183 ], 

184 [ 

185 [0.98886872, 0.43308440, 0.89633381], 

186 [0.02065388, 0.79040970, 0.93651642], 

187 ], 

188 ] 

189 ) 

190 self._applied_2 = np.array( 

191 [ 

192 [ 

193 [0.98486877, 0.53461565, 0.05614915], 

194 [0.75787807, 0.68473291, 0.64540281], 

195 ], 

196 [ 

197 [0.98736681, 0.89255862, 0.18759013], 

198 [0.85682563, 0.46473837, 0.97981413], 

199 ], 

200 [ 

201 [0.84736915, 0.93403795, 0.68561444], 

202 [0.48799540, 0.99210103, 0.97606266], 

203 ], 

204 [ 

205 [0.98895283, 0.42197234, 0.89639002], 

206 [0.04585089, 0.79047033, 0.93564890], 

207 ], 

208 ] 

209 ) 

210 self._applied_3 = np.array( 

211 [ 

212 [ 

213 [0.98718085, 0.58856660, 0.06995805], 

214 [0.79062078, 0.72580416, 0.68991332], 

215 ], 

216 [ 

217 [0.98928698, 0.90826591, 0.22565356], 

218 [0.87725399, 0.52099138, 0.98286533], 

219 ], 

220 [ 

221 [0.86904691, 0.94376678, 0.72658532], 

222 [0.54348223, 0.99327846, 0.97966110], 

223 ], 

224 [ 

225 [0.99062417, 0.47963425, 0.91159110], 

226 [0.05775947, 0.81950198, 0.94514273], 

227 ], 

228 ] 

229 ) 

230 self._applied_4 = self._inverted_apply_1 

231 

232 def test_required_methods(self) -> None: 

233 """Test the presence of required methods.""" 

234 

235 required_methods = ( 

236 "__init__", 

237 "is_domain_explicit", 

238 "linear_table", 

239 "invert", 

240 "apply", 

241 "convert", 

242 ) 

243 

244 for method in required_methods: 

245 assert method in dir(LUT1D) 

246 

247 def test__init__(self) -> None: 

248 """ 

249 Test :class:`colour.io.luts.lut.LUT1D.__init__` method. 

250 """ 

251 

252 LUT = LUT1D(self._table_1) 

253 

254 np.testing.assert_allclose( 

255 LUT.table, self._table_1, atol=TOLERANCE_ABSOLUTE_TESTS 

256 ) 

257 

258 assert str(id(LUT)) == LUT.name 

259 

260 np.testing.assert_array_equal(LUT.domain, self._domain_1) 

261 

262 assert LUT.dimensions == self._dimensions 

263 

264 assert isinstance(LUT1D(self._table_3, domain=self._domain_3), LUT1D) 

265 

266 def test_table(self) -> None: 

267 """ 

268 Test :class:`colour.io.luts.lut.LUT1D.table` property. 

269 """ 

270 

271 LUT = LUT1D() 

272 

273 np.testing.assert_array_equal(LUT.table, LUT.linear_table(self._size)) 

274 

275 table_1 = self._table_1 * 0.8 + 0.1 

276 LUT.table = table_1 

277 np.testing.assert_allclose(LUT.table, table_1, atol=TOLERANCE_ABSOLUTE_TESTS) 

278 

279 def test_name(self) -> None: 

280 """ 

281 Test :class:`colour.io.luts.lut.LUT1D.name` property. 

282 """ 

283 

284 LUT = LUT1D(self._table_1) 

285 

286 assert LUT.name == str(id(LUT)) 

287 

288 LUT = LUT1D() 

289 

290 assert LUT.name == f"Unity {self._table_1.shape[0]}" 

291 

292 def test_domain(self) -> None: 

293 """ 

294 Test :class:`colour.io.luts.lut.LUT1D.domain` property. 

295 """ 

296 

297 LUT = LUT1D() 

298 

299 np.testing.assert_array_equal(LUT.domain, self._domain_1) 

300 

301 domain = self._domain_1 * 0.8 + 0.1 

302 LUT.domain = domain 

303 np.testing.assert_array_equal(LUT.domain, domain) 

304 

305 def test_size(self) -> None: 

306 """ 

307 Test :class:`colour.io.luts.lut.LUT1D.size` property. 

308 """ 

309 

310 LUT = LUT1D() 

311 

312 assert LUT.size == LUT.table.shape[0] 

313 

314 def test_dimensions(self) -> None: 

315 """ 

316 Test :class:`colour.io.luts.lut.LUT1D.dimensions` property. 

317 """ 

318 

319 LUT = LUT1D() 

320 

321 assert LUT.dimensions == self._dimensions 

322 

323 def test_comments(self) -> None: 

324 """ 

325 Test :class:`colour.io.luts.lut.LUT1D.comments` property. 

326 """ 

327 

328 LUT = LUT1D() 

329 assert LUT.comments == [] 

330 

331 comments = ["A first comment.", "A second comment."] 

332 LUT = LUT1D(comments=comments) 

333 

334 assert LUT.comments == comments 

335 

336 def test__str__(self) -> None: 

337 """ 

338 Test :class:`colour.io.luts.lut.LUT1D.__str__` property. 

339 """ 

340 

341 LUT = LUT1D(name="Nemo") 

342 

343 assert str(LUT) == self._str 

344 

345 def test__repr__(self) -> None: 

346 """ 

347 Test :class:`colour.io.luts.lut.LUT1D.__repr__` method. 

348 """ 

349 

350 LUT = LUT1D(name="Nemo", comments=["A first comment.", "A second comment."]) 

351 

352 # The default LUT representation is too large to be embedded, given 

353 # that :class:`colour.io.luts.lut.LUT3D.__str__` method is defined by 

354 # :class:`colour.io.luts.lut.AbstractLUT.__str__` method, the two other 

355 # tests should reasonably cover this case. 

356 if self._dimensions == 3: # pragma: no cover 

357 return 

358 

359 assert repr(LUT) == self._repr # pragma: no cover 

360 

361 def test__eq__(self) -> None: 

362 """ 

363 Test :class:`colour.io.luts.lut.LUT1D.__eq__` method. 

364 """ 

365 

366 LUT_1 = LUT1D() 

367 LUT_2 = LUT1D() 

368 

369 assert LUT_1 == LUT_2 

370 

371 def test__ne__(self) -> None: 

372 """ 

373 Test :class:`colour.io.luts.lut.LUT1D.__ne__` method. 

374 """ 

375 

376 LUT_1 = LUT1D() 

377 LUT_2 = LUT1D() 

378 

379 LUT_2 += 0.1 

380 assert LUT_1 != LUT_2 

381 

382 LUT_2 = LUT1D() 

383 LUT_2.domain = self._domain_1 * 0.8 + 0.1 

384 assert LUT_1 != LUT_2 

385 

386 def test_is_domain_explicit(self) -> None: 

387 """ 

388 Test :class:`colour.io.luts.lut.LUT1D.is_domain_explicit` method. 

389 """ 

390 

391 assert not LUT1D().is_domain_explicit() 

392 

393 assert LUT1D(self._table_3, domain=self._domain_3).is_domain_explicit() 

394 

395 def test_arithmetical_operation(self) -> None: 

396 """ 

397 Test :class:`colour.io.luts.lut.LUT1D.arithmetical_operation` method. 

398 """ 

399 

400 LUT_1 = LUT1D() 

401 LUT_2 = LUT1D() 

402 

403 np.testing.assert_allclose( 

404 LUT_1.arithmetical_operation(10, "+", False).table, 

405 self._table_1 + 10, 

406 atol=TOLERANCE_ABSOLUTE_TESTS, 

407 ) 

408 

409 np.testing.assert_allclose( 

410 LUT_1.arithmetical_operation(10, "-", False).table, 

411 self._table_1 - 10, 

412 atol=TOLERANCE_ABSOLUTE_TESTS, 

413 ) 

414 

415 np.testing.assert_allclose( 

416 LUT_1.arithmetical_operation(10, "*", False).table, 

417 self._table_1 * 10, 

418 atol=TOLERANCE_ABSOLUTE_TESTS, 

419 ) 

420 

421 np.testing.assert_allclose( 

422 LUT_1.arithmetical_operation(10, "/", False).table, 

423 self._table_1 / 10, 

424 atol=TOLERANCE_ABSOLUTE_TESTS, 

425 ) 

426 

427 np.testing.assert_allclose( 

428 LUT_1.arithmetical_operation(10, "**", False).table, 

429 self._table_1**10, 

430 atol=TOLERANCE_ABSOLUTE_TESTS, 

431 ) 

432 

433 np.testing.assert_allclose( 

434 (LUT_1 + 10).table, 

435 self._table_1 + 10, 

436 atol=TOLERANCE_ABSOLUTE_TESTS, 

437 ) 

438 

439 np.testing.assert_allclose( 

440 (LUT_1 - 10).table, 

441 self._table_1 - 10, 

442 atol=TOLERANCE_ABSOLUTE_TESTS, 

443 ) 

444 

445 np.testing.assert_allclose( 

446 (LUT_1 * 10).table, 

447 self._table_1 * 10, 

448 atol=TOLERANCE_ABSOLUTE_TESTS, 

449 ) 

450 

451 np.testing.assert_allclose( 

452 (LUT_1 / 10).table, 

453 self._table_1 / 10, 

454 atol=TOLERANCE_ABSOLUTE_TESTS, 

455 ) 

456 

457 np.testing.assert_allclose( 

458 (LUT_1**10).table, 

459 self._table_1**10, 

460 atol=TOLERANCE_ABSOLUTE_TESTS, 

461 ) 

462 

463 np.testing.assert_allclose( 

464 LUT_2.arithmetical_operation(10, "+", True).table, 

465 self._table_1 + 10, 

466 atol=TOLERANCE_ABSOLUTE_TESTS, 

467 ) 

468 

469 np.testing.assert_allclose( 

470 LUT_2.arithmetical_operation(10, "-", True).table, 

471 self._table_1, 

472 atol=TOLERANCE_ABSOLUTE_TESTS, 

473 ) 

474 

475 np.testing.assert_allclose( 

476 LUT_2.arithmetical_operation(10, "*", True).table, 

477 self._table_1 * 10, 

478 atol=TOLERANCE_ABSOLUTE_TESTS, 

479 ) 

480 

481 np.testing.assert_allclose( 

482 LUT_2.arithmetical_operation(10, "/", True).table, 

483 self._table_1, 

484 atol=TOLERANCE_ABSOLUTE_TESTS, 

485 ) 

486 

487 np.testing.assert_allclose( 

488 LUT_2.arithmetical_operation(10, "**", True).table, 

489 self._table_1**10, 

490 atol=TOLERANCE_ABSOLUTE_TESTS, 

491 ) 

492 

493 LUT_2 = LUT1D() 

494 

495 np.testing.assert_allclose( 

496 LUT_2.arithmetical_operation(self._table_1, "+", False).table, 

497 LUT_2.table + self._table_1, 

498 atol=TOLERANCE_ABSOLUTE_TESTS, 

499 ) 

500 

501 np.testing.assert_allclose( 

502 LUT_2.arithmetical_operation(LUT_2, "+", False).table, 

503 LUT_2.table + LUT_2.table, 

504 atol=TOLERANCE_ABSOLUTE_TESTS, 

505 ) 

506 

507 def test_linear_table(self) -> None: 

508 """ 

509 Test :class:`colour.io.luts.lut.LUT1D.linear_table` method. 

510 """ 

511 

512 LUT_1 = LUT1D() 

513 

514 np.testing.assert_allclose( 

515 LUT_1.linear_table(self._size), 

516 self._table_1, 

517 atol=TOLERANCE_ABSOLUTE_TESTS, 

518 ) 

519 

520 np.testing.assert_allclose( 

521 spow(LUT1D.linear_table(**self._table_3_kwargs), 1 / 2.6), 

522 self._table_3, 

523 atol=TOLERANCE_ABSOLUTE_TESTS, 

524 ) 

525 

526 def test_copy(self) -> None: 

527 """ 

528 Test :class:`colour.io.luts.lut.LUT1D.copy` method. 

529 """ 

530 

531 LUT_1 = LUT1D() 

532 

533 assert LUT_1 is not LUT_1.copy() 

534 assert LUT_1.copy() == LUT_1 

535 

536 def test_invert(self) -> None: 

537 """ 

538 Test :class:`colour.io.luts.lut.LUT1D.invert` method. 

539 """ 

540 

541 LUT_i = LUT1D(self._table_2).invert( 

542 interpolator=self._interpolator_1, **self._invert_kwargs_1 

543 ) 

544 

545 np.testing.assert_allclose( 

546 LUT_i.apply(RANDOM_TRIPLETS), 

547 self._inverted_apply_1, 

548 atol=TOLERANCE_ABSOLUTE_TESTS, 

549 ) 

550 

551 LUT_i = LUT1D(self._table_2).invert( 

552 interpolator=self._interpolator_2, **self._invert_kwargs_2 

553 ) 

554 

555 np.testing.assert_allclose( 

556 LUT_i.apply(RANDOM_TRIPLETS), 

557 self._inverted_apply_2, 

558 atol=TOLERANCE_ABSOLUTE_TESTS, 

559 ) 

560 

561 LUT_i = LUT1D(self._table_2, domain=self._domain_4) 

562 

563 try: 

564 LUT_i = LUT_i.invert( 

565 interpolator=self._interpolator_2, **self._invert_kwargs_2 

566 ) 

567 

568 np.testing.assert_allclose( # pragma: no cover 

569 LUT_i.apply(RANDOM_TRIPLETS), 

570 self._inverted_apply_2, 

571 atol=TOLERANCE_ABSOLUTE_TESTS, 

572 ) 

573 except NotImplementedError: # pragma: no cover 

574 pass 

575 

576 def test_apply(self) -> None: 

577 """ 

578 Test :class:`colour.io.luts.lut.LUT1D.apply` method. 

579 """ 

580 

581 if not is_scipy_installed(): # pragma: no cover 

582 return 

583 

584 LUT_1 = LUT1D(self._table_2) 

585 

586 np.testing.assert_allclose( 

587 LUT_1.apply(RANDOM_TRIPLETS), 

588 self._applied_1, 

589 atol=TOLERANCE_ABSOLUTE_TESTS, 

590 ) 

591 

592 LUT_2 = LUT1D(domain=self._domain_2) 

593 LUT_2.table = spow(LUT_2.table, 1 / 2.2) 

594 

595 np.testing.assert_allclose( 

596 LUT_2.apply(RANDOM_TRIPLETS), 

597 self._applied_2, 

598 atol=TOLERANCE_ABSOLUTE_TESTS, 

599 ) 

600 

601 LUT_3 = LUT1D(self._table_3, domain=self._domain_3) 

602 

603 np.testing.assert_allclose( 

604 LUT_3.apply(RANDOM_TRIPLETS), 

605 self._applied_3, 

606 atol=TOLERANCE_ABSOLUTE_TESTS, 

607 ) 

608 

609 LUT_4 = LUT1D(self._table_2) 

610 

611 np.testing.assert_allclose( 

612 LUT_4.apply( 

613 RANDOM_TRIPLETS, 

614 direction="Inverse", 

615 interpolator=self._interpolator_1, 

616 interpolator_kwargs=self._interpolator_kwargs_1, 

617 **self._invert_kwargs_1, 

618 ), 

619 self._applied_4, 

620 atol=TOLERANCE_ABSOLUTE_TESTS, 

621 ) 

622 

623 

624class TestLUT3x1D: 

625 """Define :class:`colour.io.luts.lut.LUT3x1D` class unit tests methods.""" 

626 

627 def setup_method(self) -> None: 

628 """Initialise the common tests attributes.""" 

629 

630 self._size = 10 

631 self._dimensions = 2 

632 samples_1 = np.linspace(0, 1, 10) 

633 samples_2 = np.linspace(-0.1, 1.5, 15) 

634 samples_3 = np.linspace(-0.1, 3.0, 20) 

635 self._domain_1 = np.array([[0, 0, 0], [1, 1, 1]]) 

636 self._domain_2 = np.array([[0.0, -0.1, -0.2], [1.0, 1.5, 3.0]]) 

637 self._domain_3 = tstack( 

638 [ 

639 np.hstack([samples_1, np.full(10, np.nan)]), 

640 np.hstack([samples_2, np.full(5, np.nan)]), 

641 samples_3, 

642 ] 

643 ) 

644 self._domain_4 = tstack([samples_1, samples_1, samples_1]) 

645 self._table_1 = self._domain_4 

646 self._table_2 = self._table_1 ** (1 / 2.2) 

647 self._table_3 = as_float_array( 

648 spow( 

649 tstack( 

650 [ 

651 np.hstack([samples_1, np.full(10, np.nan)]), 

652 np.hstack([samples_2, np.full(5, np.nan)]), 

653 samples_3, 

654 ] 

655 ), 

656 1 / 2.6, 

657 ) 

658 ) 

659 self._table_1_kwargs = {"size": self._size, "domain": self._domain_1} 

660 self._table_2_kwargs = {"size": self._size, "domain": self._domain_2} 

661 self._table_3_kwargs = { 

662 "size": np.array([10, 15, 20]), 

663 "domain": self._domain_3, 

664 } 

665 self._interpolator_1 = LinearInterpolator 

666 self._interpolator_kwargs_1 = {} 

667 self._interpolator_2 = CubicSplineInterpolator 

668 self._interpolator_kwargs_2 = {} 

669 self._invert_kwargs_1 = {} 

670 self._invert_kwargs_2 = {} 

671 self._str = textwrap.dedent( 

672 """ 

673 LUT3x1D - Nemo 

674 -------------- 

675 

676 Dimensions : 2 

677 Domain : [[ 0. 0. 0.] 

678 [ 1. 1. 1.]] 

679 Size : (10, 3) 

680 """ 

681 ).strip() 

682 self._repr = textwrap.dedent( 

683 """ 

684 LUT3x1D([[ 0. , 0. , 0. ], 

685 [ 0.11111111, 0.11111111, 0.11111111], 

686 [ 0.22222222, 0.22222222, 0.22222222], 

687 [ 0.33333333, 0.33333333, 0.33333333], 

688 [ 0.44444444, 0.44444444, 0.44444444], 

689 [ 0.55555556, 0.55555556, 0.55555556], 

690 [ 0.66666667, 0.66666667, 0.66666667], 

691 [ 0.77777778, 0.77777778, 0.77777778], 

692 [ 0.88888889, 0.88888889, 0.88888889], 

693 [ 1. , 1. , 1. ]], 

694 'Nemo', 

695 [[ 0., 0., 0.], 

696 [ 1., 1., 1.]], 

697 10, 

698 ['A first comment.', 'A second comment.']) 

699 """ 

700 ).strip() 

701 self._inverted_apply_1 = np.array( 

702 [ 

703 [ 

704 [0.92972640, 0.07631226, 0.00271066], 

705 [0.26841861, 0.16523270, 0.12595735], 

706 ], 

707 [ 

708 [0.94177862, 0.57881126, 0.01332090], 

709 [0.47923027, 0.05963181, 0.90760882], 

710 ], 

711 [ 

712 [0.45351816, 0.72429553, 0.16633644], 

713 [0.06518351, 0.96461970, 0.89124869], 

714 ], 

715 [ 

716 [0.94943065, 0.04942310, 0.59044056], 

717 [0.00187936, 0.32291386, 0.73036245], 

718 ], 

719 ] 

720 ) 

721 self._inverted_apply_2 = self._inverted_apply_1 

722 self._applied_1 = np.array( 

723 [ 

724 [ 

725 [0.98453144, 0.53304051, 0.02978976], 

726 [0.76000720, 0.68433298, 0.64753760], 

727 ], 

728 [ 

729 [0.98718436, 0.89285575, 0.14639477], 

730 [0.85784314, 0.47463489, 0.97966294], 

731 ], 

732 [ 

733 [0.84855994, 0.93486051, 0.68536703], 

734 [0.49723089, 0.99221212, 0.97606176], 

735 ], 

736 [ 

737 [0.98886872, 0.43308440, 0.89633381], 

738 [0.02065388, 0.79040970, 0.93651642], 

739 ], 

740 ] 

741 ) 

742 self._applied_2 = np.array( 

743 [ 

744 [ 

745 [0.98453144, 0.53461565, 0.05393585], 

746 [0.76000720, 0.68473291, 0.62923633], 

747 ], 

748 [ 

749 [0.98718436, 0.89255862, 0.14399599], 

750 [0.85784314, 0.46473837, 0.97713337], 

751 ], 

752 [ 

753 [0.84855994, 0.93403795, 0.67216031], 

754 [0.49723089, 0.99210103, 0.97371216], 

755 ], 

756 [ 

757 [0.98886872, 0.42197234, 0.89183123], 

758 [0.02065388, 0.79047033, 0.93681229], 

759 ], 

760 ] 

761 ) 

762 self._applied_3 = np.array( 

763 [ 

764 [ 

765 [0.98685765, 0.58844468, 0.09393531], 

766 [0.79274650, 0.72453018, 0.69347904], 

767 ], 

768 [ 

769 [0.98911162, 0.90807837, 0.25736920], 

770 [0.87825083, 0.53046097, 0.98225775], 

771 ], 

772 [ 

773 [0.87021380, 0.94442819, 0.72448386], 

774 [0.55350090, 0.99318691, 0.97922787], 

775 ], 

776 [ 

777 [0.99054268, 0.49317779, 0.91055390], 

778 [0.02408419, 0.81991814, 0.94597809], 

779 ], 

780 ] 

781 ) 

782 self._applied_4 = self._inverted_apply_1 

783 

784 def test_required_methods(self) -> None: 

785 """Test the presence of required methods.""" 

786 

787 required_methods = ( 

788 "__init__", 

789 "is_domain_explicit", 

790 "linear_table", 

791 "invert", 

792 "apply", 

793 "convert", 

794 ) 

795 

796 for method in required_methods: 

797 assert method in dir(LUT3x1D) 

798 

799 def test__init__(self) -> None: 

800 """ 

801 Test :class:`colour.io.luts.lut.LUT3x1D.__init__` method. 

802 """ 

803 

804 LUT = LUT3x1D(self._table_1) 

805 

806 np.testing.assert_allclose( 

807 LUT.table, self._table_1, atol=TOLERANCE_ABSOLUTE_TESTS 

808 ) 

809 

810 assert str(id(LUT)) == LUT.name 

811 

812 np.testing.assert_array_equal(LUT.domain, self._domain_1) 

813 

814 assert LUT.dimensions == self._dimensions 

815 

816 assert isinstance(LUT3x1D(self._table_3, domain=self._domain_3), LUT3x1D) 

817 

818 def test_table(self) -> None: 

819 """ 

820 Test :class:`colour.io.luts.lut.LUT3x1D.table` property. 

821 """ 

822 

823 LUT = LUT3x1D() 

824 

825 np.testing.assert_array_equal(LUT.table, LUT.linear_table(self._size)) 

826 

827 table_1 = self._table_1 * 0.8 + 0.1 

828 LUT.table = table_1 

829 np.testing.assert_allclose(LUT.table, table_1, atol=TOLERANCE_ABSOLUTE_TESTS) 

830 

831 def test_name(self) -> None: 

832 """ 

833 Test :class:`colour.io.luts.lut.LUT3x1D.name` property. 

834 """ 

835 

836 LUT = LUT3x1D(self._table_1) 

837 

838 assert LUT.name == str(id(LUT)) 

839 

840 LUT = LUT3x1D() 

841 

842 assert LUT.name == f"Unity {self._table_1.shape[0]}" 

843 

844 def test_domain(self) -> None: 

845 """ 

846 Test :class:`colour.io.luts.lut.LUT3x1D.domain` property. 

847 """ 

848 

849 LUT = LUT3x1D() 

850 

851 np.testing.assert_array_equal(LUT.domain, self._domain_1) 

852 

853 domain = self._domain_1 * 0.8 + 0.1 

854 LUT.domain = domain 

855 np.testing.assert_array_equal(LUT.domain, domain) 

856 

857 def test_size(self) -> None: 

858 """ 

859 Test :class:`colour.io.luts.lut.LUT3x1D.size` property. 

860 """ 

861 

862 LUT = LUT3x1D() 

863 

864 assert LUT.size == LUT.table.shape[0] 

865 

866 def test_dimensions(self) -> None: 

867 """ 

868 Test :class:`colour.io.luts.lut.LUT3x1D.dimensions` property. 

869 """ 

870 

871 LUT = LUT3x1D() 

872 

873 assert LUT.dimensions == self._dimensions 

874 

875 def test_comments(self) -> None: 

876 """ 

877 Test :class:`colour.io.luts.lut.LUT3x1D.comments` property. 

878 """ 

879 

880 LUT = LUT3x1D() 

881 assert LUT.comments == [] 

882 

883 comments = ["A first comment.", "A second comment."] 

884 LUT = LUT3x1D(comments=comments) 

885 

886 assert LUT.comments == comments 

887 

888 def test__str__(self) -> None: 

889 """ 

890 Test :class:`colour.io.luts.lut.LUT3x1D.__str__` property. 

891 """ 

892 

893 LUT = LUT3x1D(name="Nemo") 

894 

895 assert str(LUT) == self._str 

896 

897 def test__repr__(self) -> None: 

898 """ 

899 Test :class:`colour.io.luts.lut.LUT3x1D.__repr__` method. 

900 """ 

901 

902 LUT = LUT3x1D(name="Nemo", comments=["A first comment.", "A second comment."]) 

903 

904 # The default LUT representation is too large to be embedded, given 

905 # that :class:`colour.io.luts.lut.LUT3D.__str__` method is defined by 

906 # :class:`colour.io.luts.lut.AbstractLUT.__str__` method, the two other 

907 # tests should reasonably cover this case. 

908 if self._dimensions == 3: # pragma: no cover 

909 return 

910 

911 assert repr(LUT) == self._repr # pragma: no cover 

912 

913 def test__eq__(self) -> None: 

914 """ 

915 Test :class:`colour.io.luts.lut.LUT3x1D.__eq__` method. 

916 """ 

917 

918 LUT_1 = LUT3x1D() 

919 LUT_2 = LUT3x1D() 

920 

921 assert LUT_1 == LUT_2 

922 

923 def test__ne__(self) -> None: 

924 """ 

925 Test :class:`colour.io.luts.lut.LUT3x1D.__ne__` method. 

926 """ 

927 

928 LUT_1 = LUT3x1D() 

929 LUT_2 = LUT3x1D() 

930 

931 LUT_2 += 0.1 

932 assert LUT_1 != LUT_2 

933 

934 LUT_2 = LUT3x1D() 

935 LUT_2.domain = self._domain_1 * 0.8 + 0.1 

936 assert LUT_1 != LUT_2 

937 

938 def test_is_domain_explicit(self) -> None: 

939 """ 

940 Test :class:`colour.io.luts.lut.LUT3x1D.is_domain_explicit` method. 

941 """ 

942 

943 assert not LUT3x1D().is_domain_explicit() 

944 

945 assert LUT3x1D(self._table_3, domain=self._domain_3).is_domain_explicit() 

946 

947 def test_arithmetical_operation(self) -> None: 

948 """ 

949 Test :class:`colour.io.luts.lut.LUT3x1D.arithmetical_operation` method. 

950 """ 

951 

952 LUT_1 = LUT3x1D() 

953 LUT_2 = LUT3x1D() 

954 

955 np.testing.assert_allclose( 

956 LUT_1.arithmetical_operation(10, "+", False).table, 

957 self._table_1 + 10, 

958 atol=TOLERANCE_ABSOLUTE_TESTS, 

959 ) 

960 

961 np.testing.assert_allclose( 

962 LUT_1.arithmetical_operation(10, "-", False).table, 

963 self._table_1 - 10, 

964 atol=TOLERANCE_ABSOLUTE_TESTS, 

965 ) 

966 

967 np.testing.assert_allclose( 

968 LUT_1.arithmetical_operation(10, "*", False).table, 

969 self._table_1 * 10, 

970 atol=TOLERANCE_ABSOLUTE_TESTS, 

971 ) 

972 

973 np.testing.assert_allclose( 

974 LUT_1.arithmetical_operation(10, "/", False).table, 

975 self._table_1 / 10, 

976 atol=TOLERANCE_ABSOLUTE_TESTS, 

977 ) 

978 

979 np.testing.assert_allclose( 

980 LUT_1.arithmetical_operation(10, "**", False).table, 

981 self._table_1**10, 

982 atol=TOLERANCE_ABSOLUTE_TESTS, 

983 ) 

984 

985 np.testing.assert_allclose( 

986 (LUT_1 + 10).table, 

987 self._table_1 + 10, 

988 atol=TOLERANCE_ABSOLUTE_TESTS, 

989 ) 

990 

991 np.testing.assert_allclose( 

992 (LUT_1 - 10).table, 

993 self._table_1 - 10, 

994 atol=TOLERANCE_ABSOLUTE_TESTS, 

995 ) 

996 

997 np.testing.assert_allclose( 

998 (LUT_1 * 10).table, 

999 self._table_1 * 10, 

1000 atol=TOLERANCE_ABSOLUTE_TESTS, 

1001 ) 

1002 

1003 np.testing.assert_allclose( 

1004 (LUT_1 / 10).table, 

1005 self._table_1 / 10, 

1006 atol=TOLERANCE_ABSOLUTE_TESTS, 

1007 ) 

1008 

1009 np.testing.assert_allclose( 

1010 (LUT_1**10).table, 

1011 self._table_1**10, 

1012 atol=TOLERANCE_ABSOLUTE_TESTS, 

1013 ) 

1014 

1015 np.testing.assert_allclose( 

1016 LUT_2.arithmetical_operation(10, "+", True).table, 

1017 self._table_1 + 10, 

1018 atol=TOLERANCE_ABSOLUTE_TESTS, 

1019 ) 

1020 

1021 np.testing.assert_allclose( 

1022 LUT_2.arithmetical_operation(10, "-", True).table, 

1023 self._table_1, 

1024 atol=TOLERANCE_ABSOLUTE_TESTS, 

1025 ) 

1026 

1027 np.testing.assert_allclose( 

1028 LUT_2.arithmetical_operation(10, "*", True).table, 

1029 self._table_1 * 10, 

1030 atol=TOLERANCE_ABSOLUTE_TESTS, 

1031 ) 

1032 

1033 np.testing.assert_allclose( 

1034 LUT_2.arithmetical_operation(10, "/", True).table, 

1035 self._table_1, 

1036 atol=TOLERANCE_ABSOLUTE_TESTS, 

1037 ) 

1038 

1039 np.testing.assert_allclose( 

1040 LUT_2.arithmetical_operation(10, "**", True).table, 

1041 self._table_1**10, 

1042 atol=TOLERANCE_ABSOLUTE_TESTS, 

1043 ) 

1044 

1045 LUT_2 = LUT3x1D() 

1046 

1047 np.testing.assert_allclose( 

1048 LUT_2.arithmetical_operation(self._table_1, "+", False).table, 

1049 LUT_2.table + self._table_1, 

1050 atol=TOLERANCE_ABSOLUTE_TESTS, 

1051 ) 

1052 

1053 np.testing.assert_allclose( 

1054 LUT_2.arithmetical_operation(LUT_2, "+", False).table, 

1055 LUT_2.table + LUT_2.table, 

1056 atol=TOLERANCE_ABSOLUTE_TESTS, 

1057 ) 

1058 

1059 def test_linear_table(self) -> None: 

1060 """ 

1061 Test :class:`colour.io.luts.lut.LUT3x1D.linear_table` method. 

1062 """ 

1063 

1064 LUT_1 = LUT3x1D() 

1065 

1066 np.testing.assert_allclose( 

1067 LUT_1.linear_table(self._size), 

1068 self._table_1, 

1069 atol=TOLERANCE_ABSOLUTE_TESTS, 

1070 ) 

1071 

1072 np.testing.assert_allclose( 

1073 spow(LUT3x1D.linear_table(**self._table_3_kwargs), 1 / 2.6), 

1074 self._table_3, 

1075 atol=TOLERANCE_ABSOLUTE_TESTS, 

1076 ) 

1077 

1078 def test_copy(self) -> None: 

1079 """ 

1080 Test :class:`colour.io.luts.lut.LUT3x1D.copy` method. 

1081 """ 

1082 

1083 LUT_1 = LUT3x1D() 

1084 

1085 assert LUT_1 is not LUT_1.copy() 

1086 assert LUT_1.copy() == LUT_1 

1087 

1088 def test_invert(self) -> None: 

1089 """ 

1090 Test :class:`colour.io.luts.lut.LUT3x1D.invert` method. 

1091 """ 

1092 

1093 LUT_i = LUT3x1D(self._table_2).invert( 

1094 interpolator=self._interpolator_1, **self._invert_kwargs_1 

1095 ) 

1096 

1097 np.testing.assert_allclose( 

1098 LUT_i.apply(RANDOM_TRIPLETS), 

1099 self._inverted_apply_1, 

1100 atol=TOLERANCE_ABSOLUTE_TESTS, 

1101 ) 

1102 

1103 LUT_i = LUT3x1D(self._table_2).invert( 

1104 interpolator=self._interpolator_2, **self._invert_kwargs_2 

1105 ) 

1106 

1107 np.testing.assert_allclose( 

1108 LUT_i.apply(RANDOM_TRIPLETS), 

1109 self._inverted_apply_2, 

1110 atol=TOLERANCE_ABSOLUTE_TESTS, 

1111 ) 

1112 

1113 LUT_i = LUT3x1D(self._table_2, domain=self._domain_4) 

1114 

1115 try: 

1116 LUT_i = LUT_i.invert( 

1117 interpolator=self._interpolator_2, **self._invert_kwargs_2 

1118 ) 

1119 

1120 np.testing.assert_allclose( # pragma: no cover 

1121 LUT_i.apply(RANDOM_TRIPLETS), 

1122 self._inverted_apply_2, 

1123 atol=TOLERANCE_ABSOLUTE_TESTS, 

1124 ) 

1125 except NotImplementedError: # pragma: no cover 

1126 pass 

1127 

1128 def test_apply(self) -> None: 

1129 """ 

1130 Test :class:`colour.io.luts.lut.LUT3x1D.apply` method. 

1131 """ 

1132 

1133 if not is_scipy_installed(): # pragma: no cover 

1134 return 

1135 

1136 LUT_1 = LUT3x1D(self._table_2) 

1137 

1138 np.testing.assert_allclose( 

1139 LUT_1.apply(RANDOM_TRIPLETS), 

1140 self._applied_1, 

1141 atol=TOLERANCE_ABSOLUTE_TESTS, 

1142 ) 

1143 

1144 LUT_2 = LUT3x1D(domain=self._domain_2) 

1145 LUT_2.table = spow(LUT_2.table, 1 / 2.2) 

1146 

1147 np.testing.assert_allclose( 

1148 LUT_2.apply(RANDOM_TRIPLETS), 

1149 self._applied_2, 

1150 atol=TOLERANCE_ABSOLUTE_TESTS, 

1151 ) 

1152 

1153 LUT_3 = LUT3x1D(self._table_3, domain=self._domain_3) 

1154 

1155 np.testing.assert_allclose( 

1156 LUT_3.apply(RANDOM_TRIPLETS), 

1157 self._applied_3, 

1158 atol=TOLERANCE_ABSOLUTE_TESTS, 

1159 ) 

1160 

1161 LUT_4 = LUT3x1D(self._table_2) 

1162 

1163 np.testing.assert_allclose( 

1164 LUT_4.apply( 

1165 RANDOM_TRIPLETS, 

1166 direction="Inverse", 

1167 interpolator=self._interpolator_1, 

1168 interpolator_kwargs=self._interpolator_kwargs_1, 

1169 **self._invert_kwargs_1, 

1170 ), 

1171 self._applied_4, 

1172 atol=TOLERANCE_ABSOLUTE_TESTS, 

1173 ) 

1174 

1175 

1176class TestLUT3D: 

1177 """Define :class:`colour.io.luts.lut.LUT3D` class unit tests methods.""" 

1178 

1179 def setup_method(self) -> None: 

1180 """Initialise the common tests attributes.""" 

1181 

1182 self._size = 33 

1183 self._dimensions = 3 

1184 samples_1 = np.linspace(0, 1, 10) 

1185 samples_2 = np.linspace(-0.1, 1.5, 15) 

1186 samples_3 = np.linspace(-0.1, 3.0, 20) 

1187 self._domain_1 = np.array([[0, 0, 0], [1, 1, 1]]) 

1188 self._domain_2 = np.array([[0.0, -0.1, -0.2], [1.0, 1.5, 3.0]]) 

1189 self._domain_3 = tstack( 

1190 [ 

1191 np.hstack([samples_1, np.full(10, np.nan)]), 

1192 np.hstack([samples_2, np.full(5, np.nan)]), 

1193 samples_3, 

1194 ] 

1195 ) 

1196 self._domain_4 = self._domain_3 

1197 self._table_1 = as_float_array( 

1198 np.flip( 

1199 np.reshape( 

1200 np.transpose( 

1201 np.meshgrid( 

1202 *[ 

1203 np.linspace(axes[0], axes[1], 33) 

1204 for axes in reversed(tsplit(self._domain_1)) 

1205 ], 

1206 indexing="ij", 

1207 ) 

1208 ), 

1209 (33, 33, 33, 3), 

1210 ), 

1211 -1, 

1212 ) 

1213 ) 

1214 self._table_2 = self._table_1 ** (1 / 2.2) 

1215 self._table_3 = as_float_array( 

1216 spow( 

1217 np.flip( 

1218 np.reshape( 

1219 np.transpose( 

1220 np.meshgrid( 

1221 *[ 

1222 axes[: (~np.isnan(axes)).cumsum().argmax() + 1] 

1223 for axes in reversed(tsplit(self._domain_3)) 

1224 ], 

1225 indexing="ij", 

1226 ) 

1227 ), 

1228 (10, 15, 20, 3), 

1229 ), 

1230 -1, 

1231 ), 

1232 1 / 2.6, 

1233 ) 

1234 ) 

1235 self._table_1_kwargs = {"size": self._size, "domain": self._domain_1} 

1236 self._table_2_kwargs = {"size": self._size, "domain": self._domain_2} 

1237 self._table_3_kwargs = { 

1238 "size": np.array([10, 15, 20]), 

1239 "domain": self._domain_3, 

1240 } 

1241 self._interpolator_1 = table_interpolation_trilinear 

1242 self._interpolator_kwargs_1 = {} 

1243 self._interpolator_2 = table_interpolation_tetrahedral 

1244 self._interpolator_kwargs_2 = {} 

1245 self._invert_kwargs_1 = {"query_size": 1, "gamma": 1.0, "sigma": 0.0} 

1246 self._invert_kwargs_2 = {"query_size": 3, "gamma": 1.0, "sigma": 0.0} 

1247 self._str = textwrap.dedent( 

1248 """ 

1249 LUT3D - Nemo 

1250 ------------ 

1251 

1252 Dimensions : 3 

1253 Domain : [[ 0. 0. 0.] 

1254 [ 1. 1. 1.]] 

1255 Size : (33, 33, 33, 3) 

1256 """ 

1257 ).strip() 

1258 self._repr = None 

1259 self._inverted_apply_1 = np.array( 

1260 [ 

1261 [ 

1262 [0.92925301, 0.04741494, 0.0], 

1263 [0.26646642, 0.16033355, 0.125], 

1264 ], 

1265 [ 

1266 [0.93931, 0.57646832, 0.0078125], 

1267 [0.47633299, 0.03125, 0.90691877], 

1268 ], 

1269 [ 

1270 [0.45557788, 0.72406682, 0.16146634], 

1271 [0.03219981, 0.96443551, 0.89125893], 

1272 ], 

1273 [ 

1274 [0.94831328, 0.0234375, 0.58804282], 

1275 [0.0, 0.31964634, 0.72957742], 

1276 ], 

1277 ] 

1278 ) 

1279 

1280 self._inverted_apply_2 = np.array( 

1281 [ 

1282 [ 

1283 [0.92923562, 0.04741494, 0.0], 

1284 [0.26607246, 0.16033355, 0.12420096], 

1285 ], 

1286 [ 

1287 [0.94149611, 0.57887455, 0.0078125], 

1288 [0.47760706, 0.03125, 0.90728666], 

1289 ], 

1290 [ 

1291 [0.45371291, 0.72327218, 0.16146634], 

1292 [0.03219981, 0.96308094, 0.89145048], 

1293 ], 

1294 [ 

1295 [0.9485237, 0.0234375, 0.59041851], 

1296 [0.0, 0.32182126, 0.73035906], 

1297 ], 

1298 ] 

1299 ) 

1300 self._applied_1 = np.array( 

1301 [ 

1302 [ 

1303 [0.98486974, 0.53531556, 0.05950617], 

1304 [0.76022687, 0.68479344, 0.64907649], 

1305 ], 

1306 [ 

1307 [0.98747624, 0.89287549, 0.23859990], 

1308 [0.85844632, 0.47829965, 0.98002765], 

1309 ], 

1310 [ 

1311 [0.84903362, 0.93518100, 0.68577990], 

1312 [0.49827272, 0.99238949, 0.97644600], 

1313 ], 

1314 [ 

1315 [0.98912224, 0.43911364, 0.89645863], 

1316 [0.04125691, 0.79116284, 0.93680839], 

1317 ], 

1318 ] 

1319 ) 

1320 

1321 self._applied_2 = np.array( 

1322 [ 

1323 [ 

1324 [0.98486974, 0.53526504, 0.03155191], 

1325 [0.76022687, 0.68458573, 0.64850011], 

1326 ], 

1327 [ 

1328 [0.98747624, 0.89277461, 0.15505443], 

1329 [0.85844632, 0.47842591, 0.97972986], 

1330 ], 

1331 [ 

1332 [0.84903362, 0.93514331, 0.68479574], 

1333 [0.49827272, 0.99234923, 0.97614054], 

1334 ], 

1335 [ 

1336 [0.98912224, 0.43850620, 0.89625878], 

1337 [0.04125691, 0.79115345, 0.93648599], 

1338 ], 

1339 ] 

1340 ) 

1341 self._applied_3 = np.array( 

1342 [ 

1343 [ 

1344 [0.98685765, 0.58844468, 0.09393531], 

1345 [0.79274650, 0.72453018, 0.69347904], 

1346 ], 

1347 [ 

1348 [0.98911162, 0.90807837, 0.25736920], 

1349 [0.87825083, 0.53046097, 0.98225775], 

1350 ], 

1351 [ 

1352 [0.87021380, 0.94442819, 0.72448386], 

1353 [0.55350090, 0.99318691, 0.97922787], 

1354 ], 

1355 [ 

1356 [0.99054268, 0.49317779, 0.91055390], 

1357 [0.02408419, 0.81991814, 0.94597809], 

1358 ], 

1359 ] 

1360 ) 

1361 self._applied_4 = self._inverted_apply_1 

1362 

1363 def test_required_methods(self) -> None: 

1364 """Test the presence of required methods.""" 

1365 

1366 required_methods = ( 

1367 "__init__", 

1368 "is_domain_explicit", 

1369 "linear_table", 

1370 "invert", 

1371 "apply", 

1372 "convert", 

1373 ) 

1374 

1375 for method in required_methods: 

1376 assert method in dir(LUT3D) 

1377 

1378 def test__init__(self) -> None: 

1379 """ 

1380 Test :class:`colour.io.luts.lut.LUT3D.__init__` method. 

1381 """ 

1382 

1383 LUT = LUT3D(self._table_1) 

1384 

1385 np.testing.assert_allclose( 

1386 LUT.table, self._table_1, atol=TOLERANCE_ABSOLUTE_TESTS 

1387 ) 

1388 

1389 assert str(id(LUT)) == LUT.name 

1390 

1391 np.testing.assert_array_equal(LUT.domain, self._domain_1) 

1392 

1393 assert LUT.dimensions == self._dimensions 

1394 

1395 assert isinstance(LUT3D(self._table_3, domain=self._domain_3), LUT3D) 

1396 

1397 def test_table(self) -> None: 

1398 """ 

1399 Test :class:`colour.io.luts.lut.LUT3D.table` property. 

1400 """ 

1401 

1402 LUT = LUT3D() 

1403 

1404 np.testing.assert_array_equal(LUT.table, LUT.linear_table(self._size)) 

1405 

1406 table_1 = self._table_1 * 0.8 + 0.1 

1407 LUT.table = table_1 

1408 np.testing.assert_allclose(LUT.table, table_1, atol=TOLERANCE_ABSOLUTE_TESTS) 

1409 

1410 def test_name(self) -> None: 

1411 """ 

1412 Test :class:`colour.io.luts.lut.LUT3D.name` property. 

1413 """ 

1414 

1415 LUT = LUT3D(self._table_1) 

1416 

1417 assert LUT.name == str(id(LUT)) 

1418 

1419 LUT = LUT3D() 

1420 

1421 assert LUT.name == f"Unity {self._table_1.shape[0]}" 

1422 

1423 def test_domain(self) -> None: 

1424 """ 

1425 Test :class:`colour.io.luts.lut.LUT3D.domain` property. 

1426 """ 

1427 

1428 LUT = LUT3D() 

1429 

1430 np.testing.assert_array_equal(LUT.domain, self._domain_1) 

1431 

1432 domain = self._domain_1 * 0.8 + 0.1 

1433 LUT.domain = domain 

1434 np.testing.assert_array_equal(LUT.domain, domain) 

1435 

1436 def test_size(self) -> None: 

1437 """ 

1438 Test :class:`colour.io.luts.lut.LUT3D.size` property. 

1439 """ 

1440 

1441 LUT = LUT3D() 

1442 

1443 assert LUT.size == LUT.table.shape[0] 

1444 

1445 def test_dimensions(self) -> None: 

1446 """ 

1447 Test :class:`colour.io.luts.lut.LUT3D.dimensions` property. 

1448 """ 

1449 

1450 LUT = LUT3D() 

1451 

1452 assert LUT.dimensions == self._dimensions 

1453 

1454 def test_comments(self) -> None: 

1455 """ 

1456 Test :class:`colour.io.luts.lut.LUT3D.comments` property. 

1457 """ 

1458 

1459 LUT = LUT3D() 

1460 assert LUT.comments == [] 

1461 

1462 comments = ["A first comment.", "A second comment."] 

1463 LUT = LUT3D(comments=comments) 

1464 

1465 assert LUT.comments == comments 

1466 

1467 def test__str__(self) -> None: 

1468 """ 

1469 Test :class:`colour.io.luts.lut.LUT3D.__str__` property. 

1470 """ 

1471 

1472 LUT = LUT3D(name="Nemo") 

1473 

1474 assert str(LUT) == self._str 

1475 

1476 def test__repr__(self) -> None: 

1477 """ 

1478 Test :class:`colour.io.luts.lut.LUT3D.__repr__` method. 

1479 """ 

1480 

1481 LUT = LUT3D(name="Nemo", comments=["A first comment.", "A second comment."]) 

1482 

1483 # The default LUT representation is too large to be embedded, given 

1484 # that :class:`colour.io.luts.lut.LUT3D.__str__` method is defined by 

1485 # :class:`colour.io.luts.lut.AbstractLUT.__str__` method, the two other 

1486 # tests should reasonably cover this case. 

1487 if self._dimensions == 3: # pragma: no cover 

1488 return 

1489 

1490 assert repr(LUT) == self._repr # pragma: no cover 

1491 

1492 def test__eq__(self) -> None: 

1493 """ 

1494 Test :class:`colour.io.luts.lut.LUT3D.__eq__` method. 

1495 """ 

1496 

1497 LUT_1 = LUT3D() 

1498 LUT_2 = LUT3D() 

1499 

1500 assert LUT_1 == LUT_2 

1501 

1502 def test__ne__(self) -> None: 

1503 """ 

1504 Test :class:`colour.io.luts.lut.LUT3D.__ne__` method. 

1505 """ 

1506 

1507 LUT_1 = LUT3D() 

1508 LUT_2 = LUT3D() 

1509 

1510 LUT_2 += 0.1 

1511 assert LUT_1 != LUT_2 

1512 

1513 LUT_2 = LUT3D() 

1514 LUT_2.domain = self._domain_1 * 0.8 + 0.1 

1515 assert LUT_1 != LUT_2 

1516 

1517 def test_is_domain_explicit(self) -> None: 

1518 """ 

1519 Test :class:`colour.io.luts.lut.LUT3D.is_domain_explicit` method. 

1520 """ 

1521 

1522 assert not LUT3D().is_domain_explicit() 

1523 

1524 assert LUT3D(self._table_3, domain=self._domain_3).is_domain_explicit() 

1525 

1526 def test_arithmetical_operation(self) -> None: 

1527 """ 

1528 Test :class:`colour.io.luts.lut.LUT3D.arithmetical_operation` method. 

1529 """ 

1530 

1531 LUT_1 = LUT3D() 

1532 LUT_2 = LUT3D() 

1533 

1534 np.testing.assert_allclose( 

1535 LUT_1.arithmetical_operation(10, "+", False).table, 

1536 self._table_1 + 10, 

1537 atol=TOLERANCE_ABSOLUTE_TESTS, 

1538 ) 

1539 

1540 np.testing.assert_allclose( 

1541 LUT_1.arithmetical_operation(10, "-", False).table, 

1542 self._table_1 - 10, 

1543 atol=TOLERANCE_ABSOLUTE_TESTS, 

1544 ) 

1545 

1546 np.testing.assert_allclose( 

1547 LUT_1.arithmetical_operation(10, "*", False).table, 

1548 self._table_1 * 10, 

1549 atol=TOLERANCE_ABSOLUTE_TESTS, 

1550 ) 

1551 

1552 np.testing.assert_allclose( 

1553 LUT_1.arithmetical_operation(10, "/", False).table, 

1554 self._table_1 / 10, 

1555 atol=TOLERANCE_ABSOLUTE_TESTS, 

1556 ) 

1557 

1558 np.testing.assert_allclose( 

1559 LUT_1.arithmetical_operation(10, "**", False).table, 

1560 self._table_1**10, 

1561 atol=TOLERANCE_ABSOLUTE_TESTS, 

1562 ) 

1563 

1564 np.testing.assert_allclose( 

1565 (LUT_1 + 10).table, 

1566 self._table_1 + 10, 

1567 atol=TOLERANCE_ABSOLUTE_TESTS, 

1568 ) 

1569 

1570 np.testing.assert_allclose( 

1571 (LUT_1 - 10).table, 

1572 self._table_1 - 10, 

1573 atol=TOLERANCE_ABSOLUTE_TESTS, 

1574 ) 

1575 

1576 np.testing.assert_allclose( 

1577 (LUT_1 * 10).table, 

1578 self._table_1 * 10, 

1579 atol=TOLERANCE_ABSOLUTE_TESTS, 

1580 ) 

1581 

1582 np.testing.assert_allclose( 

1583 (LUT_1 / 10).table, 

1584 self._table_1 / 10, 

1585 atol=TOLERANCE_ABSOLUTE_TESTS, 

1586 ) 

1587 

1588 np.testing.assert_allclose( 

1589 (LUT_1**10).table, 

1590 self._table_1**10, 

1591 atol=TOLERANCE_ABSOLUTE_TESTS, 

1592 ) 

1593 

1594 np.testing.assert_allclose( 

1595 LUT_2.arithmetical_operation(10, "+", True).table, 

1596 self._table_1 + 10, 

1597 atol=TOLERANCE_ABSOLUTE_TESTS, 

1598 ) 

1599 

1600 np.testing.assert_allclose( 

1601 LUT_2.arithmetical_operation(10, "-", True).table, 

1602 self._table_1, 

1603 atol=TOLERANCE_ABSOLUTE_TESTS, 

1604 ) 

1605 

1606 np.testing.assert_allclose( 

1607 LUT_2.arithmetical_operation(10, "*", True).table, 

1608 self._table_1 * 10, 

1609 atol=TOLERANCE_ABSOLUTE_TESTS, 

1610 ) 

1611 

1612 np.testing.assert_allclose( 

1613 LUT_2.arithmetical_operation(10, "/", True).table, 

1614 self._table_1, 

1615 atol=TOLERANCE_ABSOLUTE_TESTS, 

1616 ) 

1617 

1618 np.testing.assert_allclose( 

1619 LUT_2.arithmetical_operation(10, "**", True).table, 

1620 self._table_1**10, 

1621 atol=TOLERANCE_ABSOLUTE_TESTS, 

1622 ) 

1623 

1624 LUT_2 = LUT3D() 

1625 

1626 np.testing.assert_allclose( 

1627 LUT_2.arithmetical_operation(self._table_1, "+", False).table, 

1628 LUT_2.table + self._table_1, 

1629 atol=TOLERANCE_ABSOLUTE_TESTS, 

1630 ) 

1631 

1632 np.testing.assert_allclose( 

1633 LUT_2.arithmetical_operation(LUT_2, "+", False).table, 

1634 LUT_2.table + LUT_2.table, 

1635 atol=TOLERANCE_ABSOLUTE_TESTS, 

1636 ) 

1637 

1638 def test_linear_table(self) -> None: 

1639 """ 

1640 Test :class:`colour.io.luts.lut.LUT3D.linear_table` method. 

1641 """ 

1642 

1643 LUT_1 = LUT3D() 

1644 

1645 np.testing.assert_allclose( 

1646 LUT_1.linear_table(self._size), 

1647 self._table_1, 

1648 atol=TOLERANCE_ABSOLUTE_TESTS, 

1649 ) 

1650 

1651 np.testing.assert_allclose( 

1652 spow(LUT3D.linear_table(**self._table_3_kwargs), 1 / 2.6), 

1653 self._table_3, 

1654 atol=TOLERANCE_ABSOLUTE_TESTS, 

1655 ) 

1656 

1657 def test_copy(self) -> None: 

1658 """ 

1659 Test :class:`colour.io.luts.lut.LUT3D.copy` method. 

1660 """ 

1661 

1662 LUT_1 = LUT3D() 

1663 

1664 assert LUT_1 is not LUT_1.copy() 

1665 assert LUT_1.copy() == LUT_1 

1666 

1667 def test_invert(self) -> None: 

1668 """ 

1669 Test :class:`colour.io.luts.lut.LUT3D.invert` method. 

1670 """ 

1671 

1672 if not is_scipy_installed(): # pragma: no cover 

1673 return 

1674 

1675 LUT_i = LUT3D(self._table_2).invert( 

1676 interpolator=self._interpolator_1, **self._invert_kwargs_1 

1677 ) 

1678 

1679 np.testing.assert_allclose( 

1680 LUT_i.apply(RANDOM_TRIPLETS), 

1681 self._inverted_apply_1, 

1682 atol=TOLERANCE_ABSOLUTE_TESTS, 

1683 ) 

1684 

1685 LUT_i = LUT3D(self._table_2).invert( 

1686 interpolator=self._interpolator_2, **self._invert_kwargs_2 

1687 ) 

1688 

1689 np.testing.assert_allclose( 

1690 LUT_i.apply(RANDOM_TRIPLETS), 

1691 self._inverted_apply_2, 

1692 atol=TOLERANCE_ABSOLUTE_TESTS, 

1693 ) 

1694 

1695 LUT_i = LUT3D(self._table_2, domain=self._domain_4) 

1696 

1697 try: 

1698 LUT_i = LUT_i.invert( 

1699 interpolator=self._interpolator_2, **self._invert_kwargs_2 

1700 ) 

1701 

1702 np.testing.assert_allclose( # pragma: no cover 

1703 LUT_i.apply(RANDOM_TRIPLETS), 

1704 self._inverted_apply_2, 

1705 atol=TOLERANCE_ABSOLUTE_TESTS, 

1706 ) 

1707 except NotImplementedError: # pragma: no cover 

1708 pass 

1709 

1710 def test_apply(self) -> None: 

1711 """ 

1712 Test :class:`colour.io.luts.lut.LUT3D.apply` method. 

1713 """ 

1714 

1715 if not is_scipy_installed(): # pragma: no cover 

1716 return 

1717 

1718 LUT_1 = LUT3D(self._table_2) 

1719 

1720 np.testing.assert_allclose( 

1721 LUT_1.apply(RANDOM_TRIPLETS), 

1722 self._applied_1, 

1723 atol=TOLERANCE_ABSOLUTE_TESTS, 

1724 ) 

1725 

1726 LUT_2 = LUT3D(domain=self._domain_2) 

1727 LUT_2.table = spow(LUT_2.table, 1 / 2.2) 

1728 

1729 np.testing.assert_allclose( 

1730 LUT_2.apply(RANDOM_TRIPLETS), 

1731 self._applied_2, 

1732 atol=TOLERANCE_ABSOLUTE_TESTS, 

1733 ) 

1734 

1735 LUT_3 = LUT3D(self._table_3, domain=self._domain_3) 

1736 

1737 np.testing.assert_allclose( 

1738 LUT_3.apply(RANDOM_TRIPLETS), 

1739 self._applied_3, 

1740 atol=TOLERANCE_ABSOLUTE_TESTS, 

1741 ) 

1742 

1743 LUT_4 = LUT3D(self._table_2) 

1744 

1745 np.testing.assert_allclose( 

1746 LUT_4.apply( 

1747 RANDOM_TRIPLETS, 

1748 direction="Inverse", 

1749 interpolator=self._interpolator_1, 

1750 interpolator_kwargs=self._interpolator_kwargs_1, 

1751 **self._invert_kwargs_1, 

1752 ), 

1753 self._applied_4, 

1754 atol=TOLERANCE_ABSOLUTE_TESTS, 

1755 ) 

1756 

1757 

1758class TestLUT_to_LUT: 

1759 """ 

1760 Define :func:`colour.io.luts.lut.LUT_to_LUT` definition unit tests 

1761 methods. 

1762 """ 

1763 

1764 def setup_method(self) -> None: 

1765 """Initialise the common tests attributes.""" 

1766 

1767 self._domain = np.array([[0.0, -0.1, -0.2], [1.0, 1.5, 3.0]]) 

1768 

1769 self._LUT_1 = LUT1D(LUT1D.linear_table(16) ** (1 / 2.2)) 

1770 self._LUT_2 = LUT3x1D( 

1771 LUT3x1D.linear_table(16) ** (1 / 2.2) * (1.0, 0.75, 0.5), 

1772 domain=self._domain, 

1773 ) 

1774 self._LUT_3 = LUT3D(LUT3D.linear_table(16) ** (1 / 2.2), domain=self._domain) 

1775 

1776 def test_LUT_to_LUT(self) -> None: 

1777 """Test :func:`colour.io.luts.lut.LUT_to_LUT` definition.""" 

1778 

1779 # "LUT" 1D to "LUT" 1D. 

1780 LUT = LUT_to_LUT(self._LUT_1, LUT1D) 

1781 

1782 assert LUT == self._LUT_1 

1783 

1784 # "LUT" 1D to "LUT" 3x1D. 

1785 LUT = LUT_to_LUT(self._LUT_1, LUT3x1D) 

1786 table = LUT1D.linear_table(16) ** (1 / 2.2) 

1787 

1788 assert LUT3x1D(tstack([table, table, table])) == LUT 

1789 

1790 # "LUT" 1D to "LUT" 3D. 

1791 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_1, LUT3D)) 

1792 

1793 LUT = LUT_to_LUT(self._LUT_1, LUT3D, force_conversion=True, size=5) 

1794 

1795 np.testing.assert_allclose( 

1796 LUT.table, 

1797 np.array( 

1798 [ 

1799 [ 

1800 [ 

1801 [0.00000000, 0.00000000, 0.00000000], 

1802 [0.00000000, 0.00000000, 0.53156948], 

1803 [0.00000000, 0.00000000, 0.72933741], 

1804 [0.00000000, 0.00000000, 0.87726669], 

1805 [0.00000000, 0.00000000, 1.00000000], 

1806 ], 

1807 [ 

1808 [0.00000000, 0.53156948, 0.00000000], 

1809 [0.00000000, 0.53156948, 0.53156948], 

1810 [0.00000000, 0.53156948, 0.72933741], 

1811 [0.00000000, 0.53156948, 0.87726669], 

1812 [0.00000000, 0.53156948, 1.00000000], 

1813 ], 

1814 [ 

1815 [0.00000000, 0.72933741, 0.00000000], 

1816 [0.00000000, 0.72933741, 0.53156948], 

1817 [0.00000000, 0.72933741, 0.72933741], 

1818 [0.00000000, 0.72933741, 0.87726669], 

1819 [0.00000000, 0.72933741, 1.00000000], 

1820 ], 

1821 [ 

1822 [0.00000000, 0.87726669, 0.00000000], 

1823 [0.00000000, 0.87726669, 0.53156948], 

1824 [0.00000000, 0.87726669, 0.72933741], 

1825 [0.00000000, 0.87726669, 0.87726669], 

1826 [0.00000000, 0.87726669, 1.00000000], 

1827 ], 

1828 [ 

1829 [0.00000000, 1.00000000, 0.00000000], 

1830 [0.00000000, 1.00000000, 0.53156948], 

1831 [0.00000000, 1.00000000, 0.72933741], 

1832 [0.00000000, 1.00000000, 0.87726669], 

1833 [0.00000000, 1.00000000, 1.00000000], 

1834 ], 

1835 ], 

1836 [ 

1837 [ 

1838 [0.53156948, 0.00000000, 0.00000000], 

1839 [0.53156948, 0.00000000, 0.53156948], 

1840 [0.53156948, 0.00000000, 0.72933741], 

1841 [0.53156948, 0.00000000, 0.87726669], 

1842 [0.53156948, 0.00000000, 1.00000000], 

1843 ], 

1844 [ 

1845 [0.53156948, 0.53156948, 0.00000000], 

1846 [0.53156948, 0.53156948, 0.53156948], 

1847 [0.53156948, 0.53156948, 0.72933741], 

1848 [0.53156948, 0.53156948, 0.87726669], 

1849 [0.53156948, 0.53156948, 1.00000000], 

1850 ], 

1851 [ 

1852 [0.53156948, 0.72933741, 0.00000000], 

1853 [0.53156948, 0.72933741, 0.53156948], 

1854 [0.53156948, 0.72933741, 0.72933741], 

1855 [0.53156948, 0.72933741, 0.87726669], 

1856 [0.53156948, 0.72933741, 1.00000000], 

1857 ], 

1858 [ 

1859 [0.53156948, 0.87726669, 0.00000000], 

1860 [0.53156948, 0.87726669, 0.53156948], 

1861 [0.53156948, 0.87726669, 0.72933741], 

1862 [0.53156948, 0.87726669, 0.87726669], 

1863 [0.53156948, 0.87726669, 1.00000000], 

1864 ], 

1865 [ 

1866 [0.53156948, 1.00000000, 0.00000000], 

1867 [0.53156948, 1.00000000, 0.53156948], 

1868 [0.53156948, 1.00000000, 0.72933741], 

1869 [0.53156948, 1.00000000, 0.87726669], 

1870 [0.53156948, 1.00000000, 1.00000000], 

1871 ], 

1872 ], 

1873 [ 

1874 [ 

1875 [0.72933741, 0.00000000, 0.00000000], 

1876 [0.72933741, 0.00000000, 0.53156948], 

1877 [0.72933741, 0.00000000, 0.72933741], 

1878 [0.72933741, 0.00000000, 0.87726669], 

1879 [0.72933741, 0.00000000, 1.00000000], 

1880 ], 

1881 [ 

1882 [0.72933741, 0.53156948, 0.00000000], 

1883 [0.72933741, 0.53156948, 0.53156948], 

1884 [0.72933741, 0.53156948, 0.72933741], 

1885 [0.72933741, 0.53156948, 0.87726669], 

1886 [0.72933741, 0.53156948, 1.00000000], 

1887 ], 

1888 [ 

1889 [0.72933741, 0.72933741, 0.00000000], 

1890 [0.72933741, 0.72933741, 0.53156948], 

1891 [0.72933741, 0.72933741, 0.72933741], 

1892 [0.72933741, 0.72933741, 0.87726669], 

1893 [0.72933741, 0.72933741, 1.00000000], 

1894 ], 

1895 [ 

1896 [0.72933741, 0.87726669, 0.00000000], 

1897 [0.72933741, 0.87726669, 0.53156948], 

1898 [0.72933741, 0.87726669, 0.72933741], 

1899 [0.72933741, 0.87726669, 0.87726669], 

1900 [0.72933741, 0.87726669, 1.00000000], 

1901 ], 

1902 [ 

1903 [0.72933741, 1.00000000, 0.00000000], 

1904 [0.72933741, 1.00000000, 0.53156948], 

1905 [0.72933741, 1.00000000, 0.72933741], 

1906 [0.72933741, 1.00000000, 0.87726669], 

1907 [0.72933741, 1.00000000, 1.00000000], 

1908 ], 

1909 ], 

1910 [ 

1911 [ 

1912 [0.87726669, 0.00000000, 0.00000000], 

1913 [0.87726669, 0.00000000, 0.53156948], 

1914 [0.87726669, 0.00000000, 0.72933741], 

1915 [0.87726669, 0.00000000, 0.87726669], 

1916 [0.87726669, 0.00000000, 1.00000000], 

1917 ], 

1918 [ 

1919 [0.87726669, 0.53156948, 0.00000000], 

1920 [0.87726669, 0.53156948, 0.53156948], 

1921 [0.87726669, 0.53156948, 0.72933741], 

1922 [0.87726669, 0.53156948, 0.87726669], 

1923 [0.87726669, 0.53156948, 1.00000000], 

1924 ], 

1925 [ 

1926 [0.87726669, 0.72933741, 0.00000000], 

1927 [0.87726669, 0.72933741, 0.53156948], 

1928 [0.87726669, 0.72933741, 0.72933741], 

1929 [0.87726669, 0.72933741, 0.87726669], 

1930 [0.87726669, 0.72933741, 1.00000000], 

1931 ], 

1932 [ 

1933 [0.87726669, 0.87726669, 0.00000000], 

1934 [0.87726669, 0.87726669, 0.53156948], 

1935 [0.87726669, 0.87726669, 0.72933741], 

1936 [0.87726669, 0.87726669, 0.87726669], 

1937 [0.87726669, 0.87726669, 1.00000000], 

1938 ], 

1939 [ 

1940 [0.87726669, 1.00000000, 0.00000000], 

1941 [0.87726669, 1.00000000, 0.53156948], 

1942 [0.87726669, 1.00000000, 0.72933741], 

1943 [0.87726669, 1.00000000, 0.87726669], 

1944 [0.87726669, 1.00000000, 1.00000000], 

1945 ], 

1946 ], 

1947 [ 

1948 [ 

1949 [1.00000000, 0.00000000, 0.00000000], 

1950 [1.00000000, 0.00000000, 0.53156948], 

1951 [1.00000000, 0.00000000, 0.72933741], 

1952 [1.00000000, 0.00000000, 0.87726669], 

1953 [1.00000000, 0.00000000, 1.00000000], 

1954 ], 

1955 [ 

1956 [1.00000000, 0.53156948, 0.00000000], 

1957 [1.00000000, 0.53156948, 0.53156948], 

1958 [1.00000000, 0.53156948, 0.72933741], 

1959 [1.00000000, 0.53156948, 0.87726669], 

1960 [1.00000000, 0.53156948, 1.00000000], 

1961 ], 

1962 [ 

1963 [1.00000000, 0.72933741, 0.00000000], 

1964 [1.00000000, 0.72933741, 0.53156948], 

1965 [1.00000000, 0.72933741, 0.72933741], 

1966 [1.00000000, 0.72933741, 0.87726669], 

1967 [1.00000000, 0.72933741, 1.00000000], 

1968 ], 

1969 [ 

1970 [1.00000000, 0.87726669, 0.00000000], 

1971 [1.00000000, 0.87726669, 0.53156948], 

1972 [1.00000000, 0.87726669, 0.72933741], 

1973 [1.00000000, 0.87726669, 0.87726669], 

1974 [1.00000000, 0.87726669, 1.00000000], 

1975 ], 

1976 [ 

1977 [1.00000000, 1.00000000, 0.00000000], 

1978 [1.00000000, 1.00000000, 0.53156948], 

1979 [1.00000000, 1.00000000, 0.72933741], 

1980 [1.00000000, 1.00000000, 0.87726669], 

1981 [1.00000000, 1.00000000, 1.00000000], 

1982 ], 

1983 ], 

1984 ] 

1985 ), 

1986 atol=TOLERANCE_ABSOLUTE_TESTS, 

1987 ) 

1988 

1989 # "LUT" 3x1D to "LUT" 1D. 

1990 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_2, LUT1D)) 

1991 

1992 channel_weights = np.array([1.0, 0.0, 0.0]) 

1993 LUT = LUT_to_LUT( 

1994 self._LUT_2, 

1995 LUT1D, 

1996 force_conversion=True, 

1997 channel_weights=channel_weights, 

1998 ) 

1999 

2000 channel_weights = np.array([1 / 3, 1 / 3, 1 / 3]) 

2001 

2002 domain = np.sum(self._domain * channel_weights, axis=-1) 

2003 

2004 LUT = LUT_to_LUT( 

2005 self._LUT_2, 

2006 LUT1D, 

2007 force_conversion=True, 

2008 channel_weights=channel_weights, 

2009 ) 

2010 

2011 assert ( 

2012 LUT1D( 

2013 np.sum(self._LUT_2.table * channel_weights, axis=-1), 

2014 domain=domain, 

2015 ) 

2016 == LUT 

2017 ) 

2018 

2019 # "LUT" 3x1D to "LUT" 3x1D. 

2020 LUT = LUT_to_LUT(self._LUT_2, LUT3x1D) 

2021 

2022 assert LUT == self._LUT_2 

2023 

2024 # "LUT" 3x1D to "LUT" 3D. 

2025 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_2, LUT3D)) 

2026 

2027 LUT = LUT_to_LUT(self._LUT_2, LUT3D, force_conversion=True, size=5) 

2028 

2029 np.testing.assert_allclose( 

2030 LUT.table, 

2031 np.array( 

2032 [ 

2033 [ 

2034 [ 

2035 [0.00000000, 0.00000000, 0.00000000], 

2036 [0.00000000, 0.00000000, 0.26578474], 

2037 [0.00000000, 0.00000000, 0.36466870], 

2038 [0.00000000, 0.00000000, 0.43863334], 

2039 [0.00000000, 0.00000000, 0.50000000], 

2040 ], 

2041 [ 

2042 [0.00000000, 0.39867711, 0.00000000], 

2043 [0.00000000, 0.39867711, 0.26578474], 

2044 [0.00000000, 0.39867711, 0.36466870], 

2045 [0.00000000, 0.39867711, 0.43863334], 

2046 [0.00000000, 0.39867711, 0.50000000], 

2047 ], 

2048 [ 

2049 [0.00000000, 0.54700305, 0.00000000], 

2050 [0.00000000, 0.54700305, 0.26578474], 

2051 [0.00000000, 0.54700305, 0.36466870], 

2052 [0.00000000, 0.54700305, 0.43863334], 

2053 [0.00000000, 0.54700305, 0.50000000], 

2054 ], 

2055 [ 

2056 [0.00000000, 0.65795001, 0.00000000], 

2057 [0.00000000, 0.65795001, 0.26578474], 

2058 [0.00000000, 0.65795001, 0.36466870], 

2059 [0.00000000, 0.65795001, 0.43863334], 

2060 [0.00000000, 0.65795001, 0.50000000], 

2061 ], 

2062 [ 

2063 [0.00000000, 0.75000000, 0.00000000], 

2064 [0.00000000, 0.75000000, 0.26578474], 

2065 [0.00000000, 0.75000000, 0.36466870], 

2066 [0.00000000, 0.75000000, 0.43863334], 

2067 [0.00000000, 0.75000000, 0.50000000], 

2068 ], 

2069 ], 

2070 [ 

2071 [ 

2072 [0.53156948, 0.00000000, 0.00000000], 

2073 [0.53156948, 0.00000000, 0.26578474], 

2074 [0.53156948, 0.00000000, 0.36466870], 

2075 [0.53156948, 0.00000000, 0.43863334], 

2076 [0.53156948, 0.00000000, 0.50000000], 

2077 ], 

2078 [ 

2079 [0.53156948, 0.39867711, 0.00000000], 

2080 [0.53156948, 0.39867711, 0.26578474], 

2081 [0.53156948, 0.39867711, 0.36466870], 

2082 [0.53156948, 0.39867711, 0.43863334], 

2083 [0.53156948, 0.39867711, 0.50000000], 

2084 ], 

2085 [ 

2086 [0.53156948, 0.54700305, 0.00000000], 

2087 [0.53156948, 0.54700305, 0.26578474], 

2088 [0.53156948, 0.54700305, 0.36466870], 

2089 [0.53156948, 0.54700305, 0.43863334], 

2090 [0.53156948, 0.54700305, 0.50000000], 

2091 ], 

2092 [ 

2093 [0.53156948, 0.65795001, 0.00000000], 

2094 [0.53156948, 0.65795001, 0.26578474], 

2095 [0.53156948, 0.65795001, 0.36466870], 

2096 [0.53156948, 0.65795001, 0.43863334], 

2097 [0.53156948, 0.65795001, 0.50000000], 

2098 ], 

2099 [ 

2100 [0.53156948, 0.75000000, 0.00000000], 

2101 [0.53156948, 0.75000000, 0.26578474], 

2102 [0.53156948, 0.75000000, 0.36466870], 

2103 [0.53156948, 0.75000000, 0.43863334], 

2104 [0.53156948, 0.75000000, 0.50000000], 

2105 ], 

2106 ], 

2107 [ 

2108 [ 

2109 [0.72933741, 0.00000000, 0.00000000], 

2110 [0.72933741, 0.00000000, 0.26578474], 

2111 [0.72933741, 0.00000000, 0.36466870], 

2112 [0.72933741, 0.00000000, 0.43863334], 

2113 [0.72933741, 0.00000000, 0.50000000], 

2114 ], 

2115 [ 

2116 [0.72933741, 0.39867711, 0.00000000], 

2117 [0.72933741, 0.39867711, 0.26578474], 

2118 [0.72933741, 0.39867711, 0.36466870], 

2119 [0.72933741, 0.39867711, 0.43863334], 

2120 [0.72933741, 0.39867711, 0.50000000], 

2121 ], 

2122 [ 

2123 [0.72933741, 0.54700305, 0.00000000], 

2124 [0.72933741, 0.54700305, 0.26578474], 

2125 [0.72933741, 0.54700305, 0.36466870], 

2126 [0.72933741, 0.54700305, 0.43863334], 

2127 [0.72933741, 0.54700305, 0.50000000], 

2128 ], 

2129 [ 

2130 [0.72933741, 0.65795001, 0.00000000], 

2131 [0.72933741, 0.65795001, 0.26578474], 

2132 [0.72933741, 0.65795001, 0.36466870], 

2133 [0.72933741, 0.65795001, 0.43863334], 

2134 [0.72933741, 0.65795001, 0.50000000], 

2135 ], 

2136 [ 

2137 [0.72933741, 0.75000000, 0.00000000], 

2138 [0.72933741, 0.75000000, 0.26578474], 

2139 [0.72933741, 0.75000000, 0.36466870], 

2140 [0.72933741, 0.75000000, 0.43863334], 

2141 [0.72933741, 0.75000000, 0.50000000], 

2142 ], 

2143 ], 

2144 [ 

2145 [ 

2146 [0.87726669, 0.00000000, 0.00000000], 

2147 [0.87726669, 0.00000000, 0.26578474], 

2148 [0.87726669, 0.00000000, 0.36466870], 

2149 [0.87726669, 0.00000000, 0.43863334], 

2150 [0.87726669, 0.00000000, 0.50000000], 

2151 ], 

2152 [ 

2153 [0.87726669, 0.39867711, 0.00000000], 

2154 [0.87726669, 0.39867711, 0.26578474], 

2155 [0.87726669, 0.39867711, 0.36466870], 

2156 [0.87726669, 0.39867711, 0.43863334], 

2157 [0.87726669, 0.39867711, 0.50000000], 

2158 ], 

2159 [ 

2160 [0.87726669, 0.54700305, 0.00000000], 

2161 [0.87726669, 0.54700305, 0.26578474], 

2162 [0.87726669, 0.54700305, 0.36466870], 

2163 [0.87726669, 0.54700305, 0.43863334], 

2164 [0.87726669, 0.54700305, 0.50000000], 

2165 ], 

2166 [ 

2167 [0.87726669, 0.65795001, 0.00000000], 

2168 [0.87726669, 0.65795001, 0.26578474], 

2169 [0.87726669, 0.65795001, 0.36466870], 

2170 [0.87726669, 0.65795001, 0.43863334], 

2171 [0.87726669, 0.65795001, 0.50000000], 

2172 ], 

2173 [ 

2174 [0.87726669, 0.75000000, 0.00000000], 

2175 [0.87726669, 0.75000000, 0.26578474], 

2176 [0.87726669, 0.75000000, 0.36466870], 

2177 [0.87726669, 0.75000000, 0.43863334], 

2178 [0.87726669, 0.75000000, 0.50000000], 

2179 ], 

2180 ], 

2181 [ 

2182 [ 

2183 [1.00000000, 0.00000000, 0.00000000], 

2184 [1.00000000, 0.00000000, 0.26578474], 

2185 [1.00000000, 0.00000000, 0.36466870], 

2186 [1.00000000, 0.00000000, 0.43863334], 

2187 [1.00000000, 0.00000000, 0.50000000], 

2188 ], 

2189 [ 

2190 [1.00000000, 0.39867711, 0.00000000], 

2191 [1.00000000, 0.39867711, 0.26578474], 

2192 [1.00000000, 0.39867711, 0.36466870], 

2193 [1.00000000, 0.39867711, 0.43863334], 

2194 [1.00000000, 0.39867711, 0.50000000], 

2195 ], 

2196 [ 

2197 [1.00000000, 0.54700305, 0.00000000], 

2198 [1.00000000, 0.54700305, 0.26578474], 

2199 [1.00000000, 0.54700305, 0.36466870], 

2200 [1.00000000, 0.54700305, 0.43863334], 

2201 [1.00000000, 0.54700305, 0.50000000], 

2202 ], 

2203 [ 

2204 [1.00000000, 0.65795001, 0.00000000], 

2205 [1.00000000, 0.65795001, 0.26578474], 

2206 [1.00000000, 0.65795001, 0.36466870], 

2207 [1.00000000, 0.65795001, 0.43863334], 

2208 [1.00000000, 0.65795001, 0.50000000], 

2209 ], 

2210 [ 

2211 [1.00000000, 0.75000000, 0.00000000], 

2212 [1.00000000, 0.75000000, 0.26578474], 

2213 [1.00000000, 0.75000000, 0.36466870], 

2214 [1.00000000, 0.75000000, 0.43863334], 

2215 [1.00000000, 0.75000000, 0.50000000], 

2216 ], 

2217 ], 

2218 ] 

2219 ), 

2220 atol=TOLERANCE_ABSOLUTE_TESTS, 

2221 ) 

2222 

2223 # "LUT" 3D to "LUT" 1D. 

2224 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_3, LUT1D)) 

2225 

2226 channel_weights = np.array([1.0, 0.0, 0.0]) 

2227 LUT = LUT_to_LUT( 

2228 self._LUT_3, 

2229 LUT1D, 

2230 force_conversion=True, 

2231 size=16, 

2232 channel_weights=channel_weights, 

2233 ) 

2234 

2235 np.testing.assert_allclose( 

2236 LUT.table, 

2237 np.array( 

2238 [ 

2239 0.00000000, 

2240 0.29202031, 

2241 0.40017033, 

2242 0.48115651, 

2243 0.54837380, 

2244 0.60691337, 

2245 0.65935329, 

2246 0.70721023, 

2247 0.75146458, 

2248 0.79279273, 

2249 0.83168433, 

2250 0.86850710, 

2251 0.90354543, 

2252 0.93702451, 

2253 0.96912624, 

2254 1.00000000, 

2255 ] 

2256 ), 

2257 atol=TOLERANCE_ABSOLUTE_TESTS, 

2258 ) 

2259 

2260 channel_weights = np.array([1 / 3, 1 / 3, 1 / 3]) 

2261 LUT = LUT_to_LUT( 

2262 self._LUT_3, 

2263 LUT1D, 

2264 force_conversion=True, 

2265 size=16, 

2266 channel_weights=channel_weights, 

2267 ) 

2268 

2269 np.testing.assert_allclose( 

2270 LUT.table, 

2271 np.array( 

2272 [ 

2273 0.04562817, 

2274 0.24699999, 

2275 0.40967557, 

2276 0.50401689, 

2277 0.57985117, 

2278 0.64458830, 

2279 0.70250077, 

2280 0.75476586, 

2281 0.80317708, 

2282 0.83944710, 

2283 0.86337188, 

2284 0.88622285, 

2285 0.90786039, 

2286 0.92160338, 

2287 0.92992641, 

2288 0.93781796, 

2289 ] 

2290 ), 

2291 atol=TOLERANCE_ABSOLUTE_TESTS, 

2292 ) 

2293 

2294 # "LUT" 3D to "LUT" 3x1D. 

2295 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_3, LUT3x1D)) 

2296 

2297 LUT = LUT_to_LUT(self._LUT_3, LUT3x1D, force_conversion=True, size=16) 

2298 

2299 np.testing.assert_allclose( 

2300 LUT.table, 

2301 np.array( 

2302 [ 

2303 [0.00000000, 0.00000000, 0.00000000], 

2304 [0.29202031, 0.29202031, 0.29202031], 

2305 [0.40017033, 0.40017033, 0.40017033], 

2306 [0.48115651, 0.48115651, 0.48115651], 

2307 [0.54837380, 0.54837380, 0.54837380], 

2308 [0.60691337, 0.60691337, 0.60691337], 

2309 [0.65935329, 0.65935329, 0.65935329], 

2310 [0.70721023, 0.70721023, 0.70721023], 

2311 [0.75146458, 0.75146458, 0.75146458], 

2312 [0.79279273, 0.79279273, 0.79279273], 

2313 [0.83168433, 0.83168433, 0.83168433], 

2314 [0.86850710, 0.86850710, 0.86850710], 

2315 [0.90354543, 0.90354543, 0.90354543], 

2316 [0.93702451, 0.93702451, 0.93702451], 

2317 [0.96912624, 0.96912624, 0.96912624], 

2318 [1.00000000, 1.00000000, 1.00000000], 

2319 ] 

2320 ), 

2321 atol=TOLERANCE_ABSOLUTE_TESTS, 

2322 ) 

2323 

2324 # "LUT" 3D to "LUT" 3D. 

2325 LUT = LUT_to_LUT(self._LUT_3, LUT3D) 

2326 

2327 assert LUT == self._LUT_3