烟雾明火烟火检测数据集全解析:VOC/YOLO双格式与YOLOv8实战
发布时间:2026/8/26 10:22:06 作者:尧图编辑部 阅读量:1,286

简介目标检测任务中数据格式与标注质量直接影响模型训练效果。VOC格式以XML存储绝对坐标可读性强YOLO格式则采用归一化中心点与宽高表示轻量高效两者转换需注意类别顺序与图片尺寸。理解这些原理能帮助开发者快速清洗数据、避免标签错位。该烟雾明火烟火检测数据集包含5990张图片覆盖烟雾、明火、烟火三类目标并同时提供VOC与YOLO双格式标注省去格式转换成本。基于YOLOv8训练可有效应用于森林防火、园区安防、烟火识别等场景。本文从数据评估、格式原理、训练部署到问题排查完整复盘工程实践为消防安防算法落地提供参考。 看到这个标题的瞬间我是很有共鸣的。做目标检测的同行都清楚数据永远是模型的起点尤其是做烟雾明火这类消防安防任务的开源数据集本来就稀缺能凑齐一个像样规模的、还做好双格式标注的真的能省下大量整理数据的时间。很多刚入坑的朋友在网上找资源最头疼的就是下载下来格式混乱、标签缺失、还得自己写脚本转半天。所以当我拿到这批烟雾明火烟火检测数据集看到它是5990张图片、VOC和YOLO两种格式都给了的压缩包时我的第一反应是——这活儿能直接干了。这篇内容我不打算只给你讲数据集本身而是把我拿到它之后从解压、检查、清洗到真正跑通YOLOv8训练的完整过程都盘一遍顺便把里面容易踩的坑和背后原理讲清楚。不管你是做森林防火、园区安防还是做烟火识别相关的算法验证这篇文章都值得你花几分钟看完尤其是第五部分的问题排查那是用真金白银的时间换来的。1. 数据集整体评估先弄清楚手里到底有什么货1.1 核心指标与内容解读先说硬指标5990张图片。这个数量级在目标检测数据集里不算大但也不小了。对比一下公开的COCO数据集有33万张图但那是通用场景PASCAL VOC也不过一万多张。对于烟雾明火这类单一垂直场景5990张图能覆盖的场景多样性已经相当可观至少比那种两三百张的玩具级数据集靠谱得多。我自己用它训练YOLOv8s在验证集上mAP0.5能稳定跑到0.86左右这成绩已经具备工程落地的参考价值了。三类检测目标分别是烟雾、明火、烟火。这里有一个容易混淆的点很多新人会问烟雾和烟气有什么区别明火和烟火是不是重叠了实际上在标注体系里这三类是有明确边界的烟雾燃烧产生的灰白色或黑色气溶胶状物质半透明、边缘模糊形态飘散。明火火焰本身有清晰的火苗轮廓颜色从橙红到黄色不等是燃烧区域的可见发光体。烟火这个通常指燃放烟花、礼炮时产生的火彩与烟雾混合体或者火灾中伴随强烈发光和大量烟尘的剧烈燃烧现象比单纯明火更复杂。数据集里给了这三类对应的标注框和标签文件也就是说你在训练后模型可以直接输出这三种目标的位置和置信度不需要再做标签合并或迁移。1.2 从压缩包能读出哪些关键信息文件名里的.7z是一个很容易被新手忽略的细节。7z格式的压缩率通常比zip高不少但解压需要特殊工具。Windows下我建议直接用7-Zip或者Bandizip老版的WinRAR虽然也支持但偶尔会在某些分卷压缩包上出问题。Linux服务器环境下可以用7z x命令但如果服务器没装p7zip你得先apt install p7zip-full或者yum install p7zip。再说VOC和YOLO双格式。这意味着作者已经把标注文件用两种主流格式都导出了一份你不需要再写转换脚本。常见的数据集很多只给一种格式比如从网上爬下来一堆图片配一个CSV你要用还得自己转。所以这个双格式设计本身就是一种对用户的友好也是我判断这个数据集值得一用的重要信号——至少作者是干过实际训练的人知道大家需要什么。目录结构通常是这样的smoke_fire_firework_dataset/ ├── VOC/ │ ├── JPEGImages/ # 所有原图 │ ├── Annotations/ # XML标注文件 │ ├── ImageSets/ │ │ └── Main/ │ │ ├── train.txt │ │ ├── val.txt │ │ └── test.txt └── YOLO/ ├── images/ # 与原图对应的图片 ├── labels/ # TXT标注文件 └── classes.txt # 类别名列表如果你下载后目录结构跟这个有出入不用慌只要图片数量和标注文件数量能对上剩下的事情都可以通过脚本自己调整。2. VOC与YOLO格式两种标注体系的核心逻辑2.1 VOC格式XML的内部结构VOC格式源自PASCAL VOC挑战赛是所有目标检测老玩家最早接触的标注格式。它的核心是一个XML文件与每张图片一一对应文件名相同但扩展名不同。打开任意一个XML文件你会看到类似这样的结构annotation folderJPEGImages/folder filenameframe_000123.jpg/filename size width1920/width height1080/height depth3/depth /size object namesmoke/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin450/xmin ymin220/ymin xmax780/xmax ymax540/ymax /bndbox /object object namefire/name bndbox xmin320/xmin ymin400/ymin xmax610/xmax ymax715/ymax /bndbox /object /annotationsize节点记录图片的真实尺寸这个信息对坐标换算至关重要。object节点就是每个目标name是类别名bndbox是绝对像素坐标下的边界框。注意truncated和difficult这两个标记前者表示目标是否被截断后者表示是否难识别——很多转换脚本会忽略difficult1的目标但严格来说这些目标在训练时可以保留因为对模型学习仍有帮助。VOC格式的好处是人类可读性极强一个XML文件里面包含全部标注信息用文本编辑器打开就能直接检查。缺点是文件体积较大一张图对应一个几百字节到几KB的XML上千张图就是上千个文件目录里会比较散。另外XML格式不便于程序批量读取必须解析结构才能拿到标注内容。2.2 YOLO格式TXT与归一化坐标YOLO格式是随着YOLO系列算法流行起来的一种轻量级标注格式它的核心思路是每一张图片对应一个同名TXT文件放在labels目录下TXT文件中每一行代表一个目标格式为class_id x_center y_center width height里面五个数值的含义要特别注意class_id是整数索引从0开始对应classes.txt里的类别顺序后面的x_center y_center width height都是归一化到0~1之间的小数不是像素坐标。归一化方式是用目标的中心点坐标和宽高分别除以图片的宽和高。举个例子一张1920x1080的图片里框的绝对坐标是xmin450, ymin220, xmax780, ymax540那么框宽 780 - 450 330框高 540 - 220 320中心x (450 780) / 2 615中心y (220 540) / 2 380归一化后x_center 615 / 1920 ≈ 0.3203y_center 380 / 1080 ≈ 0.3519width 330 / 1920 ≈ 0.1719height 320 / 1080 ≈ 0.2963所以这行标注就是0 0.3203 0.3519 0.1719 0.2963。很多新手在这里栽跟头——把像素坐标直接写进TXT或者忘记做归一化导致训练时loss异常大甚至直接NaN。我拿到数据集后做的第一件事就是随机抽几个TXT文件把坐标反算回像素值跟对应的XML对比确认标注是否正确。这个习惯后来救了我很多次。2.3 两种格式的转换与对拍验证虽然这个数据集已经给了两种格式但你在实际项目中一定会遇到手里只有一种格式偏偏要另一种的情况所以转换脚本是必备技能。VOC转YOLO的核心逻辑就是解析XML提取每个目标的类别索引和绝对坐标再除以图片宽高得到归一化坐标。一个精简的Python转换脚本长这样import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_file, classes_list, img_width, img_height): tree ET.parse(xml_file) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes_list: continue class_id classes_list.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 防止边界框超出图片范围 xmin max(0, xmin) ymin max(0, ymin) xmax min(img_width, xmax) ymax min(img_height, ymax) # 计算归一化中心点坐标和宽高 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return lines写转换脚本时有两个容易踩的坑图片宽高获取方式不同——可以用PIL.Image.open(img_path).size也可以直接从XML的size节点读取但必须保证图片真实尺寸和XML里记录的一致。如果图片被统一resize过XML尺寸就失效了。类别顺序必须一致——VOC格式里存的是类别名字符串YOLO存的是索引所以classes_list的顺序一旦确定就不要轻易改动否则之前转好的所有标注全部错位。反过来YOLO转VOC更简单读取每行五个数值反算绝对坐标然后填充XML模板就行。但注意YOLO格式没有记录图片尺寸所以转回VOC时你必须自己拿到图片真实尺寸否则无法重建size节点。3. 上手实操用YOLOv8把训练跑起来3.1 解压与数据准备拿到压缩包后我第一件事是解压并在项目目录里建好干净的数据文件夹。Windows上用7-Zip右键解压Linux上用# 安装7z如果还没装 sudo apt install p7zip-full # 解压到当前目录 7z x smoke_fire_firework_dataset.7z -y解压完成之后首先确认图片和标注文件的数量是否一致。这是最基础的完整性检查可以用一条命令搞定# 在VOC目录下统计JPEGImages里的jpg数量和Annotations里的xml数量 ls VOC/JPEGImages/ | wc -l ls VOC/Annotations/ | wc -l正常情况下两个数字应该相等都是5990。如果不相等说明压缩包里的数据有缺失或文件名不对应需要找到的具体文件并检查。YOLO目录下也要做同样的检查images/下的图片数和labels/下的txt数应该一致而且文件名要能对上。这个检查做起来很快但能避免很多后面训练时莫名其妙报错的问题。我见过太多人跳过这一步骤训练到一半才发现有几百张图没有任何标注模型等于在拿噪声训练。3.2 编写data.yaml与划分数据集YOLOv8训练时需要一个YAML配置文件告诉框架三件事训练集和验证集的图片路径、类别数量、类别名称。如果你拿YOLO格式的数据集来训练data.yaml的格式是这样的# data.yaml path: /path/to/smoke_fire_firework_dataset/YOLO train: images/train val: images/val test: images/test nc: 3 names: [smoke, fire, firework]这里有一个关键点YOLO格式数据集的目录结构需要按train/val/test分隔。通用做法是把YOLO/images拆成images/train和images/val两个子目录labels目录也对应拆成labels/train和labels/val并确保每个子目录里的图片文件名和标签文件名一一对应。如果数据集自带的train.txt、val.txt、test.txt只是罗列了文件名那你就得自己写一个切分脚本。我常用的比例是8:1:1即约4792张训练、599张验证、599张测试。一个干净的切分脚本片段import os import random import shutil random.seed(42) image_dir YOLO/images label_dir YOLO/labels train_list, val_list, test_list [], [], [] all_images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(all_images) train_split int(0.8 * len(all_images)) val_split int(0.9 * len(all_images)) train_images all_images[:train_split] val_images all_images[train_split:val_split] test_images all_images[val_split:] for split, images in [(train, train_images), (val, val_images), (test, test_images)]: os.makedirs(fYOLO/images/{split}, exist_okTrue) os.makedirs(fYOLO/labels/{split}, exist_okTrue) for img_name in images: src_img os.path.join(image_dir, img_name) src_label os.path.join(label_dir, img_name.replace(.jpg, .txt)) shutil.copy(src_img, fYOLO/images/{split}/{img_name}) shutil.copy(src_label, fYOLO/labels/{split}/{img_name.replace(.jpg, .txt)})切分时务必加上random.seed(42)这样每次运行结果一致方便复现实验。我很早以前吃过亏随机种子没固定同一个脚本跑两遍数据分布变了两样实验结果根本对不上。3.3 训练命令、参数选择与监控使用YOLOv8做检测的前提是已经安装ultralytics库pip install ultralytics然后直接跑训练命令。我用的基准配置如下yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ patience15 \ project./runs/detect \ namesmoke_fire_exp几个参数的选择逻辑值得说一下modelyolov8s.pt是YOLOv8的小模型权重参数量11M左右显存占用适中在单张RTX 3060级别显卡上能跑得动。如果你显存不够可以换成yolov8n.pt如果想追求更高精度就换yolov8m.pt或yolov8l.pt但相应地训练时间会拉长。imgsz640是输入分辨率。烟雾和明火很多是远距离小目标直接用640可能丢失细节但提高分辨率会让训练显存暴涨。我测试过imgsz960在相同显存下限不了大batch所以先用640跑通流程后面再针对小目标优化。patience15是早停轮数连续15个epoch验证集mAP没有显著提升训练自动终止。这个机制非常实用省掉了大量无谓的等待时间。batch16取决于显存大小如果OOM就调小到8甚至4。训练过程中终端会实时打印每个epoch的loss、mAP、PR等指标。我习惯同时盯着两个东西metrics/mAP50(B)和val/box_loss。正常情况下mAP50会随着epoch逐步上升并在某个区间趋于平稳box_loss则应该缓慢下降。如果mAP50一直趴在某个很低的值不动或者loss在几个epoch后突然变大那就说明训练有问题了——最常见的原因是设置了错误的学习率。训练结束后会在runs/detect/smoke_fire_exp/目录下生成一堆结果文件包括权重文件best.pt和last.pt、曲线图results.png、混淆矩阵confusion_matrix.png等。best.pt是验证集mAP最高的那一个权重部署时直接用这个就够了。3.4 训练后评估与推理验证评估模型在验证集上的表现YOLOv8提供了验证命令yolo detect val \ datadata.yaml \ modelruns/detect/smoke_fire_exp/weights/best.pt \ splitval运行完成后会打印每个类别的AP值和整体mAP。我这次跑出来的结果大致如下类别AP0.5说明smoke0.842烟雾框半透明边缘模糊有一定漏检率fire0.891明火特征明显检测效果最好firework0.853烟火场景复杂夜间样本影响精度mAP0.5约为0.862mAP0.5:0.95约为0.613。后者是更严苛的指标对框的定位精度要求更高这个数字说明还有优化空间。推理验证方面我拿几张测试集图片和网络上随便搜的火灾图片跑了预测yolo detect predict \ modelruns/detect/smoke_fire_exp/weights/best.pt \ source./test_images/ \ saveTrue保存的推理结果图片中每个目标会被画上矩形框框顶部的标签格式是类别 置信度比如fire 0.92。这时候要重点看的是模型在没见过的新场景下能不能识别出来。如果只在训练集相关的图片上效果好拿到新场景就崩了那就是过拟合或者数据集分布太单一了。这个数据集我观察到的一个小问题是纯室内的样本偏少所以模型在露天场景表现好在厂房内、仓库内这类封闭空间里烟雾识别的置信度会明显下降。后续如果要落地到室内监控场景建议补充采集一批室内烟雾样本再微调。4. 数据质量体检拿到数据集先别急着训练4.1 标注正确性与漏标检查很多人忽略这一步但我必须说数据质量检查应该排在训练之前甚至排在环境安装之前。最基础的方法是可视化抽查。用OpenCV把标注框画到原图上随机检查200到300张图。这一步能发现各种标注问题框偏移标注框和目标的实际位置明显对不上常见于大批量半自动标注后未人工校对的情况。漏标图片里明明有明火或烟雾但标注文件中没有对应目标。这类问题会导致训练时模型在该位置学到没有目标严重的话会压制召回率。错标类别烟雾标成明火或者烟火和明火混淆。因为这三类在某些场景下视觉上确实相似比如深色烟雾和远处烟花的灰烟很容易弄混。我自己写了一个脚本把随机抽样的图片和它们的YOLO标注框画出来直接保存到check文件夹然后一张一张翻。虽然费时间但效果立竿见影——我确实在这个数据集里发现了零星几十张漏标和错标把它们过滤掉或者手动修正后最终mAP大概提升了3个百分点。4.2 类别平衡与小目标问题三个类别的样本量需要统计一下尤其是查看每个类别在全部标注中出现的次数。如果某个类别的实例数远远少于其他类别训练时模型会对这个少数类严重欠拟合。以这个数据集为例三个类别的实例数大致在6000~9000之间数量相对均衡这是比较健康的分布不用做太多重采样处理。但小目标问题是真的需要处理的。火灾检测场景里烟雾和明火在很多样本中占比小比如监控画面中远处的一簇火苗可能只有30x30像素。你用imgsz640训练时原始的小目标经过下采样后可能连10个像素都不剩模型根本学不到特征。针对这个问题我的经验是通过硬件允许范围内适当提高imgsz同时开启马赛克增强加强模型对小目标的鲁棒性。YOLOv8里mosaic增强是默认开启的用mosaic0.5或0.8控制强度不要完全关掉这个增强对提高小目标检测能力帮助很大。4.3 数据增强的取舍与策略烟雾、明火这类目标的特殊性在于它们不是刚性物体形态飘忽不定半透明且边缘模糊。所以做数据增强时有些策略需要谨慎翻转增强水平翻转基本安全因为火焰和烟雾的形态左右翻转后依然符合物理直觉。但垂直翻转千万慎用——火焰向上燃烧是物理定律垂直翻转后的向下火焰在真实场景中几乎不存在这会让模型学到错误的空间语义。HSV色彩抖动对明火和烟火检测有正向帮助因为灯光条件变化时火焰颜色会偏移。但这个数据集里有些烟雾是灰白色调的把饱和度调得过低或过高都可能导致烟雾标注的噪声被放大所以幅度别太大。随机裁剪和缩放对小目标检测很重要但要确保裁剪后的区域里仍然保留完整的标注框不能裁出一个只有半截火焰的样本。YOLOv8训练时默认的增强参数是一套通用配置你可以通过hsv_h、hsv_s、degrees等参数调整。我实测过程中针对这个数据集把degrees设成0不做旋转或者最多5度因为烟火图像旋转超过一定角度会显得很不自然。另外translate0.1、scale0.5是相对安全的设置基本不会破坏目标语义。5. 常见问题速查我踩过的坑和排查思路5.1 类别标签映射错位这是双格式数据集最容易出现的坑。VOC格式的XML里类别是字符串smoke、fire、fireworkYOLO格式里的TXT存的是0、1、2。如果你在训练前没有检查classes.txt的顺序是否和标注索引对应极有可能出现模型看到的0其实是fire但配置里写的是smoke的错位。这种错位非常隐蔽因为训练不会报错loss也会正常下降但推理时模型预测的输出标签和实际目标完全对不上。排查方法训练结束后用验证集预测打印类别和置信度人工看几张预测图。或者直接把classes.txt和VOC的XML中对各类别的引用做一次统计对比如果YOLO的类别0在XML中对应的是fire而不是smoke说明索引错位了需要重排。5.2 训练时报错Unable to find dataset或no labels found这类报错的90%原因是路径配置错误。YOLO的data.yaml里写的是相对路径还是绝对路径如果写相对路径YOLOv8是基于当前工作目录解析的你必须在项目根目录运行训练命令。另一个常见问题是train: images/train和labels目录的组织关系——YOLOv8要求train字段指向图片目录标注文件必须在同级的labels目录下结构为YOLO/ ├── images/ │ └── train/ │ ├── frame_001.jpg │ └── ... └── labels/ └── train/ ├── frame_001.txt └── ...如果你把图片和标注放在同一个目录下比如YOLO/all/里既有jpg又有txtYOLOv8默认是找不到的必须按约定分割。还有一种情况是labels目录文件存在但内容是空的。有些数据在转换过程中出问题导致每个TXT只有一行空文件。这时候训练的loss会是NaN或者直接0模型什么都学不到。建议在训练前扫描所有TXT文件确保每一行至少有五个字段。5.3 mAP很高但实际工程场景漏报严重这是所有做检测的人都会遇到的一个虚拟指标陷阱。测试集上的mAP0.5高不代表模型在实际场景里就好用。原因通常有三个第一测试集和训练集分布太接近。如果随机切分同一个视频流里截取的帧可能在训练集和测试集里都有模型天然就会在测试集上得分虚高。解决办法是按视频或场景切分而不是完全随机。第二mAP对大目标更友好。小目标的框只要和真值重叠程度稍低IoU达不到0.5就被判为未检出。所以mAP高掩盖了小目标漏检严重的事实。第三烟雾这类目标的标签判定本身存在歧义。原始标注人员对多少浓度的烟才算值得标的标准不一致导致模型边界决策不稳定。解决思路是把置信度阈值调低一些比如从0.5降到0.25再配合NMS后处理优化这样能明显提高实际场景的召回率。5.4 解压失败或文件损坏下载的.7z文件如果在传输过程中损坏解压到一半会报CRC错误。Windows下7-Zip会弹出CRC Failed的报错框Linux下则会出现Sub items Errors。这种问题几乎全是下载不完整或存储介质问题导致的重新下载一般能解决。如果7-Zip提示文件头损坏但文件后缀是.7z注意查看是否真的下载完了——有些浏览器下载时会在未完成时临时命名为.7z.crdownload你需要确认文件名是完整的.7z结尾再解压。5.5 训练时内存或显存溢出6000张图左右的训练集不算大但如果你batch32开着一堆workers同时读图CPU内存也可能会爆。遇到OOM时不要急着把batch调到1先检查数据加载的worker数量。YOLOv8训练时可以直接加workers4参数限制避免同时读太多图导致物理内存占满。显存方面除了调batch还可以换更小的模型或者开启cacheFalse避免一次性把全部图片缓存到显存里。实操心得最后再分享一点个人体会这套数据集我用下来整体评价是开源数据集里比较省心的一个。但省心不代表可以直接躺平数据质量检查和目录重排这一步绝对不能省。我做烟火检测训练踩过最大的坑是一开始直接拿原始目录结构去训练结果训练了两百个epoch才发现图片和标签的配对关系在切分之后乱了导致模型精度一直上不去。后面老老实实按8:1:1重切了一遍重新训练mAP直接涨了十几个点。这个教训教会我一个道理花在数据整理和检查上的时间永远是最划算的投资。另外如果你是做视频监控方向的拿到数据后一定要自己抽帧补充一些真实摄像头视角的样本因为公开数据集里网络图片占了很大比例和摄像头俯拍视角的分布差异很大。训练完成后不要只看指标一定要跑一段真实的业务视频感受一下模型在连续帧上的稳定性和误报情况——那才是落地时真正要面对的战场。本文还有配套的精品资源点击获取