特征检测与匹配,在物体检测,视觉跟踪,三维重建等领域都有广泛的应用。所以学习features2d、xfeatures2d中函数的使用,很有必要。

1、得到特征点与特征点描述(SIFT SURF ORB  AKAZE)

(1)SIFT

#include <opencv2\xfeatures2d\nonfree.hpp>
vector<KeyPoint> key_points;
Mat descriptor;
Ptr<Feature2D> sift = xfeatures2d::SIFT::create(0, 3, 0.04, 10);
sift->detectAndCompute(image, noArray(), key_points, descriptor);

(2)SURF(可以认为是尺度不变特征变换sift的加速版)

#include <opencv2\xfeatures2d\nonfree.hpp>
Ptr<Feature2D> surf = xfeatures2d::SURF::create();
surf->detectAndCompute(image, noArray(), key_points, descriptor);

(3)ORB(实际来看,速度很快,但效果并不一定好)

#include <opencv2\features2d\features2d.hpp>
Ptr<ORB> orb = ORB::create(5000);
orb->detectAndCompute(image, noArray(), key_points, descriptor);


(4)AKAZE(与ORB在同一个hpp中)

#include <opencv2\features2d\features2d.hpp>
Ptr<AKAZE> akaze = AKAZE::create();
akaze->detectAndCompute(image, noArray(), key_points, descriptor);

2、特征点匹配的几种方法

(1)与ORB结合使用,效果较好

void match_features_knn(Mat& query, Mat& train, vector<DMatch>& matches)
{
	flann::Index flannIndex(query,flann::LshIndexParams(12,20,2),cvflann::FLANN_DIST_HAMMING);
	Mat matchindex(train.rows,2,CV_32SC1);
	Mat matchdistance(train.rows, 2, CV_32FC1);
	flannIndex.knnSearch(train, matchindex, matchdistance,2,flann::SearchParams());
	//根据劳氏算法
	for (int i = 0; i < matchdistance.rows; i++)
	{
		if (matchdistance.at<float>(i, 0) < 0.6*matchdistance.at<float>(i, 1))
		{
			DMatch dmatches(matchindex.at<int>(i, 0),i, matchdistance.at<float>(i, 0));
			matches.push_back(dmatches);
		}
	}
}

(2)个人感觉这种方法,效果与暴力匹配法没啥区别,但是被注释掉的方法,效果不好

void match_features_FLANN(Mat& query, Mat& train, vector<DMatch>& matches)
{
	FlannBasedMatcher matcher;
	
	/*vector<DMatch> match;
	matcher.match(query, train, match);
	double max_dist = 0;
	double min_dist = 100;
	for (int i = 0; i < match.size(); i++)
	{
		double dist = match[i].distance;
		if (dist < min_dist) min_dist = dist;
		if (dist > max_dist) max_dist = dist;
	}
	for (int i = 0; i < match.size(); i++)
	{
		if (match[i].distance < 2 * min_dist) matches.push_back(match[i]);
	}*/

	vector<vector<DMatch>> knn_matches;
	matcher.knnMatch(query, train, knn_matches, 2);

	//获取满足Ratio Test的最小匹配的距离
	float min_dist = FLT_MAX;
	for (int r = 0; r < knn_matches.size(); ++r)
	{
		//Ratio Test
		if (knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance)
			continue;

		float dist = knn_matches[r][0].distance;
		if (dist < min_dist) min_dist = dist;
	}

	matches.clear();
	for (size_t r = 0; r < knn_matches.size(); ++r)
	{
		//排除不满足Ratio Test的点和匹配距离过大的点
		if (
			knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance ||
			knn_matches[r][0].distance > 5 * max(min_dist, 10.0f)
			)
			continue;

		//保存匹配点
		matches.push_back(knn_matches[r][0]);
	}

}

(3)也叫暴力匹配法,此种方法结合sift、surf用的比较多

void match_features(Mat& query, Mat& train, vector<DMatch>& matches)
{
	vector<vector<DMatch>> knn_matches;
	BFMatcher matcher(NORM_L2);

	matcher.knnMatch(query, train, knn_matches, 2);

	//获取满足Ratio Test的最小匹配的距离
	float min_dist = FLT_MAX;
	for (int r = 0; r < knn_matches.size(); ++r)
	{
		//Ratio Test
		if (knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance)
			continue;

		float dist = knn_matches[r][0].distance;
		if (dist < min_dist) min_dist = dist;
	}

	matches.clear();
	for (size_t r = 0; r < knn_matches.size(); ++r)
	{
		//排除不满足Ratio Test的点和匹配距离过大的点
		if (
			knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance ||
			knn_matches[r][0].distance > 5 * max(min_dist, 10.0f)
			)
			continue;

		//保存匹配点
		matches.push_back(knn_matches[r][0]);
	}
}
看下面的效果图,会发现大部分点都是匹配正确的。但是,依然有少部分点匹配的明显不正确。



3、进一步匹配(寻找源图与目标图像之间的透视变换)

bool 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;
		//srcPoints[i] = trainKeypoints[i].pt;
		//dstPoints[i] = queryKeypoints[i].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;
}

效果如下所示(没有发现匹配不对的点)








GitHub 加速计划 / opencv31 / opencv
77.38 K
55.71 K
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:2 个月前 )
c3747a68 Added Universal Windows Package build to CI. 11 天前
9b635da5 - 11 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐