
1. 标定到底在解决什么问题为什么不能跳过相机标定与矫正几乎是我见过的视觉项目里最容易被临时抱佛脚、又最容易埋雷的环节。很多同学一上来就急着跑算法、做检测结果到了需要测量尺寸、恢复三维结构、做多目对齐的时候发现画面里的物体形状是扭曲的两条本该平行的线怎么都对不齐这才想起来要找标定的资料。说实话这个顺序基本是反的。先想清楚一件事相机本质上是把三维世界投射到二维平面的设备但它不是理想的小孔成像镜头的光学结构、装配误差、传感器平面的微小倾斜都会让图像产生各种畸变。其实你可以把相机理解成一块哈哈镜——中心区域还算正常越靠近边缘直线会变弯、距离会被拉伸或压缩。标定要做的就是给这块哈哈镜建立一张矫正表记录下它在每个位置把光线掰弯了多少然后反着算回去把图像还原成符合真实几何关系的画面。我在某视觉检测项目里就吃过亏。当时一个负责测量零件孔径的Demo在图像中心附近测量的结果还算准一旦零件落在视野边缘孔径测量值直接偏了将近两个百分点。后来排查了半天才发现根本不是算法的问题是压根没做畸变标定。重新标定并矫正之后边缘区域的测量误差立刻回到了0.3%以内。那次之后我彻底明白了一个道理标定不是锦上添花而是视觉测量系统里最基础的地基。这篇文章面向的读者我觉得有两类一类是做视觉算法、机器视觉集成、三维重建、SLAM方向的同学需要系统地把标定这件事搞清楚另一类是已经在用OpenCV之类工具做开发但只停留在调个函数、出几个系数阶段的工程师。我会尽量把原理、步骤、参数选择、坑点都串起来讲保证看完之后你能独立完成一次完整且高质量的标定与矫正而不是只跑通一个Demo。2. 标定方案选型棋盘格、圆点阵列还是ChArUco标定的第一步其实是选靶标也就是标定板。不同标定板的原理和应用场景差异很大选错了后面会特别别扭。2.1 三种主流标定板的适用场景最常见的是棋盘格标定板。它的优点是角点检测算法极其成熟OpenCV里的findChessboardCorners对棋盘格的鲁棒性很高而且角点位置是两条黑白块的交叉点亚像素精度好提周围也没有歧义特征。缺点是棋盘格必须完整出现在画面里才能检测到全部角点如果你用的镜头视场角比较小或者标定板尺寸没买对稍微靠近一点就出界了采集效率会低不少。圆点阵列标定板在某些场景下更实用。圆点的圆心检测不受透视投影影响在倾斜视角下依然能保持较高的定位精度而且Halcon等商业库对圆点标定板支持得很好。我自己用下来的感觉是圆点阵列在大倾角、大畸变场景下鲁棒性更好但如果图片模糊、光照不均匀圆点分割容易出现粘连反而比棋盘格更脆弱。ChArUco板算是棋盘格和ArUco码的结合体。它在棋盘格每个格子里嵌入了独立的编码标记优势是即使只有部分板子在画面里也能通过编码识别出当前角点的全局编号这对大标定板、近距离采集、以及需要标定大视场相机的场景非常友好。我在标定大靶面工业相机时用ChArUco板明显比普通棋盘格省心因为不用费劲保证整块板都在视场内只要有足够多的有效角点就行。2.2 标定板材质与尺寸怎么选标定板要平整这一点怎么强调都不过分。A4纸打印的棋盘格贴在桌面或硬纸板上短期做个算法验证问题不大但你要是用在精密测量项目里纸的轻微褶皱、热胀冷缩、受潮变形都会直接影响标定精度。我建议量产或精度要求高的项目直接用玻璃基底或铝基底的陶瓷标定板热膨胀系数小表面平整度能控制在微米级别。尺寸选择有个简单逻辑标定板上的方格边长决定了你在工作距离上能否稳定提取角点。一般原则是让每个方格在图像里至少占15到20个像素否则角点定位的亚像素精度会明显下降。具体做法是先估算你正常工作距离下相机能覆盖的视野宽度再除以10到20作为单个方格的期望边长。比如视野宽度是400mm你想让画面里有10到15个格子那方格边长25mm左右会比较合适。2.3 我推荐的标定板参数组合这里直接给一个经过多次验证的参考配置棋盘格推荐12x9或14x10的规格内部角点数分别是11x8和13x9保证画面中能同时出现足够多的特征点方格边长根据实际工作距离而定典型值在20mm到50mm之间如果预算允许选氧化铝陶瓷基板或者表面覆有防反光涂层的玻璃板。注意使用棋盘格时findChessboardCorners检测的是内部角点比如12x9格子的棋盘检测到的角点数量是11x8。采集和分析时不要搞混这个数字否则角点不会匹配。3. 采集数据与运行标定的完整过程标定板选好了接下来就是数据采集这一步的成败直接决定标定结果的准确性。很多人把标定想得太简单以为拿着标定板随便拍几张就能出结果实际上采集数据的质量远比算法本身更影响最终精度。3.1 采集标定图像的关键要求图像的多样性是核心。你要让标定板在画面里以不同姿态、不同位置、不同旋转角度出现而不是只在正对着相机的地方拍几张。我一般是这样控制的数量上普通广角镜头和标准镜头至少拍15到20张鱼眼或大畸变镜头建议30张以上标定板平面与成像平面之间的夹角要有变化从0度正对到大约30到45度之间都要覆盖但不要太极端夹角太大时角点提取容易失败标定板要出现在画面的各个区域左上、右上、中心、左下、右下不要只集中在中间否则边缘区域的畸变模型拟合不出来距离也要有远近变化模拟实际工作距离范围这能让内参的估计更稳定拍摄时确保标定板清晰不要有运动模糊这一点很多人容易忽视模糊的角点坐标本身就是带误差的标定出来的内参也一定不准。另外有一点要特别提醒标定时相机的光圈、焦距和对焦位置必须固定一旦标定完再动光圈或变焦内参就变了前面所有工作全部作废。工业相机一般手动锁定光圈和焦距但如果用了自动光圈镜头必须切成手动模式再采集。3.2 一张图解释内参、外参和畸变系数标定最终输出的是三样东西内参矩阵、畸变系数、以及每张标定图对应的外参。内参矩阵是3x3矩阵核心参数是fx、fy分别代表x、y方向上的等效焦距单位是像素和cx、cy主点坐标。你可以把fx理解为焦距除以单个像素的物理尺寸它决定了物体在图像里放大多少倍。cx和cy是光轴与成像平面的交点理想情况下在图像正中心实际因为装配误差会有偏移。畸变系数主要分两类。径向畸变是镜头光学结构造成的表现为直线在图像边缘变成向内弯桶形畸变或向外弯枕形畸变通常用k1、k2、k3三个系数描述。切向畸变则是镜头和传感器装配不平行导致的用p1、p2两个系数描述。OpenCV默认用的是包含k1、k2、p1、p2、k3的五参数模型。外参描述的是每张标定图拍摄时刻标定板坐标系与相机坐标系的相对位姿也就是旋转向量和平移向量。单目标定里外参本身一般不是最终目的但它是求解内参的中间产物后续如果做手眼标定、多相机标定外参就是必须品。3.3 用代码跑通一次完整的标定流程这一步我用OpenCV和Python来演示这也是工程里最常用的一套组合。import cv2 import numpy as np import glob # 棋盘格内部角点数 chessboard_size (11, 8) square_size_mm 25.0 # 准备角点的物理坐标假设标定板放在Z0平面上 pattern_points np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32) pattern_points[:, :2] np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2) pattern_points * square_size_mm object_points [] # 三维物理坐标 image_points [] # 图像上的角点坐标 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, chessboard_size, None) if ret: # 亚像素精化角点坐标 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners2 cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) object_points.append(pattern_points) image_points.append(corners2) cv2.drawChessboardCorners(img, chessboard_size, corners2, ret) cv2.imshow(detect, img) cv2.waitKey(100) cv2.destroyAllWindows() # 标定主函数 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( object_points, image_points, gray.shape[::-1], None, None ) print(内参矩阵:\n, mtx) print(畸变系数:\n, dist) print(重投影误差:, ret) # 保存标定结果 np.savez(camera_calib.npz, mtxmtx, distdist, rvecsrvecs, tvecstvecs)calibrateCamera内部实际上是在做一件什么事呢它在同时优化内参、畸变系数和每一张图的外参优化目标是让所有已知角点的三维坐标重投影到图像平面后与检测到的二维角点坐标尽可能重合这个重合误差就是重投影误差也就是calibrateCamera返回的那个ret值。角点检测失败时findChessboardCorners会返回False这种情况下要检查棋盘格是否完全在画面内、光照是否均匀、图像是否模糊。还有一个经验棋盘格不要用太密的规格比如15x15以上的否则角点数量太多检测速度慢且容易误检。提示calibrateCamera的重投影误差单位是像素经验值一般在0.05到0.3像素之间属于优秀水平0.3到0.6像素属于可用水平超过0.6像素就需要排查采集数据或检测流程的问题了。3.4 标定结果的判断标志拿到内参和畸变系数之后不要急着去矫正图像。先做个快速自检打印重投影误差看所有标定图的误差分布。如果某张图的误差明显高于其他图说明这张图里角点检测不准或者标定板姿态太极端把这张图剔除以后重新标定往往误差就能明显降下来。还有一个更直观的验证方法用标定的畸变系数对棋盘格图像做畸变矫正然后观察画面边缘的直线是否恢复平直。棋盘格天然提供了丰富的直线边缘矫正后格子边缘应该是笔直的如果还有明显扭曲说明畸变模型没拟合好。4. 从标定到矫正畸变模型、映射方式与工程实现标定只是拿到了哈哈镜的矫正表真正要出效果还必须把矫正表用起来。这里有两个层面的工作一是选对畸变模型二是选对矫正的实现方式。4.1 普通镜头与鱼眼镜头的畸变模型差异OpenCV里的calibrateCamera默认用的是普通镜头的畸变模型适合透视相机、工业定焦镜头和绝大多数手机摄像头。但如果你的镜头是鱼眼镜头视场角超过120度甚至到180度畸变极其剧烈普通多项式模型就拟合不过来了。鱼眼镜头要专门用cv2.fisheye模块它的畸变模型和普通模型不一样采用的是等距投影模型畸变系数也只有四个k1、k2、k3、k4。在我实际接触过的项目中用普通模型去标鱼眼镜头矫正后的边缘区域依然会严重扭曲换用fisheye模块后效果立刻正常。所以第一步一定是确认镜头类型再选匹配的标定接口。4.2 矫正实现方式一次映射还是两次映射OpenCV里矫正图像有两种主流做法cv2.undistort(img, mtx, dist)一步到位内部自动完成映射计算和重采样cv2.initUndistortRectifyMapcv2.remap先生成映射表再对图像做重采样。从效果上看两者差别不大但性能上差距很大。在实时视频处理场景每一帧都调用undistort会重复计算映射表CPU开销明显偏高而initUndistortRectifyMap只需要在初始化时计算一次映射表之后每帧执行remap即可效率高很多。工程上我基本只推荐第二种方案。import cv2 import numpy as np # 加载标定结果 data np.load(camera_calib.npz) mtx data[mtx] dist data[dist] # 生成映射表 h, w 1080, 1920 new_mtx, roi cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), alpha0, (w, h)) mapx, mapy cv2.initUndistortRectifyMap( mtx, dist, None, new_mtx, (w, h), cv2.CV_32FC1 ) # 对每帧执行映射 frame cv2.imread(frame.jpg) undistorted cv2.remap(frame, mapx, mapy, cv2.INTER_LINEAR)getOptimalNewCameraMatrix里的alpha参数是有讲究的。alpha0表示矫正后保留所有有效像素但边缘会出现黑边需要裁剪alpha1表示所有原图像素都保留边缘不会裁切但图像会被拉伸放大分辨率利用率下降。我一般是设alpha0然后根据返回的roi做一次裁剪把黑色区域去掉。裁剪时注意保留中心区域不要因为裁剪过度而丢失原本需要观察的视场边角。4.3 重映射的插值选择remap的插值方式在大多数场景用INTER_LINEAR就够用了速度和质量的平衡最好。如果矫正后图像要用于高精度测量并且图像本身质量不错可以尝试INTER_CUBIC边缘细节保留更好但速度会慢一些。INTER_NEAREST一般只用于快速预览矫正后的几何精度不受插值方式影响位置坐标是浮点数插值只影响灰度/颜色取值所以具体选择就看你对画质和帧率的要求。4.4 矫正 ROI 裁剪的取舍矫正后的图像因为畸变校正会丢失边缘信息这是物理必然。实际项目里如果你关心的目标基本位于画面中心区域把边缘黑边裁掉问题不大但要是在边缘区域也有检测需求就不要一刀切地裁掉太多保留部分边缘的轻微拉伸也比丢掉目标强。我在一个工业检测项目里做过对比。当时为了保证视野覆盖率矫正后没有按roi裁剪而是直接保留了整幅图像结果边缘区域的目标反而因为矫正拉伸而出现了轻微的形变测量精度反而不如不矫正。这个经验提醒我矫正在很多场景是必要手段但不一定非要对整幅图做矫正不可——如果你只关心某个固定区域可以直接用标定结果对该区域建立映射而不用对全局做处理计算量也小很多。5. 标定误差来源与常见问题排查实录这部分是真正的经验沉淀。下面这些坑我基本都踩过有些甚至踩过不止一遍。5.1 标定误差偏大应该从哪些方向排查先给一张排查速查表你照着顺序检查就行检查项优先级可能的问题处理方式图像清晰度高运动模糊、对焦不准重新采集使用小光圈增加景深用支架固定相机标定板平整度高纸板褶皱、贴膜气泡更换玻璃/陶瓷标定板确保板面绝对平整采集数量与姿态覆盖中姿态单一、集中在中心区域补拍不同角度、不同位置、不同距离的图像角点检测精度中光照不均、反光、过曝调整光源方向使用漫射光避免高光反射镜头类型选配低普通模型用于鱼眼镜头换用fisheye模块重新标定标定板规格低内外角点数搞混核对findChessboardCorners输入参数重投影误差大高概率是图像质量和标定板平整度的问题算法层面能优化的空间反而不大。有一次我怎么标都标不出低误差最后发现是标定板贴在亚克力板上亚克力有轻微弯曲换了一块真正的陶瓷板之后误差立刻从0.8像素降到0.15像素。5.2 角点检测失败的几种典型场景角点检测失败常见的有这么几种棋盘格在画面中太小角点之间像素距离不到10个像素检测算法很难分辨画面有强烈的局部高光或阴影黑白格子的对比度被破坏标定板姿态倾斜太厉害格子产生严重的透视变形分辩率太低或者图像被压缩过边缘锯齿严重。遇到这类问题优先从采集端解决不要死磕检测参数。调整光照、减小倾斜角、让标定板在画面中占比更大通常比调findChessboardCorners的参数更有效。另外拍摄时尽量让标定板占据画面的四分之一到三分之一以上既保证角点清晰又不会因为过大导致部分区域出画面。5.3 矫正后图像边缘拉伸太厉害矫正后边缘出现严重拉伸通常是因为畸变系数太大或者标定的畸变模型拟合过度了。一个常见错误是标定时把一些误检的角点也算进去了导致畸变系数被异常大值拉偏。检查方式是打印畸变系数如果k1的绝对值超过1左右就要怀疑标定数据有问题了。还有一种是使用alpha1时画面被过度放大边缘被拉伸这属于参数选择问题。一般来说alpha0更接近真实矫正效果如果实在需要保留全视场信息可以考虑把原图先缩小一点再矫正这比强制在alpha1下保留边缘更合理。5.4 内参标定结果在不同分辨率下的复用问题这里必须明确一点内参矩阵的fx、fy和cx、cy都依赖图像分辨率。同一台相机在1920x1080下标定出来的fx是2000像素在1280x720下就会变成约1333像素畸变系数是按归一化后的坐标表示的理论上可以在不同分辨率下复用但为了保险起见分辨率变了最好重新标定成本很低别省这个事。我在实际导出标定结果时一般会顺带保存标定时的图像分辨率方便后续检查。如果项目中途摄像头换了分辨率我宁可直接把整条标定流程重跑一遍也不去纠结能不能复用旧参数。这是规避很多隐性问题的笨办法也是最可靠的办法。5.5 标定结果在工程部署时的附加注意事项标定不是在实验室里跑一次就完事的操作工程上还有几个必须注意的坑镜头在运输、安装过程中可能松动安装到现场后务必要重新标定一次使用自动对焦或变焦镜头时标定参数只在当前焦距下有效工业场景建议直接用定焦镜头并锁定光圈相机会发热长时间工作后热胀冷缩可能导致内参漂移对亚像素精度测量有要求的项目建议等相机预热半小时再标定不要用在其他电脑上拷贝的标定参数直接部署到新机器上除非你能确认镜头、分辨率、相机型号完全一致。6. 我个人积累的几个标定小技巧最后分享几个实际项目中摸索出来的技巧篇幅不长但都是能直接提升效率的点。第一个技巧是采集标定图像时把相机放在三脚架或固定支架上移动标定板而不是移动相机。移动相机容易引入振动模糊移动标定板则稳定得多。如果是手持相机采集尽量用快门优先模式保证快门速度足够快。第二个技巧是采集时尽量让标定板在画面中出现不同的旋转角度。很多人习惯只做平移和倾斜很少旋转标定板但旋转角度越大对畸变系数的约束越充分尤其是切向畸变的拟合效果会好很多。我一般会在每个位置采集时顺时针和逆时针各旋转45度左右各拍一张。第三个技巧是角点检测时启用了cv2.CALIB_CB_FAST_CHECK要注意这个标志用于快速筛选棋盘格能节省检测时间但偶尔会把有效的棋盘格误判为无效。第一次标定时建议不启用它确保每张图都尽量参与标定如果后续每次都要实时检测棋盘格再启用这个标志提高速度。第四个技巧和标定板的清洁有关。玻璃标定板表面如果有指纹、灰尘或油渍在光源照射下会产生细小的局部高光角点检测精度会受影响。采集前用镜头纸擦拭标定板表面这个动作虽然不起眼但在高精度标定中影响非常显著。关于相机标定这件事我个人的体会是它并不难但特别考验耐心和细致程度。每一个步骤都有严格的物理依据图省事的代价就是后续所有依赖相机模型的算法全都不准。按照本文的方法完整走一遍流程你的标定结果应该能达到一个很稳的水平。如果后面要接着做双目标定、手眼标定或者三维重建相信你会回来发现当初认认真真把这步做好是特别值得的一件事。