4. 相机模型与针孔成像原理
做SLAM这么多年,我始终觉得相机模型是入门的第一道坎。你想想看,一个三维世界里的点,怎么就跑到二维图像上去了?这背后其实就是一个简单的几何变换——针孔成像。今天咱们就把这事彻底聊透。
4.1 针孔成像模型
说白了,针孔相机就是一个带小孔的暗箱。光线从物体上反射过来,穿过这个小孔,在后面的感光平面上形成一个倒立的像。这就是最原始的成像原理。
我记得刚入行时,导师让我标定相机。我当时觉得这有什么难的?结果第一次标出来的内参,投影误差大得离谱。后来才发现,是我对针孔模型的理解太肤浅了。
核心要点:针孔模型假设所有光线都通过一个点(光心),然后投影到成像平面。这个假设在大多数相机上都成立,但鱼眼镜头就不行了。
咱们来看一下数学表达。假设世界坐标系中有一个点 P = [X, Y, Z]ᵀ,它在图像上的投影点 p = [u, v]ᵀ。那么它们的关系是:
// 针孔投影公式
u = f * X / Z + c_u
v = f * Y / Z + c_v
这里的 f 是焦距,c_u 和 c_v 是主点偏移。嗯,这里要注意,这个公式成立的前提是相机坐标系和图像坐标系已经对齐好了。
4.2 内参矩阵
实际应用中,我们更喜欢用矩阵形式来表达。把上面的公式写成齐次坐标形式:
// 内参矩阵 K
K = [f_x, 0, c_u,
0, f_y, c_v,
0, 0, 1]
// 投影方程
s * [u, v, 1]ᵀ = K * [X, Y, Z]ᵀ
这个 K 矩阵就是相机的内参矩阵。它包含了五个参数:
- f_x, f_y:x和y方向的焦距(通常很接近)
- c_u, c_v:主点坐标(理论上在图像中心)
- s:尺度因子(齐次坐标的产物)
我的经验:实际标定时,f_x和f_y往往有微小差异。这很正常,因为CMOS传感器的像素不一定是正方形。我曾经遇到过一台相机,f_x和f_y差了3%,一开始还以为是标定出错了。
4.3 畸变模型
理想很丰满,现实很骨感。针孔模型是理想情况,但真实镜头都有畸变。最常见的两种:
- 径向畸变:镜头形状导致的,越靠近边缘越明显
- 切向畸变:镜头和传感器不平行导致的
为什么会这样?说白了,镜头是曲面玻璃,光线穿过时会发生折射。尤其是广角镜头,边缘的畸变简直不忍直视。
畸变矫正的数学模型是这样的:
// 径向畸变
x_distorted = x * (1 + k1*r² + k2*r⁴ + k3*r⁶)
y_distorted = y * (1 + k1*r² + k2*r⁴ + k3*r⁶)
// 切向畸变
x_distorted = x + 2*p1*x*y + p2*(r² + 2*x²)
y_distorted = y + p1*(r² + 2*y²) + 2*p2*x*y
这里的 k1, k2, k3 是径向畸变系数,p1, p2 是切向畸变系数。一共五个畸变参数。
避坑指南:我曾经在标定时用了错误的畸变模型,结果地图建出来全是弯曲的。后来才发现,OpenCV和MATLAB的畸变模型定义略有不同。一定要确认你用的库用的是哪种模型!
4.4 知识体系总览
为了让你更直观地理解相机模型的知识结构,我画了一张图:
4.5 实际应用中的注意事项
理论说完了,咱们聊聊实战。在SLAM系统里,相机模型主要用在两个地方:
- 特征点投影:把3D地图点投影到图像上,和检测到的特征点做匹配
- 三角化:从多个视角的2D观测恢复3D点的位置
我个人习惯在代码里把相机模型封装成一个类,这样切换相机或者传感器时特别方便。给你看看我常用的接口设计:
class CameraModel {
public:
// 3D点投影到图像
virtual Eigen::Vector2d project(const Eigen::Vector3d& pt3d) = 0;
// 图像点反投影到归一化平面
virtual Eigen::Vector3d unproject(const Eigen::Vector2d& pt2d) = 0;
// 畸变矫正
virtual Eigen::Vector2d undistort(const Eigen::Vector2d& pt_distorted) = 0;
// 获取内参
virtual const Eigen::Matrix3d& getK() const = 0;
};
这样做的好处是,以后换相机只需要继承这个基类,实现对应的投影和反投影函数就行。我在做多传感器融合时,就是靠这个设计快速适配了三种不同的相机。
小技巧:调试相机模型时,我建议你先用棋盘格拍几张照片,手动验证投影误差。别一上来就全自动标定,有时候标定板没放平,结果全废了。
好了,相机模型和针孔成像原理就聊到这儿。记住一句话:理解相机模型,就是理解三维世界到二维图像的映射关系。这是SLAM的基石,后面的所有算法都建立在这个基础之上。
公众号:蓝海资料掘金营,微信deep3321