通过solvePnP求解相机位置 (代码可运行)

2023-05-16

目录

一、Opencv函数使用

二、目标是求得相机在世界坐标系下的3D坐标。

1、法一:

2、法二:

3、法1附录源代码(可运行通过)


一、Opencv函数使用

solvePnP原型为:

bool cv::solvePnP	(	InputArray 	objectPoints,
InputArray 	imagePoints,
InputArray 	cameraMatrix,
InputArray 	distCoeffs,
OutputArray 	rvec,
OutputArray 	tvec,
bool 	useExtrinsicGuess = false,
int 	flags = SOLVEPNP_ITERATIVE 
)	

其中,rvec以及tvec为旋转向量与平移向量。

 在solvePnP调用结束后,需要使用罗德里格斯变化将旋转向量转换成旋转矩阵。

Mat r,t;
solvePnP(pts_3d,pts_2d,K,Mat(),r,t,false,cv::SOLVEPNP_EPNP);
Mat R;
Rodrigues(r, R);

二、目标是求得相机在世界坐标系下的3D坐标。

1、法一:

利用求得的旋转矩阵和平移矩阵:

Pcam代表物体在相机坐标系下的坐标,Pworld代表物体在世界坐标系下的坐标,R和T代表了将点的从世界坐标系下映射到相机坐标系下,可以知道solvePnP求出的刚好是这样的映射关系。
使Pcam = 0,则意味着物体移到了相机坐标系的原点,求出来的Pworld代表了相机在世界坐标系中的位置,P的z轴坐标就是深度信息。
0=RPworld+T
P = -inverse(R)*T

代码为:

	//Mat到Eigen格式转换
	Eigen::Matrix3f R_n;
	Eigen::Vector3f T_n;
	cv2eigen(rotMat, R_n);
	cv2eigen(tvec, T_n);
	Eigen::Vector3f P_oc;

	P_oc = -R_n.inverse()*T_n;
	cout << "世界坐标" << P_oc << std::endl;

其中cv2eigen需要头文件添加:(顺序不能错!!!先包含eigen相关库,再包含opencv库!)

#include <Eigen/Core>

#include <opencv2/core/eigen.hpp>


2、法二:

1、根据旋转矩阵求出坐标旋转角。

	double r11 = rotM.ptr<double>(0)[0];
	double r12 = rotM.ptr<double>(0)[1];
	double r13 = rotM.ptr<double>(0)[2];
	double r21 = rotM.ptr<double>(1)[0];
	double r22 = rotM.ptr<double>(1)[1];
	double r23 = rotM.ptr<double>(1)[2];
	double r31 = rotM.ptr<double>(2)[0];
	double r32 = rotM.ptr<double>(2)[1];
	double r33 = rotM.ptr<double>(2)[2];
/*************************************此处计算出相机的旋转角**********************************************/
	//计算出相机坐标系的三轴旋转欧拉角,旋转后可以转出世界坐标系。
	//旋转顺序为z、y、x
	//原理见帖子:
	double thetaz = atan2(r21, r11) / CV_PI * 180;
	double thetay = atan2(-1 * r31, sqrt(r32*r32 + r33*r33)) / CV_PI * 180;
	double thetax = atan2(r32, r33) / CV_PI * 180;

2、平移矩阵反向旋转得到相机在世界坐标系下坐标。

	/*************************************此处计算出相机坐标系原点Oc在世界坐标系中的位置**********************************************/
	/* 当原始坐标系经过旋转z、y、x三次旋转后,会与世界坐标系完全平行,而三次旋转中向量OcOw会跟着旋转 */
	/* 而我们想知道的是两个坐标系完全平行时,OcOw的值 */
	/* 因此,原始坐标系每次旋转完成后,对向量OcOw进行一次反相旋转,最终可以得到两个坐标系完全平行时的OcOw */
	/* 该向量乘以-1就是世界坐标系下相机的坐标 */
	/***********************************************************************************/

	//提出平移矩阵,表示从相机坐标系原点,跟着向量(x,y,z)走,就到了世界坐标系原点
	double tx = tvec.ptr<double>(0)[0];
	double ty = tvec.ptr<double>(0)[1];
	double tz = tvec.ptr<double>(0)[2];

	//x y z 为唯一向量在相机原始坐标系下的向量值
	//也就是向量OcOw在相机坐标系下的值
	double x = tx, y = ty, z = tz;

	//进行三次反向旋转
	codeRotateByZ(x, y, -1 * thetaz, x, y);
	codeRotateByY(x, z, -1 * thetay, x, z);
	codeRotateByX(y, z, -1 * thetax, y, z);


	//获得相机在世界坐标系下的位置坐标
	//即向量OcOw在世界坐标系下的值
	double Cx = x*-1;
	double Cy = y*-1;
	double Cz = z*-1;

重点参考:

http://www.cnblogs.com/singlex/p/pose_estimation_1.html



3、法1附录源代码(可运行通过)

#include <stdio.h>
#include <tchar.h>
#include <opencv2\opencv.hpp>
#include <math.h>
#include <iostream>
#include <fstream>
#include <Eigen/Core>
#include <Eigen/LU>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/core/eigen.hpp>
#include <Eigen/Dense>
using namespace std;
using namespace cv;



int main()
{

	/****************a6000参数**********************/
	//初始化相机参数Opencv
	double camD[9] = {
		6800.7, 0, 3065.8,
		0, 6798.1, 1667.6,
		0, 0, 1 };
	cv::Mat camera_matrix = cv::Mat(3, 3, CV_64FC1, camD);

	//畸变参数
	double distCoeffD[5] = { -0.189314, 0.444657, -0.00116176, 0.00164877, -2.57547 };
	cv::Mat distortion_coefficients = cv::Mat(5, 1, CV_64FC1, distCoeffD);

	//P1-P4为XOY面上的共面点其Z坐标为0,P5的Z坐标不为0

	//测试用图1 DSC03321
	vector<cv::Point2f> Points2D;
	Points2D.push_back(cv::Point2f(2985, 1688));	//P1
	Points2D.push_back(cv::Point2f(5081, 1690));	//P2
	Points2D.push_back(cv::Point2f(2997, 2797));	//P3
	//Points2D.push_back(cv::Point2f(5544, 2757));	//P4
	Points2D.push_back(cv::Point2f(4148, 673));	//P5

	//特征点世界坐标
	vector<cv::Point3f> Points3D;
	Points3D.push_back(cv::Point3f(0, 0, 0));		//P1 三维坐标的单位是毫米
	Points3D.push_back(cv::Point3f(0, 200, 0));		//P2
	Points3D.push_back(cv::Point3f(150, 0, 0));		//P3
	//Points3D.push_back(cv::Point3f(150, 200, 0));	//P4
	Points3D.push_back(cv::Point3f(0, 100, 105));	//P5

	//初始化输出矩阵
	cv::Mat rvec;
	cv::Mat tvec;

	//三种方法求解
	//solvePnP(Points3D, Points2D, camera_matrix, distortion_coefficients, rvec, tvec, false, CV_ITERATIVE);	//实测迭代法似乎只能用4个共面特征点求解,5个点或非共面4点解不出正确的解
	solvePnP(Points3D, Points2D, camera_matrix, distortion_coefficients, rvec, tvec, false, CV_P3P);			//Gao的方法可以使用任意四个特征点,特征点数量不能少于4也不能多于4
	//solvePnP(Points3D, Points2D, camera_matrix, distortion_coefficients, rvec, tvec, false, CV_EPNP);			//该方法可以用于N点位姿估计

	cv::Mat rotMat;
	cv::Rodrigues(rvec, rotMat);  //由于solvePnP返回的是旋转向量,故用罗德里格斯变换变成旋转矩阵

	//Mat到Eigen格式转换
	Eigen::Matrix3f R_n;
	Eigen::Vector3f T_n;
	cv2eigen(rotMat, R_n);
	cv2eigen(tvec, T_n);
	Eigen::Vector3f P_oc;

	P_oc = -R_n.inverse()*T_n;
	cout << "世界坐标" << P_oc << std::endl;


	return 0;
}

 

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

通过solvePnP求解相机位置 (代码可运行) 的相关文章

  • Unity 3D网页游戏 Demo 展示

    2011 年 xff0c 网页 3D 这一网游开发新趋势逐渐浮出水面 xff0c Unity 作为浏览器及移动设备 3D 引擎领域的佼佼者 xff0c 在国内开始崭露头角 我们团队也完成了首款 Unity Demo 的第一个版本 Demo
  • 用Ogre实现无缝地图

    用 Ogre 实现无缝地图 1 7 版本之前 xff0c 如果想用 Ogre 内建的地形系统实现一个像样的无缝地图 xff0c 恐怕要闹到抓狂 所幸 sinbad 在 1 7 为 Ogre 加入了全新的地形组件 xff0c 它囊括了一个地形
  • 一劳永逸地解决寻路问题

    一劳永逸地解决寻路问题 作者 xff1a PaulT 译者 xff1a trcj 原文 xff1a http www ai blog net archives 000152 html 通常我都会尽量避免对业内游戏产品或开发者们评头论足 但这
  • 口吐莲花

    久不更新blog xff0c 优狗 进展尚可 xff0c 新项目又开 xff0c 忙里偷闲想写点东西 xff0c 一时竟无从下笔 xff0c 以往那种花几天甚至几星期整理一篇技术文章的机会恐怕越来越少了 六月份 优狗 团队新入数名成员 xf
  • Unity3D运行时刻资源管理

    Unity运行时刻资源管理 Asset Bundles 制作 xff1a BuildPipeline BuildAssetBundle 加载 xff1a AssetBundle Load 卸载 xff1a AssetBundle Unloa
  • Unity3D页游《坦克英雄》发布!

    坦克英雄 是一款主打PVP的射击类3D竞技页游 xff0c 它基于Unity引擎 xff0c 以二战坦克为题材 xff0c 既保留了射击类游戏的操作性 xff0c 又缓和了其与页游载体看似相悖的剧烈节奏 xff0c 目前游戏的核心玩法及主体
  • 给我时间

    Jenifer Tell 39 ment de gens veulent Tell 39 ment tre aim s Pour se donner peuvent Tout abandonner Tellement d 39 erreur
  • Docker 查看Image镜像的Dockerfile方法

    Dokcer中使用的Image镜像可能别人写好 xff0c 我们下载来直接使用 xff0c 但有些情况可能不能满足我们的需求 xff0c 那就需要修改镜像 xff0c 一般可以通过在容器中修改 xff0c 之后在生成镜像 xff0c 但有时
  • (三)ROS上位机与stm32进行串口通信

    ROS上位机与stm32进行串口通信 1 1 ROS发送数据1 2 stm32接收数据2 1 stm32发送数据2 2 ROS接收数据上位机串口初始化文件代码下位机stm32的串口配置代码 总代码在文末 xff0c 需要完整的工程文件可以留
  • 智能车摄像头算法——寻线

    寻线 1 灰度图像二值化2 找边线3 获得中线 1 灰度图像二值化 如果使用的是小钻风摄像 xff08 二值化摄像头 xff09 xff0c 就不用再进行软件二值化 使用灰度摄像头 xff0c 就需要这步 以下展示常用的大津法 xff08
  • 【Vue】在vue中命名的时候会遇到 component name “index“ should always be multi-word的解决方案

    Vue 在vue中命名的时候会遇到 component name index should always be multi word的解决方案 文章目录 Vue 在vue中命名的时候会遇到 component name 34 index 3
  • docker容器和镜像的停止和删除

    文章目录 docker容器和镜像的停止和删除1 列出所有docker镜像2 查看正在运行的 或所有的docker容器3 停止所有容器4 删除所有容器5 删除所有镜像通过 image name 删除单个镜像通过 image id 删除单个镜像
  • Windows上应用Docker容器技术的动态代码测试

    转载自维克多汽车技术 xff08 上海 xff09 有限公司 xff0c 作者Vector China 随着软件项目复杂度的提升和不可控的团队资源变更 xff0c 研发组织对DevOps部署的灵活性 可快速迁移和适配CI CD的迭代提出了更
  • 写学术论文的一些感想

    我自己写得是真差 xff01 虽然和我英语程度低有一定的关系 xff0c 最重要的是没有这个基础的底蕴和不明白自己做的东西的意义 所以来总结一下关于学术论文的想法 1 最基础的 xff0c 最重要的 xff0c 你要做出东西来 xff0c
  • kvaser怎么用?Kvaser 汽车CAN通讯协议总线分析仪新手入门常见问题解决方案教程

    logo png 1 驱动安装问题 答 xff1a 驱动程序安装问题通常是由防病毒软件引起的 在驱动程序安装期间 xff0c 常见问题是无法安装枚举服务 解决方案 xff1a 确保您的防病毒软件已关闭 xff0c 然后再次安装驱动程序 2
  • 图解git使用

    1 基本用法 上面的四条命令在工作目录 暂存目录 也叫做索引 和仓库之间复制文件 git add em files em 把当前文件放入暂存区域 对比stage和working dir xff0c 如果有改变就增加 xff1b 如果没有改变
  • Docker容器 - DockerFile详解

    目录 DockerFile 一 是什么 二 构建步骤 DockerFile构建过程 一 DockerFile基础 二 Docker执行DockerFile的流程 三 总结 DockerFile常用保留字 零 参考Tomcat的DockerF
  • Docker网络 - docker network详解

    目录 是什么 一 Docker不启动时默认的网络情况 二 Docker启动时的网络情况 能干什么 常用基本命令 一 ls 1 no trunc 2 DRIVER 3 ID 4 format 二 create 三 rm 四 inspect 五
  • 时间划过的伤痕叫成长

    我要用代码敲出整个世界 也许刚看这句话的时候 很多人都嗤之以鼻 太自大太高傲了 但这是我梦想也是我目标 我出生在一个小县城的普通家庭里 经济状况也只能解决温饱 上高中的时候我就没想着要读大学 我很贪玩 几乎都是和一群 34 狐朋狗友 34
  • CMD终端中一些常用的快捷键

    1 使用键盘上的 xff0c 可以快速定位到上一次执行的命令 2 使用键盘上的tab键 xff0c 可以快速补全路径 3 使用键盘上的esc键 xff0c 能够快速清空当前已经输入的命令 4 输出cls命令 xff0c 可以清空终端

随机推荐