资讯动态

火灾烟雾人员检测数据集:从标注格式到训练部署的完整实战指南

发布时间:2026/10/1 23:38:11 来源:尧图企业网站定制
简介这份火灾烟雾人员检测数据集面向智能消防、安防监控与应急救援方向的算法开发者与研究人员用于训练和评估火灾场景下的多目标检测模型。数据全部来自真实监控与航拍环境覆盖室内外不同光照与天气条件标注为YOLO格式可直接接入YOLOv5、v7、v8等主流框架无需额外转换。压缩包共1800个文件以899张jpg图像和899个同名txt标注为主另含1个yaml数据配置与1份docx说明文档整体约36.53MB训练集744张、验证集110张、测试集45张总计899张真实场景图片。标注涵盖火灾、烟雾、人员三类关键目标边界框定位与类别标签经过严格校验可支撑火灾早期预警、公共场所危险源与人员同步检测、救援机器人视觉导航、森林防火监测及工业安全管控等任务。目前已有355人学习下载适合需要快速搭建火灾检测基线、验证多目标协同识别效果的读者直接使用。1. 火灾烟雾人员检测数据集从标注格式到训练落地的完整路径拿到「火灾烟雾人员检测数据集.zip」这个文件时多数人的第一反应是解压看图片然后直接丢进 YOLO 训练脚本。但真正跑过火灾场景检测的人都知道这个数据集和常规 COCO、VOC 完全不是一回事——烟雾的半透明边界、火焰与灯光的混淆、人员被遮挡后的残缺标注每一个都会让模型在验证集上看起来还行、一到真实监控画面就集体翻车。这个数据集的核心价值在于它把「火灾」「烟雾」「人员」三类目标放在同一帧里标注逼迫模型学会区分高温高亮区域和人体轮廓而不是简单地把所有橙色像素判为火焰。它适合做安防视觉的算法工程师、消防机器人感知模块的开发者以及想从通用检测迁移到应急场景的研究生。如果你手头正好有监控视频流需要做早期火情预警或者在做嵌入式端侧部署这个数据集能帮你省掉至少两周的标注成本。但前提是你得先搞清楚它的标注格式、类别分布和那些藏在压缩包里的坑。2. 拆开压缩包先别急着训练标注格式与类别分布核查2.1 火灾场景数据集的三种常见标注格式与转换逻辑火灾烟雾人员检测数据集通常不会只给一种格式。我见过的压缩包里最常见的是 Pascal VOC 的 XML 和 YOLO 的 TXT 混放偶尔还有 COCO 的 JSON。先别管模型用几行 Python 把目录结构和标注文件数量对齐这是避免「训练时发现一半图片没有标签」的第一道保险。import os import xml.etree.ElementTree as ET from collections import Counter dataset_root ./fire_smoke_person img_dir os.path.join(dataset_root, JPEGImages) xml_dir os.path.join(dataset_root, Annotations) img_files set(os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith((.jpg, .png))) xml_files set(os.path.splitext(f)[0] for f in os.listdir(xml_dir) if f.endswith(.xml)) # 找出有图无标签、有标签无图的样本 missing_xml img_files - xml_files missing_img xml_files - img_files print(f图片总数: {len(img_files)}, 标注总数: {len(xml_files)}) print(f缺失标注的图片: {len(missing_xml)} 张) print(f缺失图片的标注: {len(missing_img)} 个) # 统计类别分布 class_counter Counter() for xml_file in os.listdir(xml_dir): tree ET.parse(os.path.join(xml_dir, xml_file)) for obj in tree.findall(object): class_counter[obj.find(name).text] 1 print(类别分布:, class_counter)这段脚本先做集合差集把「有图无标签」和「有标签无图」的样本揪出来。火灾数据集里经常出现烟雾太淡导致标注员漏标的情况如果直接训练模型会把烟雾区域当成背景召回率直接掉一截。类别分布那几行是给你判断类别是否均衡用的——如果 person 有 8000 个框而 smoke 只有 1200 个后面就得考虑加权损失或者过采样。参数上dataset_root改成你解压后的实际路径JPEGImages和Annotations是 VOC 的标准命名如果你的压缩包里叫images和labels对应改掉就行。2.2 用可视化脚本检查烟雾与火焰的标注边界是否合理标注文件里的坐标是死的但烟雾的边界是活的。我习惯在训练前抽 20 张图把框画出来肉眼过一遍。下面这段代码用 OpenCV 把 VOC 格式的框叠在原图上重点看烟雾框是不是把整片天空都框进去了以及人员框有没有被火焰框完全覆盖。import cv2 import xml.etree.ElementTree as ET import os import random def visualize_annotation(img_path, xml_path, save_path): img cv2.imread(img_path) tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) x1 int(bbox.find(xmin).text) y1 int(bbox.find(ymin).text) x2 int(bbox.find(xmax).text) y2 int(bbox.find(ymax).text) color (0, 255, 0) if name person else (0, 165, 255) if name smoke else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, name, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(save_path, img) # 随机抽 20 张 all_xml [f for f in os.listdir(xml_dir) if f.endswith(.xml)] for xml_file in random.sample(all_xml, 20): base os.path.splitext(xml_file)[0] img_path os.path.join(img_dir, base .jpg) if os.path.exists(img_path): visualize_annotation(img_path, os.path.join(xml_dir, xml_file), f./vis/{base}_vis.jpg)颜色约定是 person 绿、smoke 橙、fire 红这样一眼能看出哪类框最乱。我遇到过 smoke 框把整栋楼都包进去的情况这种标注会让模型学出「大框烟雾」的捷径换到小烟雾场景直接失效。如果发现某类框普遍偏大或偏小要么回去修标注要么在训练时用 anchor 聚类重新适配。random.sample的 20 是抽样数量数据集小就全看数据集大就抽 50 张别省这点时间。2.3 把 VOC 转成 YOLO 格式坐标归一化与类别映射的四个边界坑YOLO 训练需要每张图对应一个 TXT每行是class_id x_center y_center width height全部归一化到 0~1。转换脚本网上一搜一大把但火灾数据集有几个特殊坑图片尺寸不统一、有些框超出图像边界、类别名大小写不一致、以及空标注文件要不要保留。import os import xml.etree.ElementTree as ET class_map {person: 0, smoke: 1, fire: 2} img_dir ./fire_smoke_person/JPEGImages xml_dir ./fire_smoke_person/Annotations out_dir ./fire_smoke_person/labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip().lower() if name not in class_map: continue bbox obj.find(bndbox) x1 max(0, int(bbox.find(xmin).text)) y1 max(0, int(bbox.find(ymin).text)) x2 min(img_w, int(bbox.find(xmax).text)) y2 min(img_h, int(bbox.find(ymax).text)) if x2 x1 or y2 y1: continue x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{class_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path os.path.join(out_dir, os.path.splitext(xml_file)[0] .txt) with open(out_path, w) as f: f.write(\n.join(lines))四个坑对应四行代码max(0, ...)和min(img_w, ...)处理越界框strip().lower()处理类别名大小写if x2 x1过滤无效框空标注文件照样写出空 TXT 让 YOLO 知道这张图是负样本。归一化用.6f保留六位小数YOLOv5/v8 默认读得进去。转换完记得抽查几个 TXT确认类别 ID 和你的data.yaml里names的顺序一致顺序错了模型会把烟雾当人员这种玄学问题查起来最费时间。3. 训练参数怎么设从 anchor 聚类到火灾场景的增强策略3.1 用 K-means 重新聚类 anchor火灾数据集的长宽比和 COCO 差多少YOLO 的默认 anchor 是在 COCO 上聚出来的而火灾烟雾人员检测数据集里的目标长宽比分布完全不同。烟雾往往是横向蔓延的大宽框火焰可能是细长的竖向框人员被遮挡后只剩半截。直接套默认 anchor召回率至少掉 5 个点。我一般用下面这个脚本在训练前跑一次 K-means。import numpy as np import os def load_boxes(label_dir): boxes [] for txt_file in os.listdir(label_dir): if not txt_file.endswith(.txt): continue with open(os.path.join(label_dir, txt_file)) as f: for line in f: parts line.strip().split() if len(parts) 5: w float(parts[3]) h float(parts[4]) if w 0 and h 0: boxes.append([w, h]) return np.array(boxes) def kmeans_anchors(boxes, k9, iterations100): np.random.seed(42) indices np.random.choice(len(boxes), k, replaceFalse) centroids boxes[indices] for _ in range(iterations): distances np.sqrt(((boxes[:, None] - centroids[None]) ** 2).sum(axis2)) labels distances.argmin(axis1) for i in range(k): if (labels i).any(): centroids[i] boxes[labels i].mean(axis0) return centroids boxes load_boxes(./fire_smoke_person/labels) anchors kmeans_anchors(boxes, k9) anchors anchors[np.argsort(anchors[:, 0] * anchors[:, 1])] print(聚类 anchor宽 高:) for a in anchors: print(f{a[0]:.4f} {a[1]:.4f})K-means 的距离用欧氏距离迭代 100 次足够收敛。k9对应 YOLOv5 的三层特征图各三个 anchor。跑完把输出按面积从小到大排填进模型的anchors配置里。如果聚类出来的 anchor 和默认值差得远比如最大 anchor 宽度超过 0.5说明数据集里有很多大烟雾框这时候输入分辨率也得跟着调640 可能不够得往上提到 960 或 1280。3.2 火灾场景的增强参数HSV 抖动、马赛克和混合的取舍火灾图像的色彩分布很集中——橙色、红色、黄色占主导。如果 HSV 抖动开太大火焰可能被调成绿色模型学出来的特征就乱了。我的经验是 H 通道抖动控制在 ±10 以内S 和 V 可以到 ±30。马赛克增强对烟雾有帮助因为烟雾经常出现在图像边缘马赛克拼接能模拟这种分布。但 MixUp 要慎用两张火灾图混在一起火焰和烟雾的边界会糊掉标注框对不上实际内容。# data.yaml 关键参数 train: ./fire_smoke_person/images/train val: ./fire_smoke_person/images/val nc: 3 names: [person, smoke, fire] # hyp.yaml 增强部分 hsv_h: 0.010 hsv_s: 0.300 hsv_v: 0.300 mosaic: 0.8 mixup: 0.0 degrees: 5.0 translate: 0.1 scale: 0.5 flipud: 0.0 fliplr: 0.5hsv_h设 0.010 就是 ±1% 的色相偏移对应 OpenCV 里大约 ±1.8 度足够模拟不同摄像头的白平衡差异又不会把火焰调变色。mosaic: 0.8表示 80% 的概率做四图拼接剩下 20% 用原图避免模型过度依赖拼接后的上下文。flipud: 0.0是因为火灾场景里火焰永远向上垂直翻转会造出物理上不可能的样本。scale: 0.5允许目标缩放范围在 0.5 到 1.5 倍之间对远近不同的烟雾都友好。3.3 类别不均衡时用 Focal Loss 还是过采样我的选择依据火灾数据集里 person 的框通常最多smoke 次之fire 最少。如果 fire 的框数不到 person 的十分之一训练时 fire 的梯度会被淹没。两种做法一是过采样含 fire 的图片二是换 Focal Loss。我一般先看 fire 的绝对数量——如果超过 2000 个框直接过采样到和 person 差不多就行简单粗暴有效。如果低于 1000 个框过采样会导致同一张图反复出现模型过拟合这时候用 Focal Loss 更稳。# 在 YOLO 的 loss.py 里把 BCEWithLogitsLoss 换成 Focal Loss 的核心逻辑 import torch import torch.nn as nn import torch.nn.functional as F class FocalLoss(nn.Module): def __init__(self, alpha0.25, gamma2.0): super().__init__() self.alpha alpha self.gamma gamma def forward(self, preds, targets): bce F.binary_cross_entropy_with_logits(preds, targets, reductionnone) pt torch.exp(-bce) focal self.alpha * (1 - pt) ** self.gamma * bce return focal.mean()alpha0.25是正样本权重gamma2.0控制难易样本的聚焦程度。火灾场景里烟雾的边界像素就是难样本Focal Loss 会让模型多关注这些区域。但注意Focal Loss 对标注噪声敏感如果烟雾标注本身就很糊用 Focal Loss 反而会放大噪声。所以我的顺序是先修标注再考虑损失函数。4. 避坑与排查火灾检测训练中那些让你白跑一天的坑4.1 损失不下降但 mAP 在涨标注噪声的典型表现现象是训练日志里 box_loss 和 cls_loss 震荡不降但验证集 mAP 缓慢上升。原因通常是标注框有系统性偏移比如所有 smoke 框都比实际烟雾大一圈。模型在学一个「模糊的正确」损失降不下去是因为预测框和标注框永远对不齐但分类置信度在提高。解决办法是抽 50 张验证集图片把预测框和标注框叠在一起看如果预测框普遍比标注框小且居中说明标注框偏大回去修标注或者用 CIOU 损失替代 GIOU。4.2 验证集 mAP 很高但实际视频流检测全乱域偏移的排查现象是验证集 mAP0.5 到了 0.85但拿一段真实监控视频跑烟雾漏检严重。原因是数据集里的图片可能是从公开火灾视频里截的分辨率和监控摄像头不同而且监控视频有压缩伪影。排查方法是把视频抽帧用训练好的模型跑一遍看置信度分布。如果大部分框置信度在 0.3 以下说明模型没适应。解决办法是在数据增强里加 JPEG 压缩模拟和运动模糊或者直接拿视频抽帧补标注。4.3 烟雾和云、蒸汽混淆负样本采集的缺失现象是模型把天空的云或者工厂的蒸汽判成烟雾。原因是训练集里没有足够的负样本。火灾数据集通常只标了火灾烟雾没标非火灾烟雾。解决办法是手动收集 200 到 500 张含云、蒸汽、雾气的负样本图片不放任何标注直接加入训练集。YOLO 对空标注图片的处理是只算背景损失能有效压低误报。4.4 训练到一半显存爆了多尺度训练与 batch size 的冲突现象是前 10 个 epoch 正常第 11 个 epoch 突然 OOM。原因是开了多尺度训练某次随机到的尺寸比之前大特征图占用显存翻倍。解决办法是把multi_scale的范围收窄比如从 320 到 640 改成 480 到 640或者把 batch size 从 16 降到 8用梯度累积补回来。别硬扛显存爆了重启训练更浪费时间。4.5 推理时框重叠严重NMS 阈值和置信度阈值的联合调参现象是一团烟雾被检出十几个框。原因是 NMS 的 IoU 阈值设太高比如 0.7导致重叠框没被抑制。火灾场景里烟雾本身边界模糊NMS 阈值建议降到 0.45 到 0.5。同时置信度阈值别设太低0.25 起步如果误报多就提到 0.4。这两个参数要一起调单独调一个容易按下葫芦浮起瓢。5. 从训练到部署用 TensorRT 加速和端侧验证的一个具体技巧训练完的模型如果要上 Jetson 或者瑞芯微的板子PyTorch 直接跑帧率通常只有个位数。我一般走 ONNX 到 TensorRT 的路线但火灾检测模型有个特殊点输入分辨率往往不是 640 而是 960 或 1280因为烟雾目标大。TensorRT 在构建 engine 时需要指定最大 batch 和最大输入尺寸如果设小了推理时动态尺寸会报错。# 导出 ONNX注意 opset 用 11 或 12YOLOv5/v8 都支持 python export.py --weights best.pt --include onnx --img 960 --opset 12 # TensorRT 构建 engine指定动态尺寸范围 trtexec --onnxbest.onnx --saveEnginebest.engine \ --minShapesimages:1x3x640x640 \ --optShapesimages:1x3x960x960 \ --maxShapesimages:1x3x1280x1280 \ --fp16--minShapes和--maxShapes把动态范围卡死--optShapes是实际推理最常用的尺寸。--fp16在 Jetson 上基本无损速度能翻倍。构建完用trtexec --loadEnginebest.engine --shapesimages:1x3x960x960跑一下 benchmark看延迟和吞吐。如果延迟超过 50ms考虑把输入降到 640或者用 INT8 量化——但 INT8 需要校准集火灾场景的校准集最好从验证集里抽 200 张别用训练集否则量化误差会偏。部署到端侧后验证方法不是看 mAP而是看连续视频流的误报间隔。我的习惯是拿一段 10 分钟的正常监控视频跑一遍统计误报次数。如果 10 分钟内误报超过 3 次说明置信度阈值还得往上提或者负样本不够。这个指标比 mAP 更贴近实际可用性。最后说个血泪教训别在训练完当天就部署隔一天再回头看验证集的可视化结果往往能发现前一天忽略的标注问题。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑