1.1Canny算法简介
Canny 边缘检测算子是 JohnF. Canny 在 1986 年提出的一个多级边缘检测算子。Canny算法与简单的基于拉普拉斯算法的不同点之一是在Canny算法中,首先在x和y方向求一阶导数,然后组合为4个方向的导数。这些方向导数达到局部最大值的点就是组成边缘的候选点。
然而,Canny算法最重要的一个新特点是其试图将独立边的候选像素拼装成轮廓。轮廓的形成是对这些像素运用滞后性阈值。这意味着有两个阈值,上限和下限。如果一个像素的梯度大于上限阈值,则被认为是边缘像素,如果低于下限阈值,则被抛弃,如果介于二者之间,只有当其与高于上限阈值的像素连接时才会被接受。
Canny推荐的上下限阈值为2:1到3:1之间。
1.2Canny流程
Canny 提出了三个严格的边缘检测标准:1)最优检测标准,即对边缘点和非边缘点具有较高的区分概率;2)最优定位准则,即判定的边缘点的位置应尽可能靠近真实边缘的中心位置;3)单边响应准则,即对于每个检测出的边缘点,应当仅有一个响应,最大可能地抑制伪边缘的出现。Canny 算子对数字图像进行边缘检测主要分为四步进行:1)对图像进行二维高斯滤波;2)通过一阶微分计算图像的灰度梯度幅值和方向;3)对计算出的梯度幅值进行非极大值抑制(Non-MaximaSuppression, NMS);4)通过人为设定的高低阈值确定图像的边缘。
(1)二维高斯滤波
(2)计算图像灰度的梯度幅值和方向
(3)对图像的梯度幅值进行非极大值抑制
图像中,梯度值较大的点不一定就是图像中的边缘点,为了进一步的剔除这些点对边缘检测的影响,细化图像中的屋脊带,更准确地定位图像中的边缘点,需要对一阶微分计算后的图像数据进行非极大值抑制,只保留幅值局部变化最大的点。非极大值抑制在像素的 3×3 邻域上,沿梯度方向进行梯度幅值的插值,并将中心像素的梯度值与沿梯度方向相邻的 2 个梯度幅值的插值结果进行比较,若像素点本身的梯度幅值H[i,j]比梯度方向上的 2 个插值小,则将H[i,j]对应的边缘标志位赋值为 0,反之,则认为该像素为初选边缘点,H[i,j]的值保持不变。通过非极大值抑制,可以把图像梯度幅值矩阵H[i,j]中的宽屋脊带细化到一个像素宽,并且保留了屋脊的梯度幅值。
(4)双阈值确定图像边缘
假设图像梯度幅值矩阵H[i,j]经非极大值抑制后的图像为Q[i,j],此时Q[i,j]中仍可能含有由于噪声和纹理存在的原因而检测到的假边缘。不同于前面所述的经典微分算子,Canny算子采用双阈值算法对图像做进一步处理,消除假边缘。假定设定的高低阈值分别为th和tl,通常情况下tl=0.5*th,那么,在图像边缘阵列 Q[i,j]中,梯度值大于th的点被标记为边缘点,梯度值小于tl的点则被视为非边缘点。对于梯度值介于th与tl之间的像素点,若其8邻域中存在已经被标记的边缘点,则该点也标记为边缘点,否则标记为非边缘点。
1.3Canny算子格式
采用 Canny 算法做边缘检测
void cvCanny( const CvArr* image, CvArr*edges, double threshold1, double threshold2, int aperture_size=3 );
image
单通道输入图像.
edges
单通道存储边缘的输出图像
threshold1
第一个阈值
threshold2
第二个阈值
aperture_size
Sobel 算子内核大小 (见 cvSobel).
函数 cvCanny 采用 CANNY 算法发现输入图像的边缘而且在输出图像中标识这些边缘。threshold1和threshold2 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。
注意事项:因此cvLoadImage的第二给参数表示是否加载有颜色的图像,因设为0,表示单通道图像,故src = cvLoadImage(argv[1], 0 );
否则会出现编译错误,会提示canny.cpp不合法。
1.4范例
#include <highgui.h>
#include <cv.h>
#include <cxcore.h> //人脸识别的一个库文件
//Canny:Implements Canny algorithm for edgedetection.
int main( int argc, char** argv )
{
IplImage*src = NULL;
IplImage*dst = NULL;
//载入图像,转换为灰度图
src= cvLoadImage( argv[1], 0 );
//为canny边缘图像申请空间,1表示单通道灰度图
dst= cvCreateImage( cvGetSize( src ), IPL_DEPTH_8U, 1 );
cvCanny(src, dst, 50, 150, 3 );//边缘检测
cvNamedWindow("src", 1 );
cvNamedWindow("canny", 1 );
cvShowImage("src", src );
cvShowImage("canny", dst );
cvWaitKey(0);
cvReleaseImage(&src );
cvReleaseImage(&dst );
cvDestroyAllWindows();
return0;
}