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 风挡轮廓补全
图像轮廓缺陷修补