I have a blob of irregular shapes like this:
Now I want to draw lines from the outer edge to the inner edge of the blob like this:
I have used cv2.connectedComponents()
on result after applying Canny edge detection to get the two edges.
originalimage = cv2.imread(R"blob.png")
edge = cv2.Canny(originalimage, 100, 200)
def get_islands(img):
n, labels = cv2.connectedComponents(img.astype(np.uint8))
islands = [(labels == i).astype(np.uint8) for i in range(1, n)]
return islands
edge_sets = get_islands(edge)
inner = edge_sets[1]
outer = edge_sets[0]
Now I want to draw line from outer to inner. I have tried calculating slope for each point in the outer layer from it's nearest neighbors. But it fails for corner points. First I have tried creating a graph of neighboring pixels.
def create_graph(binary_image):
graph = defaultdict(list)
height, width = binary_image.shape
all_points = np.nonzero(binary_image)
all_points= sorted(list(map(list, [*zip(*all_points)]))) #Transpose the List
for x, y in all_points:
for dx in range(-1, 2, 1):
for dy in range(-1, 2, 1):
if dx == 0 and dy == 0:
continue
if (x+dx>=0 and y+dy>=0 and x+dx < height and y+dy < width) and binary_image[x+dx][y+dy]:
graph[(x, y)].append((x+dx, y+dy))
return graph
outer_graph = create_graph(edge_sets[0])
inner_graph = create_graph(edge_sets[1])
Then I have tried calculating slope of the outer edge at every point from its neighboring pixels and drawing perpendicular line to it.
def get_slope(graph, point):
neighbours = graph[point]
slopes = []
eps = 1e-6
for neighbour in neighbours:
if neighbour[0] != point[0]: # Vertical Line
slopes.append((neighbour[1] - point[1]) / (neighbour[0] - point[0] + eps))
if slopes:
return np.mean(slopes)
else:
return 0
lines = []
for point in outer_graph:
slope = get_slope(outer_graph, point)
if slope == 0:
continue
perpedicular_slope = -1/slope
for inner_point in inner_graph:
if abs(get_slope(inner_graph, inner_point) - perpedicular_slope) < 0.01 \
and is_line_in_image(point, inner_point, originalimage):
lines.append([point, inner_point])
But it fails at the points where the edge is not smooth and the lines deviates a lot.
lines = np.array(lines)
line_image = cv2.cvtColor(originalimage, cv2.COLOR_GRAY2RGB)
for i, line in enumerate(lines):
if i % 100 == 0:
line_image = cv2.line(line_image, tuple(line[0][::-1]), tuple(line[1][::-1]), (255, 0, 0), 3)
plt.imshow(line_image)
Output:
How can I improve my algorithm to get the expected output? Is my approach correct?