This commit is contained in:
Andreas Mueller
2012-08-05 20:25:34 +01:00
parent 1db4ebcecb
commit 530f2c31c4
6 changed files with 53 additions and 39 deletions
-1
View File
@@ -88,7 +88,6 @@ test = _setup_test()
test_verbose = _setup_test(verbose=True)
def get_log(name=None):
"""Return a console logger.
@@ -77,13 +77,11 @@ def test_otsu_camera_image():
assert 86 < threshold_otsu(camera) < 88
def test_otsu_coins_image():
coins = skimage.img_as_ubyte(data.coins())
assert 106 < threshold_otsu(coins) < 108
def test_otsu_coins_image_as_float():
coins = skimage.img_as_float(data.coins())
assert 0.41 < threshold_otsu(coins) < 0.42
-3
View File
@@ -3,6 +3,3 @@ from .felzenszwalb import felzenszwalb_segmentation
from .slic import slic
from .quickshift import quickshift
from .boundaries import find_boundaries, visualize_boundaries
__all__ = [random_walker, quickshift, felzenszwalb_segmentation,
slic, find_boundaries, visualize_boundaries]
+12 -8
View File
@@ -8,7 +8,7 @@ from ..util import img_as_float
def _felzenszwalb_segmentation_grey(image, scale=1, sigma=0.8, min_size=20):
"""Computes Felsenszwalb's efficient graph based segmentation for a single channel.
"""Felzenszwalb's efficient graph based segmentation for a single channel.
Produces an oversegmentation of a 2d image using a fast, minimum spanning
tree based clustering on the image grid. The parameter ``scale`` sets an
@@ -37,8 +37,8 @@ def _felzenszwalb_segmentation_grey(image, scale=1, sigma=0.8, min_size=20):
Integer mask indicating segment labels.
"""
if image.ndim != 2:
raise ValueError("This algorithm works only on single-channel 2d images."
"Got image of shape %s" % str(image.shape))
raise ValueError("This algorithm works only on single-channel 2d"
"images. Got image of shape %s" % str(image.shape))
image = img_as_float(image)
# rescale scale to behave like in reference implementation
scale = float(scale) / 255.
@@ -49,16 +49,19 @@ def _felzenszwalb_segmentation_grey(image, scale=1, sigma=0.8, min_size=20):
down_cost = np.abs((image[:, 1:] - image[:, :-1]))
dright_cost = np.abs((image[1:, 1:] - image[:-1, :-1]))
uright_cost = np.abs((image[1:, :-1] - image[:-1, 1:]))
cdef np.ndarray[np.float_t, ndim=1] costs = np.hstack([right_cost.ravel(), down_cost.ravel(),
dright_cost.ravel(), uright_cost.ravel()]).astype(np.float)
cdef np.ndarray[np.float_t, ndim=1] costs = np.hstack([right_cost.ravel(),
down_cost.ravel(), dright_cost.ravel(),
uright_cost.ravel()]).astype(np.float)
# compute edges between pixels:
width, height = image.shape[:2]
cdef np.ndarray[np.int_t, ndim=2] segments = np.arange(width * height).reshape(width, height)
cdef np.ndarray[np.int_t, ndim=2] segments \
= np.arange(width * height).reshape(width, height)
right_edges = np.c_[segments[1:, :].ravel(), segments[:-1, :].ravel()]
down_edges = np.c_[segments[:, 1:].ravel(), segments[:, :-1].ravel()]
dright_edges = np.c_[segments[1:, 1:].ravel(), segments[:-1, :-1].ravel()]
uright_edges = np.c_[segments[:-1, 1:].ravel(), segments[1:, :-1].ravel()]
cdef np.ndarray[np.int_t, ndim=2] edges = np.vstack([right_edges, down_edges, dright_edges, uright_edges])
cdef np.ndarray[np.int_t, ndim=2] edges \
= np.vstack([right_edges, down_edges, dright_edges, uright_edges])
# initialize data structures for segment size
# and inner cost, then start greedy iteration over edges.
edge_queue = np.argsort(costs)
@@ -67,7 +70,8 @@ def _felzenszwalb_segmentation_grey(image, scale=1, sigma=0.8, min_size=20):
cdef np.int_t *segments_p = <np.int_t*>segments.data
cdef np.int_t *edges_p = <np.int_t*>edges.data
cdef np.float_t *costs_p = <np.float_t*>costs.data
cdef np.ndarray[np.int_t, ndim=1] segment_size = np.ones(width * height, dtype=np.int)
cdef np.ndarray[np.int_t, ndim=1] segment_size \
= np.ones(width * height, dtype=np.int)
# inner cost of segments
cdef np.ndarray[np.float_t, ndim=1] cint = np.zeros(width * height)
cdef int seg0, seg1, seg_new
+22 -13
View File
@@ -16,14 +16,16 @@ cdef extern from "math.h":
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False, sigma=0, convert2lab=True, random_seed=None):
def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False,
sigma=0, convert2lab=True, random_seed=None):
"""Segments image using quickshift clustering in Color-(x,y) space.
Produces an oversegmentation of the image using the quickshift mode-seeking algorithm.
Produces an oversegmentation of the image using the quickshift mode-seeking
algorithm.
Parameters
----------
image: (width, height, channels) ndarray
image: (width, height, channels) ndarray
Input image
ratio: float, between 0 and 1.
Balances color-space proximity and image-space proximity.
@@ -39,8 +41,8 @@ def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False, s
sigma: float
Width for Gaussian smoothing as preprocessing. Zero means no smoothing.
convert2lab: bool
Whether the input should be converted to Lab colorspace prior to segmentation.
For this purpose, the input is assumed to be RGB.
Whether the input should be converted to Lab colorspace prior to
segmentation. For this purpose, the input is assumed to be RGB.
random_seed: None or int
Random seed used for breaking ties
@@ -51,12 +53,14 @@ def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False, s
Notes
-----
The authors advocate to convert the image to Lab color space prior to segmentation, though
this is not strictly necessary. For this to work, the image must be given in RGB format.
The authors advocate to convert the image to Lab color space prior to
segmentation, though this is not strictly necessary. For this to work, the
image must be given in RGB format.
References
----------
.. [1] Quick shift and kernel methods for mode seeking, Vedaldi, A. and Soatto, S.
.. [1] Quick shift and kernel methods for mode seeking,
Vedaldi, A. and Soatto, S.
European Conference on Computer Vision, 2008
@@ -68,7 +72,8 @@ def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False, s
image = rgb2lab(image)
image = ndimage.gaussian_filter(img_as_float(image), [sigma, sigma, 0])
cdef np.ndarray[dtype=np.float_t, ndim=3, mode="c"] image_c = np.ascontiguousarray(image) * ratio
cdef np.ndarray[dtype=np.float_t, ndim=3, mode="c"] image_c \
= np.ascontiguousarray(image) * ratio
if random_seed is None:
random_state = np.random.RandomState()
@@ -98,7 +103,8 @@ def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False, s
cdef np.float_t* image_p = <np.float_t*> image_c.data
cdef np.float_t* current_pixel_p = image_p
cdef np.ndarray[dtype=np.float_t, ndim=2] densities = np.zeros((width, height))
cdef np.ndarray[dtype=np.float_t, ndim=2] densities \
= np.zeros((width, height))
# compute densities
for x, y in product(xrange(width), xrange(height)):
x_min, x_max = max(x - w, 0), min(x + w + 1, width)
@@ -115,8 +121,10 @@ def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False, s
densities += random_state.normal(scale=0.00001, size=(width, height))
# default parent to self:
cdef np.ndarray[dtype=np.int_t, ndim=2] parent = np.arange(width * height).reshape(width, height)
cdef np.ndarray[dtype=np.float_t, ndim=2] dist_parent = np.zeros((width, height))
cdef np.ndarray[dtype=np.int_t, ndim=2] parent \
= np.arange(width * height).reshape(width, height)
cdef np.ndarray[dtype=np.float_t, ndim=2] dist_parent \
= np.zeros((width, height))
# find nearest node with higher density
current_pixel_p = image_p
for x, y in product(xrange(width), xrange(height)):
@@ -139,7 +147,8 @@ def quickshift(image, ratio=1., kernel_size=5, max_dist=10, return_tree=False, s
dist_parent_flat = dist_parent.ravel()
flat = parent.ravel()
# remove parents with distance > max_dist
flat[dist_parent_flat > max_dist] = np.arange(width * height)[dist_parent_flat > max_dist]
too_far = dist_parent_flat > max_dist
flat[too_far] = np.arange(width * height)[too_far]
old = np.zeros_like(flat)
# flatten forest (mark each pixel with root of corresponding tree)
while (old != flat).any():
+19 -12
View File
@@ -6,7 +6,8 @@ from ..util import img_as_float
from ..color import rgb2lab
def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1, convert2lab=True):
def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1,
convert2lab=True):
"""Segments image using k-means clustering in Color-(x,y) space.
Parameters
@@ -19,10 +20,12 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1, convert2lab=Tru
max_iter: int
maximum number of iterations of k-means
sigma: float
Width of Gaussian smoothing kernel for preprocessing. Zero means no smoothing.
Width of Gaussian smoothing kernel for preprocessing. Zero means no
smoothing.
convert2lab: bool
Whether the input should be converted to Lab colorspace prior to segmentation.
For this purpose, the input is assumed to be RGB. Highly recommended.
Whether the input should be converted to Lab colorspace prior to
segmentation. For this purpose, the input is assumed to be RGB. Highly
recommended.
Returns
-------
@@ -57,19 +60,23 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1, convert2lab=Tru
n_seeds = len(means_y)
means_color = np.zeros((n_seeds, n_seeds, 3))
cdef np.ndarray[dtype=np.float_t, ndim=2] means = np.dstack([means_y, means_x, means_color]).reshape(-1, 5)
cdef np.ndarray[dtype=np.float_t, ndim=2] means \
= np.dstack([means_y, means_x, means_color]).reshape(-1, 5)
cdef np.float_t* current_mean
cdef np.float_t* mean_entry
n_means = means.shape[0]
# 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(step)) ** 2
cdef np.ndarray[dtype=np.float_t, ndim=3] image_yx = np.dstack([grid_y, grid_x, image / ratio]).copy("C")
cdef np.ndarray[dtype=np.float_t, ndim=3] image_yx \
= np.dstack([grid_y, grid_x, image / ratio]).copy("C")
cdef int i, k, x, y, x_min, x_max, y_min, y_max, changes
cdef double dist_mean
cdef np.ndarray[dtype=np.int_t, ndim=2] nearest_mean = np.zeros((height, width), dtype=np.int)
cdef np.ndarray[dtype=np.float_t, ndim=2] distance = np.empty((height, width))
cdef np.ndarray[dtype=np.int_t, ndim=2] nearest_mean \
= np.zeros((height, width), dtype=np.int)
cdef np.ndarray[dtype=np.float_t, ndim=2] distance \
= np.empty((height, width))
cdef np.float_t* image_p = <np.float_t*> image_yx.data
cdef np.float_t* distance_p = <np.float_t*> distance.data
cdef np.float_t* current_distance
@@ -93,8 +100,8 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1, convert2lab=Tru
mean_entry = current_mean
dist_mean = 0
for c in range(5):
# you would think the compiler can optimize this itself.
# mine can't (with O2)
# you would think the compiler can optimize this
# itself. mine can't (with O2)
tmp = current_pixel[0] - mean_entry[0]
dist_mean += tmp * tmp
current_pixel += 1
@@ -109,8 +116,8 @@ def slic(image, n_segments=100, ratio=10., max_iter=10, sigma=1, convert2lab=Tru
if changes == 0:
break
# recompute means:
means_list = [np.bincount(nearest_mean.ravel(), image_yx[:, :, j].ravel())
for j in xrange(5)]
means_list = [np.bincount(nearest_mean.ravel(),
image_yx[:, :, j].ravel()) for j in xrange(5)]
in_mean = np.bincount(nearest_mean.ravel())
in_mean[in_mean == 0] = 1
means = (np.vstack(means_list) / in_mean).T.copy("C")