mirror of
https://github.com/wassname/scikit-image.git
synced 2026-08-04 13:14:23 +08:00
Merge pull request #857 from guillempalou/slic-connectivity
Enforce SLIC superpixels connectivity
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
Version 0.11
|
||||
------------
|
||||
* Remove deprecated `reverse_map` parameter of `skimage.transform.warp`
|
||||
|
||||
* Change depecrated `enforce_connectivity=False`on skimage.segmentation.slic
|
||||
and set it to True as default
|
||||
|
||||
Version 0.10
|
||||
------------
|
||||
|
||||
+1
-1
@@ -68,4 +68,4 @@ def _changed(filename):
|
||||
with open(filename_cache, 'wb') as cf:
|
||||
cf.write(md5_new.encode('utf-8'))
|
||||
|
||||
return md5_cached != md5_new
|
||||
return md5_cached != md5_new.encode('utf-8')
|
||||
|
||||
@@ -9,7 +9,6 @@ cimport numpy as cnp
|
||||
|
||||
from skimage.util import regular_grid
|
||||
|
||||
|
||||
def _slic_cython(double[:, :, :, ::1] image_zyx,
|
||||
double[:, ::1] segments,
|
||||
Py_ssize_t max_iter,
|
||||
@@ -146,3 +145,104 @@ 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,
|
||||
Py_ssize_t min_size,
|
||||
Py_ssize_t 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[::1] ddx = np.array((1, -1, 0, 0, 0, 0))
|
||||
cdef Py_ssize_t[::1] ddy = np.array((0, 0, 1, -1, 0, 0))
|
||||
cdef Py_ssize_t[::1] ddz = np.array((0, 0, 0, 0, 1, -1))
|
||||
|
||||
# new object with connected segments initialized to -1
|
||||
cdef Py_ssize_t[:, :, ::1] connected_segments \
|
||||
= -1 * np.ones_like(segments, 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 current_segment_size = 1
|
||||
cdef Py_ssize_t bfs_visited = 0
|
||||
cdef Py_ssize_t adjacent
|
||||
|
||||
cdef Py_ssize_t zz, yy, xx
|
||||
|
||||
cdef Py_ssize_t[:, ::1] 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 connected_segments[z, y, x] >= 0:
|
||||
continue
|
||||
# find the component size
|
||||
adjacent = 0
|
||||
label = segments[z, y, x]
|
||||
connected_segments[z, y, x] = current_new_label
|
||||
current_segment_size = 1
|
||||
bfs_visited = 0
|
||||
coord_list[bfs_visited, 0] = z
|
||||
coord_list[bfs_visited, 1] = y
|
||||
coord_list[bfs_visited, 2] = x
|
||||
|
||||
#perform a breadth first search to find
|
||||
# the size of the connected component
|
||||
while bfs_visited != current_segment_size:
|
||||
for i in range(6):
|
||||
zz = coord_list[bfs_visited, 0] + ddz[i]
|
||||
yy = coord_list[bfs_visited, 1] + ddy[i]
|
||||
xx = coord_list[bfs_visited, 2] + ddx[i]
|
||||
if (0 <= xx < width and
|
||||
0 <= yy < height and
|
||||
0 <= zz < depth):
|
||||
if (segments[zz, yy, xx] == label and
|
||||
connected_segments[zz, yy, xx] == -1):
|
||||
connected_segments[zz, yy, xx] = \
|
||||
current_new_label
|
||||
coord_list[current_segment_size, 0] = zz
|
||||
coord_list[current_segment_size, 1] = yy
|
||||
coord_list[current_segment_size, 2] = xx
|
||||
current_segment_size += 1
|
||||
elif (connected_segments[zz, yy, xx] >= 0 and
|
||||
connected_segments[zz, yy, xx] != current_new_label):
|
||||
adjacent = connected_segments[zz, yy, xx]
|
||||
bfs_visited += 1
|
||||
|
||||
# change to an adjacent one, like in the original paper
|
||||
if current_segment_size < min_size:
|
||||
for i in range(current_segment_size):
|
||||
connected_segments[coord_list[i, 0],
|
||||
coord_list[i, 1],
|
||||
coord_list[i, 2]] = adjacent
|
||||
else:
|
||||
current_new_label += 1
|
||||
|
||||
return np.asarray(connected_segments)
|
||||
|
||||
@@ -6,12 +6,13 @@ from scipy import ndimage
|
||||
import warnings
|
||||
|
||||
from skimage.util import img_as_float, regular_grid
|
||||
from skimage.segmentation._slic import _slic_cython
|
||||
from skimage.segmentation._slic import _slic_cython, _enforce_label_connectivity_cython
|
||||
from skimage.color import rgb2lab
|
||||
|
||||
|
||||
def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
|
||||
spacing=None, multichannel=True, convert2lab=True, ratio=None):
|
||||
spacing=None, multichannel=True, convert2lab=True, ratio=None,
|
||||
enforce_connectivity=False, min_size_factor=0.5, max_size_factor=3):
|
||||
"""Segments image using k-means clustering in Color-(x,y,z) space.
|
||||
|
||||
Parameters
|
||||
@@ -47,7 +48,14 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
|
||||
recommended.
|
||||
ratio : float, optional
|
||||
Synonym for `compactness`. This keyword is deprecated.
|
||||
|
||||
enforce_connectivity: bool, optional (default False)
|
||||
Whether the generated segments are connected or not
|
||||
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, optional
|
||||
Proportion of the maximum connected segment size. A value of 3 works
|
||||
in most of the cases.
|
||||
Returns
|
||||
-------
|
||||
labels : 2D or 3D array
|
||||
@@ -104,6 +112,11 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
|
||||
'instead.')
|
||||
compactness = ratio
|
||||
|
||||
if enforce_connectivity is None:
|
||||
warnings.warn('Deprecation: enforce_connectivity will default to'
|
||||
' True in future versions.')
|
||||
enforce_connectivity = False
|
||||
|
||||
image = img_as_float(image)
|
||||
is_2d = False
|
||||
if image.ndim == 2:
|
||||
@@ -163,6 +176,15 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
|
||||
|
||||
labels = _slic_cython(image, segments, max_iter, spacing)
|
||||
|
||||
if enforce_connectivity:
|
||||
segment_size = depth * height * width / n_segments
|
||||
min_size = int(min_size_factor * segment_size)
|
||||
max_size = int(max_size_factor * segment_size)
|
||||
labels = _enforce_label_connectivity_cython(labels,
|
||||
n_segments,
|
||||
min_size,
|
||||
max_size)
|
||||
|
||||
if is_2d:
|
||||
labels = labels[0]
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ def test_gray_3d():
|
||||
midpoint = dim_size // 2
|
||||
slices.append((slice(None, midpoint), slice(midpoint, None)))
|
||||
slices = list(it.product(*slices))
|
||||
shades = np.arange(0, 1.000001, 1.0/7)
|
||||
shades = np.arange(0, 1.000001, 1.0 / 7)
|
||||
for s, sh in zip(slices, shades):
|
||||
img[s] = sh
|
||||
img += 0.001 * rnd.normal(size=img.shape)
|
||||
@@ -120,11 +120,34 @@ def test_spacing():
|
||||
|
||||
def test_invalid_lab_conversion():
|
||||
img = np.array([[1, 1, 1, 0, 0],
|
||||
[1, 1, 0, 0, 0]], np.float)
|
||||
[1, 1, 0, 0, 0]], np.float) + 1
|
||||
assert_raises(ValueError, slic, img, multichannel=True, convert2lab=True)
|
||||
|
||||
|
||||
def test_enforce_connectivity():
|
||||
img = np.array([[0, 0, 0, 1, 1, 1],
|
||||
[1, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 1, 1, 0]], np.float)
|
||||
|
||||
segments_connected = slic(img, 2, compactness=0.0001,
|
||||
enforce_connectivity=True,
|
||||
convert2lab=False)
|
||||
segments_disconnected = slic(img, 2, compactness=0.0001,
|
||||
enforce_connectivity=False,
|
||||
convert2lab=False)
|
||||
|
||||
result_connected = np.array([[0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 1, 1, 1]], np.float)
|
||||
|
||||
result_disconnected = np.array([[0, 0, 0, 1, 1, 1],
|
||||
[1, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 1, 1, 0]], np.float)
|
||||
|
||||
assert_equal(segments_connected, result_connected)
|
||||
assert_equal(segments_disconnected, result_disconnected)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from numpy import testing
|
||||
|
||||
testing.run_module_suite()
|
||||
|
||||
Reference in New Issue
Block a user