opencv之SURF特征点提取及匹配
2017-04-18 18:54
591 查看
1.概述
在基于特征匹配的方法中,Surf算法对物体的旋转、光照等情况有较好的鲁棒性,且教SIFT算法而言计算速度更快。通过Surf算法检测到的特征点其描述符包含了这个点的位置和尺度信息,故对两幅图片进行匹配时可以通过两幅图中特征点匹配对进行匹配。即使物体位置和光照的改变也能够有良好的匹配效果。在本片文章中将使用SurfDescriptorExtractor及其函数compute来完成特定计算,使用BruteForceMatcher匹配得到的特征向量,使用函数drawMatches来绘制检测到的匹配点。
2.OpenCV API
2.1 SurfDescriptorExtractor
Surf用来封装的用于计算特征描述子的类,其定义如下typedef SURF cv::xfeatures2d::SurfDescriptorExtractor
是SURF的一个重定义,也是类xfeatures2d的一个成员函数,其类定义如下:
class SurfDescriptorExtractor : public DescriptorExtractor
{
public:
SurfDescriptorExtractor( int nOctaves=4,
int nOctaveLayers=2, bool extended=false );
virtual void read (const FileNode &fn); virtual void write (FileStorage &fs) const; virtual int descriptorSize() const; virtual int descriptorType() const; protected: ... }
我们可以看出SurfDescriptorExtractor是从DescriptorExtractor公有继承而来。
2.2BruteForceMatcher
暴力搜索特征点匹配。对于第一集合中的特征描述子,这个匹配寻找在第二个集合中最相近的特征描述子,这种特征描述子匹配支持masking permissible特征描述子集合匹配,其定义如下:template<class Distance> class BruteForceMatcher : public DescriptorMatcher { public: BruteForceMatcher( Distance d = Distance() ); virtual ~BruteForceMatcher(); virtual bool isMaskSupported() const; virtual Ptr<DescriptorMatcher> clone( bool emptyTrainData=false ) const; protected: ... }
从DescriptorMatcher公有继承,同时又有本身的虚成员函数。
2.3 drawMatches
给定两幅图像,绘制寻找到的特征关键点及其匹配。有两种定义形式如下:void cv::drawMatches ( InputArray img1, const std::vector< KeyPoint > & keypoints1, InputArray img2, const std::vector< KeyPoint > & keypoints2, const std::vector< DMatch > & matches1to2, InputOutputArray outImg, const Scalar & matchColor = Scalar::all(-1), const Scalar & singlePointColor = Scalar::all(-1), const std::vector< char > & matchesMask = std::vector< char >(), int flags = DrawMatchesFlags::DEFAULT ) void cv::drawMatches ( InputArray img1, const std::vector< KeyPoint > & keypoints1, InputArray img2, const std::vector< KeyPoint > & keypoints2, const std::vector< std::vector< DMatch > > & matches1to2, InputOutputArray outImg, const Scalar & matchColor = Scalar::all(-1), const Scalar & singlePointColor = Scalar::all(-1), const std::vector< std::vector< char > > & matchesMask = std::vector< std::vector< char > >(), int flags = DrawMatchesFlags::DEFAULT )
两个函数的区别仅在于DMatch数据接收的数据类型不同
img1:接收的第一幅源图像
**keypoints1:**KeyPoint类型的关键点(特征点),由第一幅图像检测得到
img2:接收的第二幅图像
**keypoints2:**KeyPoint类型的关键点(特征点),由第二幅图像检测得到
**matches1to2:**DMatch类型矢量,表示从第一幅到第二幅图像的匹配点,表示每一个图1中的特征点豆在图2中有一一对应的点
outImg:完成匹配后的输出图像,其内容取决于输出图像的标志位flags
matchColor:两个匹配点进行绘制匹配的颜色,即线和点的颜色,有默认值表示颜色随机生成
singlePointColor:对于没有匹配对的特征点绘制颜色
matchesMask:确定哪些是会绘制出来的掩膜,如果掩膜为空,表示所有匹配都进行绘制
flags:绘制匹配的标志位,有默认值DrawMatchesFlags::DEFAULT。其可选项如下:
enum { DEFAULT = 0, //创建输出图像矩阵,对每一个特征点都只绘制其中间部分。 DRAW_OVER_OUTIMG = 1, //不创建输出图像矩阵,而是在输出图像上绘制匹配对 NOT_DRAW_SINGLE_POINTS = 2, //单点特征点不被绘制 DRAW_RICH_KEYPOINTS = 4 //对每个特征点,绘制带大小和方向的关键点图像 }
3.示例代码:
#include <iostream> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\nonfree\features2d.hpp> using namespace std; using namespace cv; int main() { Mat srcImage1 = imread("Surf_description_1.jpg",IMREAD_GRAYSCALE); Mat srcImage2 = imread("Surf_description_2.jpg",IMREAD_GRAYSCALE); //判断文件是否读取成功 if (srcImage1.empty() || srcImage2.empty()) { cout << "图像加载失败!"; return -1; } else cout << "图像加载成功..." << endl << endl; //检测两幅图像中的特征点 int minHessian = 2000; //定义Hessian矩阵阈值 SurfFeatureDetector detector(minHessian); //定义Surf检测器 vector<KeyPoint>keypoint1, keypoint2; //定义两个KeyPoint类型矢量存储检测到的特征点 detector.detect(srcImage1, keypoint1); detector.detect(srcImage2, keypoint2); //计算特征向量的描述子 SurfDescriptorExtractor descriptorExtractor; Mat descriptors1, descriptors2; descriptorExtractor.compute(srcImage1, keypoint1, descriptors1); descriptorExtractor.compute(srcImage2, keypoint2, descriptors2); //使用BruteForceMatcher进行描述符匹配 BFMatcher matcher(NORM_L2); vector<DMatch>matches; matcher.match(descriptors1, descriptors2, matches); //绘制匹配特征点 Mat matchImage; drawMatches(srcImage1, keypoint1, srcImage2, keypoint2, matches, matchImage); //显示匹配的图像 namedWindow("Match", WINDOW_AUTOSIZE); imshow("Match", matchImage); waitKey(0); return 0; }
运行结果
4.使用FLANN匹配
最近邻搜索的问题在图像识别、数据压缩、模式识别和分类、机器学习、文档检索系统、统计和数据分析等方面是一个重大问题,在高维空间中解决这个问题似乎是一个非常难以执行的任务,没有算法明显优于标准的蛮力搜索,因此越来越多的人把兴趣点转向执行最近邻搜索的一类算法。FLANN(Fast Library for Approximate Nearest Neighbors)时目前最完整的近似近邻开源库,不但实现了一系列查找算法还包含了一种自动选取最快算法的机制。
opencv中提供了函数FlannBasedMatcher()函数,其继承关系如下图所示:
4.1示例代码
#include <iostream> #include <stdio.h> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\nonfree\features2d.hpp> using namespace std; using namespace cv; int main() { Mat srcImage1 = imread("Surf_description_1.jpg"); Mat srcImage2 = imread("Surf_description_2.jpg"); //判断文件是否加载成功 if (srcImage1.empty() || srcImage2.empty()) { cout << "图像加载失败"; return -1; } else cout << "图像加载成功..." << endl << endl; //检测特征点 int minHessian = 700; SurfFeatureDetector detector(minHessian); vector<KeyPoint>keypoints1, keypoints2; detector.detect(srcImage1, keypoints1); detector.detect(srcImage2, keypoints2); //计算特征点描述子 SurfDescriptorExtractor extractor; Mat descriptor1, descriptor2; extractor.compute(srcImage1, keypoints1, descriptor1); extractor.compute(srcImage2, keypoints2, descriptor2); //使用FLANN进行匹配 FlannBasedMatcher matcher; vector<DMatch>matches; matcher.match(descriptor1, descriptor2, matches); double max_dist = 0; double min_dist = 100; for (int i = 0; i < descriptor1.rows; i++) { double dist = matches[i].distance; if (dist < min_dist)min_dist = dist; if (dist > max_dist)max_dist = dist; } cout << "Max dist: " << max_dist << endl; cout << "Min dist: " << min_dist << endl << endl; //绘制好的匹配点即匹配点距离小于2*min_dst vector<DMatch>good_matches; for (int i = 0; i < descriptor1.rows; i++) { if (matches[i].distance<=max(2*min_dist,0.02)) { good_matches.push_back(matches[i]); } } Mat matchImage; drawMatches(srcImage1, keypoints1, srcImage2, keypoints2, good_matches, matchImage, Scalar::all(-1), Scalar(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); namedWindow("匹配图", WINDOW_AUTOSIZE); imshow("匹配图", matchImage); //输出好的特征点匹配对 for (int i = 0; i < (int)good_matches.size(); i++) { //queryIdx为query描述子的索引,match函数中前面数集的索引 //trainIdx为train描述子的索引,match函数中后面数集的索引 printf("--Good Match [%d] Keypoint 1: %d -- Keypoint 2: %d \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx); } waitKey(0); return 0; }
运行结果如下所示:
相关文章推荐
- 使用OpenCV3进行SURF特征提取和暴力匹配代码详解
- OpenCV中feature2D学习——FAST特征点检测与SIFT/SURF/BRIEF特征提取与匹配
- python opencv-3.0 SIFT/SURF 特征提取与匹配
- OpenCV中feature2D学习——SIFT和SURF算子实现特征点提取与匹配
- OpenCV中feature2D学习——SIFT和SURF算子实现特征点提取与匹配
- OpenCV中特征点提取和匹配的通用方法
- opencv surf特征点匹配拼接源码
- OpenCV_局部图像特征的提取与匹配_源代码
- opencv3.1 surf特征匹配
- OpenCV中特征检测,提取与匹配使用方法学习
- 利用opencv提取Hu不变量特征 形状匹配 机器学习识别手写数字 傅里叶变换
- opencv3.2 SURF实现特征点匹配
- OpenCV中特征检测,提取与匹配使用方法学习
- Opencv 各种特征点提取和匹配
- OpenCV中特征点提取和匹配的通用方法
- OpenCV中提取SIFT特征点、图像匹配、图像配准
- 利用opencv提取Hu不变量特征 形状匹配
- OpenCV Using Python——基于SURF特征提取和金字塔LK光流法的单目视觉三维重建
- SURF特征提取与匹配实践
- OpenCV中特征点提取和匹配的通用方法