作者: 边城量子 ( shihezichen@live.cn )
对极约束公式由来: 十四讲公式(7.9)
已知: P=[X,Y,Z]⊤ 是空间中的一个点,它在相机两帧的成像平面上的像素点为 p1,p2, 相机内参为 K, 两帧间相机运动为 R,t, 即满足:
s1p1=KP,s2p2=K(RP+t)(1)
由以上信息,在齐次坐标表示下,推导出对极约束:
p2⊤K−⊤t∧RK−1p1=0(2)
解:
把公式(1)写成齐次坐标表示方式, 可以简化为:
p1x1≜K−1p1x2≜K−1p2R−1(K−1p2−t)∴K−1p1RK−1p1两边左乘t∧,(K−1p2)⊤t∧RK−1p1上式右边为0,(K−1p2)⊤t∧RK−1p1p2⊤K−⊤t∧RK−1p1=KP,p2=K(RP+t)=P,=RP+t⟶=P=R−1(K−1p2−t)=K−1p2−t再左乘(K−1p2)⊤=(K−1p2)⊤t∧K−1p2−(K−1p2)⊤t∧t则得到:=0=0
以下对上述过程中等号右边变为0进行说明, 右边如下:
(K−1p2)⊤t∧K−1p2−(K−1p2)⊤t∧t
分析:
- t∧t 是一个向量自己和自己的叉乘, 为0,第二项为0;
- t∧(K−1p2) 代表两个向量叉乘,其结果和两个向量都垂直,此时再与其中一个向量 K−1p2 内积,第一项为0.
得证.
八点法如何求解本质矩阵 E: 十四讲公式(7.13)
把对极约束中的 t∧R 定义为本质矩阵(Essential Matrix) E, 把 K−⊤EK−1 定义为基础矩阵(Fundamental Matrix) F。即:
EFx2Ex1=t∧R=K−⊤EK−1=0,其中x1≜K−1p1,x2≜K−1p2
因此,根据公式(2),这是一个方程组,只要找到足够多的点 p1,p2 就可以通过方程组解出 E, 因此也就得到 R,t。
由于 t∧R 是一个 3×3 的矩阵,有9个未知数。 又由于公式(2)是在齐次坐标下表示,因此存在尺度等价性, 因此8个未知数是相互独立的。所以需要8对 p1,p2 来求解 E。 这就是八点法(Eight-point-algorithm).
考虑一对点 p1,p2, 它们对应的像素平面归一化坐标为 x1=[u1,v1,1]⊤,x2=[u2,v2,1]⊤, 根据对极约束, 有下式成立:
(u2v21)⎝⎛e1e4e7e2e5e8e3e6e9⎠⎞⎝⎛u1v11⎠⎞(u2e1+v2e4+e7u2e2+v2e5+e8u2e3+v2e6+e9)⎝⎛u1v11⎠⎞u1u2e1+u1v2e4+u1e7+v1u2e2+v1v2e5+v1e8+u2e3+v2e6+e9u1u2e1+u2v1e2+u2e3+u1v2e4+v1v2e5+v2e6+u1e7+v1e8+e9(u1u2u2v1u2u1v2v1v2v2u1v11)⎝⎛e1e2e3e4e5e6e7e8e9⎠⎞=0=0=0=0=0
上面是一对点形成的方程,若选取8对点, 则可以得到一个方程组 ( u(i),v(i) 代表第 i 个点 ), 如下:
⎝⎛u1(1)u2(1)u1(2)u2(2)⋮u1(8)u2(8)u2(1)v1(1)u2(2)v1(2)⋮u2(8)v1(8)u2(1)u2(2)⋮u2(8)u1(1)v2(1)u1(2)v2(2)⋮u1(8)v2(8)v1(1)v2(1)v1(2)v2(2)⋮v1(8)v2(8)v2(1)v2(2)⋮v2(8)u1(1)u1(2)⋮u1(8)v1(1)v1(2)⋮v1(8)111⎠⎞⎝⎛e1e2e3e4e5e6e7e8e9⎠⎞=0
以上8个方程构成了一个线性方程组,解方程即可得各变量 ei, 从而得到矩阵 E。 通过E可以恢复出相机的运动 R,t.
由本质矩阵求R,t
以下仅仅是助于理解,真正的推导过程,是1992年由Hartley提出的,从本质矩阵相差一个尺度因子的情况下恢复R,t 的四个可能解。证明过程可见《计算机视觉中的多视图几何》中文版P174。
E=t∧R=UΣV⊤=UΣU⊤UV⊤=UΣU⊤UV⊤
因此可以猜到:
t∧R=UΣU⊤=UV⊤
记
RZ(2π)=⎝⎛010−100001⎠⎞
来表示沿 Z 轴旋转90°的旋转矩阵。
重新表示 t∧,R:
t1∧R1t2∧R2=URZ(2π)ΣU⊤=URZ(2π)V⊤=URZ(−2π)ΣU⊤=URZ(−2π)V⊤
程序实现
参见十四讲的7.4小节的代码: slambook2/ch7/pose_estimation_2d2d.cpp
void pose_estimation_2d2d(std::vector<KeyPoint> keypoints_1,
std::vector<KeyPoint> keypoints_2,
std::vector<DMatch> matches,
Mat &R, Mat &t) {
Mat K = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);
Point2d principal_point(325.1, 249.7);
double focal_length = 521;
vector<Point2f> points1;
vector<Point2f> points2;
for (int i = 0; i < (int) matches.size(); i++) {
points1.push_back(keypoints_1[matches[i].queryIdx].pt);
points2.push_back(keypoints_2[matches[i].trainIdx].pt);
}
Mat essential_matrix;
essential_matrix = findEssentialMat(points1, points2, focal_length, principal_point);
cout << "essential_matrix is " << endl << essential_matrix << endl;
recoverPose(essential_matrix, points1, points2, R, t, focal_length, principal_point);
cout << "R is " << endl << R << endl;
cout << "t is " << endl << t << endl;
}
opencv 直接提供了从匹配的特征点对集合获取本质矩阵的方法 findEssentialMat(),传入特征点对,以及光心、相机焦距等参数即可得到 E; 同时opencv还提供了从本质矩阵恢复相机运动 R,t的方法 recoverPose(), 传入特征点对、光心、相机焦距等,即可计算得到R,t .
其他参考