opencv python 实现Canny检测后不连续不封闭轮廓的闭合

Canny检测后轮廓的闭合在网上看了一些相关文章后总结出有以下方法:

1、使用闭运算等形态学操作来对轮廓进行处理,但作为像素点级别 的形态学操作往往不能满足要求,如:两条轮廓线相距仅为一个像素,在进行闭运算操作时会使这两条轮廓粘连在一起。

2、对于规则的图像可通过对不连续的点或线段进行拟合,通过对称的修剪和补缺来处理,但适用的对象非常有限,所以只能特例分析。

3、找到图像中不连续轮廓中的端点,通过最近端点间画直线来处理,但这仅仅适用于简单的图像处理,对于一些复杂或者断续太大的轮廓反而效果比较差。

本章主要讲第三种方法,代码如下:

import cv2
import math
import numpy as np
import timeit


start_time = timeit.default_timer()


def point(img, h, w):
    p = []
    for i in range(h):
        for j in range(w):
            if img[i, j] == 255:
                r = []
                for y in range(i-1, i+2):
                    for x in range(j-1, j+2):
                        if y == i and x == j:
                            continue
                        if img[y, x] == 255:
                            r.append((y, x))
                if len(r) > 0:
                    if len(r) == 1:
                        p.append((i, j))
                    elif len(r) == 2:
                        dy = r[0][0] - r[1][0]
                        dx = r[0][1] - r[1][1]
                        if abs(dy) + abs(dx) == 1:
                            p.append((i, j))
    return p


max_range = 30  # 设置最大的端点连线

img = cv2.imread('dian.jpg')
h, w, c = img.shape[:]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img2 = cv2.bilateralFilter(gray, 5, 150, 150)

ven = cv2.Canny(img2, 77, 220)
points = point(ven, h, w)
tu = np.zeros((h, w, c), np.uint8)
tu[ven != 0] = 255
tu1 = tu.copy()
for i, j in points:
    tu[i, j, 0] = 0  #b
    tu[i, j, 1] = 0  #G
    tu[i, j, 2] = 255  # r
cv2.imshow('tu', tu)  # 查看端点

for n, i in enumerate(points):
    points.pop(n)
    distances = [np.linalg.norm(np.array(p) - np.array(i)) for p in points]  # 计算欧几里得距离
    min_index = np.argmin(distances)  # 得到列表中最短距离的索引
    if i != points[min_index] and distances[min_index] <= max_range:
        cv2.line(tu1, tuple((i[1], i[0])), tuple((points[min_index][1], points[min_index][0])), (0, 0, 255), 1)
cv2.imshow('dabo', tu1)

end_time = timeit.default_timer()
print("程序运行时间: ", end_time - start_time, "秒")

cv2.waitKey(0)

一、程序分析

        在关于图像中端点的定义上,可分为如下情况:

 

 上两份对应着程序中len(r)==1的部分,下两份对应着程序中len(r)==2的部分

该部分程序实现了图像中不连续轮廓端点的寻找

def point(img, h, w):
    p = []
    for i in range(h):
        for j in range(w):
            if img[i, j] == 255:
                r = []
                for y in range(i-1, i+2):
                    for x in range(j-1, j+2):
                        if y == i and x == j:
                            continue
                        if img[y, x] == 255:
                            r.append((y, x))
                if len(r) > 0:
                    if len(r) == 1:
                        p.append((i, j))
                    elif len(r) == 2:
                        dy = r[0][0] - r[1][0]
                        dx = r[0][1] - r[1][1]
                        if abs(dy) + abs(dx) == 1:
                            p.append((i, j))
    return p

 二、程序展示

        本人通过手动调整了Canny算子的阈值参数,对得到的图像进行端点的寻找后,用红色进行了标出,如下图:

下图即为最终的闭合图像,可通过调整最大画线距离max_range来控制画线

 

for n, i in enumerate(points):
    points.pop(n)
    distances = [np.linalg.norm(np.array(p) - np.array(i)) for p in points]  # 计算欧几里得距离
    min_index = np.argmin(distances)  # 得到列表中最短距离的索引
    if i != points[min_index] and distances[min_index] <= max_range:
        cv2.line(tu1, tuple((i[1], i[0])), tuple((points[min_index][1], points[min_index][0])), (0, 0, 255), 1)
cv2.imshow('dabo', tu1)

三,结果分析 

        可以看到结果是非常不如意的,所以本文章仅提供一个思路,并不能直接的解决一些实际问题,可以通过对端点连线处程序进行改进,使的能够对定性的端点进行连线闭合。

        如果有更好的办法欢迎各位在评论区留言!

参考文章:

Python OpenCV 连接不封闭的轮廓

canny边缘检测不连续问题

opencv 风挡轮廓补全

图像轮廓缺陷修补