
雾天跑目标检测我相信做过多模态融合的人都体会过那种无力感。晴天测试集上mAP刷到漂亮一进雾天直接腰斩尤其小目标几乎是“团灭”。上个月我在某项目里把YOLOv13这个内部多模态版本拉到真实雾天场景做验证漏检率一度高到让我怀疑是不是数据标注出了问题。后来逐个环节排查最终定位到问题的根子不在检测头也不在融合策略而是可见光模态在输入域阶段就已经被雾“改写了颜色”。顺着这个思路我在融合前加了一个颜色增强模块CEM双色引导保真色加轻量调制提对比度雾天检测的mAP从38.5拉到45.7推理耗时只多了1.2毫秒。这篇就把这个模块从设计到落地、再到踩坑修复的完整过程讲清楚给做雾天检测、多模态融合或者域泛化的朋友一个可直接抄作业的参考。1. 雾天漏检的根因不是检测头不够强是输入域已经偏了先抛出我的结论雾天检测效果差很大一部分责任不在模型容量而在输入图像的颜色分布已经偏离了训练域。很多团队花大力气改特征金字塔、换注意力机制、堆更强的Backbone实际上是在一个已经退化的输入上做补救效率自然低。1.1 大气散射模型透射率、大气光与颜色偏移雾天成像在物理上可以用大气散射模型描述I(x) J(x) × t(x) A × (1 - t(x))其中I(x)是相机实际捕获的像素J(x)是清晰无雾的原始场景A是大气光t(x)是透射率。雾越浓t(x)越接近0意味着观测像素中场景本身的成分越来越少大气光A的成分越来越多。这里有个关键点A不是灰度值而是带颜色的环境光。在早晨的雾里它偏灰白在城市扬尘天气里偏土黄在海雾里偏蓝灰。这个带颜色的大气光叠加到每个像素上就会导致所有物体的色彩向大气光方向“漂移”。红色尾灯会褪成橙色甚至灰白绿色树木变成灰绿白色车道线直接融进背景。这种颜色偏移对检测的影响是系统性的。检测器在清晰图像上学到的颜色统计、纹理对比、边缘梯度分布在雾天全部失效。尤其小目标本身占据像素就少再丢掉颜色对比度特征在浅层就糊掉了后面怎么提特征都救不回来。1.2 多模态融合的现有路线为什么不够我最早的想法很简单可见光坏了不是还有红外、深度图吗YOLOv13的多模态分支融合了可见光、红外和3D点云特征理论上可以用红外模态补足可见光的退化。实测下来发现这个想法对了一半但也带来了新的问题。常见的多模态融合路线无非三种输入端直接拼接、特征层双流融合、注意力门控加权。前两种的问题是雾天可见光的特征和红外特征在数值分布、空间位置上差异很大直接拼接会让网络花很多容量去学习模态间的对齐真正用于目标判别的容量反而被稀释。注意力门控稍好网络学会自动降低可见光权重、提高红外权重但代价是晴天的颜色语义信息被“习惯性”削弱了。更麻烦的是对齐质量。多模态相机之间的内外参标定、帧同步、分辨率差异任何一个环节有偏差融合后的特征里就会出现错位。雾天亮度低、特征点少自动配准鲁棒性更差。红外在雾天虽然能保留热辐射信息但它没有颜色检测时容易丢失雨雾天气里很重要的尾灯、信号灯语义。所以我当时的判断是多模态融合目前的问题不在“融合”本身而在上游——可见光模态在进入融合之前就已经是一张颜色退化严重的图了。与其让融合网络自己隐式地去修正不如显式地在输入侧加一个颜色增强模块先把可见光拉回正常分布再去做模态融合。这就是CEM模块Color Enhancement Module要干的事。2. CEM颜色增强模块的核心设计双色引导保真色与轻量调制提对比度CEM模块整体上分两个子分支一个做保真色修复一个做对比度调制。两个分支各自轻量、可端到端训练、可以随时插拔。我在设计时给它们分了明确工种保真色负责“把退化的颜色拉回来”对比度调制负责“把模糊的纹理推清晰”。2.1 双色引导保真色的实现原理先说为什么叫“双色引导”。我在实验里发现大多数去雾算法的问题是过度追求“清”把物体原有的颜色也一起改掉了。检测任务并不需要极致复原它需要的是“颜色语义保真”——红色尾灯还是红色绿色指示牌还是绿色。CEM里我用了RGB和HSV两个颜色空间的分工。具体做法是这样的输入图像F_rgb在进入Backbone前被拆成两条路径。一条路径保留原始RGB信息经过一个很浅的残差卷积结构做结构保持另一条路径把RGB转成HSV重点处理H色相和S饱和度通道V亮度通道在雾天退化最严重受大气光影响最大但它恰恰是很多去雾算法用来估计透射率的依据——这不仅不够准确还会把色相信息一起带偏。在HSV路径里我加了一个可学习的色彩引导矩阵M_color。这个矩阵的输入不只是当前帧的HSV统计量还同时接收红外/深度模态的特征统计量作为参考。这一点非常重要红外模态不受雾影响它提供的全局亮度和温度分布能帮助判断哪些区域的色偏是大气光造成的、哪些是物体本身的颜色。通过这个跨模态引导模块对色度通道做全局白平衡和色彩迁移修正得到修正后的HSV图再转回RGB通过残差连接与原始RGB相加。用残差而不是直接替换是我反复实验后确认的关键设计。直接替换的问题在于一旦修正结果有点过度比如把远处的灰白车漆硬拉成了偏红检测主干原本学好的特征分布会被破坏训练早期AP反而下降。残差结构让模块刚开始时只做很小的修正网络有一个平稳过渡期随着训练推进模块再逐步加大修正力度整个过程温和且收敛更稳。2.2 轻量调制提对比度的机制保真色修正解决的是“颜色漂移”但雾天还有另一个问题局部对比度下降导致纹理细节丢失。这两个问题其实是叠加的颜色漂移是全局性的偏色对比度下降是局部性的模糊。轻量调制分支的做法参考了图像增强里常用的对比度拉伸思想但被我改到特征级去做了。它的流程是对输入特征图按通道计算全局均值和标准差然后经过一个瓶颈MLP——先用1×1卷积把通道压缩到原来的1/4ReLU激活再用1×1卷积恢复到原通道数——从而生成每个通道的调制系数α和偏置β。最终输出是F_enhanced α × G(F) β其中G(F)是对特征图做了Instance Norm归一化后的结果。归一化之后再做缩放平移相当于把每个通道的对比度重新拉开。雾越浓的区域局部方差越低归一化后的拉伸越明显理论上该区域的纹理就越清晰。为什么强调轻量因为检测器做的是端到端推理雾天场景往往又是车载、巡检这类对延迟敏感的应用。如果为了增强效果在输入侧挂一个几十层的大网络整体推理速度会崩。这个调制分支只用了两个1×1卷积加一个归一化参数量大约增加0.3M在GPU上几乎可以忽略。我在低算力平台也测过新增延迟在0.5毫秒以内性价比非常高。3. 把CEM真正嵌进YOLOv13多模态框架接入位置、融合策略与训练方案模块设计得再好接入方式不对也一样白费。CEM的接入位置我前后试了三种差异非常明显。这一章讲清楚每种方案的取舍、最终采用的组合方式以及训练时的损失函数和分阶段策略。3.1 接入位置融合前还是融合后我对比过三种接入位置总结如下表接入位置实现方式实测效果问题Backbone最前端预处理在整个网络前先跑一遍CEM对像素图做增强提升有限像素级增强没有梯度和检测目标对齐容易过平滑Backbone第一个卷积之后CEM作用在浅层特征上再进后续主干提升明显收敛稳定需要额外维护一条CEM支路特征金字塔融合之后增强只作用于多模态融合结果单独用效果弱此时低层细节已经被卷积消耗补救空间小最终我采用的是“输入端残差修正 Backbone浅层特征调制”的组合方案。第一层卷积之后加入保真色分支的残差修正让网络看到的浅层颜色分布更接近清晰域对比度调制分支放在P2和P3这两个高分辨率特征层上因为这两层对应小目标的细节和纹理是雾天最容易丢失信息的位置。这里有个细节要注意CEM的两个分支是解耦的。保真色分支作用在主干浅层对比度调制作用在特征金字塔的低层。不要把它们都堆在同一个位置否则模块内部互相干扰调参很痛苦。3.2 跨模态对齐、融合层与通道组织多模态融合部分我的做法是保留YOLOv13原有的双流结构可见光分支接CEM增强红外/深度分支保持原始平流。两个分支各自提取特征后在NECK的P2、P3、P4层做通道拼接再用1×1卷积做通道压缩和权重分配。这种“增强只见可见光融合时全模态参与”的好处是红外分支的梯度不受CEM影响训练更稳定融合层能同时看到修复后的颜色特征和可靠的红外热辐射特征。从实验结果看完整融合后的特征比单独用任一分支都更稳健尤其是浓雾场景下红外分支可以纠正CEM在极低透射率区域的信息丢失。跨模态对齐是不得不提的硬骨头。我在早期版本里直接把可见光和红外特征做拼接结果在目标边缘出现大量错位伪影。后来做了两步修正第一步训练期间对红外特征施加随机的平移、旋转扰动模拟真实标定误差让融合层学习容忍错位第二步在损失计算时对透射率极低、可见光几乎完全失效的区域做掩码降权避免网络被这些区域的不可靠增强结果带偏。3.3 损失函数与分阶段训练策略CEM不能单独训练了再接回来也不能上来就端到端硬训练。我试过直接端到端前200个迭代损失下降得很猛之后就开始震荡。原因在于CEM的参数初始状态下还没学会正确的颜色修正方向梯度信号是乱的把主干原本稳定的特征分布全带偏了。最终采用三阶段训练第一阶段单独预训练保真色分支。用合成雾图对清晰图做重建损失函数是L1加饱和度一致性约束L_sat L1(F_recovered, F_clean) λ × ||Sat(F_recovered) - Sat(F_clean)||²这个阶段只让CEM学会“把雾天颜色拉回正常”不碰检测损失。第二阶段冻结保真色分支只训练对比度调制分支和整个检测网络一起端到端。此时检测损失CIoU加分类BCE加上对比度正则L_contrast max(0, η - Var(F_enhanced))这条约束的含义是增强后特征的局部方差不能低于阈值η防止对比度调制过度平滑或过度锐化。第三阶段全部解锁联合微调。学习率降到第一阶段的1/10此时CEM已经收敛到一个合理区间主干特征也适应了增强后的分布联合微调能让两个部分配合得更默契。需要提醒的是第一阶段不要训练太久。我在合成雾图上训练超过20轮CEM开始过拟合合成雾分布真实雾天反而下降。建议保留验证集选在真实雾天图像上表现最好的checkpoint不要只看合成数据指标。4. 实验效果提升幅度、消融贡献与推理开销这一章给出一份可供复现的评测方案和实验结论。指标采用COCO风格的mAP50以及小目标AP_s分组指标数据集用的是合成雾增强训练集加真实雾天测试集全部封闭验证保证可比性。4.1 消融实验每个子模块的真实贡献我做了一组四路消融结果如下配置mAP50AP_s参数增加推理延迟增加基准YOLOv13多模态融合38.521.3--仅加保真色分支42.124.90.5M0.6ms仅加对比度调制40.322.80.3M0.5msCEM完整两个分支45.728.40.8M1.2ms先说结论保真色分支的贡献明显大于对比度调制分支这个符合预期因为雾天检测的主导问题就是颜色分布偏移单独加对比度调制只提升1.8个点说明在不修正颜色域的强况下单纯提对比度效果有限。但两个分支合在一起不是简单相加而是协同放大——完整CEM比单独使用任一分支的高值还高出3.6个点。从分组看小目标AP_s的提升幅度从21.3到28.4比大目标更明显。原因是小目标主要依赖浅层颜色纹理特征雾天最先丢失的就是这部分信息CEM恢复浅层输入域后小目标的特征恢复最彻底。4.2 不同雾浓度下的表现差异我还把真实雾天测试集按能见度分了三组轻雾能见度300米以上、中雾150米到300米、浓雾150米以下。基准和CEM在这三组的表现差异也值得一看雾浓度基准mAP50CEM mAP50提升轻雾44.248.64.4中雾36.844.98.1浓雾30.142.312.2雾越浓CEM带来的提升越大这个结论非常有价值。浓雾场景下基准模型已经接近“基本不可用”而加了CEM之后依然能维持在42以上的水平。这说明颜色增强组件在极端退化条件下比任何检测头层面的缝缝补补都更有性价比。4.3 推理开销与部署注意点参数增加0.8M推理延迟增加1.2毫秒这是在公司内部GPU集群和边缘部署板卡上分别测过的。GPU上损失很小边缘板卡上延迟略高因为1×1卷积和通道统计计算在低算力设备上还是有一定开销。如果你部署的硬件特别紧张我建议优先保留保真色分支砍掉对比度调制因为消融实验证明对比度调制的单独贡献有限如果把两个分支都保留又要压缩延迟可以对P3层不做对比度调制只做P2层延迟能控制在0.8毫秒左右。另外要提醒的是CEM的输入尺寸和Backbone第一层要严格对齐。如果中间有resize、padding操作色彩引导矩阵的统计量会被污染增强效果明显下降。这个坑我遇到过代价是排查了半天才发现是预处理管道的尺寸不一致导致的。5. 复现过程中踩过的坑与完整排查链路这里写三个我在复现和调优过程中实际踩过的坑每一个都花了不少时间定位。把这些过程完整写出来比直接给结论更有参考价值——至少你能知道排查思路长什么样遇到类似问题不至于从头开始。5.1 训练震荡且验证集AP跳变BatchNorm统计量与颜色恢复冲突现象非常典型第一阶段预训练CEM很顺利第二阶段解锁检测网络一起端到端训练后前200轮loss下降正常之后开始阶梯式震荡每个epoch之间验证集AP跳动超过5个点。一开始我以为是学习率太大调到1e-5也没有实质改善。排查链路从batch数据入手。我把每个batch增强前后的特征图统计量打印出来发现经过CEM保真色分支后RGB三通道的均值和方差在batch间波动极大有的batch R通道均值是110下一个batch直接跳到145。问题就出在CEM内部用了BatchNorm而颜色恢复操作本身就大幅改变了特征分布BatchNorm又把这种分布变化成倍放大了导致后续卷积层输入分布剧烈波动。修复方案是把CEM内部的BatchNorm全部替换成InstanceNorm。InstanceNorm在单样本上独立计算统计量不会被batch内的色彩分布差异干扰增强后的特征分布平稳了很多。替换之后训练Loss曲线立即恢复平滑验证集AP的跳变也随之消失。如果你要用BatchNorm也不是不行但需要把CEM分支和主干分支的BatchNorm分成两组统计量分别维护操作起来麻烦不如直接上InstanceNorm省心。5.2 目标边缘出现彩色伪影多模态引导矩阵放大了对齐误差第二阶段后期我在真实雾天图像上观察到一类奇怪现象检测框内的目标边缘尤其是车顶和车窗交界处出现了明显的红蓝彩边类似高光溢出的伪影。这些伪影在基准模型上不存在说明是CEM引入的。排查时我先单测了保真色分支发现单独跑RGB流没有彩边加上红外引导矩阵后立刻出现。我检查了红外图和可见光图的配准精度发现这台设备上两个模态的平均对齐误差有3到5个像素恰好和目标边缘宽度一个量级。引导矩阵从红外特征中提取统计量时把边缘附近的红外信息当成了颜色先验而且对齐误差让这个先验加到了错误的像素位置于是形成了彩色伪影。我当时给了两套修复方案。第一套是彻底的对齐增强训练时对红外特征做随机平移扰动平移量范围对应实际误差的1到2倍让融合层和CEM都学会容忍错位。第二套是针对透射率极低区域的掩码策略用轻量网络估算透射率图对透射率低于0.1的区域几乎完全被雾覆盖将引导矩阵的权重置零这些区域的信息已经不可信强行增强只会制造伪影。两套方案叠加之后彩边问题基本消失真实雾天下的mAP又提升了1个多点。5.3 浓雾区域噪声被过度放大调制参数需要约束最后一个问题出在对比度调制分支。早期版本里调制参数α没有做范围限制网络在浓雾区域为了强行拉大对比度会把α推到超过10的数值结果特征方差是被拉开了但低频噪声也被同步放大。具体表现是强雾区域的目标被一层明显的颗粒状纹理覆盖检测器把噪声误判成目标纹理产生了大量置信度虚高的误检框。排查后确认是调制参数的范围问题。修复方式是给α的logits加了一个tanh激活把最终调制系数限制在0到2之间再叠加一个残差连接让调制分支在不工作时输出等同于输入的特征。这个小小的改动让误检率下降了差不多20%而且在晴天下也几乎没有副作用——因为调制分支在晴天域会自然输出接近1的系数相当于组件自动关闭了。最后聊一个调试心得把CEM模块方案回滚过三次之后我最大的体会是任何输入侧增强组件都一定要尊重检测主干的“既有认知”。你可以在输入域上做修正但要通过残差连接给网络留出选择权——让网络自己决定用多少修正后的信息而不是强行替换掉它原先看到的内容。这个原则不仅适用于CEM也适用于所有去雾、去雨、超分这类在检测前加增强模块的设计。做完这套改进之后我养成了一个习惯每次评估雾天检测效果不再只看图表上的mAP而是会专门去找雾最浓、颜色退化最严重的那些样本逐张看增强前后的浅层特征图和最终输出。很多问题指标上已经被平滑掉了只有盯着最恶劣的少数样本才能真正知道模型还有没有救。CEM这套思路目前在我们内部的几个雾天项目里都稳定复用了希望这篇记录也能帮你在自己的多模态检测方案里少走几段弯路。