news 2026/10/10 18:19:58

安检X光危险物品识别数据集:VOC与YOLO双格式+YOLOv8训练实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
安检X光危险物品识别数据集:VOC与YOLO双格式+YOLOv8训练实践

简介:面向安检X光图像中的危险品自动识别与目标检测任务,这份数据集经过整理与标注,覆盖刀、匕首、刀片、剪刀、喷雾罐、玻璃瓶、塑料瓶等12个常见违禁品类,适用于训练YOLO、SSD、Faster R-CNN等主流检测模型,也可用于算法研究与毕设课题。资源包共2000个文件,主体为Pascal VOC格式的XML标注文件(1999个),同时提供YOLO格式的TXT标注,可以在不同框架间灵活切换;压缩包整体约350.67MB,解压后目录结构清晰,便于直接划分训练集与验证集。目前已有853人学习下载,对需要现成标注数据快速开展实验的计算机视觉学习者或安检项目开发者来说,这套数据能显著减少采集与标注成本,并支持模型迭代验证;使用者无需自行转换标签格式,可集中精力进行网络结构设计与性能调优,是一份即取即用的高质量基础数据集。

1. 安检X光危险物品识别检测数据集:9551张图、12类,VOC与YOLO双格式怎么选

做安检机图像识别的项目,最头疼的不是模型选型,而是找不到能直接拿来训练的X光数据集。开源社区里要么是你不认识的合成图,要么是标注格式东一份西一份。这个安检X光危险物品识别检测数据集,直接把Pascal VOC的xml和YOLO的txt两种格式都配齐了,9551张真实X光安检图片,12个标注类别,解压后不用写一行转换脚本就能进训练流程。它解决的是安检场景目标检测的数据冷启动问题,适合在做安检项目复现、毕业设计、以及想验证YOLOv8/v5在自己数据集上效果的工程师和学生。我实际跑过一遍,今天把文件结构、标注细节、训练参数和容易翻车的点一次说清楚。

2. 数据集内部结构:从文件名到标注格式全拆解

2.1 文件组成:jpg、xml、txt一一对应

拿到压缩包后,先别急着解压到任意目录。这个数据集的命名规则很统一,每个图片文件对应一个同名xml和同名txt。比如项目里你能看到firc_xray_3342.xml、firc_xray_8358.xml这样的文件,它们分别和firc_xray_3342.jpg、firc_xray_8358.jpg搭配。我习惯按下面的结构摆放:

FIRCXray/ ├── images/ │ ├── train/ │ │ └── firc_xray_3342.jpg │ └── val/ │ └── firc_xray_8358.jpg ├── Annotations/ │ ├── train/ │ │ └── firc_xray_3342.xml │ └── val/ │ └── firc_xray_8358.xml └── labels/ ├── train/ │ └── firc_xray_3342.txt └── val/ └── firc_xray_8358.txt

注意,xml和txt里的内容本身没有区分训练集和验证集,它们在语义上是同一份标注的两种写法。真正划分train/val是靠我们自己按比例切分,这一点在第三章会给出脚本。打开之后你会发现,xml文件数量、txt文件数量和jpg文件数量都是9551,也就是说没有多余的、缺漏的标注,这一点在二手数据集里已经算良心了。

2.2 标签类别对照与中英文误区

数据集的12个类别是:Cans、CartonDrinks、GlassBottle、PlasticBottle、SprayCans、SwissArmyKnife、Tin、VacuumCup、blade、dagger、knife、scissors。这里有个坑非常值得先说:项目描述里给的中文名有不少机翻痕迹,比如“CartonDrinks”写成了“纸箱冲洗”,实际应该是“纸盒饮料”;“VacuumCup”写成了“真空吸盘”,其实是“真空保温杯”;“Tin”也不是元素锡,而是“锡罐/金属罐”。如果你要做界面显示,千万别照抄那行中文名。

英文类别建议中文名说明
Cans罐头/易拉罐常见金属罐体
CartonDrinks纸盒饮料利乐包类饮品,不是“纸箱冲洗”
GlassBottle玻璃瓶透明瓶体,X光下呈浅色
PlasticBottle塑料瓶密度比玻璃低
SprayCans喷雾罐带喷嘴的压力罐
SwissArmyKnife瑞士军刀折刀形态
Tin锡罐/金属罐铁皮罐体,比Cans更扁
VacuumCup保温杯双层杯体,不是“真空吸盘”
blade刀片单独的小刀片
dagger匕首笔直双刃
knife刀具日常刀具
scissors剪刀X光下特征明显

另外,前8个类别的首字母是大写,后4个类别是小写,比如“blade”的b是小写。初学者在写data.yaml时容易把“SwissArmyKnife”拼错,或者把“scissors”写成“Scissors”,导致训练时类别数量对不上。建议直接从xml里提取name字段生成yaml,不要手打了事。

2.3 VOC与YOLO标注格式内的坐标关系

xml文件和txt文件表达的是同一个框,但坐标体系不同。xml里记录的是真实像素坐标,比如firc_xray_3342.xml打开后会看到:

<annotation> <folder>firc_xray</folder> <filename>firc_xray_3342.jpg</filename> <size> <width>850</width> <height>650</height> <depth>3</depth> </size> <object> <name>knife</name> <bndbox> <xmin>128</xmin> <ymin>95</ymin> <xmax>310</xmax> <ymax>260</ymax> </bndbox> </object> </annotation>

对应的txt文件则是归一化后的YOLO格式,一行一个目标,格式为class_id x_center y_center width height,全部是相对于图片宽高的比例值。上面这个knife框如果图片宽850、高650,那么txt里应该是:

10 0.257647 0.273077 0.214118 0.253846

其中类别编号10对应knife(按类表顺序从0数起),x_center=(128+310)/2/850≈0.2576,width=(310-128)/850≈0.2141。每次拿到新数据集,我都会抽两三张图用这个公式手算一遍,确认txt坐标范围在0到1之间,既不是负数也没有大于1,避免后续训练时读取到非法框。

2.4 双格式为什么同时提供

这个数据集同时提供VOC和YOLO格式,本质上是为了适配不同训练框架。Ultralytics YOLO系列直接读取txt文件,配合images目录就能训练,这也是目前最省事的路径。而MMDetection、Detectron2或者一些传统目标检测教程,习惯用VOC xml格式做数据加载,或者再转成COCO json。官方把两种格式都给了,等于把“格式转换”这一步直接替你做了。

但要注意,这个数据集没有提供分割标签,只有目标检测框。如果你在网上下载了一个号称“VOC+YOLO”的数据集,解压后发现里面带了segmentation类的txt,那才是实例分割格式。这个数据集只有一个目标检测的定位框,别指望拿去做分割训练。另外,xml文件里只有最基本的bndbox和name,没有difficult、truncated这种字段,后面我会讲这个问题怎么绕开。

3. 快速上手:把双格式数据跑进YOLOv8训练流程

3.1 环境准备与目录规整

工欲善其事,必先利其器。训练环境我建议直接用Ultralytics的YOLOv8,它对初学者最友好,对熟悉YOLOv5的人也很平滑。先把依赖装好:

conda create -n finn python=3.10 -y conda activate finn pip install ultralytics==8.2.0

我用的是8.2.0这个版本,因为后续导出ONNX时碰到的坑相对少。接下来把解压后的数据划分成训练集和验证集。这个数据集官方没有划分训练/验证比例,我习惯按8:2划分,并且用随机种子固定下来,保证每次复现结果一致:

import os import random import shutil random.seed(42) image_dir = "FIRCXray/Images" train_ratio = 0.8 images = [f for f in os.listdir(image_dir) if f.endswith(".jpg")] random.shuffle(images) split_idx = int(len(images) * train_ratio) train_images = images[:split_idx] val_images = images[split_idx:] base_dir = "FIRCXray" for sub in ["images/train", "images/val", "labels/train", "labels/val", "Annotations/train", "Annotations/val"]: os.makedirs(os.path.join(base_dir, sub), exist_ok=True) def move_set(img_list, img_sub, lab_sub, xml_sub): for img_name in img_list: stem = os.path.splitext(img_name)[0] shutil.copy(os.path.join(image_dir, img_name), os.path.join(base_dir, img_sub, img_name)) shutil.copy(os.path.join(base_dir, "labels_all", stem + ".txt"), os.path.join(base_dir, lab_sub, stem + ".txt")) shutil.copy(os.path.join(base_dir, "Annotations", stem + ".xml"), os.path.join(base_dir, xml_sub, stem + ".xml")) move_set(train_images, "images/train", "labels/train", "Annotations/train") move_set(val_images, "images/val", "labels/val", "Annotations/val")

上面这段脚本会把你原来平铺的jpg、txt、xml按YOLO训练惯例重新整理到images/train、labels/train这样的结构里。重点是让同一个文件名的图片和txt始终在同一个子集下,不会出现“图片在训练集,标签在验证集”这种低级错误。实际使用中,如果你自己对数据集已经按文件夹分好类,要保证图片名称和标签名称完全一致,扩展名不同不影响。

3.2 编写data.yaml并训练

YOLOv8训练自己的数据集,核心是写一个data.yaml。这个文件的路径写法要注意,最好用绝对路径,避免和当前工作目录纠缠。我把data.yaml放在数据集的根目录下:

path: /home/user/data/FIRCXray # 改成你的实际路径 train: images/train val: images/val nc: 12 names: 0: Cans 1: CartonDrinks 2: GlassBottle 3: PlasticBottle 4: SprayCans 5: SwissArmyKnife 6: Tin 7: VacuumCup 8: blade 9: dagger 10: knife 11: scissors

这里的names顺序必须和labels里的class_id对应。怎么验证?用Python读一个xml,按类表顺序输出它的类别名索引,再去读对应txt的第一个数字,两者应该一致。我踩过这个坑:有一次把某个数据集的names列表按字母序排了,结果模型训练出来预测的类别全是错位的。

接下来启动训练。我用YOLOv8s而不是n,因为X光图里的危险品边缘和遮挡多,模型容量太小容易欠拟合。命令如下:

yolo detect train data=/home/user/data/FIRCXray/data.yaml \ model=yolov8s.pt epochs=120 imgsz=640 batch=16 device=0 \ mosaic=1.0 close_mosaic=10

几个参数的用意说明一下:imgsz=640是YOLO系列比较通用的输入尺寸,X光原图分辨率并不统一,640能兼顾速度和精度;batch=16取决于你显卡的显存,如果你是8G显存,建议改成8;close_mosaic=10的意思是最后10个epoch关闭Mosaic增强,让模型在接近真实分布的数据上收敛,这一点在安检这种小目标多的场景非常管用,否则最后几个epoch的loss会下降得很怪。

3.3 训练日志与指标读取

训练结束后,结果默认保存在runs/detect/train/目录下。我每次会先打开results.csv,看第5列的mAP50和第6列的mAP50-95。注意看两个趋势:一是验证集loss有没有和训练集loss一起下降,如果验证loss在训练后期反弹,说明过拟合,epoch拉到100就够;二是mAP50-95和mAP50的差值是否过大,正常X光场景差值在10到15个点之间,如果差值超过20,说明框的质量普遍不高,可以试试调低置信度阈值或改anchor。

如果目录里没有生成confusion_matrix.png,你可以在训练命令后面加plots=True。这个混淆矩阵能直观告诉你哪些类之间容易互相误判,比如plastic bottle和glass bottle在X光下都是浅色半透明,误判率通常偏高。看了矩阵后再决定要不要加类别权重,比盲目调lrf有效得多。

4. 常见问题排查:标注格式翻车与类别不平衡

4.1 xml里缺difficult字段导致转换脚本报错

现象:我用一个常规的VOC转COCO脚本转换这个数据集,脚本直接崩了,报错KeyError: 'difficult'。原因:标准的VOC xml对象里有difficult字段,但这个数据集的xml只写了name和bndbox,没有difficult也没有truncated。解决:要么改用现成的YOLO txt直接训练,要么在转换脚本里给每个对象硬编码difficult=0。从那以后我拿到VOC格式数据,第一件事就是先用grep -c "difficult"扫一遍所有xml,免得脚本写到一半翻车。

4.2 类别中文名机翻导致的标签错乱

现象:朋友把data.yaml里的names直接翻译成中文,训练时提示Expected 12 classes, but got 11。原因:他把“VacuumCup”翻译成“真空吸盘”,又把“CartonDrinks”当成背景类删掉了,导致类别数量和txt里的数字对应不上。解决:忽略项目说明里的中文名,一切以xml里的name值为准,并用脚本提取类别清单。不要相信二手数据集里任何翻译后的类别名,机器翻译会把“保温杯”变成“真空吸盘”,这种错误在训练阶段非常隐蔽。

4.3 图片被resize后txt坐标越界

现象:训练前为了统一分辨率,用OpenCV把所有图片缩放到416x416,但没重新计算txt坐标,导致训练时大量告警WARNING: 2 labels in ... have out of bounds coordinates。原因:YOLO的txt是相对于原图宽高的归一化坐标,直接resize图片而不改txt,相当于把坐标原点也拉伸了。解决:要么放弃人工resize,让YOLO训练时自动做letterbox,要么写个脚本先把resize后的新坐标算出来再更新txt。我的经验是:除非你的显卡对固定输入尺寸有极强制约,否则不要自己预先resize,让训练框架处理更安全。

4.4 blade和scissors小目标检测率偏低

现象:训练完发现blade的mAP50只有45%,而Cans有92%。原因:blade数量少、目标小,在YOLO的五个下采样层里特征几乎被磨光。解决:第一,训练时用mosaic=1.0和scale=0.5,让小目标在拼接增强中多次出现;第二,把imgsz提到768或1024,代价是显存和训练时间;第三,对blade和scissors做在线随机裁剪复制,把这些小目标周围一块区域抠出来粘贴到其他图上。这个办法比较暴力,但能有效缓解类别不平衡。

4.5 随机划分导致某个类在验证集里没有样本

现象:按照默认0.8/0.2比例随机划分后,验证集中没有任何“dagger”样本,训练时每个epoch都提示标签为空。原因:dagger在数据集中个数本就不多,随机划分的偶然性会让某个小类完全掉进训练集。解决:写一个按类别分布进行分层划分的脚本,保证验证集里每个类别都有至少一张图。我一般用sklearn.model_selection.StratifiedShuffleSplit,以每张图的第一个类别标签作为分层依据。代码不复杂,但能省去后续反复补验的麻烦。

5. 从训练到落地:验证指标与模型导出细节

5.1 用精确率和混淆矩阵给“危险品”把关

安检场景和一般目标检测不一样,漏检一个刀片的代价远高于误报一个保温杯。训练结束后,不要只盯着mAP50看,我会先打开PR_curve.png,找到precision和recall曲线的交点附近,选一个偏向高召回率的置信度阈值。比如模型默认信度是0.25,在安检场景下我会把它降到0.15,让blade这类小目标更容易被框出来,代价是误报数量变多,这个需要业务层再做一次二次判断。混淆矩阵里如果scissors误判成knife,问题不大,如果plastic bottle误判成glass bottle,在安检机上也还算能接受;真正要命的是把blade判成background,那说明你的模型根本没有在该区域产生候选框,需要回炉调anchor或imgsz。

5.2 导出ONNX并在安检机上验证

训练完成后,部署到安检机边缘设备通常要导出成ONNX。Ultralytics的导出命令很简单:

yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640 opset=12

导出后,用onnxruntime跑一张测试图确认输出维度,我是拿这个脚本快速验证的:

import onnxruntime as ort import numpy as np import cv2 sess = ort.InferenceSession("best.onnx") input_name = sess.get_inputs()[0].name img = cv2.imread("test.jpg") img = cv2.resize(img, (640, 640)) img = img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGB并转CHW img = np.ascontiguousarray(img, dtype=np.float32) img /= 255.0 img = img[None, ...] outputs = sess.run(None, {input_name: img}) print(outputs[0].shape) # (1, 84, 8400)

输出维度中的84对应“4个box坐标 + 80个COCO类别概率”,但你的模型只有12类,所以这里应该是“4 + 12 = 16”。如果你导出的模型维度不是16,赶紧停下来检查类别映射,别等到部署现场才发现。ONNX里面的输出已经包含了置信度筛选逻辑,在后续部署时也要注意,opset=12是对大部分推理框架兼容性较好的版本,太新或太旧都可能遇到算子不支持的问题。

5.3 实际项目里的数据补充策略

模型上线后,安检机会不断产生新的X光图像。我一般会把客户确认过的“漏检图”和“误报图”每周整理一次,按同样的标注规范追加到这个数据集的尾部,重新训练一版增量模型。追加数据时,类别序号必须保持和原数据集完全一致,不能因为新加了类别就擅自重排。比如原来的knife是10,你在追加数据后不能把它改成其他数字,否则旧模型权重全部作废,而且混合训练时所有历史框的class_id都会错乱。每次训练完,我都会单独用一份固定的历史验证集跑一遍,确认老类别的mAP没有明显下降——这个动作能拦住很多回归问题。

我第一次拿这个数据集跑实验时,直接用了YOLOv8的默认参数训练,结果blade的召回率只有四成。后来我做了一次类别分布统计,发现blade的实例数量只有Cans的十分之一。从那以后,我每次做安检项目拿到新数据集,都会强制自己先跑一遍类别统计脚本,在开始训练前就决定要不要做过采样或调类别权重,而不是等一轮训练跑完再返工。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 18:18:39

OpenCV图像前景分割经典例程:阈值、分水岭与GrabCut实战指南

简介&#xff1a;演示GrabCut算法完整流程的图像前景分割工程&#xff0c;面向计算机视觉初学者与算法研究者&#xff0c;解决复杂场景中前景目标与背景分离的建模与实现问题。压缩包共107个文件&#xff0c;约10.31MB&#xff0c;内含GrabCut、GMM、maxflow、graph等cpp/h源码…

作者头像 李华
网站建设 2026/10/10 18:12:47

PaddleOCR打包exe离线部署实战:PyInstaller避坑与体积裁剪

简介&#xff1a;这是一份面向无Python环境用户的PaddleOCR离线文字识别工具打包资源&#xff0c;适合需要将OCR能力部署到Windows端、仅凭图片路径即可获取识别结果的开发者与运维人员。压缩包共2000个文件&#xff0c;约279.22MB&#xff0c;以319个py源码、319个pyc字节码、…

作者头像 李华
网站建设 2026/10/10 18:03:21

OpenCV水果识别实战:苹果、香蕉、梨子样本采集与SVM分类

简介&#xff1a;这份OpenCV水果识别样本面向计算机视觉入门与进阶学习者&#xff0c;聚焦苹果、香蕉、梨子三类水果的图像分类任务&#xff0c;可用于练习图像预处理、特征提取、分类器训练与测试的完整流程。包内共1624个文件&#xff0c;以1618张jpg水果图像为主体&#xff…

作者头像 李华
网站建设 2026/10/10 18:02:23

AI File Sorter 简介:用本地大模型免费整理文件的终极指南

AI 应用大模型本地部署桌面应用 【免费下载链接】ai-file-sorter Cross-platform desktop application for content-aware file organization and renaming. Supports local and remote LLMs, preview-based workflows, and fully user-controlled changes. 项目地址&#xff1…

作者头像 李华
网站建设 2026/10/10 17:53:07

REA:一个本地优先的阅读标注与全文检索工具

很多人第一次听到 REA 这个名字&#xff0c;都会以为是某个开源项目的缩写后缀&#xff0c;或者某款工具的精简代号。其实它是我自己写的一个本地阅读与标注管理工具&#xff0c;全称是 Reading Efficiency Assistant&#xff0c;这名字有点绕&#xff0c;一般我都直接叫 REA。…

作者头像 李华