开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天
1 概述
本节,我们将学习如何使用FlannBasedMatcher 接口以及函数 FLANN(),实现快速高效匹配(快速最近邻逼近搜索函数库,Fast Library for Approximate Nearest Neighbors,FLANN)。
2 FlannBasedMatcher类的简单分析
在OpenCV源代码中,找到FlannBasedMatcher类的脉络如下。
class CV_EXPORTS_W FlannBasedMatcher: public DescriptorMatcher
{
//......
};
可以发现FlannBasedMatcher类也是继承自 DescriptorMatcher,并且同样主要使用来自 DescriptorMatcher 类的match方法进行匹配。下面,让我们讲解一下此类方法的用法。
3 找到最佳匹配:DescriptorMatcher::match方法
DescriptorMatcher::match()函数从每个描述符查询集中找到最佳匹配,有两个版本的源码,下面用注释对其进行讲解。
C++:
void DescriptorMatcher::match(
const Mat& queryDescriptors,//查询描述符集
const Mat& trainDescriptors,//训练描述符集
vector<DMatch>&matches,//得到的匹配。若查询描述符有在掩膜中被标记出来,则没有匹配添加到描述符中。则匹配量可能会比查询描述符数量少
const Mat& mask=Mat()//指定输入查询和训练描述符允许匹配的掩膜
)
C++:void DescriptorMatcher::match(
const Mat& queryDescriptors,//查询描述符集
vector<DMatch>& matches,//得到的匹配。若查询描述符有在掩膜中被标记出来,则没有匹配添加到描述符中,则匹配量可能会比查询描述符数量少
const vector<Mat>& masks=vector<Mat>()//一组掩膜,每个masks[i]从第1 个图像trainDescCollection[i]指定输入查询和训练描述符允许匹配的掩膜
)
4 示例程序:使用FLANN进行特征点匹配
本节我们将演示如何使用FLANN进行特征点匹配,详细注释的示例程序代码如下。
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//----------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>
using namespace cv;
using namespace std;
//--------------------------------------【main( )函数】-----------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//--------------------------------------------------------------------------------------------
int main( )
{
//【0】改变console字体颜色
system("color 6F");
//【1】载入图像、显示并转化为灰度图
Mat trainImage = imread("1.jpg"), trainImage_gray;
imshow("原始图",trainImage);
cvtColor(trainImage, trainImage_gray, CV_BGR2GRAY);
//【2】检测Surf关键点、提取训练图像描述符
vector<KeyPoint> train_keyPoint;
Mat trainDescriptor;
SurfFeatureDetector featureDetector(80);
featureDetector.detect(trainImage_gray, train_keyPoint);
SurfDescriptorExtractor featureExtractor;
featureExtractor.compute(trainImage_gray, train_keyPoint, trainDescriptor);
//【3】创建基于FLANN的描述符匹配对象
FlannBasedMatcher matcher;
vector<Mat> train_desc_collection(1, trainDescriptor);
matcher.add(train_desc_collection);
matcher.train();
//【4】创建视频对象、定义帧率
VideoCapture cap(0);
unsigned int frameCount = 0;//帧数
//【5】不断循环,直到q键被按下
while(char(waitKey(1)) != 'q')
{
//<1>参数设置
int64 time0 = getTickCount();
Mat testImage, testImage_gray;
cap >> testImage;//采集视频到testImage中
if(testImage.empty())
continue;
//<2>转化图像到灰度
cvtColor(testImage, testImage_gray, CV_BGR2GRAY);
//<3>检测S关键点、提取测试图像描述符
vector<KeyPoint> test_keyPoint;
Mat testDescriptor;
featureDetector.detect(testImage_gray, test_keyPoint);
featureExtractor.compute(testImage_gray, test_keyPoint, testDescriptor);
//<4>匹配训练和测试描述符
vector<vector<DMatch> > matches;
matcher.knnMatch(testDescriptor, matches, 2);
// <5>根据劳氏算法(Lowe's algorithm),得到优秀的匹配点
vector<DMatch> goodMatches;
for(unsigned int i = 0; i < matches.size(); i++)
{
if(matches[i][0].distance < 0.6 * matches[i][1].distance)
goodMatches.push_back(matches[i][0]);
}
//<6>绘制匹配点并显示窗口
Mat dstImage;
drawMatches(testImage, test_keyPoint, trainImage, train_keyPoint, goodMatches, dstImage);
imshow("匹配窗口", dstImage);
//<7>输出帧率信息
cout << "当前帧率为:" << getTickFrequency() / (getTickCount() - time0) << endl;
}
return 0;
}
原图
效果图