您的位置:首页 > 运维架构

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;
}


运行结果如下所示:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息