Pillow/Tests/test_imagemorph.py

329 lines
11 KiB
Python
Raw Normal View History

# Test the ImageMorphology functionality
from .helper import PillowTestCase, hopper
2018-04-22 19:51:57 +03:00
from PIL import Image, ImageMorph, _imagingmorph
class MorphTests(PillowTestCase):
def setUp(self):
self.A = self.string_to_img(
2014-06-24 10:34:05 +04:00
"""
.......
.......
..111..
..111..
..111..
.......
.......
"""
2019-06-13 18:54:46 +03:00
)
def img_to_string(self, im):
"""Turn a (small) binary image into a string representation"""
2019-06-13 18:54:46 +03:00
chars = ".1"
width, height = im.size
2019-06-13 18:54:46 +03:00
return "\n".join(
"".join(chars[im.getpixel((c, r)) > 0] for c in range(width))
for r in range(height)
)
def string_to_img(self, image_string):
"""Turn a string image representation into a binary image"""
2019-06-13 18:54:46 +03:00
rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)]
height = len(rows)
width = len(rows[0])
2019-06-13 18:54:46 +03:00
im = Image.new("L", (width, height))
for i in range(width):
for j in range(height):
c = rows[j][i]
2019-06-13 18:54:46 +03:00
v = c in "X1"
2014-06-24 10:34:05 +04:00
im.putpixel((i, j), v)
return im
def img_string_normalize(self, im):
return self.img_to_string(self.string_to_img(im))
def assert_img_equal(self, A, B):
self.assertEqual(self.img_to_string(A), self.img_to_string(B))
def assert_img_equal_img_string(self, A, Bstring):
2019-06-13 18:54:46 +03:00
self.assertEqual(self.img_to_string(A), self.img_string_normalize(Bstring))
def test_str_to_img(self):
2019-06-13 18:54:46 +03:00
im = Image.open("Tests/images/morph_a.png")
self.assert_image_equal(self.A, im)
def create_lut(self):
2019-06-13 18:54:46 +03:00
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op)
lut = lb.build_lut()
2019-06-13 18:54:46 +03:00
with open("Tests/images/%s.lut" % op, "wb") as f:
f.write(lut)
2014-06-24 10:34:05 +04:00
# create_lut()
def test_lut(self):
2019-06-13 18:54:46 +03:00
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op)
2017-03-01 12:20:18 +03:00
self.assertIsNone(lb.get_lut())
lut = lb.build_lut()
2019-06-13 18:54:46 +03:00
with open("Tests/images/%s.lut" % op, "rb") as f:
self.assertEqual(lut, bytearray(f.read()))
2017-03-01 12:20:18 +03:00
def test_no_operator_loaded(self):
mop = ImageMorph.MorphOp()
with self.assertRaises(Exception) as e:
mop.apply(None)
2019-06-13 18:54:46 +03:00
self.assertEqual(str(e.exception), "No operator loaded")
with self.assertRaises(Exception) as e:
mop.match(None)
2019-06-13 18:54:46 +03:00
self.assertEqual(str(e.exception), "No operator loaded")
with self.assertRaises(Exception) as e:
mop.save_lut(None)
2019-06-13 18:54:46 +03:00
self.assertEqual(str(e.exception), "No operator loaded")
2017-03-01 12:20:18 +03:00
# Test the named patterns
def test_erosion8(self):
# erosion8
2019-06-13 18:54:46 +03:00
mop = ImageMorph.MorphOp(op_name="erosion8")
2014-06-24 10:34:05 +04:00
count, Aout = mop.apply(self.A)
self.assertEqual(count, 8)
2019-06-13 18:54:46 +03:00
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
.......
...1...
.......
.......
.......
2019-06-13 18:54:46 +03:00
""",
)
def test_dialation8(self):
# dialation8
2019-06-13 18:54:46 +03:00
mop = ImageMorph.MorphOp(op_name="dilation8")
2014-06-24 10:34:05 +04:00
count, Aout = mop.apply(self.A)
self.assertEqual(count, 16)
2019-06-13 18:54:46 +03:00
self.assert_img_equal_img_string(
Aout,
"""
.......
.11111.
.11111.
.11111.
.11111.
.11111.
.......
2019-06-13 18:54:46 +03:00
""",
)
def test_erosion4(self):
# erosion4
2019-06-13 18:54:46 +03:00
mop = ImageMorph.MorphOp(op_name="dilation4")
2014-06-24 10:34:05 +04:00
count, Aout = mop.apply(self.A)
self.assertEqual(count, 12)
2019-06-13 18:54:46 +03:00
self.assert_img_equal_img_string(
Aout,
"""
.......
..111..
.11111.
.11111.
.11111.
..111..
.......
2019-06-13 18:54:46 +03:00
""",
)
def test_edge(self):
2014-06-24 10:34:05 +04:00
# edge
2019-06-13 18:54:46 +03:00
mop = ImageMorph.MorphOp(op_name="edge")
2014-06-24 10:34:05 +04:00
count, Aout = mop.apply(self.A)
self.assertEqual(count, 1)
2019-06-13 18:54:46 +03:00
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
..111..
..1.1..
..111..
.......
.......
2019-06-13 18:54:46 +03:00
""",
)
def test_corner(self):
# Create a corner detector pattern
2019-06-13 18:54:46 +03:00
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"])
2014-06-24 10:34:05 +04:00
count, Aout = mop.apply(self.A)
self.assertEqual(count, 5)
2019-06-13 18:54:46 +03:00
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
..1.1..
.......
..1.1..
.......
.......
2019-06-13 18:54:46 +03:00
""",
)
# Test the coordinate counting with the same operator
coords = mop.match(self.A)
self.assertEqual(len(coords), 4)
2014-06-24 10:34:05 +04:00
self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))
coords = mop.get_on_pixels(Aout)
self.assertEqual(len(coords), 4)
2014-06-24 10:34:05 +04:00
self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))
2017-05-28 22:56:25 +03:00
def test_mirroring(self):
# Test 'M' for mirroring
2019-06-13 18:54:46 +03:00
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "M:(00. 01. ...)->1"])
2017-05-28 22:56:25 +03:00
count, Aout = mop.apply(self.A)
self.assertEqual(count, 7)
2019-06-13 18:54:46 +03:00
self.assert_img_equal_img_string(
Aout,
"""
2017-05-28 22:56:25 +03:00
.......
.......
..1.1..
.......
.......
.......
.......
2019-06-13 18:54:46 +03:00
""",
)
2017-05-28 22:56:25 +03:00
def test_negate(self):
# Test 'N' for negate
2019-06-13 18:54:46 +03:00
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "N:(00. 01. ...)->1"])
count, Aout = mop.apply(self.A)
self.assertEqual(count, 8)
2019-06-13 18:54:46 +03:00
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
..1....
.......
.......
.......
.......
2019-06-13 18:54:46 +03:00
""",
)
def test_non_binary_images(self):
2019-06-13 18:54:46 +03:00
im = hopper("RGB")
mop = ImageMorph.MorphOp(op_name="erosion8")
with self.assertRaises(Exception) as e:
mop.apply(im)
2019-06-13 18:54:46 +03:00
self.assertEqual(
str(e.exception), "Image must be binary, meaning it must use mode L"
)
with self.assertRaises(Exception) as e:
mop.match(im)
2019-06-13 18:54:46 +03:00
self.assertEqual(
str(e.exception), "Image must be binary, meaning it must use mode L"
)
with self.assertRaises(Exception) as e:
mop.get_on_pixels(im)
2019-06-13 18:54:46 +03:00
self.assertEqual(
str(e.exception), "Image must be binary, meaning it must use mode L"
)
2017-05-29 08:29:12 +03:00
def test_add_patterns(self):
# Arrange
2019-06-13 18:54:46 +03:00
lb = ImageMorph.LutBuilder(op_name="corner")
self.assertEqual(lb.patterns, ["1:(... ... ...)->0", "4:(00. 01. ...)->1"])
new_patterns = ["M:(00. 01. ...)->1", "N:(00. 01. ...)->1"]
2017-05-29 08:29:12 +03:00
# Act
lb.add_patterns(new_patterns)
# Assert
self.assertEqual(
lb.patterns,
2019-06-13 18:54:46 +03:00
[
"1:(... ... ...)->0",
"4:(00. 01. ...)->1",
"M:(00. 01. ...)->1",
"N:(00. 01. ...)->1",
],
)
2017-05-29 08:29:12 +03:00
def test_unknown_pattern(self):
2019-06-13 18:54:46 +03:00
self.assertRaises(Exception, ImageMorph.LutBuilder, op_name="unknown")
2017-05-29 08:29:12 +03:00
def test_pattern_syntax_error(self):
# Arrange
2019-06-13 18:54:46 +03:00
lb = ImageMorph.LutBuilder(op_name="corner")
new_patterns = ["a pattern with a syntax error"]
2017-05-29 08:29:12 +03:00
lb.add_patterns(new_patterns)
# Act / Assert
with self.assertRaises(Exception) as e:
lb.build_lut()
2018-06-24 15:32:25 +03:00
self.assertEqual(
2019-06-13 18:54:46 +03:00
str(e.exception), 'Syntax error in pattern "a pattern with a syntax error"'
)
2017-05-29 08:29:12 +03:00
def test_load_invalid_mrl(self):
# Arrange
2019-06-13 18:54:46 +03:00
invalid_mrl = "Tests/images/hopper.png"
2017-05-29 08:29:12 +03:00
mop = ImageMorph.MorphOp()
# Act / Assert
with self.assertRaises(Exception) as e:
mop.load_lut(invalid_mrl)
2019-06-13 18:54:46 +03:00
self.assertEqual(str(e.exception), "Wrong size operator file!")
2017-05-29 08:29:12 +03:00
2017-05-29 09:52:31 +03:00
def test_roundtrip_mrl(self):
# Arrange
2019-06-13 18:54:46 +03:00
tempfile = self.tempfile("temp.mrl")
mop = ImageMorph.MorphOp(op_name="corner")
2017-05-29 09:52:31 +03:00
initial_lut = mop.lut
# Act
mop.save_lut(tempfile)
mop.load_lut(tempfile)
# Act / Assert
self.assertEqual(mop.lut, initial_lut)
2017-05-29 08:29:12 +03:00
def test_set_lut(self):
# Arrange
2019-06-13 18:54:46 +03:00
lb = ImageMorph.LutBuilder(op_name="corner")
2017-05-29 08:29:12 +03:00
lut = lb.build_lut()
mop = ImageMorph.MorphOp()
# Act
mop.set_lut(lut)
# Assert
self.assertEqual(mop.lut, lut)
2018-04-22 19:51:57 +03:00
def test_wrong_mode(self):
2019-06-13 18:54:46 +03:00
lut = ImageMorph.LutBuilder(op_name="corner").build_lut()
imrgb = Image.new("RGB", (10, 10))
iml = Image.new("L", (10, 10))
2018-04-22 19:51:57 +03:00
with self.assertRaises(RuntimeError):
2018-04-22 19:51:57 +03:00
_imagingmorph.apply(bytes(lut), imrgb.im.id, iml.im.id)
with self.assertRaises(RuntimeError):
2018-04-22 19:51:57 +03:00
_imagingmorph.apply(bytes(lut), iml.im.id, imrgb.im.id)
with self.assertRaises(RuntimeError):
2018-04-22 19:51:57 +03:00
_imagingmorph.match(bytes(lut), imrgb.im.id)
# Should not raise
_imagingmorph.match(bytes(lut), iml.im.id)