
简介基于YOLOv5的火焰图像与视频识别项目面向目标检测初学者和需要完成毕设、课程设计的开发者提供从数据准备到模型训练的完整工程参考。压缩包包含2000个文件其中1989个XML标注文件构成火焰检测数据集4个YAML文件用于模型与训练参数配置4个TXT文件划分训练、验证与测试集另有2个Markdown说明文档和1个Python脚本可快速理解项目结构并自动生成数据文件列表。资源包大小约446MB已有340人学习/浏览适合对照练习YOLOv5训练流程、数据标注格式及推理部署思路。通过研读标注数据、配置文件和脚本能掌握火焰目标检测项目从数据整理到模型评估的完整链路为后续自主开展图像识别项目打下基础。1. 火焰图像识别为什么绕不开 yolo-v5 这类检测器做安防和工业视觉的工程师大多遇到过同一个需求在监控视频里把火焰“框”出来。这个任务看着简单很多人第一反应是用 OpenCV 做颜色分割——从 RGB 转到 HSV把红色和黄色区域挑出来再算一下面积就报警。真正部署后才发现夕阳、车灯、红色工服、抛光火花都会触发误报反过来白炽灯下的淡黄色火焰、逆光下偏白的火焰又会让简单的颜色阈值直接把目标漏掉。火焰图像识别本质上是一个小目标、强变化、语义信息弱于颜色纹理信息的检测问题只有把深度模型和合理的后处理结合起来才能同时压住误报和漏检。yolo-v5 是这类需求里落地最顺的一条路径。它在单阶段检测器里属于工程化最完整的PyTorch 训练链成熟配置项几乎全部外置到 yaml 文件里能导 ONNX、TensorRT、NCNN方便把检测能力塞进现有视频监控软件。本文按“选型依据 → 数据和训练 → 视频识别与后处理 → 最后的工程技巧”的顺序把基于 yolo-v5 做火焰图像识别从数据准备到上线的关键环节讲透适合准备做实验验证的算法工程师也适合要把模型嵌到现有 C/Java 视频平台里的开发。2. 火焰目标特性、检测难点与 yolo-v5 的选型依据2.1 火焰图像识别难在火焰不是“一个固定形状的物体”通用目标检测里人、车、箱子在同类目标间形状高度一致模型主要学“边缘纹理”的判别性特征。火焰恰好相反它没有固定的轮廓火苗每帧都在形变火焰主体和背景之间往往是渐变过渡没有清晰的强边缘半透明的火苗外层还会让背景颜色透进来干扰特征提取。真正的难点集中在三类情况。第一类是小目标。森林防火摄像头覆盖范围大火焰在画面里常常只有十几个像素到几十个像素网络下采样到 1/32 时特征几乎消失。第二类是颜色漂移。白平衡、夜晚模式、逆光、隔着一层玻璃火焰颜色会从典型的橙红色偏移到白色甚至淡黄色。第三类是易混淆对象。电焊弧光、路灯、车辆大灯、烟头、蜡烛与反光板在单帧图像上和火焰高度相似单靠特征图很难彻底区分。这些特性决定了火焰检测不能只依赖“换个更好的 Backbone”或“堆数据”。它要求检测器本身对小目标友好多尺度输出、对模糊边界容错高anchor 和 IoU 损失不能让真值框抖得太厉害并且推理速度要够快才能在多路监控画面里实时扫帧。yolo-v5 刚好处在这个平衡点上。2.2 为什么选 yolo-v5 而不是传统 CV 或两阶段检测器传统做法是“HSV 颜色阈值 面积/圆形度判断”它的优势是 CPU 上极快但本质上是在单一颜色空间里做线性分类解决不了颜色漂移和易混淆光源。另一种常见方案是帧差法或背景建模MOG2、KNN它能捕捉运动区域可火焰与背景的相对运动并不规律火苗中心几乎不动帧差法会把火焰中心当作静止背景丢掉。两阶段检测器如 Faster R-CNN在火焰这类形变目标上精度不错但单帧推理在边缘设备上通常跑不到实时。yolo-v5 是单阶段、anchor-based、多尺度输出默认就有 P3/P4/P5 三个检测头分别对应小、中、大目标训练时 autoanchor 会按当前数据重新聚类先验框这对火焰这种“宽高比接近 1、尺度跨度大”的目标比固定 anchor 的 YOLOv3 更省心。PyTorch 生态让数据加载、增强、断点续训都直接可用导出部署时也能绕过 Darknet 编译的麻烦。从实测角度给一个粗略的对比方便做方案评审时直接写进文档方案单帧延迟Jetson Nano 实测量级夕阳/车灯误报小火焰召回工程改动量HSV 阈值 形态学3-5ms高低小背景建模 面积判定5-10ms高中中Faster R-CNN200ms 以上中中大yolo-v5s60-90ms低配合后处理中高调大输入尺寸后中需要说明的是上表不是精确 benchmark只是给选型一个方向感火焰识别项目最后都要配一个“闪烁过滤”或“持续时长确认”的后处理模块来压低误报这时候模型本身的 CPU 占用越低留给后处理的空间就越大yolo-v5s 的小权重版本在这类场景里优势明显。2.3 火焰图像识别需要什么样的训练数据集训练火焰检测器数据质量比数据量更关键。公开数据集可以收集 D-Fire、Flame Dataset 这类开源火焰/烟雾数据集但里面图像分辨率、场景偏向比较明显直接训练会导致在自家摄像头画面里泛化差。建议做法是以公开数据做预训练基础再用自己实际场景的视频抽帧补充数据覆盖“小火焰远距离”“夜间火焰”“日出日落时段”“隔玻璃拍摄”四类最难的场景。拿到原始视频后按 1-2 秒间隔抽帧删除连续重复帧再交给标注人员框火焰本体。标注框不要包到整片烟雾烟雾范围大且形状发散会把背景框进去拖坏训练样本。标注好之后先跑一个统计脚本看看目标尺度分布是否集中在某个极小范围内。下面这段代码可以快速统计每个标注框的像素宽度、高度以及在整图中的面积占比import os from pathlib import Path label_dir Path(datasets/fire/labels) width_list [] height_list [] area_ratio_list [] for label_file in label_dir.glob(*.txt): with open(label_file, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue _, cx, cy, w, h parts w float(w) h float(h) if w 0 or h 0: continue # 假设训练输入是 640x640这里按归一化坐标乘 640 得到像素尺度 pixel_width w * 640 pixel_height h * 640 width_list.append(pixel_width) height_list.append(pixel_height) area_ratio_list.append(w * h) print(样本数:, len(width_list)) print(目标平均尺寸: {:.1f} x {:.1f}.format(sum(width_list)/len(width_list), sum(height_list)/len(height_list))) print(面积占比中位数: {:.4f}.format(sorted(area_ratio_list)[len(area_ratio_list)//2]))这段代码的核心逻辑是按 YOLO 的归一化标注格式读取数据集把标注框映射到 640x640 的输入尺度上统计火焰目标的典型尺寸。如果中位面积占比小于 0.02说明大部分是小目标后文会提到一个针对性调整把训练输入分辨率提高到 960 甚至 1280同时把推理时的图像按长边缩放再 padding而不是直接拉伸。参数说明w和h是标注文件中归一化后的框宽高乘以 640 只是为了得到一个可读的像素参考值不代表训练时必须用 640 输入。提示统计脚本的价值不在于看平均数值而在于看面积占比的分布尾部。如果大量真实火焰在画面里只占 0.005 的面积那就要在数据里刻意保留不能因为难标就删掉否则模型上线后遇到的第一把真实小火就会漏检。3. 用 yolo-v5 训练火焰检测模型配置文件、增强与参数调整3.1 火焰数据标注格式检查与类别配置yolo-v5 数据集目录结构遵循常规组织方式下面是一个可以直接套用的布局。images 和 labels 下分别放 jpg/png 图像和同名 txt 标注文件train.txt 和 val.txt 里写图像绝对或相对路径datasets/fire/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── fire.yaml标注前先做好三类检查一是类别号必须从 0 开始连续编号火焰识别一般只有0: fire一类不要为了将来加烟雾先占 1 号位二是标注框不能超出图像边界虽然 yolo-v5 训练时会做 clipping但超边界的框在 Mosaic 增强拼接时可能被切到不对的位置三是同一张图里多个火焰实例要独立成行贴在一起的火焰如果只框一个大框会让模型学会把两个火源判断成单个目标。fire.yaml 是训练入口内容很直接# datasets/fire/fire.yaml train: datasets/fire/images/train val: datasets/fire/images/val nc: 1 names: [fire]其中的train和val指向图像目录yolo-v5 会自动到同级 labels 目录寻找标注。nc是类别数names里的类别顺序要和标注文件里的数字一一对应。如果数据集是纯视频抽帧建议每隔一段时间随机抽一部分帧加入 val而不是只用某个固定视频段做验证原因在后面章节会说。注意不要把nc设成比实际类别多。火焰识别里习惯性留一个background类是常见的坑检测器并不需要背景类别多余的类只会让最后分类层多出无效参数。3.2 火焰场景的数据增强参数怎么调yolo-v5 默认的增强配置在data/hyps/hyp.scratch-low.yaml里火焰图像识别通常需要改几个关键值而不是用默认参数直接跑。先看实际生效的增强项和推荐范围参数默认值火焰场景建议值说明hsv_h0.0150.01色调扰动太大会让橙红色变成紫色或绿色学偏色hsv_s0.70.7保持默认即可模拟不同饱和度下的火焰hsv_v0.40.4亮度扰动模拟过曝和低照度fliplr0.50.5水平翻转对火焰目标语义无影响安全scale0.50.8加大尺度扰动覆盖小目标情况mosaic1.01.0四图拼接增强利于模型适应局部遮挡火焰颜色核心落在橙红色区域hsv_h建议从默认 0.015 降一小档从源头减少色调偏移太大导致的错乱增强。scale提高到 0.8是为了模拟摄像头在不同距离下看到的火焰大小变化——火焰目标尺度分布本来就宽从蜡烛到森林大火跨越两个数量级尺度扰动不足会直接损害小目标召回。如果发现模型在验证集上对“小面积火焰”召回率低同时训练损失已经收敛常见做法是再加一个随机裁剪的增强让模型在更大比例的裁剪图中看到大目标和小目标共存的情况。yolo-v5 的 Mosaic 已经包含这种拼接逻辑所以优先调整scale比额外写增强代码性价比更高。3.3 训练命令行参数与小目标调优数据准备好了训练命令可以按下面这样组织然后在命令行里针对火焰任务改关键参数cd yolov5 python train.py \ --data datasets/fire/fire.yaml \ --weights yolov5s.pt \ --img 960 \ --batch 16 \ --epochs 150 \ --workers 8 \ --device 0 \ --hyp data/hyps/hyp.scratch-low.yaml参数含义拆开讲--weights yolov5s.pt用 COCO 预训练权重做迁移学习火焰特征虽然和 COCO 里的目标差异大但底层纹理、边缘、颜色分布迁移依然有效比从头训练收敛快得多--img 960是训练输入尺寸火焰识别场景里这是最值得付出的算力成本——把输入从默认 640 提高到 960等于让 P3 检测头在同样感受野下看到更细的火焰纹理--batch 16根据显存调整8GB 显存跑 960 输入时建议开到 8同时把--workers调到 4-8 保证数据加载不拖后腿--hyp指向自定义增强配置直接改官方 yaml 文件或者复制一份再改都行。训练结束时先看验证集上的 mAP0.5 和 mAP0.5:0.95火焰识别任务里 mAP0.5 更重要因为部署时 IoU 阈值一般设在 0.45 左右不需要追求特别精细的框贴合火苗边缘。再看 P 和 R 曲线火焰场景的置信度阈值通常要压到 0.2-0.3因为火焰目标经常被部分遮挡或半透明P 值稍低不要紧R 值不能低后处理会补偿一部分误报。如果模型在小火焰上表现差除了提高--img还有一个针对性操作关闭 yolo-v5 的 autoanchor 自动聚类改为在训练前手动统计火焰标注框的宽高比并写进模型配置。在 yaml 末尾加一段固定的 anchors让先验框更适合“宽高比接近 1、目标偏小”的火焰目标# models/yolov5s.yaml 中替换 anchors 配置 anchors: - [5, 5, 8, 9, 12, 12] # P3/8 小目标先验框 - [17, 18, 26, 28, 40, 38] # P4/16 中目标先验框 - [60, 62, 98, 96, 160, 154] # P5/32 大目标先验框这段配置把三个检测头的 anchor 都贴合火焰近似方形的形状。注意 anchor 的数值要和输入尺寸配套上面的值按 960 输入估算如果训练时改回 640这些数值需要乘以 640/960 做缩放。手动设置 anchors 后要加上--noautoanchor参数训练防止 autoanchor 再次根据统计结果覆盖写好的配置。提示看到“小目标 mAP 上不去”先别着急换更大的模型。把输入分辨率提上去、anchors 拟合到目标分布yolo-v5s 在火焰识别里通常已经够用直接换 yolo-v5x 会让帧率下降一半以上边缘设备根本扛不住。4. 火焰视频识别模型推理、误报过滤与部署加速4.1 用 yolo-v5 对单帧图像做火焰检测的最小实现训练完模型先用官方仓库的 detect.py 验证一批单帧图像是第一步。命令如下python detect.py \ --weights runs/train/exp/weights/best.pt \ --source ./test_images/ \ --conf-thres 0.25 \ --iou-thres 0.45 \ --img 960 \ --save-txt--conf-thres 0.25是火焰识别里推荐的起点火焰目标语义特征偏弱置信度普遍比行人、车辆低 0.1-0.2用 0.5 会把大量真实火焰过滤掉。--iou-thres 0.45是 NMS 阈值火焰经常有多个检测框叠在一个火苗周围调低到 0.4-0.45 可以防止相邻的高置信度框互相压制。--save-txt会把框坐标存成 txt方便后续写脚本统计验证集上的误报。如果要把检测集成到自己的视频处理脚本里更通用的做法是直接用 PyTorch Hub 加载权重import cv2 import torch # 加载训练好的模型最后两个参数是本地路径和强制重载 model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt, force_reloadTrue) model.conf 0.25 model.iou 0.45 model.max_det 50 # 读取一帧视频并推理 frame cv2.imread(test_frame.jpg) results model(frame, size960) # results.xyxy[0] 是 [x1, y1, x2, y2, confidence, class] 的张量 for *box, conf, cls in results.xyxy[0].cpu().numpy(): x1, y1, x2, y2 [int(v) for v in box] label ffire {conf:.2f} cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(output.jpg, frame)这段代码的关键点是model(frame, size960)frame是 BGR 格式的 numpy 数组yolo-v5 内部会自动做 BGR→RGB 转换和 letterbox 填充不需要在调用前手动 resize。返回结果results.xyxy[0]是 numpy 格式的检测结果每一行包含左上角 x1/y1、右下角 x2/y2、置信度和类别索引。注意force_reloadTrue只用于调试时加载最新权重正常部署建议去掉避免每次启动都重新读取权重文件。4.2 火焰视频识别里的跳帧与检测结果平滑视频流和单帧图像最大的区别是时间维度。火焰闪烁频率在 8-12Hz 左右而普通监控视频是 25 帧/秒连续两帧的火焰形态变化很小逐帧做检测等于浪费一半计算量。常见做法是每 2-3 帧做一次检测中间帧直接用上一帧的结果插值显示报警判定仍然基于检测帧序列。跳帧之外更值得做的是“检测框平滑”。火焰边缘不稳定连续帧检出的框会轻微抖动直接画在监控屏上会显得很飘。下面这段代码用一个简单的队列对检测框做平滑from collections import deque class FireBoxSmoother: def __init__(self, window_size5): self.window deque(maxlenwindow_size) def update(self, boxes): # boxes: 当前帧检出的 [x1, y1, x2, y2, score] 列表 if not boxes: if len(self.window) 0: self.window.clear() return None self.window.append(boxes) if len(self.window) 3: return boxes # 取连续帧中置信度最高的那个框作为展示框 best_box None best_score 0 for frame_boxes in self.window: for box in frame_boxes: if box[4] best_score: best_score box[4] best_box box return [best_box]这段平滑逻辑的实用性在于火焰检测常见的一种情况是某一帧突然给出一个高置信度的误报框但它只在单帧出现下一帧就消失把连续 5 帧里的最高置信度框作为当前展示框同时清空窗口的机制保证了火焰消失时检测框立即消失不会拖延报警撤销时间。window_size的设置和报警响应速度强相关取 5 表示需要 5 个检测帧配合跳帧就是 10-15 帧视频才完成一次完整平滑太大会让真实火情报警变慢。4.3 用闪烁特征过滤静态光源误报单靠 yolo-v5 的分类置信度无法完全区分火焰和车灯、路灯这类静态光源因为它们在单帧图像上的颜色和形状高度相似。一个不依赖额外模型的后处理手段是“闪烁特征”真实火焰的亮度随时间呈周期性波动而静态光源的亮度基本恒定。具体实现是在检测框内部计算每一帧的平均亮度保存最近 20-30 帧的序列然后看这个序列的方差或过零率。火焰的帧间亮度变化大方差高路灯、LED 屏幕的亮度方差接近零。代码如下import numpy as np class FlickerFilter: def __init__(self, history_size30, var_threshold50.0): self.history [] self.history_size history_size self.var_threshold var_threshold def is_fire(self, frame, box): x1, y1, x2, y2 [int(v) for v in box[:4]] roi frame[y1:y2, x1:x2] if roi.size 0: return False # 转灰度后取均值代表这个框区域的整体亮度 gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) brightness float(gray.mean()) self.history.append(brightness) if len(self.history) self.history_size: self.history.pop(0) if len(self.history) 10: return True variance np.var(self.history) return variance self.var_threshold这段后处理代码的使用方式是yolo-v5 每帧输出若干候选框先把框坐标丢给FlickerFilter只有is_fire返回 True 的框才触发报警。几个门槛值的含义是history_size30对应 25fps 视频下约 1.2 秒的历史亮度覆盖多个火焰闪烁周期var_threshold50.0是经验值具体要拿自己场景的静态光源和火焰样本各统计一轮才能定。如果实际部署环境里有频闪的路灯或大屏方差阈值需要调大同时可以改用亮度序列的傅里叶能量集中在 5-15Hz 这一判据比单纯方差更贴合火焰闪烁的频段特征。提示闪烁特征的局限在于夜间火焰检测——火焰被风吹动时检测框可能只覆盖火苗中心亮度方差反而小。折中方案是同时保留帧间位移特征火焰框中心在多帧间有小幅随机漂移静态光源纹丝不动。4.4 导出 ONNX 与视频推理加速yolo-v5 训练好的 PyTorch 模型在设备上直接能跑但生产环境里视频流处理通常是 C 或 Java 侧调用用 PyTorch 做推理需要维护一套 Python 进程而且 PyTorch 的 CPU 推理在 Jetson、海思这类嵌入式平台上效率不高。常见做法是先把模型导出成 ONNX再用 ONNX Runtime 做推理python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --img 960 \ --opset 12导出的 ONNX 文件可以直接用 ONNX Runtime 加载推理去掉 PyTorch 的图优化开销在 CPU 上通常有 20%-40% 的加速。如果要进一步压帧延迟可以把模型转成 TensorRT 的 engine 文件在支持 GPU 的 NVR 设备上单帧延迟能降到几毫秒级。ONNX 推理和 PyTorch 推理有个容易被忽略的差异letterbox 填充和归一化需要自己在推理代码里实现。yolo-v5 训练时用的是 640 或 960 整倍数输入推理时如果输入尺寸是 1066x600直接放到 ONNX 模型里会报形状不匹配。常见做法是写一个预处理函数把原始帧按长边缩放到目标尺寸再在短边补灰边推理完成后把检测框坐标按相同的缩放系数映射回原始图像。5. 火焰图像识别验证的一个实用技巧用连续帧投票替代单帧指标评估大多数火焰识别项目容易在验证环节吃亏。模型训练结束很多同学只拿验证集图片算 mAP看到 mAP0.5 到了 0.85 就觉得可以上线。但火焰监控场景真正的判断标准不是“单帧能不能检出一张火焰图”而是“在连续视频里能不能稳定检出并且别被持续 1 秒的静态光源带偏”。推荐一个可操作的验证方法把测试视频按 3 秒一段切成片段用训练好的模型跑完整段视频然后把每一帧的检测结果按检测框 IoU 关联成一条条“目标轨迹”。只有连续命中帧数超过设定阈值的轨迹才计为一次真阳性。对火焰识别阈值一般设为 5 帧配合 25fps 视频即火焰至少持续 0.2 秒才允许触发报警。下面的脚本可以辅助统计def evaluate_video_tracks(detections, iou_threshold0.3, min_hits5): # detections: 每帧 [frame_id, x1, y1, x2, y2, score] 的列表 tracks [] for det in detections: frame_id det[frame_id] matched False for track in tracks: last_det track[-1] if last_det[frame_id] frame_id - 3: continue iou compute_iou(last_det, det) if iou iou_threshold: track.append(det) matched True break if not matched: tracks.append([det]) confirmed [t for t in tracks if len(t) min_hits] return len(confirmed)这里的compute_iou就是常规的 IoU 计算两个框相交面积除以并集面积不必写在一段代码里。iou_threshold0.3故意设得比 NMS 阈值低因为火焰形态在连续帧里有偏移太高的 IoU 会割裂同一团火焰的轨迹。min_hits5的取值要和报警联动如果业务要求 1 秒内必须报警25fps 视频下至少 20 帧连续命中才报警同时验证时把min_hits调到 20报出的指标才是真正的业务指标。这个验证技巧想说明的核心问题火焰图像识别上线后暴露的问题七成以上是“验证方式和部署场景不一致”造成的。静态单帧评测只能看模型特征提取能力看不到闪烁过滤、跳帧、平滑框这些后处理是否和模型配合良好。把验证集改成短视频片段按轨迹维度统计再用“误报持续帧数”“真实火焰首报延迟”两个业务指标回写训练迭代才是火焰检测落地最值得投入的工程环节。本文还有配套的精品资源点击获取