Reputation: 10996
There is a nice implementation of super resolution segment generation (SLIC) in skimage.segmentation package in the python sklearn package.
The slic() method returns the integer sets of labels. My question is how can I get the segments that are spatial neighbors of each other? What I would like to do is build a graph using these segments and the edges would connect the immediate neighbors. However, I cannot figure out how to get the immediate neighbors of a segment.
The python code to perform the SLIC is as follows:
from skimage import io
from skimage.segmentation import slic
from skimage.segmentation import find_boundaries
# An image of dimensions 300, 300
image = img_as_float(io.imread("image.png"))
# call slic. This returns an numpy array which assigns to every
# pixel in the image an integer label
# So segments is a numpy array of shape (300, 300)
segments = slic(image, 100, sigma = 5)
# Now I want to know the neighbourhood segment for each super-pixel
# There is a method called find_boundaries which returns a boolean
# for every pixel to show if it is a boundary pixel or not.
b = find_boundaries(segments)
Here, I am stuck. I would like to know how to parse this boundary indices and find out for a given label index (say 0), which label indexes share a boundary with label of index 0. Is there a way to do this efficiently without looping through the boundary array for every label index?
Upvotes: 3
Views: 6568
Reputation: 3548
A simple method using just np.unique
posing each segment-image pixel vs. the one to the right as well as below:
from skimage.data import astronaut
from skimage.segmentation import slic
from scipy.spatial import Delaunay
from skimage.segmentation import mark_boundaries
from matplotlib.lines import Line2D
img = astronaut().astype(np.float32) / 255.
# SLIC
segments = slic(img, n_segments=500, compactness=20)
segments_ids = np.unique(segments)
# centers
centers = np.array([np.mean(np.nonzero(segments==i),axis=1) for i in segments_ids])
vs_right = np.vstack([segments[:,:-1].ravel(), segments[:,1:].ravel()])
vs_below = np.vstack([segments[:-1,:].ravel(), segments[1:,:].ravel()])
bneighbors = np.unique(np.hstack([vs_right, vs_below]), axis=1)
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111)
plt.imshow(mark_boundaries(img, segments))
plt.scatter(centers[:,1],centers[:,0], c='y')
for i in range(bneighbors.shape[1]):
y0,x0 = centers[bneighbors[0,i]]
y1,x1 = centers[bneighbors[1,i]]
l = Line2D([x0,x1],[y0,y1], alpha=0.5)
ax.add_line(l)
An alternative (and somewhat incomplete) method, using Delaunay tessellation:
# neighbors via Delaunay tesselation
tri = Delaunay(centers)
# draw centers and neighbors
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111)
plt.imshow(mark_boundaries(img, segments))
plt.scatter(centers[:,1],centers[:,0], c='y')
# this contains the neighbors list: tri.vertex_neighbor_vertices
indptr,indices = tri.vertex_neighbor_vertices
# draw lines from each center to its neighbors
for i in range(len(indptr)-1):
N = indices[indptr[i]:indptr[i+1]] # list of neighbor superpixels
centerA = np.repeat([centers[i]], len(N), axis=0)
centerB = centers[N]
for y0,x0,y1,x1 in np.hstack([centerA,centerB]):
l = Line2D([x0,x1],[y0,y1], alpha=0.5)
ax.add_line(l)
Incomplete because some boundary neighbors will not arise from the tessellation.
Upvotes: 4
Reputation: 28748
The way I do it is to build a graph containing an edge from each pixel to its left and bottom pixel (so a 4 neighborhood), label them with their superpixel number and remove duplicates. You can find code and details in my blog post.
You can find some related functions here, thought they are not very well documented (yet).
Upvotes: 8