From cbf1657ee7980d29df671eef37f10a3e5b6a45c6 Mon Sep 17 00:00:00 2001 From: Almar Date: Wed, 27 Mar 2013 21:50:08 +0100 Subject: [PATCH 1/5] Add cube selem. --- skimage/morphology/selem.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/skimage/morphology/selem.py b/skimage/morphology/selem.py index 41be0737..a75c6d3e 100644 --- a/skimage/morphology/selem.py +++ b/skimage/morphology/selem.py @@ -114,3 +114,29 @@ def disk(radius, dtype=np.uint8): s = X**2 s += Y**2 return np.array(s <= radius * radius, dtype=dtype) + + +def cube(width, dtype=np.uint8): + """ + Generates a cube-shaped structuring element (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 + + Other Parameters + ---------------- + dtype : data-type + The data type of the structuring element. + + Returns + ------- + selem : ndarray + A structuring element consisting only of ones, i.e. every + pixel belongs to the neighborhood. + + """ + return np.ones((width, width, width), dtype=dtype) From 42c72ab10609105425c95e98bbcb52c29951a067 Mon Sep 17 00:00:00 2001 From: Almar Date: Wed, 27 Mar 2013 21:52:07 +0100 Subject: [PATCH 2/5] Add ball selem. I verified in 2D that this usage of np.mgrid yields the exact same results as np.meshgrid (np.meshgrid is not available for 3D). --- skimage/morphology/selem.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/skimage/morphology/selem.py b/skimage/morphology/selem.py index a75c6d3e..2e17a4a9 100644 --- a/skimage/morphology/selem.py +++ b/skimage/morphology/selem.py @@ -140,3 +140,34 @@ def cube(width, dtype=np.uint8): """ return np.ones((width, width, width), dtype=dtype) + + +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. + + Parameters + ---------- + radius : int + The radius of the ball-shaped structuring element. + + 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. + """ + n = 2 * radius + 1 + Z, Y, X = np.mgrid[ -radius:radius:n*1j, + -radius:radius:n*1j, + -radius:radius:n*1j] + s = X**2 + s += Y**2 + s += Z**2 + return np.array(s <= radius * radius, dtype=dtype) From fa6a30198c0621447b99e1fc073692b73722ec7d Mon Sep 17 00:00:00 2001 From: Almar Date: Wed, 27 Mar 2013 21:54:49 +0100 Subject: [PATCH 3/5] Fix typo in docstring of disk(). --- skimage/morphology/selem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/morphology/selem.py b/skimage/morphology/selem.py index 2e17a4a9..e27c6efb 100644 --- a/skimage/morphology/selem.py +++ b/skimage/morphology/selem.py @@ -93,7 +93,7 @@ def disk(radius, dtype=np.uint8): """ Generates a flat, disk-shaped structuring element of a given radius. A pixel is within the neighborhood if the euclidean distance between - it and the origin is no greater than a radius. + it and the origin is no greater than radius. Parameters ---------- From 77192b6e9a336269850b710283881c26917fde26 Mon Sep 17 00:00:00 2001 From: Almar Date: Thu, 28 Mar 2013 09:48:25 +0100 Subject: [PATCH 4/5] Adding octahedron selem (3D equivalent of diamond). --- skimage/morphology/selem.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/skimage/morphology/selem.py b/skimage/morphology/selem.py index e27c6efb..2cccae22 100644 --- a/skimage/morphology/selem.py +++ b/skimage/morphology/selem.py @@ -142,6 +142,38 @@ def cube(width, dtype=np.uint8): return np.ones((width, width, width), dtype=dtype) +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. + + Parameters + ---------- + radius : int + The radius of the octahedron-shaped structuring element. + + 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. + """ + # note that in contrast to diamond(), this method allows non-integer radii + n = 2 * radius + 1 + Z, Y, X = np.mgrid[ -radius:radius:n*1j, + -radius:radius:n*1j, + -radius:radius:n*1j] + s = np.abs(X) + np.abs(Y) + np.abs(Z) + return np.array(s <= radius, dtype=dtype) + + def ball(radius, dtype=np.uint8): """ Generates a ball-shaped structuring element of a given radius (the @@ -167,7 +199,5 @@ def ball(radius, dtype=np.uint8): Z, Y, X = np.mgrid[ -radius:radius:n*1j, -radius:radius:n*1j, -radius:radius:n*1j] - s = X**2 - s += Y**2 - s += Z**2 + s = X**2 + Y**2 + Z**2 return np.array(s <= radius * radius, dtype=dtype) From 644ab324ddf3e2bcc5ed07c400fdd4d3161b214c Mon Sep 17 00:00:00 2001 From: Almar Date: Thu, 28 Mar 2013 10:03:02 +0100 Subject: [PATCH 5/5] Added test for ball and octahedton selems. Note that only the center slice (in each dimension is tested) since there is no reference data available for these selems. I did visualize the 3D shapes to see whether they look right. --- skimage/morphology/tests/test_selem.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/skimage/morphology/tests/test_selem.py b/skimage/morphology/tests/test_selem.py index eec6cd5b..ccaf43a0 100644 --- a/skimage/morphology/tests/test_selem.py +++ b/skimage/morphology/tests/test_selem.py @@ -36,9 +36,32 @@ class TestSElem(): expected_mask = expected_mask[:, np.newaxis] assert_equal(expected_mask, actual_mask) k = k + 1 - + + def strel_worker_3d(self, fn, func): + matlab_masks = np.load(os.path.join(data_dir, fn)) + k = 0 + for arrname in sorted(matlab_masks): + expected_mask = matlab_masks[arrname] + actual_mask = func(k) + if (expected_mask.shape == (1,)): + expected_mask = expected_mask[:, np.newaxis] + # Test center slice for each dimension. This gives a good + # indication of validity without the need for a 3D reference + # mask. + c = int(expected_mask.shape[0]/2) + assert_equal(expected_mask, actual_mask[c,:,:]) + assert_equal(expected_mask, actual_mask[:,c,:]) + assert_equal(expected_mask, actual_mask[:,:,c]) + k = k + 1 + def test_selem_disk(self): self.strel_worker("disk-matlab-output.npz", selem.disk) def test_selem_diamond(self): self.strel_worker("diamond-matlab-output.npz", selem.diamond) + + def test_selem_ball(self): + self.strel_worker_3d("disk-matlab-output.npz", selem.ball) + + def test_selem_octahedron(self): + self.strel_worker_3d("diamond-matlab-output.npz", selem.octahedron)