【Compute Vision】学习OpenCV——KeyPoint Matching 优化方式

本文介绍了OpenCV中特征点匹配的优化方法,包括Brute-force和Flann-based两种匹配方式的特点及应用场景,如何通过Cross-match filter和Ratio test减少错误匹配,以及如何利用RANSAC方法进行单应性变换以提高匹配精度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载之:http://blog.youkuaiyun.com/yangtrees/article/details/19928191

今天读Mastering OpenCV with Practical Computer Vision Projects 中的第三章里面讲到了几种特征点匹配的优化方式,在此记录。

在图像特征点检测完成后(特征点检测参考:学习OpenCV——BOW特征提取函数(特征点篇)),就会进入Matching  procedure。



1. OpenCV提供了两种Matching方式

• Brute-force matcher (cv::BFMatcher) 

• Flann-based matcher (cv::FlannBasedMatcher)

Brute-force matcher就是用暴力方法找到点集一中每个descriptor在点集二中距离最近的descriptor;

Flann-based matcher 使用快速近似最近邻搜索算法寻找(用快速的第三方库近似最近邻搜索算法)

一般把点集一称为 train set (训练集)对应模板图像,点集二称为 query set(查询集)对应查找模板图的目标图像。

为了提高检测速度,你可以调用matching函数前,先训练一个matcher。训练过程可以首先使用cv::FlannBasedMatcher来优化,为descriptor建立索引树,这种操作将在匹配大量数据时发挥巨大作用(比如在上百幅图像的数据集中查找匹配图像)。而Brute-force matcher在这个过程并不进行操作,它只是将train descriptors保存在内存中。



2. 在matching过程可以使用cv::DescriptorMatcher的如下功能来进行匹配:

  • 简单查找最优匹配:void match(const Mat& queryDescriptors, vector<DMatch>& matches,const vector<Mat>& masks=vector<Mat>() );
  • 为每个descriptor查找K-nearest-matches:void knnMatch(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,const vector<Mat>&masks=vector<Mat>(),bool compactResult=false );
  • 查找那些descriptors间距离小于特定距离的匹配:void radiusMatch(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, maxDistance, const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );

3. matching结果包含许多错误匹配,错误的匹配分为两种:

  • False-positive matches: 将非对应特征点检测为匹配(我们可以对他做文章,尽量消除它)
  • False-negative matches: 未将匹配的特征点检测出来(无法处理,因为matching算法拒绝)
为了消除False-positive matches采用如下两种方式:
  • Cross-match filter:
在OpenCV中 cv::BFMatcher class已经支持交叉验证,建立  cv::BFMatcher将第二参数声明为true
cv::Ptr<cv::DescriptorMatcher> matcher(new cv::BFMatcher(cv::NORM_HAMMING,true));
经过 Cross-match filter的结果:

  • Ratio test
使用KNN-matching算法,令K=2。则每个match得到两个最接近的descriptor,然后计算最接近距离和次接近距离之间的比值,当比值大于既定值时,才作为最终match。
void PatternDetector::getMatches(const cv::Mat& queryDescriptors,  std::vector<cv::DMatch>& matches)
{
	matches.clear();
	if (enableRatioTest)
	{
		// To avoid NaNs when best match has 
		// zero distance we will use inverse ratio. 
		const float minRatio = 1.f / 1.5f;
		// KNN match will return 2 nearest 
		// matches for each query descriptor
		m_matcher->knnMatch(queryDescriptors, m_knnMatches, 2);
		for (size_t i=0; i<m_knnMatches.size(); i++)
		{
			const cv::DMatch& bestMatch = m_knnMatches[i][0];
			const cv::DMatch& betterMatch = m_knnMatches[i][1];
			float distanceRatio = bestMatch.distance / 
				betterMatch.distance;
			// Pass only matches where distance ratio between 
			// nearest matches is greater than 1.5 
			// (distinct criteria)
			if (distanceRatio < minRatio)
			{
				matches.push_back(bestMatch);
			}
		}
	}
	else
	{
		// Perform regular match
		m_matcher->match(queryDescriptors, matches);
	}
} 



4. Homography estimation

为了进一步提升匹配精度,可以采用随机样本一致性(RANSAC)方法。
因为我们是使用一幅图像(一个平面物体),我们可以将它定义为刚性的,可以在pattern image和query image的特征点之间找到单应性变换(homography transformation )。使用cv::findHomography找到这个单应性变换,使用RANSAC找到最佳单应性矩阵。(由于这个函数使用的特征点同时包含正确和错误匹配点,因此计算的单应性矩阵依赖于二次投影的准确性)
bool PatternDetector::refineMatchesWithHomography
(
const std::vector<cv::KeyPoint>& queryKeypoints,
const std::vector<cv::KeyPoint>& trainKeypoints, 
float reprojectionThreshold,
std::vector<cv::DMatch>& matches,
cv::Mat& homography
)
{
const int minNumberMatchesAllowed = 8;
if (matches.size() < minNumberMatchesAllowed)
return false;
// Prepare data for cv::findHomography
std::vector<cv::Point2f> srcPoints(matches.size());
std::vector<cv::Point2f> dstPoints(matches.size());
for (size_t i = 0; i < matches.size(); i++)
{
srcPoints[i] = trainKeypoints[matches[i].trainIdx].pt;
dstPoints[i] = queryKeypoints[matches[i].queryIdx].pt;
}
// Find homography matrix and get inliers mask
std::vector<unsigned char> inliersMask(srcPoints.size());
homography = cv::findHomography(srcPoints, 
dstPoints, 
CV_FM_RANSAC, 
reprojectionThreshold, 
inliersMask);
std::vector<cv::DMatch> inliers;
for (size_t i=0; i<inliersMask.size(); i++)
{
if (inliersMask[i])
inliers.push_back(matches[i]);
}
matches.swap(inliers);
return matches.size() > minNumberMatchesAllowed;
} 


经过单应性变换的过滤结果

The ratio test in OpenCV is a method for filtering out false matches in feature matching algorithms. In feature matching, keypoints are detected in two images and their descriptors are calculated. These descriptors are then matched to find corresponding keypoints in both images. However, some matches may be incorrect due to noise, occlusion, or other factors. The ratio test helps filter out these incorrect matches. It works by comparing the distance between the best and second-best matches for each keypoint. If the distance ratio is below a certain threshold, the match is considered correct. If the ratio is above the threshold, the match is discarded. The threshold value is typically set to 0.7, meaning that the distance to the second-best match should be at least 1.4 times greater than the distance to the best match. Here's an example code snippet for implementing the ratio test in OpenCV: ``` # detect keypoints and compute descriptors for both images kp1, des1 = detector.compute(img1, None) kp2, des2 = detector.compute(img2, None) # match descriptors using a matcher object matches = matcher.knnMatch(des1, des2, k=2) # apply ratio test to filter out false matches good_matches = [] for m, n in matches: if m.distance < 0.7 * n.distance: good_matches.append(m) ``` In this code, `detector` is a keypoint detector object and `matcher` is a descriptor matcher object. The `knnMatch` function returns a list of the two best matches for each descriptor in the first image (`des1`) with descriptors in the second image (`des2`). The ratio test is then applied to filter out false matches, and the remaining matches are stored in `good_matches`.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值