Added SLIC-zero to SLIC and changed SLIC implementation slightly

This commit is contained in:
Michal Romaniuk
2013-11-27 17:07:48 +00:00
parent 25a32502e0
commit 0bd1d4490e
2 changed files with 95 additions and 20 deletions
+80 -15
View File
@@ -3,6 +3,7 @@
#cython: nonecheck=False
#cython: wraparound=False
from libc.float cimport DBL_MAX
from cpython cimport bool
import numpy as np
cimport numpy as cnp
@@ -11,8 +12,11 @@ from skimage.util import regular_grid
def _slic_cython(double[:, :, :, ::1] image_zyx,
double[:, ::1] segments,
float step,
Py_ssize_t max_iter,
double[::1] spacing):
double[::1] spacing,
bool slic_zero,
):
"""Helper function for SLIC segmentation.
Parameters
@@ -21,12 +25,17 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
The input image.
segments : 2D array of double, shape (N, 3 + C)
The initial centroids obtained by SLIC as [Z, Y, X, C...].
step : double
The size of the step between two seeds in voxels.
max_iter : int
The maximum number of k-means iterations.
spacing : 1D array of double, shape (3,)
The voxel spacing along each image dimension. This parameter
controls the weights of the distances along z, y, and x during
k-means clustering.
k-means clustering.
slic_zero : bool
True to run SLIC-ZERO, False to run original SLIC.
Returns
-------
@@ -85,6 +94,14 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
sy = spacing[1]
sx = spacing[2]
# The colors are scaled before being passed to _slic_cython so
# max_color_sq can be initialised as all ones
cdef double[::1] max_dist_color = np.ones(n_segments, dtype=np.double)
cdef double dist_color
# The reference implementation calls this invxywt
cdef double zyx_wt = float(1) / (step ** 2)
for i in range(max_iter):
change = 0
distance[:, :, :] = DBL_MAX
@@ -105,19 +122,42 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
x_min = <Py_ssize_t>max(cx - 2 * step_x, 0)
x_max = <Py_ssize_t>min(cx + 2 * step_x + 1, width)
for z in range(z_min, z_max):
dz = (sz * (cz - z)) ** 2
for y in range(y_min, y_max):
dy = (sy * (cy - y)) ** 2
for x in range(x_min, x_max):
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
if distance[z, y, x] > dist_center:
nearest_segments[z, y, x] = k
distance[z, y, x] = dist_center
change = 1
# The loop is duplicated to avoid looking up slic_zero in every
# iteration but perhaps it's better to improve readability at
# the cost of performance.
if slic_zero:
for z in range(z_min, z_max):
dz = (sz * (cz - z)) ** 2
for y in range(y_min, y_max):
dy = (sy * (cy - y)) ** 2
for x in range(x_min, x_max):
dist_center = (dz + dy + (sx * (cx - x)) ** 2) * zyx_wt
dist_color = 0
for c in range(3, n_features):
dist_color += (image_zyx[z, y, x, c - 3]
- segments[k, c]) ** 2
dist_center += dist_color / max_dist_color[k]
if distance[z, y, x] > dist_center:
nearest_segments[z, y, x] = k
distance[z, y, x] = dist_center
change = 1
else:
for z in range(z_min, z_max):
dz = (sz * (cz - z)) ** 2
for y in range(y_min, y_max):
dy = (sy * (cy - y)) ** 2
for x in range(x_min, x_max):
dist_center = (dz + dy + (sx * (cx - x)) ** 2) * zyx_wt
for c in range(3, n_features):
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
distance[z, y, x] = dist_center
change = 1
# stop if no pixel changed its segment
if change == 0:
@@ -144,6 +184,31 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
for c in range(n_features):
segments[k, c] /= n_segment_elems[k]
# If in SLICO mode, update the color distance maxima
if slic_zero:
for z in range(depth):
for y in range(height):
for x in range(width):
k = nearest_segments[z, y, x]
dist_color = 0
for c in range(3, n_features):
dist_color += (image_zyx[z, y, x, c - 3]
- segments[k, c]) ** 2
# The reference implementation seems to only change
# the color if it increases from previous iteration
if max_dist_color[k] < dist_color:
max_dist_color[k] = dist_color
## DEBUG
print('Iter %d' % (i,))
print(str(np.asarray(max_dist_color)))
print('Image: ')
print(str(np.asarray(image_zyx)))
return np.asarray(nearest_segments)
+15 -5
View File
@@ -25,7 +25,8 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
compactness : float, optional
Balances color-space proximity and image-space proximity. Higher
values give more weight to image-space. As `compactness` tends to
infinity, superpixel shapes become square/cubic.
infinity, superpixel shapes become square/cubic. In SLICO mode, this
is the initial compactness.
max_iter : int, optional
Maximum number of iterations of k-means.
sigma : float or (3,) array-like of floats, optional
@@ -46,6 +47,8 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
Whether the input should be converted to Lab colorspace prior to
segmentation. For this purpose, the input is assumed to be RGB. Highly
recommended.
slic_zero: bool, optional
True to run SLIC-zero, False to run original SLIC.
ratio : float, optional
Synonym for `compactness`. This keyword is deprecated.
enforce_connectivity: bool, optional (default False)
@@ -171,10 +174,17 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
# we do the scaling of ratio in the same way as in the SLIC paper
# so the values have the same meaning
ratio = float(max((step_z, step_y, step_x))) / compactness
image = np.ascontiguousarray(image * ratio)
step = float(max((step_z, step_y, step_x)))
ratio = float(1) / compactness
if slic_zero:
image = np.ascontiguousarray(image * ratio)
else:
image = np.ascontiguousarray(image * ratio)
labels = _slic_cython(image, segments, max_iter, spacing)
# _slic_cython expects the image in zyx format... but isn't image in xyz
# format???
labels = _slic_cython(image, segments, step, max_iter, spacing, slic_zero)
if enforce_connectivity:
segment_size = depth * height * width / n_segments
@@ -188,4 +198,4 @@ def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=None,
if is_2d:
labels = labels[0]
return labels
return labels