三角化:Triangulation: Why Optimize?
- 1. 预备知识
- 1.1 评估 3D 点准确性
- 2. 提出的方法
- 2.1 广义加权中点法
- 2.2 可选的中点法
- 2.3 Cheirality(多视图几何中代表着3D点的正景深约束)
- 2.4 逆深度加权中点-Inverse Depth Weighted(IDW) Midpoint
- 3. 实现代码
Reference:
- GitHub:openMVG
1. 预备知识
- 向量 v \mathbf{v} v 的欧几里得范数: ∥ v ∥ \| \mathbf{v}\| ∥v∥;
- 单位向量: v ^ = v / ∥ v ∥ \hat{\mathbf{v}} =\mathbf{v}/ \| \mathbf{v}\| v^=v/∥v∥;
- 两条线 L 0 \mathbf{L_0} L0 和 L 1 \mathbf{L_1} L1 间角度: ∠ ( L 0 , L 1 ) ∈ [ 0 , π / 2 ] \angle(\mathbf{L}_0,\mathbf{L}_1)\in[0,\pi/2] ∠(L0,L1)∈[0,π/2];
- 定义 x 0 = [ x 0 , y 0 , z 0 ] T \mathbf{x_0}=[x_0, y_0, z_0]^T x0=[x0,y0,z0]T 和 x 1 = [ x 1 , y 1 , z 1 ] T \mathbf{x_1}=[x_1, y_1, z_1]^T x1=[x1,y1,z1]T 分别为相机参考系 C 0 C_0 C0 和 C 1 C_1 C1 的三维坐标;
- R \mathbf{R} R 和 t \mathbf{t} t 为两相机间的旋转和位移,则 x 1 = R x 0 + t \mathbf{x}_1=\mathbf{R}\mathbf{x}_0+\mathbf{t} x1=Rx0+t;
- 相机标定矩阵: K \mathbf{K} K;
- 每帧点观测到的其次像素坐标: u 0 = ( u 0 , v 0 , 1 ) ⊺ \mathbf{u}_{0}=(u_{0},v_{0},1)^{\intercal} u0=(u0,v0,1)⊺ 和 u 1 = ( u 1 , v 1 , 1 ) ⊺ \mathbf{u}_{1}=(u_{1},v_{1},1)^{\intercal} u1=(u1,v1,1)⊺;
- 归一化图像坐标: f 0 = [ x 0 / z 0 , y 0 / z 0 , 1 ] T \mathbf{f_{0}}=\left[x_{0}/z_{0},y_{0}/z_{0},1\right]^{T} f0=[x0/z0,y0/z0,1]T 和 f 1 = [ x 1 / z 1 , y 1 / z 1 , 1 ] T \mathbf{f_{1}}=\left[x_{1}/z_{1},y_{1}/z_{1},1\right]^{T} f1=[x1/z1,y1/z1,1]T,分别由 f 0 = K − 1 u 0 \mathbf{f}_0=\mathbf{K}^{-1}\mathbf{u}_0 f0=K−1u0 和 f 1 = K − 1 u 1 \mathbf{f}_1=\mathbf{K}^{-1}\mathbf{u}_1 f1=K−1u1 得到;
- 在 Fig.1a 的理想情况下,两个反向投影的光线相交,满足对极约束: f 1 ⋅ ( t × R f 0 ) = 0 \mathbf{f}_1\cdot(\mathbf{t}\times\mathbf{Rf}_0)=0 f1⋅(t×Rf0)=0(SLAM十四讲 P167, x 2 T t ∧ R x 1 = 0 \mathbf{x_2}^Tt^{\wedge}\mathbf{R}\mathbf{x_1}=0 x2Tt∧Rx1=0);
- 给定上图的深度标量 λ 0 \lambda_0 λ0 和 λ 1 \lambda_1 λ1,交点 x 1 \mathbf{x_1} x1 的公式为: x 1 = λ 0 R f 0 ^ + t \begin{aligned}\mathbf{x}_{1}& =\lambda_0\mathbf{R}\mathbf{\hat{f_0}}+\mathbf{t} \end{aligned} x1=λ0Rf0^+t 和 x 1 = λ 1 f 1 ^ \begin{aligned}\mathbf{x}_{1}& =\lambda_1\mathbf{\hat{f_1}}\end{aligned} x1=λ1f1^。( f 0 ^ \mathbf{\hat{f_0}} f0^ 和 f 1 ^ \mathbf{\hat{f_1}} f1^ 为理想值,由于图像测量和相机模型的不准确,这种情况很少发生。要从两条倾斜的光线推断三维点,就引出了下面的方法);
1.1 评估 3D 点准确性
一旦使用某种三角测量方法获得 3D 点 x 1 ′ \mathbf{x}_{1}' x1′ 的估计,就可以以多种方式评估其准确性:
- 计算 3D 误差,即: e 3 D = ∥ x 1 ′ − x t r u e ∥ e_{3D}=\|\mathbf{x}_1^\prime-\mathbf{x}_{\mathrm{true}}\| e3D=∥x1′−xtrue∥(见 Fig.1b);
- 计算 2D 误差,也就是重投影误差:
d i = ∥ K ( f i − f i ′ ) ∥ = ∥ K ( f i − ( [ 0 0 1 ] x i ′ ) − 1 x i ′ ) ∥ for i = 0 , 1 ( 1 ) d_i=\|\mathbf{K}\left(\mathbf{f}_i-\mathbf{f}_i'\right)\|=\left\|\mathbf{K}\left(\mathbf{f}_i-\left(\left[\mathbf{0}\text{ }\mathbf{0}\text{ }\mathbf{1}\right]\mathbf{x}_i'\right)^{-1}\mathbf{x}_i'\right)\right\|\quad\text{for}\quad i=0,1 \quad(1) di=∥K(fi−fi′)∥= K(fi−([0 0 1]xi′)−1xi′) fori=0,1(1)其中 x 0 ′ = R ⊺ ( x 1 ′ − t ) \mathbf{x}'_0=\mathbf{R}^\intercal(\mathbf{x}'_1-\mathbf{t}) x0′=R⊺(x1′−t)。这里 ( [ 0 0 1 ] x i ′ ) − 1 \left(\left[\mathbf{0}\text{ }\mathbf{0}\text{ }\mathbf{1}\right]\mathbf{x}_i'\right)^{-1} ([0 0 1]xi′)−1 求的是 z z z 轴坐标的逆,再乘上 x 1 ′ \mathbf{x}'_1 x1′ 可得归一化平面坐标(见 Fig.1b);
2D 误差表示与测量的偏差,而 3D 误差表示与真实值的偏差。与 3D 误差不同,3D 点的 2D 误差可以在不同范数下评估,如:- L 1 L_1 L1 范数: d 0 + d 1 d_0+d_1 d0+d1;
- L 2 L_2 L2 范数: d 0 2 + d 1 2 \sqrt{d_0^2+d_1^2} d02+d12;
- L ∞ L_\infty L∞ 范数: max ( d 0 , d 1 ) \max(d_0,d_1) max(d0,d1)。
- 除了 2D 和 3D 精度外,还可以评估得到的视差角精度(见 Fig.1c),视差误差定义如下(这里的表示感觉有点迷,
x
t
r
u
e
\mathbf{x}_{\mathrm{true}}
xtrue 说的应该是
C
1
C_1
C1 和
x
t
r
u
e
\mathbf{x}_{\mathrm{true}}
xtrue 的直线,而
x
t
r
u
e
−
t
\mathbf{x}_{\mathrm{true}}-\mathbf{t}
xtrue−t 为
C
0
C_0
C0 和
x
t
r
u
e
\mathbf{x}_{\mathrm{true}}
xtrue 的直线):
e β = ∣ β t r u e − β ′ ∣ = ∣ ∠ ( x t r u e , x t r u e − t ) − ∠ ( x 1 ′ , x 1 ′ − t ) ∣ ( 2 ) e_{\beta}=|\beta_{\mathrm{true}}-\beta'|=|\angle\left(\mathbf{x}_{\mathrm{true}},\mathbf{x}_{\mathrm{true}}-\mathbf{t}\right)-\angle\left(\mathbf{x}'_1,\mathbf{x}'_1-\mathbf{t}\right)| \quad(2) eβ=∣βtrue−β′∣=∣∠(xtrue,xtrue−t)−∠(x1′,x1′−t)∣(2)将“原始视差”定义为原始反投影光线之间的角度:
β r a w = ∠ ( R f 0 , f 1 ) . ( 3 ) \beta_{\mathrm{raw}}=\angle\left(\mathbf{Rf}_0,\mathbf{f}_1\right). \quad(3) βraw=∠(Rf0,f1).(3)这给出了独立于平移和三角化方法的视差角粗略估计。
2. 提出的方法
2.1 广义加权中点法
广义加权中点法 Generalized Weighted Midpoint (GWM)
包含以下三个步骤:
- 给定对应于同一点的两个反向投影光线,使用一些方法估计两条光线 ( λ 0 , λ 1 ) (\lambda_0, \lambda_1) (λ0,λ1) 的深度;
- 计算这两条深度为 λ 0 \lambda_0 λ0 和 λ 1 \lambda_1 λ1 的光线的 3D 点,也就是在 C 1 C_1 C1 帧的 t + λ 0 R f 0 ^ \mathbf{t} + \lambda_0\mathbf{R}\mathbf{\hat{f_0}} t+λ0Rf0^ 及 λ 1 f 1 ^ \lambda_1\mathbf{\hat{f_1}} λ1f1^;
- 通过计算它们的加权平均来获得 3D 点的最终估计。
以前经典的中点方法是,每条光线的两个点是有着相同权重的最接近点对。而下图展示了广义加权中点的另一个可能的示例:
2.2 可选的中点法
这里提出一种属于 GWM 的可选中点法。首先考虑两个反向投影碰巧相交的情况,如 Fig.1a。在这种情况下,最合理的解决方案是交点,可以使用正弦规则获得沿射线的对应深度:
λ
0
=
sin
(
∠
(
f
1
,
t
)
)
sin
(
∠
(
R
f
0
,
f
1
,
)
)
∥
t
∥
=
∥
f
^
1
×
t
∥
∥
R
f
^
0
×
f
^
1
∥
,
λ
1
=
sin
(
∠
(
R
f
0
,
t
)
)
sin
(
∠
(
R
f
0
,
f
1
,
)
)
∥
t
∥
=
∥
R
f
^
0
×
t
∥
∥
R
f
^
0
×
f
^
1
∥
.
(
4
)
\lambda_0=\frac{\sin\left(\angle\left(\mathbf{f}_1,\mathbf{t}\right)\right)}{\sin\left(\angle\left(\mathbf{R}\mathbf{f}_0,\mathbf{f}_1,\right)\right)}\|\mathbf{t}\|=\frac{\|\hat{\mathbf{f}}_1\times\mathbf{t}\|}{\|\mathbf{R}\hat{\mathbf{f}}_0\times\hat{\mathbf{f}}_1\|},\quad\lambda_1=\frac{\sin\left(\angle\left(\mathbf{R}\mathbf{f}_0,\mathbf{t}\right)\right)}{\sin\left(\angle\left(\mathbf{R}\mathbf{f}_0,\mathbf{f}_1,\right)\right)}\|\mathbf{t}\|=\frac{\|\mathbf{R}\hat{\mathbf{f}}_0\times\mathbf{t}\|}{\|\mathbf{R}\hat{\mathbf{f}}_0\times\hat{\mathbf{f}}_1\|}. \quad(4)
λ0=sin(∠(Rf0,f1,))sin(∠(f1,t))∥t∥=∥Rf^0×f^1∥∥f^1×t∥,λ1=sin(∠(Rf0,f1,))sin(∠(Rf0,t))∥t∥=∥Rf^0×f^1∥∥Rf^0×t∥.(4)在两条光线是倾斜的时候,也使用该公式估计深度。分别计算 3D 点在每条光线上的深度
λ
0
\lambda_0
λ0 和
λ
1
\lambda_1
λ1,得:
t
+
λ
0
R
f
^
0
=
t
+
∥
f
1
×
t
∥
∥
R
f
0
×
f
1
∥
R
f
0
and
λ
1
f
1
^
=
∥
R
f
0
×
t
∥
∥
R
f
0
×
f
1
∥
f
1
(
5
)
\mathbf{t}+\lambda_0\mathbf{R}\hat{\mathbf{f}}_0=\mathbf{t}+\frac{\|\mathbf{f}_1\times\mathbf{t}\|}{\|\mathbf{Rf}_0\times\mathbf{f}_1\|}\mathbf{Rf}_0\quad\text{and}\quad\lambda_1\hat{\mathbf{f_1}}=\frac{\|\mathbf{Rf}_0\times\mathbf{t}\|}{\|\mathbf{Rf}_0\times\mathbf{f}_1\|}\mathbf{f}_1 \quad(5)
t+λ0Rf^0=t+∥Rf0×f1∥∥f1×t∥Rf0andλ1f1^=∥Rf0×f1∥∥Rf0×t∥f1(5)取这两个点的中点有:
x
1
′
=
1
2
(
t
+
∥
f
1
×
t
∥
∥
R
f
0
×
f
1
∥
R
f
0
+
∥
R
f
0
×
t
∥
∥
R
f
0
×
f
1
∥
f
1
)
.
(
6
)
\mathbf{x}_1'=\frac{1}{2}\left(\mathbf{t}+\frac{\|\mathbf{f}_1\times\mathbf{t}\|}{\|\mathbf{R}\mathbf{f}_0\times\mathbf{f}_1\|}\mathbf{R}\mathbf{f}_0+\frac{\|\mathbf{R}\mathbf{f}_0\times\mathbf{t}\|}{\|\mathbf{R}\mathbf{f}_0\times\mathbf{f}_1\|}\mathbf{f}_1\right). \quad(6)
x1′=21(t+∥Rf0×f1∥∥f1×t∥Rf0+∥Rf0×f1∥∥Rf0×t∥f1).(6)令
p
=
R
f
^
0
×
f
^
1
\mathbf p=\mathbf R\hat{\mathbf f}_0\times\hat{\mathbf f}_1
p=Rf^0×f^1,
q
=
R
f
^
0
×
t
\mathbf q=\mathbf R\hat{\mathbf f}_0\times\mathbf t
q=Rf^0×t 以及
r
=
f
^
1
×
t
\mathbf r=\hat{\mathbf f}_1\times\mathbf t
r=f^1×t,深度公式可替换为:
λ
0
=
∣
∣
r
∣
∣
∣
∣
p
∣
∣
,
λ
1
=
∣
∣
q
∣
∣
∣
∣
p
∣
∣
(
7
)
\lambda_{0}={\frac{||\mathbf{r}||}{||\mathbf{p}||}},\quad\lambda_{1}={\frac{||\mathbf{q}||}{||\mathbf{p}||}} \quad(7)
λ0=∣∣p∣∣∣∣r∣∣,λ1=∣∣p∣∣∣∣q∣∣(7)这些形式类似于以前经典的中点法给出的深度:
λ
mid
0
=
p
^
⋅
r
∥
p
∥
,
λ
mid
1
=
p
^
⋅
q
∥
p
∥
.
(
8
)
\lambda_{\text{mid}0}=\frac{\hat{\mathbf{p}}\cdot\mathbf{r}}{\|\mathbf{p}\|},\quad\lambda_{\text{mid}1}=\frac{\hat{\mathbf{p}}\cdot\mathbf{q}}{\|\mathbf{p}\|}. \quad(8)
λmid0=∥p∥p^⋅r,λmid1=∥p∥p^⋅q.(8)这两种公式的不同之处在于分子,equ.7 有大小
r
\mathbf{r}
r 和
q
\mathbf{q}
q,而 equ.8 将它们投影到
p
\mathbf{p}
p 上。因此,我们总是得到
λ
0
≥
λ
mid
0
\lambda_0\geq \lambda_{\text{mid}0}
λ0≥λmid0 和
λ
1
≥
λ
mid
1
\lambda_1\geq \lambda_{\text{mid}1}
λ1≥λmid1。在大多数情况下,这意味着该方法得到的中点会比经典中点更远。在 Fig.2 描述的例子里,当我们估计一个远离相机的点时,通常(传统三角化方法)会导致估计的视差角较小。
2.3 Cheirality(多视图几何中代表着3D点的正景深约束)
当三角化的点有负深度时,违反了 3D 点的正景深约束。这可能有很多原因产生,如点对的关联错了或极点附近的图像点有噪声。通常它不会造成严重的问题,因为可以很轻易检查每个点的正景深并抛弃坏点。对于经典的中点方法,可以通过检查 equ.8 给出的深度符号确认。但对于该文中给出的方法是不能这样确认的,因为给出的深度总为正。上图说明了两种方法间的差异。因此在该方法中,仅靠深度不能说明三角测量的结果是否可靠。
这时需要使用其他方法来测试该点是否为坏点(满足以下不等式即为坏点):如果将至少一个深度的符号改为负会导致光线上两点间距变小,则抛弃这个点的对应关系:
∥
t
+
λ
0
R
f
0
^
−
λ
1
f
^
1
∥
2
≥
min
(
∥
t
+
λ
0
R
f
0
^
+
λ
1
f
1
^
∥
2
,
∥
t
−
λ
0
R
f
0
^
−
λ
1
f
^
1
∥
2
,
∥
t
−
λ
0
R
f
0
^
+
λ
1
f
1
^
∥
2
)
(
9
)
\|\mathbf{t}+\lambda_0\mathbf{R}\hat{\mathbf{f_0}}-\lambda_1\hat{\mathbf{f}}_1\|^2\geq\min\left(\|\mathbf{t}+\lambda_0\mathbf{R}\hat{\mathbf{f_0}}+\lambda_1\hat{\mathbf{f_1}}\|^2,\|\mathbf{t}-\lambda_0\mathbf{R}\hat{\mathbf{f_0}}-\lambda_1\hat{\mathbf{f}}_1\|^2,\|\mathbf{t}-\lambda_0\mathbf{R}\hat{\mathbf{f_0}}+\lambda_1\hat{\mathbf{f_1}}\|^2\right)\quad(9)
∥t+λ0Rf0^−λ1f^1∥2≥min(∥t+λ0Rf0^+λ1f1^∥2,∥t−λ0Rf0^−λ1f^1∥2,∥t−λ0Rf0^+λ1f1^∥2)(9)对于经典的中点法,令
λ
0
=
∣
λ
m
i
d
0
∣
\lambda_0=|\lambda_{mid0}|
λ0=∣λmid0∣ 和
λ
1
=
∣
λ
m
i
d
1
∣
\lambda_1=|\lambda_{mid1}|
λ1=∣λmid1∣ 可以有效得到与检验正景深约束相同的结果(显而易见,都是验证这个数是不是大于零的)。而现方法在 equ.9 在 Fig.3a 成立,是因为当
λ
0
=
−
∣
λ
m
i
d
0
∣
\lambda_0=-|\lambda_{mid0}|
λ0=−∣λmid0∣ 及
λ
1
=
−
∣
λ
m
i
d
1
∣
\lambda_1=-|\lambda_{mid1}|
λ1=−∣λmid1∣ 时两点最接近。
2.4 逆深度加权中点-Inverse Depth Weighted(IDW) Midpoint
由 equ.6 给出的为加权中点通常会导致两幅图像不成比例的重投影误差,Fig.3c 给出了一个例子。注意具有较小深度的光线往往会产生较大的重投影误差。为了补偿这种不平衡,文中建议使用逆深度作为权重:
x
1
′
=
λ
0
−
1
(
t
+
λ
0
R
f
0
^
)
+
λ
1
−
1
(
λ
1
k
1
^
)
λ
0
−
1
+
λ
1
−
1
=
∥
q
∥
∥
q
∥
+
∥
r
∥
(
t
+
∥
r
∥
∥
p
∥
(
k
f
0
^
+
f
1
^
)
)
.
\mathbf{x}_1'=\frac{\lambda_0^{-1}\left(\mathbf{t}+\lambda_0\mathbf{R}\hat{\mathbf{f_0}}\right)+\lambda_1^{-1}\left(\lambda_1\hat{\mathbf{k_1}}\right)}{\lambda_0^{-1}+\lambda_1^{-1}}=\frac{\|\mathbf{q}\|}{\|\mathbf{q}\|+\|\mathbf{r}\|}\left(\mathbf{t}+\frac{\|\mathbf{r}\|}{\|\mathbf{p}\|}\left(\mathbf{k}\hat{\mathbf{f_0}}+\hat{\mathbf{f_1}}\right)\right).
x1′=λ0−1+λ1−1λ0−1(t+λ0Rf0^)+λ1−1(λ1k1^)=∥q∥+∥r∥∥q∥(t+∥p∥∥r∥(kf0^+f1^)).
3. 实现代码
// Helper function
// Compute Relative motion between two absolute poses parameterized by Rt
// Rotate one bearing vector according to the relative motion
inline void AbsoluteToRelative(const Mat3 &R0, const Vec3 &t0, const Mat3 &R1, const Vec3 &t1, const Vec3 &x0, Mat3 &R, Vec3 &t, Vec3 &Rx0)
{
R = R1 * R0.transpose();
t = t1 - R * t0;
Rx0 = R * x0;
}
bool TriangulateIDWMidpoint(const Mat3 & R0, const Vec3 & t0, const Vec3 & x0, const Mat3 & R1, const Vec3 & t1, const Vec3 & x1, Vec3* X_euclidean)
{
// absolute to relative
Mat3 R;
Vec3 t, Rx0;
AbsoluteToRelative(R0, t0, R1, t1, x0, R, t, Rx0);
const double p_norm = Rx0.cross(x1).norm();
const double q_norm = Rx0.cross(t).norm();
const double r_norm = x1.cross(t).norm();
// Eq. (10)
const auto xprime1 = ( q_norm / (q_norm + r_norm) )
* ( t + (r_norm / p_norm) * (Rx0 + x1) );
// relative to absolute
*X_euclidean = R1.transpose() * (xprime1 - t1);
// Eq. (7)
const Vec3 lambda0_Rx0 = (r_norm / p_norm) * Rx0;
const Vec3 lambda1_x1 = (q_norm / p_norm) * x1;
// Eq. (9) - test adequation
return (t + lambda0_Rx0 - lambda1_x1).squaredNorm()
<
std::min(std::min(
(t + lambda0_Rx0 + lambda1_x1).squaredNorm(),
(t - lambda0_Rx0 - lambda1_x1).squaredNorm()),
(t - lambda0_Rx0 + lambda1_x1).squaredNorm());
}