不同PNP求解算法性能测试

关键词:OpenCV::solvePnP

文章类型:方法封装、测试

@Author:VShawn([email protected])

@Date:2016-11-27

@Lab: CvLab202@CSU

前言

今天给大家带来的是一篇关于程序功能、性能测试的文章,读过《相机位姿估计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/

本文版权归作者所有,欢迎转载,但未经博客作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。



你可能感兴趣的:(图像处理)