I came up with a solution to detect shape from an intersected image by using parent and child contour relation and contour smoothing method.

In the above last image red border is the parent contour and yellow is the child contour's border. Iterate all child contour with parent contour apply contour smoothing approach to extract shapes. This error smoothing approach is discussed in my question by ian-chu
import cv2
import numpy as np
def get_parent_contour(contours, hierarchy):
parent = hierarchy[0, :, 3]
unique_parent, counts_parent = np.unique(parent, return_counts=True)
parent_contour_id = -1
max_area = 0
for c in unique_parent:
if c != -1:
area = cv2.contourArea(contours[c])
if area > max_area:
max_area = area
parent_contour_id = c
return parent_contour_id, contours[parent_contour_id]
def get_child_contours(contours, hierarchy, parent_contour_id):
parent = hierarchy[0, :, 3]
# Select all contour whose parent is selected parent above
child_contour_id = [i for i, v in enumerate(parent) if v == parent_contour_id]
child_contours = [contours[i] for i in child_contour_id]
return child_contour_id, child_contours
def smoothed(contour, dists, cutoff):
smooth_con = []
for a in range(len(dists)):
if dists[a] < cutoff:
smooth_con.append(contour[a])
return np.asarray(smooth_con)
# get the distance list for an array of points
def distList(src, other):
dists = []
for point in src:
point = point[0] # drop extra brackets
_, dist = closestPoint(point, other)
dists.append(dist)
return dists
# returns squared distance of two points
def squaredDist(one, two):
dx = one[0] - two[0]
dy = one[1] - two[1]
return dx * dx + dy * dy
# find closest point (just do a linear search)
def closestPoint(point, arr):
# init tracker vars
closest = None
best_dist = np.Inf
# linear search
for other in arr:
other = other[0] # remove extra brackets
dist = squaredDist(point, other)
if dist < best_dist:
closest = other
best_dist = dist
return closest, best_dist
image = cv2.imread("image.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, threshold = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("threshold", threshold)
contours, hierarchy = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
blank_image = np.zeros_like(image)
if hierarchy is not None:
for contour in contours:
color = np.random.randint(0, 255, size=(3,))
color = (int(color[0]), int(color[1]), int(color[2]))
cv2.drawContours(blank_image, [contour], 0, color, 2)
cv2.imshow("contour image", blank_image)
parent_contour_id, parent_contour = get_parent_contour(contours, hierarchy)
child_contour_ids, child_contours = get_child_contours(contours, hierarchy, parent_contour_id)
blank_image_2 = np.zeros_like(image)
cv2.drawContours(blank_image_2, [parent_contour], 0, (0, 0, 255), 2)
for child_cnt in child_contours:
cv2.drawContours(blank_image_2, [child_cnt], 0, (0, 255, 255), 1)
cv2.imshow("parent child contours", blank_image_2)
for child_cnt in child_contours:
one = parent_contour
two = child_cnt
# get distances
one_dists = distList(one, two)
two_dists = distList(two, one)
try:
# dump values greater than 10
smooth_one = smoothed(one, one_dists, 35)
smooth_two = smoothed(two, two_dists, 35)
# draw new contour
blank_image_3 = np.zeros_like(threshold)
cv2.drawContours(blank_image_3, [smooth_one], -1, 255, -1)
cv2.drawContours(blank_image_3, [smooth_two], -1, 0, -1)
cv2.imshow("smooth image", blank_image_3)
# you can apply filters on smoothed contour image to accepting and reject smooth contour
# And apply single shape detection algorithm on smoothed contour image to find shapes
except:
print("no near points. ")
cv2.waitKey(0)
cv2.destroyAllWindows()
Smooth output Images
