此博文为本人写的第一篇博文,写博文的主要目的呢有两个:第一就是对自己做过的工作进行总结;第二就是希望跟志同道合的人相互学习交流~
本篇博文主要是我自学SIFT、SURF、ORB三种算法(三种特征描述子)过程的笔记以及运行的代码。博文主要是对于三种算法的 归纳以及加入我自己的一些思考与理解。当然里面还有一些出现的问题不知道怎么解决的,也放到博文里面(希望有大神可以在评论不吝赐教),后续也会继续更新,请广大读者批评指正谢谢。
在opencv3中,这三个算子都转移到一个名为xfeature2d的第三方库中,而在opencv2中这三个算子在nonfree库中,为此我还特意把opencv3.2改为opencv2.4.9和2.4.13。
关于在vs下配置opencv可参考https://blog.csdn.net/poem_qianmo/article/details/19809337。个人感觉这个教程比较好,注意版本号不一样修改对应的lib就好了。
一.SIFT
Scale Invariant Feature Transform(SIFT) 尺度不变特征变换。SIFT特征用于描述图像这种的局部特征。是一种关键点(或者叫做特征点)的检测和描述的算法。SIFT算法应用于图像特征点的提取,首先建立图像的尺度空间表示,接着在尺度空间中搜索图像的极值点,通过这些极值点(也称关键点,特征点。包含三个主要信息:位置、尺度、方向),从而建立特征描述向量。通过特征描述向量来做图像识别与检测方面的问题。
SIFT由David Lowe提出,且已经申请了专利保护。
SIFT的算法流程图如下图所示:
Mat srcImage2 = imread("hand2.jpg", 1);
//CV_Assert用于判断输入数据的合法性,当该函数为false时,返回一个错误信息
CV_Assert(srcImage1.data != NULL && srcImage2.data != NULL);
//转换为灰度图并做归一化
Mat grayMat1, grayMat2;
cvtColor(srcImage1, grayMat1, CV_BGR2GRAY);
normalize(grayMat1, grayMat1, 0, 255, NORM_MINMAX);
cvtColor(srcImage2, grayMat2, CV_BGR2GRAY);
normalize(grayMat2, grayMat2, 0, 255, NORM_MINMAX);
//定义SIFT描述子
SiftFeatureDetector detector;
//这个对象顾名思义就是SIFT特征的探测器,用它来探测图片中SIFT点的特征,存到一个KeyPoint类型的vector中。
/*keypoint只是保存了opencv的sift库检测到的特征点的一些基本信息,但sift所提取出来的特征向量其实不是在这个里面,特征向量通过SiftDescriptorExtractor 提取,
结果放在一个Mat的数据结构中。这个数据结构才真正保存了该特征点所对应的特征向量。*/
//得到keypoint只是达到了关键点的位置,方向等信息,并无该特征点的特征向量,要想提取得到特征向量就还要进行SiftDescriptorExtractor 的工作。
SiftDescriptorExtractor extractor;
//建立了SiftDescriptorExtractor 对象后,通过该对象,对之前SIFT产生的特征点进行遍历,找到该特征点所对应的128维特征向量。
//SiftDescriptorExtractor对应于SIFT算法中特征向量提取的工作,通过他对关键点周围邻域内的像素分块进行梯度运算,得到128维的特征向量。
//特征点的检测,并放入keypoint类型的vector中
vector keypoints1;
detector.detect(grayMat1, keypoints1);
vector keypoints2;
detector.detect(grayMat2, keypoints2);
//计算特征点描述子
Mat descriptors1;
extractor.compute(grayMat1, keypoints1, descriptors1);
Mat descriptors2;
extractor.compute(grayMat2, keypoints2, descriptors2);
//特征点匹配
//两幅图片的特征向量被提取出来后,我们就可以使用BruteForceMatcher对象对两幅图片的descriptor进行匹配,得到匹配的结果到matches中
vector matches;
BruteForceMatcher > matcher;
matcher.match(descriptors1, descriptors2, matches);
//二分排序
int N = 80;
nth_element(matches.begin(), matches.begin() + N - 1, matches.end());
//方法是,nth位置的元素放置的值就是把所有元素排序后在nth位置的值.把所有不大于nth的值放到nth的前面,把所有不小于nth的值放到nth后面.
matches.erase(matches.begin() + N, matches.end());//去除特征点不匹配情况。erase(pos,n); 删除从pos开始的n个字符。
//绘制检测结果
Mat resultMat;
drawMatches(srcImage1, keypoints1, srcImage2, keypoints2, matches, resultMat);
imshow("jieguo", resultMat);
waitKey( );
return 0;
}
程序是可以正常运行的,出来的结果如下:
SIFT sift(200);//设置了200个特征点
vector key_points;//存放特征点,存放检测出来的特征点
Mat descriptors, mascara;// descriptors为描述符,mascara为掩码矩阵
sift(img, mascara, key_points, descriptors);//执行sift运算
Mat output_img; //输出图像矩阵
//在输出图像上绘制特征点
drawKeypoints(img, //输入图像
key_points, //特征点矢量
output_img, //输出图像
Scalar::all(-1), //绘制特征点的颜色,为随机
//以特征点为中心画圆,圆的半径表示特征点的大小,直线表示特征点的方向
DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("sift tu", output_img);
waitKey(0);
return 0;
}
结果如下:
这样每个小区域就有4个值,所以每个特征点就是16*4=64维的向量,相比sift而言,少了一半,这在特征匹配过程中会大大加快匹配速度。
代码演示:
opencv中SURF部分设计到三个类:SURF、SurfFeatureDetector、SurfDescriptorExtractor
根据features2d.hpp头文件中的两句定义:
-
typedef SURF SurfFeatureDetector;
-
typedef SURF SurfDescriptorExtractor;
其中,typedef声明是为现有类型创建一个新的名字,类型别名,即SURF类有了两个新名字SurfFeatureDetector以及SurfDescriptorExtractor。也就是说,SurfFeatureDetector类和SurfDescriptorExtractor类,其实就是SURF类,他们三者等价。下图给出了SURF相关类之间的关系:
SurfFeature(src1,src2,1000);
return 0;
}
运行结果如下:
imshow("测试图",src);
//创建放置关键点的vector
vector keyPoints;
//创建FAST对象,并将阈值设定为55
FastFeatureDetector fast(55);
//获取特征点
fast.detect(src, keyPoints);
//在原图上画出特征点
drawKeypoints(src, keyPoints, src, Scalar(0, 0, 255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
imshow("FAST feature", src);
waitKey(0);
return 0;
}
结果图如下:
Mat srcImage2 = imread("hand2.jpg", 1);
//转换为灰度图
Mat grayImage1, grayImage2;
cvtColor(srcImage1, grayImage1, CV_RGB2GRAY);
cvtColor(srcImage2, grayImage2, CV_RGB2GRAY);
//定义特征点检测器(注意SurfFeatureDetector和SurfDescriptorExtractor是等价的)
SurfFeatureDetector detector(400);
//声明存放关键点的容器
vector keyPoints1, keyPoints2;
//特征点检测
detector.detect(grayImage1, keyPoints1);
detector.detect(grayImage2, keyPoints2);
//定义特征提取器,采用了BRIEF特征描述子
BriefDescriptorExtractor extractor(64);// //参数表示字节数,采用长度为64×8=512的向量表示
//注意bytes参数表示的是描述子占用的字节数不是描述子长度,如默认采用32字节对应描述子长度为32×8=256;
//声明存放特征向量的特征描述矩阵
Mat descriptorMat1, descriptorMat2;
//计算SURF特征描述矩阵(计算特征向量)
extractor.compute(grayImage1, keyPoints1, descriptorMat1);
extractor.compute(grayImage2, keyPoints2, descriptorMat2);
//用匹配器进行匹配。之前介绍过FLANN匹配器和暴力匹配器(BruteForceMatcher)
BFMatcher matcher(NORM_HAMMING); //汉明距离匹配特征点
//定义特征点匹配向量
vector matches;
matcher.match(descriptorMat1, descriptorMat2, matches);
//画图
Mat image;
drawMatches(srcImage1, keyPoints1, srcImage2, keyPoints2, matches, image);
imshow("BRIEF特征描述子出来的匹配结果", image);
定义特征提取器,采用了SURF的特征描述子
//SurfDescriptorExtractor extractor1;// //参数表示字节数,采用长度为64×8=512的向量表示
声明存放特征向量的特征描述矩阵
//Mat descriptorMat1_1, descriptorMat2_1;
计算SURF特征描述矩阵(计算特征向量)
//extractor1.compute(grayImage1, keyPoints1, descriptorMat1_1);
//extractor1.compute(grayImage2, keyPoints2, descriptorMat2_1);
用匹配器进行匹配。之前介绍过FLANN匹配器和暴力匹配器(BruteForceMatcher)
//BFMatcher matcher1; //汉明距离匹配特征点
//
定义特征点匹配向量
//vector matches1;
//matcher1.match(descriptorMat1_1, descriptorMat2_1, matches1);
画图
//Mat image1;
//drawMatches(srcImage1, keyPoints1, srcImage2, keyPoints2, matches1, image1);
//imshow("采用了SURF的特征描述子出来的匹配结果", image1);
waitKey(0);
return 0;
}
结果如下图所示:
Mat src2 = imread("hand2.jpg", 1);
ORB_feature_and_compare(src1, src2);
return 0;
}
结果如图所示:
匹配的效果不是特别的好~~~至于原因我也没有去深究,如果读者有什么想法,可以在评论一下赐教(会不会是匹配的速度快,对应准确率就没那么高呢?)
总结:三种算法其实非常相似,在opencv中表现只是特征提取的函数以及特征描述的函数不一样而已。
三种算法用于识别两张图的目标是否相同,总结基本流程总结如下:
1、分别找出这两张图中的特征点。通过特征检测器进行特征检测,检测的结果放于KeyPoint类型的vector中。
2、描述这些特征点的属性。特征的描述也叫特征的提取,就是第一步获得的仅仅是一系列特征点,第二步就要生成特征向量,用特征提取器获得描述子,并放于特征描述矩阵中。
3、比较这两张图片的特征点的属性,如果有足够多的特征点具有相同的属性,那么可以认为这两张图片中的目标是相同的。通过匹配器进行特征匹配(匹配器分为FLANN和暴力匹配),匹配结果放于DMatch类型的vector中。
至此,本人学习SIFT、SURF和ORB三种特征算子的旅途就告一段落了,希望本博客能给有需要的人带来帮助,同时也欢迎广大读者批评赐教。感觉本人对于这三种算子的理解目前还是处于纸上谈兵的状态,希望能与大家多交流,后续我也会自己做一些项目,有新的心得体会会及时更新这篇博客。同时这篇博客理论部分很多都是参考其他博客与书籍,适当的地方都给出了引用,本博客无商业用途,仅仅作为本人的学习笔记~谢谢。
Reference:(除了在上面分别指出的博客链接外,本博文还参考了以下资料) 《OpenCV图像处理编程实例》
《Opencv3编程入门》