[关闭]
@kokerf 2017-09-28T03:46:54.000000Z 字数 3982 阅读 1460

三角形法恢复空间点深度

计算机视觉

通常,在已知两个相机的相对位姿的情况下,得到在两个视图下的对应匹配点,我们就可以求得该对应点在空间中的位置,也就是求得图像点的深度。接下来介绍两种求解方法。

1.求解空间点坐标

当我们得到两个视图的一组匹配点,我们希望能恢复出世界点在三维世界的坐标。这里就涉及到使用三角形法来恢复点在3D空间的结构。一般比较常用的方法是线性三角形法(Linear triangulation methods )。线性三角形法使用直接线性变化(DLT)对点的世界坐标进行求解。

已知点对和两个图像的投影矩阵 ,根据相机投影模型,对应3D点满足

使用DLT我们需要把式子改变成的形式。由于是齐次坐标的表示形式,使用叉乘消去齐次因子,有

按照行展开代入,对第一幅图有

可见第三个式子可以由上两个式子线性表示,所以只需要取前连个式子即可,从而有形如的方程,其中

由于是自由度为3的齐次方程,所以这是一个冗余的方程,这里相当于解一个线性最小二乘问题。方程的解为的最小奇异值对应的单位奇异矢量,解得,则最后令缩放使得的最后一项为1即可得到我们所求的3D点的坐标。

ORB-SLAM2[1]中的三角形法的代码如下:

  1. void Initializer::Triangulate(const cv::KeyPoint &kp1, const cv::KeyPoint &kp2, const cv::Mat &P1, const cv::Mat &P2, cv::Mat &x3D)
  2. {
  3. cv::Mat A(4,4,CV_32F);
  4. A.row(0) = kp1.pt.x*P1.row(2)-P1.row(0);
  5. A.row(1) = kp1.pt.y*P1.row(2)-P1.row(1);
  6. A.row(2) = kp2.pt.x*P2.row(2)-P2.row(0);
  7. A.row(3) = kp2.pt.y*P2.row(2)-P2.row(1);
  8. cv::Mat u,w,vt;
  9. cv::SVD::compute(A,w,u,vt,cv::SVD::MODIFY_A| cv::SVD::FULL_UV);
  10. x3D = vt.row(3).t();
  11. x3D = x3D.rowRange(0,3)/x3D.at<float>(3);
  12. }

2.求解空间点深度

在深度滤波中经常使用这种形式,通常把首先观测到某一个图像点的图像帧设置为参考帧,在其他的图像帧中上做对极线,在对极线上搜索匹配点,然后通过匹配点进行三角法恢复深度。我们设相机内参为,则有:


这里的是单位平面上的点。通过点的对应关系,我们有:

这里的分别对应空间点在两个相机坐标系下的深度。于是我们可以把公式化为:

这里就是的形式,要解该方程,可以用正规方程来求解,也就是解,也就是有:

所以解得:

在SVO[2]中的实现如下,返回在参考帧下点的深度:

  1. bool depthFromTriangulation(
  2. const SE3& T_search_ref,
  3. const Vector3d& f_ref,
  4. const Vector3d& f_cur,
  5. double& depth)
  6. {
  7. Matrix<double,3,2> A; A << T_search_ref.rotation_matrix() * f_ref, f_cur;
  8. const Matrix2d AtA = A.transpose()*A;
  9. if(AtA.determinant() < 0.000001)
  10. return false;
  11. // d = - (ATA)^(-1) * AT * t
  12. const Vector2d depth2 = - AtA.inverse()*A.transpose()*T_search_ref.translation();
  13. depth = fabs(depth2[0]);
  14. return true;
  15. }

由于解向量是二维的,其实在得到公式之后,我们也可以直接采用克莱默法则求解。在REMODE[3]和《SLAM十四讲》中单目稠密重建的代码[4]的实现就是这样,其中REMODE代码如下,返回在参考帧下的空间点位置:

  1. float3 triangulatenNonLin(
  2. const float3 &bearing_vector_ref,
  3. const float3 &bearing_vector_curr,
  4. const SE3<float> &T_ref_curr)
  5. {
  6. const float3 t = T_ref_curr.getTranslation();
  7. float3 f2 = T_ref_curr.rotate(bearing_vector_curr);
  8. const float2 b = make_float2(dot(t, bearing_vector_ref),
  9. dot(t, f2));
  10. float A[2*2];
  11. A[0] = dot(bearing_vector_ref, bearing_vector_ref);
  12. A[2] = dot(bearing_vector_ref, f2);
  13. A[1] = -A[2];
  14. A[3] = dot(-f2, f2);
  15. const float2 lambdavec = make_float2(A[3]*b.x - A[1]*b.y,
  16. -A[2]*b.x + A[0]*b.y) / (A[0]*A[3] - A[1]*A[2]);
  17. const float3 xm = lambdavec.x * bearing_vector_ref;
  18. const float3 xn = t + lambdavec.y * f2;
  19. return (xm + xn)/2.0f;
  20. }

参考

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注