多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
##特征提取算法SIFT与SURF 当尝试在不同图像之间进行特征匹配时,通常会遇到图像的大小、方向等参数发生改变的问题,简而言之,就是尺度变化的问题。每幅图像在拍摄时与目标物体的距离是不同的,因此要识别的目标物体在图像中自然会存在不同的尺寸。 因此,计算机视觉中引入尺度不变的特征,主要的思想是每个检测到的特征点都伴随对应的尺度因子。1999年David Lowe提出了著名的尺度不变特征检测器SIFT(Scale Invariant Feature Transform)算法,它具有尺度,旋转,仿射,视角,光照不变性。而加速鲁棒特性特征SURF(Speeded Up Robust Features)算法是SIFT的高效变种。这两个算法申请了专利保护,其专利的拥有者为英属哥伦比亚大学。 关于SIFT和SURF的特征介绍,已经有很多的blog对其进行简介了,见参考的blog。由于还没有将2004年那篇原文精细看完,因此这里只能粗浅地分析两种算法,并描述在OpenCV中如何实现这两种算法的特征检测。 **一、基本概念** 1.1 算法背景 尺度不变特征转换SIFT是一种著名的计算机视觉的算法,主要用来检测图像中的局部特征,通过在图像中寻找极值点特征,并提取出其这些特征点的位置、尺度和旋转不变量等信息。  其应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。 局部影像特征的描述与侦测可以帮助辨识物体,SIFT 特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。对于光线、噪声、些微视角改变的容忍度也相当高。基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。使用 SIFT特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即时运算。SIFT特征的信息量大,适合在海量数据库中快速准确匹配。(来自百度百科的解释) 1.2 SIFT算法的主要优点 1\. SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;  2\. 独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;  3\. 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;  4\. 高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;  5\. 可扩展性,可以很方便的与其他形式的特征向量进行联合。 1.3 SIFT算法的适用环境 目标的自身状态、场景所处的环境和成像器材的成像特性等因素影响图像配准/目标识别跟踪的性能。而SIFT算法在一定程度上可解决: 1\. 目标的旋转、缩放、平移(RST)  2\. 图像仿射/投影变换(视点viewpoint)  3\. 光照影响(illumination)  4\. 目标遮挡(occlusion)  5\. 杂物场景(clutter)  6\. 噪声 SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。 1.4 算法的基本步骤 Lowe将SIFT算法分解为如下四步: 1\. 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。  2\. 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。  3\. 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。  4\. 特征点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。 1.5 SIFT算法的缺点 SIFT算法十分强大,精度很高,Mikolajczyk和Schmid曾经针对不同的场景,对光照变化、图像几何变形、分辨率差异、旋转、模糊和图像压缩等6种情况,就多种最具代表性的描述子(如SIFT,矩不变量,互相关等10种描述子)进行了实验和性能比较,结果表明,在以上各种情况下,SIFT描述子的性能最好。但算法的高深同样带来了一些缺点,如: 1\. 实时性不高。  2\. 有时特征点较少。  3\. 对边缘光滑或经过平滑的图像无法准确提取特征点。 针对这些问题,一些改进算法应运而生,SURF就是广为人知的其中一种算法。 2.1 SURF算法介绍 SURF算法是SIFT算法的高效变种,在满足一定效果的情况下完成两幅图像中物体的匹配,并基本实现了实时处理。SURF也检测空间域和尺度域上的局部极大值作为特征,但是使用的是Hessian行列式响应而不是Laplacian行列式。 SURF的实现如下,首先对每个像素计算Hessian矩阵以得到特征,该矩阵测量一个函数的局部曲率,定义如下: ![](https://box.kancloud.cn/2015-12-30_5683a75d6b747.jpg) 该矩阵的行列式给出曲率的强度,定义角点为具有较高局部曲率的像素点(即在多个方向都具有高曲率)。由于该函数是由二阶导数组成,因此可以使用不同的![](https://box.kancloud.cn/2015-12-30_5683a75d84216.jpg) 尺度的Laplacian Gaussian核进行计算,因此Hessian变成了三个变量的函数。当Hessian的值同时在空间域和尺度域上均达到局部极大值时(需要运行3*3*3的非极大值抑制),可以认为找到了尺度不变的特征。 所有这些不同尺度的运算都很耗时,而SURF算法的目的是尽可能高效。因此会使用近似的高斯核,仅涉及少量整数加法,结构如下图所示: ![](https://box.kancloud.cn/2015-12-30_5683a75d91895.jpg) 左侧的核用于估计混合二阶导数,右侧的核则用于估算垂直方向的二阶导数,而其旋转版本则用于估计水平方向的二阶导数。 一旦识别了局部极大值,每个特征点的精确位置可以通过空间域及尺度域上进行插值获取,结果是一组具有亚像素精确度的特征点,以及一个对应的尺度值。 ![](https://box.kancloud.cn/2015-12-30_5683a75dabdbb.jpg) 2.2 性能比较 无需赘述,A comparison of SIFT, PCA-SIFT and SURF 一文给出了SIFT和SURF的性能比较,对原图像进行尺度、旋转、模糊、亮度变化、仿射变换等变化后,再与原图像进行匹配,统计匹配的效果,源图片来源于Graffiti dataset。 ![](https://box.kancloud.cn/2015-12-30_5683a75dc3bdc.jpg) 这里使用的开发平台是Qt5.3.2+OpenCV2.4.9。SURF、SIFT特征在OpenCV中的实现均使用了cv::FeatureDetector接口,OpenCV中的opencv_nonfree模块包含了SURF和SIFT的相关函数,因此在使用时注意添加相关的头文件。在这里,特征点的计算基于浮点核,因此这两种算法相比其他算法在空间和尺度检测上更加精确,但相对耗时。 至于理论部分有待进一步学习。 **二、实现SIFT特征检测算法** 直接在Qt中创建一个控制台项目,在main函数中添加: ~~~ #include <QCoreApplication> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/nonfree/nonfree.hpp> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 读入图像 cv::Mat image= cv::imread("c:/018.jpg",0); cv::namedWindow("Original Image"); cv::imshow("Original Image", image); // 特征点的向量 std::vector<cv::KeyPoint>keypoints; // 构造SIFT特征检测器 cv::SiftFeatureDetector sift( 0.03, // 特征的阈值 10.); // 用于降低 // 检测SIFT特征值 sift.detect(image,keypoints); cv::drawKeypoints(image, // 原始图像 keypoints, // 特征点的向量 featureImage, // 生成图像 cv::Scalar(255,255,255), // 特征点的颜色 cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); // 标志位 cv::namedWindow("SIFT Features"); cv::imshow("SIFT Features",featureImage); return a.exec(); } ~~~ 效果如下,在函数cv::drawKeypoints中我们使用cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS作为标志位,这样唉使用DRAW_RICH_KEYPOINTS之后每个关键点上圆圈的尺寸与特征的尺度成正比: ![](https://box.kancloud.cn/2015-12-30_5683a75dd84f7.jpg) **三、实现SURF特征检测算法** ~~~ #include <QCoreApplication> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/nonfree/nonfree.hpp> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 读入图像 cv::Mat image= cv::imread("c:/018.jpg",0); cv::namedWindow("Original Image"); cv::imshow("Original Image", image); // 特征点的向量 std::vector<cv::KeyPoint>keypoints; // 构造SURF特征检测器 cv::SurfFeatureDetector surf(2500); // 检测SURF特征 surf.detect(image,keypoints); cv::Mat featureImage; cv::drawKeypoints(image, // 原始图像 keypoints, // 特征点的向量 featureImage, // 生成图像 cv::Scalar(255,255,255), // 特征点的颜色 cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); // 标志位 cv::namedWindow("SURF Features"); cv::imshow("SURF Features",featureImage); return a.exec(); } ~~~ 效果: ![](https://box.kancloud.cn/2015-12-30_5683a75e3f0a7.jpg) 从两种算法的输出结果可以看出,SURF与SIFT描述子的区别主要是速度及精度。SURF描述子大部分基于强度的差值,计算更加快捷,而SIFT描述子通常在搜索正确的特征点时更加精确。 参考资料: David G.Lowe Object Recognition from Local Scale-Invariant Features. 1999. David G.Lowe Distinctive Image Features from Scale-Invariant Keypoints. January 5, 2004. SIFT官网的Rob Hess [hess@eecs.oregonstate.edu](mailto:hess@eecs.oregonstate.edu) SIFT源码 参考博客: [http://blog.csdn.net/zddblog/article/details/7521424](http://blog.csdn.net/zddblog/article/details/7521424) (个人认为是目前最详尽的SIFT介绍) [http://underthehood.blog.51cto.com/2531780/658350](http://underthehood.blog.51cto.com/2531780/658350) [http://blog.csdn.net/xiaowei_cqu/article/details/8069548](http://blog.csdn.net/xiaowei_cqu/article/details/8069548) [http://www.cnblogs.com/tornadomeet/archive/2012/08/16/2643168.html](http://www.cnblogs.com/tornadomeet/archive/2012/08/16/2643168.html)