尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

相机标定原理与实操:从张正友标定法到OpenCV畸变校正

相机标定原理与实操:从张正友标定法到OpenCV畸变校正 简介本资源是一份面向计算机视觉初学者与工程实践者的相机标定MATLAB实现脚本聚焦于解决图像几何失真校正与二维-三维坐标映射精度提升问题适用于自动驾驶感知模块开发、机器人视觉定位、工业测量及3D重建等实际场景。压缩包仅含1个核心文件camera_calibration.m为2KB大小的MATLAB脚本封装了棋盘格角点检测、内参焦距、主点、径向/切向畸变系数与外参旋转平移矩阵联合优化、以及基于Levenberg-Marquardt算法的非线性最小二乘求解全流程支持用户输入基础参数后一键完成标定与畸变校正。已有239人学习下载脚本结构清晰、注释完备无需额外依赖库即可运行可直接用于教学演示、课程实验或嵌入小型视觉系统作为标定工具模块显著降低从理论到落地的实现门槛。1. 先聊聊相机标定这件事到底有多重要搞机器视觉的朋友应该都有过这种经历项目前期算法调得挺顺特征提取、模板匹配都跑通了结果一上产线精度就是差那么几个像素。查来查去最后发现根本不是算法的问题而是相机本身的成像“歪了”。这里的“歪”不是指相机没装正而是镜头畸变、焦距偏差这些内参没校准导致图像坐标和真实世界坐标之间差了那么一层“翻译”。相机标定英文常写作 camera_calibration部分资料里也会叫“像机标定”其实指的都是同一件事确定相机内部几何参数焦距、主点、畸变系数和外部位姿参数旋转、平移的过程。简单说就是让算法知道“图像上的一个像素到底对应现实世界里的哪个点、在哪个方向、距离多远”。我见过不少刚入行的同学觉得标定就是跑个OpenCV接口打印一张棋盘格拍几张照就完事了。结果换了个镜头、换了工作距离精度立刻崩盘。原因很简单标定的本质是“建立数学模型并求解参数”而模型的准确度直接取决于你输入的数据质量。拍得不规范、棋盘格不平整、覆盖区域不完整求出来的参数就是错的。这个错不是小错而是系统性的错后面做三维重建、手眼标定、尺寸测量全都会跟着错。这篇内容我按自己这些年做项目的实际路径来写先说清标定的原理和模型再说方案怎么选然后给一套从打印棋盘格到结果评估的完整实操流程最后把常见问题和排查手段整理成速查表。无论你是刚接触视觉的小白还是被标定精度折磨过的老手应该都能找到点有用的东西。2. 相机成像模型与畸变标定到底在算什么很多人以为标定就是“算个焦距”其实远远不止。要理解标定得先搞清楚相机是怎么把三维世界变成二维图像的。2.1 小孔成像模型理想情况下的坐标变换理想情况下相机成像可以用小孔成像模型来描述。空间中的一个三维点经过四个坐标系的变换最终落到图像平面上世界坐标系三维单位毫米或米→相机坐标系以相机光心为原点三维→图像物理坐标系以主点为原点二维单位毫米→图像像素坐标系以图像左上角为原点二维单位像素这四个步骤中第一步涉及外部参数旋转矩阵R和平移向量T后两步涉及内部参数焦距fx、fy主点cx、cy。整个映射可以用一个3×4的投影矩阵表示但这里我不堆公式只说核心逻辑相机的内参决定了“光心到像素”的比例关系外参决定了“相机在世界坐标系中的位置和朝向”。标定要做的就是同时解出这两类参数。实际项目中外参往往在手眼标定或位姿估计时单独求解。你拿棋盘格拍一组照片做标定本质上就是用“已知三维结构的标定板”来反推相机的内参和畸变。2.2 畸变模型为什么直线会变弯任何镜头都不可能是完美的小孔成像光线经过镜片时会发生折射导致图像边缘出现变形。畸变主要分两类径向畸变由镜片形状引起越靠近边缘越明显。表现为直线向外凸桶形畸变或向内凹枕形畸变。用k1、k2、k3三个系数来描述。切向畸变由镜头与成像平面不平行引起表现为图像像被“拧”了一下。用p1、p2两个系数来描述。OpenCV默认的畸变模型就是那五个系数[k1, k2, p1, p2, k3]。鱼眼镜头可能还要用更复杂的模型如Fisheye模型支持k1-k4但大部分常规镜头用五参数模型就够了。我经常用一个比喻来解释畸变镜头就像一面哈哈镜只不过哈哈镜的变形太大能直接看出来相机镜头的变形很小需要精确测量时才会发现“歪了”。标定的核心任务之一就是把这面“微哈哈镜”的变形规律用数学模型精确描述出来然后在图像处理时反向纠正掉。2.3 标定板上的角点天然的“已知量”张正友标定法是目前最主流的相机标定方法它的巧妙之处在于只需要拍摄不同姿态下的平面标定板通常是棋盘格或圆点阵列就能同时求解内参、外参和畸变系数。不需要昂贵的精密设备一张打印出来的棋盘格贴在平板上就能干活。为什么用棋盘格因为黑白交界的角点内角点具有两个方向上的梯度都很大的特征亚像素级定位精度高而且检测算法成熟稳定。圆点阵列如Halcon常用的方式也有优势——圆心定位精度高但对透视变形和离焦更敏感。普通项目用棋盘格最省事高精度项目可以试试圆点阵列。这里有个需要注意的细节标定板棋盘格的规格要写清楚比如“9×6方格边长20mm”。这里的“9×6”指的是内角点数不是格子数。OpenCV检测时说的patternSize是角点行列数比如9×6表示每行9个角点、每列6个角点对应棋盘上10×7的格子。这个参数填错程序必然报错或检不到。3. 标定方案选型不是所有项目都该用同样的方法选标定方案先想清楚你的精度需求和应用场景。我习惯按下图逻辑来分要标定的相机是单目还是双目工作距离是固定还是变化的精度要求是毫米级还是亚像素级有没有条件用标定板3.1 单目标定最常见、最基础单目标定就是求单个相机的内参和畸变适用于以下几种场景平面测量比如工件尺寸测量、定位抓取前提是物体近似处于一个平面内配合标定板求出一个“像素到毫米”的比例关系有时甚至不需要完整内参算个单应性矩阵就行。畸变纠正比如用广角镜头拍全景图先标定再对图像去畸变。三维重建的基础双目视觉、结构光、多相机系统都先要完成每个相机的单目标定。单目标定的投入产出比最高十几分钟拍图半小时处理就能让相机从“浑浑噩噩”变成“心里有数”。3.2 双目标定不只是标两次双目标定的流程是在单目标定基础上再计算两个相机之间的相对位姿旋转矩阵R和平移向量T以及本质矩阵或基础矩阵。做完双目标定才能对左右图像做极线校正让对应的像素点落在同一水平线上这样才能用视差计算深度。有一点容易被忽略双目标定对左右图像同步性要求很高。如果两个相机不是硬件触发同步采集标定板稍微动一下左右图的角点位置就不对应求出来的外参会不准。实操中建议使用支持硬件触发或外部信号的工业相机采集时把标定板固定不动再触发。3.3 相机-激光雷达、相机-机械臂的联合标定再往深走一步如果你的系统里有多个传感器如相机激光雷达或者相机机械臂还需要做联合标定。这类方案我简要说一下思路相机与机械臂手眼标定求解的是相机坐标系与机械臂末端坐标系之间的变换关系常见做法是控制机械臂带着标定板运动到多个位姿分别记录标定板在相机下的位姿和机械臂的正运动学位姿然后用AXXB方程求解。相机与激光雷达通过采集包含标定板的点云和图像提取标定板在两者中的位姿最小化重投影误差得到外参。这些联合标定的基础都是单目标定如果单目内参不准后面的标定就全白搭。所以很多同学问“为什么手眼标定精度那么差”我第一反应都是先让他回去查单目标定结果。3.4 无标定板方案什么时候能凑合什么时候不能有时候现场条件不允许摆标定板比如产线不能停机有人会问能不能用“自标定”或“无标定”方案。我的回答是能凑合但别指望高精度。自标定方法利用多视图几何约束不需要标定板但需要足够的纹理特征和基准尺度信息精度一般只有标定板方案的几分之一。适合做AR、SLAM这类对绝对精度要求不高的场景。如果你要做尺寸测量、缺陷检测的坐标定位老老实实用标定板。4. 完整实操流程从打印棋盘格到标定结果评估下面这部分是重点我按自己跑过无数遍的流程写。照着做基本不会翻车。4.1 准备工作标定板的选择与制作标定板有三种获取方式在线生成棋盘格图片并打印网上能搜到很多标定板生成器也可以用OpenCV代码生成再拿激光打印机打出来贴在硬平板上。购买陶瓷或玻璃基板的工业标定板精度高、平整度好适合精密测量项目价格从几百到几千都有。用液晶显示器显示标定板适合科研验证但屏幕的平整度取决于显示器本身且屏上覆盖玻璃会有重影不推荐现场用。我的建议普通视觉项目用A4纸打印棋盘格贴在平整的亚克力板或玻璃板上就够。但要注意三点打印介质不能有褶皱贴的时候要确保完全服帖。棋盘格区域要保持哑光避免反光导致角点检测失败。方格边长要尽量精确建议用游标卡尺量一下实际打印出来的格子边长填到代码里要用实测值别用理论值。顺便说一句方格边长是否精确直接影响标定结果的尺度意义。如果标定时填的边长是20mm实际打印出来是19.8mm那么标定出的焦距也会按比例偏差约1%。对很多测量场景来说这个误差不能接受。4.2 拍摄采集质量远比数量重要拍标定板照片是很多人最容易翻车的一步。先说结论15到20张不同姿态的清晰图像远比30张千篇一律的正面图有用。采集时记住这几个关键原则姿态要多样包括正面、左右倾斜、上下倾斜、旋转让标定板在画面中形成不同的透视关系。畸变系数对视角变化很敏感只拍正面图无法正确估计畸变。覆盖整个画面标定板要出现在图像的边缘、角落和中心尤其在四角多拍几张。因为径向畸变在画面边缘最严重如果标定板只在画面中心区域出现畸变系数基本就是瞎猜。距离要有变化靠近和远离相机各拍一些这样能更好地约束焦距和主点。标定板要平整、静止拍摄瞬间标定板不能抖动不然角点会模糊。光照要均匀避免强光直射导致棋盘格过曝或者阴影遮挡部分格子。另外工业相机尽量用固定光圈和焦距拍标定过程中不要去动镜头调焦环。道理很简单标定算出来的内参是和“当前镜头状态”绑定的你标完再去拧镜头先前的标定就作废了。4.3 OpenCV标定代码核心流程怎么写我以Python OpenCV为例给一个可以直接改来用的流程骨架。核心步骤就五段读图、找角点、亚像素细化、标定、评估。import cv2 import numpy as np import glob # 棋盘格内角点行列数以9x6为例 pattern_size (9, 6) # 方格边长单位毫米务必用实测值 square_size 20.0 # 准备对象点标定板上所有角点的三维坐标z0 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 # 收集所有图像中的角点 obj_points [] # 世界坐标系下的三维点 img_points [] # 图像坐标系下的二维点 images glob.glob(calib_imgs/*.jpg) for fname in images: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners cv2.findChessboardCorners(gray, pattern_size, None) if ret: # 亚像素细化提升角点精度 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) obj_points.append(objp) img_points.append(corners) cv2.drawChessboardCorners(img, pattern_size, corners, ret) cv2.imshow(corners, img) cv2.waitKey(100) cv2.destroyAllWindows() # 执行标定 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None ) print(重投影误差:, ret) print(内参矩阵:\n, mtx) print(畸变系数:\n, dist)这段代码的核心逻辑是利用棋盘格尺寸构造三维对象点所有角点 z0。在每张图上找角点并做亚像素细化。用所有图像中的二维-三维对应点通过张正友算法优化出内参。关于亚像素细化有个细节值得说明cornerSubPix的窗口大小(11, 11)不是固定死的图像分辨率越大、棋盘格角点越稀疏窗口可以适当调大比如(15, 15)或(21, 21)。窗口太小细化效果不明显窗口太大可能跨过角点邻域反而引入误差。4.4 标定结果评估别只看重投影误差OpenCV的calibrateCamera返回的第一个值ret是重投影误差RMS单位是像素。很多人以为这个值小于0.1就万事大吉其实不一定。重投影误差的计算方式是把标定得到的参数代回去把三维角点投影到图像上算投影点和实际检测点的像素距离然后求均方根。它反映的是“模型拟合程度”但不直接反映实际测量的毫米精度。我的经验标准是ret 0.1优。说明数据一致性很好。ret 0.3良。大多数项目可接受。ret 0.5差。建议排查数据问题标定板不平、图像模糊、姿态单一等。但更关键的是要盯住内参矩阵和畸变系数是否符合物理直觉焦距fx、fy是否接近镜头标称焦距换算到像素的值。换算公式为f_pixel f_mm / pixel_size比如8mm镜头像元尺寸3.45μmfx大约在2319像素左右。如果差得离谱说明标定数据有问题。主点cx、cy是否接近图像中心。一般偏差几十个像素内合理差几百像素就要警惕。畸变系数k1是否在合理范围。普通镜头k1通常在-0.5到0.5之间如果出来一个-5或者5这种离谱值基本说明数据不可用。4.5 图像去畸变标定完怎么用标定得到的畸变系数最终要用来纠正图像。OpenCV提供两种方式# 方式一直接去畸变 undistorted cv2.undistort(img, mtx, dist, None, new_mtx) # 方式二先算映射表再用remap重映射适合实时处理 mapx, mapy cv2.initUndistortRectifyMap(mtx, dist, None, new_mtx, (w, h), cv2.CV_32FC1) undistorted cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)实际项目中强烈推荐用方式二remap。因为undistort每次调用都重新计算映射而initUndistortRectifyMap可以只算一次映射表之后每帧只做一次查表重映射性能差距可能在几毫秒以上。在需要实时处理的产线上这个优化很关键。这里还有一个细节new_mtx默认和mtx一样但如果你想在去畸变后适当裁剪边缘避免图像边缘的黑色区域可以用cv2.getOptimalNewCameraMatrix来获取一个调整后的内参矩阵它会根据可选的alpha参数决定保留多少边缘像素。alpha0表示裁剪掉所有黑色区域alpha1表示保留全部原始像素会看到黑边。h, w img.shape[:2] new_mtx, roi cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 0, (w, h))去畸变后如果只想保留有效区域可以用返回的roi对图像做裁剪。4.6 像素到物理尺寸的换算测量项目的最后一步去畸变之后图像坐标和相机坐标之间就有了一个干净的单应关系。对平面测量场景最常用的做法是用标定板计算一个单应性矩阵H然后直接把像素坐标转换为毫米坐标。具体操作是放一张标定板在测量平面上检测角点得到图像坐标和对应的世界坐标毫米用cv2.findHomography求H。之后图像上任意一个像素点p都可通过P H * p换算到毫米坐标。这种方法比“单独用内参深度信息”更简单而且精度通常更高因为它直接把畸变纠正和尺度变换一步到位。但它有一个前提被测物体必须在标定板所在的同一个平面上否则透视变换的结果是错的。如果你要测的物体有高度差那就得用双目视觉或结构光方案了不是单张图能解决的。5. 常见问题与排查技巧实录这部分我花了大量篇幅整理因为真正干活时卡住你的往往不是原理而是各种奇怪的现象。5.1 棋盘格角点检测失败或误检这是新手遇到最多的问题。典型表现是findChessboardCorners返回False或者检测出的角点排列错乱。排查步骤确认pattern_size填得对。记住是内角点数不是格子数。9×6棋盘实际有10×7个格子这个最容易填错。检查图像是否过曝或欠曝。棋盘格需要黑白分明如果高光盖住了白色格子或者阴影让黑色格子变成灰色检测必挂。检查标定板是否太小。如果标定板在图像中占比太小角点间的像素距离太小亚像素定位精度会大幅下降。建议标定板在画面中至少占三分之一以上面积。试试adapative threshold。OpenCV内部检测有自适应阈值机制但遇到光照不均时可以手动预处理先高斯模糊再大津法二值化让黑白边界更清晰。换检测参数或改用不同方法。新版OpenCV中有cv2.findChessboardCornersSB对模糊和光照变化更鲁棒实测效果比老接口好不少。5.2 标定结果每次都不一样如果你反复标定发现结果差异较大比如焦距波动超过几十像素先检查标定照片数量是否稳定。照片太少结果波动肯定大。是否用了不同分辨率的图像。同一相机切了ROI之后再标定内参和分辨率相关需要重新标定。镜头是否松动。工业相机镜头锁紧螺丝没拧紧轻微震动就会让焦距变化。以前在产线遇到过这个问题折腾了一天才发现是镜头没锁紧。我一般会用“稳定重复标定验证法”同一组图像标定三次如果结果差异极小说明算法层面没问题然后换一组图像再标定如果差异大说明采集数据的可重复性差。5.3 重投影误差小但实际测量却不准这个现象最容易让人迷惑。理论上重投影误差小说明参数拟合度高为什么测量还是差最常见的原因是标定板的平面度不够。如果棋盘格贴在表面不平整的板子上相邻角点之间的实际距离和理想值不一致重投影误差依然可能很小因为畸变模型“吸收”了这些误差但真实世界中的测量精度就是上不去。另一个原因是标定时的工作距离/对焦状态和实际使用时不一样。标定时的焦距是固定的如果你在实际测量时改变了工作距离、重新调过焦内参就变了。有些镜头有微距对焦环稍微一碰就变了。还有一个坑用了自动光圈或自动增益。光照变化时光圈自动调整画面亮度变了但镜头的畸变特性也可能微变。精密测量项目中相机应该设置为手动曝光、固定增益。5.4 畸变纠正后图像边缘还“发飘”如果你做了去畸变但边缘的直线依然弯曲可能的原因畸变模型不够比如普通五参数模型不适用于某些大畸变的广角镜头。可以试试OpenCV的Fisheye模块或者用更高阶的多项式模型。标定板在边缘的图像太少导致边缘畸变估计不准。这时要多拍几张标定板在画面四角的照片重新标定。传感器与镜头不匹配。比如大靶面相机配了小靶面镜头成像圈不够大边缘画质崩坏严重畸变不再是平滑的曲线变化。这种情况建议直接换镜头别指望标定能救。5.5 常见的标定姿势错误汇总我把实操中的典型错误整理成一个表格方便大家对照自查典型错误造成的影响正确做法标定板只出现在画面中央畸变系数估计不准让标定板覆盖画面中心和四角所有照片姿态几乎一样外参求解不稳定内参受牵连拍正面、倾斜、旋转等多样化姿态标定板轻微弯曲角点三维坐标假设被破坏用厚板或玻璃板确保平整方格边长用理论值没实测尺度误差直接进入内参和测量结果用卡尺实测格子边长标定后调焦或变光圈内参失效标定和使用时的镜头状态保持一致光照不均或反光角点定位偏差均匀漫射照明避免镜面反射照片数量太少结果随机性大重复性差至少15张20张最佳忽略亚像素细化角点定位精度差用cornerSubPix做细化6. 标定之后还能做什么扩展思路标定做完一套内参和畸变系数只是起点后面能接的东西其实不少。三维重建与深度估计有了相机内参双目立体匹配才有了计算视差的基础再配合外参就能算出深度图。手眼标定与机器人抓取把相机标定的结果传给手眼标定算法把“看到的物体位置”换算成“机械臂要去的位姿”。多相机拼接多台相机做大幅面测量时用各相机标定结果做图像拼接和坐标统一让测量范围扩大而不损失精度。视觉SLAM视觉里程计和SLAM系统初始化时通常需要相机内参作为已知量输入标定精度直接影响轨迹估计和建图质量。我个人体会是标定这件事做好之后整个视觉系统就有了一个可以依赖的“坐标系语言”。后面无论是做算法调试还是排查问题都有一个可信的基准。反过来如果内参不对后面的每一步都是“从错误的坐标出发做推理”越推越偏。最后分享一个我自己检查标定质量的小习惯标定完不急着用先拍一张标定板在画面各个位置的照片用标定结果去畸变再用findChessboardCorners重新检测角点看角点坐标在去畸变前后的差异。如果边缘角点偏移明显、中间角点偏移很小说明畸变模型基本合理如果边缘偏移忽大忽小或者方向无序说明数据采集有问题回去重新拍。这个验证方法不用额外工具几行代码就能做能帮你省下很多后面排查的功夫。本文还有配套的精品资源点击获取
返回列表