From 846765e5f9e35cd9be24d0a391a4fde106ba6b9e Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 16 Sep 2013 17:52:25 +1000 Subject: [PATCH] Add spacing support for new, speeded-up SLIC --- skimage/segmentation/_slic.pyx | 15 +++++++++++---- skimage/segmentation/slic_superpixels.py | 12 ++++++++++-- skimage/segmentation/tests/test_slic.py | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/skimage/segmentation/_slic.pyx b/skimage/segmentation/_slic.pyx index d818a501..57c9990a 100644 --- a/skimage/segmentation/_slic.pyx +++ b/skimage/segmentation/_slic.pyx @@ -12,7 +12,8 @@ from skimage.util import regular_grid def _slic_cython(double[:, :, :, ::1] image_zyx, double[:, ::1] segments, - Py_ssize_t max_iter): + Py_ssize_t max_iter, + double[:] spacing): """Helper function for SLIC segmentation. Parameters @@ -23,6 +24,7 @@ def _slic_cython(double[:, :, :, ::1] image_zyx, The initial centroids obtained by SLIC as [Z, Y, X, C...]. max_iter : int The maximum number of k-means iterations. + spacing : 1D array of double, shape (3,) Returns ------- @@ -55,6 +57,11 @@ def _slic_cython(double[:, :, :, ::1] image_zyx, cdef char change cdef double dist_center, cx, cy, cz, dy, dz + cdef double sz, sy, sx + sz = spacing[0] + sy = spacing[1] + sx = spacing[2] + for i in range(max_iter): change = 0 distance[:, :, :] = DBL_MAX @@ -76,11 +83,11 @@ def _slic_cython(double[:, :, :, ::1] image_zyx, x_max = min(cx + 2 * step_x + 1, width) for z in range(z_min, z_max): - dz = (cz - z) ** 2 + dz = (sz * (cz - z)) ** 2 for y in range(y_min, y_max): - dy = (cy - y) ** 2 + dy = (sy * (cy - y)) ** 2 for x in range(x_min, x_max): - dist_center = dz + dy + (cx - x) ** 2 + dist_center = dz + dy + (sx * (cx - x)) ** 2 for c in range(3, n_features): dist_center += (image_zyx[z, y, x, c - 3] - segments[k, c]) ** 2 diff --git a/skimage/segmentation/slic_superpixels.py b/skimage/segmentation/slic_superpixels.py index be07fafe..e9ae8ee7 100644 --- a/skimage/segmentation/slic_superpixels.py +++ b/skimage/segmentation/slic_superpixels.py @@ -11,7 +11,7 @@ from skimage.color import rgb2lab def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None, - multichannel=True, convert2lab=True, ratio=None): + spacing=None, multichannel=True, convert2lab=True, ratio=None): """Segments image using k-means clustering in Color-(x,y,z) space. Parameters @@ -31,6 +31,9 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None, Width of Gaussian smoothing kernel for pre-processing for each dimension of the image. The same sigma is applied to each dimension in case of a scalar value. Zero means no smoothing. + spacing : (3,) array-like of floats, optional + The voxel spacing along each image dimension. By default, `slic` + assumes uniform spacing (same voxel resolution along z, y and x). multichannel : bool, optional Whether the last axis of the image is to be interpreted as multiple channels or another spatial dimension. @@ -103,11 +106,16 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None, # Add channel as single last dimension image = image[..., np.newaxis] + if spacing is None: + spacing = np.ones(3) + elif type(spacing) in [list, tuple]: + spacing = np.array(spacing, float) if not isinstance(sigma, coll.Iterable): sigma = np.array([sigma, sigma, sigma], float) elif type(sigma) in [list, tuple]: sigma = np.array(sigma, float) if (sigma > 0).any(): + sigma /= spacing.astype(float) sigma = list(sigma) + [0] image = ndimage.gaussian_filter(image, sigma) @@ -139,7 +147,7 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None, ratio = float(max((step_z, step_y, step_x))) / compactness image = np.ascontiguousarray(image * ratio) - labels = _slic_cython(image, segments, max_iter) + labels = _slic_cython(image, segments, max_iter, spacing) if is2d: labels = labels[0] diff --git a/skimage/segmentation/tests/test_slic.py b/skimage/segmentation/tests/test_slic.py index 2a190de8..a4657785 100644 --- a/skimage/segmentation/tests/test_slic.py +++ b/skimage/segmentation/tests/test_slic.py @@ -101,6 +101,24 @@ def test_list_sigma(): assert_equal(seg_sigma, result_sigma) +def test_spacing(): + rnd = np.random.RandomState(0) + img = np.array([[1, 1, 1, 0, 0], + [1, 1, 0, 0, 0]], np.float) + result_non_spaced = np.array([[0, 0, 0, 1, 1], + [0, 0, 1, 1, 1]], np.int) + result_spaced = np.array([[0, 0, 0, 0, 0], + [1, 1, 1, 1, 1]], np.int) + img += 0.1 * rnd.normal(size=img.shape) + seg_non_spaced = slic(img, n_segments=2, sigma=0, multichannel=False, + compactness=1.0) + seg_spaced = slic(img, n_segments=2, sigma=0, spacing=[1, 500, 1], + compactness=1.0, multichannel=False) + assert_equal(seg_non_spaced, result_non_spaced) + assert_equal(seg_spaced, result_spaced) + + + if __name__ == '__main__': from numpy import testing testing.run_module_suite()