OpenCV
开源计算机视觉库
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
使用FLANN进行特征匹配

上一教程: 特征描述
下一教程: Features2D + 单应性查找已知物体

原作者Ana Huamán
兼容性OpenCV >= 3.0

目标

在本教程中,您将学习如何:

警告
您需要 OpenCV contrib 模块 才能使用 SURF 特征(替代方案是 ORB、KAZE 等特征)。

理论

经典的特征描述符(SIFT、SURF 等)通常使用欧几里得距离(或 L2 范数)进行比较和匹配。由于 SIFT 和 SURF 描述符表示邻域中定向梯度的直方图(对于 SURF 来说是 Haar 小波响应),因此欧几里得距离的替代方案是基于直方图的度量(χ2、Earth Mover’s Distance (EMD) 等)。

Arandjelovic 等人在 [13] 中提出扩展到 RootSIFT 描述符

使用平方根(Hellinger)核代替标准欧几里得距离来测量 SIFT 描述符之间的相似性,这会导致管道所有阶段的性能大幅提升。

二进制描述符(ORB、BRISK 等)使用 汉明距离 进行匹配。此距离等效于计算二进制字符串中不同元素的数量(应用 XOR 运算后的总体计数)

dhamming(a,b)=i=0n1(aibi)

为了过滤匹配项,Lowe 在 [173] 中提出使用距离比率测试来尝试消除错误匹配。计算考虑的关键点的两个最近匹配之间的距离比率,当此值低于阈值时,它是一个良好的匹配。实际上,该比率有助于区分模糊匹配(两个最近邻之间的距离比率接近于 1)和良好区分的匹配。下图来自 SIFT 论文,它说明了基于最近邻距离比率测试的匹配正确的概率。

替代或附加的过滤测试是

  • 交叉检验测试(如果特征 fbIbfa 的最佳匹配,而特征 faIafb 的最佳匹配,则为良好匹配 (fa,fb)
  • 几何测试(消除不符合几何模型的匹配,例如,用于平面物体的 RANSAC 或鲁棒单应性)

代码

本教程代码如下所示。您也可以从 此处 下载。

#include <iostream>
#include "opencv2/core.hpp"
#ifdef HAVE_OPENCV_XFEATURES2D
using namespace cv;
using namespace cv::xfeatures2d;
using std::cout;
using std::endl;
const char* keys =
"{ help h | | 打印帮助信息。 }"
"{ input1 | box.png | 输入图像1的路径。 }"
"{ input2 | box_in_scene.png | 输入图像2的路径。 }";
int main( int argc, char* argv[] )
{
CommandLineParser parser( argc, argv, keys );
Mat img1 = imread( samples::findFile( parser.get<String>("input1") ), IMREAD_GRAYSCALE );
Mat img2 = imread( samples::findFile( parser.get<String>("input2") ), IMREAD_GRAYSCALE );
if ( img1.empty() || img2.empty() )
{
cout << "无法打开或找到图像!\n" << endl;
parser.printMessage();
return -1;
}
//-- 步骤1:使用SURF检测器检测关键点,计算描述符
int minHessian = 400;
Ptr<SURF> detector = SURF::create( minHessian );
std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;
detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 );
detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 );
//-- 步骤2:使用基于FLANN的匹配器匹配描述符向量
// 由于SURF是浮点描述符,因此使用NORM_L2
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);
std::vector< std::vector<DMatch> > knn_matches;
matcher->knnMatch( descriptors1, descriptors2, knn_matches, 2 );
//-- 使用Lowe比率测试过滤匹配项
const float ratio_thresh = 0.7f;
std::vector<DMatch> good_matches;
for (size_t i = 0; i < knn_matches.size(); i++)
{
if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
{
good_matches.push_back(knn_matches[i][0]);
}
}
//-- 绘制匹配项
Mat img_matches;
drawMatches( img1, keypoints1, img2, keypoints2, good_matches, img_matches, Scalar::all(-1),
Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
//-- 显示检测到的匹配项
imshow("良好匹配", img_matches );
waitKey();
return 0;
}
#else
int main()
{
std::cout << "此教程代码需要运行 xfeatures2d contrib 模块。" << std::endl;
return 0;
}
#endif
用于命令行解析。
定义 utility.hpp:890
n 维密集数组类
定义 mat.hpp:829
bool empty() const
如果数组没有元素,则返回 true。
std::string String
定义 cvstd.hpp:151
std::shared_ptr< _Tp > Ptr
定义 cvstd_wrapper.hpp:23
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
定义 xfeatures2d.hpp:67
定义 core.hpp:107

说明

结果

  • 以下是使用距离比率测试的SURF特征匹配结果