公共场所危险物品检测数据集:1431张VOC+YOLO双格式训练指南

发布时间:2026/10/4 14:29:40
公共场所危险物品检测数据集:1431张VOC+YOLO双格式训练指南 简介本数据集面向安防监控、公共场景智能检测方向的算法工程师与深度学习学习者提供可直接用于目标检测训练与验证的标注数据覆盖刀具、手枪、纸币、钱包、智能手机、卡片等六类公共场所常见危险或敏感物品。资源共2000个文件以1431个Pascal VOC格式xml标注文件和569个YOLO格式txt标注文件为主另含1431张jpg原图压缩包约126.77MB两种标注格式可分别对接不同检测框架省去格式转换环节。全部标注由labelImg完成采用矩形框方式共1497个标注框其中knife类别框数最多达1035个其余类别分布相对稀疏便于开展类别不平衡场景下的实验对比。目前已有330人学习下载。数据集仅提供准确合理的标注不对训练所得模型或权重精度作任何保证适合用于课程设计、算法验证与数据增强等实践环节。1. 公共场所危险物品检测数据集1431 张 VOCYOLO 双格式到底能训出什么安检口传送带上的包最怕的不是刀是那种混在充电宝和钥匙串里、只露出一个角的东西。公共场所危险物品检测这个方向难就难在目标小、遮挡重、类别间长得像而公开可用的高质量数据又少。这份 1431 张、6 类别的 VOCYOLO 双格式数据集解决的就是“从零起步没有标注数据”的问题——它把 XML 和 TXT 两套标注都给你备好了省掉格式转换那一步。适合谁适合想快速验证 YOLO 训练链路的学生、需要给安检/园区场景做原型验证的工程师以及拿它当 baseline 再扩自己数据的人。1431 张不算大但作为冷启动的第一批燃料够用。2. 先搞懂 VOC 与 YOLO 双格式为什么同一批图要存两套标注2.1 VOC 的 XML 和 YOLO 的 TXT 到底差在哪VOC 格式的标注是一个图对应一个 XML 文件里面用object节点记录每个目标的类别名和边界框的左上角、右下角绝对像素坐标。YOLO 格式则是一个图对应一个 TXT每行一个目标格式是类别索引 中心x 中心y 宽 高后四个值全部除以图片宽高归一化到 0~1。两者最本质的区别VOC 存的是绝对坐标加类别字符串YOLO 存的是归一化坐标加类别整数索引。这个差异决定了你不能直接把 XML 丢给 YOLO 训练脚本必须先转。为什么这份数据集要同时给两套因为工具链是分裂的。LabelImg、部分可视化脚本、某些传统检测框架吃 XML而 Ultralytics 系的 YOLOv5/v8/v11 训练只认 TXT 加一个data.yaml。双格式意味着你拿到手就能分别喂给两套流程不用自己写转换再担心坐标算错。常见做法是用 VOC 做标注复核和可视化用 YOLO 格式直接开训。2.2 目录结构怎么摆才不出错解压后你大概率会看到AnnotationsXML、JPEGImages原图、ImageSets/Main划分文件这一套 VOC 骨架外加一个labels目录放 TXT。别急着改目录名先按下面这个结构核对一遍缺哪个补哪个dataset/ ├── Annotations/ # VOC 的 XML一图一文件 ├── JPEGImages/ # 所有原图jpg/png 混放也行 ├── labels/ # YOLO 的 TXT文件名必须与图同名 ├── ImageSets/Main/ # train.txt / val.txt只写文件名不带后缀 └── data.yaml # YOLO 训练配置关键点labels里的 TXT 文件名必须和JPEGImages里的图完全同名只是后缀不同。ImageSets/Main/train.txt里每行只写xxx不带.jpg这是 VOC 的老规矩很多转换脚本默认按这个读。如果你发现 TXT 和图片对不上号九成是文件名大小写或者后缀不一致先ls两个目录对比一遍再往下走。2.3 6 个类别与类别索引的映射关系6 类别意味着data.yaml里nc: 6names列表顺序必须和 TXT 里用的整数索引严格对应。索引错一位模型学出来的就是“把 A 认成 B”而且 loss 还会正常下降属于最阴的坑。拿到数据集第一件事不是开训是确认索引映射。常见做法是写个脚本统计所有 TXT 里出现过的类别索引看最大值是不是 5、有没有跳号import os, glob from collections import Counter label_dir dataset/labels counter Counter() for txt in glob.glob(os.path.join(label_dir, *.txt)): with open(txt) as f: for line in f: line line.strip() if line: cls_id int(line.split()[0]) # 每行第一个字段是类别索引 counter[cls_id] 1 print(出现的类别索引及数量:, dict(sorted(counter.items()))) # 期望看到 0~5 全部出现且没有 6 及以上的值这段脚本遍历所有 TXT把每行第一个字段类别索引抽出来计数。如果输出里最大索引是 5 且 0~5 都有样本说明索引连续、映射正常如果出现 6 或负数说明标注里有脏数据或者类别数不是 6得回去查。参数上没什么可调的label_dir换成你的实际路径即可。跑完这一步你才对“6 类别”这件事有了实证而不是只信文件名。3. 用这份数据集跑通 YOLO 训练的最小闭环3.1 从 VOC 的 XML 转出 YOLO 的 TXT虽然数据集号称双格式但你自己扩标的数据往往只有 XML所以转换脚本必须会写。核心是把绝对坐标转成归一化中心点坐标并且把类别名映射成索引。下面这个脚本处理单张 XML逻辑清晰、方便你改成批量import xml.etree.ElementTree as ET from PIL import Image # 类别名到索引的映射顺序必须和 data.yaml 的 names 一致 CLASS_MAP {knife: 0, scissors: 1, lighter: 2, spray: 3, battery: 4, hammer: 5} def voc_to_yolo(xml_path, img_path, out_txt): tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) # 图片真实宽度 h int(root.find(size/height).text) # 图片真实高度 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] bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 转成归一化的中心点 宽高 cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines))逻辑说明先读 XML 里的图片宽高这是归一化的分母读错整个框就飘了。然后遍历每个object用CLASS_MAP把类别名换成索引没在映射表里的直接跳过——这一步是防止你扩标时手滑写了个新类别名导致索引越界。坐标转换公式就是(xminxmax)/2/w这一套保留 6 位小数足够。参数上CLASS_MAP必须和data.yaml的names顺序一模一样这是唯一需要你手动对齐的地方。批量跑的时候套个glob循环把out_txt的路径指到labels/下同名文件即可。3.2 data.yaml 的四个必填字段与路径写法YOLO 训练读的是data.yaml写错路径是最常见的“训练启动即报错”。四个字段path数据集根目录、train训练集图片列表或目录、val验证集、names类别名列表。路径写法有两种绝对路径最省心相对路径则相对于path字段解析。我一般用绝对路径避免在path和train之间绕晕path: /data/danger_dataset # 数据集根目录绝对路径 train: images/train # 相对 path 的训练图目录 val: images/val # 相对 path 的验证图目录 nc: 6 # 类别数必须和 names 长度一致 names: # 顺序即类别索引不能乱 0: knife 1: scissors 2: lighter 3: spray 4: battery 5: hammer注意nc和names长度必须相等写 6 就得有 6 个名字。train和val如果指向目录YOLO 会自动扫描目录下所有图片并去找同名 TXT如果指向 txt 列表文件则按列表读。两种都行目录方式更省事。改完 yaml 别急着训先用python -c import yaml; print(yaml.safe_load(open(data.yaml)))确认能正常解析YAML 对缩进极其敏感多一个空格就报错。3.3 启动训练与关键超参怎么设最小闭环命令就一行但参数决定你能不能收敛yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20逐项说modelyolov8n.pt用 nano 版1431 张图这个量级大模型直接过拟合nano 或 s 版足够。imgsz640是 YOLO 的默认输入尺寸危险物品目标偏小如果你显存够可以试 800 甚至 1024小目标召回会好一些但速度掉得明显。batch16看显存调8G 显存跑 640 一般能到 16爆显存就降到 8。lr00.01是初始学习率小数据集别设太大0.01 是稳妥起点loss 震荡就降到 0.005。patience20是早停20 轮验证指标不涨就停防止白跑。1431 张图、6 类别100 轮在单卡上大概几十分钟到一两小时取决于卡。跑完看runs/detect/train/下的results.png重点看mAP50和mAP50-95两条曲线是否还在涨。4. 小数据集训练危险物品检测的避坑与排查4.1 类别索引错位loss 正常降但 mAP 极低现象训练 loss 一路下降看起来很正常但验证集 mAP 常年在 0.1 以下模型预测的框位置对但类别全乱。原因TXT 里的类别索引和data.yaml的names顺序没对齐比如 TXT 里 0 是 knifeyaml 里 0 写成了 scissors。解决跑 2.3 节那段统计脚本把 TXT 里实际出现的索引和 yaml 的 names 逐一对一遍顺序错位就改 yaml索引跳号就回去查标注。这个坑最阴的地方在于 loss 不会报错只能靠 mAP 异常发现。4.2 空标注文件导致训练中断现象训练启动后报IndexError或ZeroDivisionError或者某个 batch 直接崩。原因有些图片没有目标对应的 TXT 是空文件YOLO 在读空 TXT 时某些版本会出问题。解决先扫一遍labels/下有没有 0 字节的 TXT有的话要么删掉对应图片要么在 TXT 里留一行占位不推荐。更稳的做法是训练前统一过滤find dataset/labels -name *.txt -size 0 -print # 列出所有空标注确认后决定删除还是补标4.3 图片与标注文件名不匹配现象训练能启动但日志里train的图片数量远少于预期或者验证时找不到标签。原因labels/里的 TXT 和JPEGImages/里的图不同名常见于批量重命名时后缀没统一.jpgvs.JPG。解决写个脚本对比两个目录的文件名集合差集就是问题文件。Linux 下文件名大小写敏感Windows 拷过来的数据尤其容易中招。4.4 小目标漏检严重先查标注框尺寸再调模型现象模型对大目标检测正常但小目标比如远处的打火机几乎全漏。原因1431 张图里小目标本身像素就少640 输入下经过下采样后特征几乎消失。解决先统计标注框的宽高分布确认小目标占比如果确实小目标多把imgsz提到 800 或 1024同时开mosaic增强YOLO 默认开。别一上来就换模型结构先把输入分辨率和增强调到位性价比最高。4.5 验证集划分不合理导致指标虚高现象mAP 看着不错但换一批新图测试就崩。原因train.txt和val.txt划分时把同一场景、同一批次的图分到了两边验证集和训练集高度相似指标虚高。解决按场景或拍摄批次划分而不是随机按比例切。1431 张如果来自几个不同场景确保每个场景在训练和验证里都有这样验证指标才可信。随机切分在小数据集上特别容易骗自己。5. 把 1431 张用出 3000 张效果增强、扩标与验证技巧1431 张、6 类别平均每类两百多张直接训能出 baseline但想上生产远远不够。我的习惯是先把增强拉满再谈扩标。YOLO 自带的mosaic、mixup、hsv增强对小数据集提升明显mosaic1.0默认开mixup可以设 0.1~0.2别太高否则小目标更糊。degrees旋转增强对危险物品要慎用刀和锤子旋转后语义还在但打火机转 180 度可能就不像了建议degrees10以内。scale缩放增强可以开到 0.5模拟远近变化对小目标召回有帮助。扩标比调参划算。1431 张里挑出模型漏检和误检的图用 LabelImg 补标优先补小目标和遮挡目标补 300~500 张往往比调一周参管用。补标时严格沿用原来的 6 类索引别新增类别否则nc和names全要改。补完重新跑 3.1 的转换脚本把新 TXT 并进labels/。验证环节别只看 mAP。危险物品检测的漏检代价远大于误检所以重点看召回率。训练完在验证集上跑yolo detect val看metrics/recall这一列如果召回低于 0.8优先补小目标标注而不是调阈值。另外做个混淆矩阵看哪两类最容易混——比如 lighter 和 battery 如果经常互认说明特征太像得靠更多样本或更高分辨率区分。最后说个我踩过的坑有次为了冲 mAP把imgsz从 640 提到 1280结果训练时间翻三倍mAP 只涨了 0.02小目标召回倒是涨了 0.05。值不值取决于你的场景——如果漏检一个小目标就是事故那值如果只是做个 demo640 够了。别盲目追高分辨率先算清楚你的算力预算和业务容忍度。希望帮到你。本文还有配套的精品资源点击获取