我想出了一个解决方案,通过使用父子轮廓关系和轮廓平滑方法从相交的图像中检测形状。
在上图中,红色边框是父轮廓,黄色是子轮廓的边框。使用父轮廓迭代所有子轮廓应用轮廓平滑方法来提取形状。 my questionian-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()
平滑输出图像