mirror of
https://github.com/python-pillow/Pillow.git
synced 2026-01-11 03:01:29 +03:00
Allow 1 mode images in MorphOp (#9348)
This commit is contained in:
commit
f130c10a9c
Binary file not shown.
|
Before Width: | Height: | Size: 83 B After Width: | Height: | Size: 79 B |
|
|
@ -15,13 +15,10 @@ def string_to_img(image_string: str) -> Image.Image:
|
||||||
rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)]
|
rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)]
|
||||||
height = len(rows)
|
height = len(rows)
|
||||||
width = len(rows[0])
|
width = len(rows[0])
|
||||||
im = Image.new("L", (width, height))
|
im = Image.new("1", (width, height))
|
||||||
for i in range(width):
|
for x in range(width):
|
||||||
for j in range(height):
|
for y in range(height):
|
||||||
c = rows[j][i]
|
im.putpixel((x, y), rows[y][x] in "X1")
|
||||||
v = c in "X1"
|
|
||||||
im.putpixel((i, j), v)
|
|
||||||
|
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -42,10 +39,10 @@ def img_to_string(im: Image.Image) -> str:
|
||||||
"""Turn a (small) binary image into a string representation"""
|
"""Turn a (small) binary image into a string representation"""
|
||||||
chars = ".1"
|
chars = ".1"
|
||||||
result = []
|
result = []
|
||||||
for r in range(im.height):
|
for y in range(im.height):
|
||||||
line = ""
|
line = ""
|
||||||
for c in range(im.width):
|
for x in range(im.width):
|
||||||
value = im.getpixel((c, r))
|
value = im.getpixel((x, y))
|
||||||
assert not isinstance(value, tuple)
|
assert not isinstance(value, tuple)
|
||||||
assert value is not None
|
assert value is not None
|
||||||
line += chars[value > 0]
|
line += chars[value > 0]
|
||||||
|
|
@ -165,10 +162,12 @@ def test_edge() -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_corner() -> None:
|
@pytest.mark.parametrize("mode", ("1", "L"))
|
||||||
|
def test_corner(mode: str) -> None:
|
||||||
# Create a corner detector pattern
|
# Create a corner detector pattern
|
||||||
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"])
|
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"])
|
||||||
count, Aout = mop.apply(A)
|
image = A.convert(mode) if mode == "L" else A
|
||||||
|
count, Aout = mop.apply(image)
|
||||||
assert count == 5
|
assert count == 5
|
||||||
assert_img_equal_img_string(
|
assert_img_equal_img_string(
|
||||||
Aout,
|
Aout,
|
||||||
|
|
@ -184,7 +183,7 @@ def test_corner() -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test the coordinate counting with the same operator
|
# Test the coordinate counting with the same operator
|
||||||
coords = mop.match(A)
|
coords = mop.match(image)
|
||||||
assert len(coords) == 4
|
assert len(coords) == 4
|
||||||
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
|
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
|
||||||
|
|
||||||
|
|
@ -232,14 +231,14 @@ def test_negate() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_incorrect_mode() -> None:
|
def test_incorrect_mode() -> None:
|
||||||
im = hopper("RGB")
|
im = hopper()
|
||||||
mop = ImageMorph.MorphOp(op_name="erosion8")
|
mop = ImageMorph.MorphOp(op_name="erosion8")
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="Image mode must be L"):
|
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
||||||
mop.apply(im)
|
mop.apply(im)
|
||||||
with pytest.raises(ValueError, match="Image mode must be L"):
|
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
||||||
mop.match(im)
|
mop.match(im)
|
||||||
with pytest.raises(ValueError, match="Image mode must be L"):
|
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
||||||
mop.get_on_pixels(im)
|
mop.get_on_pixels(im)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
================================
|
================================
|
||||||
|
|
||||||
The :py:mod:`~PIL.ImageMorph` module allows `morphology`_ operators ("MorphOp") to be
|
The :py:mod:`~PIL.ImageMorph` module allows `morphology`_ operators ("MorphOp") to be
|
||||||
applied to L mode images::
|
applied to 1 or L mode images::
|
||||||
|
|
||||||
from PIL import Image, ImageMorph
|
from PIL import Image, ImageMorph
|
||||||
img = Image.open("Tests/images/hopper.bw")
|
img = Image.open("Tests/images/hopper.bw")
|
||||||
|
|
|
||||||
|
|
@ -233,14 +233,15 @@ class MorphOp:
|
||||||
Returns a tuple of the number of changed pixels and the
|
Returns a tuple of the number of changed pixels and the
|
||||||
morphed image.
|
morphed image.
|
||||||
|
|
||||||
|
:param image: A 1-mode or L-mode image.
|
||||||
:exception Exception: If the current operator is None.
|
:exception Exception: If the current operator is None.
|
||||||
:exception ValueError: If the image is not L mode."""
|
:exception ValueError: If the image is not 1 or L mode."""
|
||||||
if self.lut is None:
|
if self.lut is None:
|
||||||
msg = "No operator loaded"
|
msg = "No operator loaded"
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
if image.mode != "L":
|
if image.mode not in ("1", "L"):
|
||||||
msg = "Image mode must be L"
|
msg = "Image mode must be 1 or L"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
outimage = Image.new(image.mode, image.size)
|
outimage = Image.new(image.mode, image.size)
|
||||||
count = _imagingmorph.apply(bytes(self.lut), image.getim(), outimage.getim())
|
count = _imagingmorph.apply(bytes(self.lut), image.getim(), outimage.getim())
|
||||||
|
|
@ -253,29 +254,29 @@ class MorphOp:
|
||||||
Returns a list of tuples of (x,y) coordinates of all matching pixels. See
|
Returns a list of tuples of (x,y) coordinates of all matching pixels. See
|
||||||
:ref:`coordinate-system`.
|
:ref:`coordinate-system`.
|
||||||
|
|
||||||
:param image: An L-mode image.
|
:param image: A 1-mode or L-mode image.
|
||||||
:exception Exception: If the current operator is None.
|
:exception Exception: If the current operator is None.
|
||||||
:exception ValueError: If the image is not L mode."""
|
:exception ValueError: If the image is not 1 or L mode."""
|
||||||
if self.lut is None:
|
if self.lut is None:
|
||||||
msg = "No operator loaded"
|
msg = "No operator loaded"
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
if image.mode != "L":
|
if image.mode not in ("1", "L"):
|
||||||
msg = "Image mode must be L"
|
msg = "Image mode must be 1 or L"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
return _imagingmorph.match(bytes(self.lut), image.getim())
|
return _imagingmorph.match(bytes(self.lut), image.getim())
|
||||||
|
|
||||||
def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]:
|
def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]:
|
||||||
"""Get a list of all turned on pixels in a grayscale image
|
"""Get a list of all turned on pixels in a 1 or L mode image.
|
||||||
|
|
||||||
Returns a list of tuples of (x,y) coordinates of all non-empty pixels. See
|
Returns a list of tuples of (x,y) coordinates of all non-empty pixels. See
|
||||||
:ref:`coordinate-system`.
|
:ref:`coordinate-system`.
|
||||||
|
|
||||||
:param image: An L-mode image.
|
:param image: A 1-mode or L-mode image.
|
||||||
:exception ValueError: If the image is not L mode."""
|
:exception ValueError: If the image is not 1 or L mode."""
|
||||||
|
|
||||||
if image.mode != "L":
|
if image.mode not in ("1", "L"):
|
||||||
msg = "Image mode must be L"
|
msg = "Image mode must be 1 or L"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
return _imagingmorph.get_on_pixels(image.getim())
|
return _imagingmorph.get_on_pixels(image.getim())
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user