做相机 ISP 标定的朋友应该都有过这种经历:标定流程跑完,重投影误差也压到 0.1 像素以内了,可一旦把图像拿去畸变校正或者做双目深度,总感觉哪里不对劲——边缘线条还是弯的、左右目视差对不齐、镜头阴影校正(LSC)在四角发暗得特别快。排查到最后,问题往往不在畸变系数,而是光学中心偏移没处理对。
这里说的“光学中心”,在相机内参矩阵里对应的就是 cx、cy。它并不是图像的正中心(W/2, H/2),而是镜头光轴与传感器平面的实际交点。由于镜头模组装配公差、传感器贴片偏差、镜头入瞳偏心等因素,它和图像几何中心之间通常有几像素到几十像素的偏移。这个偏移量对普通拍照可能无所谓,但对 ISP 标定、机器视觉测量、双目/SLAM 这类对几何精度要求高的场景,就是绕不过去的坎。
这篇文章我会从光学中心偏移的物理成因讲起,再把计算方法和实操流程一步步拆开:你需要什么样的标定板、怎么拍图、角点检测怎么做、OpenCV 标定脚本怎么跑、怎么从内参矩阵里正确解析出光学中心偏移量,以及怎么验证结果靠谱。适合刚接触相机标定又想搞明白背后原理的同学,也适合被 ISP 调试中各种“玄学”问题折磨过的工程师。
1. 光学中心偏移是什么,为什么它值得花力气标定
1.1 光学中心不是图像中心
先从物理层面说清楚。一个相机镜头组可以近似看成一个厚透镜系统,光线经过镜头后汇聚,光轴(所有镜片表面中心的连线)与传感器平面的交点,就是光学中心在图像传感器上的投影。理想情况下,镜头光轴垂直于传感器平面,并且穿过传感器靶面的几何中心,所以光学中心应该落在 (W/2, H/2)。
但现实里镜头是螺纹旋进镜座的,传感器是在封装工序里通过贴片机贴到 PCB 上的,镜座和感光芯片之间还有隔圈、胶水、FPC 连接。每一道工序都有机械公差,光轴就很容易和传感器法线不平行,或者和传感器中心错开。哪怕只错开几十微米,在像素尺寸只有 1.4μm 左右的现代相机上,就是几十个像素的偏移。
我经常用投影仪来打比方:投影仪镜头相当于相机镜头,幕布相当于传感器。投影仪摆歪了,投出的画面中心就不在幕布几何中心上——你能看到画面,但画面整体偏了。相机内部也是这么回事,只是偏移量远小于画面尺寸,平时肉眼看图根本注意不到。
1.2 光学中心偏移会带来哪些实际影响
偏移小的时候,直接看图几乎无感。但一旦进入 ISP 或者机器视觉算法链路,影响就被放大了:
- 畸变校正不对称。径向畸变的多项式是围绕光学中心计算的,偏移会让校正后的图像中心区域正确但边缘不对称,典型表现是校正后画面有一侧“鼓”,另一侧“瘪”。
- 镜头阴影校正(Lens Shading Correction)出现偏轴补偿错误。Lens Shading 的增益曲面通常也以光学中心为原点来拟合,中心偏了,暗角补偿的对称性就被破坏,四角亮度不一致。
- 双目立体匹配和 3D 重建精度下降。极线校正是基于内参和外参联合求解的,光学中心误差会直接导致极线不水平,匹配搜索范围变大,深度精度变差。
- 机器视觉里的测量、定位、引导都会产生系统误差。比如用像素坐标换算物理坐标时,主点偏移会让同样的像素距离在不同视场位置对应不同的实际距离,做高精度尺寸测量时尤其明显。
- 多传感器标定、手眼标定这类场景中,光学中心误差会污染外参估计。相机和激光雷达、机械臂的坐标变换本身就是从像素观测反推的,内参不准,外参一定跟着歪。
1.3 哪些项目必须较真,哪些可以略过
判断自己要不要花精力抠光学中心偏移,先看应用类型。如果是手机拍照片、安防监控、普通文档扫描这类以人眼观感为准的场景,几像素的偏移在最后出图里基本不可见,不值得折腾。但如果你做的是以下任何一类项目,建议把光学中心作为一个明确的内参项专门标定:
- 高精度视觉测量(比如 PCB 尺寸测量、精密装配定位)
- 双目相机 / 多目相机的深度估计
- SLAM、视觉里程计
- ADAS 摄像头(测距、目标尺寸估计)
- AR/VR 的透视显示与图像渲染
- 科学成像、显微镜定量分析
连工业相机出厂都会趁镜头和传感器相对位置固定的情况下做一次出厂标定,把光学中心存进相机用户区,底层逻辑就是知道这玩意儿会随装配批次变化,不能靠图纸上的理想值。
2. 标定之前:内参、畸变模型和光学中心的数学关系
2.1 内参矩阵里的 cx、cy 就是光学中心的像素坐标
相机内参矩阵 K 通常写成这样:
K = [ fx 0 cx 0 fy cy 0 0 1 ]fx、fy 是归一化焦距(单位是像素),cx、cy 就是主点(principal point),也就是光学中心在像素坐标系下的坐标。注意这里面有个容易误解的点:cx、cy 的单位是像素,而且它们的原点在图像左上角。也就是说,对一张宽 W、高 H 的图,光学中心在图像上的像素位置就是 (cx, cy),它相对几何中心的偏移量就是:
offset_x = cx - W/2 offset_y = cy - H/2有人会把 cx、cy 理解成“畸变中心”,严格来说不完全对。畸变中心是畸变模型展开的参考点,大多数标定工具把主点同时当作畸变中心来优化,因为两者在物理上很接近。但这带来一个后果:如果镜头畸变大,主点和畸变中心之间会互相耦合,标定结果会波动。后面讲验证方法时我会再说怎么评估这个耦合带来的误差。
2.2 畸变模型与光学中心是怎么互相拉扯的
以最常见的 Brown-Conrady 畸变模型(OpenCV 默认的 plumb_bob 模型)为例,畸变公式是:
x_distorted = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) + 2*p1*x*y + p2*(r^2 + 2*x^2) y_distorted = y * (1 + k1*r^2 + k2*r^4 + k3*r^6) + p1*(r^2 + 2*y^2) + 2*p2*x*y其中 x、y 是理想针孔模型下以光学中心为原点的归一化坐标,r^2 = x^2 + y^2。注意这里的关键点:kx 和 kx 计算的径向畸变是相对于哪个中心来量的?是这个公式里的 x、y,而 x、y 在标定计算中依赖 cx、cy。也就是说,如果光学中心标错了,畸变系数的估计也会被带偏。
具体表现是什么?我举一个实际遇到的例子。有一款工业镜头,边缘畸变接近 -2%,我最初用厂家给的默认 cx、cy 做标定,畸变系数 k1、k2 的数值波动非常大,重投影误差很难降到 0.15px 以下。后来把 cx、cy 也放开让优化算法自己迭代,误差立刻降到 0.07px,而且 k1、k2 也变得稳定。这说明光学中心和畸变系数在优化过程里是一组强耦合变量,单独优化某一个,不如整体联合优化。
2.3 常用标定方法是怎样求解光学中心的
当前工业界和学术界最常用的还是张正友平面标定法。它的核心思想是:把标定板当成一个已知尺寸的平面靶标,拍摄多张不同位姿的图像,利用单应性矩阵(Homography)建立起 2D 图像点到 3D 靶标点的投影关系,然后通过最小化重投影误差来求解内参和外参。张正友法的好处是不需要知道相机和标定板之间的精确相对位姿,只需要标定板的几何尺寸精确。
在张正友法的求解过程中,cx、cy 和其他内参一起出现在优化目标函数里。初始值一般取图像中心 (W/2, H/2),然后通过非线性最小二乘(通常是 Levenberg-Marquardt)迭代更新。最终收敛后,cx、cy 的最优估计就是这个相机在当前分辨率下的光学中心像素坐标。
Kalibr、OpenCV、MATLAB Camera Calibrator 等工具在处理上基本一致,区别在于畸变模型的表示和优化细节。Kalibr 支持多种畸变模型(包括 Kannala-Brandt、Equidistant、FOV 等),适合鱼眼镜头;OpenCV 的 calibrateCamera 对常规镜头足够用;MATLAB 的优势是交互界面和可视化做得比较好,适合不习惯写代码的工程师。对于大多数 ISP 标定项目,OpenCV 就够了,而且可以直接把结果固化到自己的标定脚本里。
3. 实操流程:棋盘格图像采集与角点检测的关键细节
3.1 标定板与设备准备,别在这些地方省事
标定板看着简单,但很多标定失败案例最后都能追溯到标定板身上。首选是陶瓷基板或者玻璃基板上做的棋盘格,表面平整、黑白对比度高、尺寸稳定。纸质打印棋盘格不是不能用,但要贴在刚性平板上,并在拍摄前确认没有褶皱和油渍。温度和湿度变化会让纸张尺寸发生变化,从而影响标定精度。
棋盘格的参数要选对。设计上主要两个参数:内角点数量和方格边长。比如我常用的格子是 9x6(内角点数),方格边长 30mm,打印在 A3 纸上,配合普通工业镜头,工作距离 200mm-600mm 时一张图里能覆盖足够的角点数量。如果镜头视野很大或者很小,要相应调整方格边长,保证单张图像里内角点数在 30 个以上为宜——太少的话角点检测容易出错,太多的话检测耗时长而且亚像素精度会下降。
相机和镜头的状态在采集期间必须冻结:固定光圈、固定焦距、固定增益和曝光时间。调焦环、光圈环用胶带或者锁紧螺丝固定住,否则镜头内镜片组位置稍有移动,光学中心和畸变就会变。我见过有人在标定过程中顺手调了一下光圈,然后整批数据报废,只能重拍。
3.2 图像采集套路:怎么拍出合格的标定图
很多人觉得标定就是拿标定板各个角度拍十几张,其实这里面的讲究不少。我总结的拍摄准则如下:
- 数量:20-30 张左右。太少容易陷入局部最优,太多优化耗时且容易混入低质量样本。
- 姿态:标定板平面与相机光轴的夹角要变化,理想是覆盖 15°到 45°的倾角范围,并包含向左倾斜、向右倾斜、向前俯仰、向后仰等组合。纯正对相机的图占一小部分就行,不要全部正对。
- 位置:棋盘格在图像中的位置要尽量覆盖中心、左上、右上、左下、右下、上下边缘和左右边缘。尤其是图像边缘位置的样本越多,畸变系数和光学中心的解耦越好。
- 距离:在保证清晰度的前提下,远近都拍一些。距离变化可以帮助算法解算尺度信息。
- 光照:使用漫射光源,避免标定板表面出现高光反射。高光区域会让角点检测的亚像素定位偏移。户外拍摄时避开太阳直射。
- 稳定性:避免运动模糊,手持拍摄时宁可提高 ISO 一点也要保证快门速度足够。
拍摄时最好实时检查每张图的角点提取情况,不合格的当场重拍。尤其是边缘姿态的图,一旦棋盘格角点被截断,OpenCV 的 findChessboardCorners 会直接返回失败,或者返回错误的角点顺序。
3.3 基于 OpenCV 的角点检测与标定代码流程
假设你已经采集好了 20-30 张标定图像,存放在 calib_images 目录里。以下是使用 Python 和 OpenCV 的标定核心流程。
先做角点检测:
import cv2 import numpy as np import glob # 棋盘格内角点配置,比如 9x6 代表每张图中有 9 列 6 行内角点 pattern_size = (9, 6) square_size = 30.0 # 单位:mm # 生成物理坐标系中的靶标点坐标 objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32) objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) objp *= square_size objpoints = [] # 3D 靶标点 imgpoints = [] # 2D 图像角点 images = glob.glob('calib_images/*.jpg') criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) for fname in sorted(images): img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, pattern_size, None) if ret: corners = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) objpoints.append(objp) imgpoints.append(corners) else: print(f"failed to detect: {fname}")这一步有两点要提醒。第一,我用了 cornerSubPix 做亚像素精化,它能利用角点邻域内的灰度梯度信息把角点定位到亚像素级,这是后续标定精度的基础。第二,如果某张图检测失败,不要直接跳过,先把图打开看一眼是不是反光或者运动模糊,能重拍就重拍,因为边缘姿态的样本本来就稀有,轻易放弃很可惜。
然后执行标定:
h, w = gray.shape rms, mtx, dist, rvecs, tvecs = cv2.calibrateCamera( objpoints, imgpoints, (w, h), None, None ) print("RMS re-projection error:", rms) print("Camera matrix:\n", mtx) print("Distortion coefficients:", dist.ravel())calibrateCamera 的输出 mtx 就是内参矩阵。RMS 是整体重投影误差的均方根,单位是像素。以我的经验,常规工业镜头用良好数据标定,RMS 应该小于 0.1px;如果超过 0.2px,就需要检查数据质量或者怀疑标定板是否平整了。
4. 从标定结果中提取光学中心,并验证准确性
4.1 如何从内参矩阵中读出光学中心偏移量
拿到 mtx 之后,光学中心非常直观:
cx = mtx[0, 2] cy = mtx[1, 2] offset_x = cx - w / 2 offset_y = cy - h / 2 print(f"optical center: ({cx:.2f}, {cy:.2f})") print(f"offset from image center: ({offset_x:.2f}, {offset_y:.2f}) px")举个例子。我手头一款 500 万像素工业相机,分辨率 2592 x 1944,用 12mm 定焦镜头标定后得到 cx=1308.4,cy=978.9。图像中心是 (1296.0, 972.0),所以光学中心偏移是 (+12.4, +6.9) 像素。换算成物理尺寸,这款相机的像素尺寸是 2.2μm,那么偏移量就是:
12.4 px * 2.2 μm/px = 27.3 μm 6.9 px * 2.2 μm/px = 15.2 μm几十微米的偏移,放在光学镜头装配公差里是常见的。但如果不校正,在做像素级特征匹配时就会引入系统偏置。
如果想知道光学中心偏移对角度有多大影响,可以结合 fx 来估算。fx 表示焦距对应的像素长度,所以光学中心相对光轴的角度偏移大约是:
angle_x = atan2(offset_x, fx) angle_y = atan2(offset_y, fy)沿用上面的例子,假设 fx=4320,那么 angle_x = atan2(12.4, 4320) ≈ 0.164°。对远距离测距来说,这个角度偏差会线性放大。比如测 10m 外的目标,单目测距系统如果不校正,横向距离可能会有 10m * tan(0.164°) ≈ 28.6mm 的系统误差。在 ADAS 这类场景里,这是不可接受的。
4.2 重投影误差、标准差与置信度
单看一个 RMS 不够,还要看每次相机位姿对应的单张重投影误差分布。OpenCV 没有直接给每个 view 的误差,但可以自己算:
def compute_reprojection_error(objpoints, imgpoints, rvecs, tvecs, mtx, dist): total_error = 0.0 total_points = 0 per_view_errors = [] for i in range(len(objpoints)): imgpoints_proj, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) err = cv2.norm(imgpoints[i], imgpoints_proj, cv2.NORM_L2) / len(objpoints[i]) per_view_errors.append(err) total_error += err * len(objpoints[i]) total_points += len(objpoints[i]) return total_error / total_points, per_view_errors mean_error, per_view = compute_reprojection_error(objpoints, imgpoints, rvecs, tvecs, mtx, dist) print("mean error:", mean_error) print("per-view min/max:", min(per_view), max(per_view))理想情况下每张图的误差都比较接近,不会出现某张特别大、其他都很小的状况。如果某张图误差明显偏大,通常是这张图的角点检测有毛刺,或者标定板在这张图中发生了弯曲。这种图建议剔除后重新标定。
另一个值得关注的是 cx、cy 本身的不确定度。OpenCV 的 calibrateCamera 不直接给内参标准差,但你可以用以下思路估计:把采集的图随机抽掉 20% 再用不同子集标定几次,看 cx、cy 的波动范围。如果不同子集之间 cx 波动超过 2 像素,说明当前数据对光学中心的约束不足,需要补充边缘位置和倾斜姿态的图。这个方法虽然粗暴,但在工程里非常实用。
4.3 验证方法:畸变校正、直线度、交叉验证
标定完不能立刻信,要验证光学中心和整体内参是否真的准。我推荐以下三个层次的验证,按成本从低到高排列。
第一层:畸变校正“眼见为实”。任选一张画面里有明显直线结构(比如门框、导轨、标尺边缘)的图,用刚才标定的结果做去畸变:
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, mtx, (w, h), cv2.CV_32FC1) undistorted = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR) cv2.imwrite('undistorted.jpg', undistorted)然后在校正后的图上把直线边缘叠加参考线,肉眼观察是否有残余弯曲。光学中心和畸变系数准确时,原先弯曲的边缘在校正后应该变成直线。如果你发现边缘弯曲量很小但整体图像中心区域和边缘区域亮度、放大率不协调,那就要怀疑光学中心偏移是否没吃透。
第二层:中心对称性验证。去畸变后再检测一次棋盘格,重投影误差如果仍然很小,说明整体内参一致。但这个只能验证整体一致性,不能单独把光学中心的准确性分离出来。更严格的验证是:拍摄一组以标定板中心大致对准画面中不同位置(比如左上、右下)的图像,对每张图单独计算它的外参,然后检查在不同位置估计出的光轴方向是否一致。因为光学中心是相机的固有属性,不应该随着标定板放置位置变化而漂移。如果发现光轴方向明显变化,说明内参里光学中心和畸变系数的耦合没有解干净。
第三层:交叉工具验证。用 OpenCV 跑一遍,再用 Kalibr 或者 MATLAB 跑一遍同组数据。两个工具如果 cx、cy 的结果相差在 1-2 像素以内,说明结果可信;如果相差超过 5 像素,优先怀疑畸变模型选择不同导致的主点吸收误差。lens model 差异会直接影响 cx、cy 的可比性,这时候要回到畸变模型选择上去分析。
5. 常见问题与排查技巧实录
5.1 问题:重投影误差压不下去,始终大于 0.2px
排查思路按顺序来:
- 标定板是否平整?陶瓷板一般没问题,纸板贴在亚克力板上容易有轻微弯曲,尤其尺寸大时中间会鼓起,这是重投影误差大的第一大来源。
- 光圈是否太大导致边缘画质下降?标定时建议收小 1-2 档光圈,保证整个画面角点清晰锐利,尤其是边角区域。大光圈下边缘的色散和模糊会直接影响亚像素角点定位。
- 角点亚像素精化窗口是否太小?cornerSubPix 的窗口从 (5,5) 改到 (11,11) 往往能改善一点,但窗口太大会引入邻近角点的干扰,需要权衡。
- 高光反射是否在棋盘格黑白交界处形成过曝?检查 raw 图中角点区域有没有像素值超过 250 或低于 5 的,有的话当前图宁愿删掉。
5.2 问题:cx、cy 偏离图像中心特别多,超过图像尺寸的 5%
这分两种情况。第一种是真偏移,通常是镜头固定结构装配公差大或者镜头本身光轴跑偏。这时候你不用怀疑标定过程,用另一种方法辅助确认:拍一张点光源图像(比如晚上拍一个小 LED),让它位于画面中心附近,看它在 raw 图像上的光斑亮度分布是否以某个像素为中心对称——光斑最亮且扩散最对称的位置,通常可以粗略看作 point spread function 的峰值位置,可以近似光学中心投影点。两个方法的偏差如果在 3-5 像素内,说明结论可信。
第二种是标定数据本身不足导致优化器把 cx、cy “推”到了不合理位置。尤其是当所有标定板都放在画面中心附近、很少覆盖边缘时,cx、cy 和畸变系数高度耦合,算法可能用一个偏心的主点搭配一组畸变来拟合同样一组观测。解决办法是补拍边缘位置的图,重新标定。
5.3 问题:不同标定工具给出的光学中心不一致
这是我在实际项目中遇到比较多的现象。同一组图,OpenCV 得到 cx=1288,Kalibr 得到 cx=1295,差了 7 个像素。别急着下结论说哪个工具错了,先检查两点:
- 畸变模型是否一致。OpenCV 默认是 pinhole + plumb_bob(即 Brown-Conrady),Kalibr 里对应的是 pinhole-radtan。如果你用的是 pinhole-equidistant 或者 Kannala-Brandt,主点数值不直接可比。
- 图像坐标原点是否一致。有些工具默认原点在图像左上角(OpenCV),有些可能在传感器中心,导致 cx、cy 值直接差 W/2、H/2。
- 是否一个工具用 OpenCV 的 findChessboardCorners 检测角点、另一个用棋盘四边形检测,角点坐标本身就有亚像素级差异,累计到主点上就会放大。
在工程上,选定一个和你 ISP 流程兼容的工具,固定使用,不要混用。除非你在做高精度标定,否则跨工具比较意义有限。
5.4 问题:标定板角点在边缘位置反复检测失败
这是广角镜头或者大畸变镜头里常见的问题。棋盘格跑到画面边缘时,畸变导致方格变成非常不规则的形状,findChessboardCorners 的默认参数可能检测不出来。解决办法:
- 用 findChessboardCornersSB 替代。基于 SAT(Summed-Area Table)加速和更稳健的角点检测算子,对模糊和畸变的鲁棒性更好。
- 把标定图分辨率降低一半去检测,检测到角点后再映射回原分辨率做亚像素精化,这个方法对边缘大畸变很有效。
- 改用圆点标定板(Circle Grid)。圆点形变后的椭圆中心检测对大畸变更稳健,不过圆点的圆心检测对透视畸变的敏感度更高,需要配套算法校正。
6. 后续扩展:ISP 管线中的光学中心应用与多传感器标定
6.1 光学中心在 ISP Pipeline 里的几个落点
标定出光学中心之后,不是把它写进一个配置文件就完事了,而是要放进 ISP 流程里真正起作用。我梳理一下常见流程中光学中心参与的关键环节:
镜头阴影校正(LSC)。ISP 的 LSC 模块通常会预设一个 shading 曲面,曲面的原点就是光学中心投影点。偏移量不校正时,曲面中心对不上实际光斑中心,增益补偿在边缘不收敛,图像四角明暗不均。实际上做 LSC 标定时,很多人会利用光斑中心来对齐遮光校准(shading calibration),而这个光斑中心就是光学中心。
畸变校正(Distortion Correction)。在 ISP 中做电子防抖(EIS)、超广角校正、图像拼接时,几何变换表(LUT)的生成必须考虑光学中心。多数 ISP 的 DMA 或 GDC(Geometric Distortion Correction)模块会提供 center_x、center_y 参数,你直接填入标定得到的 cx、cy 即可。填错的话,校正表在图像边缘会产生不可忽略的残余畸变。
数字变焦和裁剪。如果 ISP 支持 sensor crop 或者电子变焦,crop 会改变图像坐标系,因此光学中心也要随之平移。一个常见错误是裁成 1080p 后仍然沿用全分辨率下的 cx、cy,导致裁剪后的图像几何关系全错。每次 crop 都重新计算:
cx_crop = cx_orig - offset_x_of_crop_region cy_crop = cy_orig - offset_y_of_crop_region6.2 与手眼标定、Lidar-IMU 标定的联动
光学中心标定是很多上层标定的前置条件。比如手眼标定(Hand-Eye Calibration),无论你用 eye-in-hand 还是 eye-to-hand,都要靠相机观测标定板来估算机械臂末端和相机之间的位姿变换。如果相机内参里光学中心偏移未校正,手眼标定求解出的外参会带着系统误差,机械臂引导时末端定位就会漂移。
再比如 Lidar-IMU-相机多传感器融合系统,相机和激光雷达联合标定时,相机内参不准确会直接影响标定板上的点云与图像特征之间的匹配。在项目里做这类联合标定之前,我通常先把相机内参单独标定一遍,并且明确记录分辨率、镜头焦距、光圈状态,避免后续传感器状态漂移造成标定失效。
至于 9 点标定(九点标定),它在原理上是一个平面映射(通常用仿射或单应性变换)标定,和光学中心标定是两码事。九点标定做的是像素坐标到机械手平面坐标的映射,它会把相机内参误差的一部分“吸收”进映射表中,所以如果你的系统用的是九点标定,有时可以不单独抠光学中心。但一旦系统中有多个平面、多个工作高度,或者相机姿态有变化,这种映射就不再稳定,最终还是得回归到准确的相机几何标定。
6.3 一个实用技巧:用点光源粗测光学中心辅助验证
除了棋格盘标定法,我偶尔会用一种更直观的粗测方案作为交叉验证。具体做法是这样的:晚上或者暗室里放一个直径很小的点光源(LED 灯珠配上小孔遮罩,直径控制在 2mm 以内),让相机的光圈缩小到 F8-F11,拍摄点光源图像。由于光圈很小,光斑接近艾里斑的投影,亮度分布近似对称。把光斑的质心提取出来,移动点光源使得质心正好落在图像中心——这时候光轴方向对准了这个点光源,质心位置近似就是光学中心。
这个方法的精度受限于光斑质心提取误差,一般也就几个像素,不能替代精确标定。但它有两个好处:一是原理简单,适合给刚接触标定的同事讲清楚光学中心到底指什么;二是可以反向验证张正友标定的结果有没有系统性错误。如果张正友给出的 cx 和点光源法测出来的质心差出几十像素,那多半是标定数据或流程出了问题。
最后再分享一点个人习惯:在做任何 ISP 标定项目前,我永远先把分辨率、帧率、镜头工作温度、光圈、焦距这些状态参数记进标定日志。光学中心会随温度变化漂移(镜筒热胀冷缩、胶水应力释放),还会随对焦位置变化。同一个相机,在 25°C 标定的光学中心和 50°C 工作温度下测得的光学中心,可能会有好几个像素的差异。条件允许的话,在目标工作温度下做一次复标,把两次的 cx、cy 折中或者分别存成温度补偿表,是治本的做法。这条经验不是标定文档里会教你的,但做车载或户外相机的同学一定深有体会。