4. 相机内参标定原理:针孔模型、畸变模型与标定流程
相机内参标定,说白了就是搞清楚你的眼睛(镜头)到底是怎么看世界的。我刚开始接触3D视觉时,总觉得标定是个“玄学”——明明棋盘格摆得端端正正,结果就是不对。后来踩的坑多了,才明白这背后其实就两件事:针孔模型和畸变模型。
这一章,咱们就把这两个模型掰开揉碎,再聊聊标定的完整流程。你想想看,如果连相机怎么看世界都不清楚,后面的3D测量和抓取怎么可能准?
4.1 针孔模型——相机成像的“理想国”
针孔模型是最简单的成像模型。它假设光线从物体出发,穿过一个小孔,在后面的成像平面上形成倒像。这个模型虽然理想化,但它是所有标定算法的基础。
核心公式其实就一个投影关系:
// 世界坐标系到像素坐标系的映射
// [u, v, 1]^T = K * [R|t] * [X, Y, Z, 1]^T
// 其中 K 是内参矩阵:
// K = [fx, 0, cx;
// 0, fy, cy;
// 0, 0, 1]
这里 fx、fy 是焦距(以像素为单位),cx、cy 是主点坐标。说白了,内参矩阵就是描述“从相机坐标系到像素坐标系”的映射关系。
关键点:针孔模型假设镜头是完美的,光线沿直线传播。但现实中没有完美的镜头,所以我们需要畸变模型来修正。
4.2 畸变模型——现实世界的“不完美”
我在项目中遇到过最头疼的问题,就是标定完发现边缘的测量误差特别大。后来一查,原来是畸变参数没调好。畸变主要分两类:径向畸变和切向畸变。
4.2.1 径向畸变
径向畸变是镜头曲率造成的。离光心越远,畸变越明显。它分为两种:
- 桶形畸变:图像边缘向外凸出,像桶一样。常见于广角镜头。
- 枕形畸变:图像边缘向内凹陷。常见于长焦镜头。
数学模型用泰勒级数展开:
x_corrected = x * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)
y_corrected = y * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)
其中 r 是像素到光心的距离,k1、k2、k3 是径向畸变系数。一般只用 k1 和 k2 就够了,k3 只在鱼眼镜头等极端情况下才用。
4.2.2 切向畸变
切向畸变是镜头和成像平面不平行造成的。说白了就是镜头装歪了。它的模型是:
x_corrected = x + [2 * p1 * x * y + p2 * (r^2 + 2 * x^2)]
y_corrected = y + [p1 * (r^2 + 2 * y^2) + 2 * p2 * x * y]
p1、p2 是切向畸变系数。我建议你在标定时,先只标径向畸变,如果残差还大,再考虑切向。因为切向畸变参数多了,容易过拟合。
个人经验:我曾经在一个项目中,标定结果总是差那么0.1像素。折腾了两天,最后发现是镜头没拧紧,产生了微小的切向畸变。拧紧后重新标定,残差直接降到0.05像素以下。所以,硬件稳定性比算法更重要。
4.3 标定流程——从采集到优化
标定流程就三步:采集 → 提取 → 优化。听起来简单,但每一步都有坑。
4.3.1 采集——标定板图像
采集标定板图像时,要注意几点:
- 数量:至少15-20张。太少的话,优化容易陷入局部最优。
- 姿态:标定板要覆盖视场的各个区域——左上、右下、中间、倾斜、旋转。我习惯让标定板占画面1/3到1/2,不要太满。
- 光照:均匀光照,避免反光。反光会导致角点提取不准。
避坑指南:我曾经为了省事,只拍了10张标定板图像,结果标定出来的内参在边缘区域误差很大。后来补到20张,效果明显改善。记住:宁多勿少。
4.3.2 提取——角点检测
提取角点是标定的关键步骤。Halcon 提供了 find_caltab 和 find_marks_and_pose 算子。我个人习惯用 find_caltab 先定位标定板,再用 find_marks_and_pose 提取角点。
* Halcon 标定板角点提取示例
read_image (Image, 'calibration_image_01')
find_caltab (Image, CaltabRegion, 'caltab_30mm.descr', 3, 112, 5)
find_marks_and_pose (Image, CaltabRegion, 'caltab_30mm.descr', 3, 112, 5, [], [], RCoord, CCoord, StartPose)
提取完成后,检查角点是否准确。如果角点偏移超过0.5像素,这张图就不要用了。
4.3.3 优化——最小化重投影误差
优化的目标是最小化重投影误差——也就是把3D点投影到图像上,和实际提取的2D点之间的像素距离。Halcon 使用 camera_calibration 算子进行优化。
* Halcon 相机标定优化
camera_calibration (CalibDataID, 'all', 'pose', Errors, 'calibration_object', 'caltab_30mm.descr')
优化完成后,检查 Errors 输出。一般平均重投影误差应小于0.1像素。如果大于0.3像素,说明标定质量有问题,需要重新采集或检查标定板。
核心指标:重投影误差 < 0.1 像素 → 优秀;0.1-0.3 像素 → 可接受;> 0.3 像素 → 需要重新标定。
4.4 知识结构总览
下面这张图,把相机内参标定的核心逻辑串起来了。你可以把它当作一个快速参考。
4.5 标定参数对比表
为了方便你快速查阅,我把标定涉及的主要参数整理成了表格:
| 参数 | 符号 | 说明 | 典型范围 |
|---|---|---|---|
| 焦距(x方向) | fx | 以像素为单位的x方向焦距 | 500-5000 |
| 焦距(y方向) | fy | 以像素为单位的y方向焦距 | 500-5000 |
| 主点(x方向) | cx | 光心在x方向的像素坐标 | 图像宽度/2 ± 50 |
| 主点(y方向) | cy | 光心在y方向的像素坐标 | 图像高度/2 ± 50 |
| 径向畸变系数1 | k1 | 二阶径向畸变 | -0.5 ~ 0.5 |
| 径向畸变系数2 | k2 | 四阶径向畸变 | -0.5 ~ 0.5 |
| 径向畸变系数3 | k3 | 六阶径向畸变(鱼眼用) | -0.5 ~ 0.5 |
| 切向畸变系数1 | p1 | 切向畸变 | -0.1 ~ 0.1 |
| 切向畸变系数2 | p2 | 切向畸变 | -0.1 ~ 0.1 |
小技巧:标定完成后,可以用 gen_caltab 生成一个虚拟标定板,然后用标定好的内参去投影,看看和实际图像是否吻合。如果偏差大,说明标定有问题。