8.5-8.12:canny边缘检测
【一】 基本原理
边缘检测的一般步骤:
1)滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波方法主要有高斯滤波,即采用离散化的高斯函数产生一组归一化的高斯核(具体见“高斯滤波原理及其编程离散化实现方法”一文),然后基于高斯核函数对图像灰度矩阵的每一点进行加权求和(具体程序实现见下文)。
2)增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。
3)检测:经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。
Canny边缘检测是由Laplace滤波器改进而来的,前者先在x和y方向上求得一阶导,再将它们组合成为四个方向导数,将其中方向导数是最大值的点作为组成边缘的候选项,最后通过滞后阈值来形成轮廓。
【二】 步骤
- 对原始图像进行灰度化
- 对图像进行高斯滤波
滤去理想图像中叠加的高频噪声。通常滤波和边缘检测是矛盾的概念,抑制了噪声会使得图像边缘模糊,这回增加边缘定位的不确定性;而如果要提高边缘检测的灵敏度,同时对噪声也提高了灵敏度。实际工程经验表明,高斯函数确定的核可以在抗噪声干扰和边缘检测精确定位之间提供较好的折衷方案。 - 计算梯度的幅值和方向
图像灰度值得梯度可使用一阶有限差分来进行近似,这样就可以得图像在x和y方向上偏导数的两个矩阵。常用sobel等算子。 - 对梯度幅值进行非极大值抑制
图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这仅仅是属于图像增强的过程)。在Canny算法中,非极大值抑制是进行边缘检测的重要步骤,通俗意义上是指寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉一大部分非边缘的点 - 采用滞后阈值方法检测和连接边缘
Canny算法中减少假边缘数量的方法是采用双阈值法。选择两个阈值,根据高阈值得到一个边缘图像,在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时,该算法会在断点的8邻域点中寻找满足低阈值的点,再根据此点收集新的边缘,直到整个图像边缘闭合。
【三】 实验
实验进行了两次canny转换,一次直接对原图像进行转换,一次转灰度图以及进行高斯滤波后进行转换。两次转换高低阈值均取150,100。
源代码:
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<stdio.h>
using namespace cv;
int main()
{
Mat pic, gray,gauss, canny1, canny2;
//读入图像
pic = imread("rain1.jpg");
//显示原图
imshow("原图", pic);
//原图直接进行canny变换
Canny(pic, canny1, 150, 100, 3, true);
//显示直接canny变换结果
imshow("直接cannya变换结果", canny1);
//原图转换为灰度图
cvtColor(pic, gray, COLOR_BGR2GRAY);
//进行高斯滤波
GaussianBlur(pic, gauss, Size(3, 3), 0, 0);
//显示高斯滤波结果
imshow("高斯滤波结果", gauss);
//canny变换
Canny(gauss, canny2, 150, 100, 3, true);
//显示canny变换结果
imshow("canny变换结果", canny2);
waitKey(0);
return 0;
}
结果如下: