Canny算子原理:
Canny算子首先在x和y方向求一阶导数,然后组合为4个方向的导数。这些方向导数达到局部最大值(又叫非极大值抑制)的点就是组成边缘的候选点。然而,Canny算法最重要的一个新特点是其试图将独立的候选像素拼装成轮廓。轮廓的形成是对这些像素运用滞后性阈值。这意味着有两个阈值,上限和下限。如果一个像素的梯度大于上限阈值,则被认为是边缘像素,如果低于下限阈值,则被抛弃(灰度值被置为0),如果介于二者之间,只有当其与高于上限阈值的像素连接时才会被接受为边缘点(灰度值被置为255),最后canny的结果为二值图像。
Canny算子步骤:
1.消除噪声。 使用高斯平滑滤波器卷积降噪。 下面显示了一个 的高斯内核示例:
2.计算梯度幅值和方向。 此处,按照Sobel滤波器的步骤:
a.运用一对卷积阵列 (分别作用于 和 方向):
b.使用下列公式计算梯度幅值和方向:
c.梯度方向近似到四个可能角度之一(一般 0, 45, 90, 135)
3.非极大值抑制。这一步排除非边缘像素,仅仅保留了一些细线条(候选边缘)
图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这仅仅是属于图像增强的过程)。在Canny算法中,非极大值抑制是进行边缘检测的重要步骤,通俗意义上是指寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉一大部分非边缘的点(这是本人的理解)。
图1 非极大值抑制原理
根据图1 可知,要进行非极大值抑制,就首先要确定像素点C的灰度值在其8值邻域内是否为最大。图1中蓝色的线条方向为C点的梯度方向,这样就可以确定其局部的最大值肯定分布在这条线上,也即出了C点外,梯度方向的交点dTmp1和dTmp2这两个点的值也可能会是局部最大值。因此,判断C点灰度与这两个点灰度大小即可判断C点是否为其邻域内的局部最大灰度点。如果经过判断,C点灰度值小于这两个点中的任一个,那就说明C点不是局部极大值,那么则可以排除C点为边缘。这就是非极大值抑制的工作原理。
作者认为,在理解的过程中需要注意以下两点:
a.中非最大抑制是回答这样一个问题:“当前的梯度值在梯度方向上是一个局部最大值吗?” 所以,要把当前位置的梯度值与梯度方向上两侧的梯度值进行比较;
b.梯度方向垂直于边缘方向。
但实际上,我们只能得到C点邻域的8个点的值,而dTmp1和dTmp2并不在其中,要得到这两个值就需要对该两个点两端的已知灰度进行线性插值,也即根据图1中的g1和g2对dTmp1进行插值,根据g3和g4对dTmp2进行插值,这要用到其梯度方向,这是上文Canny算法中要求解梯度方向矩阵Thita的原因。
非极大值抑制的作用是:细化幅值图像中的屋脊带,即只保留幅值局部变化最大的点。
将梯度角的变化范围减小到圆周的四个扇区之一,方向角和幅值分别为:
非极大值抑制通过抑制梯度线上所有非屋脊峰值的幅值来细化M[i,j],中的梯度幅值屋脊.这一算法首先将梯度角θ[i,j]的变化范围减小到圆周的四个扇区之一,如下图所示:
接着可以使用简化的算法来实现非极大值抑制,舍去灰度插值的过程,其原理如下图:
4.最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):
Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。
#include "cv.h"
#include "highgui.h"
IplImage* doCanny(
IplImage* in,
double lowThresh,
double highThresh,
double aperture)
{
if (in->nChannels != 1)
return(0); // Canny only handles gray scale images
IplImage* out = cvCreateImage(
cvGetSize( in ),
in->depth, //IPL_DEPTH_8U,
1);
cvCanny( in, out, lowThresh, highThresh, aperture );
return( out );
};
int main( int argc, char** argv )
{
IplImage* img_rgb = cvLoadImage( "G:/数据/OpenCV数据/shark.jpg" );
IplImage* img_gry = cvCreateImage( cvSize( img_rgb->width,img_rgb->height ), img_rgb->depth, 1);
cvCvtColor(img_rgb, img_gry ,CV_BGR2GRAY);
cvNamedWindow("Example Gray", CV_WINDOW_AUTOSIZE );
cvNamedWindow("Example Canny", CV_WINDOW_AUTOSIZE );
cvShowImage("Example Gray", img_gry );
IplImage* img_cny = doCanny( img_gry, 10, 100, 3 );
cvShowImage("Example Canny", img_cny );
cvWaitKey(0);
cvReleaseImage( &img_rgb);
cvReleaseImage( &img_gry);
cvReleaseImage( &img_cny);
cvDestroyWindow("Example Gray");
cvDestroyWindow("Example Canny");
}
文章引用:opencv中文网站、学步园、江南烟雨
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.html
http://www.xuebuyuan.com/1541968.html