diff --git a/skimage/segmentation/_slic.pyx b/skimage/segmentation/_slic.pyx index 6f97abff..8f5af582 100644 --- a/skimage/segmentation/_slic.pyx +++ b/skimage/segmentation/_slic.pyx @@ -9,107 +9,10 @@ cimport numpy as cnp from skimage.util import regular_grid -def _enforce_label_connectivity_cython(Py_ssize_t[:, :, ::1] nearest_segments, - Py_ssize_t n_segments, - int min_size, - int max_size): - """ Helper function to remove small disconnected regions from the labels - - Parameters - ---------- - nearest_segments : 3D array of int, shape (Z, Y, X) - The label field/superpixels found by SLIC. - n_segments: int - number of specified segments - min_size: int - minimum size of the segment - max_size: int - maximum size of the segment. This is done for performance reasons, - to pre-allocate a sufficiently large array for the breadth first search - Returns - ------- - connected_nearest_segments : 3D array of int, shape (Z, Y, X) - A label field with connected labels starting at label=1 - """ - - #get image dimensions - cdef Py_ssize_t depth, height, width - depth = nearest_segments.shape[0] - height = nearest_segments.shape[1] - width = nearest_segments.shape[2] - - #neighborhood arrays - cdef Py_ssize_t[:] ddx = np.array((1,-1,0,0,0,0)) - cdef Py_ssize_t[:] ddy = np.array((0,0,1,-1,0,0)) - cdef Py_ssize_t[:] ddz = np.array((0,0,0,0,1,-1)) - - #new object with connected segments - cdef Py_ssize_t[:, :, ::1] new_nearest_segments \ - = np.zeros((depth, height, width), dtype=np.intp) - - cdef Py_ssize_t current_new_label = 0 - cdef Py_ssize_t label = 0 - - #variables for the breadth first search - cdef Py_ssize_t count = 1 - cdef Py_ssize_t p = 0 - cdef Py_ssize_t adjacent - - cdef Py_ssize_t zz,yy,xx - - cdef Py_ssize_t[:, :] coord_list \ - = np.zeros((max_size,3), dtype=np.intp) - - #loop through all image - for z in range(depth): - for y in range(height): - for x in range(width): - if (new_nearest_segments[z,y,x] > 0): - continue - #find the component size - adjacent = 0 - label = nearest_segments[z,y,x] - current_new_label = current_new_label+1 - new_nearest_segments[z,y,x] = current_new_label - - count = 1 - p = 0 - coord_list[p,0] = z - coord_list[p,1] = y - coord_list[p,2] = x - - #perform a breadth first search to find the size of the connected component - while (p != count): - for i in range(6): - zz = coord_list[p,0] + ddz[i] - yy = coord_list[p,1] + ddy[i] - xx = coord_list[p,2] + ddx[i] - if (xx >= 0 and xx < width and yy >= 0 and yy < height and zz >= 0 and zz < depth): - if (nearest_segments[zz,yy,xx] == label and new_nearest_segments[zz,yy,xx] == 0): - new_nearest_segments[zz,yy,xx] = current_new_label - coord_list[count,0] = zz - coord_list[count,1] = yy - coord_list[count,2] = xx - count = count + 1 - elif (new_nearest_segments[zz,yy,xx] > 0 and new_nearest_segments[zz,yy,xx] != current_new_label): - adjacent = new_nearest_segments[zz,yy,xx] - p = p + 1 - - - #change to an adjacent one, like in the original paper - if (count < min_size): - #print("Changing segment {0} label {1} ".format(current_new_label, label)) - for i in range(count): - new_nearest_segments[coord_list[i,0],coord_list[i,1],coord_list[i,2]] = adjacent - - return np.asarray(new_nearest_segments) - def _slic_cython(double[:, :, :, ::1] image_zyx, double[:, ::1] segments, Py_ssize_t max_iter, - double[::1] spacing, - int enforce_connectivity, - Py_ssize_t min_size = True): + double[::1] spacing): """Helper function for SLIC segmentation. Parameters @@ -124,8 +27,6 @@ def _slic_cython(double[:, :, :, ::1] image_zyx, The voxel spacing along each image dimension. This parameter controls the weights of the distances along z, y, and x during k-means clustering. - enforce_connectivity: int indicating whether the returned label - field must have connected labels Returns ------- @@ -214,7 +115,8 @@ def _slic_cython(double[:, :, :, ::1] image_zyx, dist_center += (image_zyx[z, y, x, c - 3] - segments[k, c]) ** 2 if distance[z, y, x] > dist_center: - nearest_segments[z, y, x] = k + # segments start at 1 + nearest_segments[z, y, x] = k+1 distance[z, y, x] = dist_center change = 1 @@ -230,7 +132,8 @@ def _slic_cython(double[:, :, :, ::1] image_zyx, for z in range(depth): for y in range(height): for x in range(width): - k = nearest_segments[z, y, x] + #compensate the label offset 1 + k = nearest_segments[z, y, x] - 1 n_segment_elems[k] += 1 segments[k, 0] += z segments[k, 1] += y @@ -244,3 +147,97 @@ def _slic_cython(double[:, :, :, ::1] image_zyx, segments[k, c] /= n_segment_elems[k] return np.asarray(nearest_segments) + + +def _enforce_label_connectivity_cython(Py_ssize_t[:, :, ::1] segments, + Py_ssize_t n_segments, + int min_size, + int max_size): + """ Helper function to remove small disconnected regions from the labels + + Parameters + ---------- + segments : 3D array of int, shape (Z, Y, X) + The label field/superpixels found by SLIC. + n_segments: int + number of specified segments + min_size: int + minimum size of the segment + max_size: int + maximum size of the segment. This is done for performance reasons, + to pre-allocate a sufficiently large array for the breadth first search + Returns + ------- + connected_segments : 3D array of int, shape (Z, Y, X) + A label field with connected labels starting at label=1 + """ + + #get image dimensions + cdef Py_ssize_t depth, height, width + depth = segments.shape[0] + height = segments.shape[1] + width = segments.shape[2] + + #neighborhood arrays + cdef Py_ssize_t[:] ddx = np.array((1,-1,0,0,0,0)) + cdef Py_ssize_t[:] ddy = np.array((0,0,1,-1,0,0)) + cdef Py_ssize_t[:] ddz = np.array((0,0,0,0,1,-1)) + + #new object with connected segments + cdef Py_ssize_t[:, :, ::1] connected_segments = np.zeros_like(segments) + + cdef Py_ssize_t current_new_label = 0 + cdef Py_ssize_t label = 0 + + #variables for the breadth first search + cdef Py_ssize_t count = 1 + cdef Py_ssize_t p = 0 + cdef Py_ssize_t adjacent + + cdef Py_ssize_t zz,yy,xx + + cdef Py_ssize_t[:, :] coord_list = np.zeros((max_size,3), dtype=np.intp) + + #loop through all image + for z in range(depth): + for y in range(height): + for x in range(width): + if (new_segments[z,y,x] > 0): + continue + #find the component size + adjacent = 0 + label = segments[z,y,x] + current_new_label += 1 + connected_segments[z,y,x] = current_new_label + + count = 1 + p = 0 + coord_list[p,0] = z + coord_list[p,1] = y + coord_list[p,2] = x + + #perform a breadth first search to find the size of the connected component + while (p != count): + for i in range(6): + zz = coord_list[p,0] + ddz[i] + yy = coord_list[p,1] + ddy[i] + xx = coord_list[p,2] + ddx[i] + if (xx >= 0 and xx < width and yy >= 0 and yy < height and zz >= 0 and zz < depth): + if (segments[zz,yy,xx] == label and connected_segments[zz,yy,xx] == 0): + connected_segments[zz,yy,xx] = current_new_label + coord_list[count,0] = zz + coord_list[count,1] = yy + coord_list[count,2] = xx + count = count + 1 + elif (new_segments[zz,yy,xx] > 0 and connected_segments[zz,yy,xx] != current_new_label): + adjacent = connected_segments[zz,yy,xx] + p = p + 1 + + + #change to an adjacent one, like in the original paper + if (count < min_size): + connected_segments[*coords.T] = adjacent + #for i in range(count): + # connected_segments[coord_list[i,0],coord_list[i,1],coord_list[i,2]] = adjacent + + return np.asarray(connected_segments) \ No newline at end of file diff --git a/skimage/segmentation/slic_superpixels.py b/skimage/segmentation/slic_superpixels.py index 60d4ac13..98801c62 100644 --- a/skimage/segmentation/slic_superpixels.py +++ b/skimage/segmentation/slic_superpixels.py @@ -50,10 +50,10 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None, Synonym for `compactness`. This keyword is deprecated. enforce_connectivity: bool, optional Whether the generated segments are connected or not - min_size_factor: float + min_size_factor: float, optional proportion of the minimum segment size to be removed with respect to the supposed segment size (depth*width*height/n_segments) - max_size_factor: float + max_size_factor: float, optional proportion of the maximum connected segment size. A value of 3 works in most of the cases. Returns @@ -169,14 +169,14 @@ 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, spacing, enforce_connectivity) + labels = _slic_cython(image, segments, max_iter, spacing) if (enforce_connectivity): - segment_size = depth*height*width/n_segments + segment_size = depth * height * width / n_segments labels = _enforce_label_connectivity_cython(labels, n_segments, - min_size_factor*segment_size, - max_size_factor*segment_size) + min_size_factor * segment_size, + max_size_factor * segment_size) if is_2d: labels = labels[0]