关于OpenCV的那些事——相机姿态更新

来源:互联网 发布:淘宝卖stussy的正品店 编辑:程序博客网 时间:2024/04/23 22:12

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

注意点1:solvePnP调用的是cvFindExtrinsicCameraParams2通过已知的内参进行未知外参求解,是一个精确解;而cvPOSIT是用仿射投影模型近似透视投影模型下,不断迭代计算出来的估计值(在物体深度变化相对于物体到摄像机的距离比较大的时候,这种算法可能不收敛)。

注意点2:solvePnP使用至少4个点(共面不共线)或者6个点(不共面)。

注意点3:solvePnPRansac使用Ransac方法得到最优的姿态但是速度慢不适合实时系统。

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

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

[cpp] view plain copy
 print?
  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角点并亚像素化:

[cpp] view plain copy
 print?
  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点(按正方形左上顶点开始顺时针),然后查找所选点附近的角点,若找到则压入跟踪点集合:
[cpp] view plain copy
 print?
  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空间点集合:

[cpp] view plain copy
 print?
  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光流法跟踪已选定角点:

[cpp] view plain copy
 print?
  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平面查看是否匹配:

[cpp] view plain copy
 print?
  1. if(k == 4)        
  2.     getPlanarSurface(points[0]);  
[cpp] view plain copy
 print?
  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点的关系。过程中搞懂了为什么solvePnP计算rotation和translation的时候需要至少4组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个,由于旋转矩阵中每行每列的平方和都为1,所以R13可以用R11,R12表示, R23可以用R21,R22表示, R33可以用R31,R32表示(或者用R13,R23表示), R31可以用R11,R21表示, R32可以用R12,R22表示。所以未知数就只用7个了,而每一组2D/3D点提供的x y Xw Yw可以确立两个方程,所以4组2D/3D点的坐标能确立8个方程从而解出7个未知数。

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


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

2016/1/28号补充:

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

R的第i行 表示摄像机坐标系中的第i个坐标轴方向的单位向量在世界坐标系里的坐标;
R的第i列 表示世界坐标系中的第i个坐标轴方向的单位向量在摄像机坐标系里的坐标;
T 表示世界坐标系的原点在摄像机坐标系的坐标;
R的转置 * T 表示摄像机坐标系的原点在世界坐标系的坐标。(如果错误,希望各位大神们指正出来!)

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 不小心怀孕了该怎么办 45岁不小心怀了怎么办 喝了酒胃不舒服怎么办 备孕期间孕酮低怎么办 2个月宝宝不睡觉怎么办 生完小孩肚子还是很大怎么办 生完孩子肚皮疼怎么办 生完孩子有肚腩怎么办 生完宝宝肚子还是很大怎么办 生完孩子小腹大怎么办 生完孩子肚皮松怎么办 生过孩子肚子松怎么办 生完孩子肚皮痒怎么办 生完小孩肚子松弛怎么办 生了孩子肚子大怎么办 嫁到别的省户口怎么办 孕中期假性宫缩怎么办 频繁的假性宫缩怎么办 显卡风扇不转了怎么办 老是想不好的事怎么办 做人工受孕多囊怎么办 子宫内有囊肿该怎么办 子宫长了个囊肿怎么办 怀孕后不想要该怎么办 20多岁雌激素低怎么办 生完小孩后子宫下垂怎么办 取环之前同房了怎么办 做人流后又怀孕怎么办 仓鼠长了个肿瘤怎么办 过敏留下的黑印怎么办 脸上痘痘发炎了怎么办 脸上皮肤红痒怎么办啊 皮肤有一片红痒怎么办 脸过敏期间很干怎么办 怀孕了用了消糜栓怎么办 乳酸杆菌少或无怎么办 怀孕了白带有异味怎么办 怀孕清洁度iv度怎么办 怀孕了下面有异味怎么办 孕妇尿白细胞1是怎么办 药流期间喂奶了怎么办