图像处理之使用FLANN进行特征点匹配

467 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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;
}

原图

1.jpg

2.jpg

效果图

3.PNG

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天