Причина, по которой вы получаете два отдельных контура, заключается в том, что вы применяете детектор краев Canny к изображению в градациях серого.Это дает результат «белых линий» - краев на чистом изображении.cv2.findContours()
в терминах непрофессионалов ищет подключенные белые пиксели на двоичном изображении.Таким образом, поскольку у вас есть две отдельные линии, он находит два контура.cv2.findContours()
возвращает массив точек контуров, поэтому, прежде чем искать интересующую вас область, вы должны сначала соединить эти две линии, чтобы получить только один контур.Это можно сделать, перебирая точки и добавляя точки со значениями 0 для x
или y
и / или значениями высоты или ширины изображений.Это даст вам 4 очка - начальную и конечную точку первого контура плюс начало и конец второго.Затем вы можете измерить расстояние между этими точками, чтобы определить, какой из них подходит.Насколько я помню, формула для расстояния между двумя точками равна sqrt((x2-x1)^2+(y2-y1)^2)
.Таким образом, если вы вычислите расстояние от одной точки до оставшихся 3, самое короткое расстояние означает, что эти две точки соответствуют тогетру (как и две точки, которые соответствуют тогетеру, который находится дальше).Теперь у вас есть начальная и конечная точки обеих линий, и вы можете соединить их.Конечно, вы можете сделать другой критерий, например, если первая точка имеет 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()
Исходное изображение:
Края:
Края + линии:
Контуры:
При этом вы можете попытаться портировать изображение вместо поискадля краев.Это может дать вам один контур от getgo и сделает все намного проще.Не могу сказать наверняка, потому что вы не опубликовали исходное изображение!
Пример кода:
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()
Изображение с порогом:
Контуры: