不同PNP求解算法性能测试
关键词:OpenCV::solvePnP
文章类型:方法封装、测试
@Author:VShawn([email protected])
@Date:2016-11-27
@Lab: [email protected]
前言
今天给大家带来的是一篇关于程序功能、性能测试的文章,读过《相机位姿估计1:根据四个特征点估计相机姿态》一文的同学应该会发现,直接使用OpenCV的solvePnP来估计相机位姿,在程序调用上相当麻烦,从一开始的参数设定到最后将计算出的矩阵转化为相机的位姿参数,需要花费近两百行代码。因此为了更方便地调用程序,今天我就给大家带来一个我自己对solvePnP的封装类PNPSolver,顺便将OpenCV自带的三种求解方法测试一遍。
类的封装
封装的思路我就不写了,由于博客更新速度赶不上我写程序的速度,现在发上来的类已经修改过好几次了,思路也换了几次。不过大的方向没变,目的就是只需要输入参数,输入坐标点后直接可以得到相机在世界坐标系的坐标。
类的调用顺序:
1.初始化PNPSolver类;
2.调用SetCameraMatrix(),SetDistortionCoefficients()设置好相机内参数与镜头畸变参数;
3.向Points3D,Points2D中添加一一对应的特征点对;
4.调用Solve()方法运行计算;
5.从属性Theta_C2W中提取旋转角,从Position_OcInW中提取出相机在世界坐标系下的坐标。
一个典型的调用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
//初始化PNPSolver类
PNPSolver
p4psolver;
//初始化相机参数
p4psolver.SetCameraMatrix(fx,
fy, u0, v0);
//设置畸变参数
p4psolver.SetDistortionCoefficients(k1,
k2, p1, p2, k3);
//设置特征点的世界坐标
p4psolver.Points3D.push_back(cv::Point3f(0,
0, 0)); //P1三维坐标的单位是毫米
p4psolver.Points3D.push_back(cv::Point3f(0,
200, 0)); //P2
p4psolver.Points3D.push_back(cv::Point3f(150,
0, 0)); //P3
//p4psolver.Points3D.push_back(cv::Point3f(150,
200, 0)); //P4
p4psolver.Points3D.push_back(cv::Point3f(0,
100, 105)); //P5
cout
<< "test2:特征点世界坐标
= " <<
endl << p4psolver.Points3D << endl;
//设置特征点的图像坐标
p4psolver.Points2D.push_back(cv::Point2f(2985,
1688)); //P1
p4psolver.Points2D.push_back(cv::Point2f(5081,
1690)); //P2
p4psolver.Points2D.push_back(cv::Point2f(2997,
2797)); //P3
//p4psolver.Points2D.push_back(cv::Point2f(5544,
2757)); //P4
p4psolver.Points2D.push_back(cv::Point2f(4148,
673)); //P5
cout
<< "test2:图中特征点坐标
= " <<
endl << p4psolver.Points2D << endl;
if (p4psolver.Solve(PNPSolver::METHOD::CV_P3P)
== 0)
cout
<< "test2:CV_P3P方法:
相机位姿→" << "Oc坐标=" <<
p4psolver.Position_OcInW << "
相机旋转=" <<
p4psolver.Theta_W2C << endl;
if (p4psolver.Solve(PNPSolver::METHOD::CV_ITERATIVE)
== 0)
cout
<< "test2:CV_ITERATIVE方法:
相机位姿→" << "Oc坐标=" <<
p4psolver.Position_OcInW << "
相机旋转=" <<
p4psolver.Theta_W2C << endl;
if (p4psolver.Solve(PNPSolver::METHOD::CV_EPNP)
== 0)
cout
<< "test2:CV_EPNP方法:
相机位姿→" << "Oc坐标=" <<
p4psolver.Position_OcInW << "
相机旋转=" <<
p4psolver.Theta_W2C << endl;
|
方法测试
OpenCV提供了三种方法进行PNP计算,三种方法具体怎么计算的就请各位自己查询opencv documentation以及相关的论文了,我看了个大概然后结合自己实际的测试情况给出一个结论,不一定正确,仅供参考:
方法名 |
说明 |
测试结论 |
CV_P3P |
这个方法使用非常经典的Gao方法解P3P问题,求出4组可能的解,再通过对第四个点的重投影,返回重投影误差最小的点。 论文《Complete Solution Classification for the Perspective-Three-Point Problem》 |
可以使用任意4个特征点求解,不要共面,特征点数量不为4时报错 |
CV_ITERATIVE |
该方法基于Levenberg-Marquardt optimization迭代求解PNP问题,实质是迭代求出重投影误差最小的解,这个解显然不一定是正解。 实测该方法只有用4个共面的特征点时才能求出正确的解,使用5个特征点或4点非共面的特征点都得不到正确的位姿。
|
只能用4个共面的特征点来解位姿 |
CV_EPNP |
该方法使用EfficientPNP方法求解问题,具体怎么做的当时网速不好我没下载到论文,后面又懒得去看了。 论文《EPnP: Efficient Perspective-n-Point Camera Pose Estimation》 |
对于N个特征点,只要N>3就能够求出正解。 |
测试截图:
1.使用四个共面的特征点,显然三种方法都能得到正解,但相互之间略有误差。
2使用四个非共面的特征点,CV_ITERATIVE方法解错了。
3.使用5个特征点求解,只有CV_EPNP能够用
性能测试
最后对三种方法的性能进行测试,通过对test1重复执行1000次获得算法的运行时间,从结果可以看出迭代法显然是最慢的,Gao的P3P+重投影法用时最少,EPNP法其次。
总结
综合以上的测试,推荐使用CV_P3P来解决实际问题,该方法对于有四个特征点的情况限制少、运算速度快。当特征点数大于4时,可以取多组4特征点计算出结果再求平均值,或者为了简单点就直接使用CV_EPNP法。
https://github.com/vshawn/Shawn_pose_estimation_by_opencv/tree/master/%E7%9B%B8%E6%9C%BA%E4%BD%8D%E5%A7%BF%E4%BC%B0%E8%AE%A11_1%EF%BC%9AsolvePnP%E4%BA%8C%E6%AC%A1%E5%B0%81%E8%A3%85%E4%B8%8E%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95
作者:VShawn
出处:http://www.cnblogs.com/singlex/
本文版权归作者所有,欢迎转载,但未经博客作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。