3. 相机内参标定基础:内参矩阵、畸变模型、HALCON内参标定算子简介

相机内参标定,说白了就是搞清楚镜头和传感器之间的「内在关系」。你想想看,我们拍到的图像是二维的,但真实世界是三维的。要从二维像素坐标反推三维空间信息,就必须知道相机内部是怎么「映射」的。

我个人习惯把内参标定比作「给相机做体检」。你得知道它的焦距是多少、光心偏不偏、镜头有没有畸变。这些参数不准确,后面的手眼标定就是空中楼阁。

3.1 内参矩阵:相机的心脏

内参矩阵(Intrinsic Matrix)是相机模型的核心。它描述了从三维相机坐标系到二维图像坐标系的投影关系。数学上,它是一个 3×3 的上三角矩阵:

⎡ fx   0   cx ⎤
⎢ 0    fy  cy ⎥
⎣ 0    0    1 ⎦

其中:

  • fx, fy:分别是 x 和 y 方向的焦距(以像素为单位)。理想情况下 fx = fy,但实际制造误差会导致两者略有差异。
  • cx, cy:主点坐标,即光轴与成像平面的交点。理论上应该在图像正中心,但实际往往有偏移。
我的经验: 我在项目中遇到过一台工业相机,标定出来的 cx 偏离中心多达 15 个像素。一开始我以为是标定板没放正,反复标定了三次才发现是相机装配问题。所以,别盲目相信「出厂参数」,自己标一遍最靠谱。

内参矩阵的作用很简单:给定一个三维点 (X, Y, Z),它在图像上的像素坐标 (u, v) 可以通过以下公式计算:

u = fx * (X / Z) + cx
v = fy * (Y / Z) + cy

嗯,这里要注意:这个公式假设镜头是完美的针孔模型。但现实中没有完美的镜头,所以我们需要引入畸变模型。

3.2 畸变模型:镜头的「真实面目」

镜头畸变是光学系统固有的缺陷。我刚开始做视觉时,总觉得畸变校正可有可无。直到有一次做高精度测量,发现边缘的物体位置偏差了 0.5 毫米……从那以后,我再也不敢忽略畸变了。

常见的畸变分为两类:

畸变类型 现象 数学模型 典型场景
径向畸变 直线变弯曲,越靠近边缘越明显 k1, k2, k3(三阶多项式) 广角镜头、鱼眼镜头
切向畸变 镜头与成像平面不平行导致的倾斜 p1, p2(两个参数) 镜头装配偏差、碰撞后

在 HALCON 中,畸变模型通常用 5 个参数表示:k1, k2, k3, p1, p2。其中 k1, k2 是主要的径向畸变参数,k3 用于高畸变镜头(比如鱼眼),p1, p2 是切向畸变参数。

避坑指南: 我曾经遇到一个案例,标定出来的 k3 值特别大,但图像边缘的畸变反而校正过度了。后来发现是标定板图像数量不够(只拍了 5 张),导致高阶参数过拟合。我的建议是:普通镜头只用 k1, k2 就够了,除非你确定需要 k3。

3.3 HALCON 内参标定算子简介

HALCON 提供了一套完整的内参标定工具链。我个人最喜欢的是它的「一步到位」风格——你只需要提供标定板图像,它就能自动完成角点提取、参数求解和误差评估。

核心算子就三个:

  1. create_calib_data:创建标定数据模型。你需要指定标定对象类型(通常是标定板)和相机数量。
  2. set_calib_data_cam_param:设置相机初始参数。这里可以给一个初始猜测值,比如焦距设为图像宽度的一半,主点设为图像中心。
  3. calibrate_cameras:执行标定。这个算子会同时优化所有相机的内参和外参。

一个典型的标定流程如下:

* 创建标定数据模型
create_calib_data ('calibration_object', 'calplate', 1, CalibDataID)

* 设置相机初始参数(以 5 百万像素相机为例)
StartCamParam := ['area_scan_division', 0.008, 0, 0, 0, 0, 0, 2448, 2048]
set_calib_data_cam_param (CalibDataID, 0, [], StartCamParam)

* 设置标定板描述文件
set_calib_data_calib_object (CalibDataID, 0, 'caltab_30mm.descr')

* 对每张标定板图像进行角点提取和位姿估计
for i := 1 to 20 by 1
    read_image (Image, 'calib_images/' + i$'02d')
    find_calib_object (Image, CalibDataID, 0, 0, i-1, [], [])
    get_calib_data_observ_contours (Contours, CalibDataID, 'caltab', 0, 0, i-1)
endfor

* 执行标定
calibrate_cameras (CalibDataID, [])
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
关键点: StartCamParam 的格式是 ['area_scan_division', Kappa, FocalLength, Sx, Sy, Cx, Cy, ImageWidth, ImageHeight]。其中 Kappa 是畸变系数(对于 division 模型),FocalLength 是焦距(米),Sx, Sy 是像元尺寸(米/像素)。如果你不知道像元尺寸,可以查相机 datasheet,或者设为 0 让 HALCON 自动估计。

3.4 知识体系总览

为了让你更直观地理解内参标定的知识结构,我画了一张图:

相机内参标定知识体系 相机内参标定 内参矩阵 畸变模型 HALCON算子 fx, fy cx, cy 径向畸变 切向畸变 create_calib_data calibrate_cameras 核心流程 采集标定板图像 → 提取角点 → 求解内参 → 评估重投影误差 重投影误差 < 0.1 像素才算合格

3.5 标定质量评估

标定做完不等于万事大吉。我见过太多人标定完就直接用,结果测量精度一塌糊涂。怎么判断标定好不好?看两个指标:

  • 重投影误差(Reprojection Error):把标定板上的三维点投影回图像,计算投影点与实际检测到的角点之间的像素距离。HALCON 的 calibrate_cameras 会自动输出这个值。我个人习惯要求误差小于 0.1 像素,如果超过 0.3 像素,我会重新采集数据。
  • 畸变校正效果:用标定得到的内参对一张图像做畸变校正,观察图像边缘的直线是否变直了。如果校正后的图像边缘还是弯的,说明标定有问题。
小技巧: 我通常会在标定完成后,用 change_radial_distortion_cam_par 生成一张畸变校正后的图像,然后拿尺子在屏幕上量一下标定板格点的间距是否均匀。这个方法虽然土,但很有效。

好了,内参标定的基础就讲到这里。记住:内参矩阵是相机模型的骨架,畸变参数是修正镜头缺陷的补丁,HALCON 的算子则是帮你自动完成这一切的工具。这三者缺一不可。