目录
1 什么是Canny算子
Canny边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。它在边缘检测过程中具有以下特点:
- 低错误率:能够检测到大多数真实边缘,并且尽量减少错误检测。
- 高定位精度:检测到的边缘位置相对精确。
- 最小响应:每个边缘只有一个响应点。
2 Canny边缘检测流程
它包括如下五个步骤:
-
噪声抑制:通过使用高斯滤波器对图像进行平滑处理,降低噪声对边缘检测的影响。
-
计算梯度幅值和方向:利用Sobel算子计算图像在水平和垂直方向上的梯度,然后根据梯度幅值和方向计算每个像素点的梯度幅值和方向。
-
非极大值抑制:对图像中的梯度幅值进行非极大值抑制,即在每个像素点的梯度方向上比较幅值,并保留局部最大值,以细化边缘。
-
双阈值检测:根据设定的两个阈值(高阈值和低阈值),对梯度幅值进行阈值处理。高于高阈值的像素点被标记为强边缘,低于低阈值的像素点被排除,介于两个阈值之间的像素点被标记为弱边缘。
-
边缘连接:根据强边缘像素点的连通性,通过连接弱边缘像素点来形成完整的边缘。
3 Canny具体步骤
3.1 图像平滑
图像平滑就是让图像之间的像素差距更小一些,能够更好地提取特征更加明显的边缘,所以图像平滑的目的就是去除部分噪声。 图像平滑操作,就是让临近的像素之间差距变小,图像模糊的作用不就是这样吗?所以图像平滑操作就是进行之前所讲的图像模糊(滤波)操作,而我们最常用的模糊操作,是高斯模糊。
3.2 计算图像中每个像素的梯度方向和幅值
Canny算法的基本思想是寻找一幅图像中灰度强度变化最强的位置。所谓变化最强,就是指梯度方向。 梯度求解:
平滑后的图像中每个像素点的梯度可以由Sobel算子来获得。利用如下的核来分别求得沿水平(x)和垂直(y)方向的梯度G_X和G_Y。
采用下列公式计算梯度和方向:
3.3 非极大值抑制
这一步的目的是将模糊的边界变得清晰(sharp)。通俗的讲,就是保留了每个像素点上梯度强度的极大值,而删掉其他的值。 具体操作分为如下几部:
a) 将其梯度方向近似为以下值中的一个: (0,45,90,135,180,225,270,315),即上下左右和45度方向。
b) 比较该像素点,和其梯度方向正负方向的像素点的梯度强度。
c) 如果该像素点梯度强度最大则保留,否则抑制(删除,即置为0)。 例:
例子: 下图中的数字代表了像素点的梯度强度,箭头方向代表了梯度方向。以第二排第三个像素点为例,由于梯度方向向上,则将这一点的强度(7)与其上下两个像素点的强度(5和4)比较,由于这一点强度最大,则保留。
3.4 双阈值求可能边
经过非极大抑制后图像中仍然有很多噪声点。Canny算法中应用了一种叫双阈值的技术。即设定一个阈值上界和阈值下界(opencv中通常由人为指定的),图像中的像素点如果大于阈值上界则认为必然是边界(称为强边界,strong edge),小于阈值下界则认为必然不是边界,两者之间的则认为是候选项(称为弱边界,weak edge),需进行进一步处理。
3.5 消除孤立的弱边缘
在弱边缘的8领域范围寻找强边缘,如果8领域存在强边缘,就保留该弱边缘,否则就删除弱边缘,最终输出边缘检测结果。
4 函数详解
在OpenCV中,Canny求解边缘可以通过Canny()函数来实现。函数的定义如下:
void Canny( InputArray src, OutputArray edges, double threshold1, doubel threshold2, int apertureSize = 3, bool L2gradient = false);
函数参数的含义:
src -- 输入图像
edges -- 输出边缘图像
threshold1 -- 滞后过程的第一个阈值
threshold2 -- 滞后过程的第二个阈值
apertureSize -- Sobel算子核尺寸
L2gradient -- 是否应使用更精确的L2范数来计算图像梯度幅值 (true),或者是否应使用默认的L1范数(false)
5 用C++编写代码进行实现
下面是使用OpenCV中Canny()函数进行求取边缘的示例代码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char**argv)
{
Mat src = imread("E:/image/lena.png",0);
if (src.empty())
{
printf("could not load image....");
return -1;
}
Mat canny_low,canny_hight;
Canny(src, canny_low, 20, 40,3);
Canny(src, canny_hight, 100, 200, 3);
imshow("canny_low", canny_low);
imshow("canny_hight", canny_hight);
waitKey(0);
return 0;
}