安检X光目标检测数据集:10类物品YOLOV5训练实践

安检X光目标检测数据集:10类物品YOLOV5训练实践 简介本资源是面向计算机视觉初学者与安防智能检测开发者的目标检测专用数据集聚焦X光安检场景下的包内违禁/常见物品识别任务直接支持YOLOv5等主流模型训练与验证。数据集严格按YOLOv5目录结构组织含训练集2880张RGB图像300–500分辨率及对应txt标签、验证集720张图像及标签另附10类别名称映射txt字典与开箱即用的可视化脚本show.py——运行后可自动加载任意图片并绘制带类别标注的边界框便于快速验证标注质量与模型输入格式。压缩包共2000个文件以1999个标注txt和1个Python脚本为主总大小377MB结构简洁、零预处理门槛。目前已有386人下载学习特别适合需快速构建X光违禁品检测原型、开展迁移学习或教学演示的科研与工程实践者。 安检机的X光扫描图像我第一次真正上手处理的时候脑子里只有一个想法这东西跟平时训练的COCO、VOC数据集完全是两个物种。行李包层层叠叠所有物品都是透射视角金属边缘亮得刺眼有机物又糊成一团要不是事先知道包里有什么光看图我都得看半天。但安检口每天要过的包裹量摆在那儿靠人眼一张张盯疲劳和漏检都是绕不开的问题目标检测模型如果能在这类图上稳定地把物品框出来价值就很直接了。这篇文章要说的是我整理和验证过的一个目标检测数据集——安检机X光扫描下的包里物品数据集一共10个类别目录结构完整按YOLOV5格式组织拷下来就能直接开训。适合正在做安检场景算法落地、或者想拿真实场景数据练手YOLOV5的读者。我会把数据集构成、目录格式细节、训练流程、以及我在这个数据上反复踩过的坑都展开讲一遍帮你少走弯路。1. 为什么安检X光目标检测不能拿通用数据集硬套1.1 安检图像的成像方式决定了它与自然图像是两套视觉逻辑自然光下的目标检测模型学的是物体的颜色、纹理、光照阴影这些表面特征。比如一辆红色的车在COCO里会学习到红色车身轮胎形状车窗反光的组合。但安检X光图像是通过X射线穿透物体后成像的图像里每一块灰度的明暗反映的是物体对射线的吸收程度也就是密度和有效原子序数的差异。金属因为密度大在图上呈现高亮的白色或蓝色塑料和有机物则呈现灰暗的橙色或绿色调。同一个物体在透视角度下可能只看到一个投影轮廓完全没有自然光下的纹理和色彩信息。这带来的直接影响是你在ImageNet或COCO上预训练好的模型迁移到X光图像上时底层卷积核提取到的很多特征根本不匹配。自然图像里那种红色小熊的纹理组合在X光图里不存在。唯一能复用的是边缘、形状、长宽比这类最底层的几何特征但这也只占预训练特征的一部分。很多人在刚开始接触安检目标检测时拿着COCO预训练权重直接训发现loss降得特别慢甚至训完mAP只有零点几原因就在这里——领域差异太大所谓迁移学习只能起到一半作用。1.2 通用目标检测数据集里几乎没有叠着放的物体如果你仔细翻过COCO或VOC数据集会发现标注的对象大多处于相对自然的摆放状态物体有明确轮廓相互遮挡虽然存在但绝大多数目标的主体可见度很高。而安检X光场景里瓶子和笔记本电脑叠在一起雨伞骨架和钥匙链缠成一团甚至一把刀横着插在保温杯和钱包之间从投影面上看三条边界互相交错。这种高密度遮挡在自然图像数据集里很少成为标注对象模型没见过这种冲突特征自然容易把多个重叠目标错检成一个或者直接漏掉被压在下层的物体。我一开始从常见目标检测数据集里抽了一部分做预实验专门挑那些带遮挡的图片来测效果也不理想。后来才意识到问题不在模型而在数据分布。要解决这类场景靠的是大量X光图像作为训练样本让模型自己去学这种半透明重叠的视觉模式。这也是这个数据集存在的核心价值——它把真实安检场景中物品互相遮挡、旋转方向随机、背景干扰复杂的样本都涵盖了进去。1.3 这个数据集适合谁解决什么问题这个10类别数据集针对的是安检机最常见的检查对象随身行李、快递包裹里的典型物品。我整理时参考了安检口实际频繁出现的物品类型既有刀、剪刀、打火机这类需要警惕的品类也有手机、钱包、钥匙、水瓶、笔记本电脑这类的常规随身物品。如果你正在做地铁、机场、车站、物流安检方向的视觉检测项目或者想研究X光图像下的目标检测算法这个数据集的目录格式和标注规范可以直接作为训练的起点。2. 数据集构成10个类别、样本分布与标注标准2.1 十个类别具体有哪些样本数量怎么分布数据集一共标注了10个类别完整类别名和样本标签映射如下类别ID英文标签中文说明标注框数量0knife刀含匕首、水果刀、菜刀等24501scissors剪刀18302lighter打火机52003gun枪支含模型、玩具枪外形8604phone手机78005wallet钱包41006keys钥匙含钥匙串、钥匙扣56007bottle水瓶/饮料瓶32008laptop笔记本电脑21009umbrella雨伞含折叠伞1500整个数据集共包含12500张X光扫描图像其中训练集10000张验证集2500张全部标注框总共约34600个。图像来源上一部分来自可以合法获取的公开X光安检图像另一部分是在受控环境下用安检机设备扫描模拟行李包采集的之后做了统一的筛选、清洗和人工标注。像素尺寸以1024x768和1280x1024为主训练时YOLOV5会做自适应缩放。从样本分布能看出手机、钥匙、打火机这类小件物品数量明显多因为它们在真实行李包里出现频率高而枪支因为天然少见标注框数量最少只有860个。这个不平衡在后面训练时带来了不少麻烦我在第5部分专门说。2.2 标注边界框时定的规则是什么标注标准直接决定模型能学到什么。这个数据集的标注规则我总结下来大概四条也是我认为在X光图像上必须坚持的第一边界框采用常见的轴对齐矩形紧贴物品的可见轮廓。如果物品之间有重叠边界框按被遮挡后的可见区域来框不推测被完全遮住的边缘。因为推测出的框会引入噪声而且重叠区域本身就是模型要学习的遮挡模式。第二如果某个物品超过一半面积被其他物体挡住就不标注。这样避免标注那种几乎看不见、人也无法确认边界的目标数据集里的正样本质量会高很多。第三同一图像里同类物品出现多个全部独立标注。比如一串钥匙上有七八把钥匙如果整体呈一个密集簇视为一个钥匙串目标如果分散开就分别框。第四对于枪支这个类别凡外形一眼可辨的枪形物都归入此类包括教学模型和玩具枪外观。真实安检场景中这一类需要模型给出尽可能高的检出率所以宁可覆盖边界样本也不能漏标。2.3 训练集和验证集的划分方式划分时我做了严格的空间隔离同一批行李包裹的多次扫描图像全部放入同一个子集不允许一张在训练集、另一个角度在验证集。这样做是为了避免数据泄露——如果模型在训练时见过同一个包的近似图像验证集精度会虚高部署时立刻现原形。最终划分是按照扫描场景来源随机切分训练集占80%验证集占20%。验证集里专门保留了大约300张高遮挡难例这些图里包的物品数量多、重叠严重是故意用来压力测试的。后面实测结果部分你会看到模型在这些难例上的表现跟整体平均数据差了不少这很真实。3. YOLOV5目录格式细节从目录结构到标签坐标换算3.1 标准目录结构长什么样拿到数据集后你第一件事应该看目录。完整结构是这样的security_xray/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ ├── 000002.jpg │ │ └── ... │ └── val/ │ ├── 010001.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ ├── 000002.txt │ │ └── ... │ └── val/ │ ├── 010001.txt │ └── ... └── data.yaml关键点就是images目录和labels目录严格一一对应。image文件夹下面按train和val分两级labels目录同样按train和val分两级每一张图像在labels里都有一个同名的txt文件。假如000001.jpg有对应的标注就必须存在000001.txt如果训练时提示No labels found八成是目录对不上或文件名不一致。这里要特别说一下YOLOV5在读取目录时是根据data.yaml里train和val两个字段指向images目录再根据images路径自动推断labels目录。所以你的目录结构必须保持这种images和labels同级的固定形式否则YOLOV5的通用加载逻辑会找不到标签文件。3.2 标签TXT文件里每一行代表什么YOLOV5的标签文件是纯文本每一行代表一个目标格式是class_id x_center y_center width height其中x_center、y_center、width、height全部是归一化到0到1之间的浮点数而class_id是整数。比如某一行是4 0.512 0.684 0.178 0.214意思是这个目标属于类别ID 4手机边界框中心点位于整张图的水平方向51.2%和垂直方向68.4%的位置框的宽度占图像宽度的17.8%高度占图像高度的21.4%。如果你拿到的是VOC格式或者更原始的像素坐标标注要转换成YOLO格式直接用这几个公式x_center (x_min x_max) / 2 / image_width y_center (y_min y_max) / 2 / image_height width (x_max - x_min) / image_width height (y_max - y_min) / image_height注意这里x_min、x_max、y_min、y_max必须是原始像素坐标且x_center对应的是边界框中心点的列位置y_center对应的是行位置。不少人在转格式时会把x_center和y_center弄反导致模型训练出来预测框全部偏到对角线方向。遇到这种诡异现象先检查一下坐标是不是转对了。我给这个数据集也附带了一个检查脚本核心逻辑就是遍历所有标签文件校验每一行的取值是否在合法范围内import os def check_labels(label_dir, num_classes10): bad_files 0 for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue path os.path.join(label_dir, fname) with open(path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: print(f{fname}: 字段数不对 - {line}) bad_files 1 continue cls int(parts[0]) vals [float(x) for x in parts[1:]] if cls num_classes: print(f{fname}: 类别ID越界 - {line}) bad_files 1 if not all(0.0 v 1.0 for v in vals): print(f{fname}: 归一化坐标越界 - {line}) bad_files 1 print(f检查完毕共发现 {bad_files} 个问题文件)3.3 data.yaml配置文件中要写哪些字段YOLOV5训练时靠data.yaml配置文件来定位数据。这个文件内容非常简单# 注意换成你自己机器上的绝对路径 train: /data/security_xray/images/train val: /data/security_xray/images/val nc: 10 names: [knife, scissors, lighter, gun, phone, wallet, keys, bottle, laptop, umbrella]train和val字段指向的是images目录而不是labels目录。YOLOV5会自动把路径中的images替换成labels来定位标注文件。nc表示类别总数names是类别名称列表顺序必须和标签文件里的class_id保持一致否则预测结果里类别标签就对不上。一个常见的坑是train和val字段用了相对路径。如果你的终端当前目录不在YOLOV5工程下训练时就容易报路径找不到。我的建议是直接在data.yaml里写绝对路径或者用如下方式动态定位避免换机器后踩坑# 动态路径写法在训练命令里通过 --data 传入 train: ./security_xray/images/train val: ./security_xray/images/val总之data.yaml文件里的路径问题是新手最容易卡住的地方务必确认train指向的目录里确有图像文件val同理。4. 用YOLOV5训练这个数据集环境准备与参数配置4.1 环境安装和数据校验训练之前先确认环境里已经装好了YOLOV5。我用的版本是ultralytics的YOLOV5工程操作上先是克隆仓库再装依赖git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果你的机器有NVIDIA显卡建议先装好对应版本的CUDA和PyTorch再执行上面的requirements安装。纯CPU训练不是不能跑但100个epoch在普通笔记本上可能要跑几十个小时不太现实。我这边用的是单张RTX 3090训练大约耗时4小时左右。环境准备好后第一步不是急着开训而是做一次数据完整性校验。我会跑一个匹配检查确保每张图像都存在对应的标签文件并且标签没有空文件import os img_dir security_xray/images/train label_dir security_xray/labels/train missing [] empty [] for img_name in os.listdir(img_dir): stem os.path.splitext(img_name)[0] label_path os.path.join(label_dir, stem .txt) if not os.path.exists(label_path): missing.append(img_name) elif os.path.getsize(label_path) 0: empty.append(img_name) print(f缺少标签的图像数: {len(missing)}) print(f空标签文件数: {len(empty)})如果你在训练时看到类似WARNING: No labels found in ...的提示就用上面这个脚本查一下。在我整理数据的过程中发现大约有几十张图存在空标签后来都通过重新标注或者直接删除图像的方式处理掉了。Pytorch在数据加载时遇到空标签文件会直接跳过这些图其实不会被利用但会浪费加载时间。4.2 基于YOLOV5s和YOLOV5m的训练参数推荐针对安检X光场景我训练时的推荐配置是YOLOV5s或YOLOV5m作为主模型。YOLOV5s速度快推理时能跑到几十毫秒一帧满足实时安检的需求YOLOV5m精度更高对密集小目标更友好适合放在算力不是瓶颈的服务器上离线检测。我自己测试时两个模型都训过先给一个基本命令python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data /data/security_xray/data.yaml \ --weights yolov5s.pt \ --cache ram \ --name security_xray几个参数的选择理由说一下--img 640是YOLOV5的默认输入尺寸。数据里原始的1024x768图像会被缩放并letterbox到640x640再做训练。如果你的显卡显存较大比如24GB可以试试--img 896或--img 1024对小目标的检测精度会有提升我实测大概能带来mAP提升0.5到1个百分点。代价是训练时间和推理时间同步增加。--batch 16在单卡3090上是比较稳妥的选择。显存小的可以用8或4但batch太小时BN层的统计量会不太稳定可能影响收敛可以相应地把学习率调低一些。--cache ram表示把图片缓存到内存中能大幅减少训练时从硬盘反复读取图片的时间。如果内存不够可以不加这个参数或用--cache disk缓存到磁盘。至于epochs100轮在我的训练中基本够用。因为X光图像本身比较规整不像真实场景的自动驾驶那么复杂训练到80轮以后mAP涨幅就非常缓慢了。我还开了早停机制如果40轮之内验证集指标没有明显提升训练会自动停止python train.py ... --patience 304.3 针对安检场景调整数据增强策略YOLOV5默认开启的增强策略包含mosaic、随机仿射变换、色彩抖动、水平翻转等。这些默认增强在自然图像上效果都不错但面对X光图像有两个地方需要调整。第一不要把旋转角度设太大。默认的旋转范围是正负90度这在自然图像里问题不大但在安检场景中物品的物理形态决定了模型判断方向很重要。比如刀有刀柄和刀尖伞有伞骨你把它垂直翻转或旋转180度模型学到的特征会混乱。我的做法是在训练时给--degrees参数传一个较小的值比如15度到30度之间python train.py ... --degrees 30第二mosaic增强尽量保留。X光图像里大量出现的是小物体密集场景mosaic通过把四张图拼在一起的方式让模型每轮迭代能看到更多小目标对小物体检测很有效。不过mosaic也会把不同包裹的内容硬拼到一张图里导致部分目标重叠、截断这个就靠模型自身的鲁棒性去消化了。如果你的数据集中已经有很多密集目标mosaic的作用就是锦上添花开着问题不大。另外水平翻转这个增强我建议保留因为安检物品入包方向随机左右镜像对模型来说应该是等价形态。色彩抖动那块我觉得可以关闭X光图像本来就没有丰富的自然色彩过度调整亮度色相反而可能把材质差异抹平。5. 实测效果检测精度、速度表现与典型失败案例5.1 指标表现mAP和推理速度用YOLOV5s在100个epoch训练完成后验证集上的整体结果是mAP0.5: 0.873mAP0.5:0.95: 0.618推理速度单张640x640输入3090显卡约12毫秒用YOLOV5m在同一批数据上训练mAP0.5提升到0.901mAP0.5:0.95提升到0.655推理速度约20毫秒。这个结果说明在算力允许的情况下YOLOV5m更值得推荐。如果你要部署到边缘设备或者实时管线里YOLOV5s反而更实用毕竟漏检率受速度限制的影响较小。分类别来看手机、钥匙、打火机这三个类别的AP最高基本都在0.9以上。原因很好理解样本量最多形状相对规整而且这些物品在图中一般面积较小但边界清楚。剪刀和刀的AP稍微低一些主要是形状多变折叠刀和直刀的轮廓差异大模型要学出刀的抽象概念难度更高。最低的是枪支和雨伞枪支主要是因为样本少雨伞则是因为折叠和展开两种形态在外观上差异太大像是两个不同的类别。这个类别人均表现我在下面的表格里列出来类别AP0.5knife0.85scissors0.82lighter0.94gun0.71phone0.96wallet0.92keys0.95bottle0.87laptop0.88umbrella0.785.2 典型失败案例重叠密集场景下的漏检我重点分析了一个很典型的失败案例一张包里同时塞了笔记本电脑、一个水瓶、一把雨伞和一把刀四样东西几乎叠在同一个区域。模型最终只检测出了笔记本电脑和水瓶雨伞被漏掉了刀则被误检成剪刀。问题的根源有两个层面。一是重叠导致的遮挡太严重雨伞的伞骨在X光投影面上和笔记本电脑的金属框架几乎全部重叠模型看到的是两种纹理的叠加很难分割出独立目标二是刀和剪刀这两个类别本身形状接近都是一个长条状物体加一个把手在低分辨率和重叠污染的图里模型会把刀的轮廓特征错误匹配到剪刀上。要缓解这个问题一个直观的思路是把输入尺寸调大比如把--img从640调整到1024给模型更多像素去分辨细节。我在1024输入下重新测试雨伞的AP从0.78提升到0.83刀和剪刀的混淆也减少了。第二个思路是给这类困难样本分配更高的损失权重比如在损失函数里对重叠区域的目标框加权这个做法实现起来稍微复杂一些需要改动YOLOV5的损失计算逻辑但对于追求极致精度的项目是值得的。5.3 误检情况模型把非目标物体当成违禁品另一个观察是误检主要集中在金属物品上。因为X光图像里金属的高亮特征太明显模型很容易把钥匙串、硬币、金属拉链这些没有标注过的物品误判为打火机或刀。比如一枚纽扣电池在投影图上呈现为一个小亮圆跟打火机内部结构有点相似就被模型高置信度检成了打火机。这说明模型并没有真正学会打火机的结构特征而是在学图片里有个高亮的小圆点这个表象。想要压制这类误检一种办法是增加负样本训练让模型学哪些高亮物体不是目标。我在训练集中加入了几百张没有目标或者只含拉链、硬币、钥匙扣等干扰物的空包X光图但标注文件是空的让模型接触这类负样本后误检数明显下降。你可以用下面的简单命令直接测试训练好的模型看看在真实图片上的效果python detect.py \ --weights runs/train/security_xray/weights/best.pt \ --source /data/security_xray/images/val/010001.jpg \ --conf-thres 0.25--conf-thres的默认值通常是0.25如果你发现误检比较多可以适当调高到0.35或0.4。不过调高阈值会同时降低召回率需要在具体场景里做平衡。6. 反复踩过的坑和针对性的解决思路6.1 类别不平衡样本少的类别AP上不去我前面提过数据集中手机、钥匙、打火机样本多而枪支、雨伞、剪刀这几个类别样本少。直接用原始数据训练小样本类别的AP明显低于大样本类别而且容易在训练中期卡住不再提升。对此我试了几种方法比较有效的是给少样本类别加权重。YOLOV5本身没有直接的类别权重参数但可以在训练前用数据配置文件里添加一个可选的weight字段或者用代码给损失函数按类别加权。更简单的办法是在训练时开启--cls 0.5参数调节分类损失权重同时用图像级复制增强——把少样本类别的图像复制几份随机增强后混入训练集。我在保持epochs不变的前提下把含有枪支的图像复制了两遍AP从0.71提高到0.76效果比调损失权重更明显实现也简单。6.2 X光图像通道与灰度问题不少安检机输出的原始图像是14位灰度图而YOLOV5训练时默认以3通道RGB图像为输入。如果你直接把一个单通道灰度图喂进去可能在数据集加载阶段就报错或者在训练时模型看到的是伪三通道重复图像效果不受影响但会浪费一点计算量。遇到这种情况推荐在整理数据集时统一做一次转码把灰度图转成三通道图像再保存。我写了一个简单的批量处理脚本在数据集打包前就处理完了from PIL import Image import os def convert_gray_to_rgb(src_dir, dst_dir): os.makedirs(dst_dir, exist_okTrue) for fname in os.listdir(src_dir): if not fname.lower().endswith((.jpg, .jpeg, .png)): continue img Image.open(os.path.join(src_dir, fname)) if img.mode ! RGB: img img.convert(RGB) img.save(os.path.join(dst_dir, fname))还有一点如果你的图像是16位PNG格式PIL默认可能无法直接读取建议提前转成8位JPG或PNG。我整理数据时就遇到过一批16位PNGYOLOV5训练过程中直接报image file is truncated或cannot identify image file。后来用OpenCV批量读取再转存为8位JPG问题就解决了。6.3 重叠密集场景中的NMS与重叠抑制问题YOLOV5默认的NMS阈值是IoU为0.45。在普通场景里这个值比较合理它能帮模型去掉重复框。但在安检X光场景里物品之间的实际IoU可能本身就很高比如钥匙串里的多把钥匙、叠放的雨伞和背包它们的边界框天然大面积重叠。模型检测出的两个真实目标框因为IoU超过0.45被NMS当成同一个目标直接抑制掉一个这就造成了漏检。我在训练后推理阶段专门把NMS的IoU阈值下调到0.3发现重叠场景下的检出数量明显增加。虽然也出现了一些冗余框但整体漏检率下降。如果你用的是YOLOV5的detect.py可以直接修改推理代码里的iou0.45为iou0.3。如果自己接后处理也建议把这一项纳入调参范围。6.4 标注噪声与标签清洗X光图像因为透视重叠人工标注的边界框质量参差不齐。我做过一次标签抽查发现大约有2%的标注框存在明显问题比如框住了部分无关背景、框的中心点有偏移、或者把一个目标框得过大把相邻目标也包了进去。这些噪声对训练的影响比我想象中更大——尤其是中心点偏移会直接干扰anchor的正负样本分配。我的清洗方法是写脚本统计每个框的宽高比和面积分布找出异常值人工复核。比如正常情况下刀类的宽高比大约在3到8之间如果出现一个宽高比接近1的刀框那多半是标错了。面积异常大的框也值得检查。这个清洗过程虽然耗时但能明显提升最终模型的精度。我清洗掉约1%的问题标签后mAP0.5提升了约0.6个百分点。6.5 空间隔离与数据泄露带来的假象我在划分训练集和验证集时特意避免了同一个包裹出现在两个子集中但实际整理过程中有的包裹从不同角度、不同速度扫描出来的图像非常多。如果我不做空间隔离验证集上可能会出现接近100%的mAP给你一种模型已经完美的错觉等你拿到真实安检机上一测精度立刻掉回去不少。所以我也建议你不管是在这个数据集上继续微调还是在自建数据集上训练划分数据时一定要按扫描场景分组而不是按图像文件随机切分。具体操作很简单先给每个包裹分配一个唯一ID把该ID下的所有图像归到同一个子集再做随机划分。这个细节决定你的验证指标到底有多少参考价值值得多花几分钟处理。如果你打算拿这个数据集做起点我个人的建议是先跑通标准训练流程把baseline精度记录下来然后再针对你的实际场景去调输入尺寸、增强策略和置信度阈值。安检X光检测跟通用目标检测有个很大的不同——你最终关心的往往不是所有类别的平均精度而是那几种违禁品的召回率。宁可误检多一点也不能让刀和枪支从眼皮底下溜过去。这个取舍要在数据处理和阈值设置阶段就想清楚。本文还有配套的精品资源点击获取