首页 / 资讯中心 / 文章详情

基于18800张图片的火焰烟雾检测:YOLO与VOC格式转换及训练避坑指南

基于18800张图片的火焰烟雾检测:YOLO与VOC格式转换及训练避坑指南 ★ FEATURED ARTICLE
简介这份资源面向计算机视觉方向的学习者与工程开发者提供一套可直接投入训练的火焰与烟雾目标检测数据集解决自建数据采集难、标注成本高的问题适合训练YOLO系列检测模型也可用于安防监控、森林防火等场景的算法验证。压缩包共收录2000个文件以XML标注文件为主对应VOC格式的边界框与类别信息同时提供YOLO所需的TXT格式标注图片总量达18800张覆盖fire、smoke等类别包体约765.03MB目录组织清晰拿到后无需任何转换或清洗即可直接用于训练。目前已有1428人学习下载数据规模与标注完整度在同类资源中较为突出可帮助读者省去数据准备环节把精力集中在模型结构调优、训练策略对比与指标分析上也便于快速复现火焰烟雾检测实验、验证不同骨干网络与数据增强方案的效果。1. 火焰烟雾检测数据集选型18800 张图片到底该怎么用手上有个火焰烟雾检测的项目要落地第一道坎往往不是模型结构而是数据。你搜到一份标注好的资源18800 张图片同时给了 YOLO 的 TXT 和 VOC 的 XML 两套标注。这个量级在烟火检测里算中等偏上够训一个能用的基线但离“随便训都收敛”还差得远。烟火检测的难点在于目标形态极不固定——火焰有跳动、半透明、边缘模糊烟雾更是没有清晰轮廓同一堆火在不同光照下标注框能差出一大截。所以拿到这份数据真正要解决的问题是格式怎么选、类别怎么对齐、划分怎么切、增强怎么做才能让 YOLO 系列模型在 mAP 上站得住。这篇面向的是已经拿到或准备用这类数据集的算法工程师和落地开发者从格式转换一路讲到训练参数和排错新手能照着跑熟手能对着边界调。2. TXT 与 XML 双格式先搞清 YOLO 和 VOC 的坐标逻辑2.1 两种标注格式的本质差异VOC 的 XML 是“绝对坐标 图像尺寸”的描述方式。一个object里有bndbox里面是xmin、ymin、xmax、ymax单位是像素原点在左上角。它天然携带图片宽高所以框的位置是自解释的。YOLO 的 TXT 则是“归一化中心点 宽高”每行class_id cx cy w h全部除以图片宽高取值 0 到 1。它不记录图片尺寸解析时必须配合原图才能还原。这两种格式没有谁更高级只有谁更贴合你的训练框架。Ultralytics 系的 YOLO 默认吃 TXTPascal VOC 时代的检测框架和部分评估脚本吃 XML。这份数据集两套都给省了你从零标注的功夫但省不掉对齐的功夫。2.2 类别映射别让 class_id 错位XML 里类别是字符串比如namefire/name、namesmoke/nameTXT 里是整数0、1。转换时最容易翻车的地方就是映射表不统一。有的数据集 fire 是 0、smoke 是 1有的反过来还有的把“火焰”“烟雾”“fire”“smoke”混着写。你拿到数据第一件事不是转格式是统计两套标注里的类别名确认它们指向同一套语义。下面这段脚本干的就是这件事同时输出每个类别的框数量方便你判断有没有类别极度不平衡。import os import xml.etree.ElementTree as ET from collections import Counter def count_voc_classes(xml_dir): counter Counter() for f in os.listdir(xml_dir): if not f.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, f)) for obj in tree.findall(object): name obj.find(name).text.strip() counter[name] 1 return counter def count_yolo_classes(txt_dir, names): counter Counter() for f in os.listdir(txt_dir): if not f.endswith(.txt): continue with open(os.path.join(txt_dir, f)) as fh: for line in fh: parts line.strip().split() if len(parts) 5: counter[names[int(parts[0])]] 1 return counter # 用法先跑 VOC 统计再跑 YOLO 统计对比两边类别名和数量 print(count_voc_classes(annotations_xml)) print(count_yolo_classes(labels_txt, names[fire, smoke]))逻辑说明VOC 侧直接读 XML 的object/nameYOLO 侧靠names列表把整数还原成字符串两边才能对比。参数上names的顺序必须和生成 TXT 时用的data.yaml完全一致否则统计出来的“fire”可能其实是 smoke。如果两边数量对不上先别急着转去查是不是有图片只有 XML 没有 TXT或者某张图漏标了。2.3 从 XML 转 TXT一个可复用的转换脚本确认类别对齐后转换本身不复杂但边界处理要小心。XML 里的框可能越界xmax 超过图片宽也可能宽高为 0这些脏框直接转成归一化坐标会得到负数或大于 1 的值训练时轻则 loss 异常重则直接 NaN。下面脚本在转换时做了裁剪和过滤。import os import xml.etree.ElementTree as ET from PIL import Image CLASS_MAP {fire: 0, smoke: 1} def voc_to_yolo(xml_path, img_dir, out_dir): tree ET.parse(xml_path) root tree.getroot() img_name root.find(filename).text img_path os.path.join(img_dir, img_name) w, h Image.open(img_path).size lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASS_MAP: continue cls_id CLASS_MAP[name] box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 裁剪到图像范围内 xmin max(0, min(xmin, w - 1)) ymin max(0, min(ymin, h - 1)) xmax max(0, min(xmax, w - 1)) ymax max(0, min(ymax, h - 1)) if xmax xmin or ymax ymin: continue # 丢弃无效框 cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_path os.path.join(out_dir, os.path.splitext(img_name)[0] .txt) with open(out_path, w) as f: f.write(\n.join(lines))逻辑说明CLASS_MAP是唯一权威的类别映射改这里就能适配不同命名。裁剪用min/max把坐标压回图像范围xmax xmin的判断过滤掉零面积框。归一化保留 6 位小数足够 YOLO 训练精度也避免文件过大。参数上img_dir必须和 XML 里的filename对得上如果 XML 里存的是相对路径或改了名需要先做一次文件名对齐。3. 18800 张烟火数据怎么切分与增强才不翻车3.1 训练验证测试的切分比例与陷阱18800 张不是小数目但烟火数据有个隐蔽问题同一场火灾的连续帧或同一场景的多角度图如果随机切分训练集和验证集会高度相似验证 mAP 虚高上线就露馅。常见做法是按“场景/视频源”分组切分而不是按图片随机切。如果你拿到的数据没有场景标签至少要做一次感知哈希去重把近似重复的图归到同一组再切。比例上8:1:1 是稳妥起点验证集和测试集各留 1880 张左右足够评估。如果某类样本特别少比如烟雾只占两成切分时要保证每个子集里两类都有别让验证集全是火焰。3.2 针对火焰烟雾的增强策略通用增强里翻转、缩放、色彩抖动都能用但有几条要特别注意。火焰和烟雾的颜色是重要线索HSV 空间的色调抖动幅度别太大否则火焰的橙红被抖成紫色模型学到的就是噪声。马赛克增强对烟火检测帮助明显因为它能制造多目标和小目标场景但要注意别把火焰和烟雾拼到完全不合理的位置比如火焰悬在半空。MixUp 在烟火上要慎用半透明叠加会让烟雾和背景糊成一片标注框失去意义。下面是一份 Ultralytics 训练配置里增强相关的关键参数直接对应到 YAML 或命令行。# data.yaml path: ./fire_smoke train: images/train val: images/val test: images/test names: 0: fire 1: smoke # 训练时的增强参数命令行或 cfg hsv_h: 0.015 # 色调抖动烟火场景建议调小 hsv_s: 0.7 hsv_v: 0.4 degrees: 10.0 # 旋转角度火焰旋转合理别超过 15 translate: 0.1 scale: 0.5 mosaic: 1.0 # 马赛克开启 mixup: 0.0 # 烟火场景建议关闭 fliplr: 0.5 flipud: 0.0 # 火焰上下翻转不符合物理关闭逻辑说明hsv_h从默认 0.015 已经算保守如果你发现模型对颜色过拟合可以再降到 0.01。flipud关掉是因为火焰和烟雾受重力影响有方向性上下翻转会制造不存在的物理场景。mixup设 0 是血泪经验烟雾和火焰叠加后标注框会覆盖两个语义区域模型学到的边界是错的。参数不是死的如果你的数据里火焰方向本来就杂乱degrees可以适当放大但每次改完都要看验证集的可视化结果。3.3 用 YOLO 跑通第一个基线数据切好、增强配好先跑一个基线确认流程通。以 YOLOv8 为例命令行一行就能起训但参数要按你的显存调。yolo detect train \ data./fire_smoke/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ workers8 \ device0 \ projectfire_smoke_runs \ namebaseline逻辑说明modelyolov8s.pt是速度和精度的折中18800 张图用 s 或 m 都合理n 可能欠拟合l 在单卡上显存吃紧。batch16对应 640 分辨率如果你用 24G 显存可以上 32但要注意学习率同步调整。workers8取决于 CPU 核数设太大反而拖慢数据加载。跑完先看results.png里的 mAP50 和 mAP50-95 曲线如果 mAP50 在 30 个 epoch 后还在 0.3 以下别急着调模型回去查标注。4. 烟火检测训练避坑从 loss 异常到漏检的排查清单4.1 现象loss 从第一个 epoch 就是 NaN原因通常不在模型在标注。归一化坐标里出现了负数或大于 1 的值或者某个 TXT 文件里混入了非数字字符。转换脚本没做裁剪、或者手动改标注时手滑都会埋这个雷。解决方式是跑一遍全量校验遍历所有 TXT检查每行是否 5 个字段、后四个是否在 [0,1] 区间、class_id 是否在合法范围。发现异常文件直接打印文件名和行号修完再训。别用try/except吞掉吞掉只会让问题更隐蔽。4.2 现象验证集 mAP 很高实际推理漏检严重这是典型的过拟合到验证集分布。原因多半是切分时同一场景的图同时进了训练和验证模型记住了背景而不是目标。解决方式是重新按场景分组切分或者至少做一次基于图像哈希的去重把相似图强制分到同一侧。另一个可能是增强过猛训练集分布被扭曲验证集却用了原始分布。检查mosaic和mixup的开启比例验证时这些增强是关闭的如果训练时开太满两者分布差距会拉大。4.3 现象烟雾检测框抖动大、置信度忽高忽低烟雾没有固定形状模型在相邻帧上给出的框可能差很多。原因除了数据本身还有 NMS 的 IoU 阈值设得不合适。烟火场景里烟雾框重叠是常态IoU 阈值太高会保留大量冗余框太低会把真实框也压掉。常见做法是把iou从默认 0.7 降到 0.5 到 0.6 之间试同时开agnostic_nms让两类共享 NMS避免火焰和烟雾框互相抑制。如果推理时用的是 TensorRT 或 ONNX注意 NMS 是在后处理里做的参数要同步改。4.4 现象小目标火焰完全检不出18800 张里如果有大量远距离小火点640 分辨率下可能只剩几个像素。原因不是模型不行是输入分辨率不够。解决方式有两个一是把imgsz提到 960 或 1280代价是显存和推理时间上涨二是切图推理把大图切成带重叠的小块分别检测再合并。切图时重叠区域建议留 20% 到 30%否则边界上的目标会被切碎。如果训练时就用高分辨率记得同步调整batch别让显存爆了。4.5 现象XML 和 TXT 数量对不上训练时报找不到标签原因通常是文件名不一致。XML 里的filename可能是img_001.jpg而 TXT 是按img_001.txt命名的但图片实际叫img_001.jpeg。扩展名大小写、前后空格、中文文件名都会导致匹配失败。解决方式是写一个对齐脚本以图片文件名为基准统一 XML、TXT 和图片三者的主文件名去掉扩展名差异。跑训练前用ls images/train | wc -l和ls labels/train | wc -l对一下数量差一个都别开训。5. 把 18800 张烟火数据用出上限从基线到可部署的几个技巧基线跑通只是起点。想让这份数据真正撑起一个可部署的烟火检测模型有几个我踩过坑之后固定下来的习惯。第一永远保留一份“干净验证集”——从原始数据里单独切出 500 到 1000 张不参与任何训练和调参只在最后评估用。这份验证集要覆盖不同光照、不同距离、有无干扰源比如夕阳、车灯、蒸汽的场景它才是你判断模型能不能上线的唯一依据。第二训练日志里的val/cls_loss比 mAP 更早暴露问题如果分类 loss 震荡不降多半是类别标注有歧义比如把带火焰的烟雾同时标了两类回去统一标注规则。第三导出 ONNX 或 TensorRT 时把预处理里的归一化和 letterbox 一起写进模型或固定在后处理里别让部署端再实现一遍版本一多必出错。第四烟火检测的误报大多来自类似火焰的暖色光源和类似烟雾的水汽如果上线后误报高别急着加数据先做一轮 hard negative 挖掘把误报图加进训练集比盲目扩量有效得多。我自己的习惯是每训完一版都把验证集里置信度在 0.3 到 0.6 之间的框导出来人工看一遍这批“灰色地带”样本往往指向标注或增强里的系统性问题。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站