本文为原创文章,未经本人允许,禁止转载。转载请注明出处。
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 );
int normType
为距离计算方式,可设为NORM_L1
、NORM_L2
、NORM_HAMMING
、NORM_HAMMING2
。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;
queryDescriptors
和trainDescriptors
为两幅图的特征描述子,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
用于指定递归遍历的次数,次数越多,精度越高,消耗的时间也越多。结果可视化: