Canny算法 边缘检测

边缘检测是图像处理计算机视觉中的基本问题,边缘检测的目的是标识数字图像亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括(i)深度上的不连续、(ii)表面方向不连续、(iii)物质属性变化和(iv)场景照明变化。
 边缘检测是图像处理计算机视觉中,尤其是特征提取中的一个研究领域。

图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大和最小值来检测边界,通常是将边界定位在梯度最大的方向。基于零穿越的方法通过寻找图像二阶导数零穿越来寻找边界,通常是Laplacian过零点或者非线性差分表示的过零点。

Canny 边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。更为重要的是 Canny 创立了边缘检测计(Computational theory of edge detection)解释这项技术如何工作。

Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:

  • 好的检测 - 算法能够尽可能多地标识出图像中的实际边缘。
  • 好的定位 - 标识出的边缘要尽可能与实际图像中的实际边缘尽可能接近。
  • 最小响应 - 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。

为了满足这些要求 Canny 使用了变分法,这是一种寻找满足特定功能函数的方法。最优检测使用四个指数函数项的和表示,但是它非常近似于高斯函数的一阶导数

/***********************************************Canny算法实现过程*************************************************/

分为以下几个步骤:

1、图像灰度化:只有灰度图才能进行边缘检测

2、去噪:噪声点将影响边缘检测的准确性

3、求解梯度幅度和方向:利用sobel算子求解

4、非极大值抑制:定位准确的边缘同时可缩小边缘线宽

5、双阀值算法检测及连接边缘


1、图像灰度化

Canny算法通常处理的图像为灰度图,因此如果摄像机获取的是彩色图像,那首先就得进行灰度化。对一幅彩色图进行灰度化,就是根据图像各个通道的采样值进行加权平均。以RGB格式的彩图为例,通常灰度化采用的方法主要有:

        方法1:Gray=(R+G+B)/3;

        方法2:Gray=0.299R+0.587G+0.114B;

注意1:至于其他格式的彩色图像,可以根据相应的转换关系转为RGB然后再进行灰度化;

注意2:在编程时要注意图像格式中RGB的顺序通常为BGR


2、去噪

噪声的存在影响到边缘的检测,首先将图片进行高斯去噪,高斯去噪其实就是一个低通滤波器,滤除高频噪声。

3、求解梯度幅度和方向

梯度的幅度及方向采用sobel算子求解,索贝尔算子(Sobel operator)是图像处理中的算子之一,主要用作边缘检测。在技术上,它是一离散性差分算子,用来运算图像亮度函数的梯度之近似值。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。

该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以\mathbf{A}代表原始图像,\mathbf{G_x}\mathbf{G_y}分别代表经横向及纵向边缘检测的图像,其公式如下:


图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小。


然后可用以下公式计算梯度方向。


在以上例子中,如果以上的角度\Theta等于零,即代表图像该处拥有纵向边缘,左方较右方暗。


4、非极大值抑制

在求出的幅值图像中,可能存在多个较大幅值临近的情况,但真正的边缘点只有一个,针对这样的情况我们进行非极大值抑制,找出局部最大值,从而可以剔除大部分非边缘点


如上图所示,我们对每一像素点做如下处理:

根据该像素点的梯度方向,确定需进行比较的临近像素点位置,如上图所示(过像素点方向为梯度方向的直线与该像素点临近的八像素点所组成的矩形,相交于dTmp1和dTmp2,根据g1,g2a和g3,g4估算出交点像素,如果该像素点均大于两交点像素则为极大值边缘点,否则为非边缘点


5、双阀值算法检测及连接边缘

  1. 滞后阈值: 最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):

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

    Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。

下面我们将看到基于opencv中的canny边缘算法的具体实现(opencv中已经实现了canny算法我们只需调用即可,具体实现可见源码):

/**************************************************
 * C++ Canny:Canny边缘检测
 **************************************************/

#include "stdafx.h"

#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
 
using namespace cv;
using namespace std;
 
int edgeThresh = 1;
 
// 声明 原始图片,灰度图片,和 canny边缘图片
IplImage *image;
IplImage *gray, *edge;
 
void onTrackbar(int, void*)
{
	// 高斯滤波
	cvSmooth(gray, edge, CV_GAUSSIAN, 3, 3, 0 );
 
	// Canny 边缘检测
	cvCanny(gray,edge, edgeThresh, edgeThresh*3, 3);
    
	// 显示图片
	cvShowImage("Edge map", edge);
}
 
int main()
{
 
	// 载入图片
	image = cvLoadImage("E:\\test\\lenna.bmp");
 
	// 判断载入图片是否成功
	if( image == NULL )
	{
		printf("miss the image file\n");
		return -1;
	}
 
	// 生成灰度图片,因为只有灰度图片才能生成边缘图片
	gray = cvCreateImage( cvGetSize(image),IPL_DEPTH_8U ,1 );
	edge = cvCreateImage( cvGetSize(image),IPL_DEPTH_8U ,1 );
	cvCvtColor(image,gray, CV_BGR2GRAY);
 
	// 新建一个窗口
	namedWindow("Edge map", 1);
 
	// 生成一个进度条来控制边缘检测
	createTrackbar("Canny Threshold", "Edge map", &edgeThresh, 100, onTrackbar);
 
	// 初始化图像
	onTrackbar(0,0);
 
	waitKey(0);
	cvReleaseImage( &image );
	cvReleaseImage( &edge );
	cvReleaseImage( &gray );
 
	return 0;
}


posted @ 2013-08-07 10:46  Black Small  阅读(12747)  评论(1编辑  收藏  举报