mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-03 11:27:45 +08:00
102 lines
4.0 KiB
Cython
102 lines
4.0 KiB
Cython
#cython: cdivision=True
|
|
#cython: boundscheck=False
|
|
#cython: nonecheck=False
|
|
#cython: wraparound=False
|
|
import collections as coll
|
|
import numpy as np
|
|
from time import time
|
|
from scipy import ndimage
|
|
|
|
cimport numpy as cnp
|
|
|
|
from ..util import img_as_float, regular_grid
|
|
from ..color import rgb2lab, gray2rgb
|
|
|
|
|
|
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):
|
|
"""Helper function for SLIC segmentation.
|
|
|
|
Parameters
|
|
----------
|
|
image_zyx : 4D np.ndarray of double, shape (Z, Y, X, 6)
|
|
The image with embedded coordinates, that is, `image_zyx[i, j, k]` is
|
|
`array([i, j, k, r, g, b])` or `array([i, j, k, L, a, b])`, depending
|
|
on the colorspace.
|
|
nearest_mean : 3D np.ndarray of long, shape (Z, Y, X)
|
|
The (initially empty) label field.
|
|
distance : 3D np.ndarray of double, shape (Z, Y, X)
|
|
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
|
|
The approximate/desired number of segments.
|
|
|
|
Returns
|
|
-------
|
|
nearest_mean : 3D np.ndarray of long, shape (Z, Y, X)
|
|
The label field/superpixels found by SLIC.
|
|
"""
|
|
|
|
# initialize on grid:
|
|
cdef Py_ssize_t depth, height, width
|
|
depth, height, width = (image_zyx.shape[0], image_zyx.shape[1],
|
|
image_zyx.shape[2])
|
|
# approximate grid size for desired n_segments
|
|
cdef Py_ssize_t step_z, step_y, step_x
|
|
slices = regular_grid((depth, height, width), n_segments)
|
|
step_z, step_y, step_x = [int(s.step) for s in slices]
|
|
|
|
n_means = means.shape[0]
|
|
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
|
|
distance[:, :, :] = np.inf
|
|
# assign pixels to means
|
|
for k in range(n_means):
|
|
# compute windows:
|
|
z_min = int(max(means[k, 0] - 2 * step_z, 0))
|
|
z_max = int(min(means[k, 0] + 2 * step_z, depth))
|
|
y_min = int(max(means[k, 1] - 2 * step_y, 0))
|
|
y_max = int(min(means[k, 1] + 2 * step_y, height))
|
|
x_min = int(max(means[k, 2] - 2 * step_x, 0))
|
|
x_max = int(min(means[k, 2] + 2 * step_x, width))
|
|
for z in range(z_min, z_max):
|
|
for y in range(y_min, y_max):
|
|
for x in range(x_min, x_max):
|
|
dist_mean = 0
|
|
for c in range(6):
|
|
# you would think the compiler can optimize the
|
|
# 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:
|
|
nearest_mean[z, y, x] = k
|
|
distance[z, y, x] = dist_mean
|
|
changes = 1
|
|
if changes == 0:
|
|
break
|
|
# recompute means:
|
|
nearest_mean_ravel = np.asarray(nearest_mean).ravel()
|
|
means_list = []
|
|
for j in range(6):
|
|
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)
|
|
in_mean[in_mean == 0] = 1
|
|
means = (np.vstack(means_list) / in_mean).T.copy("C")
|
|
return np.ascontiguousarray(nearest_mean)
|