图像特征检测(Image Feature Detection)


作者:王先荣
前言
    图像特征提取是计算机视觉和图像处理中的一个概念。它指的是使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征。本文主要探讨如何提取图像中的“角点”这一特征,及其相关的内容。而诸如直方图、边缘、区域等内容在前文中有所提及,请查看相关文章。OpenCv(EmguCv)中实现了多种角点特征的提取方法,包括:Harris角点、ShiTomasi角点、亚像素级角点、SURF角点、Star关键点、FAST关键点、Lepetit关键点等等,本文将逐一介绍如何检测这些角点。在此之前将会先介绍跟角点检测密切相关的一些变换,包括Sobel算子、拉普拉斯算子、Canny算子、霍夫变换。另外,还会介绍一种广泛使用而OpenCv中并未实现的SIFT角点检测,以及最近在OpenCv中实现的MSER区域检测。所要讲述的内容会很多,我这里尽量写一些需要注意的地方及实现代码,而参考手册及书本中有的内容将一笔带过或者不会提及。

Sobel算子
    Sobel算子用多项式计算来拟合导数计算,可以用OpenCv中的cvSobel函数或者EmguCv中的Image.Sobel方法来进行计算。需要注意的是,xorder和yorder中必须且只能有一个为非零值,即只能计算x方向或者y反向的导数;如果将方形滤波器的宽度设置为特殊值CV_SCHARR(-1),将使用Scharr滤波器代替Sobel滤波器。
    使用Sobel滤波器的示例代码如下:

Sobel算子

 
拉普拉斯算子
    拉普拉斯算子可以用作边缘检测;可以用OpenCv中的cvLaplace函数或者EmguCv中的Image.Laplace方法来进行拉普拉斯变换。需要注意的是:OpenCv的文档有点小错误,apertureSize参数值不能为CV_SCHARR(-1)。
    使用拉普拉斯变换的示例代码如下:

拉普拉斯算子

 
Canny算子
    Canny算子也可以用作边缘检测;可以用OpenCv中的cvCanny函数或者EmguCv中的Image.Canny方法来进行Canny边缘检测。所不同的是,Image.Canny方法可以用于检测彩色图像的边缘,但是它只能使用apertureSize参数的默认值3;
而cvCanny只能处理灰度图像,不过可以自定义apertureSize。cvCanny和Canny的方法参数名有点点不同,下面是参数对照表。
Image.Canny    CvInvoke.cvCanny
thresh                                         lowThresh
threshLinking                               highThresh
3                                                apertureSize
值得注意的是,apertureSize只能取3,5或者7,这可以在cvcanny.cpp第87行看到:


aperture_size &= INT_MAX;
    if( (aperture_size & 1) == 0 || aperture_size < 3 || aperture_size > 7 )
        CV_ERROR( CV_StsBadFlag, "" );

使用Canny算子的示例代码如下:

Canny算子

 

另外,在http://www.china-vision.net/blog/user2/15975/archives/2007/804.html有一种自动获取Canny算子高低阀值的方法,作者提供了用C语言实现的代码。我将其改写成了C#版本,代码如下:

计算图像的自适应Canny算子阀值

  霍夫变换     霍夫变换是一种在图像中寻找直线、圆及其他简单形状的方法,在OpenCv中实现了霍夫线变换和霍夫圆变换。值得注意的地方有以下几点:(1)HoughLines2需要先计算Canny边缘,然后再检测直线;(2)HoughLines2计算结果的获取随获取方式的不同而不同;(3)HoughCircles检测结果似乎不正确。     使用霍夫变换的示例代码如下所示:

霍夫变换

 

Harris角点     cvCornerHarris函数检测的结果实际上是一幅包含Harris角点的浮点型单通道图像,可以使用类似下面的代码来计算包含Harris角点的图像:

Harris角点

    如果要计算Harris角点列表,需要使用cvGoodFeatureToTrack函数,并传递适当的参数。

ShiTomasi角点     在默认情况下,cvGoodFeatureToTrack函数计算ShiTomasi角点;不过如果将参数use_harris设置为非0值,那么它会计算harris角点。 使用cvGoodFeatureToTrack函数的示例代码如下:

ShiTomasi角点

 

亚像素级角点     在检测亚像素级角点前,需要提供角点的初始为止,这些初始位置可以用本文给出的其他的角点检测方式来获取,不过使用GoodFeaturesToTrack得到的结果最方便直接使用。     亚像素级角点检测的示例代码如下:

亚像素级角点

 

SURF角点     OpenCv中的cvExtractSURF函数和EmguCv中的Image.ExtractSURF方法用于检测SURF角点。     SURF角点检测的示例代码如下:

SURF角点

 

Star关键点     OpenCv中的cvGetStarKeypoints函数和EmguCv中的Image.GetStarKeypoints方法用于检测“星型”附近的点。     Star关键点检测的示例代码如下:

Star关键点

  FAST角点检测     FAST角点由E. Rosten教授提出,相比其他检测手段,这种方法的速度正如其名,相当的快。值得关注的是他所研究的理论都是属于实用类的,都很快。Rosten教授实现了FAST角点检测,并将其提供给了OpenCv,相当的有爱呀;不过OpenCv中的函数和Rosten教授的实现似乎有点点不太一样。遗憾的是,OpenCv中目前还没有FAST角点检测的文档。下面是我从Rosten的代码中找到的函数声明,可以看到粗略的参数说明。 /* The references are:

 * Machine learning for high-speed corner detection,      E. Rosten and T. Drummond, ECCV 2006  * Faster and better: A machine learning approach to corner detection

   E. Rosten, R. Porter and T. Drummond, PAMI, 2009

*/ void cvCornerFast( const CvArr* image, int threshold, int N,

                   int nonmax_suppression, int* ret_number_of_corners,                    CvPoint** ret_corners);

image:      OpenCV image in which to detect corners. Must be 8 bit unsigned.

threshold:  Threshold for detection (higher is fewer corners). 0--255

N:          Arc length of detector, 9, 10, 11 or 12. 9 is usually best.

nonmax_suppression: Whether to perform nonmaximal suppression.

ret_number_of_corners: The number of detected corners is returned here.

ret_corners: The corners are returned here. EmguCv中的Image.GetFASTKeypoints方法也实现了FAST角点检测,不过参数少了一些,只有threshold和nonmaxSupression,其中N我估计取的默认值9,但是返回的角点数目我不知道是怎么设置的。 使用FAST角点检测的示例代码如下:

FAST关键点

 

Lepetit关键点     Lepetit关键点由Vincent Lepetit提出,可以在他的网站(http://cvlab.epfl.ch/~vlepetit/)上看到相关的论文等资料。EmguCv中的类LDetector实现了Lepetit关键点的检测。     使用Lepetit关键点检测的示例代码如下:

Lepetit关键点

SIFT角点     SIFT角点是一种广泛使用的图像特征,可用于物体跟踪、图像匹配、图像拼接等领域,然而奇怪的是它并未被OpenCv实现。提出SIFT角点的David Lowe教授已经用C和matlab实现了SIFT角点的检测,并开放了源代码,不过他的实现不方便直接使用。您可以在http://www.cs.ubc.ca/~lowe/keypoints/看到SIFT的介绍、相关论文及David Lowe教授的实现代码。下面我要介绍由Andrea Vedaldi和Brian Fulkerson先生创建的vlfeat开源图像处理库,vlfeat库有C和matlab两种实现,其中包含了SIFT检测。您可以在http://www.vlfeat.org/下载到vlfeat库的代码、文档及可执行文件。     使用vlfeat检测SIFT角点需要以下步骤:     (1)用函数vl_sift_new()初始化SIFT过滤器对象,该过滤器对象可以反复用于多幅尺寸相同的图像;     (2)用函数vl_sift_first_octave()及vl_sift_process_next()遍历缩放空间的每一阶,直到返回VL_ERR_EOF为止;     (3)对于缩放空间的每一阶,用函数vl_sift_detect()来获取关键点;     (4)对每个关键点,用函数vl_sift_calc_keypoint_orientations()来获取该点的方向;     (5)对关键点的每个方向,用函数vl_sift_calc_keypoint_descriptor()来获取该方向的描述;     (6)使用完之后,用函数vl_sift_delete()来释放资源;     (7)如果要计算某个自定义关键点的描述,可以使用函数vl_sift_calc_raw_descriptor()。     直接使用vlfeat中的SIFT角点检测示例代码如下:

通过P/Invoke调用vlfeat函数来进行SIFT检测

    要在.net中使用vlfeat还是不够方便,为此我对vlfeat中的SIFT角点检测部分进行了封装,将相关操作放到了类SiftDetector中。     使用SiftDetector需要两至三步:     (1)用构造函数初始化SiftDetector对象;     (2)用Process方法计算特征;     (3)视需要调用Dispose方法释放资源,或者等待垃圾回收器来自动释放资源。     使用SiftDetector的示例代码如下:

通过dotnet封装的SiftDetector类来进行SIFT检测

    对vlfeat库中的SIFT部分封装代码如下所示:

定义SiftDetector类

MSER区域     OpenCv中的函数cvExtractMSER以及EmguCv中的Image.ExtractMSER方法实现了MSER区域的检测。由于OpenCv的文档中目前还没有cvExtractMSER这一部分,大家如果要看文档的话,可以先去看EmguCv的文档。     需要注意的是MSER区域的检测结果是区域中所有的点序列。例如检测到3个区域,其中一个区域是从(0,0)到(2,1)的矩形,那么结果点序列为:(0,0),(1,0),(2,0),(2,1),(1,1),(0,1)。     MSER区域检测的示例代码如下:

MSER(区域)特征检测

 

各种特征检测方法性能对比     上面介绍了这么多的特征检测方法,那么它们的性能到底如何呢?因为它们的参数设置对处理时间及结果的影响很大,我们在这里基本都使用默认参数处理同一幅图像。在我机器上的处理结果见下表:

特征 用时(毫秒) 特征数目
Sobel算子 5.99420 n/a
拉普拉斯算子 3.13440 n/a
Canny算子 3.41160 n/a
霍夫线变换 13.70790 10
霍夫圆变换 78.07720 0
Harris角点 9.41750 n/a
ShiTomasi角点 16.98390 18
亚像素级角点 3.63360 18
SURF角点 266.27000 151
Star关键点 14.82800 56
FAST角点 31.29670 159
SIFT角点 287.52310 54
MSER区域 40.62970 2

(图片尺寸:583x301,处理器:AMD ATHLON IIx2 240,内存:DDR3 4G,显卡:GeForce 9500GT,操作系统:Windows 7)

 

感谢您耐心看完本文,希望对您有所帮助。

下一篇文章我们将一起看看如何来跟踪本文讲到的特征点(角点)。

另外,如果需要本文的源代码,请点击这里下载