相机位姿求解——P3P问题
1、位姿求解是计算机视觉中经常遇到的,Perspective-n-Points, PnP(P3P)提供了一种解决方案,它是一种由3D-2D的位姿求解方式,即需要已知匹配的3D点和图像2D点。目前遇到的场景主要有两个,其一是求解相机相对于某2维图像/3维物体的位姿,具体的如AR应用,人脸跟踪等;其二就是SLAM算法中估计相机位姿时通常需要PnP给出相机初始位姿。
这里要说明的是在场景1中,我们通常输入的是物体在世界坐标系下的3D点以及这些3D点在图像上投影的2D点,因此求得的是相机(相机坐标系)相对于真实物体(世界坐标系)的位姿,如图所示:
而在场景2中,通常输入的是上一帧中的3D点(在上一帧的相机坐标系下表示的点)和这些3D点在当前帧中的投影得到的2D点,所以它求得的是当前帧相对于上一帧的位姿变换,如图所示:
两种情况本质上是相同的,都是基于已知3D点和对应的图像2D点求解相机运动的过程。下面详细探讨P3P的求解过程。
2、我们首先需要知道的是P3P并不是直接根据3D-2D点求出相机位姿矩阵,而是先求出对应的2D点在当前相机坐标系下的3D坐标,然后根据世界坐标系下的3D坐标和当前相机坐标系下的3D坐标求解相机位姿的。P3P的求解是从余弦定理开始的,设相机坐标中心为点P,A、B、C为不共线的三个3D点,D为验证3D点,根据余弦定理有如下公式:
接下来其实是对上述3个式子消元化简的过程,同时除以
,
并且使得
,
则可得:
然后再次进行替换,另:
,
可得:
将第一个式子代入第2,3式,可以化简得到:
接下来的过程就是如何通过上述两个式子求解A,B,C在当前相机坐标系下的坐标。首先需要明确的是哪些量是已知量,输入的是3D-2D的坐标,也即
都是已知的。因为首先AB,BC,AC的距离都是可以根据输入的3D点求得,而输入的2D点可以求解三个余弦值(如何求解,像素坐标根据相机内参矩阵和畸变参数可以求得在归一化图像平面上的3D坐标,此时 z=1,故余弦值可求)。此时未知数仅x,y两个,所以理论上两个未知数两个方程,是可求的。(从x,y求PA,PB,PC也可求)
3、具体的求解过程:
3.1、首先是根据2D坐标求解余弦值得过程,首先是由像素坐标到归一化图像坐标的转变,根据就是相机模型
然后是L2归一化的过程,我们知道求解角度的时候用的是归一化坐标(此归一化非彼归一化,上面是归一化到z值等于1的平面上,这里讲的是数学上的归一化)
有了上述值就可以求解余弦值了
同理可求。
3.2、根据3D坐标求解AB,AC,BC的值,以AB为例
AC,BC同理可求,所以v,w也可以求解。
3.3、接下来就是一个二元二次方程的求解,比较难求,但是这在数学上是可以求解的,需要用到Wu Ritt的零点分解方法,它可以将原方程等效成一组特征列(Characteristic Serial, CS),凡是原方程组的解都会是CS的解,但是CS的解不一定是原方程的解,所以需要验证,这里的等效方程为:
其中的未知数a1~a4都是已知的,因为原方程的系数是已知的,后文有系数附录,因此我们可以求得x,y的值,4次方程组理论上有4组解,但其实只有一组是合适的。
3.4、求得了x,y的值,就可以求取PA,PB,PC的值,根据下面的公式,AB已知,可以先求PC,然后分别求解PB,PA:
但是我们需要的是A,B,C在相机坐标系下的坐标,而不是PA,PB,PC的长度,所以还需根据长度求取点的坐标,求解方法是用向量公式:
其中a是单位向量,||PA||是模值,所得即A在相机坐标系下的坐标。
最后求得了A,B,C的坐标就可以通过世界坐标系到当前相机坐标的变换求解相机位姿,注意上面求得了4组解,这里需要使用D点确认哪组解是最合适的。
4、代码对应:看看上述过程是如何代码实现的
- //像素坐标转变为归一化图像坐标;
- mu0 = inv_fx * mu0 - cx_fx;
- mv0 = inv_fy * mv0 - cy_fy;
- //归一化图像坐标归一化
- norm = sqrt(mu0 * mu0 + mv0 * mv0 + );
- mk0 = . / norm; mu0 *= mk0; mv0 *= mk0;
- mu1 = inv_fx * mu1 - cx_fx;
- mv1 = inv_fy * mv1 - cy_fy;
- norm = sqrt(mu1 * mu1 + mv1 * mv1 + );
- mk1 = . / norm; mu1 *= mk1; mv1 *= mk1;
- mu2 = inv_fx * mu2 - cx_fx;
- mv2 = inv_fy * mv2 - cy_fy;
- norm = sqrt(mu2 * mu2 + mv2 * mv2 + );
- mk2 = . / norm; mu2 *= mk2; mv2 *= mk2;
- //世界坐标系中,ABC三点的距离;
- double distances[];
- distances[] = sqrt( (X1 - X2) * (X1 - X2) + (Y1 - Y2) * (Y1 - Y2) + (Z1 - Z2) * (Z1 - Z2) );
- distances[] = sqrt( (X0 - X2) * (X0 - X2) + (Y0 - Y2) * (Y0 - Y2) + (Z0 - Z2) * (Z0 - Z2) );
- distances[] = sqrt( (X0 - X1) * (X0 - X1) + (Y0 - Y1) * (Y0 - Y1) + (Z0 - Z1) * (Z0 - Z1) );
- //三点之间的角度值;
- // Calculate angles
- double cosines[];
- cosines[] = mu1 * mu2 + mv1 * mv2 + mk1 * mk2;
- cosines[] = mu0 * mu2 + mv0 * mv2 + mk0 * mk2;
- cosines[] = mu0 * mu1 + mv0 * mv1 + mk0 * mk1;
- //吴消元法求解PA,PB,PC的值,有四组解;
- double lengths[][];
- int n = solve_for_lengths(lengths, distances, cosines);
- int nb_solutions = ;
- for(int i = ; i < n; i++) {
- double M_orig[][];
- //对每个点求坐标值,单位向量乘以距离;
- M_orig[][] = lengths[i][] * mu0;
- M_orig[][] = lengths[i][] * mv0;
- M_orig[][] = lengths[i][] * mk0;
- M_orig[][] = lengths[i][] * mu1;
- M_orig[][] = lengths[i][] * mv1;
- M_orig[][] = lengths[i][] * mk1;
- M_orig[][] = lengths[i][] * mu2;
- M_orig[][] = lengths[i][] * mv2;
- M_orig[][] = lengths[i][] * mk2;
- //计算每个解对应的位姿矩阵R,t
- if (!align(M_orig, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2, R[nb_solutions], t[nb_solutions]))
- continue;
- nb_solutions++;
- }
这里面主要是使用吴消元法求解PA,PB,PC的距离
- int p3p::solve_for_lengths(double lengths[][], double distances[], double cosines[])
- {
- //吴消元法,数据准备
- double p = cosines[] * ;
- double q = cosines[] * ;
- double r = cosines[] * ;
- double inv_d22 = . / (distances[] * distances[]);
- double a = inv_d22 * (distances[] * distances[]);
- double b = inv_d22 * (distances[] * distances[]);
- double a2 = a * a, b2 = b * b, p2 = p * p, q2 = q * q, r2 = r * r;
- double pr = p * r, pqr = q * pr;
- // Check reality condition (the four points should not be coplanar)
- if (p2 + q2 + r2 - pqr - == )
- return ;
- double ab = a * b, a_2 = *a;
- double A = - * b + b2 + a2 + + ab*( - r2) - a_2;
- //A, B, C, D, E 为四次多项式的系数;
- // Check reality condition
- if (A == ) return ;
- double a_4 = *a;
- double B = q*(-*(ab + a2 + - b) + r2*ab + a_4) + pr*(b - b2 + ab);
- double C = q2 + b2*(r2 + p2 - ) - b*(p2 + pqr) - ab*(r2 + pqr) + (a2 - a_2)*( + q2) + ;
- double D = pr*(ab-b2+b) + q*((p2-)*b + * (ab - a2) + a_4 - );
- double E = + *(b - a - ab) + b2 - b*p2 + a2;
- double temp = (p2*(a-+b) + r2*(a--b) + pqr - a*pqr);
- double b0 = b * temp * temp;
- // Check reality condition
- if (b0 == )
- return ;
- //求解四次多项式;
- double real_roots[];
- int n = solve_deg4(A, B, C, D, E, real_roots[], real_roots[], real_roots[], real_roots[]);
- if (n == )
- return ;
- int nb_solutions = ;
- double r3 = r2*r, pr2 = p*r2, r3q = r3 * q;
- double inv_b0 = . / b0;
- // For each solution of x
- for(int i = ; i < n; i++) {
- double x = real_roots[i];
- // Check reality condition
- if (x <= )
- continue;
- double x2 = x*x;
- //对应附录中的b1
- double b1 =
- ((-a-b)*x2 + (q*a-q)*x + - a + b) *
- (((r3*(a2 + ab*( - r2) - a_2 + b2 - *b + )) * x +
- (r3q*(*(b-a2) + a_4 + ab*(r2 - ) - ) + pr2*( + a2 + *(ab-a-b) + r2*(b - b2) + b2))) * x2 +
- (r3*(q2*(-*a+a2) + r2*(b2-ab) - a_4 + *(a2 - b2) + ) + r*p2*(b2 + *(ab - b - a) + + a2) + pr2*q*(a_4 + *(b - ab - a2) - - r2*b)) * x +
- *r3q*(a_2 - b - a2 + ab - ) + pr2*(q2 - a_4 + *(a2 - b2) + r2*b + q2*(a2 - a_2) + ) +
- p2*(p*(*(ab - a - b) + a2 + b2 + ) + *q*r*(b + a_2 - a2 - ab - )));
- // Check reality condition
- if (b1 <= )
- continue;
- double y = inv_b0 * b1;
- double v = x2 + y*y - x*y*r;
- if (v <= )
- continue;
- double Z = distances[] / sqrt(v);
- double X = x * Z;
- double Y = y * Z;
- lengths[nb_solutions][] = X;
- lengths[nb_solutions][] = Y;
- lengths[nb_solutions][] = Z;
- nb_solutions++;
- }
- return nb_solutions;
- }
看看是如何从4组解中选择合适的解的:
- int ns = ;
- double min_reproj = ;
- for(int i = ; i < n; i++) {
- double X3p = Rs[i][][] * X3 + Rs[i][][] * Y3 + Rs[i][][] * Z3 + ts[i][];
- double Y3p = Rs[i][][] * X3 + Rs[i][][] * Y3 + Rs[i][][] * Z3 + ts[i][];
- double Z3p = Rs[i][][] * X3 + Rs[i][][] * Y3 + Rs[i][][] * Z3 + ts[i][];
- double mu3p = cx + fx * X3p / Z3p;
- double mv3p = cy + fy * Y3p / Z3p;
- //通过R,t计算第4个点的重投影误差选择合理的解
- double reproj = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
- //选择重投影误差最小的解
- if (i == || min_reproj > reproj) {
- ns = i;
- min_reproj = reproj;
- }
- }
大概就酱。
附:吴消元法求解系数
,
,
参考:http://iplimage.com/blog/p3p-perspective-point-overview/#Appendix
相机位姿求解——P3P问题的更多相关文章
- 相机位姿估计1_1:OpenCV:solvePnP二次封装与性能测试
关键词:OpenCV::solvePnP 文章类型:方法封装.测试 @Author:VShawn(singlex@foxmail.com) @Date:2016-11-27 @Lab: CvLab20 ...
- 相机位姿估计0:基本原理之如何解PNP问题
关键词:相机位姿估计 PNP问题求解 用途:各种位姿估计 文章类型:原理 @Author:VShawn(singlex@foxmail.com) @Date:2016-11-18 @Lab: CvLa ...
- python+opencv2相机位姿估计
最近在做基于图像的室内定位方面的研究,于是使用到了百度最新的室内数据库Image-based Localization (IBL) .由于该数据库给出的数据是每幅图像和其对应相机的内外参数和光心投影方 ...
- Kinect相机位姿
可以直接得到吧 还是要反求 pose.txt 里面一共有5个七参数.正好对应5幅图片.
- COLMAP简易教程(命令行模式)
完整的 multi view stereo pipeline 会有以下步骤 structure from motion(SfM)==> camera parameters, sparse poi ...
- 【opencv】 solvepnp 和 solvepnpRansac 求解 【空间三维坐标系 到 图像二维坐标系】的 三维旋转R 和 三维平移 T 【opencv2使用solvepnp求解rt不准的问题】
参考: pnp问题 与 solvepnp函数:https://www.jianshu.com/p/b97406d8833c 对图片进行二维仿射变换cv2.warpAffine() or 对图片进行二维 ...
- g2o求解BA 第10章
1.g2o_bal_class.h1.1 projection.hg2o还是用图模型和边,顶点就是相机和路标,边就是观测,就是像素坐标.只不过这里的相机是由旋转(3个参数,轴角形式,就是theta*n ...
- 【视频开发】【计算机视觉】相机标定(Camera calibration)原理、步骤
相机标定(Camera calibration)原理.步骤 author@jason_ql(lql0716) http://blog.csdn.net/lql0716 在图像测量过程以及机器视觉应用 ...
- 相机IMU融合四部曲(三):MSF详细解读与使用
相机IMU融合四部曲(三):MSF详细解读与使用 极品巧克力 前言 通过前两篇文章,<D-LG-EKF详细解读>和<误差状态四元数详细解读>,已经把相机和IMU融合的理论全部都 ...
随机推荐
- 程序员的算法课(6)-最长公共子序列(LCS)
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/de ...
- C语言I作业08
C语言I作业08 这个作业属于哪个课程 C语言程序设计ll 这个作业的要求在哪里 https://edu.cnblogs.com/campus/zswxy/SE2019-2/homework/9981 ...
- 申请SSL证书
1.为什么需要申请SSL证书呢? 因为之前公司网站是通过http访问的,现在要通过https方式访问,前面多了一个s,那就需要SSL证书,用https方式访问的,会加密用户上传和下载的数据,使访问更加 ...
- Linux命令行初学(一)
linux命令大全:https://www.linuxcool.com/ 大概了解到有哪些命令,如果有需要的话可以在该网站上查询. 另外在实验楼学习了一些基础,该篇博客就此次对linux命令行的学习进 ...
- 使用cookies弹出层每24小时弹出一次
第一步:下载cookies的库 https://github.com/js-cookie/js-cookie 第二步:设置Cookies的失效时间,这里有两种方法,按天计算和按小时计算 functio ...
- 使用Python编写打字训练小程序【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/devcloud/article/detail ...
- Python异常处理与上下文管理器
Python异常处理 异常与错误 错误 可以通过IDE或者解释器给出提示的错误opentxt('a.jpg','r') 语法层面没有问题,但是自己代码的逻辑有问题if age>18: print ...
- 第七章终结篇——8251A的总结
总算把这个第七章复习完了,我把剩下一点关于8251A的发上来吧 本来在讲解8251A书本上还有关于RS232和串口通信的讲解,但是太浅了,就不放了,有兴趣的朋友可以自行参考其他文章 串行通信芯片825 ...
- 用JS实现HTML转PDF
遇到这个需求,现把实现代码整理出来,方便大家参考 <!-- html转PDF --> <script src="https://cdnjs.cloudflare.com/a ...
- ARTS-S ansible-playbook
文件a.yml --- - hosts: cluster remote_user: ksotest gather_facts: false tasks: - name: delete dir if e ...