Canny 边缘检测算法 是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,此算法被很多人认为是边缘检测的最优算法,相对其他边缘检测算法来说其识别图像边缘的准确度要高很多。
最优边缘检测的三个主要评价标准是:
- 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
- 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
- 最小响应: 图像中的边缘只能标识一次。
对Canny算子作深入剖析就会发现Canny算子对上述三个条件都能较好的满足。下面先描述下OpenCV中对Canny算子的实现函数:
//! applies Canny edge detector and produces the edge map.
CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
double threshold1, double threshold2,
int apertureSize=3, bool L2gradient=false );
其中:
4. image是输入图像,要求必须是8位单通道灰度图;
5. edges是输出图像,其是8位单通道二值化图像;
6. htreshold1是小阈值,当检测到的像素点的灰度值小于此阈值时将其标记为非边缘像素;
7. threshold2是大阈值,当检测到的像素点的灰度值大于此阈值时才标记为边缘像素;
注:当检测到的像素点的灰度值 g(x,y) > 小阈值 且 g(x,y) < 大阈值时,只有该像素点g(x,y)与边缘像素点连接(即g(x,y)为边缘像素点的邻域内的点)时才标记为边缘像素;
8. apertureSize为Sobel卷积核的内核大小,默认使用内核大小为3的卷积核;
Canny算子进行边缘检测步骤
1.转换灰度图
因为Canny算子只能对单通道灰度图像进行处理,因此在进行边缘检测之前需要将原图像进行灰度转换。openCV中已经封装好了一个函数用于转换各种格式的图像,如下:
//! converts image from one color space to another
CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn=0 );
使用该函数可以将多通道彩色图像转换为单通道灰度图。
2.滤波降噪处理
理想中的图像信息是无噪声的,图像质量很好,但是现实中由于采集设备、环境干扰等多方面的原因导致采集到的图像信息都是含有大量噪声信息的,这些噪声最常见的就是椒盐噪声和高斯噪声;Canny算子是一种综合在抗噪声干扰和精确定位之间寻求最佳折中方案的边缘检测方法,一般使用高斯滤波来去除噪声,下面是常见的3X3的卷积核模板:
[ 1 / 16 2 / 16 1 / 16 2 / 16 4 / 16 2 / 16 1 / 16 2 / 16 1 / 16 ] \left[ \begin{matrix} 1/16 & 2/16 & 1/16 \\ 2/16 & 4/16 & 2/16 \\ 1/16 & 2/16 & 1/16 \end{matrix} \right] ⎣⎡1/162/161/162/164/162/161/162/161/16⎦⎤
高斯滤波可以将图像中的噪声部分过滤出来,避免后面进行边缘检测时将错误的噪声信息也误识别为边缘了。但是滤波核的维数不宜选的过大,否则可能会将边缘信息给平滑掉,使得边缘检测算子无法正确识别边缘信息。
3.用一阶偏导的有限差分计算梯度的幅值和方向
使用一阶有限差分计算梯度可以得到图像在x和y方向上偏导数的两个矩阵,Canny算子中使用的是 Sobel 算子作为梯度算子,当然还可以自己构造其它的如:Roberts算子、Prewitt算子等一阶边缘检测算子来作为梯度算子。下面以Sobel算子为例来计算梯度的幅值和方向:
X方向:
S x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] Sx= \left[ \begin{matrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{matrix} \right] Sx=⎣⎡−1−2−1000121⎦⎤
Y方向:
S y = [ − 1 − 2 − 1 0 0 0 1 2 1 ] Sy= \left[ \begin{matrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{matrix} \right] Sy=⎣⎡−101−202−101⎦⎤
假设点 H ( i , j ) H{(i,j)} H(i,j)为我们要计算的图像:
H ( i , j ) = [ A 0 A 1 A 2 A 3 C A 5 A 6 A 7 A 8 ] H(i,j)= \left[ \begin{matrix} A0 & A1 & A2 \\ A3 & C & A5 \\ A6 & A7 & A8 \end{matrix} \right] H(i,j)=⎣⎡A0A3A6A1CA7A2