利用Canny边缘检测算子进行边缘检测的原理及OpenCV代码实现

Canny算子是John Canny在1986年发表的论文中首次提出的边缘检测算子,该算子检测性能比较好,应用广泛。
Canny算子进行边缘检测的原理和步骤如下:
⑴消除噪声。边缘检测的算法主要是基于图像强度的一阶和二阶微分操作,但导数通常对噪声很敏感,边缘检测算法常常需要根据图像源的数据进行预处理操作,因此采用滤波器来改善与噪声有关的边缘检测性能,比如在进行边缘检测前,可以对原始数据先作高斯滤波处理。其实不仅对噪声,如果不做滤波平滑处理,原图片中不是边缘但是灰度变化频率较高的部分也容易被认为是边缘,这样导致了边缘检测性能的下降。
⑵计算梯度的幅度与方向。OpenCV中的Canny函数是使用Sobel卷积核来计算梯度的幅度与方向的。计算出的幅度与方向作为后面提取图像边缘的原始数据。
【利用Canny边缘检测算子进行边缘检测的原理及OpenCV代码实现】⑶非极大值抑制。非极大值抑制的目的是剔除第⑵部中计算出来的结果中的大部分非边缘点。其原理是通过像素的八邻域来判断要不要将这个像素置为边缘点,如果不置为边缘点,那么就置为背景色。判断的方法如下:
①判断范围是像素的八邻域,所以是局部最优判断法;
②判断的标准是如果某个像素在其八邻域内,既是最大值,梯度值也最大,那么可判断该点为像素边缘点,否则就不是。如何判断呢?
首先,梯度值的判断是很好判断的,用边缘检测微分算子得到的结果直接比较就可以了,但是最大值的判断可不是只比较其旁边的八个点哦,还要比较另外两个点,详情如下:
利用Canny边缘检测算子进行边缘检测的原理及OpenCV代码实现
文章图片

如果已经判断出上图中的C点比其旁边的8个点的像素值都大,那么接下来判断上图中dTmp1和dTmp2的值是否也小于C点的值,那么dTmp1和dTmp2的值怎么求呢?上图中蓝色的线条方向为C点的梯度方向,这样就可以确定其局部的最大值肯定分布在这条线上,也即除了C点外,梯度方向的交点dTmp1和dTmp2这两个点的值也可能会是局部最大值。因此,判断C点灰度与这两个点灰度大小即可判断C点是否为其邻域内的局部最大灰度点。如果经过判断,C点灰度值小于这两个点中的任一个,那就说明C点不是局部极大值,那么则可以排除C点为边缘。
⑷用滞后阈值算法求解图像边缘。上一步对边缘检测算子的结果进行了非极大值抑制,接下来我们用二值化的方法来求解图像边缘。单阈值处理边缘效果不好,所以Cannny算法中采用滞后阈值法求解。滞后阈值法需要设置一个高阈值和一个低阈值,解后按如下法则进行:
第一,如果某一像素位置的梯度幅值超过高阈值,则像素被保留为边缘像素;
第二,如果某一像素位置的梯度幅值小于低阈值,则像素被排除;
第三,如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于高阈值的像素时被保留。
在以上的法则中,推荐的高阈值与低阈值比在2:1到3:1之间!
通过消除噪声、计算梯度幅度与方向、非极大值抑制及用滞后阈值算法求解图像边缘四个步骤就可实现Canny边缘检测。
Canny函数原型如下:
void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false );
Image为输入图像,单通道8bit;
edges为输出图像,与输入图像同类型同尺寸;
threshold1为滞后阈值算法的低阈值;
threshold2为为滞后阈值算法的高阈值;
apertureSize为Sobel算子的窗口(卷积核)阶数;
L2gradient表示是否使用L2范数来计算图像梯度幅值。
以下是使用函数Canny实现图像边缘检测的代码:
源代码参见博文https://blog.csdn.net/lehuoziyuan/article/details/84136050

    推荐阅读