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
« 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."""
3from __future__ import annotations
5import os
6import textwrap
7import typing
9import numpy as np
10import pytest
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
22if typing.TYPE_CHECKING:
23 from colour.hints import NDArrayFloat
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
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"
36__all__ = [
37 "ROOT_RESOURCES",
38 "RANDOM_TRIPLETS",
39 "AbstractTestLUT",
40 "TestLUT1D",
41 "TestLUT3x1D",
42 "TestLUT3D",
43]
45ROOT_RESOURCES: str = os.path.join(os.path.dirname(__file__), "resources")
47RANDOM_TRIPLETS: NDArrayFloat = np.reshape(
48 random_triplet_generator(8, random_state=np.random.RandomState(4)),
49 (4, 2, 3),
50)
53class AbstractTestLUT:
54 """Define :class:`colour.io.luts.lut.AbstractLUT` class unit tests methods."""
56 def test_required_attributes(self) -> None:
57 """Test the presence of required attributes."""
59 required_attributes = (
60 "table",
61 "name",
62 "dimensions",
63 "domain",
64 "size",
65 "comments",
66 )
68 for attribute in required_attributes:
69 assert attribute in dir(AbstractLUT)
71 def test_required_methods(self) -> None:
72 """Test the presence of required methods."""
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 )
99 for method in required_methods:
100 assert method in dir(AbstractLUT)
103class TestLUT1D:
104 """Define :class:`colour.io.luts.lut.LUT1D` class unit tests methods."""
106 def setup_method(self) -> None:
107 """Initialise the common tests attributes."""
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 ------------
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
232 def test_required_methods(self) -> None:
233 """Test the presence of required methods."""
235 required_methods = (
236 "__init__",
237 "is_domain_explicit",
238 "linear_table",
239 "invert",
240 "apply",
241 "convert",
242 )
244 for method in required_methods:
245 assert method in dir(LUT1D)
247 def test__init__(self) -> None:
248 """
249 Test :class:`colour.io.luts.lut.LUT1D.__init__` method.
250 """
252 LUT = LUT1D(self._table_1)
254 np.testing.assert_allclose(
255 LUT.table, self._table_1, atol=TOLERANCE_ABSOLUTE_TESTS
256 )
258 assert str(id(LUT)) == LUT.name
260 np.testing.assert_array_equal(LUT.domain, self._domain_1)
262 assert LUT.dimensions == self._dimensions
264 assert isinstance(LUT1D(self._table_3, domain=self._domain_3), LUT1D)
266 def test_table(self) -> None:
267 """
268 Test :class:`colour.io.luts.lut.LUT1D.table` property.
269 """
271 LUT = LUT1D()
273 np.testing.assert_array_equal(LUT.table, LUT.linear_table(self._size))
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)
279 def test_name(self) -> None:
280 """
281 Test :class:`colour.io.luts.lut.LUT1D.name` property.
282 """
284 LUT = LUT1D(self._table_1)
286 assert LUT.name == str(id(LUT))
288 LUT = LUT1D()
290 assert LUT.name == f"Unity {self._table_1.shape[0]}"
292 def test_domain(self) -> None:
293 """
294 Test :class:`colour.io.luts.lut.LUT1D.domain` property.
295 """
297 LUT = LUT1D()
299 np.testing.assert_array_equal(LUT.domain, self._domain_1)
301 domain = self._domain_1 * 0.8 + 0.1
302 LUT.domain = domain
303 np.testing.assert_array_equal(LUT.domain, domain)
305 def test_size(self) -> None:
306 """
307 Test :class:`colour.io.luts.lut.LUT1D.size` property.
308 """
310 LUT = LUT1D()
312 assert LUT.size == LUT.table.shape[0]
314 def test_dimensions(self) -> None:
315 """
316 Test :class:`colour.io.luts.lut.LUT1D.dimensions` property.
317 """
319 LUT = LUT1D()
321 assert LUT.dimensions == self._dimensions
323 def test_comments(self) -> None:
324 """
325 Test :class:`colour.io.luts.lut.LUT1D.comments` property.
326 """
328 LUT = LUT1D()
329 assert LUT.comments == []
331 comments = ["A first comment.", "A second comment."]
332 LUT = LUT1D(comments=comments)
334 assert LUT.comments == comments
336 def test__str__(self) -> None:
337 """
338 Test :class:`colour.io.luts.lut.LUT1D.__str__` property.
339 """
341 LUT = LUT1D(name="Nemo")
343 assert str(LUT) == self._str
345 def test__repr__(self) -> None:
346 """
347 Test :class:`colour.io.luts.lut.LUT1D.__repr__` method.
348 """
350 LUT = LUT1D(name="Nemo", comments=["A first comment.", "A second comment."])
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
359 assert repr(LUT) == self._repr # pragma: no cover
361 def test__eq__(self) -> None:
362 """
363 Test :class:`colour.io.luts.lut.LUT1D.__eq__` method.
364 """
366 LUT_1 = LUT1D()
367 LUT_2 = LUT1D()
369 assert LUT_1 == LUT_2
371 def test__ne__(self) -> None:
372 """
373 Test :class:`colour.io.luts.lut.LUT1D.__ne__` method.
374 """
376 LUT_1 = LUT1D()
377 LUT_2 = LUT1D()
379 LUT_2 += 0.1
380 assert LUT_1 != LUT_2
382 LUT_2 = LUT1D()
383 LUT_2.domain = self._domain_1 * 0.8 + 0.1
384 assert LUT_1 != LUT_2
386 def test_is_domain_explicit(self) -> None:
387 """
388 Test :class:`colour.io.luts.lut.LUT1D.is_domain_explicit` method.
389 """
391 assert not LUT1D().is_domain_explicit()
393 assert LUT1D(self._table_3, domain=self._domain_3).is_domain_explicit()
395 def test_arithmetical_operation(self) -> None:
396 """
397 Test :class:`colour.io.luts.lut.LUT1D.arithmetical_operation` method.
398 """
400 LUT_1 = LUT1D()
401 LUT_2 = LUT1D()
403 np.testing.assert_allclose(
404 LUT_1.arithmetical_operation(10, "+", False).table,
405 self._table_1 + 10,
406 atol=TOLERANCE_ABSOLUTE_TESTS,
407 )
409 np.testing.assert_allclose(
410 LUT_1.arithmetical_operation(10, "-", False).table,
411 self._table_1 - 10,
412 atol=TOLERANCE_ABSOLUTE_TESTS,
413 )
415 np.testing.assert_allclose(
416 LUT_1.arithmetical_operation(10, "*", False).table,
417 self._table_1 * 10,
418 atol=TOLERANCE_ABSOLUTE_TESTS,
419 )
421 np.testing.assert_allclose(
422 LUT_1.arithmetical_operation(10, "/", False).table,
423 self._table_1 / 10,
424 atol=TOLERANCE_ABSOLUTE_TESTS,
425 )
427 np.testing.assert_allclose(
428 LUT_1.arithmetical_operation(10, "**", False).table,
429 self._table_1**10,
430 atol=TOLERANCE_ABSOLUTE_TESTS,
431 )
433 np.testing.assert_allclose(
434 (LUT_1 + 10).table,
435 self._table_1 + 10,
436 atol=TOLERANCE_ABSOLUTE_TESTS,
437 )
439 np.testing.assert_allclose(
440 (LUT_1 - 10).table,
441 self._table_1 - 10,
442 atol=TOLERANCE_ABSOLUTE_TESTS,
443 )
445 np.testing.assert_allclose(
446 (LUT_1 * 10).table,
447 self._table_1 * 10,
448 atol=TOLERANCE_ABSOLUTE_TESTS,
449 )
451 np.testing.assert_allclose(
452 (LUT_1 / 10).table,
453 self._table_1 / 10,
454 atol=TOLERANCE_ABSOLUTE_TESTS,
455 )
457 np.testing.assert_allclose(
458 (LUT_1**10).table,
459 self._table_1**10,
460 atol=TOLERANCE_ABSOLUTE_TESTS,
461 )
463 np.testing.assert_allclose(
464 LUT_2.arithmetical_operation(10, "+", True).table,
465 self._table_1 + 10,
466 atol=TOLERANCE_ABSOLUTE_TESTS,
467 )
469 np.testing.assert_allclose(
470 LUT_2.arithmetical_operation(10, "-", True).table,
471 self._table_1,
472 atol=TOLERANCE_ABSOLUTE_TESTS,
473 )
475 np.testing.assert_allclose(
476 LUT_2.arithmetical_operation(10, "*", True).table,
477 self._table_1 * 10,
478 atol=TOLERANCE_ABSOLUTE_TESTS,
479 )
481 np.testing.assert_allclose(
482 LUT_2.arithmetical_operation(10, "/", True).table,
483 self._table_1,
484 atol=TOLERANCE_ABSOLUTE_TESTS,
485 )
487 np.testing.assert_allclose(
488 LUT_2.arithmetical_operation(10, "**", True).table,
489 self._table_1**10,
490 atol=TOLERANCE_ABSOLUTE_TESTS,
491 )
493 LUT_2 = LUT1D()
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 )
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 )
507 def test_linear_table(self) -> None:
508 """
509 Test :class:`colour.io.luts.lut.LUT1D.linear_table` method.
510 """
512 LUT_1 = LUT1D()
514 np.testing.assert_allclose(
515 LUT_1.linear_table(self._size),
516 self._table_1,
517 atol=TOLERANCE_ABSOLUTE_TESTS,
518 )
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 )
526 def test_copy(self) -> None:
527 """
528 Test :class:`colour.io.luts.lut.LUT1D.copy` method.
529 """
531 LUT_1 = LUT1D()
533 assert LUT_1 is not LUT_1.copy()
534 assert LUT_1.copy() == LUT_1
536 def test_invert(self) -> None:
537 """
538 Test :class:`colour.io.luts.lut.LUT1D.invert` method.
539 """
541 LUT_i = LUT1D(self._table_2).invert(
542 interpolator=self._interpolator_1, **self._invert_kwargs_1
543 )
545 np.testing.assert_allclose(
546 LUT_i.apply(RANDOM_TRIPLETS),
547 self._inverted_apply_1,
548 atol=TOLERANCE_ABSOLUTE_TESTS,
549 )
551 LUT_i = LUT1D(self._table_2).invert(
552 interpolator=self._interpolator_2, **self._invert_kwargs_2
553 )
555 np.testing.assert_allclose(
556 LUT_i.apply(RANDOM_TRIPLETS),
557 self._inverted_apply_2,
558 atol=TOLERANCE_ABSOLUTE_TESTS,
559 )
561 LUT_i = LUT1D(self._table_2, domain=self._domain_4)
563 try:
564 LUT_i = LUT_i.invert(
565 interpolator=self._interpolator_2, **self._invert_kwargs_2
566 )
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
576 def test_apply(self) -> None:
577 """
578 Test :class:`colour.io.luts.lut.LUT1D.apply` method.
579 """
581 if not is_scipy_installed(): # pragma: no cover
582 return
584 LUT_1 = LUT1D(self._table_2)
586 np.testing.assert_allclose(
587 LUT_1.apply(RANDOM_TRIPLETS),
588 self._applied_1,
589 atol=TOLERANCE_ABSOLUTE_TESTS,
590 )
592 LUT_2 = LUT1D(domain=self._domain_2)
593 LUT_2.table = spow(LUT_2.table, 1 / 2.2)
595 np.testing.assert_allclose(
596 LUT_2.apply(RANDOM_TRIPLETS),
597 self._applied_2,
598 atol=TOLERANCE_ABSOLUTE_TESTS,
599 )
601 LUT_3 = LUT1D(self._table_3, domain=self._domain_3)
603 np.testing.assert_allclose(
604 LUT_3.apply(RANDOM_TRIPLETS),
605 self._applied_3,
606 atol=TOLERANCE_ABSOLUTE_TESTS,
607 )
609 LUT_4 = LUT1D(self._table_2)
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 )
624class TestLUT3x1D:
625 """Define :class:`colour.io.luts.lut.LUT3x1D` class unit tests methods."""
627 def setup_method(self) -> None:
628 """Initialise the common tests attributes."""
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 --------------
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
784 def test_required_methods(self) -> None:
785 """Test the presence of required methods."""
787 required_methods = (
788 "__init__",
789 "is_domain_explicit",
790 "linear_table",
791 "invert",
792 "apply",
793 "convert",
794 )
796 for method in required_methods:
797 assert method in dir(LUT3x1D)
799 def test__init__(self) -> None:
800 """
801 Test :class:`colour.io.luts.lut.LUT3x1D.__init__` method.
802 """
804 LUT = LUT3x1D(self._table_1)
806 np.testing.assert_allclose(
807 LUT.table, self._table_1, atol=TOLERANCE_ABSOLUTE_TESTS
808 )
810 assert str(id(LUT)) == LUT.name
812 np.testing.assert_array_equal(LUT.domain, self._domain_1)
814 assert LUT.dimensions == self._dimensions
816 assert isinstance(LUT3x1D(self._table_3, domain=self._domain_3), LUT3x1D)
818 def test_table(self) -> None:
819 """
820 Test :class:`colour.io.luts.lut.LUT3x1D.table` property.
821 """
823 LUT = LUT3x1D()
825 np.testing.assert_array_equal(LUT.table, LUT.linear_table(self._size))
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)
831 def test_name(self) -> None:
832 """
833 Test :class:`colour.io.luts.lut.LUT3x1D.name` property.
834 """
836 LUT = LUT3x1D(self._table_1)
838 assert LUT.name == str(id(LUT))
840 LUT = LUT3x1D()
842 assert LUT.name == f"Unity {self._table_1.shape[0]}"
844 def test_domain(self) -> None:
845 """
846 Test :class:`colour.io.luts.lut.LUT3x1D.domain` property.
847 """
849 LUT = LUT3x1D()
851 np.testing.assert_array_equal(LUT.domain, self._domain_1)
853 domain = self._domain_1 * 0.8 + 0.1
854 LUT.domain = domain
855 np.testing.assert_array_equal(LUT.domain, domain)
857 def test_size(self) -> None:
858 """
859 Test :class:`colour.io.luts.lut.LUT3x1D.size` property.
860 """
862 LUT = LUT3x1D()
864 assert LUT.size == LUT.table.shape[0]
866 def test_dimensions(self) -> None:
867 """
868 Test :class:`colour.io.luts.lut.LUT3x1D.dimensions` property.
869 """
871 LUT = LUT3x1D()
873 assert LUT.dimensions == self._dimensions
875 def test_comments(self) -> None:
876 """
877 Test :class:`colour.io.luts.lut.LUT3x1D.comments` property.
878 """
880 LUT = LUT3x1D()
881 assert LUT.comments == []
883 comments = ["A first comment.", "A second comment."]
884 LUT = LUT3x1D(comments=comments)
886 assert LUT.comments == comments
888 def test__str__(self) -> None:
889 """
890 Test :class:`colour.io.luts.lut.LUT3x1D.__str__` property.
891 """
893 LUT = LUT3x1D(name="Nemo")
895 assert str(LUT) == self._str
897 def test__repr__(self) -> None:
898 """
899 Test :class:`colour.io.luts.lut.LUT3x1D.__repr__` method.
900 """
902 LUT = LUT3x1D(name="Nemo", comments=["A first comment.", "A second comment."])
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
911 assert repr(LUT) == self._repr # pragma: no cover
913 def test__eq__(self) -> None:
914 """
915 Test :class:`colour.io.luts.lut.LUT3x1D.__eq__` method.
916 """
918 LUT_1 = LUT3x1D()
919 LUT_2 = LUT3x1D()
921 assert LUT_1 == LUT_2
923 def test__ne__(self) -> None:
924 """
925 Test :class:`colour.io.luts.lut.LUT3x1D.__ne__` method.
926 """
928 LUT_1 = LUT3x1D()
929 LUT_2 = LUT3x1D()
931 LUT_2 += 0.1
932 assert LUT_1 != LUT_2
934 LUT_2 = LUT3x1D()
935 LUT_2.domain = self._domain_1 * 0.8 + 0.1
936 assert LUT_1 != LUT_2
938 def test_is_domain_explicit(self) -> None:
939 """
940 Test :class:`colour.io.luts.lut.LUT3x1D.is_domain_explicit` method.
941 """
943 assert not LUT3x1D().is_domain_explicit()
945 assert LUT3x1D(self._table_3, domain=self._domain_3).is_domain_explicit()
947 def test_arithmetical_operation(self) -> None:
948 """
949 Test :class:`colour.io.luts.lut.LUT3x1D.arithmetical_operation` method.
950 """
952 LUT_1 = LUT3x1D()
953 LUT_2 = LUT3x1D()
955 np.testing.assert_allclose(
956 LUT_1.arithmetical_operation(10, "+", False).table,
957 self._table_1 + 10,
958 atol=TOLERANCE_ABSOLUTE_TESTS,
959 )
961 np.testing.assert_allclose(
962 LUT_1.arithmetical_operation(10, "-", False).table,
963 self._table_1 - 10,
964 atol=TOLERANCE_ABSOLUTE_TESTS,
965 )
967 np.testing.assert_allclose(
968 LUT_1.arithmetical_operation(10, "*", False).table,
969 self._table_1 * 10,
970 atol=TOLERANCE_ABSOLUTE_TESTS,
971 )
973 np.testing.assert_allclose(
974 LUT_1.arithmetical_operation(10, "/", False).table,
975 self._table_1 / 10,
976 atol=TOLERANCE_ABSOLUTE_TESTS,
977 )
979 np.testing.assert_allclose(
980 LUT_1.arithmetical_operation(10, "**", False).table,
981 self._table_1**10,
982 atol=TOLERANCE_ABSOLUTE_TESTS,
983 )
985 np.testing.assert_allclose(
986 (LUT_1 + 10).table,
987 self._table_1 + 10,
988 atol=TOLERANCE_ABSOLUTE_TESTS,
989 )
991 np.testing.assert_allclose(
992 (LUT_1 - 10).table,
993 self._table_1 - 10,
994 atol=TOLERANCE_ABSOLUTE_TESTS,
995 )
997 np.testing.assert_allclose(
998 (LUT_1 * 10).table,
999 self._table_1 * 10,
1000 atol=TOLERANCE_ABSOLUTE_TESTS,
1001 )
1003 np.testing.assert_allclose(
1004 (LUT_1 / 10).table,
1005 self._table_1 / 10,
1006 atol=TOLERANCE_ABSOLUTE_TESTS,
1007 )
1009 np.testing.assert_allclose(
1010 (LUT_1**10).table,
1011 self._table_1**10,
1012 atol=TOLERANCE_ABSOLUTE_TESTS,
1013 )
1015 np.testing.assert_allclose(
1016 LUT_2.arithmetical_operation(10, "+", True).table,
1017 self._table_1 + 10,
1018 atol=TOLERANCE_ABSOLUTE_TESTS,
1019 )
1021 np.testing.assert_allclose(
1022 LUT_2.arithmetical_operation(10, "-", True).table,
1023 self._table_1,
1024 atol=TOLERANCE_ABSOLUTE_TESTS,
1025 )
1027 np.testing.assert_allclose(
1028 LUT_2.arithmetical_operation(10, "*", True).table,
1029 self._table_1 * 10,
1030 atol=TOLERANCE_ABSOLUTE_TESTS,
1031 )
1033 np.testing.assert_allclose(
1034 LUT_2.arithmetical_operation(10, "/", True).table,
1035 self._table_1,
1036 atol=TOLERANCE_ABSOLUTE_TESTS,
1037 )
1039 np.testing.assert_allclose(
1040 LUT_2.arithmetical_operation(10, "**", True).table,
1041 self._table_1**10,
1042 atol=TOLERANCE_ABSOLUTE_TESTS,
1043 )
1045 LUT_2 = LUT3x1D()
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 )
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 )
1059 def test_linear_table(self) -> None:
1060 """
1061 Test :class:`colour.io.luts.lut.LUT3x1D.linear_table` method.
1062 """
1064 LUT_1 = LUT3x1D()
1066 np.testing.assert_allclose(
1067 LUT_1.linear_table(self._size),
1068 self._table_1,
1069 atol=TOLERANCE_ABSOLUTE_TESTS,
1070 )
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 )
1078 def test_copy(self) -> None:
1079 """
1080 Test :class:`colour.io.luts.lut.LUT3x1D.copy` method.
1081 """
1083 LUT_1 = LUT3x1D()
1085 assert LUT_1 is not LUT_1.copy()
1086 assert LUT_1.copy() == LUT_1
1088 def test_invert(self) -> None:
1089 """
1090 Test :class:`colour.io.luts.lut.LUT3x1D.invert` method.
1091 """
1093 LUT_i = LUT3x1D(self._table_2).invert(
1094 interpolator=self._interpolator_1, **self._invert_kwargs_1
1095 )
1097 np.testing.assert_allclose(
1098 LUT_i.apply(RANDOM_TRIPLETS),
1099 self._inverted_apply_1,
1100 atol=TOLERANCE_ABSOLUTE_TESTS,
1101 )
1103 LUT_i = LUT3x1D(self._table_2).invert(
1104 interpolator=self._interpolator_2, **self._invert_kwargs_2
1105 )
1107 np.testing.assert_allclose(
1108 LUT_i.apply(RANDOM_TRIPLETS),
1109 self._inverted_apply_2,
1110 atol=TOLERANCE_ABSOLUTE_TESTS,
1111 )
1113 LUT_i = LUT3x1D(self._table_2, domain=self._domain_4)
1115 try:
1116 LUT_i = LUT_i.invert(
1117 interpolator=self._interpolator_2, **self._invert_kwargs_2
1118 )
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
1128 def test_apply(self) -> None:
1129 """
1130 Test :class:`colour.io.luts.lut.LUT3x1D.apply` method.
1131 """
1133 if not is_scipy_installed(): # pragma: no cover
1134 return
1136 LUT_1 = LUT3x1D(self._table_2)
1138 np.testing.assert_allclose(
1139 LUT_1.apply(RANDOM_TRIPLETS),
1140 self._applied_1,
1141 atol=TOLERANCE_ABSOLUTE_TESTS,
1142 )
1144 LUT_2 = LUT3x1D(domain=self._domain_2)
1145 LUT_2.table = spow(LUT_2.table, 1 / 2.2)
1147 np.testing.assert_allclose(
1148 LUT_2.apply(RANDOM_TRIPLETS),
1149 self._applied_2,
1150 atol=TOLERANCE_ABSOLUTE_TESTS,
1151 )
1153 LUT_3 = LUT3x1D(self._table_3, domain=self._domain_3)
1155 np.testing.assert_allclose(
1156 LUT_3.apply(RANDOM_TRIPLETS),
1157 self._applied_3,
1158 atol=TOLERANCE_ABSOLUTE_TESTS,
1159 )
1161 LUT_4 = LUT3x1D(self._table_2)
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 )
1176class TestLUT3D:
1177 """Define :class:`colour.io.luts.lut.LUT3D` class unit tests methods."""
1179 def setup_method(self) -> None:
1180 """Initialise the common tests attributes."""
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 ------------
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 )
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 )
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
1363 def test_required_methods(self) -> None:
1364 """Test the presence of required methods."""
1366 required_methods = (
1367 "__init__",
1368 "is_domain_explicit",
1369 "linear_table",
1370 "invert",
1371 "apply",
1372 "convert",
1373 )
1375 for method in required_methods:
1376 assert method in dir(LUT3D)
1378 def test__init__(self) -> None:
1379 """
1380 Test :class:`colour.io.luts.lut.LUT3D.__init__` method.
1381 """
1383 LUT = LUT3D(self._table_1)
1385 np.testing.assert_allclose(
1386 LUT.table, self._table_1, atol=TOLERANCE_ABSOLUTE_TESTS
1387 )
1389 assert str(id(LUT)) == LUT.name
1391 np.testing.assert_array_equal(LUT.domain, self._domain_1)
1393 assert LUT.dimensions == self._dimensions
1395 assert isinstance(LUT3D(self._table_3, domain=self._domain_3), LUT3D)
1397 def test_table(self) -> None:
1398 """
1399 Test :class:`colour.io.luts.lut.LUT3D.table` property.
1400 """
1402 LUT = LUT3D()
1404 np.testing.assert_array_equal(LUT.table, LUT.linear_table(self._size))
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)
1410 def test_name(self) -> None:
1411 """
1412 Test :class:`colour.io.luts.lut.LUT3D.name` property.
1413 """
1415 LUT = LUT3D(self._table_1)
1417 assert LUT.name == str(id(LUT))
1419 LUT = LUT3D()
1421 assert LUT.name == f"Unity {self._table_1.shape[0]}"
1423 def test_domain(self) -> None:
1424 """
1425 Test :class:`colour.io.luts.lut.LUT3D.domain` property.
1426 """
1428 LUT = LUT3D()
1430 np.testing.assert_array_equal(LUT.domain, self._domain_1)
1432 domain = self._domain_1 * 0.8 + 0.1
1433 LUT.domain = domain
1434 np.testing.assert_array_equal(LUT.domain, domain)
1436 def test_size(self) -> None:
1437 """
1438 Test :class:`colour.io.luts.lut.LUT3D.size` property.
1439 """
1441 LUT = LUT3D()
1443 assert LUT.size == LUT.table.shape[0]
1445 def test_dimensions(self) -> None:
1446 """
1447 Test :class:`colour.io.luts.lut.LUT3D.dimensions` property.
1448 """
1450 LUT = LUT3D()
1452 assert LUT.dimensions == self._dimensions
1454 def test_comments(self) -> None:
1455 """
1456 Test :class:`colour.io.luts.lut.LUT3D.comments` property.
1457 """
1459 LUT = LUT3D()
1460 assert LUT.comments == []
1462 comments = ["A first comment.", "A second comment."]
1463 LUT = LUT3D(comments=comments)
1465 assert LUT.comments == comments
1467 def test__str__(self) -> None:
1468 """
1469 Test :class:`colour.io.luts.lut.LUT3D.__str__` property.
1470 """
1472 LUT = LUT3D(name="Nemo")
1474 assert str(LUT) == self._str
1476 def test__repr__(self) -> None:
1477 """
1478 Test :class:`colour.io.luts.lut.LUT3D.__repr__` method.
1479 """
1481 LUT = LUT3D(name="Nemo", comments=["A first comment.", "A second comment."])
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
1490 assert repr(LUT) == self._repr # pragma: no cover
1492 def test__eq__(self) -> None:
1493 """
1494 Test :class:`colour.io.luts.lut.LUT3D.__eq__` method.
1495 """
1497 LUT_1 = LUT3D()
1498 LUT_2 = LUT3D()
1500 assert LUT_1 == LUT_2
1502 def test__ne__(self) -> None:
1503 """
1504 Test :class:`colour.io.luts.lut.LUT3D.__ne__` method.
1505 """
1507 LUT_1 = LUT3D()
1508 LUT_2 = LUT3D()
1510 LUT_2 += 0.1
1511 assert LUT_1 != LUT_2
1513 LUT_2 = LUT3D()
1514 LUT_2.domain = self._domain_1 * 0.8 + 0.1
1515 assert LUT_1 != LUT_2
1517 def test_is_domain_explicit(self) -> None:
1518 """
1519 Test :class:`colour.io.luts.lut.LUT3D.is_domain_explicit` method.
1520 """
1522 assert not LUT3D().is_domain_explicit()
1524 assert LUT3D(self._table_3, domain=self._domain_3).is_domain_explicit()
1526 def test_arithmetical_operation(self) -> None:
1527 """
1528 Test :class:`colour.io.luts.lut.LUT3D.arithmetical_operation` method.
1529 """
1531 LUT_1 = LUT3D()
1532 LUT_2 = LUT3D()
1534 np.testing.assert_allclose(
1535 LUT_1.arithmetical_operation(10, "+", False).table,
1536 self._table_1 + 10,
1537 atol=TOLERANCE_ABSOLUTE_TESTS,
1538 )
1540 np.testing.assert_allclose(
1541 LUT_1.arithmetical_operation(10, "-", False).table,
1542 self._table_1 - 10,
1543 atol=TOLERANCE_ABSOLUTE_TESTS,
1544 )
1546 np.testing.assert_allclose(
1547 LUT_1.arithmetical_operation(10, "*", False).table,
1548 self._table_1 * 10,
1549 atol=TOLERANCE_ABSOLUTE_TESTS,
1550 )
1552 np.testing.assert_allclose(
1553 LUT_1.arithmetical_operation(10, "/", False).table,
1554 self._table_1 / 10,
1555 atol=TOLERANCE_ABSOLUTE_TESTS,
1556 )
1558 np.testing.assert_allclose(
1559 LUT_1.arithmetical_operation(10, "**", False).table,
1560 self._table_1**10,
1561 atol=TOLERANCE_ABSOLUTE_TESTS,
1562 )
1564 np.testing.assert_allclose(
1565 (LUT_1 + 10).table,
1566 self._table_1 + 10,
1567 atol=TOLERANCE_ABSOLUTE_TESTS,
1568 )
1570 np.testing.assert_allclose(
1571 (LUT_1 - 10).table,
1572 self._table_1 - 10,
1573 atol=TOLERANCE_ABSOLUTE_TESTS,
1574 )
1576 np.testing.assert_allclose(
1577 (LUT_1 * 10).table,
1578 self._table_1 * 10,
1579 atol=TOLERANCE_ABSOLUTE_TESTS,
1580 )
1582 np.testing.assert_allclose(
1583 (LUT_1 / 10).table,
1584 self._table_1 / 10,
1585 atol=TOLERANCE_ABSOLUTE_TESTS,
1586 )
1588 np.testing.assert_allclose(
1589 (LUT_1**10).table,
1590 self._table_1**10,
1591 atol=TOLERANCE_ABSOLUTE_TESTS,
1592 )
1594 np.testing.assert_allclose(
1595 LUT_2.arithmetical_operation(10, "+", True).table,
1596 self._table_1 + 10,
1597 atol=TOLERANCE_ABSOLUTE_TESTS,
1598 )
1600 np.testing.assert_allclose(
1601 LUT_2.arithmetical_operation(10, "-", True).table,
1602 self._table_1,
1603 atol=TOLERANCE_ABSOLUTE_TESTS,
1604 )
1606 np.testing.assert_allclose(
1607 LUT_2.arithmetical_operation(10, "*", True).table,
1608 self._table_1 * 10,
1609 atol=TOLERANCE_ABSOLUTE_TESTS,
1610 )
1612 np.testing.assert_allclose(
1613 LUT_2.arithmetical_operation(10, "/", True).table,
1614 self._table_1,
1615 atol=TOLERANCE_ABSOLUTE_TESTS,
1616 )
1618 np.testing.assert_allclose(
1619 LUT_2.arithmetical_operation(10, "**", True).table,
1620 self._table_1**10,
1621 atol=TOLERANCE_ABSOLUTE_TESTS,
1622 )
1624 LUT_2 = LUT3D()
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 )
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 )
1638 def test_linear_table(self) -> None:
1639 """
1640 Test :class:`colour.io.luts.lut.LUT3D.linear_table` method.
1641 """
1643 LUT_1 = LUT3D()
1645 np.testing.assert_allclose(
1646 LUT_1.linear_table(self._size),
1647 self._table_1,
1648 atol=TOLERANCE_ABSOLUTE_TESTS,
1649 )
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 )
1657 def test_copy(self) -> None:
1658 """
1659 Test :class:`colour.io.luts.lut.LUT3D.copy` method.
1660 """
1662 LUT_1 = LUT3D()
1664 assert LUT_1 is not LUT_1.copy()
1665 assert LUT_1.copy() == LUT_1
1667 def test_invert(self) -> None:
1668 """
1669 Test :class:`colour.io.luts.lut.LUT3D.invert` method.
1670 """
1672 if not is_scipy_installed(): # pragma: no cover
1673 return
1675 LUT_i = LUT3D(self._table_2).invert(
1676 interpolator=self._interpolator_1, **self._invert_kwargs_1
1677 )
1679 np.testing.assert_allclose(
1680 LUT_i.apply(RANDOM_TRIPLETS),
1681 self._inverted_apply_1,
1682 atol=TOLERANCE_ABSOLUTE_TESTS,
1683 )
1685 LUT_i = LUT3D(self._table_2).invert(
1686 interpolator=self._interpolator_2, **self._invert_kwargs_2
1687 )
1689 np.testing.assert_allclose(
1690 LUT_i.apply(RANDOM_TRIPLETS),
1691 self._inverted_apply_2,
1692 atol=TOLERANCE_ABSOLUTE_TESTS,
1693 )
1695 LUT_i = LUT3D(self._table_2, domain=self._domain_4)
1697 try:
1698 LUT_i = LUT_i.invert(
1699 interpolator=self._interpolator_2, **self._invert_kwargs_2
1700 )
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
1710 def test_apply(self) -> None:
1711 """
1712 Test :class:`colour.io.luts.lut.LUT3D.apply` method.
1713 """
1715 if not is_scipy_installed(): # pragma: no cover
1716 return
1718 LUT_1 = LUT3D(self._table_2)
1720 np.testing.assert_allclose(
1721 LUT_1.apply(RANDOM_TRIPLETS),
1722 self._applied_1,
1723 atol=TOLERANCE_ABSOLUTE_TESTS,
1724 )
1726 LUT_2 = LUT3D(domain=self._domain_2)
1727 LUT_2.table = spow(LUT_2.table, 1 / 2.2)
1729 np.testing.assert_allclose(
1730 LUT_2.apply(RANDOM_TRIPLETS),
1731 self._applied_2,
1732 atol=TOLERANCE_ABSOLUTE_TESTS,
1733 )
1735 LUT_3 = LUT3D(self._table_3, domain=self._domain_3)
1737 np.testing.assert_allclose(
1738 LUT_3.apply(RANDOM_TRIPLETS),
1739 self._applied_3,
1740 atol=TOLERANCE_ABSOLUTE_TESTS,
1741 )
1743 LUT_4 = LUT3D(self._table_2)
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 )
1758class TestLUT_to_LUT:
1759 """
1760 Define :func:`colour.io.luts.lut.LUT_to_LUT` definition unit tests
1761 methods.
1762 """
1764 def setup_method(self) -> None:
1765 """Initialise the common tests attributes."""
1767 self._domain = np.array([[0.0, -0.1, -0.2], [1.0, 1.5, 3.0]])
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)
1776 def test_LUT_to_LUT(self) -> None:
1777 """Test :func:`colour.io.luts.lut.LUT_to_LUT` definition."""
1779 # "LUT" 1D to "LUT" 1D.
1780 LUT = LUT_to_LUT(self._LUT_1, LUT1D)
1782 assert LUT == self._LUT_1
1784 # "LUT" 1D to "LUT" 3x1D.
1785 LUT = LUT_to_LUT(self._LUT_1, LUT3x1D)
1786 table = LUT1D.linear_table(16) ** (1 / 2.2)
1788 assert LUT3x1D(tstack([table, table, table])) == LUT
1790 # "LUT" 1D to "LUT" 3D.
1791 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_1, LUT3D))
1793 LUT = LUT_to_LUT(self._LUT_1, LUT3D, force_conversion=True, size=5)
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 )
1989 # "LUT" 3x1D to "LUT" 1D.
1990 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_2, LUT1D))
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 )
2000 channel_weights = np.array([1 / 3, 1 / 3, 1 / 3])
2002 domain = np.sum(self._domain * channel_weights, axis=-1)
2004 LUT = LUT_to_LUT(
2005 self._LUT_2,
2006 LUT1D,
2007 force_conversion=True,
2008 channel_weights=channel_weights,
2009 )
2011 assert (
2012 LUT1D(
2013 np.sum(self._LUT_2.table * channel_weights, axis=-1),
2014 domain=domain,
2015 )
2016 == LUT
2017 )
2019 # "LUT" 3x1D to "LUT" 3x1D.
2020 LUT = LUT_to_LUT(self._LUT_2, LUT3x1D)
2022 assert LUT == self._LUT_2
2024 # "LUT" 3x1D to "LUT" 3D.
2025 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_2, LUT3D))
2027 LUT = LUT_to_LUT(self._LUT_2, LUT3D, force_conversion=True, size=5)
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 )
2223 # "LUT" 3D to "LUT" 1D.
2224 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_3, LUT1D))
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 )
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 )
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 )
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 )
2294 # "LUT" 3D to "LUT" 3x1D.
2295 pytest.raises(ValueError, lambda: LUT_to_LUT(self._LUT_3, LUT3x1D))
2297 LUT = LUT_to_LUT(self._LUT_3, LUT3x1D, force_conversion=True, size=16)
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 )
2324 # "LUT" 3D to "LUT" 3D.
2325 LUT = LUT_to_LUT(self._LUT_3, LUT3D)
2327 assert LUT == self._LUT_3