Added unit tests to skeletonization

This commit is contained in:
Neil Yager
2011-10-13 14:35:37 +01:00
parent 8ad8946900
commit 3d2613cdfa
2 changed files with 101 additions and 7 deletions
+18 -7
View File
@@ -6,6 +6,7 @@ Original author: Neil Yager
import numpy as np
from scipy.ndimage import correlate
from .. import util
def skeletonize(image):
"""
@@ -26,7 +27,8 @@ def skeletonize(image):
image: ndarray (2D)
A binary image containing the objects to be skeletonized. '1'
represents foreground, and '0' represents background.
represents foreground, and '0' represents background. It
also accepts arrays of boolean values where True is foreground.
Notes
-----
@@ -62,15 +64,24 @@ def skeletonize(image):
3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,3,1,3,0,0,1,3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,3,0,1,0,0,0,1,0,0,0,0,0,0,0,0,3,3,0,1,0,0,0,0,2,2,0,0,2,0,0,0]
# convert to unsigned int (this should work for boolean values)
skeleton = np.array(image).astype(np.uint8)
# check some properties of the input image:
# - 2D
# - binary image with only 0's and 1's
if skeleton.ndim != 2:
raise ValueError('Skeletonize requires a 2D array')
for val in np.unique(skeleton):
if val not in [0, 1]:
raise ValueError('Invalid value in the image: %d'%(val))
# initialize the skeleton to the original image
# TODO: how to handle data types
skeleton = image.copy().astype(np.int8)
# create the mask that will assign a value based on neighbouring pixels
# create the mask that will assign a unique value based on the
# arrangement of neighbouring pixels
mask = np.array([[ 1, 2, 4],
[128, 0, 8],
[ 64, 32, 16]], np.int8)
[ 64, 32, 16]], np.uint8)
pixelRemoved = True
while pixelRemoved:
@@ -0,0 +1,83 @@
import unittest
import numpy as np
from scikits.image.morphology import skeletonize
import numpy.testing
from scikits.image.draw import draw
from scipy.ndimage import correlate
class TestSkeletonize(unittest.TestCase):
def test_skeletonize_no_foreground(self):
im = np.zeros((5,5))
result = skeletonize.skeletonize(im)
numpy.testing.assert_array_equal(result, np.zeros((5,5)))
def test_skeletonize_wrong_dim1(self):
im = np.zeros((5))
self.assertRaises(ValueError, skeletonize.skeletonize, im)
def test_skeletonize_wrong_dim2(self):
im = np.zeros((5, 5, 5))
self.assertRaises(ValueError, skeletonize.skeletonize, im)
def test_skeletonize_not_binary(self):
im = np.zeros((5, 5))
im[0, 0] = 1
im[0, 1] = 2
self.assertRaises(ValueError, skeletonize.skeletonize, im)
def test_skeletonize_unexpected_value(self):
im = np.zeros((5, 5))
im[0, 0] = 2
self.assertRaises(ValueError, skeletonize.skeletonize, im)
def test_skeletonize_all_foreground(self):
im = np.ones((3,4))
result = skeletonize.skeletonize(im)
def test_skeletonize_single_point(self):
im = np.zeros((5, 5), np.uint8)
im[3, 3] = 1
result = skeletonize.skeletonize(im)
numpy.testing.assert_array_equal(result, im)
def test_skeletonize_already_thinned(self):
im = np.zeros((5, 5), np.uint8)
im[3,1:-1] = 1
im[2, -1] = 1
im[4, 0] = 1
result = skeletonize.skeletonize(im)
numpy.testing.assert_array_equal(result, im)
def test_skeletonize_num_neighbours(self):
# an empty image
image = np.zeros((300, 300))
# foreground object 1
image[10:-10, 10:100] = 1
image[-100:-10, 10:-10] = 1
image[10:-10, -100:-10] = 1
# foreground object 2
rs, cs = draw.bresenham(250, 150, 10, 280)
for i in range(10): image[rs+i, cs] = 1
rs, cs = draw.bresenham(10, 150, 250, 280)
for i in range(20): image[rs+i, cs] = 1
# foreground object 3
ir, ic = np.indices(image.shape)
circle1 = (ic - 135)**2 + (ir - 150)**2 < 30**2
circle2 = (ic - 135)**2 + (ir - 150)**2 < 20**2
image[circle1] = 1
image[circle2] = 0
result = skeletonize.skeletonize(image)
# there should never be a 2x2 block of foreground pixels
# in a skeleton
mask = np.array([[1, 1],
[1, 1]], np.uint8)
blocks = correlate(result, mask, mode='constant')
self.assertFalse(numpy.any(blocks == 4))
if __name__ == '__main__':
unittest.main()