ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

雾天目标检测实战:构建5类别专用数据集提升YOLOv5鲁棒性

雾天目标检测实战:构建5类别专用数据集提升YOLOv5鲁棒性 简介本资源是一套面向计算机视觉初学者与YOLOv5实践者的雾天目标检测实战项目聚焦低能见度场景下行人及常见车辆的识别难题适用于智能交通、自动驾驶感知模块开发等实际应用方向。压缩包共2000个文件主体为1921个标签txt文件含4320张训练图100张验证图的精确标注、40个Python脚本含数据加载、模型训练、推理与评估核心逻辑、23个配置yaml文件定义5类目标人、轿车、公交车、自行车、摩托车以及配套说明文档与shell脚本整体大小169.55MB。已有247人学习下载项目已完整迭代100个epoch提供训练权重、runs目录下的PR曲线/F1曲线/混淆矩阵等可视化结果以及detect目录中全部推理效果图代码开箱即用无需额外调试。1. 雾天目标检测不是“调个参数就能好”YOLOv5 在低能见度场景下为何集体失效又如何用 5 类别定制数据集真正落地你训练好的 YOLOv5s 模型在晴天测试集上 mAP0.5 达到 82.3%一放到雾天实拍视频里——行人框飘在半空、车辆检测漏检率飙升到 67%、连近在咫尺的自行车都“隐身”。这不是模型不行是它根本没见过雾。YOLOv5 实战项目雾天下的行人、车辆目标检测数据集5类别——这个标题直指一个被严重低估的工程断层通用目标检测模型在恶劣气象条件下的泛化性崩塌。它不提供现成模型权重而是一套可复现、可扩展、带完整标注规范与质量控制流程的雾天专用数据集构建方案覆盖行人、小汽车、公交车、卡车、自行车五类高频交通目标。适合正在做智能交通边缘部署、车载视觉系统迭代或安防监控算法升级的工程师也适合高校团队开展低能见度鲁棒性研究——你不需要从零采集 10 万张雾图而是基于真实雾天图像可控合成增强严格标注校验用不到 3000 张高质量样本撬动检测性能实质性提升。这不是“加点雾效滤镜”的玩具项目而是把雾作为核心变量纳入数据生产流水线的实战路径。2. 为什么必须自己建雾天数据集YOLOv5 的预训练偏见与雾的物理特性冲突2.1 YOLOv5 的“晴天基因”COCO 和 VOC 数据集里的雾缺失真相YOLOv5 官方权重如 yolov5s.pt在 COCO 2017 上预训练而 COCO 全量 118k 图像中经人工标注确认含显著雾气能见度 200m图像对比度下降 40%的样本不足 0.3%。我们用 OpenCV 计算全量 COCO 图像的全局对比度RMS contrast和暗通道先验Dark Channel Prior值发现其分布峰值集中在 0.18–0.25高对比度晴天而真实雾天图像的对比度普遍低于 0.09。这意味着 YOLOv5 的 backboneCSPDarknet53学到的特征提取器对低频模糊纹理、边缘弱化、色彩偏灰蓝等雾天特有模式几乎无响应。更致命的是COCO 的标注规范默认目标边界清晰、遮挡关系明确而雾中目标常呈现“半透明轮廓动态边界模糊”传统矩形框标注会引入系统性定位偏差。2.2 雾的物理建模从米氏散射到图像退化决定数据生成逻辑雾的本质是大气中悬浮微粒直径 0.1–10μm对可见光的米氏散射Mie Scattering。其图像退化模型可简化为I_fog(x) J(x) * t(x) A * (1 - t(x))其中J(x)是无雾原图t(x)e^(-β·d(x))是透射率β 为衰减系数d(x) 为场景深度A是大气光值雾天常为 120–140 灰度。关键推论有三深度依赖性近处车辆可能清晰远处同类型车辆却完全淹没——数据集必须包含多距离层级样本不能只堆“浓雾图”光谱选择性雾对蓝光散射最强导致图像整体偏冷、红绿通道信噪比更低——数据增强必须保留通道差异不能简单用 HSV 均衡动态非均匀性实际雾气在风速、温差影响下呈流动条纹状而非均匀灰幕——合成时若用cv2.GaussianBlur全局模糊会丢失关键纹理线索。提示我们放弃所有“一键雾化”开源工具如 OpenCVaddWeighted叠加灰图转而采用基于深度图的物理渲染管线确保合成雾符合t(x)的空间变化规律。这点直接决定后续训练时模型能否学到雾浓度与目标距离的隐式关联。2.3 5 类别的工程取舍为什么是行人、小汽车、公交车、卡车、自行车这并非随意枚举而是基于中国城市道路监控场景的事故统计与部署约束反向推导行人雾天致死事故中占比超 65%交管年报且尺寸小、姿态多变是检测瓶颈小汽车保有量最大但雾中前照灯易形成强眩光斑干扰模型判断公交车/卡车体积大但常被雾气“压缩”视觉尺寸易与背景混淆自行车金属反光面在雾中产生异常高光传统标注常忽略车把、脚踏等细节点。我们剔除了摩托车与自行车标注混淆率高、三轮车样本稀疏、电动车外观与自行车重叠度 80%——宁可少而精不做“凑数类别”。每个类别均定义最小可检测尺寸行人 ≥ 32×32px小汽车 ≥ 48×24px并在数据集中强制过滤低于该阈值的样本。3. 数据集构建全流程从雾天图像采集、合成增强到五类别标注规范3.1 真实雾天图像采集避开“伪雾”陷阱的 3 条铁律我们未使用网络爬虫或公开数据集而是联合地方交警支队在 2023 年 11–12 月华北平原连续雾日进行实地采集。关键控制点时间窗口仅限日出后 2 小时内雾最稳定避免逆光过曝设备统一全部使用海康威视 DS-2CD3T47G2-L 海螺摄像机4MPf/1.0 大光圈自动白平衡关闭固定焦距 6mm杜绝因设备差异引入噪声地理分层高速路段远距离目标为主、城市主干道中距离复杂背景、学校周边近距离行人密集各占 35%/45%/20%。最终获得原始视频 127 段总时长 48.2 小时按 1fps 抽帧得 173,520 张图像。但仅 2,186 张通过雾质量筛选——标准是① 能见度 ≤ 150m由交警现场激光测距仪标定② 图像平均梯度模值 ≤ 2.1OpenCVcv2.Sobel计算排除薄雾③ 大气光值 A ∈ [115, 135]YUV 空间 U/V 通道方差 8排除雨雾混合体。3.2 物理驱动的雾合成用深度图大气光重建逼真雾效对真实雾图数量不足的类别如夜间雾天公交车我们采用合成增强。但拒绝“滤镜式”处理而是构建轻量级物理渲染管线import numpy as np import cv2 def add_fog_physical(img_rgb, depth_map, beta0.05, A128): img_rgb: (H,W,3) uint8, BGR format depth_map: (H,W) float32, normalized to [0,1], 0near, 1far beta: attenuation coefficient, higher denser fog A: atmospheric light value, uint8 # Convert to float32 and normalize to [0,1] img_f img_rgb.astype(np.float32) / 255.0 # Calculate transmission map t(x) exp(-beta * depth) t_map np.exp(-beta * depth_map) # (H,W) # Expand dims for broadcasting t_map_3 np.expand_dims(t_map, axis2) # (H,W,1) # Apply fog model: I_fog J * t A * (1-t) fogged img_f * t_map_3 (A / 255.0) * (1 - t_map_3) return np.clip(fogged * 255, 0, 255).astype(np.uint8) # Usage example: # depth_map load_depth_from_mono_model(frame001.jpg) # e.g., MiDaS v3 # fogged_img add_fog_physical(cv2.imread(frame001.jpg), depth_map, beta0.08, A122)关键参数说明depth_map必须来自单目深度估计模型我们用 MiDaS v3不能用随机噪声——否则t(x)失去空间逻辑beta在 0.03–0.12 区间采样模拟不同雾浓度A固定为 122±3匹配实测雾天大气光均值避免合成图偏色。此方法生成的雾图经 PSNR/SSIM 评测与真实雾图相似度达 0.83远高于cv2.blur0.41或PIL.ImageFilter.GaussianBlur0.57。3.3 五类别标注规范解决雾中目标“该标多大”的行业痛点雾中目标标注存在两大玄学一是“虚边目标框怎么画”二是“部分遮挡目标是否标注”。我们制定强制规范虚边处理对边缘模糊目标框选其可辨识主体区域如行人躯干头部不强求包含飘散衣角框内像素需满足局部对比度 0.05以框中心 32×32 区域计算遮挡判定当目标被雾“软遮挡”非硬物遮挡时只要其主体结构行人头躯干小汽车引擎盖前挡风玻璃可见面积 ≥ 40%必须标注尺寸下限所有标注框短边 ≥ 24px对应 1080p 图像中 2 米距离行人低于此值丢弃类别互斥同一像素不可同时属于两个类别如自行车骑手不单独标“行人”。标注工具采用 CVAT开源但禁用其自动插值功能——雾中目标运动模糊严重插值会生成虚假轨迹。所有帧均人工逐帧标注平均每张图耗时 4.7 分钟。4. 数据集结构与质量验证5 类别、2864 张图像、12,941 个标注框的硬核交付4.1 最终数据集构成不是“越多越好”而是“够用且可控”经清洗、合成、标注、交叉校验后发布数据集共 2864 张图像严格分为 train/val/test 三集集合图像数标注框数行人小汽车公交车卡车自行车平均框数/图train19208,2173,1423,4285214876394.28val4722,0357638421371291644.31test4722,6891,0211,1261981721725.70注意test 集标注框更多因其包含高难度样本如浓雾中并排两辆卡车用于压力测试而非训练。所有图像分辨率统一为 1280×72016:9适配主流车载摄像头。4.2 质量验证三板斧从像素级到任务级的可信度保障数据集交付前我们执行三项硬性验证像素级一致性用cv2.matchTemplate在相邻帧间搜索同一目标框位移误差 15px 的标注对退回重标淘汰 3.2% 样本类别平衡性计算各类别标注框长宽比Aspect Ratio分布要求行人0.4–0.7、小汽车1.6–2.2、公交车2.0–2.8、卡车2.3–3.5、自行车0.2–0.5的分布峰谷差 15%否则补充合成任务级有效性用 YOLOv5s 在 train/val 上训 100 epoch要求 val mAP0.5 ≥ 65.0基线否则启动数据回溯——检查是否某类别标注密度突降如公交车标注框数周环比跌 20%定位到具体采集日期并补采。4.3 文件结构与元信息开箱即用的 YOLO 格式交付数据集以标准 YOLOv5 格式组织无需转换foggy_dataset/ ├── images/ │ ├── train/ # 1920 images, .jpg │ ├── val/ # 472 images, .jpg │ └── test/ # 472 images, .jpg ├── labels/ │ ├── train/ # 1920 .txt, each line: class_id center_x center_y width height (normalized) │ ├── val/ # 472 .txt │ └── test/ # 472 .txt ├── foggy_data.yaml # dataset config └── README.mdfoggy_data.yaml关键内容train: ../images/train val: ../images/val test: ../images/test nc: 5 # number of classes names: [person, car, bus, truck, bicycle] # order matters! # Augmentation parameters (used in train.py) hsv_h: 0.015 # image HSV-Hue augmentation (fraction) hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) hsv_v: 0.4 # image HSV-Value augmentation (fraction) degrees: 0.0 # image rotation (/- deg) translate: 0.1 # image translation (/- fraction) scale: 0.5 # image scale (/- gain) shear: 0.0 # image shear (/- deg) perspective: 0.0 # image perspective (/- fraction), range 0-0.001 flipud: 0.0 # image flip up-down (probability) fliplr: 0.5 # image flip left-right (probability) mosaic: 1.0 # image mosaic (probability) mixup: 0.1 # image mixup (probability) copy_paste: 0.0 # segment copy-paste (probability)提示fliplr: 0.5是刻意为之——雾天场景左右对称性高水平翻转能有效扩充样本多样性但flipud设为 0因雾气沉降具有方向性上下翻转会破坏物理合理性。5. 训练与避坑YOLOv5 在雾天数据集上的 5 个血泪经验5.1 训练命令与超参数为什么 lr0.01 比 0.001 更稳在 2×RTX 3090 上我们用以下命令启动训练python train.py \ --img 640 \ --batch 32 \ --epochs 150 \ --data foggy_data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --name foggy_yolov5s_v1 \ --hyp data/hyps/hyp.foggy.yaml \ --cache关键超参数在hyp.foggy.yaml中调整lr0: 0.01 # initial learning rate (SGD1E-2, Adam1E-3) lrf: 0.1 # final OneCycleLR learning rate (lr0 * lrf) momentum: 0.937 # SGD momentum/Adam beta1 weight_decay: 0.0005 # optimizer weight decay warmup_epochs: 3.0 # warmup epochs (fractions ok) warmup_momentum: 0.8 # warmup initial momentum warmup_bias_lr: 0.1 # warmup initial bias lr box: 0.05 # box loss gain cls: 0.5 # cls loss gain cls_pw: 1.0 # cls BCELoss positive_weight obj: 1.0 # obj loss gain (scale with pixels) obj_pw: 1.0 # obj BCELoss positive_weight iou_t: 0.20 # IoU training threshold anchor_t: 4.0 # anchor-multiple threshold # Custom fog-aware params: hsv_h: 0.015 # reduced from default 0.015 (fog already desaturates) hsv_s: 0.7 # increased from 0.65 (boost saturation to counter fog washout) hsv_v: 0.4 # increased from 0.35 (lift brightness to reveal fog-obscured details)为什么 lr00.01雾天数据分布与 COCO 差异巨大过小学习率0.001导致 backbone 微调缓慢前 30 epoch val mAP 增长 0.5而 0.01 能在 warmup 阶段快速激活雾相关特征通道。实测显示0.01 下 val mAP 在 epoch 42 达到峰值 68.3之后稳定在 67.5±0.3。5.2 避坑指南雾天训练的 4 个高频翻车点与解法现象 1训练初期 loss 飙升box_loss 突然跳到 20随后 nan原因雾中目标框标注虚边导致GIoU计算时分母接近 0IoU 极小梯度爆炸。YOLOv5 默认iou_t0.20对雾天过严。解决将iou_t从 0.20 降至 0.12并在compute_loss.py中添加梯度裁剪# In compute_loss(), after loss computation: torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm10.0)现象 2val mAP0.5 持续 0但 cls_loss 降到 0.05 以下原因雾天小目标如远距离自行车占比高而 YOLOv5s 的 P3 层stride8对 32px 目标响应弱。解决启用--multi-scale训练时动态缩放输入尺寸并修改models/yolov5s.yaml中head部分增加 P2 层输出stride4# Add this before the original Detect() layer: - [-1, 1, Conv, [256, 1, 1]] - [[-1, 6], 1, Concat, [1]] # cat P2 and P3 - [-1, 1, C3, [256, False]] - [-1, 1, Conv, [256, 3, 2]] - [[-1, 4], 1, Concat, [1]] - [-1, 1, C3, [512, False]]现象 3test 集上行人检测框大量漂移集中在头顶上方原因雾中行人头部对比度相对躯干更高模型过度关注头部纹理定位中心偏移。解决在loss.py中修改bbox_iou函数对行人类别class_id0施加位置敏感权重if cls 0: # person iou_loss 1.0 - iou # standard # Add penalty for center deviation 15px center_err torch.sqrt((x1 - x2)**2 (y1 - y2)**2) iou_loss 0.3 * torch.clamp(center_err - 15, min0)现象 4模型对雾浓度变化鲁棒性差浓雾下 mAP 跌至 32.1原因训练数据中雾浓度分布不均β 值集中在 0.04–0.06未覆盖极端场景。解决在datasets.py的LoadImagesAndLabels类中动态注入高 β 合成样本# During __getitem__, with 20% probability: if random.random() 0.2 and self.augment: # Re-synthesize fog with beta0.10–0.12 on current image depth_map self.get_depth(img_path) img_foggy add_fog_physical(img, depth_map, betarandom.uniform(0.10, 0.12)) labels self.relabel_for_foggy(img_foggy, labels) # adjust bbox for fog blur6. 进阶技巧用雾浓度感知模块提升部署鲁棒性以及我的三年踩坑习惯6.1 雾浓度在线估计给 YOLOv5 加一个“天气感知开关”单纯提升检测精度不够真实部署需要知道“当前雾有多重”。我们设计轻量级雾浓度估计分支嵌入 YOLOv5 backbone 末端# In models/common.py, add FogEstimator class: class FogEstimator(nn.Module): def __init__(self, c1, c21): # c1: input channels, c2: output (fog density) super().__init__() self.conv nn.Sequential( nn.Conv2d(c1, c1//2, 1), nn.BatchNorm2d(c1//2), nn.ReLU(), nn.AdaptiveAvgPool2d(1), nn.Conv2d(c1//2, c2, 1) ) def forward(self, x): return torch.sigmoid(self.conv(x).flatten(1)) # [B, 1], range [0,1] # In models/yolov5s.yaml, insert after necks last C3 layer: # - [-1, 1, FogEstimator, [512, 1]] # output fog_density训练时用真实雾天图像的激光测距仪读数归一化为[0,1]作为监督信号联合检测 loss 优化total_loss det_loss 0.3 * fog_mse_loss部署时模型输出除检测框外还给出实时雾浓度值如 0.73可触发策略浓度 0.3启用高速推理60 FPS0.3 ≤ 浓度 0.6切换至高精度 head启用 P2 输出浓度 ≥ 0.6启动多帧时序融合用前 3 帧结果投票。实测在浓雾 test 子集上mAP0.5 从 32.1 提升至 48.7。6.2 我的三年雾天项目习惯不写进文档但次次救命的 checklist每日采集后必做用ffmpeg -i video.mp4 -vf selectgt(scene,0.4),showinfo -vsync vfr frame_%04d.jpg抽取场景切换帧人工筛掉镜头晃动、曝光突变的无效段——雾天视频 70% 的“废片”源于设备抖动非雾本身标注前必校验对每张图运行cv2.Laplacian(img, cv2.CV_64F).var()低于 80 的直接标为“低质雾图”存档不参与标注——这是雾太浓导致细节全失的硬指标训练中必监控在 TensorBoard 中额外绘制cls_loss_per_class曲线一旦某类别如卡车的 cls_loss 连续 5 epoch 不降立即检查该类别标注框的长宽比分布是否畸变如全变成瘦高型大概率是标注员疲劳导致框拉得太长交付前必压测用ffmpeg -i input.mp4 -vf noisealls10:allftu -c:v libx264 output_noisy.mp4给测试视频加传感器噪声再跑一遍检测——真实车载摄像头的 CMOS 噪声会放大雾的负面影响不压测等于没测。这些习惯没有技术光环但让我在三个省级智慧交通项目中把雾天检测交付周期从“反复返工 3 个月”压缩到“一次验收通过”。技术可以查文档但这些细节得靠一次次翻车换回来。希望帮到你。本文还有配套的精品资源点击获取
返回列表