尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

灭火器识别数据集:从标注到YOLOv8部署的完整链路

灭火器识别数据集:从标注到YOLOv8部署的完整链路 简介这份灭火器识别数据集面向从事目标检测的深度学习开发者与算法学习者可用于消防场景下的灭火器自动检测任务帮助快速搭建并验证YOLO系列、Faster R-CNN、SSD等检测模型。资源包共约2000个文件以txt标签文件为主另含1个yaml类别配置文件压缩包大小约311MB图片与txt标签已按训练集、验证集、测试集划分完毕并同时提供VOC格式的xml标签方便不同框架直接读取。数据集类别为extinguisher图片数量3262张覆盖多种真实场景可直接投入YOLOv5至YOLOv10等系列算法的训练与调优。目前已有1427人学习下载适合需要现成标注数据做模型训练、对比实验或课程设计的中高级开发者省去自行采集与标注的成本把精力集中在网络结构改进与精度提升上。1. 灭火器识别数据集从标注到部署一条能跑通的链路消防通道被杂物堵死、灭火器箱前堆满纸箱、压力表指针已经掉进红区——这些画面在监控里天天出现但靠人盯着几十路视频去数灭火器基本等于没数。灭火器识别数据集要解决的就是这件事让目标检测模型自动框出画面里的灭火器顺带判断它是不是被遮挡、是不是还在原位。这个方向适合两类人一类是做智慧消防、园区安防的算法工程师需要一套能直接训练、能落地到边缘盒子的数据另一类是刚接触目标检测的开发者想找一个类别单一、标注清晰、场景真实的练手数据集把 YOLO 系列从训练到推理的完整流程走一遍。灭火器这个目标有个特点——颜色鲜艳但形态固定正样本好标难的是小目标、遮挡和反光这三类样本决定了模型上线后会不会频繁误报。2. 灭火器数据集长什么样类别设计、标注格式与采集边界2.1 类别怎么定只框灭火器还是把箱体和压力表一起标我见过不少团队一开始只标“灭火器”一个类训出来的模型在真实画面里会把红色消防栓、红色垃圾桶甚至红色广告牌一起框进去。后来我们把类别拆成三个extinguisher瓶体本身、extinguisher_box灭火器箱或支架、extinguisher_sign指示标志。拆开之后模型能区分“灭火器在不在箱子里”和“标志还在但灭火器没了”这两种完全不同的告警场景。如果你的场景只需要知道“画面里有没有灭火器”单类就够了标注成本能省一半以上。但如果你要做“灭火器缺失检测”或者“通道占用检测”建议至少保留extinguisher和extinguisher_box两类因为箱体位置固定瓶体位置会变两者一对比就能判断是否被挪用。类别命名不要用中文也不要带空格统一小写加下划线。标注文件里出现的类别名必须和训练时的data.yaml完全一致大小写差一个字母都会导致训练时类别丢失。2.2 标注格式选 VOC 还是 YOLO转换脚本与四个边界坑灭火器数据集的原始标注常见两种来源一种是外包团队用 LabelImg 出的 Pascal VOC XML另一种是直接拿 Labelme 转的 JSON。不管来源是什么最终喂给 YOLO 训练的一定是归一化的class_id x_center y_center width height格式。下面这个脚本是我常用的 VOC 转 YOLO 版本处理过几千张灭火器图片边界情况基本覆盖了。import xml.etree.ElementTree as ET import os from pathlib import Path # 类别映射必须和 data.yaml 里的 names 顺序一致 CLASS_MAP {extinguisher: 0, extinguisher_box: 1, extinguisher_sign: 2} def voc_to_yolo(xml_path, img_w, img_h, out_txt_path): tree ET.parse(xml_path) root tree.getroot() 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] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 边界裁剪标注框超出图像范围时直接截断否则归一化后会出现负值 xmin max(0, min(xmin, img_w)) ymin max(0, min(ymin, img_h)) xmax max(0, min(xmax, img_w)) ymax max(0, min(ymax, img_h)) # 过滤掉宽高为 0 的无效框 if xmax - xmin 1 or ymax - ymin 1: continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: Path(out_txt_path).write_text(\n.join(lines), encodingutf-8) return len(lines) # 批量处理图片和 XML 同名同目录 img_dir fire_extinguisher/images xml_dir fire_extinguisher/annotations label_dir fire_extinguisher/labels os.makedirs(label_dir, exist_okTrue) for xml_file in Path(xml_dir).glob(*.xml): img_file Path(img_dir) / (xml_file.stem .jpg) if not img_file.exists(): continue # 这里用 PIL 读尺寸避免 OpenCV 在某些中文路径下读图失败 from PIL import Image with Image.open(img_file) as im: w, h im.size voc_to_yolo(str(xml_file), w, h, str(Path(label_dir) / (xml_file.stem .txt)))这段代码里最容易被忽略的是边界裁剪和无效框过滤。灭火器数据集里经常出现标注员把框拉到图像外面或者框选了一个几乎看不见的反光点宽高算出来是 0。这两种情况不处理训练时 loss 会直接变成 NaN而且报错信息不会告诉你哪张图有问题只能一张张翻属于典型的血泪经验。参数上CLASS_MAP的顺序就是最终模型输出的类别索引改顺序必须同步改data.yaml。归一化保留 6 位小数足够YOLO 内部会再做一次缩放。如果图片是 PNG 带透明通道PIL 读出来的尺寸没问题但训练时建议统一转成 JPG避免通道数不一致导致 dataloader 报错。2.3 采集边界多少张图、哪些场景必须覆盖灭火器数据集不是越多越好而是越“杂”越好。我一般按场景分层采集室内走廊、地下车库、配电房、厨房、仓库各占一定比例。每个场景里再分正常光照、逆光、夜间红外、烟雾遮挡四种条件。一个能上线的灭火器识别数据集至少需要 2000 张标注图其中小目标灭火器在画面中占比小于 5%不少于 300 张遮挡样本不少于 200 张。如果只做 demo500 张也能跑出 0.8 以上的 mAP但一到真实摄像头就会翻车因为 demo 数据集里灭火器都是正对镜头、光线充足、没有遮挡。真实场景里灭火器经常被消防水带挡住一半或者挂在墙角只露出一个红色瓶底。采集时故意保留这些“不完美”样本模型上线后的误报率会低很多。3. 用 YOLOv8 训练灭火器识别模型从 data.yaml 到第一轮推理3.1 环境与目录把数据集摆成 YOLO 认识的形状YOLOv8 对目录结构有固定要求摆错了不会报错但训练时一张图都读不到。标准结构如下fire_extinguisher/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片可选 ├── labels/ │ ├── train/ # 训练集标注 txt │ ├── val/ │ └── test/ └── data.yamldata.yaml内容path: /data/fire_extinguisher train: images/train val: images/val test: images/test names: 0: extinguisher 1: extinguisher_box 2: extinguisher_signpath写绝对路径最稳相对路径在不同工作目录下启动训练时容易找不到。names的索引必须和转换脚本里的CLASS_MAP完全对应顺序错了模型会把灭火器箱认成灭火器而且 loss 曲线看起来还挺正常属于玄学翻车。安装环境用 pip 即可pip install ultralytics yolo checksyolo checks会打印环境信息重点看 CUDA 是否可用。如果显示 CPU训练 2000 张图大概要几个小时建议至少有一张 8G 显存的卡。3.2 训练命令与必调参数imgsz、batch、lr0 怎么定启动训练的命令本身很简单yolo detect train \ data/data/fire_extinguisher/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/fire_extinguisher \ nameexp1参数逐个说。model选yolov8n.pt还是yolov8s.pt取决于部署硬件边缘盒子用 n 版服务器用 s 版。灭火器类别少、形态固定n 版在 2000 张图上通常能到 0.85 mAP 以上没必要上大模型。imgsz640是默认值但如果你的摄像头画面里灭火器特别小比如 1080P 画面里只占 40 像素宽那 640 下采样后只剩 20 多像素模型很难学到特征。这种情况把imgsz提到 960 或 1280代价是显存和推理时间翻倍。我一般先用 640 跑一轮看验证集里小目标的召回率低于 0.6 再提分辨率。batch16是 8G 显存下的安全值显存不够就降到 8但 batch 太小会让 BN 层统计不稳定mAP 波动变大。lr00.01是 SGD 的初始学习率如果 loss 在前 10 个 epoch 就炸到 NaN降到 0.001 再试。patience20表示验证集 mAP 连续 20 轮不提升就早停灭火器数据集通常 60 到 80 轮就收敛了设 100 轮是留余量。训练过程中重点看三个指标box_loss是否稳定下降、mAP50是否持续上升、precision和recall是否差距过大。如果 recall 远低于 precision说明模型漏检多优先补小目标和遮挡样本如果 precision 低说明误报多检查标注里有没有把红色非灭火器物体标进去。3.3 推理与验证用测试集图片看真实效果训练完成后用yolo detect predict跑单张或整个目录yolo detect predict \ modelruns/fire_extinguisher/exp1/weights/best.pt \ source/data/fire_extinguisher/images/test \ conf0.25 \ iou0.45 \ saveTrueconf0.25是置信度阈值低于这个值的框不输出。灭火器识别里这个值可以适当调低到 0.2因为漏检一个灭火器的代价比多框一个红色物体高。iou0.45是 NMS 的 IoU 阈值如果画面里灭火器密集排列比如消防柜里并排三个这个值要提到 0.5 以上否则相邻的框会被合并成一个。推理结果会保存在runs/detect/predict下重点看三类图小目标灭火器有没有框到、被遮挡一半的有没有框到、红色非灭火器物体有没有被误框。这三类图各挑 20 张人工数一遍比看 mAP 数字更直观。4. 灭火器识别落地避坑标注、训练、部署里的五个真实翻车点4.1 现象训练 loss 正常但 mAP 一直是 0原因通常是data.yaml里的names索引和标注文件里的class_id对不上。比如标注里灭火器是 0但names里 0 写成了extinguisher_box模型学到的就是错位的类别。另一种可能是train和val路径下没有图片YOLO 会静默跳过loss 显示为 0 但不报错。解决训练前用脚本统计一遍标注文件里出现的所有class_id和names逐一对齐。同时确认images/train和labels/train的文件名一一对应缺一个都会导致该图被跳过。4.2 现象模型在验证集上 mAP 很高一到真实摄像头就疯狂误报原因是验证集和真实场景的数据分布不一致。验证集里灭火器都是正对、清晰、光线好真实摄像头里灭火器可能只露出一个角或者画面里有红色消防水带、红色安全帽。模型学到的是“红色圆柱形灭火器”而不是灭火器的本质特征。解决从真实摄像头里截取 200 张误报图人工标出其中的灭火器如果有加入训练集重新微调。同时把误报的红色物体作为负样本在标注时不框任何东西让模型学会区分。4.3 现象小目标灭火器召回率极低mAP50 只有 0.4原因是imgsz640下采样后小目标特征丢失。1080P 画面里 40 像素宽的灭火器缩到 640 后只剩 23 像素再经过 backbone 的 32 倍下采样特征图上的响应几乎消失。解决把imgsz提到 960 或 1280同时开启mosaic增强YOLOv8 默认开启让模型在拼接图中见到更多小目标组合。如果显存不够用rectTrue做矩形推理减少 padding 浪费。4.4 现象推理时同一张图里灭火器被框了两次NMS 没起作用原因是iou阈值设得太高或者灭火器标注框本身重叠严重。如果训练数据里同一个灭火器被标了两个框模型会学到重复输出。解决先检查标注文件同一个目标只保留一个框。推理时把iou从默认 0.7 降到 0.45 到 0.5 之间具体值用验证集试看 mAP 和框数量的平衡点。4.5 现象模型部署到边缘盒子后推理速度只有 2 FPS原因是模型输入分辨率太高或者用了yolov8s以上的模型。边缘盒子的算力通常只有几 TOPS跑 1280 输入的 s 版模型很吃力。解决导出 ONNX 或 TensorRT 时固定输入尺寸用yolov8n加imgsz640在盒子上的推理速度能到 15 FPS 以上。如果精度不够用知识蒸馏把 s 版的能力迁移到 n 版而不是直接上大模型。5. 把灭火器识别做稳的进阶习惯从单帧检测到时序确认单帧检测永远会有误报这是目标检测的固有局限。我在实际项目里会把灭火器识别和时序逻辑绑在一起连续 5 帧里至少 3 帧检测到同一个位置的灭火器才触发一次有效告警。这个逻辑用简单的跟踪算法就能实现不需要上复杂的 ReID。# 简易时序确认基于 IoU 匹配的帧间计数 from collections import defaultdict class TemporalConfirmer: def __init__(self, window5, threshold3, iou_thr0.5): self.window window # 滑动窗口帧数 self.threshold threshold # 触发告警所需命中次数 self.iou_thr iou_thr # 帧间匹配的 IoU 阈值 self.history defaultdict(list) # track_id - 命中记录 def update(self, detections, frame_id): # detections: [(x1, y1, x2, y2, cls_id, conf), ...] # 简化处理按类别和位置粗略匹配实际项目可换成 ByteTrack for det in detections: matched False for tid, records in self.history.items(): last_box records[-1][1] if self._iou(det[:4], last_box) self.iou_thr: records.append((frame_id, det[:4])) matched True break if not matched: new_id len(self.history) self.history[new_id].append((frame_id, det[:4])) # 清理超出窗口的记录 for tid in list(self.history.keys()): self.history[tid] [ r for r in self.history[tid] if frame_id - r[0] self.window ] if not self.history[tid]: del self.history[tid] # 返回确认告警的 track_id confirmed [] for tid, records in self.history.items(): if len(records) self.threshold: confirmed.append(tid) return confirmed staticmethod def _iou(box_a, box_b): xa max(box_a[0], box_b[0]) ya max(box_a[1], box_b[1]) xb min(box_a[2], box_b[2]) yb min(box_a[3], box_b[3]) inter max(0, xb - xa) * max(0, yb - ya) area_a (box_a[2] - box_a[0]) * (box_a[3] - box_a[1]) area_b (box_b[2] - box_b[0]) * (box_b[3] - box_b[1]) union area_a area_b - inter return inter / union if union 0 else 0这段代码的核心思想是单帧检测结果只作为候选只有连续多帧稳定出现的检测框才被确认为真实目标。window5表示看最近 5 帧threshold3表示至少命中 3 帧。这两个参数根据摄像头帧率调整25 FPS 下 5 帧只有 0.2 秒对灭火器这种静态目标足够如果摄像头有抖动把window提到 10threshold提到 6。iou_thr0.5是帧间匹配的宽松阈值因为摄像头轻微晃动会导致同一灭火器的框位置偏移IoU 可能降到 0.6 左右。如果场景里灭火器密集这个值要提到 0.6 以上避免相邻灭火器被误匹配成同一个。这套逻辑上线后误报率通常能从单帧的每天几十次降到每天两三次。代价是告警延迟增加了 0.2 秒对消防场景来说完全可以接受。我自己的习惯是每换一个摄像头点位先跑 24 小时纯推理不告警把检测结果全部存下来人工看一遍误报和漏报再决定conf和时序参数怎么调。这个“后悔药”步骤花不了一天但能省掉上线后被业务方追着改 bug 的两周。希望帮到你。本文还有配套的精品资源点击获取
返回列表