Opencv 之ORB特征提取与匹配API简介及使用例程

Opencv 之ORB特征提取与匹配API简介及使用例程

  • ORB因其速度较快常被用于视觉SLAM中的位姿估计、视觉里程、图像处理中的特征提取与匹配及图像拼接等领域
  • 本文将详细给出使用例程及实现效果展示

1. API 简介

  • 创建
static Ptr<ORB> cv::ORB::create	(
	int nfeatures = 500,                                   //nfeatures 最终输出最大特征点数目
	float scaleFactor = 1.2f,                            // scaleFactor 金字塔上采样比率
	int nlevels = 8,                                            // nlevels 金字塔层数
	int edgeThreshold = 31,                                // edgeThreshold 边缘阈值
	int firstLevel = 0,
	int WTA_K = 2,                                                // WTA_K这个是跟BRIEF描述子用的
	ORB::ScoreType 	scoreType = ORB::HARRIS_SCORE,        //  scoreType 对所有的特征点进行排名用的方法
	int patchSize = 31,
	int fastThreshold = 20 
)
  • 检测
void cv::Feature2D::detect	(	InputArray 	image,   //输入图像
                                std::vector< KeyPoint > & 	keypoints,    //待搜索特征点
                                InputArray 	mask = noArray()    //操作掩码
                             )	
  • 计算
void cv::Feature2D::compute	(	InputArrayOfArrays 	images,    //输入图像
                                std::vector< std::vector< KeyPoint > > & 	keypoints,
                                OutputArrayOfArrays 	descriptors   //描述子
                            )	
  • 检测与计算
 void cv::Feature2D::detectAndCompute	(	InputArray 	image,
                                            InputArray 	mask,
                                            std::vector< KeyPoint > & 	keypoints,
                                            OutputArray 	descriptors,
                                            bool 	useProvidedKeypoints = false 
                                        )	
  • 绘制特征点
void cv::drawMatches	(	InputArray 	img1,
                            const std::vector< KeyPoint > & 	keypoints1,
                            InputArray 	img2,
                            const std::vector< KeyPoint > & 	keypoints2,
                            const std::vector< DMatch > & 	matches1to2,
                            InputOutputArray 	outImg,
                            const Scalar & 	matchColor = Scalar::all(-1),
                            const Scalar & 	singlePointColor = Scalar::all(-1),
                            const std::vector< char > & 	matchesMask = std::vector< char >(),
                            DrawMatchesFlags 	flags = DrawMatchesFlags::DEFAULT 
                         )	
  • 绘制匹配点对
void cv::drawMatches	(	InputArray 	img1,
                            const std::vector< KeyPoint > & 	keypoints1,
                            InputArray 	img2,
                            const std::vector< KeyPoint > & 	keypoints2,
                            const std::vector< DMatch > & 	matches1to2,
                            InputOutputArray 	outImg,
                            const Scalar & 	matchColor = Scalar::all(-1),
                            const Scalar & 	singlePointColor = Scalar::all(-1),
                            const std::vector< char > & 	matchesMask = std::vector< char >(),
                            DrawMatchesFlags 	flags = DrawMatchesFlags::DEFAULT 
                       )	

2. 特征提取

  • 讲述特征点提取与描述子计算,实现如下:
int main()
{
	Mat img = imread("./data/test3/lena.png");
	if (!img.data || img.empty())
	{
		cout << "图像读取错误!" << endl;
		return -1;
	}

	//创建ORB关键点

	Ptr<ORB> orb = ORB::create(500, 1.2f);
	double t1 = getTickCount();
	vector<KeyPoint>Keypoints;
	Mat descriptions;
#if 0
	//计算ORB关键点
	orb->detect(img, Keypoints);

	//计算ORB描述子
	orb->compute(img, Keypoints, descriptions);
#else
	orb->detectAndCompute(img, cv::Mat(), Keypoints, descriptions);
#endif // 0

	double t2 = (getTickCount() - t1) / getTickFrequency() * 1000;
	cout << "img.size = " << img.size() << " , cost time = " << t2 << "msn";

	//绘制特征点
	Mat imgAngle;
	img.copyTo(imgAngle);
	//绘制不含角度和大小的结果
	drawKeypoints(img, Keypoints, img, Scalar(255, 255, 255));
	//绘制含有角度和大小的结果
	drawKeypoints(img, Keypoints, imgAngle, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

	//显示结果
	string wname1 = "不含角度和大小的结果", wname2 = "含角度和大小的结果";
	namedWindow(wname1, WINDOW_NORMAL);
	namedWindow(wname2, 0);
	imshow(wname1, img);
	imshow(wname2, imgAngle);
	waitKey(0);

	return 1;
}

在这里插入图片描述

3. 特征匹配

  • 暴力匹配实现:
#include<iostream>
#include<opencv2/opencv.hpp>
#include<vector>

using namespace std;
using namespace cv;

int main()
{
	//灰度格式读取
	Mat img1, img2;
	img1 = imread("./data/test3/1.jpg",IMREAD_GRAYSCALE);
	img2 = imread("./data/test3/2.jpg",0);
	if (img1.empty() || img2.empty())
	{
		cout << "img.empty!!!n";
		return -1;
	}
	//提取orb特征点
	vector<KeyPoint>Keypoints1, Keypoints2;
	Mat descriptions1, descriptions2;

	//计算特征点
	orb_features(img1, Keypoints1, descriptions1);
	orb_features(img2, Keypoints2, descriptions2);

	//特征点匹配
	vector<DMatch>matches; 
	BFMatcher matcher(NORM_HAMMING); //定义特征点匹配的类,使用汉明距离
	matcher.match(descriptions1, descriptions2, matches);
	cout << "matches = " << matches.size() << endl;

	//通过汉明距离筛选匹配结果
	double min_dist = 10000, max_dist = 0;
	for (int i = 0; i < matches.size(); ++i)
	{
		double dist = matches[i].distance;
		min_dist = min_dist < dist ? min_dist : dist;
		max_dist = max_dist > dist ? max_dist : dist;
	}
	//输出计算的最大、最小距离
	cout << "min_dist = " << min_dist << endl;
	cout << "max_dist = " << max_dist << endl;

	//通过距离提出误差大的点
	vector<DMatch>goodmatches;
	for (int i = 0; i < matches.size(); ++i)
	{
		if (matches[i].distance <= MAX(1.8 * min_dist, 30)) //需调参
		{
			goodmatches.push_back(matches[i]);
		}
	}
	cout << "good_min = " << goodmatches.size() << endl;

	//绘制结果
	Mat outimg, outimg1;
	drawMatches(img1, Keypoints1, img2, Keypoints2, matches, outimg);
	drawMatches(img1, Keypoints1, img2, Keypoints2, goodmatches, outimg1);
	string wname1 = "未筛选结果", wname2 = "最小汉明距离筛选";
	namedWindow(wname1, WINDOW_NORMAL);
	namedWindow(wname2, 0);
	imshow(wname1, outimg);
	imshow(wname2, outimg1);
	waitKey(0);

	return 1;
}

其效果如下:

在这里插入图片描述

在这里插入图片描述

  • 最近邻匹配实现如下:
if (descriptions1.type() != CV_32F)
	{
		descriptions1.convertTo(descriptions1, CV_32F);
		descriptions2.convertTo(descriptions2, CV_32F);
	}
	//特征点匹配
	vector<DMatch>matches;
	FlannBasedMatcher matcher; //定义特征点匹配的类,使用汉明距离
	matcher.match(descriptions1, descriptions2, matches);
	cout << "matches = " << matches.size() << endl;

	//通过汉明距离筛选匹配结果
	double min_dist = 10000, max_dist = 0;
	for (int i = 0; i < matches.size(); ++i)
	{
		double dist = matches[i].distance;
		min_dist = min_dist < dist ? min_dist : dist;
		max_dist = max_dist > dist ? max_dist : dist;
	}
	//输出计算的最大、最小距离
	cout << "min_dist = " << min_dist << endl;
	cout << "max_dist = " << max_dist << endl;

	//通过距离提出误差大的点
	vector<DMatch>goodmatches;
	for (int i = 0; i < matches.size(); ++i)
	{
		if (matches[i].distance <= 0.6 * max_dist) //需调参
		{
			goodmatches.push_back(matches[i]);
		}
	}
	cout << "good_min = " << goodmatches.size() << endl;

其效果如下:
在这里插入图片描述
在这里插入图片描述

  • RANSAC优化匹配
void ransac(vector<DMatch>matches, vector<KeyPoint>queryKeyPoint,
	vector<KeyPoint>trainKeyPoint, vector<DMatch>& matches_ransac)
{
	//定义保存匹配点对坐标
	vector<Point2f>srcPoints(matches.size()), dstPoints(matches.size());
	//保存从关键点中提取到的匹配点对坐标
	for (int i = 0; i < matches.size(); ++i)
	{
		srcPoints[i] = queryKeyPoint[matches[i].queryIdx].pt;
		dstPoints[i] = trainKeyPoint[matches[i].trainIdx].pt;
	}
	//匹配点对RANSAC过滤
	vector<int>inlierMask(srcPoints.size());
	findHomography(srcPoints, dstPoints, RANSAC, 5, inlierMask);

	//手动保留RANSAC过滤后的匹配点对
	for (int i = 0; i < inlierMask.size(); ++i)
	{
		if (inlierMask[i])
			matches_ransac.push_back(matches[i]);
	}
}

//*************************RANSAC*******************************************
//main函数中放在暴力匹配代码后:
    //特征点匹配
	vector<DMatch>matches; 
	BFMatcher matcher(NORM_HAMMING); //定义特征点匹配的类,使用汉明距离
	matcher.match(descriptions1, descriptions2, matches);
	cout << "matches = " << matches.size() << endl;

	//通过汉明距离筛选匹配结果
	double min_dist = 10000, max_dist = 0;
	for (int i = 0; i < matches.size(); ++i)
	{
		double dist = matches[i].distance;
		min_dist = min_dist < dist ? min_dist : dist;
		max_dist = max_dist > dist ? max_dist : dist;
	}
	//输出计算的最大、最小距离
	cout << "min_dist = " << min_dist << endl;
	cout << "max_dist = " << max_dist << endl;

	//通过距离提出误差大的点
	vector<DMatch>goodmatches;
	for (int i = 0; i < matches.size(); ++i)
	{
		if (matches[i].distance <= MAX(1.8 * min_dist, 30)) //需调参
		{
			goodmatches.push_back(matches[i]);
		}
	}
	cout << "good_min = " << goodmatches.size() << endl;

	//RANSAC优化:
	vector<DMatch>good_ransac;
	ransac(goodmatches, Keypoints1, Keypoints2, good_ransac);
	cout << "good_ransac = " << good_ransac.size() << endl;
	Mat output_;
	drawMatches(img1, Keypoints1, img2, Keypoints2, good_ransac, output_);
	namedWindow("ransac", 0);
	imshow("ransac", output_);

在这里插入图片描述