Pillow/Tests/test_color_lut.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

654 lines
23 KiB
Python
Raw Normal View History

from __future__ import annotations
2024-01-20 14:23:03 +03:00
2018-04-12 11:54:54 +03:00
from array import array
2024-02-12 13:06:17 +03:00
from types import ModuleType
2018-04-12 11:54:54 +03:00
import pytest
2018-03-29 16:37:35 +03:00
from PIL import Image, ImageFilter
2020-02-23 00:03:01 +03:00
from .helper import assert_image_equal
2018-03-26 19:30:00 +03:00
2024-02-12 13:06:17 +03:00
numpy: ModuleType | None
2018-04-14 19:17:15 +03:00
try:
import numpy
except ImportError:
numpy = None
2018-03-26 19:30:00 +03:00
2020-02-23 00:03:01 +03:00
class TestColorLut3DCoreAPI:
2024-02-07 11:16:28 +03:00
def generate_identity_table(
self, channels: int, size: int | tuple[int, int, int]
) -> tuple[int, int, int, int, list[float]]:
2018-03-26 19:30:00 +03:00
if isinstance(size, tuple):
size_1d, size_2d, size_3d = size
2018-03-26 19:30:00 +03:00
else:
size_1d, size_2d, size_3d = (size, size, size)
2018-03-26 19:30:00 +03:00
table = [
[
r / (size_1d - 1) if size_1d != 1 else 0,
g / (size_2d - 1) if size_2d != 1 else 0,
b / (size_3d - 1) if size_3d != 1 else 0,
r / (size_1d - 1) if size_1d != 1 else 0,
g / (size_2d - 1) if size_2d != 1 else 0,
2018-03-26 19:30:00 +03:00
][:channels]
for b in range(size_3d)
for g in range(size_2d)
for r in range(size_1d)
2018-03-26 19:30:00 +03:00
]
return (
2019-06-13 18:53:42 +03:00
channels,
size_1d,
size_2d,
size_3d,
2019-06-13 18:53:42 +03:00
[item for sublist in table for item in sublist],
)
2018-03-26 19:30:00 +03:00
def test_wrong_args(self) -> None:
2019-06-13 18:53:42 +03:00
im = Image.new("RGB", (10, 10), 0)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="filter"):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BICUBIC, *self.generate_identity_table(3, 3)
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="image mode"):
2019-06-13 18:53:42 +03:00
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"wrong", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="table_channels"):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(5, 3)
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="table_channels"):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(1, 3)
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="table_channels"):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(2, 3)
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="Table size"):
2019-06-13 18:53:42 +03:00
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, (1, 3, 3)),
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="Table size"):
2019-06-13 18:53:42 +03:00
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, (66, 3, 3)),
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match=r"size1D \* size2D \* size3D"):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, 0] * 7
)
2018-03-26 22:29:50 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match=r"size1D \* size2D \* size3D"):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, 0] * 9
)
2018-03-26 22:29:50 +03:00
with pytest.raises(TypeError):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, "0"] * 8
)
with pytest.raises(TypeError):
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d("RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, 16)
2018-04-14 17:47:53 +03:00
def test_correct_args(self) -> None:
2019-06-13 18:53:42 +03:00
im = Image.new("RGB", (10, 10), 0)
2018-03-26 19:30:00 +03:00
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
)
2018-03-26 19:30:00 +03:00
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"CMYK", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
)
2018-03-26 19:30:00 +03:00
2019-06-13 18:53:42 +03:00
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, (2, 3, 3)),
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
2019-06-13 18:53:42 +03:00
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, (65, 3, 3)),
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
2019-06-13 18:53:42 +03:00
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, (3, 65, 3)),
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
2019-06-13 18:53:42 +03:00
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, (3, 3, 65)),
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
def test_wrong_mode(self) -> None:
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="wrong mode"):
2019-06-13 18:53:42 +03:00
im = Image.new("L", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="wrong mode"):
2019-06-13 18:53:42 +03:00
im = Image.new("RGB", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="wrong mode"):
2019-06-13 18:53:42 +03:00
im = Image.new("L", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="wrong mode"):
2019-06-13 18:53:42 +03:00
im = Image.new("RGB", (10, 10), 0)
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
2019-06-13 18:53:42 +03:00
)
2018-03-26 19:30:00 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="wrong mode"):
2019-06-13 18:53:42 +03:00
im = Image.new("RGB", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
)
2018-03-26 19:30:00 +03:00
def test_correct_mode(self) -> None:
2019-06-13 18:53:42 +03:00
im = Image.new("RGBA", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
)
2018-03-26 19:30:00 +03:00
2019-06-13 18:53:42 +03:00
im = Image.new("RGBA", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
)
2018-03-26 19:30:00 +03:00
2019-06-13 18:53:42 +03:00
im = Image.new("RGB", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"HSV", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
)
2018-03-26 19:30:00 +03:00
2019-06-13 18:53:42 +03:00
im = Image.new("RGB", (10, 10), 0)
2022-01-15 01:02:31 +03:00
im.im.color_lut_3d(
"RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
)
2018-03-26 19:30:00 +03:00
def test_identities(self) -> None:
2019-06-13 18:53:42 +03:00
g = Image.linear_gradient("L")
im = Image.merge(
2022-01-15 01:02:31 +03:00
"RGB",
[
g,
g.transpose(Image.Transpose.ROTATE_90),
g.transpose(Image.Transpose.ROTATE_180),
],
2019-06-13 18:53:42 +03:00
)
2018-03-26 22:29:50 +03:00
# Fast test with small cubes
for size in [2, 3, 5, 7, 11, 16, 17]:
assert_image_equal(
2019-06-13 18:53:42 +03:00
im,
im._new(
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, size),
2019-06-13 18:53:42 +03:00
)
),
)
2018-03-26 22:29:50 +03:00
# Not so fast
assert_image_equal(
2019-06-13 18:53:42 +03:00
im,
im._new(
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGB",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, (2, 2, 65)),
2019-06-13 18:53:42 +03:00
)
),
)
2018-03-26 22:29:50 +03:00
def test_identities_4_channels(self) -> None:
2019-06-13 18:53:42 +03:00
g = Image.linear_gradient("L")
im = Image.merge(
2022-01-15 01:02:31 +03:00
"RGB",
[
g,
g.transpose(Image.Transpose.ROTATE_90),
g.transpose(Image.Transpose.ROTATE_180),
],
2019-06-13 18:53:42 +03:00
)
# Red channel copied to alpha
assert_image_equal(
2019-06-13 18:53:42 +03:00
Image.merge("RGBA", (im.split() * 2)[:4]),
im._new(
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGBA",
Image.Resampling.BILINEAR,
*self.generate_identity_table(4, 17),
2019-06-13 18:53:42 +03:00
)
),
)
def test_copy_alpha_channel(self) -> None:
2019-06-13 18:53:42 +03:00
g = Image.linear_gradient("L")
im = Image.merge(
"RGBA",
[
g,
2022-01-15 01:02:31 +03:00
g.transpose(Image.Transpose.ROTATE_90),
g.transpose(Image.Transpose.ROTATE_180),
g.transpose(Image.Transpose.ROTATE_270),
2019-06-13 18:53:42 +03:00
],
)
assert_image_equal(
2019-06-13 18:53:42 +03:00
im,
im._new(
im.im.color_lut_3d(
2022-01-15 01:02:31 +03:00
"RGBA",
Image.Resampling.BILINEAR,
*self.generate_identity_table(3, 17),
2019-06-13 18:53:42 +03:00
)
),
)
def test_channels_order(self) -> None:
2019-06-13 18:53:42 +03:00
g = Image.linear_gradient("L")
im = Image.merge(
2022-01-15 01:02:31 +03:00
"RGB",
[
g,
g.transpose(Image.Transpose.ROTATE_90),
g.transpose(Image.Transpose.ROTATE_180),
],
2019-06-13 18:53:42 +03:00
)
2018-03-26 22:29:50 +03:00
# Reverse channels by splitting and using table
2019-06-13 18:53:42 +03:00
# fmt: off
assert_image_equal(
2018-03-26 22:29:50 +03:00
Image.merge('RGB', im.split()[::-1]),
2022-01-15 01:02:31 +03:00
im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR,
3, 2, 2, 2, [
0, 0, 0, 0, 0, 1,
0, 1, 0, 0, 1, 1,
2018-03-26 22:29:50 +03:00
1, 0, 0, 1, 0, 1,
1, 1, 0, 1, 1, 1,
])))
2019-06-13 18:53:42 +03:00
# fmt: on
2018-03-26 22:29:50 +03:00
def test_overflow(self) -> None:
2019-06-13 18:53:42 +03:00
g = Image.linear_gradient("L")
im = Image.merge(
2022-01-15 01:02:31 +03:00
"RGB",
[
g,
g.transpose(Image.Transpose.ROTATE_90),
g.transpose(Image.Transpose.ROTATE_180),
],
2019-06-13 18:53:42 +03:00
)
2018-03-26 22:29:50 +03:00
2019-06-13 18:53:42 +03:00
# fmt: off
2022-01-15 01:02:31 +03:00
transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR,
3, 2, 2, 2,
[
-1, -1, -1, 2, -1, -1,
-1, 2, -1, 2, 2, -1,
-1, -1, 2, 2, -1, 2,
-1, 2, 2, 2, 2, 2,
])).load()
2019-06-13 18:53:42 +03:00
# fmt: on
assert transformed[0, 0] == (0, 0, 255)
assert transformed[50, 50] == (0, 0, 255)
assert transformed[255, 0] == (0, 255, 255)
assert transformed[205, 50] == (0, 255, 255)
assert transformed[0, 255] == (255, 0, 0)
assert transformed[50, 205] == (255, 0, 0)
assert transformed[255, 255] == (255, 255, 0)
assert transformed[205, 205] == (255, 255, 0)
2018-03-26 22:29:50 +03:00
2019-06-13 18:53:42 +03:00
# fmt: off
2022-01-15 01:02:31 +03:00
transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR,
3, 2, 2, 2,
[
-3, -3, -3, 5, -3, -3,
-3, 5, -3, 5, 5, -3,
-3, -3, 5, 5, -3, 5,
-3, 5, 5, 5, 5, 5,
])).load()
2019-06-13 18:53:42 +03:00
# fmt: on
assert transformed[0, 0] == (0, 0, 255)
assert transformed[50, 50] == (0, 0, 255)
assert transformed[255, 0] == (0, 255, 255)
assert transformed[205, 50] == (0, 255, 255)
assert transformed[0, 255] == (255, 0, 0)
assert transformed[50, 205] == (255, 0, 0)
assert transformed[255, 255] == (255, 255, 0)
assert transformed[205, 205] == (255, 255, 0)
2018-03-26 22:29:50 +03:00
2018-03-26 19:30:00 +03:00
2020-02-23 00:03:01 +03:00
class TestColorLut3DFilter:
def test_wrong_args(self) -> None:
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should be either an integer"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT("small", [1])
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should be either an integer"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT((11, 11), [1])
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match=r"in \[2, 65\] range"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT((11, 11, 1), [1])
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match=r"in \[2, 65\] range"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT((11, 11, 66), [1])
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="table should have .+ items"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT((3, 3, 3), [1, 1, 1])
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="table should have .+ items"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT((3, 3, 3), [[1, 1, 1]] * 2)
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have a length of 4"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT((3, 3, 3), [[1, 1, 1]] * 27, channels=4)
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have a length of 3"):
2018-03-29 16:37:35 +03:00
ImageFilter.Color3DLUT((2, 2, 2), [[1, 1]] * 8)
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="Only 3 or 4 output"):
ImageFilter.Color3DLUT((2, 2, 2), [[1, 1]] * 8, channels=2)
def test_convert_table(self) -> None:
2018-03-30 02:02:37 +03:00
lut = ImageFilter.Color3DLUT(2, [0, 1, 2] * 8)
assert tuple(lut.size) == (2, 2, 2)
assert lut.name == "Color 3D LUT"
2018-03-29 16:37:35 +03:00
2019-06-13 18:53:42 +03:00
# fmt: off
2018-03-30 02:02:37 +03:00
lut = ImageFilter.Color3DLUT((2, 2, 2), [
2018-03-29 16:37:35 +03:00
(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11),
(12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23)])
2019-06-13 18:53:42 +03:00
# fmt: on
assert tuple(lut.size) == (2, 2, 2)
assert lut.table == list(range(24))
2018-03-29 16:37:35 +03:00
2019-06-13 18:53:42 +03:00
lut = ImageFilter.Color3DLUT((2, 2, 2), [(0, 1, 2, 3)] * 8, channels=4)
assert tuple(lut.size) == (2, 2, 2)
assert lut.table == list(range(4)) * 8
2018-03-29 16:37:35 +03:00
2020-02-23 00:03:01 +03:00
@pytest.mark.skipif(numpy is None, reason="NumPy not installed")
def test_numpy_sources(self) -> None:
2024-02-12 13:06:17 +03:00
assert numpy is not None
2018-04-14 19:17:15 +03:00
table = numpy.ones((5, 6, 7, 3), dtype=numpy.float16)
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have either channels"):
2018-04-14 19:17:15 +03:00
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
table = numpy.ones((7, 6, 5, 3), dtype=numpy.float16)
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
assert isinstance(lut.table, numpy.ndarray)
assert lut.table.dtype == table.dtype
assert lut.table.shape == (table.size,)
2018-04-14 19:17:15 +03:00
table = numpy.ones((7 * 6 * 5, 3), dtype=numpy.float16)
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
assert lut.table.shape == (table.size,)
2018-04-14 19:17:15 +03:00
table = numpy.ones((7 * 6 * 5 * 3), dtype=numpy.float16)
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
assert lut.table.shape == (table.size,)
2018-04-14 19:17:15 +03:00
# Check application
2019-06-13 18:53:42 +03:00
Image.new("RGB", (10, 10), 0).filter(lut)
2018-04-14 19:17:15 +03:00
# Check copy
table[0] = 33
assert lut.table[0] == 1
2018-04-14 19:17:15 +03:00
# Check not copy
table = numpy.ones((7 * 6 * 5 * 3), dtype=numpy.float16)
lut = ImageFilter.Color3DLUT((5, 6, 7), table, _copy_table=False)
table[0] = 33
assert lut.table[0] == 33
2018-04-14 19:17:15 +03:00
2020-02-23 00:03:01 +03:00
@pytest.mark.skipif(numpy is None, reason="NumPy not installed")
def test_numpy_formats(self) -> None:
2024-02-12 13:06:17 +03:00
assert numpy is not None
2019-06-13 18:53:42 +03:00
g = Image.linear_gradient("L")
im = Image.merge(
2022-01-15 01:02:31 +03:00
"RGB",
[
g,
g.transpose(Image.Transpose.ROTATE_90),
g.transpose(Image.Transpose.ROTATE_180),
],
2019-06-13 18:53:42 +03:00
)
2018-04-15 00:33:15 +03:00
2019-06-13 18:53:42 +03:00
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
2018-04-15 00:33:15 +03:00
lut.table = numpy.array(lut.table, dtype=numpy.float32)[:-1]
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have table_channels"):
2018-04-15 00:33:15 +03:00
im.filter(lut)
2019-06-13 18:53:42 +03:00
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float32).reshape((7 * 9 * 11), 3)
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have table_channels"):
2018-04-15 00:33:15 +03:00
im.filter(lut)
2019-06-13 18:53:42 +03:00
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
2018-04-15 00:33:15 +03:00
lut.table = numpy.array(lut.table, dtype=numpy.float16)
assert_image_equal(im, im.filter(lut))
2018-04-15 00:33:15 +03:00
2019-06-13 18:53:42 +03:00
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
2018-04-15 00:33:15 +03:00
lut.table = numpy.array(lut.table, dtype=numpy.float32)
assert_image_equal(im, im.filter(lut))
2018-04-15 00:33:15 +03:00
2019-06-13 18:53:42 +03:00
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
2018-04-15 00:33:15 +03:00
lut.table = numpy.array(lut.table, dtype=numpy.float64)
assert_image_equal(im, im.filter(lut))
2018-04-15 00:33:15 +03:00
2019-06-13 18:53:42 +03:00
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
2018-04-15 00:33:15 +03:00
lut.table = numpy.array(lut.table, dtype=numpy.int32)
im.filter(lut)
lut.table = numpy.array(lut.table, dtype=numpy.int8)
im.filter(lut)
def test_repr(self) -> None:
2018-04-12 11:54:54 +03:00
lut = ImageFilter.Color3DLUT(2, [0, 1, 2] * 8)
assert repr(lut) == "<Color3DLUT from list size=2x2x2 channels=3>"
2018-04-12 11:54:54 +03:00
lut = ImageFilter.Color3DLUT(
2019-06-13 18:53:42 +03:00
(3, 4, 5),
array("f", [0, 0, 0, 0] * (3 * 4 * 5)),
channels=4,
target_mode="YCbCr",
_copy_table=False,
)
assert (
repr(lut)
== "<Color3DLUT from array size=3x4x5 channels=4 target_mode=YCbCr>"
2019-06-13 18:53:42 +03:00
)
2018-04-11 17:05:48 +03:00
2020-02-23 00:03:01 +03:00
class TestGenerateColorLut3D:
def test_wrong_channels_count(self) -> None:
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="3 or 4 output channels"):
ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
5, channels=2, callback=lambda r, g, b: (r, g, b)
)
2018-03-29 16:37:35 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have either channels"):
2018-04-11 17:05:48 +03:00
ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b, r))
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have either channels"):
ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
5, channels=4, callback=lambda r, g, b: (r, g, b)
)
2018-04-11 17:05:48 +03:00
def test_3_channels(self) -> None:
2018-03-30 02:02:37 +03:00
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
assert tuple(lut.size) == (5, 5, 5)
assert lut.name == "Color 3D LUT"
2019-06-13 18:53:42 +03:00
# fmt: off
assert lut.table[:24] == [
2018-03-29 17:26:21 +03:00
0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 0.5, 0.0, 0.0, 0.75, 0.0, 0.0,
1.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.25, 0.25, 0.0, 0.5, 0.25, 0.0]
2019-06-13 18:53:42 +03:00
# fmt: on
2018-03-29 17:26:21 +03:00
def test_4_channels(self) -> None:
lut = ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
5, channels=4, callback=lambda r, g, b: (b, r, g, (r + g + b) / 2)
)
assert tuple(lut.size) == (5, 5, 5)
assert lut.name == "Color 3D LUT"
2019-06-13 18:53:42 +03:00
# fmt: off
assert lut.table[:24] == [
2018-03-29 17:26:21 +03:00
0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.125, 0.0, 0.5, 0.0, 0.25,
2018-06-24 15:32:25 +03:00
0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125
]
2019-06-13 18:53:42 +03:00
# fmt: on
2018-03-29 17:26:21 +03:00
def test_apply(self) -> None:
2018-04-11 17:05:48 +03:00
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
2018-03-29 17:26:21 +03:00
2019-06-13 18:53:42 +03:00
g = Image.linear_gradient("L")
im = Image.merge(
2022-01-15 01:02:31 +03:00
"RGB",
[
g,
g.transpose(Image.Transpose.ROTATE_90),
g.transpose(Image.Transpose.ROTATE_180),
],
2019-06-13 18:53:42 +03:00
)
assert im == im.filter(lut)
2018-04-11 17:05:48 +03:00
2020-02-23 00:03:01 +03:00
class TestTransformColorLut3D:
def test_wrong_args(self) -> None:
2018-04-11 17:05:48 +03:00
source = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="Only 3 or 4 output"):
2018-04-11 17:05:48 +03:00
source.transform(lambda r, g, b: (r, g, b), channels=8)
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have either channels"):
2018-04-11 17:05:48 +03:00
source.transform(lambda r, g, b: (r, g, b), channels=4)
2018-03-29 17:26:21 +03:00
2020-02-22 19:07:04 +03:00
with pytest.raises(ValueError, match="should have either channels"):
2018-04-11 17:05:48 +03:00
source.transform(lambda r, g, b: (r, g, b, 1))
with pytest.raises(TypeError):
2018-04-11 17:05:48 +03:00
source.transform(lambda r, g, b, a: (r, g, b))
def test_target_mode(self) -> None:
2018-04-11 17:05:48 +03:00
source = ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
2, lambda r, g, b: (r, g, b), target_mode="HSV"
)
2018-04-11 17:05:48 +03:00
lut = source.transform(lambda r, g, b: (r, g, b))
assert lut.mode == "HSV"
2018-04-11 17:05:48 +03:00
2019-06-13 18:53:42 +03:00
lut = source.transform(lambda r, g, b: (r, g, b), target_mode="RGB")
assert lut.mode == "RGB"
2018-04-11 17:05:48 +03:00
def test_3_to_3_channels(self) -> None:
2019-06-13 18:53:42 +03:00
source = ImageFilter.Color3DLUT.generate((3, 4, 5), lambda r, g, b: (r, g, b))
lut = source.transform(lambda r, g, b: (r * r, g * g, b * b))
assert tuple(lut.size) == tuple(source.size)
assert len(lut.table) == len(source.table)
assert lut.table != source.table
2022-04-25 15:50:08 +03:00
assert lut.table[:10] == [0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]
2018-04-11 17:05:48 +03:00
def test_3_to_4_channels(self) -> None:
2019-06-13 18:53:42 +03:00
source = ImageFilter.Color3DLUT.generate((6, 5, 4), lambda r, g, b: (r, g, b))
lut = source.transform(lambda r, g, b: (r * r, g * g, b * b, 1), channels=4)
assert tuple(lut.size) == tuple(source.size)
assert len(lut.table) != len(source.table)
assert lut.table != source.table
2019-06-13 18:53:42 +03:00
# fmt: off
2022-04-25 15:50:08 +03:00
assert lut.table[:16] == [
2018-04-11 17:05:48 +03:00
0.0, 0.0, 0.0, 1, 0.2**2, 0.0, 0.0, 1,
0.4**2, 0.0, 0.0, 1, 0.6**2, 0.0, 0.0, 1]
2019-06-13 18:53:42 +03:00
# fmt: on
2018-04-11 17:05:48 +03:00
def test_4_to_3_channels(self) -> None:
2018-04-11 17:05:48 +03:00
source = ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
(3, 6, 5), lambda r, g, b: (r, g, b, 1), channels=4
)
lut = source.transform(
lambda r, g, b, a: (a - r * r, a - g * g, a - b * b), channels=3
)
assert tuple(lut.size) == tuple(source.size)
assert len(lut.table) != len(source.table)
assert lut.table != source.table
2019-06-13 18:53:42 +03:00
# fmt: off
2022-04-25 15:50:08 +03:00
assert lut.table[:18] == [
2018-04-11 17:05:48 +03:00
1.0, 1.0, 1.0, 0.75, 1.0, 1.0, 0.0, 1.0, 1.0,
1.0, 0.96, 1.0, 0.75, 0.96, 1.0, 0.0, 0.96, 1.0]
2019-06-13 18:53:42 +03:00
# fmt: on
2018-04-11 17:05:48 +03:00
def test_4_to_4_channels(self) -> None:
2018-04-11 17:05:48 +03:00
source = ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
(6, 5, 4), lambda r, g, b: (r, g, b, 1), channels=4
)
lut = source.transform(lambda r, g, b, a: (r * r, g * g, b * b, a - 0.5))
assert tuple(lut.size) == tuple(source.size)
assert len(lut.table) == len(source.table)
assert lut.table != source.table
2019-06-13 18:53:42 +03:00
# fmt: off
2022-04-25 15:50:08 +03:00
assert lut.table[:16] == [
2018-04-11 17:05:48 +03:00
0.0, 0.0, 0.0, 0.5, 0.2**2, 0.0, 0.0, 0.5,
0.4**2, 0.0, 0.0, 0.5, 0.6**2, 0.0, 0.0, 0.5]
2019-06-13 18:53:42 +03:00
# fmt: on
2018-04-11 17:05:48 +03:00
def test_with_normals_3_channels(self) -> None:
2018-04-11 17:05:48 +03:00
source = ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
(6, 5, 4), lambda r, g, b: (r * r, g * g, b * b)
)
2018-04-11 17:05:48 +03:00
lut = source.transform(
2019-06-13 18:53:42 +03:00
lambda nr, ng, nb, r, g, b: (nr - r, ng - g, nb - b), with_normals=True
)
assert tuple(lut.size) == tuple(source.size)
assert len(lut.table) == len(source.table)
assert lut.table != source.table
2019-06-13 18:53:42 +03:00
# fmt: off
2022-04-25 15:50:08 +03:00
assert lut.table[:18] == [
2018-04-11 17:05:48 +03:00
0.0, 0.0, 0.0, 0.16, 0.0, 0.0, 0.24, 0.0, 0.0,
0.24, 0.0, 0.0, 0.8 - (0.8**2), 0, 0, 0, 0, 0]
2019-06-13 18:53:42 +03:00
# fmt: on
2018-04-11 17:05:48 +03:00
def test_with_normals_4_channels(self) -> None:
2018-04-11 17:05:48 +03:00
source = ImageFilter.Color3DLUT.generate(
2019-06-13 18:53:42 +03:00
(3, 6, 5), lambda r, g, b: (r * r, g * g, b * b, 1), channels=4
)
2018-04-11 17:05:48 +03:00
lut = source.transform(
2019-06-13 18:53:42 +03:00
lambda nr, ng, nb, r, g, b, a: (nr - r, ng - g, nb - b, a - 0.5),
with_normals=True,
)
assert tuple(lut.size) == tuple(source.size)
assert len(lut.table) == len(source.table)
assert lut.table != source.table
2019-06-13 18:53:42 +03:00
# fmt: off
2022-04-25 15:50:08 +03:00
assert lut.table[:16] == [
2018-04-11 17:05:48 +03:00
0.0, 0.0, 0.0, 0.5, 0.25, 0.0, 0.0, 0.5,
0.0, 0.0, 0.0, 0.5, 0.0, 0.16, 0.0, 0.5]
2019-06-13 18:53:42 +03:00
# fmt: on