特征检测+SIFT点匹配+PNP位姿确定

2023-05-16

SIFT特征检测+FLANN点匹配+PNP位姿确定

软件环境

  1. windows 10

  2. vs2013

  3. opencv3.1.0+opencv3.1.0 contrib

基本原理

​ 1.SIFT 特征点检测+匹配

​ 2. PNP位姿确定

代码实现

1. 参考博客

  • sif+flann解决匹配问题
  • solvepnp解决位姿估计

2. 修改

  • 首先是更换库,3.x以上版本的SIFT算法detect和compute的调用和opencv2.x版本的不太一样,使用时如果包含库或者报错记得及时修改
  • 使用的匹配算法和剔除算法都可以更改,相应的实例有很多
  • 能力有限,findHomography已经能返回一个单应性矩阵,如果加以利用理论上也可以恢复位姿?但是我不太会用,所以把perspectiveTransform获得的四个目标像素角点重新输入了olvePnP解算相机和检测平面间的平移旋转
  • 这个思路和用aruco或者二维码码块检测本质上是一致的,都是匹配检测获得四个角点然后用P3P解决位姿估计问题,差别是如果可以获得任意一个物体的正面图,我们就能实现对这个物体初步的检测和估计,而摆脱了对标记的需求。

3. 源码:

#include "stdafx.h"
#include <ctime>
#include <iostream>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp> 
#include<opencv2/core/core.hpp>
#include<opencv_modules.hpp>
#include <opencv2/xfeatures2d.hpp>	//包含 SiftFeatureDetector 的头文件
#include "opencv2/features2d/features2d.hpp"	//包含 FlannBasedMatcher 的头文件
#include "opencv2/calib3d/calib3d.hpp"			//包含 findHomography 的头文件

using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;

vector<Vec3d> usePnP(vector<Point2f> scenePoints);


int main()
{
	
	//读图,匹配对象,待搜寻图
	cv::Mat imgObject = cv::imread("D:/Celeste/VS Code/SiftandFlann/101202.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	cv::Mat imgScene = cv::imread("D:/Celeste/VS Code/SiftandFlann/101203.jpg", CV_LOAD_IMAGE_GRAYSCALE);

	if (imgObject.empty() || imgScene.empty())
	{
		std::cout << " --(!) Error reading images " << std::endl;
		return -1;
	}

	double begin = clock();


	Ptr<Feature2D> f2d = xfeatures2d::SIFT::create();
	///-- Step 1: 使用SIFT算子检测特征点
	//cv::SiftFeatureDetector detector;//( minHessian );
	//opencv3之后都使用对象指针
	std::vector<cv::KeyPoint> keypointsObject, keypointsScene;
	f2d->detect(imgObject, keypointsObject);
	f2d->detect(imgScene, keypointsScene);
	std::cout << "object--number of keypoints: " << keypointsObject.size() << std::endl;
	std::cout << "scene--number of keypoints: " << keypointsScene.size() << std::endl;

	///-- Step 2: 使用SIFT算子提取特征(计算特征向量)
	//cv::SiftDescriptorExtractor extractor;
	cv::Mat descriptorsObject, descriptorsScene;
	f2d->compute(imgObject, keypointsObject, descriptorsObject);
	f2d->compute(imgScene, keypointsScene, descriptorsScene);

	///-- Step 3: 使用FLANN法进行匹配
	cv::FlannBasedMatcher matcher;
	std::vector< cv::DMatch > allMatches;
	matcher.match(descriptorsObject, descriptorsScene, allMatches);
	std::cout << "number of matches before filtering: " << allMatches.size() << std::endl;

	//-- 计算关键点间的最大最小距离
	double maxDist = 0;
	double minDist = 100;
	for (int i = 0; i < descriptorsObject.rows; i++)
	{
		double dist = allMatches[i].distance;
		if (dist < minDist)
			minDist = dist;
		if (dist > maxDist)
			maxDist = dist;
	}
	printf("	max dist : %f \n", maxDist);
	printf("	min dist : %f \n", minDist);

	//-- 过滤匹配点,保留好的匹配点(这里采用的标准:distance<3*minDist)
	//也可以用RANSAC剔除
	std::vector< cv::DMatch > goodMatches;
	for (int i = 0; i < descriptorsObject.rows; i++)
	{
		if (allMatches[i].distance < 2 * minDist)
			goodMatches.push_back(allMatches[i]);
	}
	std::cout << "number of matches after filtering: " << goodMatches.size() << std::endl;

	//-- 显示匹配结果
	cv::Mat resultImg;
	drawMatches(imgObject, keypointsObject, imgScene, keypointsScene,
		goodMatches, resultImg, cv::Scalar::all(-1), cv::Scalar::all(-1), std::vector<char>(),
		cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS //不显示未匹配的点
		);
	//-- 输出匹配点的对应关系
	//for (size_t i = 0; i < goodMatches.size(); i++)
		//printf("	good match %d: keypointsObject [%d]  -- keypointsScene [%d]\n", i, goodMatches[i].queryIdx, goodMatches[i].trainIdx);

	///-- Step 4: 使用findHomography找出相应的透视变换
	//object和scene的赋值循环可以放到goodmatch剔点中直接写
	std::vector<cv::Point2f> object;
	std::vector<cv::Point2f> scene;
	for (size_t i = 0; i < goodMatches.size(); i++)
	{
		//-- 从好的匹配中获取关键点: 匹配关系是关键点间具有的一 一对应关系,可以从匹配关系获得关键点的索引
		//-- e.g. 这里的goodMatches[i].queryIdx和goodMatches[i].trainIdx是匹配中一对关键点的索引
		object.push_back(keypointsObject[goodMatches[i].queryIdx].pt);
		scene.push_back(keypointsScene[goodMatches[i].trainIdx].pt);
	}
	cv::Mat H = findHomography(object, scene, CV_RANSAC);
	//返回的H矩阵就是两个平面变换的单应性矩阵

	///-- Step 5: 使用perspectiveTransform映射点群,在场景中获取目标位置
	std::vector<cv::Point2f> objCorners(4);
	objCorners[0] = cvPoint(0, 0);
	objCorners[1] = cvPoint(imgObject.cols, 0);
	objCorners[2] = cvPoint(imgObject.cols, imgObject.rows);
	objCorners[3] = cvPoint(0, imgObject.rows);
	std::vector<cv::Point2f> sceneCorners(4);//目标的像素坐标
	cv::perspectiveTransform(objCorners, sceneCorners, H);
	

	//-- 在被检测到的目标四个角之间划线(左上第一个点,顺时针)
	//sceneCorners和像素点位置之间的关系,差一个参考图列数
	line(resultImg, sceneCorners[0] + cv::Point2f(imgObject.cols, 0), sceneCorners[1] + cv::Point2f(imgObject.cols, 0), cv::Scalar(0, 0, 0), 4);
	line(resultImg, sceneCorners[1] + cv::Point2f(imgObject.cols, 0), sceneCorners[2] + cv::Point2f(imgObject.cols, 0), cv::Scalar(0, 0, 0), 4);
	line(resultImg, sceneCorners[2] + cv::Point2f(imgObject.cols, 0), sceneCorners[3] + cv::Point2f(imgObject.cols, 0), cv::Scalar(0, 255, 0), 4);
	line(resultImg, sceneCorners[3] + cv::Point2f(imgObject.cols, 0), sceneCorners[0] + cv::Point2f(imgObject.cols, 0), cv::Scalar(255, 255, 255), 4);

	//-- 显示检测结果
	cv::imshow("detection result", resultImg);
	vector<int> compression_params;
	compression_params.push_back(IMWRITE_PNG_COMPRESSION);
	compression_params.push_back(3);
	//存图
	imwrite("D:/Celeste/VS Code/SiftandFlann/good.png", resultImg, compression_params);
	double end = clock();
	std::cout << "\nSIFT--elapsed time: " << (end - begin) / CLOCKS_PER_SEC * 1000 << " ms\n";

	//--计算位置姿态
	Vec3d rvec, tvec;	
	vector<Vec3d> res(2);
	//rvec是旋转向量
	//tvec是平移向量
	res=usePnP(sceneCorners);
	rvec = res[0];
	tvec = res[1];	
	cout << "rvec:" <<rvec<< endl;
	cout << "tvec" << tvec << endl;

	//后续计算矩阵转换关系、获得目标对象的世界坐标系

	cv::waitKey(0);
	return 0;

}

//使用pnp求解位置,输入是四个顶点的像素坐标,输出是旋转平移向量
vector<Vec3d> usePnP(vector<Point2f> scenePoints)
{
	//参考物体在实际空间中的位置,以参考物体左上角为原点,顺时针方向,point3d用mm单位真实数据,pnp解得得R\T就是相机相对参考物体的位姿旋转
	//根据实际物体修改参数
	vector<Point3f> objectPoints(4);
	objectPoints[0] = CvPoint3D32f(0, 0, 0.0f);
	objectPoints[1] = CvPoint3D32f(230.0f, 0.0f, 0.0f);
	objectPoints[2] = CvPoint3D32f(230.0f, 289.0f, 0.0f);
	objectPoints[3] = CvPoint3D32f(0.0f, 289.0f, 0.0f);
	//相机内参、畸变矩阵重新填写
	cv::Mat cameraMatrix, distCoeffs;
	cameraMatrix = (Mat_<double>(3, 3) << 598.29493, 0, 304.76898, 0, 597.56086, 233.34673, 0, 0, 1);
	distCoeffs = (Mat_<double>(5, 1) << -0.53572, 1.35993, -0.00244, 0.00620, 0.00000);
	//参数返回值
	Vec3d rvec, tvec;
	//计算rvec\tvec;采用p3p模式
	bool IsSolve=cv::solvePnP(objectPoints, scenePoints, cameraMatrix, distCoeffs, rvec, tvec, false, CV_P3P);
	vector<Vec3d> res(2);
	if (IsSolve)
	{
		cout << "solvePnP successfully!" << endl;
		res[0] = rvec;
	    res[1] = tvec;

	}
	else
	{
		cout << " solvePnP unsuccessfully……check your data again" << endl;
		res[0] = Vec3d(0, 0, 0);
		res[1] = Vec3d(0, 0, 0);
	}
	return res;

}

4. 效果演示

匹配和目标检测

控制台返回

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

特征检测+SIFT点匹配+PNP位姿确定 的相关文章

  • 多传感器融合框架-ESKF

    架构 基本同图优化框架差不多 内容简述 数据预处理节点 订阅imu原始数据 xff0c gnss原始数据 xff0c 完成数据时间戳同步 点云畸变补偿发布如下消息 畸变补偿后的点云 synced cloudgnss里程计 synced gn
  • OpenCV笔记4.3

    C 43 43 读取目录下所有文件名称 1 包含头文件 96 span class token macro property span class token directive hash span span class token dir
  • 半小时内实现Esp32-Cam模型训练和图像识别

    Esp32 Cam图像识别 一 网页显示视频流1 Linux式例程2 MicroPython式例程步骤1 下载Thonny步骤2 烧录Esp32 Cam固件步骤3 运行相应代码 3 Arduino式例程步骤1 下载Arduino步骤2 安装
  • MANIFOLD 2-G开发 之 利用ROS话题编程实现UART1串口通讯

    目录 MANIFOLD 2 G开发笔记1 项目描述2 遇到的问题3 解决方案4 示例代码5 注意事项6 资源附件6 运行与调试 MANIFOLD 2 G开发笔记 本博客内容将记录MANIFOLD 2 G 开发过程中遇到的问题及解决方案 xf
  • ONOS源码笔记--机制

    app注册 private ApplicationId appId appId 61 coreService registerApplication 34 org onosproject fwd 34 注册应用 xff0c 一般在activ
  • git 本地分支与远程分支关联

    github上已经有master分支 和dev分支 在本地 git checkout b dev 新建并切换到本地dev分支 git pull origin dev 本地分支与远程分支相关联 github上没有dev分支 git check
  • 做设计师还是程序员?一张图你就明白!

    平时大家相安无事 xff0c 可一旦项目滑了水 栽了坑 二重奏就开始没完没了的唱起来了 请看下图 xff1a 你的桌子是有什么 小编反手一摸 xff0c 还好小编的头发还再 你头发呢 xff1f 相信这里有很多学习java的朋友 xff0c
  • 【ROS】动态链接库(.so文件)的生成和调用

    ROS 动态链接库 xff08 so文件 xff09 的生成和使用 引言 xff11 生成so文件 xff12 调用so文件 xff13 运行结果结语 引言 由于项目有涉及到源代码是否交付的选项 xff0c 想着如果不交付源代码 xff0c
  • 用TortoiseGit删除git仓库中的文件/文件夹

    用TortoiseGit删除git仓库中的文件 文件夹 点击删除后出现弹框 xff0c 点击删除 删除后会显示移除一个文件 xff1b 想要还原可以在当前文件夹中右键选择TortoiseGit gt 还原 xff08 revert xff0
  • 02-Jetson Xavier NX 训练自己的yolov4-tiny模型并利用TensorRT完成部署(Python API)

    记录原因 xff1a 因前面完成了yolov4 tiny源码的部署 xff0c 可以跑到50FPS 但那个跑的是别人的模型 xff0c 可以识别80类 xff0c 但是在特定的情况下比如识别螺丝螺帽 xff0c 只需要两种 xff0c 在训
  • RPLIDAR在ROS下快速上手教程

    本教程由 臭皮匠机器人 原创 转载请注明出处 收到一个RPLidar后 xff0c 干的第一件事应该是先让雷达跑起来 xff0c 边做边学是最高效的 之前写了一篇在windows底下的 RPLIDAR十分钟极速入门教程 xff0c 今天将教
  • RPLIDAR的坐标系问题

    本教程由 臭皮匠机器人 原创 转载请注明出处 RPLIDAR自身的坐标系 RPLIDAR给出来的数据是角度和距离 xff0c 也就是极坐标系 转换成直角坐标系的话应该照转换公式计算 xff0c 假设r是距离 xff0c theta是角度 x
  • Makefile模板(取自vscode)

    文章目录 Makefile模板vscode插件 xff1a 单一目标编译多个目标 Makefile模板 vscode插件 xff1a 单一目标 最近在学习Makefile写法 xff0c 但是总觉得自己写的太烂了 xff0c 找到了vsco
  • 使用docker-compose配置mysql数据库并且配置用户密码

    下面要求环境 xff1a 一定要安装docker ce和docker compose才能进行下面步骤 linux找到你要放mysql的目录 创建一个docker compose yml 以下配置了外部数据卷 外部配置文件 外部初始化文件 x
  • 使用ONOS的REST API来下发流表

    1 启动ONOS后 xff0c 浏览器进入doc http 10 109 247 211 8181 onos v1 docs 2 找到Flow xff0c 并打开POST 3 可以直接在这个上面编辑flow stream里面模拟GET获得的
  • 形态学处理

    原文链接 xff1a https blog csdn net qq 40732350 article details 116718329 spm 61 1001 2014 3001 5506 形态学处理 0 前言1 腐蚀与膨胀1 1 结构元
  • leetcode用到的函数汇总

    reverse isalnum tolower
  • Keil MDK-ARM软件官网最新版本的下载方法

    我之前的几篇博客讲的都是关于Keil MDK5 ARM软件的一些配置方法 xff0c 但是我居然忘了说一下如何去下载这个软件了 xffe3 xffe3 xff5c xff5c xff0c 今天这篇博客就把这个小知识点补上 第一步 xff0c
  • 给Word文档中的公式自动编号的方法

    最近在做毕业设计 xff0c 然后需要翻译一篇英文文献 xff0c 我在翻译英文文献的时候 xff0c 需要对公式进行编号 xff0c 而且需要使编号显示在最右侧 xff0c 而公式居中 xff0c 像下图这样 xff1a 如果一个个手动添
  • PHP如何使用strrev()函数反转字符串?(代码示例)

    反转字符串是最基本的字符串操作之一 xff0c 在PHP中可以使用内置函数strrev 来实现字符串的反转 下面本篇文章就来带大家了解一下PHP strrev 函数怎么用 xff0c 希望对大家有所帮助 PHP strrev 函数 strr

随机推荐

  • 离线安装Linux包的方法--以imblearn为例

    包的 whl文件直接在https pypi org 上搜索下载就好 xff0c 注意要搜索全名scikit learn xff01 你搜sklearn是找不到合适的包的 如图 xff0c 选择第一个就好 xff1a 接着要匹配你的系统架构和
  • px4 EKF中Q、R阵设置的思考

    关于Q R的讨论 Q阵 xff0c 状态转移误差矩阵 xff0c 代表从Xt 1到Xt过程中 xff0c 状态转移和真实过程之间的误差 xff0c 具体其中变量可能是对状态转移有影响的变量 xff0c 比如在有一些场景下 xff0c 可能为
  • Ubuntu查看硬盘信息

    lsscsi包默认是不安装的 xff0c 可以使用以下命令安装lsscsi span class token function sudo span span class token function apt get span span cl
  • 在Python3环境中使用ROS的cv_bridge

    之前使用ROS的时候只用了C 43 43 xff0c 没有发现cv bridge这个坑 xff0c 最近增加了一个使用tensorflow2的节点 xff0c 为此使用Anaconda配置了一个只有Python3 7的环境 xff0c 运行
  • 解决mac安装完torch_geometric,import的时候报错的问题。

    问题描述 使用torch geometric官方的命令安装好了torch geometric xff0c 安装的过程非常丝滑流畅 xff0c 但是安装好了import的时候就报错OSerror 注意事项 不要用他生成的命令安装 xff1a
  • CXF内容总结

    CXF 61 XFire webservice框架 43 Celtrix ESB框架 http cxf apache org 内置jettyweb服务器 服务器端 xff1a 1 开发webservice接口 xff0c 要用 64 web
  • Could NOT find UV (missing: UV_LIBRARY UV_INCLUDE_DIR)

    cd var tmp git clone https github com libuv libuv git cd libuv autogen sh configure make make install 注 xff1a 下不来直接去网页下载
  • 【STM32】HAL库开发教程(四)—串口FIFO使用

    前言 不必害怕未知 xff0c 无需恐惧犯错 xff0c 做一个Creator xff01 本文主要介绍STM32 HAL库开发中串口 FIFO的使用 一 开发步骤 1 Cubemx配置 在左侧引脚配置栏选择目标串口号在串口模式处配置串口模
  • 【STM32】串口数据帧接收与分析处理算法

    基于对串口FIFO的通信数据帧进行接收和分析处理 xff08 关于串口FIFO使用可以参见作者另一篇博文 xff09 算法流程 xff1a 串口中断函数接收数据到FIFO 根据通信协议GetInterUARTMessage 函数对数据帧进行
  • 【STM32】HAL库开发教程(三)—定时器使用

    前言 不必害怕未知 xff0c 无需恐惧犯错 xff0c 做一个Creator xff01 本文主要介绍STM32 HAL库开发中定时器的使用 一 开发步骤 1 STM32CubeMX配置 在左侧引脚配置处勾选TIM2进行配置在TIM2模式
  • 直立车想节能——2020全国大学生智能车车竞赛经验记录之我想要直立(平衡小车串级pid调参经验)

    平衡车我相信很多电赛测控的入门的玩家都也玩过 xff0c 很多朋友们也都听说过平衡小车之家这家淘宝店铺 而串级PID也是一个经典且牛批的方案而广为人知 xff0c 所以在这样的比赛结束之后作为俩套方案都试过的我 xff0c 谈一谈平衡小车之
  • 慎用apt autoremove

    1 remove 卸载软件包 2 autoremove 卸载所有自动安装且不再使用的软件包 3 purge 卸载并清除软件包的配置 平时经常会卸载或删除一些东西 xff0c 系统也会友好提示你进行自动删除一些相关的文件 xff0c 但是有可
  • 推送镜像到DockerHub报无访问权限

    推送镜像到DockerHub 我们推送镜像到DockerHub时会遇到没有访问资源权限的问题 requested access to the resource is deni ed 这是因为dockerhub不知道是谁推送的镜像 xff0c
  • mysql5主从配置教程

    mysql5主从配置 同学们会有mysql主从配置的需求吗 xff1f 这里我给大家讲一下mysql5 7的主从配置教程 我们通过docker来安装mysql 这样会方便很多 拉取镜像 docker pull docker pull mys
  • 解决ubuntu下kazam录制视频无法在windows播放问题

    记录贴 Kazam是ubuntu下一个功能性截屏软件 xff0c 但是在使用的过程中 xff0c 发现Kazam录屏的视频能够在QQ发送 xff0c 但是只能播放 xff0c 不能下载 xff0c window下也不能播放 查找了一些资料发
  • 深入理解以太网PHY自协商及调试心得

    最近调某个phy芯片 xff0c 心态爆炸 xff0c 不过好歹最后有个好结果 自协商基本原理 自动协商模式是端口根据另一端设备的连接速度和双工模式 xff0c 自动把它的速度调节到最高的公共水平 xff0c 即线路两端能具有的最快速度和双
  • 平衡小车的资料

    根据网上平衡小车之家的资料引脚需要用 xff1a LED PA12 Key PA15 OLED RST Clr PC13 RS Clr PB4 SCLK Clr PC15 SDIN Clr PC14 ADC PA4 TIM3 PB0 PB1
  • 注解

    好处 把问题暴露在编译时期 xff1b 增强程序健壮性 xff1b 可以让JVM检查除了语法错误以外的其他的自定义检查 xff1b 位置 可以加到包 xff0c 类 xff0c 方法 xff0c 字段 xff0c 局部变量等位置 xff1b
  • 【知识图谱】课程笔记1---知识图谱简介

    1 知识图谱 知识图谱是一种基于图的数据结构 由节点 point 和边 Edge 组成 每个节点表示一个 实体 每条边为实体与实体之间的 关系 知识图谱本质上是语义网络 通过这种先验的知识网络 xff0c 让机器像人类一样可以联想 推理 它
  • 特征检测+SIFT点匹配+PNP位姿确定

    SIFT特征检测 43 FLANN点匹配 43 PNP位姿确定 软件环境 windows 10 vs2013 opencv3 1 0 43 opencv3 1 0 contrib 基本原理 1 SIFT 特征点检测 43 匹配 2 PNP位