获得两个单独轮廓的原因是您将 Canny 边缘检测器应用于灰度图像。这给出了“白线”的结果 - 空白图像上的边缘。外行术语中的cv2.findContours() 在二值图像上寻找连接的白色像素。所以因为你有两条单独的线,它会找到两个轮廓。 cv2.findContours() 返回一个轮廓点数组,因此在搜索感兴趣区域之前,您必须首先连接这两条线以获得一个轮廓。您可以通过遍历这些点并附加具有 x 或 y 的值为 0 和/或具有图像高度或宽度的值的点来执行此操作。这将为您提供 4 个点 - 第一个轮廓的起点和终点加上第二个轮廓的起点和终点。然后您可以测量这些点之间的距离以确定哪个更适合。我记得两点之间的距离公式是sqrt((x2-x1)^2+(y2-y1)^2)。因此,如果您计算一个点与其余 3 个点的距离,则最短距离意味着这两个点更适合(就像这两个点更适合更远的点一样)。所以现在你有了两条线的起点和终点,你可以连接它们。 Ofcorse您可以制定其他标准,例如第一个点是否有x==0和y>0,然后在(0,0)点上画一条线,然后从那里再到第二个点,这样你就不会切掉部分感兴趣的区域。我做了一个简单的例子来向你展示实现这一点的逻辑。请注意,这不是自动化的有效解决方案 - 要实现这一目标,您必须对其进行大量升级。
示例代码:
import cv2
import numpy as np
img = cv2.imread('vessel.png')
h, w, ch = img.shape
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(img,100,200)
_, contours, hierarchy = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
points = []
cv2.imshow('edges', edges)
for cnt in contours:
for i in cnt[:,0]:
x = int(i[0])
y = int(i[1])
if x == 0:
points.append((x,y))
elif y == 0:
points.append((x,y))
elif w-1<= x <= w+1:
points.append((x,y))
elif h-1<= y <= h+1:
points.append((x,y))
if len(points) == 4:
x1, y1 = points[0]
x2, y2 = points[1]
x3, y3 = points[2]
x4, y4 = points[3]
dist1 = np.sqrt((x2-x1)**2 + (y2-y1)**2)
dist2 = np.sqrt((x3-x1)**2 + (y3-y1)**2)
dist3 = np.sqrt((x4-x1)**2 + (y4-y1)**2)
if dist2 < dist1 and dist2 < dist3:
cv2.line(edges, (x3,y3), (x1,y1), 255, 1)
cv2.line(edges, (x2,y2), (x4,y4), 255, 1)
_, contours, hierarchy = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, 0, (0,255,0), 2)
cv2.imshow('img', img)
cv2.imshow('edges+lines', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
原图:
边缘:
边+线:
轮廓:
话虽如此,您可以尝试对图像设置阈值而不是搜索边缘。这可以从一开始就为您提供一个轮廓,并使事情变得容易得多。不能肯定,因为你还没有发布原图!
示例代码:
import cv2
import numpy as np
img = cv2.imread('vessel.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, threshold = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
_, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, 0, (0,255,0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
阈值图像:
轮廓: