Pinhole Camera Model
Last updated on November 26, 2023 pm
[TOC]
成像流水线
针孔相机模型
相机将三维世界中的坐标点(单位:米)映射到二维图像平面(单位:像素)的过程能够用一个几何模型来描述,其中最简单的称为 针孔相机模型 (pinhole camera model) ,其框架如下图所示。
世界坐标系中三维点 $M=[X,Y,Z]^T$ 和 像素坐标系中二维点 $m=[u,v]^T$ 的关系为:
即(针孔相机模型)
其中,$s$ 为缩放因子,$A$ 为相机的内参矩阵,$[R \quad t]$ 为相机的外参矩阵,$\tilde{m}$ 和 $\tilde{M}$ 分别为 $m$ 和 $M$ 对应的齐次坐标。
世界坐标系 到 相机坐标系
相机坐标系 到 像素坐标系
根据三角形相似关系,有
整理,得
像素坐标系 和 成像平面坐标系 之间,相差一个缩放和平移,联合上式整理得
或
其中,
则
或
其中,
- $f$ 为镜头焦距,单位为米;
- $\alpha$、$\beta$ 的单位为像素/米;
- $dx$、$dy$ 为传感器x轴和y轴上单位像素的尺寸大小,单位为米/像素;
- $f_x$、$f_y$ 为x、y方向的焦距,单位为像素;
- $f{nx}$、$f{ny}$ 为x、y方向的归一化焦距;
- $(c_x,c_y)$ 为主点,图像的中心,单位为像素。
最终,写成矩阵的形式为:
或
主焦距 & 有效焦距
畸变模型
多项式畸变模型 (radial-tangential)
透镜的畸变主要分为径向畸变和切向畸变。
下图是距离光心不同距离上的点经过透镜 径向畸变 后点位的偏移示意图,距离光心越远,径向位移越大,表示畸变也越大,在光心附近,几乎没有偏移。
径向畸变 是由于透镜形状的制造工艺导致,且越向透镜边缘移动径向畸变越严重,实际情况中我们常用r=0处的泰勒级数展开的前几项来近似描述径向畸变,径向畸变后的归一化坐标为:
切向畸变 是由于透镜和CMOS或者CCD的安装位置误差导致,切向畸变需要两个额外的畸变参数来描述,切向畸变后的归一化坐标为:
联合上式,整理得
其中,$r^2 = x^2 + y^2$
综上,我们一共需要5个畸变参数 $(k_1, k_2, k_3, p_1, p_2)$ 来描述透镜畸变。
畸变矫正
整张图
- [图像]畸变校正详解
核心示例代码 (from here)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26for (int v = 0; v < height; v++) {
for (int u = 0; u < width; u++) {
double u_distorted = 0, v_distorted = 0;
double x = (u-cx)/fx;
double y = (v-cy)/fy;
double x2 = x*x, y2 = y*y, xy = x*y, r2 = x2 + y2;
double x_radial = x * (1 + k1*r2 + k2*r2*r2);
double y_radial = y * (1 + k1*r2 + k2*r2*r2);
double x_tangential = 2*p1*xy + p2*(r2 + 2*x2);
double y_tangential = 2*p2*xy + p1*(r2 + 2*y2);
double xd = x_radial + x_tangential;
double yd = y_radial + y_tangential;
u_distorted = xd*fx + cx;
v_distorted = yd*fy + cy;
// 最近邻插值
if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < width && v_distorted < height)
img_dst(v, u) = (*this)((int) v_distorted, (int) u_distorted);
else
img_dst(v, u) = 0;
}
}
单点
上面逆向过程 (PinholeCamera::liftProjective()
)
1 |
|
Apply Inverse distortion model
1
2
3
4
5
6
7
8
9
10
11
12
13// Apply inverse distortion model proposed by Heikkila
mx2_d = mx_d*mx_d;
my2_d = my_d*my_d;
mxy_d = mx_d*my_d;
rho2_d = mx2_d+my2_d;
rho4_d = rho2_d*rho2_d;
radDist_d = k1*rho2_d+k2*rho4_d;
Dx_d = mx_d*radDist_d + p2*(rho2_d+2*mx2_d) + 2*p1*mxy_d;
Dy_d = my_d*radDist_d + p1*(rho2_d+2*my2_d) + 2*p2*mxy_d;
inv_denom_d = 1/(1+4*k1*rho2_d+6*k2*rho4_d+8*p1*my_d+8*p2*mx_d);
mx_u = mx_d - inv_denom_d*Dx_d;
my_u = my_d - inv_denom_d*Dy_d;Recursive distortion model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// Recursive distortion model
int n = 8;
Eigen::Vector2d d_u;
distortion(Eigen::Vector2d(mx_d, my_d), d_u);
// Approximate value
mx_u = mx_d - d_u(0);
my_u = my_d - d_u(1);
for (int i = 1; i < n; ++i)
{
distortion(Eigen::Vector2d(mx_u, my_u), d_u);
mx_u = mx_d - d_u(0);
my_u = my_d - d_u(1);
}