做机器视觉这些年接触线扫相机的项目不算少从印刷品表面检测到锂电池极片外观检测十有八九都会遇到同一个问题同样一块产品在画面中间区域灰度正常一到边缘就明显发暗或者整幅图像上有一条条水平方向若有若无的亮暗条纹。客户张口就问“相机是不是有问题”其实相机真的挺冤的。这个现象的根源通常在于照明不均匀、镜头边缘光量下降和传感器像元响应不一致叠加在一起。解决它最标准、最有效的手段就是平场矫正英文叫 Flat Field Correction简称 FCC。这篇文章我以海康线扫相机为例子把平场矫正从理论到落地操作完整过一遍包括 MVS 客户端里的操作、SDK 里的自动化实现以及那些网上不常说但项目里一定会踩的坑。不管你是刚接触线扫相机的电气工程师还是已经在做视觉算法的调试人员这篇内容都值得先收藏再慢慢对照着调。1. 为什么要做平场矫正线扫成像最常见的“暗边”与“条纹”1.1 线扫相机的成像方式决定了它更容易暴露光照不均线扫相机和面阵相机的本质区别在于它的传感器只有单排或少数几排感光单元每次只采集一行图像靠物体运动或相机扫描拼接成整幅二维图。这意味着画面横向每个位置对应一个固定的物理像元而纵向每一条像素都是同一个像元在不同时刻的输出。一旦某个像元本身响应偏弱或者镜头边缘进光量偏少体现在图像上就是一条贯穿整幅画面的竖向暗线或者从中心到两侧逐渐变暗的横向渐变。面阵相机遇到这些问题时还可以通过缩小视野、加权平均、或者干脆只取画面中心区域来规避。线扫相机不行它是为连续宽幅材料检测设计的视野横跨整个产品幅面图像中的任何一条线都可能被算法当成缺陷特征。所以平场矫正在线扫项目里不是可有可无的优化项而是出图之后的第一道标准化工序。1.2 一个典型的平场矫正需求场景我接过一个印刷品在线检测项目相机横跨印刷幅面扫描白卡纸光源用的是两根条形 LED 灯。刚把图拉出来时表面看整张纸都挺亮的但用取点工具一量左边灰度 180中间 205右边只有 172。这种不均匀对于人眼来说并不明显可是对阈值分割、边缘提取这类算法来说就是灾难。后来把算法直接跑上去问题全部暴露了整张白纸被分割出大量“缺陷”密密麻麻的误报根本没法用。典型的错误做法是反复调阈值去适应灰度不均结果顾了左边丢了右边越调越崩溃。正确的思路就是上平场矫正先把图像背景拉平让白纸在白光下呈一个接近均匀的灰度水平之后再做缺陷检测阈值分割效果立刻干净很多。1.3 平场矫正和“看起来很像”的噪声要分开看不少刚入行的读者会把所有亮度不均都归为同一个问题其实平场矫正主要对付的是固定模式噪声。固定模式噪声包括像元之间的暗电流差异、光电响应差异、镜头渐晕以及光源沿扫描宽度方向形成的固定照度分布。这些成分在时间上是稳定的所以可以通过参考帧计算系数再逐像素补偿回去。但还有一类亮度不均不属于固定模式噪声比如光源控制器频闪导致的高频横纹、环境光干扰、电磁干扰引起的带状噪声。这些东西随时间变化平场矫正不仅救不了甚至可能因为把参考帧的瞬时状态固化下来反而把新的横纹“烙”在图像里。我的习惯是在决定做平场矫正之前先连续抓几十帧图逐帧看确认画面中的明暗分布是固定的再来做矫正。1.4 什么情况下可以先不做平场矫正平场矫正也不是万金油。如果检测对象本身就不是均匀背景比如带纹理的金属表面、大量字符叠加的印刷标签或者算法对灰度不均不敏感那就不一定需要一上来就做。做平场矫正会增加调试工作量还有一定风险如果参考帧采集环境不严谨矫正反而会把产品固有纹理当成噪声抹掉或者引入新的条纹。我通常会在方案阶段用一张标准白纸快速拍一帧看中心和边缘的灰度差。如果差异在 5 个灰度级以内且检测算法没有强灰度依赖就先把平场矫正放下集中精力搞打光。如果差异超过 810 个灰度级或者后续要跑斑点分析、亮度统计这类对灰度敏感的算法那不用犹豫直接把平场矫正列为必做环节。2. 平场矫正的原理和数学表达别看公式简单坑都在细节里2.1 线阵相机输出的灰度到底是怎么来的要理解平场矫正先要理解传感器每个像元的输出模型。你可以把它想象成一个简单公式输出灰度 入射光能量 × 像元光电响应率 固定暗电流值 随机噪声其中入射光能量是真实的物理量而像元光电响应率和固定暗电流值在不同像元之间是有散差的。芯片制造工艺再好也不可能让几十万个像元的性能完全一致。同一束光打在像元 A 和像元 B 上出来的灰度可能就是 200 和 210 的区别这就是光响应非均匀性PRNU在没有光的情况下像元依然会输出一个不为零的底数而且这个底数在空间上并不均匀这就是暗电流非均匀性DSNU。再加上镜头中心到边缘的透过率差异光线从不同角度进入镜头时损失不同最终传感器接收到的光能量分布已经不再是真实物体的亮度分布了。平场矫正本质上就是做两步先把暗电流减掉再把各个像元的响应率拉齐。2.2 暗帧、亮帧、校正系数三者的关系平场矫正需要两个参考数据暗帧和亮帧。暗帧是在完全遮光条件下用当前曝光时间和增益采集的图像它代表的是像元的暗电流输出。理论上暗帧应该是一条灰度接近 0 的平线但实际中不同像元的暗电流有差异所以会呈现轻微的起伏。亮帧是在均匀光照条件下用相同曝光时间和增益采集的图像。它代表的是“当前光照下每个像元能输出多少灰度”。实际中因为光照分布和像元响应的叠加亮帧会是一条有波动的曲线。有了这两个参考就能对任意一帧原始图像做逐像素校正校正后灰度 (原始灰度 - 暗帧灰度) × 校正系数其中校正系数的计算需要指定一个目标灰度校正系数 (目标灰度 - 暗帧灰度) / (亮帧灰度 - 暗帧灰度)这里的逻辑是先把原始图像减去暗电流得到纯光响应部分再乘以一个系数让亮帧中偏暗的像元放大、偏亮的像元缩小最终把整条像素的响应统一到目标灰度这条线上。2.3 一个具体的系数计算例子举个例子假设某行像素中像元 X 在暗帧中灰度是 8亮帧中灰度是 205目标灰度设为 180。那么该像元的校正系数(180 - 8) / (205 - 8) 172 / 197 ≈ 0.873如果某一帧原始图像中这个像元的灰度是 168那么校正后的灰度为(168 - 8) × 0.873 ≈ 139.7你可能会问为什么不直接套用更简单的公式“校正后灰度 原始灰度 × 目标灰度 / 亮帧灰度”当然可以这种近似算法在暗电流很小的时候和上述公式差别很小。但一旦传感器温度升高、暗电流增大或者使用了较长曝光时间暗帧就不再是一条值为 0 的直线了。这时候把暗电流单独减掉再做增益补偿才能保证暗处的噪声不会被错误放大。这也是专业相机平场校正模块里都要先拍暗场的根本原因。2.4 多帧平均和校正表是怎么生成的实际项目中参考帧不会只拍一帧就拿来用。原因很简单任何一帧图像里都混有随机噪声如果直接把单帧暗场和单帧亮场作为参考等于把随机噪声也固化成了校正系数校正之后会出现新的固定条纹。我通常的做法是暗场、亮场各采集 32 到 128 帧逐像素求平均用平均值来生成校正表。线扫相机的行频往往很高单帧曝光时间短信噪比本来就不高所以多帧平均这一步非常必要。尤其对于高速线扫场景比如行频 20kHz 以上每个像素的有效信息非常少随机噪声占比高参考帧数量不够的话校正效果会大打折扣。校正表本质上是一个一维数组长度等于相机传感器在宽度方向上的有效像素数。每个像素位置存储一个浮点系数应用时对每一行做一次查表和乘加运算。这也是线扫相机平场矫正与面阵相机的主要区别面阵需要一张与图像同分辨率的二维校正映射而线扫只需要一行系数内存开销和计算量都要小很多。2.5 使用校正公式时要注意的几个陷阱第一个陷阱是 “亮帧灰度 - 暗帧灰度” 接近 0。比如遮光没做好暗帧灰度高达 250亮帧灰度也是 250那这个像元的分母就是 0校正系数会变成无穷大校正后的图像会出现一条异常亮线。遇到这种情况首先要排查遮光是否严密其次要在校正算法中对过小分母做保护。第二个陷阱是校正系数远离 1。如果某个像元的系数达到了 3 倍以上说明这个像元自身的响应已经严重偏离平均水平靠增益补偿只会把随机噪声一起放大。这种情况下应该启用相机自带的坏点矫正功能把坏点周围的像素插值替换掉而不是硬用平场矫正去拉齐。第三个陷阱是目标灰度选择不当。目标灰度不能设置得太高一般建议在 180220 之间。如果目标灰度设为 250而亮场平均灰度本身只有 180那么整个校正过程的增益会偏高随机噪声会被整体放大画面会变得很“脏”。3. 海康线扫相机上的平场矫正实操MVS 客户端与 SDK 两条路线3.1 开始之前的准备工作决定校正成败的那一半平场矫正做得好不好七分在准备三分在操作。我在调海康线扫相机时第一件事不是急着打开 MVS而是确认物理链路和成像参数。相机安装必须稳定镜头光圈、焦距、物距、光源角度都要固定在实际工作状态。光源要提前打开并预热很多LED光源在刚通电的前几分钟亮度漂移非常明显我遇到过光源要稳定 20 分钟才能达到恒定亮度的型号。相机也要先运行一段时间让传感器温度达到正常工作温度因为暗电流受温度影响很大冷机状态下采集的暗帧和热机状态下实际成像时根本对不上。还需要记录当前使用的曝光时间、增益、行频、像素格式等信息。这些参数在后续采集暗帧、亮帧时都必须保持完全一致否则校正系数毫无意义。3.2 MVS 客户端手动校正步骤海康的 MVS 客户端是配套机器视觉相机的基础软件平场校正操作在它里面就能完成。不同型号、不同固件版本在界面布局上会有些差异但大体流程是一致的。第一打开 MVS在设备列表中找到线扫相机点击打开图像采集窗口。先把相机设置成连续采集模式关闭外部触发避免触发行数不稳定造成图像混乱。第二设置曝光时间、增益和行频调到接近实际生产使用的参数值。如果项目里还没有最终确定这些参数就先定一组合理的中间值等定稿后再重新做一遍校正。第三做暗场采集。把镜头前端完全遮住最好用不透光的镜头盖或者用黑色遮光布包住镜头。注意检查镜头接口、滤镜、机壳缝隙处有没有漏光。然后在 MVS 的相机属性中查找平场校正相关节点一般会看到 FCC、FlatField、DarkField 这类关键字。执行暗场参考采集软件会按照设定的帧数自动抓取并求平均。第四做亮场采集。撤掉遮挡打开光源先把画面亮度调到合适的范围。对于 8bit 图像我习惯让亮场灰度峰值在 200220 之间不要太靠近 255防止过曝的饱和像元破坏参考数据也不要太低否则校正系数偏大噪声会被放大。执行亮场参考采集软件会生成亮场平均图。第五启用平场校正。在 MVS 中选择加载暗场和亮场参考把校正模式设置为“启用”实时查看图像效果。如果结果显示均匀度不错就把参数保存到相机用户配置组中方便断电重启后一键载入。需要注意海康不同型号线扫相机暴露出来的 FCC 节点名不完全一样有些叫 “FCCMode”有些叫 “FlatFieldCorrectionEnable”。最可靠的办法是在 MVS 的属性列表里用关键字搜索 “FCC” 或者 “FlatField”把所有相关节点列出来对照。如果你手头有 MVS 安装目录下的相机描述文件直接打开搜索 FCC能更快定位到当前相机的全部校正节点。3.3 通过 SDK 在 WinForm / C 中实现自动化平场校正现场设备一到量产阶段靠人工打开 MVS 点按钮做校正是不现实的。产线环境复杂操作人员不一定是视觉工程师所有调试步骤最好都封装到上位机软件里。海康官方 SDK 提供了完整的接口以 C# 为例结合 WinForm 可以比较简单地实现自动化采集和校正参数下发。关键思路是上位机控制相机完成遮光、亮场图像采集然后把采集到的参考数据通过 SDK 写入相机的校正节点。这样产线每换一次产品、每动一次光源操作人员只需要在界面上点一个“一键校正”按钮软件自动完成参考帧采集和系数计算。下面是一段简化后的 C# 示意代码核心是用 MvCameraControl.Net 中的 SetCommandValue 和 SetEnumValue 等接口操作相机节点。不同固件版本节点名有差异实际开发时建议先用 MVS 节点列表确认当前相机支持的节点名称。// 假设已经通过 SDK 打开相机 camera // 1. 设置为连续采集 camera.SetEnumValue(AcquisitionMode, 1); // 2. 采集暗场参考 // 这里通过节点触发暗场采集有的型号使用命令节点有的需要手动抓图并计算 camera.SetCommandValue(FCCDarkFieldCapture); System.Threading.Thread.Sleep(200); // 3. 采集亮场参考 camera.SetCommandValue(FCCLightFieldCapture); System.Threading.Thread.Sleep(200); // 4. 启用平场校正 camera.SetEnumValue(FCCMode, 1); camera.SetBoolValue(FCCEnable, true); // 5. 保存参数到用户配置 camera.SetCommandValue(UserSetSave);这段代码只能作为开发框架参考因为海康不同相机固件对平场校正节点的开放程度并不一致。有的型号直接提供一键采集暗场亮场的命令节点有的型号则需要你自己抓取多帧图像、求出平均图、然后把平均图转换成系数写入固定节点。后一种情况我在现场也遇到过处理思路是用 SDK 的接口连续抓取 32 帧图在内存里求平均再根据第 2 节里的公式逐像素计算系数最后封装成相机需要的数组格式写入。3.4 用海康 VisionMaster 做软件层面的平场校正如果手上的相机固件不支持硬件平场校正或者你已经把图像采集流程放到了海康 VisionMasterVM里也可以在 VM 的图像预处理模块里挂一个平场校正算子。VM 的思路是把参考图作为参数导入软件在处理每一帧图像时自动完成减法、增益等运算。这样做的好处是不依赖相机硬件同一套代码在不同品牌相机上也能用。但代价也很明显软件平场校正是吃 CPU 的。线扫相机分辨率通常很高常见的是 2K、4K、8K 甚至更高。以 8K 线扫相机按 10kHz 行频计算每秒会产生 8192 × 10000 ≈ 8 千万像素的数据量软件逐像素做减法和乘法计算开销相当可观。如果视觉流程本身已经很重再加平场矫正很容易掉帧。所以我的项目选型原则是如果相机本身支持硬件平场矫正优先用硬件只有相机不支持或者需要统一处理多个不同品牌相机时才用 VM 软件方案同时要提前做性能测试确认不会影响最大节拍。4. 校正效果验证与量化指标不要只靠眼睛说“看起来平了”4.1 目视检查的正确姿势平场矫正做完之后很多人的第一反应是肉眼看画面“看起来均匀多了”。这个判断没错但不够严谨。人眼对灰度差异的感知是非线性的而且很容易被图像整体亮度欺骗。更靠谱的方式是在 MVS 里打开直方图工具观察灰度分布的宽度。理想情况下一张均匀白纸的图像直方图应该是一个窄峰集中分布在校正目标灰度附近。如果直方图仍然很宽或者出现双峰说明校正并没有完全解决问题。同时要注意观察图像边缘和中心区域用取点工具分别量几个位置的灰度值看看变化趋势。我还会刻意去看两个地方一是图像最左边和最右边 100 像素范围内这通常是镜头渐晕最严重的区域二是整幅图像有没有突然亮一条线、暗一条线的情况那可能是坏点或者参考帧随机噪声被固化成了新条纹。4.2 量化指标用标准差和极差说话目视检查过关之后定量计算是少不了的。我常用的指标有两个灰度标准差和最大最小灰度差。标准差能反映整体波动最大最小差能体现极端情况。对于线扫相机我习惯取图像中间若干行把每一行的灰度分布拉出来分析。用一个简单的 Python 脚本就能完成这里给个参考import numpy as np from PIL import Image img np.array(Image.open(white_uniform.bmp).convert(L)) row img[img.shape[0] // 2, :].astype(np.float32) mean row.mean() std row.std() peak np.percentile(row, 99.9) valley np.percentile(row, 0.1) print(mean %.2f % mean) print(std %.2f % std) print(cv %.4f % (std / mean)) print(max-min %.2f % (peak - valley))这里 cv 是变异系数用来做归一化对比。我在实际项目里看到的数据一般是未校准时整行像素 cv 在 2%5%max-min 有 2040 个灰度级做完平场矫正后cv 能降到 0.5%1%max-min 压缩到 10 个灰度级以内。如果能达到这个水平绝大多数视觉算法都可以稳定工作。4.3 验证时必须保持应用条件一致这是最容易出问题、也最容易被忽略的一点。平场校正表是在一组特定参数下生成的包括曝光、增益、行频、光源亮度、光源色温、镜头光圈、工作距离。任何一个条件变化校正效果都可能失效。举个例子我在一个项目里做完校正后画面均匀度很好但第二天客户开机发现图像左侧重新变暗了。排查到最后发现是夜班工人调了光源控制器上的旋钮把左灯亮度调低了。平场矫正遇到这种问题完全无能为力因为光的物理分布已经变了。所以验证时不仅要保证当次条件一致还要和现场人员沟通好校正完成后所有影响成像的参数必须固定能加参数权限就加权限。5. 常见问题与排查经验速查表问题现象可能原因处理方式校正后出现新的竖向条纹参考帧帧数不足随机噪声被固化成了系数暗场、亮场各采集 32 帧以上求平均校正后边缘仍然偏暗亮场参考图本身边缘照度不足或参考图未覆盖全幅面检查光源均匀性重新打光后再采集亮场校正后图像出现亮斑/暗斑亮场参考中存在过曝或过暗像素调整光源亮度保证亮场峰值在 200220不过曝修改曝光或增益后校正失效校正系数与曝光参数强相关修改参数后必须重新采集暗场、亮场并生成新系数暗场采集时图像仍有明显内容遮光不严或镜头接口漏光检查镜头盖、接圈、滤镜、机身散热孔附近漏光触发模式校正效果差行频随运动速度波动导致图像各行曝光量不一致优先在连续采集模式完成校正触发模式需保证物速稳定图像频繁出现“buffer contains incomplete data”相机带宽超过网口或采集卡实际吞吐能力降低行频或 ROI检查网卡巨型帧设置必要时升级万兆网方案彩色线扫相机校正后偏色只对整体亮度做了校正没有分别处理 RGB 通道彩色相机必须分别对 R、G、B 三个通道做独立的平场校正运行一段时间后校正效果慢慢变差传感器温度漂移导致暗电流变化开机预热 20 分钟后再做校正作业环境保持温度稳定5.1 校正后反而出现条纹的处理经验这种情况我碰到过好几次。一次是暗场只拍了 5 帧随机噪声比较大校正表把噪声固化成了系数结果整幅画面出现细密竖纹。解决方法是把参考帧数量加到 64 帧同时把相机增益降到 0dB 附近再做校正效果立刻改善。另一次是亮场图中有一小块区域因为光源反光过曝变成了 255计算出的系数在那个区域异常矫正后出现一块“黑影”。后来把亮场灰度峰值控制在 220 以内这个问题就消失了。5.2 触发模式下要不要做平场矫正很多线扫项目是配合编码器或者外部行程信号触发成像的有人问“海康线扫相机能不能输入行程”意思就是用外部触发控制行采集。这种情况下平场矫正仍然可以做但有几个前提。外部触发行频必须稳定物体运动速度不能忽快忽慢否则不同行之间的曝光积分时间不同平场校正表无法补偿动态亮度差异。如果在连续模式下的校正表直接拿到触发模式下用效果常常不理想。我的建议是实际现场是什么触发模式就在什么触发模式下采集暗场亮场让校正表和应用条件完全对齐。5.3 带宽不足导致的校正数据异常报错信息里常见的“the buffer contains incomplete data due to insufficient band width”在高速线扫相机上经常遇到。平场校正本身不直接产生带宽问题但如果你在采集暗场亮场时丢包了那参考帧就是不完整的校正后图像会出现断线或错位。出现这个报错时先检查网卡是否开启了巨型帧Jumbo Frame再确认是否使用了单根网线传输超过 1Gbps 的数据。线扫相机分辨率高、行频高带宽很容易撞上限必要时降低 ROI 高度、降低行频或者换用万兆网接口和更高规格的采集卡。5.4 温度漂移问题线扫相机在产线上往往是 24 小时不间断运行传感器温度会从开机时的常温逐步升高到稳定的工作温度。暗电流随温度变化暗帧参考值就会发生偏移。最常见的现象是早上开机做完平场校正到下午图像整体亮度发生变化或者重新出现暗边。这不是校正失效而是参考条件变了。解决思路是开机后先让相机运行 20 分钟以上等温度稳定下来再做校正。如果现场环境温度波动大还要考虑加装散热或者把校正周期做成定时执行。5.5 彩色线扫相机需要分通道处理彩色线扫相机的传感器通常使用拜耳排列或者三线传感器结构R、G、B 通道的响应曲线不完全一致光源在不同波段的照度分布也不一样。做平场校正时必须对三个通道分别计算暗场和亮场系数不能拿一张灰度图混着校正。海康彩色线扫相机在 MVS 里一般会提供颜色通道相关的 FCC 节点采集亮场时最好用白色标准反射板确保三个通道都得到足够的信号强度。校正完成后再用标准白板验证一次白平衡看 RGB 三通道峰值是否接近。5.6 平场矫正与坏点矫正的关系线扫相机使用久了个别像元可能出现响应异常表现为图像上有一条固定的亮线或暗线。很多人试图用平场矫正去抵消坏点把它的响应系数拉高或拉低。这种做法在坏点轻微时有短期效果但坏点本身响应不稳定校正系数过大会把随机噪声同步放大。海康相机一般有独立的坏点矫正功能会自动检测坏点位置并用邻域像素插值替代。正确流程是先做坏点矫正再做平场矫正顺序不要反。5.7 现场参数变更流程管理最后想提醒一件容易被忽视的事平场校正不是一次调试一劳永逸。产线改动曝光时间、换光源、调线缆、改行频任何一步都可能让之前的校正表失去意义。我每个项目都会要求现场把平场校正固化成一个标准的变更流程参数变更后必须重新做校正并且在上位机软件里加一个简单的一键校正工具让现场人员也能独立完成。项目管理上校正看板最好记录每次校正的时间、参数、参考帧数量方便回溯问题。我在实际调试中的体会是平场矫正这个功能看起来不起眼但它对后续所有视觉算法的影响是根本性的。很多看似复杂的缺陷误检问题回头一查根因只是图像背景没有拉平。把原理吃透把操作流程固化下来再配合可靠的量化验证线扫相机的成像质量基本就能稳定住。只要记住一条原则暗场、亮场、应用三者参数一致校正表就是可靠的参数一变一切重来。这个过程不复杂但严格执行的人不多你如果能做到项目里的图像问题至少能少一半。
阅读完成 · 觉得有帮助?