在一个应用中,需要提取轮廓线,但初次提取的会有一些细小分支,由于知道起点和终点,所以这里采用回溯法去掉轮廓分支
import cv2
def findPath(start, end, image, stack):
# image: 二值图 value: 0 or 255
# start: 起点位置 (int h, int w)
# end: 终点位置 (int h, int w)
h, w = image.shape[:2]
if start[0] >= h or start[0] < 0 or start[1] >= w or start[1] < 0:
return False
if end[0] >= h or end[0] < 0 or end[1] >= w or end[1] < 0:
return False
stack.append(start)
if len(image.shape) > 2:
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 灰度化
_, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY) # 二值化
if image[start[0]][start[1]] != 255 or image[end[0]][end[1]] != 255:
print("起点或者终点不符合要求")
return False
while len(stack) > 0:
cur = stack[-1]
x_id = cur[0]
y_id = cur[1]
# 八领域 右-->右下-->下-->左下-->左-->左上-->上-->右上
nb_ids = [[x_id, y_id+1], [x_id+1, y_id+1], [x_id+1, y_id], [x_id+1, y_id-1],
[x_id, y_id-1], [x_id-1, y_id-1], [x_id-1, y_id], [x_id-1, y_id+1]]
flag = -1
for nb_id in nb_ids:
x = nb_id[0]
y = nb_id[1]
if x == end[0] and y == end[1]:
stack.append(end_point)
for x in range(h):
for y in range(w):
if image[x][y] == 255:
image[x][y] = 0
return True
if x >= h or x < 0 or y >= w or y < 0:
continue
if image[x][y] == 255:
stack.append(nb_id)
image[x][y] = 128
flag = 1
break
if flag == -1:
stack.pop()
image[x_id][y_id] = 0
if len(stack) == 0:
print("未找到")
return False
img_path = r"D://imageGumlineTest.png"
img = cv2.imread(img_path)
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]
hull = cv2.convexHull(cnt, returnPoints=False)
defects = cv2.convexityDefects(cnt, hull)
max_dist = 0
start_point = -1
end_point = -1
far_point = -1
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
if d > max_dist:
max_dist = d
start_point = start
end_point = end
far_point = far
cv2.line(img, start_point, end_point, [0, 255, 0], 1)
cv2.circle(img, far_point, 2, [0, 0, 255], -1)
print(f"start_point: {start_point}, end_point: {end_point}")
cv2.imshow('final_img', img)
skeleton_img = cv2.imread(r"D://refCenterLineTest1.png")
gray_img = cv2.cvtColor(skeleton_img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY)
print(f"start: {thresh[42][27]}, end: {thresh[35][289]}")
cv2.line(skeleton_img, start_point, end_point, [0, 255, 0], 1)
cv2.circle(skeleton_img, start_point, 3, [0, 0, 255], -1)
cv2.circle(skeleton_img, end_point, 3, [0, 0, 255], -1)
cv2.circle(skeleton_img, far_point, 3, [0, 0, 255], -1)
cv2.imshow('thresh', thresh)
stack = []
s_p = [42, 27]
e_p = [35, 289]
findPath(s_p, e_p, thresh, stack)
cv2.imshow("skeleton", thresh)
cv2.waitKey(0)
stack里面存放得结果即为一条起点到终点得路径