mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-01 13:28:44 +08:00
Merge pull request #377 from jni/jni-segmentation-join
Add functions to join and relabel segmentations
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
==========================================
|
||||
Find the intersection of two segmentations
|
||||
==========================================
|
||||
|
||||
When segmenting an image, you may want to combine multiple alternative
|
||||
segmentations. The `skimage.segmentation.join_segmentations` function
|
||||
computes the join of two segmentations, in which a pixel is placed in
|
||||
the same segment if and only if it is in the same segment in _both_
|
||||
segmentations.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from scipy import ndimage as nd
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
|
||||
from skimage.filter import sobel
|
||||
from skimage.segmentation import slic, join_segmentations
|
||||
from skimage.morphology import watershed
|
||||
|
||||
from skimage import data
|
||||
|
||||
coins = data.coins()
|
||||
|
||||
# make segmentation using edge-detection and watershed
|
||||
edges = sobel(coins)
|
||||
markers = np.zeros_like(coins)
|
||||
foreground, background = 1, 2
|
||||
markers[coins < 30] = background
|
||||
markers[coins > 150] = foreground
|
||||
|
||||
ws = watershed(edges, markers)
|
||||
seg1 = nd.label(ws == foreground)[0]
|
||||
|
||||
# make segmentation using SLIC superpixels
|
||||
|
||||
# make the RGB equivalent of `coins`
|
||||
coins_colour = np.tile(coins[..., np.newaxis], (1, 1, 3))
|
||||
seg2 = slic(coins_colour, n_segments=30, max_iter=160, sigma=1, ratio=9,
|
||||
convert2lab=False)
|
||||
|
||||
# combine the two
|
||||
segj = join_segmentations(seg1, seg2)
|
||||
|
||||
### Display the result ###
|
||||
|
||||
# make a random colormap for a set number of values
|
||||
def random_cmap(im):
|
||||
np.random.seed(9)
|
||||
cmap_array = np.concatenate(
|
||||
(np.zeros((1, 3)), np.random.rand(np.ceil(im.max()), 3)))
|
||||
return mpl.colors.ListedColormap(cmap_array)
|
||||
|
||||
# show the segmentations
|
||||
fig, axes = plt.subplots(ncols=4, figsize=(9, 2.5))
|
||||
axes[0].imshow(coins, cmap=plt.cm.gray, interpolation='nearest')
|
||||
axes[0].set_title('Image')
|
||||
axes[1].imshow(seg1, cmap=random_cmap(seg1), interpolation='nearest')
|
||||
axes[1].set_title('Sobel+Watershed')
|
||||
axes[2].imshow(seg2, cmap=random_cmap(seg2), interpolation='nearest')
|
||||
axes[2].set_title('SLIC superpixels')
|
||||
axes[3].imshow(segj, cmap=random_cmap(segj), interpolation='nearest')
|
||||
axes[3].set_title('Join')
|
||||
|
||||
for ax in axes:
|
||||
ax.axis('off')
|
||||
plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, right=1)
|
||||
plt.show()
|
||||
@@ -4,3 +4,4 @@ from ._slic import slic
|
||||
from ._quickshift import quickshift
|
||||
from .boundaries import find_boundaries, visualize_boundaries, mark_boundaries
|
||||
from ._clear_border import clear_border
|
||||
from ._join import join_segmentations, relabel_from_one
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import numpy as np
|
||||
|
||||
def join_segmentations(s1, s2):
|
||||
"""Return the join of the two input segmentations.
|
||||
|
||||
The join J of S1 and S2 is defined as the segmentation in which two voxels
|
||||
are in the same segment in J if and only if they are in the same segment
|
||||
in *both* S1 and S2.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
s1, s2 : numpy arrays
|
||||
s1 and s2 are label fields of the same shape.
|
||||
|
||||
Returns
|
||||
-------
|
||||
j : numpy array
|
||||
The join segmentation of s1 and s2.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from skimage.segmentation import join_segmentations
|
||||
>>> s1 = np.array([[0, 0, 1, 1],
|
||||
... [0, 2, 1, 1],
|
||||
... [2, 2, 2, 1]])
|
||||
>>> s2 = np.array([[0, 1, 1, 0],
|
||||
... [0, 1, 1, 0],
|
||||
... [0, 1, 1, 1]])
|
||||
>>> join_segmentations(s1, s2)
|
||||
array([[0, 1, 3, 2],
|
||||
[0, 5, 3, 2],
|
||||
[4, 5, 5, 3]])
|
||||
"""
|
||||
if s1.shape != s2.shape:
|
||||
raise ValueError("Cannot join segmentations of different shape. " +
|
||||
"s1.shape: %s, s2.shape: %s" % (s1.shape, s2.shape))
|
||||
s1 = relabel_from_one(s1)[0]
|
||||
s2 = relabel_from_one(s2)[0]
|
||||
j = (s2.max() + 1) * s1 + s2
|
||||
j = relabel_from_one(j)[0]
|
||||
return j
|
||||
|
||||
def relabel_from_one(label_field):
|
||||
"""Convert labels in an arbitrary label field to {1, ... number_of_labels}.
|
||||
|
||||
This function also returns the forward map (mapping the original labels to
|
||||
the reduced labels) and the inverse map (mapping the reduced labels back
|
||||
to the original ones).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
label_field : numpy ndarray (integer type)
|
||||
|
||||
Returns
|
||||
-------
|
||||
relabeled : numpy array of same shape as ar
|
||||
forward_map : 1d numpy array of length np.unique(ar) + 1
|
||||
inverse_map : 1d numpy array of length len(np.unique(ar))
|
||||
The length is len(np.unique(ar)) + 1 if 0 is not in np.unique(ar)
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from skimage.segmentation import relabel_from_one
|
||||
>>> label_field = array([1, 1, 5, 5, 8, 99, 42])
|
||||
>>> relab, fw, inv = relabel_from_one(label_field)
|
||||
>>> relab
|
||||
array([1, 1, 2, 2, 3, 5, 4])
|
||||
>>> fw
|
||||
array([0, 1, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 5])
|
||||
>>> inv
|
||||
array([ 0, 1, 5, 8, 42, 99])
|
||||
>>> (fw[label_field] == relab).all()
|
||||
True
|
||||
>>> (inv[relab] == label_field).all()
|
||||
True
|
||||
"""
|
||||
labels = np.unique(label_field)
|
||||
labels0 = labels[labels != 0]
|
||||
m = labels.max()
|
||||
if m == len(labels0): # nothing to do, already 1...n labels
|
||||
return label_field, labels, labels
|
||||
forward_map = np.zeros(m+1, int)
|
||||
forward_map[labels0] = np.arange(1, len(labels0) + 1)
|
||||
if not (labels == 0).any():
|
||||
labels = np.concatenate(([0], labels))
|
||||
inverse_map = labels
|
||||
return forward_map[label_field], forward_map, inverse_map
|
||||
@@ -0,0 +1,40 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal, assert_raises
|
||||
from skimage.segmentation import join_segmentations, relabel_from_one
|
||||
|
||||
def test_join_segmentations():
|
||||
s1 = np.array([[0, 0, 1, 1],
|
||||
[0, 2, 1, 1],
|
||||
[2, 2, 2, 1]])
|
||||
s2 = np.array([[0, 1, 1, 0],
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 1]])
|
||||
|
||||
# test correct join
|
||||
# NOTE: technically, equality to j_ref is not required, only that there
|
||||
# is a one-to-one mapping between j and j_ref. I don't know of an easy way
|
||||
# to check this (i.e. not as error-prone as the function being tested)
|
||||
j = join_segmentations(s1, s2)
|
||||
j_ref = np.array([[0, 1, 3, 2],
|
||||
[0, 5, 3, 2],
|
||||
[4, 5, 5, 3]])
|
||||
assert_array_equal(j, j_ref)
|
||||
|
||||
# test correct exception when arrays are different shapes
|
||||
s3 = np.array([[0, 0, 1, 1], [0, 2, 2, 1]])
|
||||
assert_raises(ValueError, join_segmentations, s1, s3)
|
||||
|
||||
def test_relabel_from_one():
|
||||
ar = np.array([1, 1, 5, 5, 8, 99, 42])
|
||||
ar_relab, fw, inv = relabel_from_one(ar)
|
||||
ar_relab_ref = np.array([1, 1, 2, 2, 3, 5, 4])
|
||||
assert_array_equal(ar_relab, ar_relab_ref)
|
||||
fw_ref = np.zeros(100, int)
|
||||
fw_ref[1] = 1; fw_ref[5] = 2; fw_ref[8] = 3; fw_ref[42] = 4; fw_ref[99] = 5
|
||||
assert_array_equal(fw, fw_ref)
|
||||
inv_ref = np.array([0, 1, 5, 8, 42, 99])
|
||||
assert_array_equal(inv, inv_ref)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
np.testing.run_module_suite()
|
||||
Reference in New Issue
Block a user