基于YOLOv5的火焰图像识别实战:从数据集标注到视频推理与误报抑制
发布时间:2026/9/16 2:48:50来源:尧图网络
简介这套项目以YOLOv5目标检测框架为核心围绕火焰图像及视频识别任务提供了从数据集准备到模型训练的整体方案。面向零基础学习者和进阶开发者覆盖毕业论文设计、课程作业、工程实训等常见场景能够帮助用户理解目标检测的数据组织方式与工程实践路径。资源包为ZIP压缩格式共2000个文件大小约446MB其中1989个XML标注文件是标注好的火焰位置数据4个YAML文件定义了模型与数据配置4个TXT文件划分了训练、验证和测试数据集2个MD文件讲解了项目说明与使用流程另有maketxt.py脚本用于自动生成数据列表便于直接开展YOLOv5模型训练。已有340人学习下载适合需要快速部署火焰识别项目、参考完整标注数据与工程结构的读者可用于课程设计、毕业设计或科研入门参考。1. 火焰图像识别为什么绕不开 YOLOv5防火监控最怕的不是火没拍到而是误报。早期做火焰图像识别的人先用颜色阈值分割把红橙亮区域圈出来配合 HSV 范围调参固定机位、固定天气下能用一旦镜头里出现晚霞、红色车灯、路灯、电焊火花误报率直接冲到无法商用。火焰不是刚体颜色分布跨红橙黄白边缘闪烁、中心泛白、外焰半透明任何固定规则都没法把这些特征写成边界清晰的判定式。YOLOv5 把火焰当作一个普通目标类别做监督学习靠标注数据学会「什么形状、什么纹理组合该报警」不依赖手写规则同时保持单阶段检测器的高帧率适合视频流场景。这篇顺着数据集、训练参数、视频推理、误报抑制四层往下写新手能按命令跑通老手看参数取舍和易错点。2. 火焰检测数据集标注协议与增强清单2.1 公开数据能预训练检测框必须自己清网上能搜到的 FireNet、FLAME、Fire-Detection 这类数据集大多是分类或分割标注告诉模型「这张图有火」但没有给出火焰的外接矩形。YOLOv5 训练需要的是「类别 bbox」的检测标注字段格式为class x_center y_center width height坐标全部归一化到 0~1。所以常见做法是用公开数据集做预训练或做预筛选再拿自己场景的截图重新标注一份检测数据。标注火焰和标注人、车不一样。火焰的外焰是半透明的要不要把整个扩散区域框进去我一般建议只框「肉眼能稳定判定的高亮火体」也就是内焰加可见的橙色区域把飘散的烟和透明热气排除在外。原因是烟雾训练时容易学成火焰部署后遇到烟囱、水蒸气、雾霾就会连环误报。另一个容易忽视的点是小目标。几十米外的初期火苗在 1080p 画面里可能只有 20x20 像素这种框也必须标不能因为看不清就跳过YOLOv5 的 anchor 对小目标本身就敏感训练样本里小目标比例过低模型会退化成只认大火焰。标注工具用 LabelImg 或 Label Studio 都行。LabelImg 保存的是 PASCAL VOC 的 XMLLabel Studio 可以导出 COCO 格式最后都要转成 YOLO 的 txt。标注量上单个场景至少准备 2000 到 3000 张有效图片其中正样本有火和负样本无火但颜色干扰多建议按 7:3 到 6:4 混合。负样本不是给模型「学无火」而是让模型在验证阶段看到类似火焰颜色的干扰物逼着训练过程把决策边界收紧。2.2 VOC 转 YOLO 格式与数据集划分脚本标注完的 XML 需要统一转成 YOLO 格式并按场景维度做训练集、验证集划分。场景维度划分的意思是同一段监控视频抽出来的连续帧要么全进训练集要么全进验证集不能随机打散否则验证集里会有大量与训练集仅差几帧的相似图mAP 虚高部署时打回原形。下面这段脚本完成 XML 解析、坐标归一化和按视频片段划分import os import random import xml.etree.ElementTree as ET CLASSES [fire] # 类别列表可按需扩展为 fire, smoke def voc_to_yolo(xml_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in CLASSES: continue cls_id CLASSES.index(cls_name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 归一化到 0~1并限定在图片范围内 x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines)) def split_by_scene(image_paths, val_ratio0.2, seed42): random.seed(seed) # 按目录名分组保证同一场景的帧只进一个集合 scenes {} for p in image_paths: scene os.path.dirname(p) scenes.setdefault(scene, []).append(p) val_scenes random.sample(list(scenes.keys()), max(1, int(len(scenes) * val_ratio))) val_imgs, train_imgs [], [] for scene, imgs in scenes.items(): if scene in val_scenes: val_imgs.extend(imgs) else: train_imgs.extend(imgs) return train_imgs, val_imgs这段脚本的核心是把xmin, ymin, xmax, ymax从像素坐标转成中心点加宽高的归一化坐标。注意x_center ((x1 x2) / 2) / img_w用了浮点除法配合cls_id一起写入 txt顺序是类别在前、坐标在后这和 YOLOv5 的标签读取顺序严格对应写反会导致训练时 loss 直接 Nan。划分函数按目录名作为场景 ID因为同一台摄像机的视频抽帧通常会放在同一个目录下这种做法比全量随机划分更贴近真实部署分布。2.3 火焰样本的增强取舍YOLOv5 自带 Mosaic、MixUp、HSV 扰动、随机翻转等增强但火焰有自己的特殊性。Mosaic 拼接四张图小目标容易被切掉一半或被边界截断初期训练可以开最后 50 个 epoch 建议把mosaic关掉让模型在干净样本上收敛。色彩抖动参数hsv_h、hsv_s、hsv_v直接影响烟雾和火焰的区分——火焰颜色是判别性最强的特征之一色相偏移调太大红橙会偏成紫红等于把训练集里最硬的特征破坏了。下表是我在火焰项目里常用的一组增强配置参数YOLOv5 默认火焰场景建议原因hsv_h0.0150.005降低色相偏移保住红橙特征hsv_s0.70.5适度变化饱和度模拟不同光照hsv_v0.40.4明度扰动保留模拟白天黑夜mosaic1.0前 250 epoch 开之后 0避免小火焰被拼接截断fliplr0.50.5水平翻转安全火焰左右对称mixup0.00.0火焰叠加会造出虚假的高亮区域最后一项需要多说一句MixUp 把两张图按比例混合火焰是半透明的混合后可能产生「半透明的火」真实场景里不存在这种目标模型会为了拟合它学到错误的边缘特征。除了内置增强我会额外加高斯模糊模拟低分辨率摄像头缩放区间 0.5 到 1.5 倍让模型对不同焦距的画面更鲁棒。3. YOLOv5 训练火焰模型的参数取舍3.1 模型尺寸先选 s别一上来就上 lYOLOv5 同一代版本里有n/s/m/l/x五个尺寸网络深度和宽度依次放大。火焰检测的部署端通常是 1080p 或 2K 监控视频流单路画面要求至少 15 到 25 FPS 才能跟上火焰蔓延速度部分场景还要在同一张 GPU 上跑多路解码。我从YOLOv5s起步原因有三个火焰目标语义相对简单不依赖大模型的强表征s 在 RTX 3060 级别显卡上训练一批 320 张图大约 4 到 6 小时迭代实验成本低在 Jetson 这类边缘设备上 s 的 TensorRT 版本能跑到实时l 就很勉强。各尺寸的取舍对照模型适用场景参数量级训练显存batch 16640px部署端 fps 预期YOLOv5s大多数安防场景平衡型小约 8~10 GB较高YOLOv5m视野远、小火焰密集中约 12~14 GB中等YOLOv5l追求极限 mAP忽略成本较大约 18~20 GB低火焰误报通常不是模型不够强而是训练数据里没有覆盖足够多的「类火干扰物」。所以先跑 s 看 mAP 和误报率如果单一 GPU 上推理速度有富余再往 m 升级。版本方面用 v6.0 或 v7.0 分支即可训练流程和命令保持一致更早的 v5.0 在train.py的参数名上有些出入没必要为省显存去迁就旧版本。3.2 train.py 训练命令与超参数说明训练命令我习惯写成 shell 脚本固定下来避免每次敲错参数python train.py \ --data fire.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 300 \ --patience 80 \ --cache ram \ --multi-scale \ --seed 42 \ --project runs/fire_train \ --name exp1fire.yaml里必填三项train和val指向数据集图片路径nc: 1表示只有 fire 一个类别names: [fire]必须和训练标签里的cls_id顺序一致。--multi-scale在每轮迭代随机缩放输入尺寸0.5 到 1.5 倍对火焰这类尺寸跨度大的目标有效但它会增加训练时间batch size 拉不满时可以关掉。--patience 80是早停配置验证集损失连续 80 个 epoch 不下降就结束配合--epochs 300防止无效长训练。--cache ram把数据集预加载进内存机械硬盘训练时能节省一半时间但需要足够的内存容量3000 张 640px 图片大约占 3 到 4 GB。训练过程里我只看两个收敛信号val/obj_loss稳定下降以及metrics/mAP_0.5不再上升。火焰目标的 AP 通常会偏高因为正样本特征集中训练后期 mAP 能到 95% 以上并不稀奇真正要看的是mAP_0.5:0.95它要求预测框和标注框的 IoU 逐档对齐小火焰的框稍微偏几个像素分数就掉这个指标更真实。3.3 火焰小目标的 anchor 与 anchor-free 讨论YOLOv5 是 anchor-based 模型data/hyps/hyp.scratch-low.yaml里的 anchor 不是训练时学习的而是通过聚类数据集所有标注框在yolov5s.yaml或模型加载阶段选取的先验值。公开数据集预训练出来的默认 anchor 对火焰未必合适。解决方式是训练前单独跑一次自适应 anchorpython train.py --data fire.yaml --weights --cfg yolov5s.yaml --epochs 1--epochs 1不是真的只训一个 epoch而是让 YOLOv5 在数据加载阶段重新计算 k-means anchor 并写入模型配置。跑完之后打开模型 yaml检查最小一组 anchor 的大小。如果数据里超过 30% 的火焰框小于 20x20 像素默认 anchor 里最小的一档往往是 10x13、16x30 这类形状需要手动调小到 4x5、8x9 附近。anchor 调小了召回会上升但误检也会跟着变多因为网络更容易把碎小的红色区域响应成目标需要靠置信度阈值和后处理压住。新版 YOLOv5 本身也支持 anchor-free 的 head 配置但改动模型结构牵扯到训练稳定性我不推荐在火焰项目里首次就上。先用 anchor-based 跑通基线把误报案例收集好再决定要不要换结构。4. 视频流火焰识别从 detect.py 到帧间去抖4.1 直接用 detect.py 跑视频的坑YOLOv5 自带推理脚本跑视频一条命令就够python detect.py \ --weights runs/fire_train/exp1/weights/best.pt \ --source fire_test.mp4 \ --conf-thres 0.30 \ --iou-thres 0.45 \ --max-det 10 \ --line-thickness 2 \ --save-txt --save-conf--conf-thres设 0.3 对火焰来说偏保守因为火焰的颜色和纹理特征强置信度通常很高设太低会把远处模糊的红色噪点判成火焰。--max-det 10限制单帧最多输出的目标数避免画面里大量类火干扰物同时出现时输出几十个框给后面报警逻辑添乱。--save-conf把每个框的置信度写进 txt这个文件后面做视频平滑时可以直接拿来用。直接跑完会发现两个问题。第一是 FPS 跟不上实际视频帧率detect.py 逐帧解码、逐帧推理没有跳帧逻辑1080p 视频在普通显卡上大约只能跑 10 到 20 FPS输出视频会慢半拍。第二是逐帧独立推理的置信度抖动很厉害火苗摆动时第 100 帧置信度 0.8、第 101 帧掉到 0.35、第 102 帧又回到 0.72如果报警阈值设在 0.5画面里的报警提示就会一闪一闪没法用。4.2 用 EMA 平滑置信度抑制帧间闪烁解决抖动不能靠降低置信度阈值那样会把误报也放进来。我一般会给每个目标维护一个指数移动平均EMA置信度并加一个「连续确认帧数」条件单帧达到高置信度先不报连续若干帧的平均置信度超过阈值才触发。import cv2 import torch import numpy as np model torch.hub.load(ultralytics/yolov5, custom, pathruns/fire_train/exp1/weights/best.pt, force_reloadFalse) model.conf 0.30 # 检测层置信度下限尽量放宽让目标不漏 model.iou 0.45 model.max_det 10 ALPHA 0.3 # EMA 平滑系数 FIRE_CONF 0.55 # 报警置信度阈值 CONFIRM_FRAMES 5 # 连续超过阈值才报警 cap cv2.VideoCapture(fire_test.mp4) fps cap.get(cv2.CAP_PROP_FPS) writer cv2.VideoWriter(fire_output.mp4, cv2.VideoWriter_fourcc(*mp4v), fps, (1280, 720)) ema_conf 0.0 confirm_count 0 while True: ok, frame cap.read() if not ok: break frame cv2.resize(frame, (1280, 720)) results model(frame) boxes results.xyxy[0].cpu().numpy() frame_conf max(boxes[:, 4]) if len(boxes) 0 else 0.0 # EMA 更新平滑单帧置信度的抖动 ema_conf ALPHA * frame_conf (1 - ALPHA) * ema_conf has_fire 0 if ema_conf FIRE_CONF: confirm_count 1 if confirm_count CONFIRM_FRAMES: has_fire 1 else: confirm_count 0 colors [(0, 0, 255) if has_fire else (0, 200, 255)] for det in boxes: x1, y1, x2, y2, conf, cls det cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), colors[0], 2) cv2.putText(frame, ffire{has_fire} ema{ema_conf:.2f}, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.8, colors[0], 2) writer.write(frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() writer.release()逻辑拆开看model.conf 0.30是网络输出的下限比报警阈值低目的是先保住召回把「疑似目标」都送进平滑器EMA更新的位置不在if分支里而是每一帧都执行这样连续无火帧会把ema_conf平滑拉低火苗短暂消失时报警不会立刻断。CONFIRM_FRAMES相当于消防里的防抖确认连续 5 帧都超过阈值才置has_fire1对应 200 毫秒左右的持续火焰。colors根据报警状态切换框的颜色画面里能直观区分「正在燃烧」和「疑似目标」。4.3 多目标场景下的匹配策略上面脚本只取了单帧最大置信度一个画面里同时出现两处火源时会丢掉次要目标。多目标平滑比单目标麻烦常见做法是按 IoU 做帧间目标关联当前帧的每个框分别与上一帧所有框计算 IoU超过 0.3 就认为是同一个目标各自维护独立的 EMA 和计数。具体实现时可以用scipy.spatial.distance.cdist建代价矩阵再用匈牙利算法做分配YOLOv5 仓库里也可以用deep_sort集成做跟踪。火焰没有像人那样的长期形变帧间关联简单不需要复杂的 ReID 特征。报警延迟按帧率折算25 FPS 的视频流里CONFIRM_FRAMES5意味着目标出现后约 200 毫秒才能确认加上模型推理的流水线延迟端到端大约 300 到 500 毫秒。这个量级对火焰完全可接受因为初期火焰从出现到蔓延到不可控通常有数秒到数分钟与其抢这 200 毫秒不如用这 200 毫秒换掉大量闪烁误报。5. 误报抑制与 ONNX 部署加速5.1 火焰特有的四类误报场景火焰误报的来源和行人检测完全不同整理成清单便于排查误报来源视觉特征与火焰的相似点常用抑制手段傍晚夕阳/晚霞大面积红橙渐变颜色分布高度相似增加面积上限过滤红色车灯/刹车灯高亮红色小区域颜色、亮度接近时序确认帧数运动检测电焊火花亮白点状飞溅高亮、闪烁、位置随机最小面积过滤连续性要求红色指示灯/LED稳定高亮红点颜色纯正位置规则静态目标排除后三条有一个共同点它们要么是稳定不动的要么是随机闪烁的而真实火焰有面积增长趋势、边缘高频抖动且中心亮度波动。把「检测」和「确认」拆成两步检测用 YOLOv5 输出候选框确认用帧间逻辑再过滤一次误报率能压下一个数量级。5.2 面积增长趋势作为第二道验证死死火焰的特征是持续燃烧时面积单调增长。我常在第一层 YOLOv5 之后加一个简单验证目标框宽度或面积在连续 N 帧里持续增长超过 15%才允许触发报警。代码实现并不复杂在每个目标上维护面积列表def check_growth(area_history, min_growth0.15): if len(area_history) CONFIMR_FRAMES: return False first area_history[-CONFIMR_FRAMES] last area_history[-1] return (last - first) / (first 1e-6) min_growth这段过滤逻辑放在 EMA 平滑之后area_history只记录同一目标关联上的面积。车灯面积恒定增长率为 0直接排除晚霞如果被检测框框住面积通常从大变小太阳落山也过不了。到此误报抑制不是靠训练时要模型多聪明而是靠时序逻辑把检测器不擅长的规则补上两部分各司其职。5.3 导出 ONNX 跑多路流验证完效果就可以进部署阶段。YOLOv5 导出 ONNX 的命令固定python export.py \ --weights runs/fire_train/exp1/weights/best.pt \ --include onnx \ --opset 12 \ --img 640 \ --dynamic--dynamic把 batch 维度设为动态方便后续多路视频流用同一个模型实例推理--opset 12兼顾新老推理引擎兼容性。导出后的 ONNX 可以直接上 ONNX Runtime不需要 Pytorch 环境GPU 机器上pip install onnxruntime-gpu即可。如果部署端是 Jetson 系列再用 TensorRT 的trtexec把 ONNX 转 engine显存占用和延迟都会比 ONNX Runtime 原生更好。部署环境比训练环境小很多这点对现场项目很关键。一个只装了 ONNX Runtime、OpenCV 和 Python 的工控机就能跑完整个视频流识别链路不需要装 CUDA Toolkit、PyTorch 全家桶。推理代码里session ort.InferenceSession(fire.onnx, providers[CUDAExecutionProvider])输入名和输出名从导出日志里读取后处理和调试逻辑完全复用前面写好的 EMA 平滑、面积增长判断。最后留一个习惯现场部署后的负样本图要按月归档。夏季傍晚的霞光、冬季车灯反光、雨天地面积水的红色反光都是季节性的单次采集覆盖不全。每收集一批误报图就补进训练数据的负样本集重训一轮比增大置信度阈值更治本因为阈值调高会让真正的初期小火苗一起丢掉。本文还有配套的精品资源点击获取
网站建设高端定制企业官网