diff --git a/skimage/morphology/selem.py b/skimage/morphology/selem.py index 57c17214..f75022ab 100644 --- a/skimage/morphology/selem.py +++ b/skimage/morphology/selem.py @@ -5,18 +5,18 @@ import numpy as np from scipy import ndimage - +from skimage import draw def square(width, dtype=np.uint8): - """ - Generates a flat, square-shaped structuring element. Every pixel - along the perimeter has a chessboard distance no greater than radius - (radius=floor(width/2)) pixels. + """Generates a flat, square-shaped structuring element. + + Every pixel along the perimeter has a chessboard distance + no greater than radius (radius=floor(width/2)) pixels. Parameters ---------- width : int - The width and height of the square + The width and height of the square. Other Parameters ---------------- @@ -34,17 +34,17 @@ def square(width, dtype=np.uint8): def rectangle(width, height, dtype=np.uint8): - """ - Generates a flat, rectangular-shaped structuring element of a - given width and height. Every pixel in the rectangle belongs - to the neighboorhood. + """Generates a flat, rectangular-shaped structuring element. + + Every pixel in the rectangle generated for a given width and given height + belongs to the neighboorhood. Parameters ---------- width : int - The width of the rectangle + The width of the rectangle. height : int - The height of the rectangle + The height of the rectangle. Other Parameters ---------------- @@ -62,11 +62,11 @@ def rectangle(width, height, dtype=np.uint8): def diamond(radius, dtype=np.uint8): - """ - Generates a flat, diamond-shaped structuring element of a given - radius. A pixel is part of the neighborhood (i.e. labeled 1) if - the city block/manhattan distance between it and the center of the - neighborhood is no greater than radius. + """Generates a flat, diamond-shaped structuring element. + + A pixel is part of the neighborhood (i.e. labeled 1) if + the city block/manhattan distance between it and the center of + the neighborhood is no greater than radius. Parameters ---------- @@ -92,8 +92,8 @@ def diamond(radius, dtype=np.uint8): def disk(radius, dtype=np.uint8): - """ - Generates a flat, disk-shaped structuring element of a given radius. + """Generates a flat, disk-shaped structuring element. + A pixel is within the neighborhood if the euclidean distance between it and the origin is no greater than radius. @@ -118,16 +118,60 @@ def disk(radius, dtype=np.uint8): return np.array((X ** 2 + Y ** 2) <= radius ** 2, dtype=dtype) -def cube(width, dtype=np.uint8): +def ellipse(width, height, dtype=np.uint8): + """Generates a flat, ellipse-shaped structuring element. + + Every pixel along the perimeter of ellipse satisfies + the equation ``(x/width+1)**2 + (y/height+1)**2 = 1``. + + Parameters + ---------- + width : int + The width of the ellipse-shaped structuring element. + height : int + The height of the ellipse-shaped structuring element. + + Other Parameters + ---------------- + dtype : data-type + The data type of the structuring element. + + Returns + ------- + selem : ndarray + The structuring element where elements of the neighborhood + are 1 and 0 otherwise. + + Examples + -------- + >>> from skimage.morphology import selem + >>> selem.ellipse(5, 3) + array([[0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]], dtype=uint8) + """ - Generates a cube-shaped structuring element (the 3D equivalent of - a square). Every pixel along the perimeter has a chessboard distance + selem = np.zeros((2 * height + 1, 2 * width + 1), dtype=dtype) + rows, cols = draw.ellipse(height, width, height + 1, width + 1) + selem[rows, cols] = 1 + return selem + + +def cube(width, dtype=np.uint8): + """ Generates a cube-shaped structuring element. + + This is the 3D equivalent of a square. + Every pixel along the perimeter has a chessboard distance no greater than radius (radius=floor(width/2)) pixels. Parameters ---------- width : int - The width, height and depth of the cube + The width, height and depth of the cube. Other Parameters ---------------- @@ -145,12 +189,12 @@ def cube(width, dtype=np.uint8): def octahedron(radius, dtype=np.uint8): - """ - Generates a octahedron-shaped structuring element of a given radius - (the 3D equivalent of a diamond). A pixel is part of the - neighborhood (i.e. labeled 1) if the city block/manhattan distance - between it and the center of the neighborhood is no greater than - radius. + """Generates a octahedron-shaped structuring element. + + This is the 3D equivalent of a diamond. + A pixel is part of the neighborhood (i.e. labeled 1) if + the city block/manhattan distance between it and the center of + the neighborhood is no greater than radius. Parameters ---------- @@ -179,11 +223,11 @@ def octahedron(radius, dtype=np.uint8): def ball(radius, dtype=np.uint8): - """ - Generates a ball-shaped structuring element of a given radius (the - 3D equivalent of a disk). A pixel is within the neighborhood if the - euclidean distance between it and the origin is no greater than - radius. + """Generates a ball-shaped structuring element. + + This is the 3D equivalent of a disk. + A pixel is within the neighborhood if the euclidean distance between + it and the origin is no greater than radius. Parameters ---------- @@ -210,10 +254,11 @@ def ball(radius, dtype=np.uint8): def octagon(m, n, dtype=np.uint8): - """ - Generates an octagon shaped structuring element with a given size of - horizontal and vertical sides and a given height or width of slanted - sides. The slanted sides are 45 or 135 degrees to the horizontal axis + """Generates an octagon shaped structuring element. + + For a given size of (m) horizontal and vertical sides + and a given (n) height or width of slanted sides octagon is generated. + The slanted sides are 45 or 135 degrees to the horizontal axis and hence the widths and heights are equal. Parameters @@ -250,9 +295,10 @@ def octagon(m, n, dtype=np.uint8): def star(a, dtype=np.uint8): - """ - Generates a star shaped structuring element that has 8 vertices and is an - overlap of square of size `2*a + 1` with its 45 degree rotated version. + """Generates a star shaped structuring element. + + Start has 8 vertices and is an overlap of square of size `2*a + 1` + with its 45 degree rotated version. The slanted sides are 45 or 135 degrees to the horizontal axis. Parameters @@ -298,9 +344,9 @@ def star(a, dtype=np.uint8): def _default_selem(ndim): - """ - Generates a cross-shaped structuring element (connectivity=1). This is the - default structuring element (selem) if no selem was specified. + """Generates a cross-shaped structuring element (connectivity=1). + + This is the default structuring element (selem) if no selem was specified. Parameters ---------- diff --git a/skimage/morphology/tests/test_selem.py b/skimage/morphology/tests/test_selem.py index 35559fe4..ebefdfa1 100644 --- a/skimage/morphology/tests/test_selem.py +++ b/skimage/morphology/tests/test_selem.py @@ -102,6 +102,25 @@ class TestSElem(): assert_equal(expected_mask1, actual_mask1) assert_equal(expected_mask2, actual_mask2) + def test_selem_ellipse(self): + """Test ellipse structuring elements""" + expected_mask1 = np.array([[0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]], dtype=np.uint8) + actual_mask1 = selem.ellipse(5, 3) + expected_mask2 = np.array([[1, 1, 1], + [1, 1, 1], + [1, 1, 1]], dtype=np.uint8) + actual_mask2 = selem.ellipse(1, 1) + assert_equal(expected_mask1, actual_mask1) + assert_equal(expected_mask2, actual_mask2) + assert_equal(expected_mask1, selem.ellipse(3, 5).T) + assert_equal(expected_mask2, selem.ellipse(1, 1).T) + def test_selem_star(self): """Test star structuring elements""" expected_mask1 = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],