简介:这份数据集面向目标检测训练场景,采用Pascal VOC与YOLO双格式组织,覆盖cig-pack(烟盒)与smoke(烟雾)两个类别,适合YOLO系列等主流检测模型的训练、验证与迁移调参;标注由labelImg按矩形框规则完成,类别框紧贴目标且未包含分割路径信息,格式规范可直接接入常用训练流程。
压缩包大小约802MB,共2000个文件,其中1999个为XML标注文件、1个为txt使用说明,便于快速查看目录结构与标注规则。整套数据包含22559张jpg图片及等量的VOC格式XML、YOLO格式TXT标注,总标注框28472个,其中cig-pack框数2907、smoke框数25565;xml与txt同名对应,训练时无需额外转换。目前已有420人学习下载,适用于抽烟行为识别、消防隐患监测、公共场所安全巡检等场景。资源不附带模型权重,但标注准确合理,可作为目标检测课程实训或毕业设计的数据底座,省去大量人工采集与标注成本。
1. 抽烟检测数据集:22559 张双格式标注,拿来就能训
这份资源不是训练好的模型,而是从现场监控画面整理的抽烟检测基础数据集。我拿到手第一反应是看它的标注标注是否干净——毕竟现在网上流出的数据集,真正能做到“下载即用、不用返工清洗”的不多。这套数据集的构成很直接:22559 张 JPEG 原图,每张图对应一个 VOC 格式 XML 和一个 YOLO 格式 TXT,标注类别只有两类——cig-pack(烟盒)和smoke(烟雾),总计 28472 个矩形框。适合的受众很明确:正在做加油站、化工厂、森林防火或工地安全帽场景下抽烟行为识别的开发者,以及需要给 YOLOv5/v8 做微调但缺干净训练数据的算法工程师。它不是权重包,不打包任何模型精度承诺,但它能帮你把数据管线从零到一快速跑通。
2. 双格式标注文件:先拆开看 VOC 和 YOLO 的字段构成
2.1 每个 XML 文件内的关键字段映射
VOC 格式的 XML 文件是这个数据集最原始的标注形态。我用 labelImg 打开过几个样本,结构是标准的 Pascal VOC 规约:<annotation>根节点下挂<folder>、<filename>、<source>、<size>和<object>列表。一个典型的 XML 节点长这样:
<annotation> <folder>fir_smoke</folder> <filename>fir_smoke_1148.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>smoke</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>624</xmin> <ymin>358</ymin> <xmax>892</xmax> <ymax>627</ymax> </bndbox> </object> </annotation>注意<folder>字段是fir_smoke,这是数据集作者整理时用的统一目录名。<filename>只存了图片名,没有完整路径,这意味着你后续做训练集划分时得自己拼路径前缀。<size>里的宽高要重点看——YOLO 格式做归一化时需要它,不同图片分辨率不一致时不能写死。<object>里每个字段的含义都常规,但difficult=0值得留意:这批数据没有故意标注为难样本,所有框都是一视同仁参与训练的。
2.2 YOLO 格式 TXT 的解析逻辑
每个 XML 对应的 TXT 文件是标签的另一种表达,也是 YOLO 训练直接读取的格式。文件每行代表一个目标框,格式为class_id x_center y_center width height,值域全部归一化到 0~1 之间。用一段 Python 读它最直观:
with open("fir_smoke_1148.txt", "r") as f: for line in f.readlines(): cls_id, x_c, y_c, w, h = map(float, line.strip().split()) print(f"类别ID: {int(cls_id)}, 中心点: ({x_c:.4f}, {y_c:.4f}), 宽: {w:.4f}, 高: {h:.4f}")上面代码里line.strip().split()把每行按空格拆成 5 个数值,前两个是类别和中心点坐标,后两个是归一化宽高。整个数据集里类别 ID 只有 0 和 1——分别对应cig-pack和smoke。我建议你拿到数据后不要直接开训,先用这段代码快速过一遍所有 TXT,确认没有空文件、没有越界的数值(比如宽高大于 1 或小于 0),这是第一个质量闸口。
2.3 两份格式方案的对应关系
你可以把 VOC XML 看作“人读版本”,YOLO TXT 看作“机读版本”。train 时如果用的是 YOLOv5/v8 官方仓库,默认读 TXT;如果要用 Detectron2 或 MMDetection,通常得转回 VOC 或 COCO 格式。两类标注从信息量上是等价的,只是坐标系不同——XML 里是像素绝对坐标,TXT 里是相对坐标。作者用 labelImg 标注后直接导出了两种格式,省去了你转换的工序,但因此也埋下了一个“一致性验证”的需求:你需要确认同一张图的 XML 和 TXT 是否指向同一组框。
3. 两类标注规则:烟盒与烟雾的判定标准和框选边界
3.1cig-pack类别的判定逻辑
cig-pack指的是香烟包装盒,包括硬盒和软包,而且不限于完整的一整条——数据里大量出现的是放在桌面、货架或地上的一包或者半包。标注规则是画矩形框把可见的烟盒完整框住,遮挡超过一半的烟盒则不标,多个烟盒紧挨在一起时分别框。这类别的总框数只有 2907,远少于smoke,说明数据集中烟盒的出现频率比烟雾低很多——这也符合实际场景逻辑:从抽烟行为看,烟雾是最显性特征,烟盒则是辅助特征。
3.2smoke类别的判定逻辑
smoke是数据集的主体,总共 25565 个框,也是抽烟检测任务里最核心的监督信号。标注规则是针对画面中可见的烟雾区域画矩形框,注意“区域”而非“人”——如果一个人正在抽烟,smoke框只框烟雾本身,不把人和手纳入框内。烟雾在画面中是半透明、无固定形状的,因此矩形框只能近似覆盖主要烟雾团,框的边缘有轻微裁切是正常的,不需要为了完美贴合而缩小或放大标注。数据集中绝大多数smoke框都集中在画面中上部,因为烟雾向上飘散,这和真实监控画面的物理规律一致。
3.3 数量比例与训练权重的关系
28472 个总框中,smoke占 25565、cig-pack占 2907,比例大约 8.8:1。这个比例不需要额外做类别平衡处理——直接按原生比例训 YOLO 模型即可,因为cig-pack作为辅助类别,本身出现的频率就低,强行过采样反而会造成对烟盒的假阳。但如果你确实想提升烟盒类别的召回,可以在 loss 里给cig-pack加权重,或者用 mosaic 增强时提高含有cig-pack图片的采样概率。我的经验是先把原生比例跑一版基线,再根据混淆矩阵决定要不要干预。
4. 数据校验与提取:解压合档后的三大常见陷阱
4.1 解压后文件数量核对方法
拿到压缩包后,先别急着配训练环境,第一步是把文件层次弄清楚。解压后你会看到一整个平铺目录,JPG、XML、TXT 三类文件混在一起,没有子目录分层。用下面这段 bash 统计数量最直接:
ls -1 *.jpg | wc -l ls -1 *.xml | wc -l ls -1 *.txt | wc -l正常的输出应该是三个 22559。如果发现 XML 或 TXT 数量和 JPG 不一致,说明有图片缺失标注或者存在多余文件。我碰到过有人把图片和标注分开存放的情况,训练时忘了指定标注路径,模型默默训练了 50 个 epoch 后才发现 loss 一直降不下去——这种血泪经验值得你提前避开。核对完数量后,再抽查几个文件名的 XML 和 TXT 是否一一对应:取中间编号比如fir_smoke_12911,分别打开它的.xml和.txt,人工比对第一个框的类别和粗略位置。
4.2 常见问题:路径分隔符与非法字符
Windows 下解压 7z 包时要注意文件名中的下划线——这批文件名全是fir_smoke_数字的格式,没有中文和空格,但如果作者后续更新过文件,可能混入特殊字符。Linux 环境用unar或p7zip解压时,如果遇到Cannot create symbolic link之类的报错,多半是压缩包内文件名包含 Windows 保留字符。另一个高频翻车点:filename字段和实际文件名大小写不一致,在 Linux 下jpg和.JPG是两回事。用下面这段脚本批量检查:
import os from pathlib import Path xml_files = list(Path(".").glob("*.xml")) missing_img = [f.stem for f in xml_files if not (f.parent / (f.stem + ".jpg")).exists()] print("缺失图片的标注文件数:", len(missing_img))这段代码遍历所有 XML 文件,检查对应 JPG 是否存在。如果你的 XML 里filename写的是全路径,上面逻辑会失效——先把filename字段切出来看看结构。神经网络训练时这个错误不会报错,只会让图片加载失败后静默跳过,损失函数曲线会时不时跳一下,极其隐蔽。
4.3 常见问题:XML 与 TXT 内容不一致
这是双格式数据集最容易出问题的点。作者导出两份标签时如果中间手动修改过其中一份,就会造成“紫鸭同笼”的不一致。验证脚本我一般写:
import xml.etree.ElementTree as ET def xml_to_yolo(xml_path, img_w, img_h): tree = ET.parse(xml_path) objs = [] for obj in tree.findall("object"): name = obj.find("name").text cls_id = {"cig-pack": 0, "smoke": 1}.get(name, -1) 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) x_c = ((x1 + x2) / 2) / img_w y_c = ((y1 + y2) / 2) / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h objs.append(f"{cls_id} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}") return objs xml_boxes = xml_to_yolo("fir_smoke_1148.xml", 1920, 1080) with open("fir_smoke_1148.txt", "r") as f: txt_lines = [line.strip() for line in f.readlines()] print("两边框数是否一致:", len(xml_boxes) == len(txt_lines))逐一比对class_id和坐标前后几位小数,允许微小浮点误差。如果发现不一致,以 XML 为准重新生成 TXT。这种校验在整个数据量上跑一遍耗时很长,我通常用多进程处理,multiprocessing.Pool分 8 个 worker 并行校验,大概几分钟能过完。
5. 用交叉验证法吃透数据:一个习惯让你少走弯路
拿到任何数据集,我都会建立一套“清洗-可视化-子集拆分”的标准流程,这套流程不仅适用于这个抽烟检测数据,也适用于所有 VOC+YOLO 格式的目标检测数据。清洗阶段先做上面第 4 章的脚本校验;可视化阶段把标注框画回原图,人工抽查 200 张,看框是否贴合目标、有没有错标漏标。子集拆分阶段最重要的一个技巧是按时序拆分而非随机拆分——如果这个数据集来自连续监控视频帧,随机拆分会导致训练集和验证集包含高度相似的相邻帧,评估结果虚高。
我常用的是固定比例 8:1:1 的划分,把文件列表按文件名排序后切片,而不是随机打乱,这样能最大程度避免同源帧泄漏。这个坑我踩过不止一次:某个模型在验证集上 mAP 高达 0.92,部署到真实摄像头直接掉到 0.3,最后排查发现是数据划分时同一段视频的连续帧同时进训练和验证,模型哪里是泛化,分明是背题。从那以后我每次接新数据集都强制走一遍“校验脚本 + 画框回看 + 非随机划分”的流程,犯懒只跑训练不管数据质量,后续调参都是在垃圾堆上盖楼,浪费的时间远比省下的多。
最后给一个进阶验证技巧:训练完成后用模型跑一遍全部 22559 张图的推理,把置信度低于 0.3 的预测框和 GT 做对比,统计哪些图片里模型完全没检出smoke。这些往往是烟雾极淡、背景极复杂的样本,也是你后续扩充数据时要重点补充的方向。希望帮到你。
本文还有配套的精品资源,点击获取