Merge pull request #666 from jni/slic-scaling

Fix scaling (`ratio`) parameter in SLIC.
This commit is contained in:
Stefan van der Walt
2013-08-06 01:59:58 -07:00
3 changed files with 24 additions and 18 deletions
+4 -7
View File
@@ -17,7 +17,7 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
long[:, :, ::1] nearest_mean,
double[:, :, ::1] distance,
double[:, ::1] means,
float ratio, int max_iter, int n_segments):
int max_iter, int n_segments):
"""Helper function for SLIC segmentation.
Parameters
@@ -32,8 +32,6 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
The (initially infinity) array of distances to the nearest centroid.
means : 2D np.ndarray of double, shape (n_segments, 6)
The centroids obtained by SLIC.
ratio : float
The ratio of xyz-space and colorspace in the clustering.
max_iter : int
The maximum number of k-means iterations.
n_segments : int
@@ -58,7 +56,6 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
cdef Py_ssize_t i, k, x, y, z, x_min, x_max, y_min, y_max, z_min, z_max, \
changes
cdef double dist_mean
cdef double tmp
for i in range(max_iter):
changes = 0
@@ -81,8 +78,7 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
# squaring itself. mine can't (with O2)
tmp = image_zyx[z, y, x, c] - means[k, c]
dist_mean += tmp * tmp
# some precision issue here. Doesnt work if testing ">"
if distance[z, y, x] - dist_mean > 1e-10:
if distance[z, y, x] > dist_mean:
nearest_mean[z, y, x] = k
distance[z, y, x] = dist_mean
changes = 1
@@ -92,7 +88,8 @@ def _slic_cython(double[:, :, :, ::1] image_zyx,
nearest_mean_ravel = np.asarray(nearest_mean).ravel()
means_list = []
for j in range(6):
image_zyx_ravel = np.ascontiguousarray(image_zyx[:, :, :, j]).ravel()
image_zyx_ravel = (
np.ascontiguousarray(image_zyx[:, :, :, j]).ravel())
means_list.append(np.bincount(nearest_mean_ravel,
image_zyx_ravel))
in_mean = np.bincount(nearest_mean_ravel)
+15 -8
View File
@@ -10,8 +10,8 @@ from ..color import rgb2lab, gray2rgb, guess_spatial_dimensions
from ._slic import _slic_cython
def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1,
multichannel=None, convert2lab=True):
def slic(image, n_segments=100, compactness=10., max_iter=10, sigma=1,
multichannel=None, convert2lab=True, ratio=None):
"""Segments image using k-means clustering in Color-(x,y) space.
Parameters
@@ -21,9 +21,10 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1,
(see `multichannel` parameter).
n_segments : int, optional (default: 100)
The (approximate) number of labels in the segmented output image.
ratio: float, optional (default: 10)
Balances color-space proximity and image-space proximity.
Higher values give more weight to color-space.
compactness: float, optional (default: 10)
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.
max_iter : int, optional (default: 10)
Maximum number of iterations of k-means.
sigma : float, optional (default: 1)
@@ -38,6 +39,8 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1,
Whether the input should be converted to Lab colorspace prior to
segmentation. For this purpose, the input is assumed to be RGB. Highly
recommended.
ratio : float, optional
Synonym for `compactness`. This keyword is deprecated.
Returns
-------
@@ -79,6 +82,10 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1,
>>> # Increasing the ratio parameter yields more square regions
>>> segments = slic(img, n_segments=100, ratio=20)
"""
if ratio is not None:
msg = 'Keyword `ratio` is deprecated. Use `compactness` instead.'
warnings.warn(msg)
compactness = ratio
spatial_dims = guess_spatial_dimensions(image)
if spatial_dims is None and multichannel is None:
msg = ("Images with dimensions (M, N, 3) are interpreted as 2D+RGB" +
@@ -122,15 +129,15 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1,
means = np.ascontiguousarray(means)
# we do the scaling of ratio in the same way as in the SLIC paper
# so the values have the same meaning
ratio = (ratio / float(max((step_z, step_y, step_x)))) ** 2
ratio = float(max((step_z, step_y, step_x))) / compactness
image_zyx = np.concatenate([grid_z[..., np.newaxis],
grid_y[..., np.newaxis],
grid_x[..., np.newaxis],
image / ratio], axis=-1).copy("C")
image * ratio], axis=-1).copy("C")
nearest_mean = np.zeros((depth, height, width), dtype=np.intp)
distance = np.empty((depth, height, width), dtype=np.float)
segment_map = _slic_cython(image_zyx, nearest_mean, distance, means,
ratio, max_iter, n_segments)
max_iter, n_segments)
if segment_map.shape[0] == 1:
segment_map = segment_map[0]
return segment_map
+5 -3
View File
@@ -16,7 +16,7 @@ def test_color_2d():
img[img < 0] = 0
with warnings.catch_warnings():
warnings.simplefilter("ignore")
seg = slic(img, sigma=0, n_segments=4)
seg = slic(img, n_segments=4, sigma=0)
# we expect 4 segments
assert_equal(len(np.unique(seg)), 4)
@@ -35,7 +35,8 @@ def test_gray_2d():
img += 0.0033 * rnd.normal(size=img.shape)
img[img > 1] = 1
img[img < 0] = 0
seg = slic(img, sigma=0, n_segments=4, ratio=20.0, multichannel=False)
seg = slic(img, sigma=0, n_segments=4, compactness=20.0,
multichannel=False)
assert_equal(len(np.unique(seg)), 4)
assert_array_equal(seg[:10, :10], 0)
@@ -79,7 +80,8 @@ def test_gray_3d():
img += 0.001 * rnd.normal(size=img.shape)
img[img > 1] = 1
img[img < 0] = 0
seg = slic(img, sigma=0, n_segments=8, ratio=20.0, multichannel=False)
seg = slic(img, sigma=0, n_segments=8, compactness=20.0,
multichannel=False)
assert_equal(len(np.unique(seg)), 8)
for s, c in zip(slices, range(8)):