【OpenCV基础】第四十二课:特征匹配

BFMatcher,FLANN

Posted by x-jeff on January 6, 2024

本文为原创文章,未经本人允许,禁止转载。转载请注明出处。

1.特征匹配

我们以SURF特征为例,在【OpenCV基础】第三十九课:SURF特征检测中,我们仅仅展示了SURF对特征点的检测结果。通常在检测完特征点后,我们还要计算每个点的特征描述子,然后通过特征描述子对两幅图像进行匹配。匹配的方式有很多,本文介绍两种常见的特征匹配方法。

2.Brute-force matcher

即暴力匹配,对于第一幅图中的某个特征描述子,遍历第二幅图中的所有特征描述子,找到最接近的那个。

首先,我们构建SURF特征检测器

1
2
int minHessian = 400;
Ptr<SURF> detector = SURF::create(minHessian);

分别计算两张图的特征点及特征描述子:

1
2
3
4
5
vector<KeyPoint> keypoints_1;
vector<KeyPoint> keypoints_2;
Mat descriptor_1, descriptor_2;
detector->detectAndCompute(img1, Mat(), keypoints_1, descriptor_1); //第二个参数Mat()为mask,下同
detector->detectAndCompute(img2, Mat(), keypoints_2, descriptor_2);

假设img1一共检测到786个特征点,每个特征点的特征描述子维度为64,因此求得的descriptor_1维度为$786 \times 64$,即每一行对应一个特征点。然后调用BFMatcher实现暴力匹配:

1
2
3
BFMatcher matcher(NORM_L2);
vector<DMatch> matches;
matcher.match(descriptor_1, descriptor_2, matches);

其中BFMatcher的创建方法为:

1
CV_WRAP BFMatcher( int normType=NORM_L2, bool crossCheck=false );
  1. int normType为距离计算方式,可设为NORM_L1NORM_L2NORM_HAMMINGNORM_HAMMING2
    • SIFTSURF通常用NORM_L1NORM_L2
    • ORB、BRISK、BRIEF通常用NORM_HAMMING
    • ORB(WTA_K==3 or 4)通常用NORM_HAMMING2
  2. bool crossCheck
    • 为false时,是单向匹配,假设有第一幅图的特征描述子A,计算其与第二幅图所有特征描述子之间的距离,假设其到特征描述子B的距离最近,则视为(A,B)匹配成功。
    • 为true时,是双向匹配,假设有第一幅图的特征描述子A,计算其与第二幅图所有特征描述子之间的距离,假设其到特征描述子B的距离最近,与此同时,对于第一幅图的所有特征描述子,B到A的距离也是最近的,此时才视为(A,B)匹配成功。

match函数:

1
2
3
4
5
CV_WRAP void match( 
	InputArray queryDescriptors, 
	InputArray trainDescriptors,
	CV_OUT std::vector<DMatch>& matches, 
	InputArray mask=noArray() ) const;

queryDescriptorstrainDescriptors为两幅图的特征描述子,matches为匹配的结果,mask为mask。

上图是matches结果的一个示例,一共有786个匹配结果,每个匹配结果里存储了匹配成功的两个特征描述子各自的索引以及这两个描述子之间的距离。

最后,我们可以对匹配结果进行可视化:

1
2
Mat matchesImg;
drawMatches(img1, keypoints_1, img2, keypoints_2, matches, matchesImg);

3.Flann-based descriptor matcher

FLANN(http://www.cs.ubc.ca/research/flann/)全称是Fast Library for Approximate Nearest Neighbors,从名字可以看出,FLANN是一个用于在高维空间中执行快速最近邻搜索的库。其包含一组最适合最近邻搜索的算法,并且FLANN会根据数据集自动选择最佳算法和最佳参数。在面对大数据集时,它的效果要优于BFMatcher

核心调用代码:

1
2
3
FlannBasedMatcher matcher;
vector<DMatch> matches;
matcher.match(descriptor_obj, descriptor_scene, matches);

FlannBasedMatcher的创建:

1
2
CV_WRAP FlannBasedMatcher( const Ptr<flann::IndexParams>& indexParams=makePtr<flann::KDTreeIndexParams>(),
                       const Ptr<flann::SearchParams>& searchParams=makePtr<flann::SearchParams>() );

第一个参数indexParams用于指定搜索算法,第二个参数searchParams用于指定递归遍历的次数,次数越多,精度越高,消耗的时间也越多。结果可视化:

4.代码地址

  1. 特征匹配

5.参考资料

  1. Feature Matching