Sunday 20 December 2015

一步步学习SFM 五 Opencv multiple view geometry

之前做VO只能做到两帧之间对比的up to scale的版本,之后受到理论理解不深的限制很难再继续做优化之类的得到metric scale。看来还是需要好好先把理论搞清楚。正好看到了这本书,于是补一补基础知识。

分解F矩阵
两帧之中特征点和相机姿态的关系受到epipolar geometry的约束。通常假定第二个相机的姿态是相对第一个相机而言的,就是说第二个相机的旋转R和平移t都是相对第一个相机。两帧之中的特征点满足一个关系,叫做epipolar constrait,跟特征矩阵F有关。F又跟R和t有关。F的rank是2,determinant是0.也就是说相机的姿态可以从F中恢复,具体推导过程在这本书The essential and fundamental matrices这个章节。

假设一个3D点X在第一帧的投影是x1,在第二帧中的投影是x2,那么x1的位置使得x2只能在第二帧的一条线上,叫做epipolar line。F可以帮助在这条线上寻找x2的位置。所有的epipolar line都相交于一点,叫做epipole。

F是一个3x3的矩阵,不过尺度是不确定的,所以只需要有8个对应点就能计算F。如果特征点数目多于8个,就用least square找到误差最小的F。不过当8个点都在同一平面上的时候,这个方法不能使用。Opencv中的cv::findFundamentalMat函数还可以用7个点计算F,因为我们已经知道所有的epipolar line都交于一点,这个条件也限定了F。findFundamentalMat在用7点算法的时候可能返回3,因为这时可能有三个解。在8点算法,ransac, LMedS都只返回1或者0.检查findFundamentalMat的返回数很重要,因为有些时候点选取的不好,无法计算F。

这本书中提到了一个重要的问题,就是求解F的时候图像中对应点的选取非常关键。这些特征点需要在图像中均匀分布,并且包含不同深度的点。实际上可以用RANSAC来得到更准的F。首先用SURF寻找和描述特征点,再用knn matcher来找到两个最近的匹配点。如果最好匹配的距离比次好匹配的距离小很多,那么可以肯定最好匹配没有误差,否者说明最好和次好匹配很难区分,要去掉该特征点。接下来从图像A到B和图像B到A分别做匹配,只选取那些都是最好匹配的特征点。最后就是用RANSAC找到F和inlier。之前去掉错误匹配的过程是为了让ransca有个更好的初始条件,inlier更多就更容易得到准的F。

假如对应点都在同一个平面上,可以让它们的Z坐标都是0,那么3x4的projection matrix就变成了3x3的matrix,也就是homography。

当知道两个相机的姿态,可以用三角定位来确定一对对应特征点的3D坐标。如果已知3D点和图像中的投影点,也能算出相机姿态,这个问题叫做camera resectioning。

在已知相机矩阵的情况下,图像点的坐标根据相机矩阵K进行normalize之后,它们之间的关系跟essential matrix有关,这里用E矩阵来表示这是已知K的情况下的F。从E中可以计算出四个可能的相机姿态解,不过只有一个能让所有点都在两个相机前面。E和H的区别是,H假设了看到的点在同一个平面,所以可以把一个3D点定位到一个2D点。E没有平面假设,只能把一个3D点定位到另外一个图像的一条线上。E和F的区别是,E描述了同一个3D点在左右两个相机中的3D坐标的关系,而F描述了同一个2D点在左右两个相机中坐标的关系。

为了得到robust的解,还可以用ransac。在多帧的计算中相机姿态的误差会累积,这时用bundle adjustment优化3D点和相机姿态。

POSIT
这本书中提到了POSIT算法,用来计算已知物体大小的R,t运动。我们需要知道物体上4个不共面的点。算法首先假设这些点都在同一个深度,它们的大小变化仅仅跟相机距离物体远近有关。也就是说假设物体距离相机足够远,我们可以忽略物体的深度变化,所以物体上的点深度都一样。这个假设也叫weak perspective approximation.

从已知的点可以计算出一个并不是很准的pose,通过这个pose又可以反投影到4个2D点,然后根据它们和3D点的对应关系又可以更新pose,一直到pose收敛。

注意这里假设物体距离相机很远,所以物体上面点的深度变化可以忽略。如果这个假设不成立,那么pose要么是错误的,要么不会收敛。更多的共面的点也不会让算法更准,反而会导致错误的结果。更多的不共面的点则可以帮助减少误差。这个不是很理解,既然本来选取的点就不共面,表示深度不同,可是算法本身还假设深度相同,是不是有点矛盾?

在旧版的opencv中,cvPOSIT只能读取一个focal_length,因为这里假定像素的x和y方向是正方形。


No comments:

Post a Comment