Opencv2.4.9源码分析——SIFT

时间:2022-01-28 09:21:30


SIFT(尺度不变特征变换,Scale-Invariant Feature Transform)是在计算机视觉领域中检测和描述图像中局部特征的算法,该算法于1999年被David Lowe提出,并于2004年进行了补充和完善。该算法应用很广,如目标识别,自动导航,图像拼接,三维建模,手势识别,视频跟踪等。不幸的是,该算法已经在美国申请了专利,专利拥有者为Lowe所在的加拿大不列颠哥伦比亚大学,因此我们不能随意使用它。

由于SIFT算法在计算机视觉的特征检测和特征描述中表现十分优异,因此该算法一经提出,就引起了广泛的关注。国内外对其研究的人很多,相关的资料也很多。在csdn中,有几位作者的文章对SIFT算法介绍得很详细,如网名为:zddhub、Rachel Zhang和xiaowei_cqu。由王永明和王贵锦所编著的,由国防工业出版社出版的《图像局部不变性特征与描述》也对该算法进行了详细的介绍。上述文章对我帮助很大。

经过一段时间的研究,并结合opencv中的源代码,自认为对SIFT算法有了一定的认识和体会,因此也写了一篇关于SIFT的文章。该文章共分为三部分,首先是SIFT的算法分析,然后是opencv的源码分析,最后是应用实例。在算法分析中,注意了每个细节的描述;在源码分析中,基本做到了每条代码都进行了注释;在应用实例中,列举了特征提取和图像匹配两个实例。

本想把这篇文章发表在这里,但文章比较长(有30多页),关键是公式太多,复制粘贴太麻烦,排版也不好。因此我把这篇文章分别上传到了csdn和百度文库,地址是:

http://wenku.baidu.com/view/d7edd2464b73f242336c5ffa.html

http://download.csdn.net/detail/zhaocj/8294793

可以在线阅读,也可以免费下载(在这里,鄙视那些设置下载权限和积分的人!!!)。如果上述方法都不方便,可以留下email,向我索要。文章中错误的地方欢迎指正!

为了不使这篇博文过于空洞,我把这篇文章的第三部分粘贴在这里。

首先给出的是特征点的检测:


#include "opencv2/core/core.hpp"
#include "highgui.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/nonfree/nonfree.hpp"

using namespace cv;
//using namespace std;

int main(int argc, char** argv)
{
   Mat img = imread("box_in_scene.png");

   SIFT sift;    //实例化SIFT类

   vector<KeyPoint> key_points;    //特征点
   // descriptors为描述符,mascara为掩码矩阵
   Mat descriptors, mascara;
   Mat output_img;    //输出图像矩阵

   sift(img,mascara,key_points,descriptors);    //执行SIFT运算
   //在输出图像中绘制特征点
   drawKeypoints(img,     //输入图像
	   key_points,      //特征点矢量
	   output_img,      //输出图像
	   Scalar::all(-1),      //绘制特征点的颜色,为随机
       //以特征点为中心画圆,圆的半径表示特征点的大小,直线表示特征点的方向
	   DrawMatchesFlags::DRAW_RICH_KEYPOINTS);     

   namedWindow("SIFT");
   imshow("SIFT", output_img);
   waitKey(0);

   return 0;
}

结果如下图所示:

Opencv2.4.9源码分析——SIFT

上面的程序需要说明一点的是,如果需要改变SIFT算法的默认参数,可以通过实例化SIFT类的时候更改,例如我们只想检测20个特征点,则实例化SIFT的语句为:


  SIFT sift(20);


 


下面给出利用描述符进行图像匹配的实例:


#include "opencv2/core/core.hpp"
#include "highgui.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
   //待匹配的两幅图像,其中img1包括img2,也就是要从img1中识别出img2
   Mat img1 = imread("box_in_scene.png");
   Mat img2 = imread("box.png");

   SIFT sift1, sift2;

   vector<KeyPoint> key_points1, key_points2;

   Mat descriptors1, descriptors2, mascara;

   sift1(img1,mascara,key_points1,descriptors1);
   sift2(img2,mascara,key_points2,descriptors2);
   
   //实例化暴力匹配器——BruteForceMatcher
   BruteForceMatcher<L2<float>> matcher;  
   //定义匹配器算子
   vector<DMatch>matches;  
   //实现描述符之间的匹配,得到算子matches
   matcher.match(descriptors1,descriptors2,matches);

   //提取出前30个最佳匹配结果
   std::nth_element(matches.begin(),     //匹配器算子的初始位置
	   matches.begin()+29,     // 排序的数量
	   matches.end());       // 结束位置
   //剔除掉其余的匹配结果
   matches.erase(matches.begin()+30, matches.end());

   namedWindow("SIFT_matches");  
   Mat img_matches;  
   //在输出图像中绘制匹配结果
   drawMatches(img1,key_points1,         //第一幅图像和它的特征点
	   img2,key_points2,      //第二幅图像和它的特征点
	   matches,       //匹配器算子
	   img_matches,      //匹配输出图像
	   Scalar(255,255,255));     //用白色直线连接两幅图像中的特征点
   imshow("SIFT_matches",img_matches);  
   waitKey(0);

   return 0;
}

结果如下图所示:

Opencv2.4.9源码分析——SIFT

程序是通过距离测度实现两幅图像描述符之间的比较的,距离越小,匹配性越好,越说明这两个描述符表示的是同一事物。描述符的匹配结果保存在匹配器算子matches中。如果直接使用matches,匹配效果并不好,因为它是尽可能的匹配所有的描述符。因此我们要进行筛选,只保留那些好的结果。在这里,我们利用排序,选择距离最小的前30个匹配结果,并进行输出。另外,matcher.match函数中,两个描述符的顺序一定不能写反,否则运行会出错。