如何连接图像中不连贯的线条或边缘?

How to connect disjointed lines or edges in images?

我目前正在研究从二进制图像中提取线条。我最初执行了一些图像处理步骤,包括阈值分割并获得了以下二值图像。

正如在二值图像中所看到的那样,线条分裂或断开。我想加入虚线,如下图红色标记所示。为了演示,我手动标记了红线。

仅供参考,我使用以下代码执行预处理。

img = cv2.imread('original_image.jpg')  # loading image 
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # coverting to gray scale
median_filter = cv2.medianBlur (gray_image, ksize = 5) # median filtering 

th, thresh = cv2.threshold (median_filter, median_filter.mean(), 255, cv2.THRESH_BINARY) # theshold segmentation

# small dots and noise removing 
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh, None, None, None, 8, cv2.CV_32S)
areas = stats[1:,cv2.CC_STAT_AREA]
result = np.zeros((labels.shape), np.uint8)
min_size = 150 
for i in range(0, nlabels - 1):
    if areas[i] >= min_size:   #keep
        result[labels == i + 1] = 255

fig, ax = plt.subplots(2,1, figsize=(30,20))
ax[0].imshow(img)
ax[0].set_title('Original image')

ax[1].imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
ax[1].set_title('preprocessed image')

如果您对如何连接线路有任何建议或步骤,我将不胜感激?谢谢

使用以下一系列方法,我能够得到一个粗略的近似值。这是一个非常简单的解决方案,可能不适用于所有情况。

1.形态学运算

要合并相邻线,请对二值图像执行形态学(膨胀)操作。

img = cv2.imread('image_path', 0)     # grayscale image
img1 = cv2.imread('image_path', 1)    # color image

th = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (19, 19))
morph = cv2.morphologyEx(th, cv2.MORPH_DILATE, kernel)

2。寻找轮廓和极值点

  1. 我现在的想法是找轮廓

  2. 然后求每个轮廓的极值点

  3. 最终找到相邻轮廓之间这些极值点之间的最近距离。并在它们之间画一条线。

    cnts1 = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    cnts = cnts1[0] # storing contours in a variable

让我们绕个弯路,想象一下这些极值点的位置:

# visualize extreme points for each contour
for c in cnts:
    left = tuple(c[c[:, :, 0].argmin()][0])
    right = tuple(c[c[:, :, 0].argmax()][0])
    top = tuple(c[c[:, :, 1].argmin()][0])
    bottom = tuple(c[c[:, :, 1].argmax()][0])

    # Draw dots onto image
    cv2.circle(img1, left, 8, (0, 50, 255), -1)
    cv2.circle(img1, right, 8, (0, 255, 255), -1)
    cv2.circle(img1, top, 8, (255, 50, 0), -1)
    cv2.circle(img1, bottom, 8, (255, 255, 0), -1)

(注:极值点点是基于形态学运算得到的轮廓,但绘制在原图上)

3。查找相邻轮廓之间的最近距离

很抱歉有很多循环。

  1. 首先,遍历图像中的每个轮廓(分割线)。

  2. 求出它们的极值点。极值点是指基于其各自边界框的 top-most、bottom-most、right-most 和 left-most 点。

  3. 将等高线的每个极值点与其他等高线的每个极值点之间的距离进行比较。并在距离最短的点之间画一条线。

    我在范围内(len(cnts)):

    min_dist = max(img.shape[0], img.shape[1])
    
    cl = []
    
    ci = cnts[i]
    ci_left = tuple(ci[ci[:, :, 0].argmin()][0])
    ci_right = tuple(ci[ci[:, :, 0].argmax()][0])
    ci_top = tuple(ci[ci[:, :, 1].argmin()][0])
    ci_bottom = tuple(ci[ci[:, :, 1].argmax()][0])
    ci_list = [ci_bottom, ci_left, ci_right, ci_top]
    
    for j in range(i + 1, len(cnts)):
        cj = cnts[j]
        cj_left = tuple(cj[cj[:, :, 0].argmin()][0])
        cj_right = tuple(cj[cj[:, :, 0].argmax()][0])
        cj_top = tuple(cj[cj[:, :, 1].argmin()][0])
        cj_bottom = tuple(cj[cj[:, :, 1].argmax()][0])
        cj_list = [cj_bottom, cj_left, cj_right, cj_top]
    
        for pt1 in ci_list:
            for pt2 in cj_list:
                dist = int(np.linalg.norm(np.array(pt1) - np.array(pt2)))     #dist = sqrt( (x2 - x1)**2 + (y2 - y1)**2 )
                if dist < min_dist:
                    min_dist = dist             
                    cl = []
                    cl.append([pt1, pt2, min_dist])
    if len(cl) > 0:
        cv2.line(img1, cl[0][0], cl[0][1], (255, 255, 255), thickness = 5)
    

4. Post-processing

由于最终输出并不完美,您可以进行额外的形态学操作,然后将其骨架化。