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

news2024/11/27 4:41:30

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 << "ms\n";

	//绘制特征点
	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_);

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/877562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习笔记整理-面向对象-03-构造函数

一、构造函数 1. 用new调用函数的四步走 new 函数();JS规定&#xff0c;使用new操作符调用函数会进行"四步走"&#xff1a; 函数体内会自动创建出一个空白对象函数的上下文(this)会指向这个对象函数体内的语句会执行函数会自动返回上下文对象&#xff0c;即使函数没…

事件过滤器(eventfilter)的说明与使用

事件过滤器可以在不定义一个新的类的情况下&#xff0c;对界面组件的事件进行处理。事件过滤器通过将一个对象的事件委托给另一个对象来监视并进行处理&#xff1b;如一个窗口可以作为其界面上的QLabel组件的事件过滤器&#xff0c;派发给QLabel组建的事件由窗口去处理&#xf…

Blender 混合现实3D模型制作指南【XR】

本教程分步展示如何&#xff1a; 减少 3D 模型的多边形数量&#xff0c;使其满足 Microsoft Dynamics 365 Guides 和使用 Microsoft Power Apps 创建的应用程序中包含的混合现实组件的特定性能目标的性能需求。将 3D 模型的多种材质&#xff08;颜色&#xff09;组合成可应用于…

matplotlib绘制位置-时序甘特图

文章目录 1 前言2 知识点2.1 matplotlib.pyplot.barh2.2 matplotlib.legend的handles参数 3 代码实现4 绘制效果5 总结参考 1 前言 这篇文章的目的是&#xff0c;总结记录一次使用matplotlib绘制时序甘特图的经历。之所以要绘制这个时序甘特图&#xff0c;是因为22年数模研赛C…

关于consul的下载方法

linux下 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo sudo yum -y install consulwindow下 https://developer.hashicorp.com/consul/downloads 然后把里面的exe文件放在gopath下就行了 验证…

手机照片误删怎么办,电脑照片误删怎么办怎么才能找回,EasyRecovery来帮您

手机照片误删怎么办&#xff0c;电脑照片误删怎么办怎么才能找回&#xff0c;EasyRecovery 2023来帮您&#xff01;&#xff01;&#xff01; EasyRecovery 2023是一款操作安全、价格便宜、用户自主操作的 数据恢复 方案&#xff0c;它支持从各种各样的 存储介质 恢复删除 或者…

12 注册登录

12 注册登录 整体概述 使用数据库连接池实现服务器访问数据库的功能&#xff0c;使用POST请求完成注册和登录的校验工作。 本文内容 介绍同步实现注册登录功能&#xff0c;具体涉及到流程图、载入数据库表、提取用户名和密码、注册登录流程与页面跳转的代码实现。 流程图&a…

加了ComponentScan,但是feign接口无法注入的原因

正文 正确的注入 如果发现无法注入&#xff1a;看看启动类Application是否有加入注解&#xff1a;EnableFeignClients(AppConstant.BASE_PACKAGES) 注意&#xff1a;EnableFeignClients和ComponentScan是两个独立的扫描&#xff0c;所以&#xff0c;如果只配置了ComponentSca…

FPGA控制RGB灯WS2812B

文章目录 FPGA控制RGB灯WS2812B1、简介1.1水一水1.2程序完成目标1.3项目工程结构 2、代码3、仿真代码4、结果展示 FPGA控制RGB灯WS2812B 1、简介 1.1水一水 最近在学习WS2812B手册&#xff0c;是一个简单的协议编写&#xff0c;做的时间也算是比较久&#xff0c;相对做出了一…

00-认识C++

2、认识C 2.1、例子 一个简单的C例子 #include <iostream>int main() {using namespace std; //使用名称空间cout << "Com up and C me some time.";cout << endl; //换行符&#xff0c;还可以cout<<"\n";cout <…

【JAVA】集合(Collection、Map)

集合和数组都是容器 数组&#xff1a;类型确定&#xff0c;长度固定&#xff0c;可以存储基本类型和引用类型的数据 集合&#xff1a;类型可以不固定&#xff0c;大小可变&#xff0c;只能存储引用数据类型的数据 Collection单列单列集合&#xff0c;每个元素只包含一个值Ma…

伴随矩阵的特征值跟原矩阵特征值居然有关系!!!附证明

文章目录 论点&#xff1a;A与其伴随矩阵的特征值相乘等于|A|证明证明证明中涉及的相关定理&#xff1a;1.逆矩阵与伴随矩阵的关系2.A可逆时&#xff0c;A的逆矩阵的特征值是原矩阵特征值的倒数 论点&#xff1a;A与其伴随矩阵的特征值相乘等于|A| 证明 证明 证明中涉及的相关…

html css实现爱心

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>/* 爱心 */.lo…

基于Matlab实现小偷体貌识别仿真(附上源码+数据集)

小偷体貌识别是一种应用于安全领域的重要技术&#xff0c;它利用计算机视觉和机器学习的方法&#xff0c;通过对监控视频中的人体特征进行提取和分析&#xff0c;来识别出可能的小偷。在本文中&#xff0c;我们将介绍如何使用Matlab实现小偷体貌识别的仿真。 文章目录 介绍部分…

测牛学堂:软件测试工程师都要懂的软件开发模型总结

软件开发模型之快速原型 之前跟大家介绍了最早期的瀑布模型。今天聊一下快速原型模型。 快速原型&#xff0c;在初步确定用户需求之后&#xff0c;快速构造出一个原型项目&#xff0c;可以跟用户展示软件的功能&#xff0c;用户根据原型进行评审&#xff0c;提出更进一步的细…

JavaWeb-Servlet服务连接器(终)

上一篇文章JavaWeb-Servlet服务连接器&#xff08;三&#xff09;_Alphamilk的博客-CSDN博客 目录 1.ServletContext通信 会话技术Cookie与Session 1.Cookie 2.Session 1.ServletContext通信 概念&#xff1a;代表了整个web应用&#xff0c;用于与服务器实现通信 可以通…

6.2 口令破解攻击

数据参考&#xff1a;CISP官方 目录 口令安全问题口令破解攻击口令破解安全防护口令使用安全管理 一、口令安全问题 什么是口令 身份验证的机制&#xff0c;俗称 “密码"&#xff0c;对应英文单词为 password成本较低&#xff0c;得到广泛应用信息安全中的 “密码”…

基于 Nginx All In One 的 Outline Wiki 部署方法

1. Outline 简介 官网&#xff1a;https://www.getoutline.com/ Outline 是一个开源的知识库和团队协作工具&#x1f9e0;&#xff0c;旨在帮助团队共享、组织和协作文档&#x1f4dd;。它提供了一个简洁的界面&#xff0c;使用户能够轻松创建、编辑和查看文档。 以下是 Out…

sudo免密码设置以及设置失败解决方法

使用sudo visudo修改\etc\sudoers文件 打开后有很多已有的设置大致格式username ALL(ALL:ALL) ALL&#xff0c;都不要动&#xff01; 在文件结尾加上一句话&#xff1a; username ALL(ALL:ALL) NOPASSWD: ALLusername就是目前你这个账户的名字&#xff0c;开机时会输密码登录…

Nginx访问日志及巡检统计

一、前言 二、词汇解释 2.1、pv和uv的区别 PV&#xff08;Page View访问量/也卖弄浏览数&#xff09;&#xff1a;指在一定统计周期内&#xff0c;用户每次刷新网页一次即计算一次。PV高不一定代表来访者多:PV与来访者的数量成正比&#xff0c;但是PV并不直接决定页面的真实来…