OpenCV相机标定与3D重建(1)概述
- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
本节中的函数使用所谓的针孔相机模型。通过使用透视变换将场景中的3D点 P w P_w Pw 投影到图像平面上,从而获得场景的视图,形成相应的像素点 p p p 和 P w P_w Pw均用齐次坐标表示,即分别为3D和2D齐次向量。在本节引言的末尾,您将找到关于射影几何、齐次向量和齐次变换的简要介绍。为了更加简洁的符号表示,我们通常省略“齐次”一词,而直接说向量而不是齐次向量。
由针孔相机模型给出的无畸变的射影变换如下所示。
s
p
=
A
[
R
∣
t
]
P
w
,
s \; p = A \begin{bmatrix} R|t \end{bmatrix} P_w,
sp=A[R∣t]Pw,
其中
P
w
P_w
Pw是相对于世界坐标系表示的3D点,p 是图像平面上的2D像素,A 是相机内参矩阵,R 和t 是描述从世界坐标系到相机坐标系(或相机框架)的坐标变化的旋转和平移,s 是射影变换的任意缩放因子,并不是相机模型的一部分。相机内参矩阵A(使用的符号如[314]中所述,也通常记作K)将给定在相机坐标系中的3D点投影到2D像素坐标,即:
p
=
A
P
c
.
p = A P_c.
p=APc.
相机内参矩阵 A 由以像素单位表示的焦距
f
x
f_x
fx和
f
y
f_y
fy以及主点
(
c
x
,
c
y
)
(c_x, c_y)
(cx,cy)组成,主点通常接近图像中心:
A
=
[
f
x
0
c
x
0
f
y
c
y
0
0
1
]
A = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix}
A=
fx000fy0cxcy1
因此
[
u
v
1
]
=
[
f
x
0
c
x
0
f
y
c
y
0
0
1
]
[
X
c
Y
c
Z
c
]
\begin{bmatrix}{u}\\{v}\\{1}\end{bmatrix}=\begin{bmatrix}{f_x}&{0}&{c_x}\\ {0}&{f_y}&{c_y}\\ {0}&{0}&{1} \end{bmatrix} \begin{bmatrix}{X_c}\\{Y_c}\\{Z_c}\end{bmatrix}
uv1
=
fx000fy0cxcy1
XcYcZc
内参矩阵不依赖于所观察的场景。因此,一旦估计出来,只要焦距固定(对于变焦镜头而言),就可以重复使用。因此,如果相机的图像按某个比例因子缩放,那么所有这些参数都需要按相同的比例因子进行缩放(相应地乘以或除以)。
联合旋转-平移矩阵
[
R
∣
t
]
[R|t]
[R∣t]是射影变换和齐次变换的矩阵乘积。3x4 的射影变换将用相机坐标表示的3D点映射到图像平面上的2D点,并且这些2D点用归一化的相机坐标
x
′
=
X
c
/
Z
c
x' = X_c / Z_c
x′=Xc/Zc和
y
′
=
Y
c
/
Z
c
y' = Y_c / Z_c
y′=Yc/Zc
Z
c
[
x
′
y
′
1
]
=
[
1
0
0
0
0
1
0
0
0
0
1
0
]
[
X
c
Y
c
Z
c
1
]
Z_c \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} X_c \\ Y_c \\ Z_c \\ 1 \end{bmatrix}
Zc
x′y′1
=
100010001000
XcYcZc1
齐次变换由外参
R
R
R和
t
t
t编码,表示从世界坐标系w到相机坐标系c的基底变换。因此,给定世界坐标中的点P
的表示
P
w
P_w
Pw,我们可以通过
P
c
=
[
R
∣
0
;
t
∣
1
]
P
w
P_c = [R | 0; t | 1] P_w
Pc=[R∣0;t∣1]Pw
这个齐次变换由 R,一个3x3的旋转矩阵,和t,一个3x1的平移向量组成:
[
R
∣
0
;
t
∣
1
]
=
[
r
11
r
12
r
13
t
x
r
21
r
22
r
23
t
y
r
31
r
32
r
33
t
z
0
0
0
1
]
[R | 0; t | 1] = \begin{bmatrix} r_{11} & r_{12} & r_{13} & t_x \\ r_{21} & r_{22} & r_{23} & t_y \\ r_{31} & r_{32} & r_{33} & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix}
[R∣0;t∣1]=
r11r21r310r12r22r320r13r23r330txtytz1
因此,
[
X
c
Y
c
Z
c
1
]
=
[
r
11
r
12
r
13
t
x
r
21
r
22
r
23
t
y
r
31
r
32
r
33
t
z
0
0
0
1
]
[
X
w
Y
w
Z
w
1
]
\begin{bmatrix} X_c \\ Y_c \\ Z_c \\ 1 \end{bmatrix} = \begin{bmatrix} r_{11} & r_{12} & r_{13} & t_x \\ r_{21} & r_{22} & r_{23} & t_y \\ r_{31} & r_{32} & r_{33} & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix}
XcYcZc1
=
r11r21r310r12r22r320r13r23r330txtytz1
XwYwZw1
结合射影变换和齐次变换,我们得到将世界坐标中的3D点映射到图像平面上2D点的射影变换,这些2D点用归一化的相机坐标表示:
其中,
x
′
=
X
c
/
Z
c
x' = X_c / Z_c
x′=Xc/Zc和
y
′
=
Y
c
/
Z
c
y' = Y_c / Z_c
y′=Yc/Zc。将内参和外参的方程组合起来,我们可以写出
s
p
=
A
[
R
∣
t
]
P
w
s \; p = A \begin{bmatrix} R|t \end{bmatrix} P_w
sp=A[R∣t]Pw如下:
s
[
u
v
1
]
=
[
f
x
0
c
x
0
f
y
c
y
0
0
1
]
[
r
11
r
12
r
13
t
x
r
21
r
22
r
23
t
y
r
31
r
32
r
33
t
z
]
[
X
w
Y
w
Z
w
1
]
s \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} r_{11} & r_{12} & r_{13} & t_x \\ r_{21} & r_{22} & r_{23} & t_y \\ r_{31} & r_{32} & r_{33} & t_z \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix}
s
uv1
=
fx000fy0cxcy1
r11r21r31r12r22r32r13r23r33txtytz
XwYwZw1
如果
Z
c
≠
0
Z_c \ne 0
Zc=0上面的变换等价于:
[
u
,
v
]
=
[
f
x
(
X
c
/
Z
c
)
+
c
x
,
f
y
(
Y
c
/
Z
c
)
+
c
y
]
[u, v] = [f_x(X_c / Z_c) + c_x, f_y(Y_c / Z_c) + c_y]
[u,v]=[fx(Xc/Zc)+cx,fy(Yc/Zc)+cy]
其中
[
X
c
Y
c
Z
c
]
=
[
R
∣
t
]
[
X
w
Y
w
Z
w
1
]
\begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} = [R | t] \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix}
XcYcZc
=[R∣t]
XwYwZw1
下面的图示展示了针孔相机模型。
[
u
,
v
]
=
[
f
x
x
′
′
+
c
x
,
f
y
y
′
′
+
c
y
]
[u, v] = [f_x x'' + c_x, f_y y'' + c_y]
[u,v]=[fxx′′+cx,fyy′′+cy]
其中
[
x
′
′
y
′
′
]
=
[
x
′
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
+
(
1
+
k
4
r
2
+
k
5
r
4
+
k
6
r
6
)
(
2
p
1
x
′
y
′
+
p
2
(
r
2
+
2
x
′
2
)
+
s
1
r
2
+
s
2
r
4
)
y
′
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
+
(
1
+
k
4
r
2
+
k
5
r
4
+
k
6
r
6
)
(
p
1
(
r
2
+
2
y
′
2
)
+
2
p
2
x
′
y
′
+
s
3
r
2
+
s
4
r
4
)
]
\begin{bmatrix} x'' \\ y'' \end{bmatrix} = \begin{bmatrix} \frac{x'}{1 + k_1 r^2 + k_2 r^4 + k_3 r^6} + (1 + k_4 r^2 + k_5 r^4 + k_6 r^6) (2 p_1 x' y' + p_2 (r^2 + 2 x'^2) + s_1 r^2 + s_2 r^4) \\ \frac{y'}{1 + k_1 r^2 + k_2 r^4 + k_3 r^6} + (1 + k_4 r^2 + k_5 r^4 + k_6 r^6) (p_1 (r^2 + 2 y'^2) + 2 p_2 x' y' + s_3 r^2 + s_4 r^4) \end{bmatrix}
[x′′y′′]=[1+k1r2+k2r4+k3r6x′+(1+k4r2+k5r4+k6r6)(2p1x′y′+p2(r2+2x′2)+s1r2+s2r4)1+k1r2+k2r4+k3r6y′+(1+k4r2+k5r4+k6r6)(p1(r2+2y′2)+2p2x′y′+s3r2+s4r4)]
这里
r
2
=
x
′
2
+
y
′
2
r^2 = x'^2 + y'^2
r2=x′2+y′2
并且
[
x
′
y
′
]
=
[
X
c
/
Z
c
Y
c
/
Z
c
]
,
\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} X_c / Z_c \\ Y_c / Z_c \end{bmatrix},
[x′y′]=[Xc/ZcYc/Zc],
如果
Z
c
≠
0
Z_c \ne 0
Zc=0
畸变参数包括径向系数
𝑘
1
,
𝑘
2
,
𝑘
3
,
𝑘
4
,
𝑘
5
,
𝑘
6
𝑘 1 , 𝑘 2 , 𝑘 3 , 𝑘 4 , 𝑘 5 , 𝑘 6
k1,k2,k3,k4,k5,k6,切向畸变系数
p
1
p1
p1和
p
2
p2
p2以及薄棱镜畸变系数
s
1
,
s
2
,
s
3
,
s
4
s_1,s2,s3,s4
s1,s2,s3,s4OpenCV 中不考虑更高阶的系数。下图展示了两种常见的径向畸变类型:桶形畸变(
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
1 + k_1 r^2 + k_2 r^4 + k_3 r^6
1+k1r2+k2r4+k3r6单调递减)和枕形畸变(
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
1 + k_1 r^2 + k_2 r^4 + k_3 r^6
1+k1r2+k2r4+k3r6单调递增)对于实际的镜头,径向畸变总是单调的,如果估计结果是非单调的,应视为标定失败。更一般地说,径向畸变必须是单调的,且畸变函数必须是双射的。一个失败的估计结果可能在图像中心附近看起来很好,但在 AR/SFM 等应用中表现不佳。OpenCV 相机标定中使用的优化方法不包括这些约束,因为框架不支持所需的整数规划和多项式不等式。有关更多信息,请参见 issue #15992。
在某些情况下,为了聚焦相机前的倾斜平面(Scheimpflug 原理),图像传感器可能会倾斜。这对于粒子图像测速 (PIV) 或使用激光扇进行三角测量非常有用。这种倾斜会导致
x
′′
x′′
x′′和
𝑦
′′
𝑦′′
y′′的透视畸变。这种畸变可以用以下方式建模,参见例如 [167]。
[
u
,
v
]
=
[
f
x
x
′
′
′
+
c
x
,
f
y
y
′
′
′
+
c
y
]
,
[u, v] = [f_x x''' + c_x, f_y y''' + c_y],
[u,v]=[fxx′′′+cx,fyy′′′+cy],
其中
s
[
x
′
′
′
y
′
′
′
1
]
=
[
R
33
(
τ
x
,
τ
y
)
0
0
0
R
33
(
τ
x
,
τ
y
)
0
−
R
13
(
τ
x
,
τ
y
)
−
R
23
(
τ
x
,
τ
y
)
1
]
R
(
τ
x
,
τ
y
)
[
x
′
′
y
′
′
1
]
s \begin{bmatrix} x''' \\ y''' \\ 1 \end{bmatrix} = \begin{bmatrix} R_{33}(\tau_x, \tau_y) & 0 & 0 \\ 0 & R_{33}(\tau_x, \tau_y) & 0 \\ -R_{13}(\tau_x, \tau_y) & -R_{23}(\tau_x, \tau_y) & 1 \end{bmatrix} R(\tau_x, \tau_y) \begin{bmatrix} x'' \\ y'' \\ 1 \end{bmatrix}
s
x′′′y′′′1
=
R33(τx,τy)0−R13(τx,τy)0R33(τx,τy)−R23(τx,τy)001
R(τx,τy)
x′′y′′1
矩阵
R
(
τ
x
,
τ
y
)
R(\tau_x, \tau_y)
R(τx,τy)由两个分别具有角度参数
τ
x
\tau_x
τx和
τ
y
\tau_y
τy的旋转定义:
R
(
τ
x
,
τ
y
)
=
[
cos
(
τ
y
)
0
sin
(
τ
y
)
0
1
0
−
sin
(
τ
y
)
0
cos
(
τ
y
)
]
[
1
0
0
0
cos
(
τ
x
)
−
sin
(
τ
x
)
0
sin
(
τ
x
)
cos
(
τ
x
)
]
=
[
cos
(
τ
y
)
0
sin
(
τ
y
)
sin
(
τ
y
)
sin
(
τ
x
)
cos
(
τ
x
)
−
cos
(
τ
y
)
sin
(
τ
x
)
−
sin
(
τ
y
)
cos
(
τ
x
)
sin
(
τ
x
)
cos
(
τ
y
)
cos
(
τ
x
)
]
.
R(\tau_x, \tau_y) = \begin{bmatrix} \cos(\tau_y) & 0 & \sin(\tau_y) \\ 0 & 1 & 0 \\ -\sin(\tau_y) & 0 & \cos(\tau_y) \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos(\tau_x) & -\sin(\tau_x) \\ 0 & \sin(\tau_x) & \cos(\tau_x) \end{bmatrix} = \begin{bmatrix} \cos(\tau_y) & 0 & \sin(\tau_y) \\ \sin(\tau_y) \sin(\tau_x) & \cos(\tau_x) & -\cos(\tau_y) \sin(\tau_x) \\ -\sin(\tau_y) \cos(\tau_x) & \sin(\tau_x) & \cos(\tau_y) \cos(\tau_x) \end{bmatrix}.
R(τx,τy)=
cos(τy)0−sin(τy)010sin(τy)0cos(τy)
1000cos(τx)sin(τx)0−sin(τx)cos(τx)
=
cos(τy)sin(τy)sin(τx)−sin(τy)cos(τx)0cos(τx)sin(τx)sin(τy)−cos(τy)sin(τx)cos(τy)cos(τx)
.
在以下函数中,系数作为向量传递或返回:
(
k
1
,
k
2
,
p
1
,
p
2
[
,
k
3
[
,
k
4
,
k
5
,
k
6
[
,
s
1
,
s
2
,
s
3
,
s
4
[
,
τ
x
,
τ
y
]
]
]
]
)
(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6 [, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])
(k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]])
即,如果向量包含四个元素,则意味着
k
3
=
0
k_3=0
k3=0。畸变系数不依赖于所观察的场景。因此,它们也属于相机的内部参数,并且与捕获的图像分辨率无关。例如,如果一个相机已经在 320 x 240 分辨率的图像上进行了标定,那么相同的畸变系数也可以用于同一相机拍摄的 640 x 480 图像,而
f
x
f_x
fx,
f
y
f_y
fy,
c
x
c_x
cx,
c
y
c_y
cy需要适当缩放。
以下函数使用上述模型执行以下操作:
- 将 3D 点投影到图像平面上,给定内部和外部参数。
- 给定内部参数、几个 3D 点及其投影,计算外部参数。
- 从已知标定图案的多个视图中估计相机的内部和外部参数(每个视图由多个 3D-2D 点对应关系描述)。
- 估计立体相机“头部”的相对位置和方向,并计算使相机光轴平行的校正变换。
齐次坐标
齐次坐标是一种在射影几何中使用的坐标系统。它们的使用允许用有限坐标表示无穷远点,并且与笛卡尔坐标相比简化了公式,例如,它们的优点是可以将仿射变换表示为线性齐次变换。
通过在 n 维笛卡尔向量 P 的末尾附加一个 1,可以得到齐次向量
P
h
P_h
Ph,例如,对于一个 3D 笛卡尔向量,映射
P
→
P
h
P \rightarrow P_h
P→Ph为:
[
X
Y
Z
]
→
[
X
Y
Z
1
]
.
\begin{bmatrix} X \\ Y \\ Z \end{bmatrix} \rightarrow \begin{bmatrix} X \\ Y \\ Z \\ 1 \end{bmatrix}.
XYZ
→
XYZ1
.
对于逆映射
P
h
→
P
P_h \rightarrow P
Ph→P,将齐次向量的所有元素除以其最后一个元素,例如,对于一个 3D 齐次向量,其 2D 笛卡尔对应物为:
[
X
Y
W
]
→
[
X
/
W
Y
/
W
]
,
\begin{bmatrix} X \\ Y \\ W \end{bmatrix} \rightarrow \begin{bmatrix} X / W \\ Y / W \end{bmatrix},
XYW
→[X/WY/W],
如果
W
≠
0
W \ne 0
W=0
由于这种映射,齐次点的所有倍数
k
P
h
k P_h
kPhh(其中
k
≠
0
k \ne 0
k=0)表示同一个点 。这一属性的直观理解是,在射影变换下,所有
P
h
P_h
Ph的倍数都被映射到同一点。这是针孔相机的物理观察结果,因为沿相机针孔的光线上的所有点都被投影到同一个图像点,例如,上述针孔相机模型图像中的红色光线上的所有点都会被映射到同一个图像坐标。这一属性也是针孔相机模型方程中的尺度模糊s的来源。
如前所述,通过使用齐次坐标,我们可以将由
R
R
R和
t
t
t参数化的任何基底变换表示为线性变换,例如,从坐标系 0 到坐标系 1 的基底变换可以表示为:
P
1
=
R
P
0
+
t
→
P
h
1
=
[
R
∣
0
;
t
∣
1
]
P
h
0
.
P_1 = R P_0 + t \rightarrow P_{h1} = [R | 0; t | 1] P_{h0}.
P1=RP0+t→Ph1=[R∣0;t∣1]Ph0.
注意事项
本模块中的许多函数都将相机内部矩阵作为输入参数。尽管所有函数假设该参数具有相同的结构,但它们可能使用不同的名称。然而,参数的描述将是明确的,即需要一个具有上述结构的相机内部矩阵。
标定示例
- 3 个水平排列的相机的标定示例可以在 opencv_source_code/samples/cpp/3calibration.cpp 中找到。
- 基于图像序列的标定示例可以在 opencv_source_code/samples/cpp/calibration.cpp 中找到。
- 用于 3D 重建的标定示例可以在 opencv_source_code/samples/cpp/build3dmodel.cpp 中找到。
- 立体相机标定的示例可以在 opencv_source_code/samples/cpp/stereo_calib.cpp 中找到。
- 立体匹配的标定示例可以在 opencv_source_code/samples/cpp/stereo_match.cpp 中找到。
- (Python)相机标定示例可以在 opencv_source_code/samples/python/calibrate.py 中找到。