pnp估计相机的姿态

上一节我们使用张正友相机标定法获得了相机内参,这一节我们使用 PnP (Perspective-n-Point)算法估计相机初始姿态并更新之。推荐3篇我学习的博客:【姿态估计】Pose estimation algorithm 之 Robust Planar Pose (RPP)algorithmPOSIT算法的原理--opencv 3D姿态估计三维姿态:关于solvePnP与cvPOSIT


2016/6/20

关于PnP问题我会重新写一篇博客,讲一下概念,最少需要几组对应的3D/2D点,

3D点共面时怎么处理,PnP有哪些主流解法,以及会更新一篇G2O的PnP解法。

/


注意点1:solvePnP里有三种解法:P3P, EPnP,迭代法(默认);opencv2里参数分别为CV_P3P,CV_EPNP,CV_ITERATIVE (opencv3里多了DLS和UPnP解法)。

注意点2:solvePnP需要至少3组点:P3P只使用4组点,3组求出多个解,第四组确定最优解;EPnP使用大于等于3组点;迭代法调用cvFindExtrinsicCameraParams2,进而使用SVD分解并调用cvFindHomography,而cvFindHomography需要至少4组点。


接下来我们使用OpenCV实现相机姿态更新:

上一节得到的相机内参和相机畸变:

  1. double camD[9] = {618.526381968738, 0, 310.8963715614199,  
  2.                     0, 619.4548980786033, 248.6374860176724,  
  3.                     0, 0, 1};  
  4. double distCoeffD[5] = {0.09367405350511771, -0.08731677320554751, 0.002823563134787144, -1.246739177460954e-005, -0.0469061739387372};  
  5. Mat camera_matrix = Mat(3,3,CV_64FC1,camD);  
  6. Mat distortion_coefficients = Mat(5,1,CV_64FC1,distCoeffD);  

首先检测ORB角点并亚像素化:

  1. cap >> frame;  
  2. if( frame.empty() )  
  3.     break;  
  4.   
  5. frame.copyTo(image);  
  6. if(needToGetgf)  
  7. {  
  8.     cvtColor(image, gray, COLOR_BGR2GRAY);  
  9.   
  10.     // automatic initialization  
  11.     orb.detect(gray, keypoints);  
  12.     goodfeatures.clear();  
  13.     forsize_t i = 0; i < keypoints.size(); i++ ) {  
  14.         goodfeatures.push_back(keypoints[i].pt);  
  15.     }  
  16.     cornerSubPix(gray, goodfeatures, subPixWinSize, Size(-1,-1), termcrit);  
  17.     for(size_t i = 0; i < goodfeatures.size(); i++ )  
  18.     {  
  19.         circle( image, goodfeatures[i], 3, Scalar(0,255,0), -1, 8);  
  20.     }  
  21. }  
使用鼠标选定4个2D点(按正方形左上顶点开始顺时针),然后查找所选点附近的角点,若找到则压入跟踪点集合:
  1. void on_mouse(int event,int x,int y,int flag, void *param)  
  2. {  
  3.     if(event==CV_EVENT_LBUTTONDOWN)  
  4.     {  
  5.         if(needtomap && points[1].size()<4)  
  6.         {  
  7.             for(size_t i = 0;i<goodfeatures.size();i++)  
  8.             {  
  9.                 if(abs(goodfeatures[i].x-x)+abs(goodfeatures[i].y-y)<3)  
  10.                 {  
  11.                     points[1].push_back(goodfeatures[i]);  
  12.                     trackingpoints++;  
  13.                     break;  
  14.                 }  
  15.             }  
  16.         }     
  17.     }  
  18. }  
建立与2D跟踪点集合相对应的3D空间点集合:
  1. objP.push_back(Point3f(0,0,0));    //三维坐标的单位是毫米  
  2. objP.push_back(Point3f(5,0,0));  
  3. objP.push_back(Point3f(5,5,0));  
  4. objP.push_back(Point3f(0,5,0));  
  5. Mat(objP).convertTo(objPM,CV_32F);  
使用LK光流法跟踪已选定角点:
  1. vector<uchar> status;  
  2. vector<float> err;  
  3. if(prevGray.empty())  
  4.     gray.copyTo(prevGray);  
  5. calcOpticalFlowPyrLK(prevGray, gray, points[0], points[1], status, err);  
  6. size_t i,k;  
  7. for(i = k = 0; i < points[1].size(); i++ )  
  8. {  
  9.     if( !status[i] )  
  10.         continue;  
  11.     points[1][k++] = points[1][i];  
  12.     circle( image, points[1][i], 3, Scalar(0,0,255), -1, 8);  
  13. }  
若4个点均跟踪成功,使用solvePnP计算相机姿态,并使用计算出的相机姿态重画3D空间点到2D平面查看是否匹配:
  1. if(k == 4)        
  2.     getPlanarSurface(points[0]);  
  1. void getPlanarSurface(vector<Point2f>& imgP){  
  2.   
  3.     Rodrigues(rotM,rvec);  
  4.     solvePnP(objPM, Mat(imgP), camera_matrix, distortion_coefficients, rvec, tvec);  
  5.     Rodrigues(rvec,rotM);  
  6.   
  7.     cout<<"rotation matrix: "<<endl<<rotM<<endl;  
  8.     cout<<"translation matrix: "<<endl<<tv[0]<<" "<<tv[1]<<" "<<tv[2]<<endl;  
  9.       
  10.     projectedPoints.clear();  
  11.     projectPoints(objPM, rvec, tvec, camera_matrix, distortion_coefficients, projectedPoints);  
  12.           
  13.     for(unsigned int i = 0; i < projectedPoints.size(); ++i)  
  14.     {  
  15.         circle( image, projectedPoints[i], 3, Scalar(255,0,0), -1, 8);  
  16.     }  
  17. }  
通过查看cmd中输出的旋转矩阵和平移向量以及重画的2D点,我们发现solvePnP运行良好。 点这里获得程序源码

下一节我们将结合相机外参使用OpenGL画出AR物体。

============================================================================================================

2015/10/20号补充:

这几天在做跟踪恢复的时候需要用给定的2D点和R,T计算3D点,于是重新手算了一边图像2D点和空间3D点的关系。过程中搞懂了为什么PnP计算rotation和translation的时候需要至少3组2D/3D点。

首先来看图像2D点和空间3D点的关系:


对于R和T展开并且对矩阵相乘展开我们得到:


把(3)式带入(1)式和(2)式,整理得:

Xw * ( fx * R11 + u0 * R31 - x * R31) + Yw * (fx * R12 + u0 * R32 - x * R32) + Zw * (fx * R13 + u0 * R33 - x * R33) = T3 * x - fx * T1 - u0 * T3

Xw * ( fy * R21 + v0 * R31 - y * R31) + Yw * (fy * R22 + v0 * R32 - y * R32) + Zw * (fy * R23 + v0 * R33 - y * R33) = T3 * y - fy * T2 - v0 * T3

我们可以看出,fx fy u0 v0是相机内参,上一节中已经求出,Xw Yw x y是一组3D/2D点的坐标,所以未知数有R11 R12 R13 R21 R22 R23 R31 R32 R33 T1 T2 T3一共12个,由于旋转矩阵是正交矩阵,每行每列都是单位向量且两两正交,所以R的自由度为3,秩也是3,比如知道R11 R12 R21就能求出剩下的Rxx。加上平移向量的3个未知数,一共6个未知数,而每一组2D/3D点提供的x y Xw Yw Zw可以确立两个方程,所以3组2D/3D点的坐标能确立6个方程从而解出6个未知数。

故PnP需要知道至少3组2D/3D点。


============================================================================================================

2016/1/28号补充:

最近在用平均最小误差求精准相机姿态的过程中,需要搞清楚R和t的具体含义。

R的第i行 表示摄像机坐标系中的第i个坐标轴方向的单位向量在世界坐标系里的坐标;
R的第i列 表示世界坐标系中的第i个坐标轴方向的单位向量在摄像机坐标系里的坐标;
t 表示世界坐标系的原点在摄像机坐标系的坐标;
-R的转置 * t 表示摄像机坐标系的原点在世界坐标系的坐标。(原理如下图,t表示平移,T表示转置)



2016/6/20

关于PnP问题我会重新写一篇博客,讲一下概念,最少需要几组对应的3D/2D点,

3D点共面时怎么处理,PnP有哪些主流解法(P3P, EPnP, DLS,  UPnP, 传统迭代)

以及会更新一篇G2O的PnP解法(传统迭代,最小化重投影误差)。

/

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值