2014-06-05 00:03:00 +04:00
|
|
|
# Test the ImageMorphology functionality
|
2015-08-28 15:54:17 +03:00
|
|
|
from helper import unittest, PillowTestCase, hopper
|
2014-06-05 00:03:00 +04:00
|
|
|
|
|
|
|
from PIL import Image
|
|
|
|
from PIL import ImageMorph
|
|
|
|
|
2014-06-24 03:52:10 +04:00
|
|
|
|
|
|
|
class MorphTests(PillowTestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.A = self.string_to_img(
|
2014-06-24 10:34:05 +04:00
|
|
|
"""
|
2014-06-24 03:52:10 +04:00
|
|
|
.......
|
|
|
|
.......
|
|
|
|
..111..
|
|
|
|
..111..
|
|
|
|
..111..
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
"""
|
2014-06-24 10:34:05 +04:00
|
|
|
)
|
2014-06-24 03:52:10 +04:00
|
|
|
|
|
|
|
def img_to_string(self, im):
|
|
|
|
"""Turn a (small) binary image into a string representation"""
|
|
|
|
chars = '.1'
|
|
|
|
width, height = im.size
|
|
|
|
return '\n'.join(
|
2016-11-15 04:48:54 +03:00
|
|
|
''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))
|
|
|
|
for r in range(height))
|
2014-06-24 03:52:10 +04:00
|
|
|
|
|
|
|
def string_to_img(self, image_string):
|
|
|
|
"""Turn a string image representation into a binary image"""
|
2014-06-24 10:34:05 +04:00
|
|
|
rows = [s for s in image_string.replace(' ', '').split('\n')
|
2014-06-24 03:52:10 +04:00
|
|
|
if len(s)]
|
|
|
|
height = len(rows)
|
|
|
|
width = len(rows[0])
|
2014-06-24 10:34:05 +04:00
|
|
|
im = Image.new('L', (width, height))
|
2014-06-24 03:52:10 +04:00
|
|
|
for i in range(width):
|
|
|
|
for j in range(height):
|
|
|
|
c = rows[j][i]
|
|
|
|
v = c in 'X1'
|
2014-06-24 10:34:05 +04:00
|
|
|
im.putpixel((i, j), v)
|
2014-06-24 03:52:10 +04:00
|
|
|
|
|
|
|
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):
|
2014-06-24 10:34:05 +04:00
|
|
|
self.assertEqual(
|
|
|
|
self.img_to_string(A),
|
|
|
|
self.img_string_normalize(Bstring))
|
2014-06-24 03:52:10 +04:00
|
|
|
|
|
|
|
def test_str_to_img(self):
|
|
|
|
im = Image.open('Tests/images/morph_a.png')
|
|
|
|
self.assert_image_equal(self.A, im)
|
|
|
|
|
|
|
|
def create_lut(self):
|
2014-06-24 10:34:05 +04:00
|
|
|
for op in (
|
|
|
|
'corner', 'dilation4', 'dilation8',
|
|
|
|
'erosion4', 'erosion8', 'edge'):
|
2014-06-24 03:52:10 +04:00
|
|
|
lb = ImageMorph.LutBuilder(op_name=op)
|
2014-10-01 17:50:33 +04:00
|
|
|
lut = lb.build_lut()
|
2014-06-24 03:52:10 +04:00
|
|
|
with open('Tests/images/%s.lut' % op, 'wb') as f:
|
|
|
|
f.write(lut)
|
|
|
|
|
2014-06-24 10:34:05 +04:00
|
|
|
# create_lut()
|
2014-06-24 03:52:10 +04:00
|
|
|
def test_lut(self):
|
2014-06-24 10:34:05 +04:00
|
|
|
for op in (
|
|
|
|
'corner', 'dilation4', 'dilation8',
|
|
|
|
'erosion4', 'erosion8', 'edge'):
|
2014-06-24 03:52:10 +04:00
|
|
|
lb = ImageMorph.LutBuilder(op_name=op)
|
2017-03-01 12:20:18 +03:00
|
|
|
self.assertIsNone(lb.get_lut())
|
|
|
|
|
2014-06-24 03:52:10 +04:00
|
|
|
lut = lb.build_lut()
|
2014-06-24 10:34:05 +04:00
|
|
|
with open('Tests/images/%s.lut' % op, 'rb') as f:
|
2014-06-24 03:52:10 +04:00
|
|
|
self.assertEqual(lut, bytearray(f.read()))
|
|
|
|
|
2017-03-01 12:20:18 +03:00
|
|
|
def test_no_operator_loaded(self):
|
|
|
|
mop = ImageMorph.MorphOp()
|
2017-09-01 14:05:40 +03:00
|
|
|
self.assertRaises(Exception, mop.apply, None)
|
|
|
|
self.assertRaises(Exception, mop.match, None)
|
|
|
|
self.assertRaises(Exception, mop.save_lut, None)
|
2017-03-01 12:20:18 +03:00
|
|
|
|
2014-06-24 03:52:10 +04:00
|
|
|
# Test the named patterns
|
|
|
|
def test_erosion8(self):
|
|
|
|
# erosion8
|
|
|
|
mop = ImageMorph.MorphOp(op_name='erosion8')
|
2014-06-24 10:34:05 +04:00
|
|
|
count, Aout = mop.apply(self.A)
|
|
|
|
self.assertEqual(count, 8)
|
2014-06-24 03:52:10 +04:00
|
|
|
self.assert_img_equal_img_string(Aout,
|
|
|
|
"""
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
...1...
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_dialation8(self):
|
|
|
|
# dialation8
|
|
|
|
mop = ImageMorph.MorphOp(op_name='dilation8')
|
2014-06-24 10:34:05 +04:00
|
|
|
count, Aout = mop.apply(self.A)
|
|
|
|
self.assertEqual(count, 16)
|
2014-06-24 03:52:10 +04:00
|
|
|
self.assert_img_equal_img_string(Aout,
|
|
|
|
"""
|
|
|
|
.......
|
|
|
|
.11111.
|
|
|
|
.11111.
|
|
|
|
.11111.
|
|
|
|
.11111.
|
|
|
|
.11111.
|
|
|
|
.......
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_erosion4(self):
|
|
|
|
# erosion4
|
|
|
|
mop = ImageMorph.MorphOp(op_name='dilation4')
|
2014-06-24 10:34:05 +04:00
|
|
|
count, Aout = mop.apply(self.A)
|
|
|
|
self.assertEqual(count, 12)
|
2014-06-24 03:52:10 +04:00
|
|
|
self.assert_img_equal_img_string(Aout,
|
|
|
|
"""
|
|
|
|
.......
|
|
|
|
..111..
|
|
|
|
.11111.
|
|
|
|
.11111.
|
|
|
|
.11111.
|
|
|
|
..111..
|
|
|
|
.......
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_edge(self):
|
2014-06-24 10:34:05 +04:00
|
|
|
# edge
|
2014-06-24 03:52:10 +04: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)
|
2014-06-24 03:52:10 +04:00
|
|
|
self.assert_img_equal_img_string(Aout,
|
|
|
|
"""
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
..111..
|
|
|
|
..1.1..
|
|
|
|
..111..
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_corner(self):
|
|
|
|
# Create a corner detector pattern
|
2014-06-24 10:34:05 +04:00
|
|
|
mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',
|
|
|
|
'4:(00. 01. ...)->1'])
|
|
|
|
count, Aout = mop.apply(self.A)
|
|
|
|
self.assertEqual(count, 5)
|
2014-06-24 03:52:10 +04:00
|
|
|
self.assert_img_equal_img_string(Aout,
|
|
|
|
"""
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
..1.1..
|
|
|
|
.......
|
|
|
|
..1.1..
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
""")
|
|
|
|
|
|
|
|
# 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)))
|
2014-06-24 03:52:10 +04:00
|
|
|
|
|
|
|
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
|
|
|
|
mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',
|
|
|
|
'M:(00. 01. ...)->1'])
|
|
|
|
count, Aout = mop.apply(self.A)
|
|
|
|
self.assertEqual(count, 7)
|
|
|
|
self.assert_img_equal_img_string(Aout,
|
|
|
|
"""
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
..1.1..
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
""")
|
|
|
|
|
2017-05-28 23:02:06 +03:00
|
|
|
def test_negate(self):
|
|
|
|
# Test 'N' for negate
|
|
|
|
mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',
|
|
|
|
'N:(00. 01. ...)->1'])
|
|
|
|
count, Aout = mop.apply(self.A)
|
|
|
|
self.assertEqual(count, 8)
|
|
|
|
self.assert_img_equal_img_string(Aout,
|
|
|
|
"""
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
..1....
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
.......
|
|
|
|
""")
|
|
|
|
|
2015-08-28 15:54:17 +03:00
|
|
|
def test_non_binary_images(self):
|
|
|
|
im = hopper('RGB')
|
|
|
|
mop = ImageMorph.MorphOp(op_name="erosion8")
|
|
|
|
|
2017-09-01 14:05:40 +03:00
|
|
|
self.assertRaises(Exception, mop.apply, im)
|
|
|
|
self.assertRaises(Exception, mop.match, im)
|
|
|
|
self.assertRaises(Exception, mop.get_on_pixels, im)
|
2015-08-28 15:54:17 +03:00
|
|
|
|
2017-05-29 08:29:12 +03:00
|
|
|
def test_add_patterns(self):
|
|
|
|
# Arrange
|
|
|
|
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']
|
|
|
|
|
|
|
|
# Act
|
|
|
|
lb.add_patterns(new_patterns)
|
|
|
|
|
|
|
|
# Assert
|
|
|
|
self.assertEqual(
|
|
|
|
lb.patterns,
|
|
|
|
['1:(... ... ...)->0',
|
|
|
|
'4:(00. 01. ...)->1',
|
|
|
|
'M:(00. 01. ...)->1',
|
|
|
|
'N:(00. 01. ...)->1'])
|
|
|
|
|
|
|
|
def test_unknown_pattern(self):
|
|
|
|
self.assertRaises(
|
|
|
|
Exception,
|
2017-09-01 14:05:40 +03:00
|
|
|
ImageMorph.LutBuilder, op_name='unknown')
|
2017-05-29 08:29:12 +03:00
|
|
|
|
|
|
|
def test_pattern_syntax_error(self):
|
|
|
|
# Arrange
|
|
|
|
lb = ImageMorph.LutBuilder(op_name='corner')
|
|
|
|
new_patterns = ['a pattern with a syntax error']
|
|
|
|
lb.add_patterns(new_patterns)
|
|
|
|
|
|
|
|
# Act / Assert
|
2017-09-01 14:05:40 +03:00
|
|
|
self.assertRaises(Exception, lb.build_lut)
|
2017-05-29 08:29:12 +03:00
|
|
|
|
|
|
|
def test_load_invalid_mrl(self):
|
|
|
|
# Arrange
|
|
|
|
invalid_mrl = 'Tests/images/hopper.png'
|
|
|
|
mop = ImageMorph.MorphOp()
|
|
|
|
|
|
|
|
# Act / Assert
|
2017-09-01 14:05:40 +03:00
|
|
|
self.assertRaises(Exception, mop.load_lut, invalid_mrl)
|
2017-05-29 08:29:12 +03:00
|
|
|
|
2017-05-29 09:52:31 +03:00
|
|
|
def test_roundtrip_mrl(self):
|
|
|
|
# Arrange
|
|
|
|
tempfile = self.tempfile('temp.mrl')
|
|
|
|
mop = ImageMorph.MorphOp(op_name='corner')
|
|
|
|
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
|
|
|
|
lb = ImageMorph.LutBuilder(op_name='corner')
|
|
|
|
lut = lb.build_lut()
|
|
|
|
mop = ImageMorph.MorphOp()
|
|
|
|
|
|
|
|
# Act
|
|
|
|
mop.set_lut(lut)
|
|
|
|
|
|
|
|
# Assert
|
|
|
|
self.assertEqual(mop.lut, lut)
|
|
|
|
|
2014-06-24 11:01:43 +04:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|