边缘提取Canny算子

Canny算子原理:

    Canny算子首先在x和y方向求一阶导数,然后组合为4个方向的导数。这些方向导数达到局部最大值(又叫非极大值抑制)的点就是组成边缘的候选点。然而,Canny算法最重要的一个新特点是其试图将独立的候选像素拼装成轮廓。轮廓的形成是对这些像素运用滞后性阈值。这意味着有两个阈值,上限和下限。如果一个像素的梯度大于上限阈值,则被认为是边缘像素,如果低于下限阈值,则被抛弃(灰度值被置为0),如果介于二者之间,只有当其与高于上限阈值的像素连接时才会被接受为边缘点(灰度值被置为255),最后canny的结果为二值图像。

Canny算子步骤:

1.消除噪声。 使用高斯平滑滤波器卷积降噪。 下面显示了一个size = 5 的高斯内核示例:

K = \dfrac{1}{159}\begin{bmatrix}          2 & 4 & 5 & 4 & 2 \\          4 & 9 & 12 & 9 & 4 \\          5 & 12 & 15 & 12 & 5 \\          4 & 9 & 12 & 9 & 4 \\          2 & 4 & 5 & 4 & 2                  \end{bmatrix}

2.计算梯度幅值和方向。 此处,按照Sobel滤波器的步骤:

           a.运用一对卷积阵列 (分别作用于xy 方向):

G_{x} = \begin{bmatrix}-1 & 0 & +1  \\-2 & 0 & +2  \\-1 & 0 & +1\end{bmatrix}G_{y} = \begin{bmatrix}-1 & -2 & -1  \\0 & 0 & 0  \\+1 & +2 & +1\end{bmatrix}

b.使用下列公式计算梯度幅值和方向:

\begin{array}{l}G = \sqrt{ G_{x}^{2} + G_{y}^{2} } \\\theta = \arctan(\dfrac{ G_{y} }{ G_{x} })\end{array}

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 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):

  1. 如果某一像素位置的幅值超过 阈值, 该像素被保留为边缘像素。
  2. 如果某一像素位置的幅值小于 阈值, 该像素被排除。
  3. 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 阈值的像素时被保留。

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 

  http://blog.csdn.net/xiajun07061225/article/details/6926108


你可能感兴趣的:(OpenCV技术,边缘检测,canny算法,opencv)