做相机ISP调试这些年,我踩得最多的坑之一,就是光学中心偏移没算准。很多刚入行的朋友拿到一颗新sensor,第一件事就是打开亮场、看中心十字线,然后直接在代码里把(width/2, height/2)当成主点去配LSC、去校畸变,结果出来的图边缘色偏、画面发飘、双摄死活对不齐。今天就把这块东西彻底讲透,从光学中心到底是什么、为什么会偏,到怎么用标定板把偏移量算出来,再讲怎么把结果真正用进ISP pipeline里。这篇东西既写给做ISP的工程师,也写给做相机标定、双摄模组、机器视觉项目的人,内容以实操为主,原理只讲够用的深度,争取看完就能上手去算。
1. 光学中心到底是什么?偏移又是哪来的?
1.1 先分清几个容易混的概念
说光学中心之前,得先把几个词理清楚,不然后面所有公式都白搭。常说的“光学中心”在标定语境里一般指主点(Principal Point),也就是镜头光轴与传感器感光平面的交点。理想情况下,光轴垂直穿过sensor正中心,主点就等于图像的几何中心(width/2, height/2),但现实里这事基本不存在。
还有两个概念容易被绕进去:一是“镜头光学中心”,指的是镜片组里光轴通过的那个位置,这是纯光学概念;二是“图像中心”,就是像素阵列的几何中点。标定时我们关心的是这两者之间的偏差,也就是主点像素坐标相对图像几何中心的偏移量。这个偏移量的单位可以是像素,也可以换算成物理尺寸(微米或毫米)。
另外很多人会拿“畸变中心”和“主点”混用,严格来说畸变中心是畸变模型中各项对称性所围绕的中心,对大多数普通镜头而言,它和主点非常接近,工程上经常直接拿主点当畸变中心用。但如果你做的是高精度测量类项目,最好把二者分开标定,否则畸变残差会悄悄吃掉你的精度。
1.2 偏移是哪里来的:装调公差与镜片偏心
一块sensor和一颗镜头组装到一起,光轴要精确落在sensor正中心,对工艺要求非常高。实际模组生产中,偏移主要来自三个环节。
第一是镜头本身的镜片偏心。镜片在镜筒里是靠机械结构固定的,每个镜片的光轴不可能完全重合,装配公差会让整组镜头的实际光轴相对机械轴产生一个倾斜或者平移。镜片越多、光圈越大,这个偏差越难控制。
第二是镜头与sensor的贴合位置。镜头座焊接或者胶合在PCB上时,哪怕只有0.1mm的位置度误差,在sensor的像素尺寸只有1微米左右的情况下,就已经偏差了100个像素了。通常模组厂会把光轴偏移控制在几个像素到几十个像素之间,低端模组甚至可能偏移超过图像宽度的5%。
第三是sensor本身的封装。sensor die在封装时相对于封装基板也会有贴装误差,感光区的几何中心与封装中心并不是严格重合的。这意味着就算镜头座装得很正,光轴相对感光区中心依然可能是歪的。
我还遇到过一种更隐蔽的情况:同一颗sensor配不同型号的镜头,主点位置明显不同;同一颗镜头换到另一颗sensor上,偏移量也不一样。所以光学中心偏移是“镜头+模组+sensor”三者耦合出来的结果,必须整机装配完成后逐颗标定,不能只靠图纸上的公差链去推算,这就是为什么产线上需要有光学中心标定这道工序。
2. 为什么ISP标定必须先算光学中心偏移?
2.1 主点偏移会影响哪些ISP模块
如果你只是做普通拍照,主点偏移几个像素可能看不太出来。但一旦涉及像素级算法,主点就是无数计算的原点,它偏了,后面全偏。
最直接受影响的是镜头阴影校正(LSC)。LSC的本质是给画面不同位置乘一个增益,补偿镜头边缘亮度衰减,而增益的分布是以光轴中心为圆心、按径向变化的。如果你把LSC中心设置成图像几何中心,而实际光轴偏移了50个像素,你会发现暗角的补偿会不对称:一边压过头,另一边还没补够。这也是调试LSC时最经典的坑:查了半天LSC表格,结果发现是中心点给错了。
第二个受影响的是畸变校正。径向畸变的数学模型是围绕畸变中心展开的,即校正时需要先算出像素点相对畸变中心的半径,再做多项式映射。如果畸变中心偏了,校正完的画面会出现残留的“膨胀”或“枕形”变形,直线依然不直,边缘尤其明显。
第三个是防抖(EIS/OIS)。电子防抖需要根据陀螺仪数据做旋转补偿,旋转的中心就是主点。主点偏了,防抖裁切后画面会有一个整体的、随旋转角度变化的平移,表现出来就是“画面飘”。OIS的shift补偿同理,光学防抖镜头移动时也是绕光轴中心在转。
还有双摄和多摄模组的对齐标定。双目极线校正时,两个相机的内参都会被用上,如果其中一个主点偏移没算准,极线校正后对应点在行方向就会产生残留视差,立体匹配的精度直接崩掉。
2.2 ISP pipeline里主点的“位置”
展开来讲一下ISP pipeline中主点是在哪个环节被消费的,这样你调试时才知道去哪改。
在常见的ISP链路中,sensor输出的RAW图先经过坏点校正(DPC),然后做黑电平校正、镜头阴影校正(LSC)、去噪(NR)、白平衡(AWB)、颜色校正(CCM)、Gamma、畸变校正(LDC)、缩放裁切(Crop/Scale),最后输出YUV或RGB图像。其中的LSC和LDC都要用主点坐标,有的ISP还把主点用在局部对比度增强、HDR融合的对齐环节里。
以我调试过的一颗芯片为例,ISP寄存器里会有一组lsc_center_x、lsc_center_y,另一组ldc_center_x、ldc_center_y,还有一组eis_center_x、eis_center_y。这三组坐标的默认值往往就是传感器输出分辨率的一半,也就是几何中心。你要做的就是把光学中心偏移算出来,然后分别写进这些寄存器。如果ISP支持“用主点坐标作为LSC/畸变校正中心”的选项,别忘了在驱动配置里把这个开关打开,否则填了也白填。
2.3 不标定会怎样:一个实测例子
说个具体数据,我之前调试一颗500万像素的模组,输出分辨率为2592x1944,sensor单像素尺寸1.12um。标定出来的主点像素坐标是(1305, 983),而几何中心是(1296, 972),也就是说X方向偏移了9个像素,Y方向偏移了11个像素,换算成物理尺寸大约是10um和12.3um。
光看数字好像不大,但放到LSC上就明显了。暗角补偿曲线在半径r处随距离变化,中心偏了之后,画面左边缘和右边缘同半径处的增益值不一样,实测边缘色差(R/B通道比例)左右差了3%左右,人眼在低照度下能明显看出画面一侧发暗、一侧正常。畸变校正后的棋盘格画面,四周直线仍有1~2个像素的波浪状残差。如果做双目结构光或ToF,这个级别的误差会造成深度图边缘出现条状噪点。
所以别小看这十几二十微米的偏移,在ISP这种每个像素都要算的领域,主点就是“地基里的基准点”,地基歪了,上面的楼层越高歪得越明显。
3. 核心原理:怎么从标定板算出主点坐标
3.1 针孔模型与内参矩阵:主点藏在哪
要算光学中心偏移,最标准的做法是通过相机标定求解内参矩阵。这里只需要用针孔模型就可以了,原理很简单。
在针孔模型里,空间点P(X, Y, Z)投影到图像点p(u, v)的关系用内参矩阵K表示:
u fx 0 cx X v = 0 fy cy * Y 1 0 0 1 Z其中fx、fy是归一化焦距(单位也是像素),cx、cy就是我们说的主点坐标。注意这个cx、cy是相对于 RAW 图或标定输入图的坐标原点(通常左上角)的像素坐标,不是相对于图像中心的偏移量。你要求“偏移量”,得拿它减去(width/2, height/2)。
有一件事必须要提醒:fx、fy和cx、cy的单位都是像素。要换算物理偏移,还必须知道sensor的像素尺寸,比如上面的1.12um/像素,乘以像素数就能得到微米值。如果你不知道像素尺寸,可以在sensor datasheet里找 pixel size,或者用 sensor 靶面尺寸除以总像素数得到。
3.2 为什么棋盘格能解出主点
张正友标定法是目前用的最广的方法,OpenCV和Matlab的标定工具箱都基于它。它的核心思路是拍摄不同姿态下的平面棋盘格,对每张图检测角点,利用“棋盘格平面”和“图像平面”之间的单应矩阵建立约束方程。
单应矩阵H是3x3矩阵,它把棋盘格平面上的点映射到图像点,和相机内参、外参的关系是:
H = K * [r1, r2, t]其中r1、r2是旋转矩阵的前两列。因为我们知道每幅图上角点的“像素坐标”和“棋盘格空间坐标”是一一对应的,所以可以先求出每张图的H。然后利用旋转矩阵列向量之间的正交性(r1^T * r2 = 0,|r1| = |r2|)构建出关于fx、fy、cx、cy以及畸变系数的线性方程组,多张图联合起来就能把内参解出来。
说白了,棋盘格的作用是给相机提供了一个“已知尺寸的参考坐标系”。棋盘格格子大小要精确测量,因为它决定焦距的物理尺度,但对主点本身的解算影响不算大,真正影响主点精度的是拍摄姿态的多样性和角点检测的精度。
从数学角度看,单张棋盘格图像只有8个自由度(3x3矩阵去掉一个尺度因子),而内参有4个、外参有6个,自由度不够,所以单张图解不出完整内参。至少需要3张以上姿态差异大的图像,实际使用时我一般推荐拍15-20张,保证棋盘格在视场的各个区域都有分布,尤其是边缘位置要覆盖到。
3.3 畸变模型与主点的耦合问题
标定主点时,最大的干扰来自镜头畸变。因为畸变模型里也有一个“畸变中心”,如果畸变中心和主点重合,那么两者可以一起解;如果不重合,标定算法会把它们耦合在一起,导致主点结果被畸变“带偏”。
OpenCV默认的畸变模型只包含径向和切向畸变系数,没有单独对“畸变中心”建模,它默认畸变中心就是主点。所以用OpenCV标出来的cx、cy实际上是“主点和畸变中心的折中”,对大多数镜头来说这个误差在可接受范围内(通常1-3个像素)。但如果你用的是鱼眼镜头或者畸变很大的广角镜头,这个误差会变大,这时候需要改用支持独立畸变中心的高精度标定算法,或者用多点拟合的方法单独做畸变中心标定。
再提一个容易踩的坑:标定输入图的分辨率和实际ISP处理分辨率不一致时,主点坐标必须按比例缩放。比如你用2592x1944的RAW标定出主点是(1305, 983),但ISP实际输出是1920x1080,且这个1080p是通过中心裁切得到的,那么主点在1080p图中的坐标要重新算。如果是整体缩放,就按缩放系数乘;如果是裁切,还要减去裁切的偏移。这个换算关系搞错了,后面所有ISP模块全都偏。
4. 实操:用棋盘格完成光学中心偏移标定
4.1 准备工作:标定板、拍摄环境与工具
工具上我推荐三种方案,按精度和成本排序:
- 高精度方案:专业玻璃棋盘格标定板(陶瓷或玻璃基板,图案精度在微米级),搭配工业相机或架好三脚架的相机,使用Matlab的Camera Calibrator,或者OpenCV。适合量产前精标。
- 常规方案:打印在平整硬板上的棋盘格(A3或A4,用哑光纸,不要覆膜反光),用普通相机或手机拍摄。适合前期算法验证。
- 产线快测方案:使用带背光的点阵标定板或同心圆标定板,配合厂家私有标定算法,一次拍一张图就能得到主点。这种一般用于模组厂量产线,因为速度快但原理是另一套,后面有机会再单独讲。
棋盘格参数方面,我建议用 12x9 或 15x11 的格子阵列,格子边长用 20mm~30mm。格子数太少了,单张图信息量不够;太多了角点容易受镜头畸变影响,边缘角点检测误差变大。事前拿游标卡尺或者尺子量一下打印后的实际格子边长,别直接用设计的理论值,打印机的缩放误差会直接引入标定误差。
拍摄环境要保证光照均匀、无高光反射。棋盘格平面不要反光,最好有漫射光照明。相机固定,棋盘格在不同姿态下拍摄,要求:棋盘格在画面中占 1/4 到 2/3 的像素面积;姿态覆盖左右偏转、上下俯仰、平面内旋转;至少有几张棋盘格出现在画面边缘区域。
我特别强调一点:不要把棋盘格每次都放在画面正中央转来转去,那样内参中的焦距和主点解算会退化,标定结果看着“能过”,实际主点偏差很大。要让棋盘格“跑”到画面四个角和四边,这样畸变模型才能被有效约束。
4.2 角点检测与数据质量检查
拍完图之后,第一步是角点检测。OpenCV提供cv2.findChessboardCorners,检测前最好先对图像做一下灰度和直方图均衡,尤其是光照不均匀的图。角点检测参数里一个容易被忽略的点是CALIB_CB_ADAPTIVE_THRESH和CALIB_CB_NORMALIZE_IMAGE这两个标志,建议都开上,对面内旋转角度大的图有奇效。
角点检测完成后,用cv2.cornerSubPix做亚像素细化。这一步不能省,因为主点的计算精度直接受角点坐标精度影响,亚像素细化能把角点精度从1个像素提升到0.1像素以下。
数据质量检查方面,我习惯把检测到的角点画回原图看一遍,重点看边缘区域的角点是否和真实棋盘格角点重合。打印棋盘格如果有轻微弧度或纸张褶皱,边缘角点会偏2-3个像素,这种图要直接删掉,否则会把畸变模型带歪。另外,如果某张图棋盘格在画面中太小(角点间距小于10像素),也建议删掉,因为角点间距太小,图像的亚像素精度优势就发挥不出来了。
一个经验数值供参考:对于1000万像素级别的相机,有效标定图至少保留12张以上,且其中至少有3张的棋盘格覆盖到了画面边角区域。少于这个数,主点解算的稳定性会明显下降。
4.3 用OpenCV求解内参并提取主点
下面是完整可运行的Python代码,用来标定内参并计算光学中心偏移量。
import cv2 import numpy as np import glob # 棋盘格参数:内角点数量,假设你的棋盘格是 10x7 个内角点 CHECKERBOARD = (10, 7) # 实际测量得到的格子边长,单位:毫米 SQUARE_SIZE = 25.0 # 准备对象点:棋盘格平面上的三维点,z=0 objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) objp *= SQUARE_SIZE obj_points = [] # 实际3D点 img_points = [] # 图像2D点 images = glob.glob('./calib_images/*.jpg') for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 角点检测 ret, corners = cv2.findChessboardCorners( gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE, ) if not ret: print(f"跳过图像: {fname}") continue # 亚像素细化 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) obj_points.append(objp) img_points.append(corners_refined) # 可视化校验(可选) draw = cv2.drawChessboardCorners(img.copy(), CHECKERBOARD, corners_refined, ret) cv2.imwrite('./debug/check_' + fname.split('/')[-1], draw) # 标定 ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None ) fx = mtx[0, 0] fy = mtx[1, 1] cx = mtx[0, 2] cy = mtx[1, 2] print("内参矩阵 K =") print(mtx) print(f"fx = {fx:.3f}, fy = {fy:.3f}") print(f"主点 cx = {cx:.3f}, cy = {cy:.3f}") # 图像几何中心(以标定时输入图像分辨率计算) img_w = gray.shape[1] img_h = gray.shape[0] center_x = img_w / 2.0 center_y = img_h / 2.0 print(f"图像分辨率: {img_w} x {img_h}") print(f"几何中心: ({center_x:.3f}, {center_y:.3f})") # 像素偏移量 offset_px_x = cx - center_x offset_px_y = cy - center_y print(f"光学中心像素偏移: dx = {offset_px_x:.3f} px, dy = {offset_px_y:.3f} px") # 物理偏移量:填入sensor的像素尺寸,单位 um/pixel pixel_size_um = 1.12 # 示例:1.12um offset_um_x = offset_px_x * pixel_size_um offset_um_y = offset_px_y * pixel_size_um print(f"光学中心物理偏移: dx = {offset_um_x:.3f} um, dy = {offset_um_y:.3f} um")跑完之后你会得到几个关键值:cx、cy就是主点像素坐标,offset_px_x、offset_px_y就是相对几何中心的偏移量。
这里要注意,cv2.calibrateCamera输入图像的尺寸就是灰度图的宽高(gray.shape[1], gray.shape[0])。如果你们的标定输入是RAW导出的,别忘了把尺寸和实际ISP输入对齐,或者明确记录,后面应用时要按分辨率缩放。
4.4 如何判断标定结果靠不靠谱
算出主点坐标之后,不要急着写进寄存器,先做几个检查。
第一,看重投影误差。OpenCV返回的ret值就是平均重投影误差,单位是像素。这个值通常建议小于0.3像素,工业级标定能到0.1像素以下。如果重投影误差大于0.5像素,说明标定图质量差或畸变模型不匹配,结果不可信。
第二,看主点坐标是不是在图像范围内。理论上一颗正常的摄像头,主点应该在图像区域内,而且通常距离几何中心不会超过图像宽度的10%-20%。如果你标出cx是负的或者远大于图像宽度,九成是代码里棋盘格参数写错了,或者标定板角点检测出错,重投影误差也会很大。
第三,多跑几次标定,用不同的图像子集,看主点坐标的稳定性。比如取15张图跑一次,再随机去掉3张跑一次,两次主点差值应该在1-2个像素以内。如果差别很大,说明某些姿态的图质量差,需要重拍。
我遇到过一种情况是标定结果每次都“很稳定”,但主点偏了二十几个像素,后来发现是打印棋盘格纸张轻微弯折,导致边缘角点在亚像素细化时系统性偏移。把棋盘格贴在玻璃板上压平重拍之后,主点立刻回到了合理范围。所以标定时一定要保证标定板平、硬、不反光。
5. 常见问题与排查技巧实录
5.1 主点算出来为负值或超过图像边界
这个我见过不少新手踩。遇到这种情况,先别怀疑数学,九个原因里八个是图像编号顺序搞错了,或者角点检测时棋盘格是旋转的。
具体检查:确认CHECKERBOARD的宽高顺序是不是和棋盘格实际内角点一致。比如一个10x7的格子阵列,如果长边有10个内角点,CHECKERBOARD = (10, 7)是对的;如果你写反成(7, 10),角点会检测成横向少、纵向多,检测源数据形状对不上,结果自然乱穿。
另外注意,cv2.findChessboardCorners返回的角点顺序是按棋盘格排布的,如果标定板在画面中旋转了90度,OpenCV的角点排布方向和对象点定义的顺序一定要一致。如果单张图旋转了,对象点的坐标参考方向不变,相当于外参变化了而已,这不影响内参解算。会出问题的是:标定时用了不同分辨率的输入图,或者一张图里棋盘格方向变了而你手动修改了内角点数量,这种低级错误会导致主点完全偏离。
如果确认代码没问题,就看标定图。某张图里棋盘格太靠近画面边缘,且镜头畸变明显,角点检测可能检测到错误的亚像素位置,删掉这张图重标即可。
5.2 标定结果不稳定,换一组图主点就变
主点不稳定,最常见的原因是标定图的姿态覆盖不够。如果所有棋盘格都大致平行于图像平面,也就是相机正对着拍,那么每张图的单应矩阵近似相同,内参解算矩阵接近奇异,主点方向上的约束很弱,结果就会在几像素到十几像素之间漂移。
解决办法是增加棋盘格绕X轴和Y轴的倾角,一般控制在15度到30度之间比较合适。倾角太小约束不够,倾角太大边缘角点变形严重、检测精度下降。
第二个原因是畸变系数和主点在数值上的耦合。尤其是径向畸变的二次项k1,它对图像边缘的像素坐标影响很大,而主点又正好决定“边缘的距离从哪算起”,两者自然会互相拉扯。要减弱这种耦合,除了多姿态拍摄,还可以先固定畸变系数初值,或者用更多的大畸变边缘图像增强约束。
如果是产线场景,用固定光照、固定姿态的“单张标定”结果不稳定,大概率是模组本身的元器件没固定好,比如镜头座松动或者sensor位移,这种情况不要只调算法,先检查机械结构。
5.3 畸变校正之后,主点偏移要重新算吗
这个问题很多人问我。如果ISP里的畸变校正模块(LDC)用的是你自己标出来的主点作为畸变中心,那么校正之后,理想情况下主点会被“搬”到图像几何中心附近,前提是校正算法把主点作为输出坐标系的原点。但实际上不同ISP实现不一样,有些校正完只是变了形变分布,主点坐标并不变,所以你需要在你们自己的平台上验证。
一个实用的办法:对同一张棋盘格图,跑畸变校正前和校正后的角点检测,用校正后的角点重新拟合一条直线,看所有横竖直线是否都拉直了。如果还有系统性的弧线,就微调畸变中心的x/y值,直到直线残差最小。这个过程我习惯在调试时通过串口工具或者寄存器写入工具在线调,实时看效果。
5.4 分辨率裁剪和缩放后主点怎么换算
ISP输出分辨率往往不是sensor原生分辨率,常见的模式有 center crop、scale、binning,每种情况主点换算方式不一样。
- 如果是整体缩放,比如从2592x1944缩放到1920x1080,注意这对不是等比缩放,严格来说是两轴分别缩放,所以主点要分别乘上x和y的缩放系数:
cx_new = cx_old * (1920.0 / 2592.0),cy_new = cy_old * (1080.0 / 1944.0)。 - 如果是中心裁切,比如从2592x1944裁切到1920x1080,需要先算出裁切窗口的左上角坐标
(offset_x, offset_y),然后cx_new = cx_old - offset_x,cy_new = cy_old - offset_y。注意裁切后的几何中心也变了,你如果要算“相对新图像的偏移量”,要用新的几何中心重新减。 - 如果是 binning(像素合并),比如2x2 binning,那相当于sensor每4个像素合并成1个,主点坐标要除以2。同时像素尺寸要乘以2,因为单像素代表的物理尺寸变大了。
这个换算环节特别容易出错,因为ISP的缩放往往不是简单等比缩放,可能先做水平缩放再做垂直缩放,中间还有对齐操作。稳妥的做法是查ISP的寄存器说明,弄清楚crop窗口的起点坐标和缩放系数,再去算主点位置。我见过有工程师直接拿标定出的RAW主点写到1080p输出下,结果整个LSC中心偏了快100个像素,画面一边亮一边暗,排查了整整两天。
6. 从标定到落地:如何把偏移量真正用进ISP
6.1 把主点坐标写进ISP寄存器
算出了cx、cy之后,接下来要把它转换成ISP寄存器数值。每颗ISP芯片的寄存器定义不同,但一般就两类:一类是绝对像素坐标,直接填cx、cy;另一类是基于某个基准点的偏移量,寄存器里可能存的是相对量。两种情况下都要注意坐标原点的定义,有的ISP以左上角为原点,有的以传感器中心为原点,还有的以输出ROI左上角为原点,填反了符号,偏移方向就反了。
调试时建议先把LSC中心写进去,拍摄一张均匀光照下的亮场图,用工具查看画面四个角的亮度是否对称。如果对称了,说明主点的X/Y至少没填错。再做畸变校正测试,拍摄一张网格图或者直线密集的场景,观察画面边缘直线是否被拉直。两个测试都过了,再去验证防抖、双摄对齐这些更高层的功能。
还有一个细节:有些ISP寄存器里的坐标单位不是像素,而是带小数点的定点数,比如Q16格式,实际坐标值等于寄存器值除以65536。遇到这种情况要给寄存器填(int)(cx * 65536.0)。填错小数点的位宽,偏移量就会大得离谱,画面直接花掉。
6.2 验证偏移量计算是否正确的三个小实验
写一个独立的小实验验证主点对不对,很实用。这里分享三个我常用的方法。
实验一:对称性测试。在均匀光照下拍一张纯白或纯灰的图,在LSC关闭或默认情况下,观察四个角的暗角程度是否不一致。如果主点偏了,暗角会明显不对称。这个方法只能定性判断,优点是不需要任何标定板,开机就能查。
实验二:旋转对称测试。用一张带有明显圆环特征的标定板(比如同心圆板),绕相机光轴拍摄多张图,然后把图像绕某个候选中心旋转对齐,看哪个中心能让旋转前后的图像残差最小。这个中心就是主点位置,可以验证棋盘格标定的结果。这个方法适合做独立复核。
实验三:畸变直线验证。拍一张密集网格图,用畸变校正算法把图像展开,检查画面边缘的网格线是否拉直。如果边缘网格还残留弧线,试着把LDC中心往弧线凸起的方向微调,反复几次,找到直线残差最小时的中心坐标,这个经验值可以和棋盘格标定的cx、cy对比,两者通常吻合得很好。
6.3 量产烧录与一致性管理
量产时不可能每颗模组都拍15张棋盘格做完整标定,所以产线上一般用工厂标定方式。比较常见的是“单张图快标定”:相机对着一个高精度的点阵或网格标定板,只拍摄一张图,利用已知的标定板物理参数和角点位置,直接解算出主点和镜头畸变参数。这种方法的精度比多张图标定低一些,但胜在速度快,能集成到产线测试程序里,每颗模组几秒钟就能标完。
标定完的数据通常以标定参数文件的形式烧录到模组的OTP(One Time Programmable)或Flash里,ISP驱动在初始化时读取,然后写入对应的寄存器。这里最关键的是要保证标定数据和当前模组对应,不能错位。很多量产品控问题就出在数据烧录错位:A模组的主点坐标烧到了B模组上,结果出厂画面都是偏的。
如果你负责量产标定,建议把标定结果的判据做严格一点:主点偏移量一般要求落在工厂公差范围内,比如长边5%以内,超出直接判为不合格,回炉重测或者返修。这能帮你把装调环节的问题提前暴露出来,而不是等到终端客户手里才翻车。
7. 最后再分享一点个人经验
做标定这件事,最忌讳的就是只记步骤、不懂原理。光学中心偏移的计算,本质上就是引出“相机的数学模型”和“物理装配的偏差”之间的关系,搞懂了内参矩阵、单应矩阵和畸变模型,你不仅能算出主点,还能判断这个主点算得对不对、在哪些条件下会不可靠。
我在实际项目里养成的一个习惯是:每次拿到新模组,先不做任何ISP调试,第一件事就是拍一组棋盘格把主点和畸变系数标出来,然后存档,再开始后面的LSC、LDC、AE/AWB调试。所有和几何相关的模块都以这份标定数据为基准。这样看起来多花了一两个小时,但后面省下的排查时间远不止这些。你们如果还在用“默认图像中心”硬调ISP,我建议下次也先把主点这个地基打牢,很多莫名其妙的对称性问题会一下子消失。