公共场所危险物品检测数据集VOC+YOLO格式解析与训练实践

公共场所危险物品检测数据集VOC+YOLO格式解析与训练实践 简介面向公共场所危险物品检测任务这套数据集提供1431个已标注样本覆盖刀具、手枪、智能手机、钱包、纸币、卡片6类目标并同时提供VOC与YOLO两种主流标注格式适合用于安检监控场景下的目标检测模型训练与算法验证。资源包共2000个文件以XML标注文件和TXT标签文件为主压缩后大小约126.77MB全部标注由LabelImg工具画矩形框完成总计1497个目标框其中刀具样本占1035框钱包、卡片等类别数量偏少可用于类别不平衡问题研究与数据增强实践。目前已有330人浏览学习下载后可直接将图像与标注对应导入训练脚本用于YOLO系列或Faster R-CNN等常见检测框架也便于初学者对比学习两种标注格式的差异与转换流程是目标检测实验和毕业设计的实用资源。1. 公共场所危险物品检测数据集为什么把格式列为第一优先级公共场所的视频监控里有大量行人、箱包、推车真正需要报警的是混在其中的刀具、可疑器械等小目标。要训练一个能实时运行的检测模型第一步不是调网络结构而是先把一份格式统一、类别划分清晰的数据集准备出来。标题里的这份公共场所危险物品检测数据集VOCYOLO格式1431张6类别把图像与两组标注同时给出等于把数据准备这个阶段压缩到半天以内。下面按两种标注格式的关系、7z 解包、目录整理、训练配置和验证的顺序展开适合刚接手目标检测项目、想在 2 到 4 天内跑通第一个可用模型的工程师。所有命令基于 Linux 环境可直接复制执行。2. 读懂 VOC 与 YOLO 双格式目录、标注与转换公式2.1 VOC XML 标注里的四个坐标字段VOCPASCAL VOC格式的标注文件是 XML每张图片对应一个同名 XML 文件。核心结构如下annotation filenameIMG_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object nameknife/name difficult0/difficult bndbox xmin420/xmin ymin210/ymin xmax640/xmax ymax510/ymax /bndbox /object /annotationxmin、ymin、xmax、ymax是像素坐标左上角和右下角的绝对位置。size里的宽高决定了坐标归一化时除以多少做格式转换时必须以该字段为准不能按图像文件名去猜分辨率。监控视频截帧常出现 EXIF 旋转XML 里的宽高可能是旋转前的值这类偏差会在转换后被放大排查成本很高。2.2 YOLO 归一化坐标与 class id 的对应关系YOLO 格式的标注是纯文本每行代表一个目标对象五列构成class_id x_center y_center width height。注意x_center、y_center是归一化到 0 到 1 的相对坐标width和height也是相对值。例如上面的 knife 目标转换后0 0.2760 0.3333 0.1146 0.2778四个数值的来源分别是0.2760 (420 640) / 2 / 19200.3333 (210 510) / 2 / 10800.1146 (640 - 420) / 19200.2778 (510 - 210) / 1080。多一个目标就多一行类别 id 从 0 开始递增由类别名到 id 的映射表决定。YOLO 系列训练框架只认这种 txt不认 XML所以标题里的“VOCYOLO 双格式”本质是同一批图像的两份镜像标注。2.3 两类格式互转的边界条件与检查点最常用的转换公式无论是 VOC 转 YOLO 还是 YOLO 转 VOC都绕不开下面这一组关系x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height反过来则由 YOLO 坐标求像素框xmin (x_center - width / 2) * img_width ymin (y_center - height / 2) * img_height xmax (x_center width / 2) * img_width ymax (y_center height / 2) * img_height转换时有三个点容易出错。第一difficult标记为 1 的难例对象很多转换脚本默认跳过但 YOLO 训练一般建议把它当作普通目标保留除非你的评估指标也遵循 VOC 的 difficult 规则。第二XML 中存在越界坐标例如 xmax 大于图宽YOLO 格式要求归一化值在 0 到 1 之间越界值会被 ultralytics 自动裁剪但会造成标签与图像边缘的轻微错位训练前最好在脚本里统一处理掉。第三类别名的顺序决定 id 编号训练开始后再改顺序之前所有权重都失效。格式文件后缀坐标类型是否依赖图像尺寸VOC.xml像素绝对坐标 (xmin, ymin, xmax, ymax)是XML 自带 size 字段YOLO.txt归一化相对坐标 (x_center, y_center, w, h)否数值已归一化提示无论用自己写的脚本还是现成转换工具转换后要随机挑 5 到 10 张图把 txt 里的框画回原图上人工核对一遍。坐标全偏、框大小异常这类问题肉眼一眼就能看出来比反复调训练参数高效得多。3. 从 7z 压缩包解出数据并核对图像与标注3.1 安装 p7zip 并验证压缩包完整性.7z后缀要先处理。7z 的压缩比通常比 zip 高包含 1431 张 JPEG 图像的数据集压缩后体积差异明显。Linux 下解压需要安装 p7zipsudo apt update sudo apt install -y p7zip-full解压前先列目录确认没有分卷损坏和加密头7z l 公共场所危险物品检测数据集VOCYOLO格式1431张6类别.7z看到类似2024-01-15 10:23:44 ....A 128345 damage_images/train/0001.jpg的行即为正常。如果提示Cannot open file as archive先用file命令确认文件真实格式与后缀一致。接着用7z t测试完整性这一步会逐文件校验校验和数据量大时耗时较长但值得做一次7z t 公共场所危险物品检测数据集VOCYOLO格式1431张6类别.7z解压时指定输出目录避免中文路径与特殊字符干扰后续脚本7z x 公共场所危险物品检测数据集VOCYOLO格式1431张6类别.7z -o./danger_data-o参数直接指定输出根目录注意-o后面没有空格。解压完成后用du -sh danger_data看实际占用空间和压缩包大小对比能大致判断文件是否全部释放。3.2 解压后的目录结构与基础统计拿到数据集后先摸清目录拓扑。常见的是以下两种布局VOC 风格和 YOLO 风格并存danger_data/ ├── VOC2007/ │ ├── JPEGImages/ # 全部图像 │ ├── Annotations/ # 全部 xml │ └── ImageSets/Main/ # train.txt, val.txt └── yololabels/ ├── images/ │ ├── train/ # 训练图像 │ └── val/ # 验证图像 └── labels/ ├── train/ # 训练标签 txt └── val/ # 验证标签 txt第一步先把统计数字跑出来确认“1431 张”属实、标注是否全部对应find danger_data -type f \( -name *.jpg -o -name *.jpeg -o -name *.png \) | wc -l find danger_data -type f -name *.xml | wc -l find danger_data -type f -name *.txt | wc -l三个数字和 1431 偏差较大时说明图像集与标注集不匹配。图像多标注少是漏标标注多图像少要考虑文件覆盖或重复命名。此时不要急着进入训练先按下一节处理对齐问题。3.3 标注缺失、中文文件名与空标签的排查压缩包在制作和传输过程中容易引入三类问题逐项排查现象判断处理方式图像存在、xml 或 txt 缺失漏标或打包遗漏从另一份格式补齐否则删除该图文件名含中文、空格、括号ultralytics 对中文路径支持不稳定统一重命名为纯英文数字txt 文件大小为 0该图无标注目标训练时会自动跳过不删除但不要放 train 目录用 Python 快速筛查 YOLO 目录import os from pathlib import Path img_dir Path(danger_data/yololabels/images/train) lbl_dir Path(danger_data/yololabels/labels/train) for img in img_dir.glob(*.jpg): txt lbl_dir / (img.stem .txt) if not txt.exists(): print(missing:, img) elif txt.stat().st_size 0: print(empty:, img)这里missing对应 XML 里能解析出object但 txt 里没有对应行empty是 txt 存在但内容为空。两种文件在 ultralytics 加载时会静默跳过导致实际参与训练的图片数少于预期最后的 mAP 曲线无法复现。输出结果里的每一行都要确认处理过再进下一步。4. 把数据集整理成 YOLO 训练目录并生成配置4.1 统一图像与标签的文件名映射无论 VOC 目录还是 YOLO 目录最终跑训练时喂给 ultralytics 的是 images 与 labels 两个平铺目录要求是目录下每张图像的 stem不含扩展名的文件名和对应 txt 的 stem 完全一致且一个 stem 只能对应一张图。import shutil from pathlib import Path src_img Path(danger_data/VOC2007/JPEGImages) src_xml Path(danger_data/VOC2007/Annotations) out_img Path(dataset/images/train) out_lbl Path(dataset/labels/train) out_img.mkdir(parentsTrue, exist_okTrue) out_lbl.mkdir(parentsTrue, exist_okTrue) for xml_path in src_xml.glob(*.xml): stem xml_path.stem img_path src_img / (stem .jpg) if not img_path.exists(): print(image not found:, stem) continue shutil.copy(img_path, out_img / f{stem}.jpg) txt_path Path(danger_data/yololabels/labels/train) / (stem .txt) if txt_path.exists(): shutil.copy(txt_path, out_lbl / f{stem}.txt)批处理的核心原则是以 stem 作为图像与标注的唯一关联键不做任何重命名和二次映射。两个不同子目录出现同名图片是数据集最常见的数据污染来源会直接导致验证集的框被训练集见过mAP 虚高到失真。4.2 按 train / val 拆分并生成 data.yaml数据准备好后需要划分训练集与验证集。公共场景检测通常采用 80% 训练、20% 验证。如果图像是连续监控帧要按时间段切分而不是随机打散相邻帧里的同一种目标会同时出现在两个集合中验证指标虚高的幅度可达 10 个点以上。import random from pathlib import Path img_dir Path(dataset/images/train) all_imgs list(img_dir.glob(*.jpg)) random.seed(42) random.shuffle(all_imgs) val_ratio 0.2 val_n int(len(all_imgs) * val_ratio) val_imgs, train_imgs all_imgs[:val_n], all_imgs[val_n:] with open(dataset/train.txt, w) as f: for img in train_imgs: f.write(str(img.resolve()) \n) with open(dataset/val.txt, w) as f: for img in val_imgs: f.write(str(img.resolve()) \n)这里random.seed(42)保证复现.resolve()生成绝对路径防止 ultralytics 在相对路径下切换工作目录时找不到文件。拆分比例是经验值类别多、小目标多时可以把验证集提升到 25%但要保证验证集中每个类别至少出现 30 个标注目标否则单类 mAP 波动剧烈。生成 data.yaml 的内容如下path: /home/user/danger_data/dataset # 改成实际绝对路径 train: train.txt val: val.txt nc: 6 names: 0: knife 1: gun 2: bottle 3: hammer 4: fire 5: suspiciouspath指向数据集根目录train与val写 txt 文件名即可。nc必须等于names的元素个数这是 ultralytics 启动时最先校对的字段。部分数据集会附带classes.txt优先读取它生成 names不要手动输入否则类别顺序写错训练过程不会报错但预测结果会张冠李戴。4.3 类别分布统计与不平衡判断准备阶段最后一步是统计类别分布这决定了第 5 章的超参数要不要调cut -d -f1 dataset/labels/train/*.txt | sort -n | uniq -c输出格式是“数量 id”。若某类数量仅为最多的类的十分之一属于典型的类别不平衡。YOLO 框架不会自动平衡类别权重loss 会由大类别主导小类别目标几乎学不出来。应对手段放在第 5.3 节展开。5. 用 ultralytics 训练 6 类别公共场景检测模型5.1 安装与最小训练命令训练使用 ultralytics 框架一条命令即可启动pip install ultralytics yolo detect train datadataset.yaml modelyolov8n.pt epochs100 imgsz640 batch16 device0参数含义data指向第 4.2 节生成的 yamlmodelyolov8n.pt使用预训练权重做迁移学习公共场景下从 n 或 s 起步不用一上来就跑 l 或 xepochs100是轮数batch16是批大小device0指定第一块 GPU。如果显存只有 8G把batch降到 8imgsz降到 512。这个命令只保证跑通不保证效果1440 张图像的数据量足够在 100 轮内看到明显收敛趋势。5.2 影响效果的 4 个超参数参数推荐值作用与调整逻辑imgsz512 或 640监控画面中目标普遍偏小降分辨率会直接损失小目标 mAPpatience30 到 50早停轮数太小会错过小类别后期的提升mosaic1.0四图拼接增强等价于扩充小目标的样本密度cos_lrFalse小数据集开启会加剧震荡默认关闭imgsz与 mAP 不是线性关系从 640 降到 512训练时间约缩短 35%但小目标的 mAP 通常会掉 2 到 4 个点。如果手里的监控原图是 1080p建议先做 ROI 裁剪把行人区域裁出来再贴标签训练而不是用整张图降采样。《公共场所危险物品检测数据集VOCYOLO格式1431张6类别》这类公共场景数据拍摄距离远、目标占像素少imgsz640比 512 稳妥。patience控制连续多少个 epoch 验证指标不上升就停止。默认值偏保守1431 张的小数据集建议设为 30省时间且能覆盖多数收敛场景。mosaic保持默认 1.0除非训练时发现 loss 曲线出现周期性尖峰再考虑临时下调。5.3 类别不平衡与小目标问题的两个应对统计结果是“knife 类 800 个目标、gun 类只有 60 个目标”时只调超参数解决不了。先试数据层面对尾部类别做图像级复制增强每次复制时随机翻转import random import shutil from pathlib import Path lbl_dir Path(dataset/labels/train) rare_class_id 1 # 假设 gun 是 id1 for txt_path in lbl_dir.glob(*.txt): lines txt_path.read_text().strip().splitlines() if any(line.split()[0] str(rare_class_id) for line in lines): img_path Path(dataset/images/train) / (txt_path.stem .jpg) if img_path.exists(): shutil.copy(img_path, img_path.with_stem(txt_path.stem _copy)) shutil.copy(txt_path, txt_path.with_stem(txt_path.stem _copy))copy复制带rare_class_id的图像和对应 txt图像不用实际翻转训练时 mosaic 和随机翻转增强会注入几何变化。复制数量控制在原样本量的 1 倍以内超过这个比例模型容易记住复制样本的纹理特征。调高mosaic概率。ultralytics 默认在图集中随机拼图目标密集时小目标被裁切保留的概率更高。理论上mosaic1.0已经是默认值关键是要配合close_mosaic10即最后 10 轮关闭 mosaic让模型在接近真实场景的分布上微调避免训练集与验证集的分布差异过大。5.4 训练日志解读与中断恢复训练输出在runs/detect/train/下results.png包含 loss 曲线、PR 曲线、混淆矩阵。第一看train/box_loss是否在 20 轮左右开始收敛再看val/box_loss是否同步下降。如果训练 loss 持续下降而验证 loss 不再动说明进入过拟合配合patience早停即可。训练中断后恢复yolo detect train resume modelruns/detect/train/weights/last.pt用last.pt而不是best.pt原因是last.pt保存的是最新 epoch 的权重和优化器状态resume会把优化器和学习率调度一起恢复best.pt只是最优 checkpoint恢复后学习率可能从初始值重新算反而打乱收敛节奏。6. 验证模型效果的三个具体手法6.1 对验证集跑指标并优先看 mAP50yolo detect val modelruns/detect/train/weights/best.pt datadataset.yaml输出里先看mAP50和mAP50-95两个数。mAP50反映框得准不准mAP50-95是 IoU 从 0.5 到 0.95 按 0.05 步进的平均值更严格但数值更低。公共场景报警业务要的是“该报的都报出来”重点看每类的 recall 而非总 mAP。逐类检查时用混淆矩阵定位召回率最低的类大概率是第 4.3 节统计出来的尾部类别。6.2 抽样预测并人工复核yolo detect predict modelruns/detect/train/weights/best.pt sourcetest_imgs/ imgsz640 conf0.4 saveTrue取 10 到 20 张与训练集场景差异较大的照片夜间的、逆光的、强遮挡的、镜头带畸变的各几张人工看结果图。只看指标不看图像是无效验证因为公共区域的监控相机安装角度和训练数据差异一旦过大mAP 可能掉了 20 个点但指标表上反应不出来。6.3 连续帧测试与置信度阈值调整conf是报警灵敏度的旋钮。值高误报少漏报多值低漏报少误报多一般取 0.25 到 0.5。部署到视频流前用一段连续 200 帧以上的测试片段跑推理观察同一目标在相邻帧间的框是否抖动。抖动明显时在代码里开启agnostic-nms让跨类别重叠框也参与非极大值抑制再把 NMS 的 IoU 阈值从默认 0.7 下调到 0.5。目标小而密集时0.7 会把相邻的不同目标合并下调后能保住边缘处的独立框。如果业务只关心“有没有危险物”不关心具体是哪一类可以在推理后把 6 类输出统一合并成一个is-danger标签。这样做单类 mAP 会略有下降但现场误报率通常能明显下降报警逻辑也更好维护。最终选择哪个conf和 NMS 参数用包含多次事件回放的 30 分钟监控片段做回归测试记录漏报数和误报数再定阈值。本文还有配套的精品资源点击获取