边缘检测算子之Canny

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Jacky_Ponder/article/details/64126411

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 算法做边缘检测

扫描二维码关注公众号,回复: 3811537 查看本文章

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;

}

猜你喜欢

转载自blog.csdn.net/Jacky_Ponder/article/details/64126411