尽管暴力匹配原理简单,但是算法的复杂度高,当遇到特征点数目比较大时,会大大影响程序运行时间,所以我们今天介绍快速最近邻搜寻库(Fast Libray for Approximate Nearest Neighbors,FLANN)用于实现特征点的高效匹配。
FLANN被集成在FlannBasedMatcher类中,此类也继承了DescriptorMatcher类,因此可以使用DescriptorMatcher类中相关函数实现特征点匹配。FlannBasedMatcher类重载函数原型如下:
cv::FlannBasedMatcher::FlannBasedMatcher(
const Ptr'<'flann::IndexParams'>'& indexParams=makePtr'<'flann::KDTreeIndexParams'>'(),
const Ptr'<'flann::SearchParams'>'& searchParams=makePtr'<'flann::SearchParams'>'()
)
indexParams:匹配时需要使用的搜索算法标志,可以选择的标志及其含义如下表。
searchParams:递归遍历的次数,遍历次数越多越准确,但是耗时更长。
标志参数 | 含义 |
---|---|
makePtr’<‘flann::KDTreeIndexParams’>'() | 采集随机k-d树寻找匹配点 |
makePtr’<‘flann::KMeansIndexParams’>'() | 采用K-means树寻找匹配点 |
makePtr’<‘flann::HierachicalClusteringIndexParams’>'() | 采用层次聚类树寻找匹配点 |
该函数能够初始化FlannBasedMatcher类变量,以便于后续的特征带你匹配任务。此函数有两个参数,都具有默认值,第一个参数是匹配时需要使用的搜寻算法标志,可以选择的的标志和其含义在上表已经给出,默认值为随机k_d树寻找匹配点,一般情况下使用默认值即可。第二个参数是递归遍历的次数,与迭代终止条件相同,迭代遍历次数终止条件也是通过函数进行定义,该参数用flann::SearchParams()函数实现,该函数具有3个含有默认值的参数,分别是遍历次数(int),误差(float)和是否排序(bool),一般情况下使用默认参数即可。
FLANN匹配与暴力匹配方式类似,两者都需要根据特征点对描述子之间的距离进行排序和筛选。但是需要注意将BFMatcher类改成FlannBasedMatcher类。其他需要注意使用FLANN方法进行匹配时描述子需要是CV_32F类型,因此ORB特征点的描述子变量需要进行类型转换后才可以实现特征点匹配。代码案例如下:
#include<opencv2\opencv.hpp>
#include<iostream>
#include<vector>
#include<xfeatures2d.hpp>
using namespace cv;
using namespace std;
using namespace xfeatures2d;
void orb_features(Mat &gray, vector<KeyPoint>&keypoints, Mat &descriptions) {
Ptr<ORB>orb = ORB::create(1000, 1.2f);
orb->detect(gray, keypoints);
orb->compute(gray, keypoints, descriptions);
}
int main() {
Mat img1, img2;
img1 = imread("D:/样本/12.png");
img2 = imread("D:/样本/14.png");
if (!(img1.data&&img2.dataend)) {
cout << "读取图像错误,请确认图像文件名称是否正确" << endl;
return -1;
}
//提取ORB特征点
vector<KeyPoint>Keypoints1, Keypoints2;
Mat descriptions1, descriptions2;
//计算ORB特征点
orb_features(img1, Keypoints1, descriptions1);
orb_features(img2, Keypoints2, descriptions2);
//判断描述子数据类型,如果数据类型不符,那么需要类型转换。主要针对ORB特征点
if ((descriptions1.type() != CV_32F) && (descriptions2.type() != CV_32F)) {
descriptions1.convertTo(descriptions1, CV_32F);
descriptions2.convertTo(descriptions2, CV_32F);
}
//特征点匹配
vector<DMatch>matches;//定义存放匹配结果的变量
FlannBasedMatcher matcher;//使用默认值
matcher.match(descriptions1, descriptions2, matches);
cout << "matches=" << matches.size() << endl;//匹配成功特征点数目
//寻找距离最大值和最小值,如果是ORB特征点,那么min_dist取值需要大一些
double max_dist = 0;
double min_dist = 0;
for (int i = 0; i < descriptions1.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;
//将最大值距离的0.4倍作为最优匹配结果进行筛选
std::vector<DMatch>good_matches;
for (int i = 0; i < descriptions1.rows; i++) {
if (matches[i].distance < 0.04*max_dist) {
good_matches.push_back(matches[i]);
}
}
//匹配成功特征点数目
cout << "good_matches=" << good_matches.size() << endl;
Mat outimg, outimg1;
//绘制匹配结果
drawMatches(img1, Keypoints1, img2, Keypoints2, matches, outimg);
drawMatches(img1, Keypoints1, img2, Keypoints2, good_matches, outimg1);
imshow("未筛选结果", outimg);
imshow("筛选结果", outimg1);
waitKey(0);
return 0;
}