利用OpenCV/Python实现二值图像中的断线连接

ar7v8xwq  于 2022-11-30  发布在  Python
关注(0)|答案(1)|浏览(526)

我有像下面这样的图像,线条是断开的。

我试过用形态学运算把它们连接起来,但效果不好。我也想过计算方向,但由于线是平行的,我不能这样做。
在Python中,有没有一种方法可以在某个方向上扩展?或者有没有其他方法可以连接这些线?
下面是我到目前为止编写的代码:

import cv2

img = cv2.imread('mask.jpg')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# convert img into binary
_, bw = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# calculating Contours
contours, _ = cv2.findContours(bw, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

def get_orientation(pts, img):
    sz = len(pts)
    data_pts = np.empty((sz, 2), dtype=np.float64)
    for i in range(data_pts.shape[0]):
        data_pts[i,0] = pts[i,0,0]
        data_pts[i,1] = pts[i,0,1]
    # Perform PCA analysis
    mean = np.empty((0))
    mean, eigenvectors, eigenvalues = cv2.PCACompute2(data_pts, mean)
    # Store the center of the object
    cntr = (int(mean[0,0]), int(mean[0,1]))
    cv2.circle(img, cntr, 3, (255, 0, 255), 2)
    p1 = (cntr[0] + 0.02 * eigenvectors[0,0] * eigenvalues[0,0], cntr[1] + 0.02 *  eigenvectors[0,1] * eigenvalues[0,0])
    p2 = (cntr[0] - 0.02 * eigenvectors[1,0] * eigenvalues[1,0], cntr[1] - 0.02 * eigenvectors[1,1] * eigenvalues[1,0])
    draw_axis(img, cntr, p1, (0, 150, 0), 1)
    draw_axis(img, cntr, p2, (200, 150, 0), 5)
    angle = atan2(eigenvectors[0,1], eigenvectors[0,0]) # orientation in radians
    return angle
def draw_axis(img, p_, q_, colour, scale):
    p = list(p_)
    q = list(q_)
    angle = atan2(p[1] - q[1], p[0] - q[0]) # angle in radians
    hypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))
    # Here we lengthen the arrow by a factor of scale
    q[0] = p[0] - scale * hypotenuse * cos(angle)
    q[1] = p[1] - scale * hypotenuse * sin(angle)
    cv2.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv2.LINE_AA)
    # create the arrow hooks
    p[0] = q[0] + 9 * cos(angle + pi / 4)
    p[1] = q[1] + 9 * sin(angle + pi / 4)
    cv2.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv2.LINE_AA)
    p[0] = q[0] + 9 * cos(angle - pi / 4)
    p[1] = q[1] + 9 * sin(angle - pi / 4)
    cv2.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv2.LINE_AA)

for i,c in enumerate(contours):
  # area of each contour
  area = cv2.contourArea(c)
  # find orientation of each shape
  orrr = get_orientation(c,img)
  print(orrr)
zdwk9cvp

zdwk9cvp1#

你没有完全清楚你到底想要什么样的结果(或者你在形态学手术上的问题是什么),但是我有机会。

  • 通过形态学操作将所有的“线”连接成一个对象。我在这里使用了一个圆形内核,我认为它给出了不错的结果。在这一点上不需要旋转。
  • 使用轮廓线为每个对象内插一条线。这只选择最大的轮廓线,如果合适的话,应用进一步的阈值。

给出:

import cv2

# get image
img = cv2.imread("<YourPathHere>", cv2.IMREAD_GRAYSCALE)

# threshold to binary
ret, imgbin = cv2.threshold(img,5,255,cv2.THRESH_BINARY)

# morph 
dilateKernelSize = 80; erodeKernelSize = 65;
imgbin = cv2.dilate(imgbin, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, [dilateKernelSize,dilateKernelSize]))
imgbin = cv2.erode(imgbin, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, [erodeKernelSize,erodeKernelSize]))

# extract contours
contours, _ = cv2.findContours(imgbin,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
print("Found ",len(contours),"contours")

# fit lines for large contours
lines = []; threshArea = 11000;
for cnt in contours:
    if(cv2.contourArea(cnt)>threshArea):
        lines += [cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)] # [vx,vy,x,y]

# show results
imgresult = cv2.cvtColor(imgbin,cv2.COLOR_GRAY2RGB)
cv2.drawContours(imgresult, contours, -1, (255,125,0), 3)

VX_ = 0; VY_ = 1; X_ = 2; Y_ = 3;
rows,cols = imgbin.shape[:2]
p1 = [0,0]; p2 = [cols-1,0];
for l in lines:
    p1[1]  = int(((    0-l[X_])*l[VY_]/l[VX_]) + l[Y_])
    p2[1] = int(((cols-l[X_])*l[VY_]/l[VX_]) + l[Y_])
    cv2.line(imgresult,p1,p2,(0,255,255),2)

# save image    
print(cv2.imwrite("<YourPathHere>", imgresult))

# HighGUI
cv2.namedWindow("img", cv2.WINDOW_NORMAL)
cv2.imshow("img", img)
cv2.namedWindow("imgresult", cv2.WINDOW_NORMAL)
cv2.imshow("imgresult", imgresult)
cv2.waitKey(0)
cv2.destroyAllWindows()

相关问题