
简介一套面向行李箱表面缺陷检测的数据集共650张清晰图片采用VOC与YOLO双格式标注可直接适配YOLO、Faster R-CNN等主流目标检测框架。压缩包共1952个文件包含jpg原图、xml标注和txt标签三类数据分别存放于JPEGImages、Annotations、labels文件夹中整体大小约25.11MB目录结构清晰。标签涵盖damaged与good_condition两种类别总计936个矩形标注框其中缺陷框199个、完好框737个可用于训练行李箱外观缺陷识别模型。图片分辨率清晰且未经增强方便研究者自行进行数据划分或在线增广实验。目前已有126人学习下载适合计算机视觉入门者、工业质检项目开发者以及需要标准格式数据集的算法调优场景。1. 650张行李箱缺陷检测数据集小样本目标检测的现实起点箱包出厂前的表面质检大多靠人工在灯光下找划痕、凹陷如果想让这条产线自动化最直接的方式就是用 YOLO 这类单阶段检测器做目标定位。而这个标题点名的「行李箱缺陷检测数据集」就是训练流程的入口650 张图片、2 类缺陷同时给出 YOLO 和 VOC 两种标注格式。650 张在目标检测里属于典型的小样本不够覆盖产线全部光照和姿态变化却足够跑通「数据怎么变成模型」这条主线。适合三类读者第一次做缺陷检测的工程师、在工业项目里评估小样本可行性的人以及想自己完成 VOC 转 YOLO 流程的算法实习生。下面按拿到 zip 以后的实际处理顺序展开。2. VOC格式与YOLO格式行李箱缺陷标注的两种坐标体系拿到压缩包第一件事不是急着训练而是先搞清楚标注格式。VOC 和 YOLO 虽然描述的是同一个框坐标语义完全不同转换脚本里写错一个除法都能让训练直接发散。VOC 即 Pascal VOC 的标注风格一张图片对应一个 XML根节点 annotation 下有多个 object每个 object 里的 name 是类别bndbox 给出 xmin、ymin、xmax、ymax单位是像素。YOLO 格式则是一张图片对应一个同名 txt每一行由类别编号和归一化后的中心点 x、中心点 y、宽、高组成。2.1 解压后先确认目录结构与标注统计拿到 zip 之后统一解压到一个工作目录先看目录结构再动手。常见做法是用 find 列出两层目录判断 images 与 annotations 是否已经分开unzip 数据集-行李箱缺陷检测数据集650张2类YOLOVOC格式.zip -d luggage_defect cd luggage_defect find . -maxdepth 2 -type d | sort这个命令把压缩包解压到 luggage_defect 目录然后只列出目录节点。如果输出里能看到 images、labels 这类分层说明压缩包已经按训练需要做过整理如果图片、XML、txt 散落在一起后面转换时就要先自己补目录。接着核对文件数量650 张图片应配套 650 个 XML 和 650 个 YOLO 标注文件find . -name *.jpg -o -name *.png | wc -l find . -name *.xml | wc -l find . -name *.txt | wc -l这里把 jpg 和 png 一起统计是因为真实产线数据偶尔会混入不同后缀。若三种文件数量对不上先停下来补齐再继续否则转换脚本会静默丢掉没有标注的图片训练时再报找不到标签文件就难排查了。2.2 两个关键陷阱类别编号从 0 开始与坐标越界VOC 里类别名是可见字符串而 YOLO 训练要求类别编号从 0 开始。650 张、2 类的常见定义是 0 对应划痕 scratch、1 对应凹陷 dent但每个数据集的 name 取值不同必须打开一个 XML 确认后才算数。第二个坑是坐标越界人工标注时很容易把 xmax 或 ymax 标得比图像本身还大直接用这样的值做分母归一化输出就会大于 1YOLO 训练时不报错却会让损失曲线异常。转换脚本里需要统一加一次边界裁剪。常见检查项和处理方式如下检查项判断方法处理建议类别名拼写grep name Annotations以实际字符串为准按出现次数排序xmin 是否小于 xmax遍历 bndbox非法框直接丢弃并记录文件名xmax 是否超出图宽与原图宽高比对裁剪到 w-1避免归一化超过 1空标注文件占比wc -l labels/*.txt空文件保留训练时会自动跳过2.3 用 Python 读一个行李箱缺陷标注看看写批量转换前先用最小脚本解析一个 XML确认 size 字段提供的是真实图像尺寸。标准库 xml.etree.ElementTree 就够用不需要引入额外依赖import xml.etree.ElementTree as ET tree ET.parse(Annotations/000001.xml) root tree.getroot() print(filename:, root.find(filename).text) print(width:, root.find(size/width).text, height:, root.find(size/height).text) for obj in root.iter(object): bb obj.find(bndbox) print(obj.find(name).text, bb.find(xmin).text, bb.find(ymin).text, bb.find(xmax).text, bb.find(ymax).text)逻辑说明iter(object) 会遍历 XML 树中所有 object 节点比 findall(object) 更稳妥因为部分 VOC 变体会把 object 放到嵌套层级。多数标注工具的 bndbox 字段名不变但个别工具会写成 x0、y0、x1、y1解析前先打开一个真实文件确认字段名。这段输出能直接看到两个类别的实际名称和框数量同时为下一章的批量转换准备参数。3. 用 Python 把 650 张 VOC 标注批量转成 YOLO 格式格式转换的难点从来不是公式而是转换结果是否可信。有些标注工具内置 VOC 转 YOLO 功能但针对离线 zip 包用脚本处理更可控整个流程也可以随时复现。下面这个脚本按「一个 XML 对应一个 txt」写入同时处理上一章提到的越界坐标。需要特别强调的是classes 列表的排列顺序就是 YOLO 的类别编号来源一旦确定后不要再随意增删。3.1 转换脚本的完整实现import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, classes): 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.iter(object): name obj.find(name).text if name not in classes: continue cat_id classes.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 边界裁剪把超出图像范围的坐标收回到有效区间 xmin max(0, min(xmin, w - 1)) ymin max(0, min(ymin, h - 1)) xmax max(0, min(xmax, w - 1)) ymax max(0, min(ymax, h - 1)) if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cat_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) if not lines: return 0 out_file os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(out_file, w) as f: f.write(\n.join(lines)) return len(lines) classes [scratch, dent] xml_dir Annotations out_dir YOLOLabels os.makedirs(out_dir, exist_okTrue) total 0 for xml_name in sorted(os.listdir(xml_dir)): if not xml_name.endswith(.xml): continue total voc_to_yolo(os.path.join(xml_dir, xml_name), out_dir, classes) print(converted boxes:, total)代码逻辑xmin 与 xmax 相加除以 2 得到像素中心点再除以 w 得到归一化中心宽高同理。裁剪时用 w - 1 而不是 w是为了避免归一化结果恰好等于 1.0YOLO 官方数据增强对边界值 1.0 的处理不够稳定。裁剪后如果宽高小于等于 0说明原始框本身非法直接跳过。classes 列表若与标注不一致脚本会静默忽略未知类名所以输出 total 比预估少很多时优先检查类名拼写而不是怀疑脚本有 bug。3.2 转换结果的验证与类别顺序核对转换完成后不要直接去训练先用两条 shell 命令验证数据形态。第一看类别编号是否只有 0 和 1第二看归一化坐标是否都在 0 到 1 之间awk {print $1} YOLOLabels/*.txt | sort | uniq -c awk {if ($20 || $21 || $30 || $31 || $40 || $41 || $50 || $51) print} YOLOLabels/*.txt | head第一条命令统计所有 txt 第一列的类别编号按编号排序后应该只有两行分别是 0 和 1。第二条扫描每行的四个数值出现负数或大于 1 就打印出来说明上一章的坐标越界没有完全被裁剪处理。常见的误用是只对图片数量做了校验没有校验坐标范围后期训练 loss 出现 NaN 再回头查就很被动。验证项针对性命令通过标准类别编号合法性awk {print $1} YOLOLabels/*.txt只有编号 0 和 1坐标范围awk 扫描第 2 至第 5 列全部落在 0~1 区间空标签占比find YOLOLabels -name *.txt -empty | wc -l空文件单独记录并确认标注框总数wc -l YOLOLabels/*.txt | tail -1与 XML 内 object 总数一致3.3 顺手把训练集和验证集划分好650 张的小样本训练不需要复杂划分策略但随机种子要固定确保每次实验可复现。下面这段脚本按 85% 与 15% 划分同时把图片与标签同步复制到目标目录避免训练时出现图有标无或标有图无mkdir -p datasets/luggage/images/train datasets/luggage/images/val mkdir -p datasets/luggage/labels/train datasets/luggage/labels/val for f in Images/*.jpg; do name$(basename $f .jpg) if [ -f YOLOLabels/$name.txt ]; then cp $f datasets/luggage/images/all/ cp YOLOLabels/$name.txt datasets/luggage/labels/all/ fi done上面的命令先把全部有效样本汇总到 all 目录再用下面的 Python 脚本按比例拆分按文件名移动会避免 shell 循环重复写目录判断import os, random, shutil img_root datasets/luggage/images label_root datasets/luggage/labels all_imgs [f for f in sorted(os.listdir(img_root /all))] random.seed(42) random.shuffle(all_imgs) split int(len(all_imgs) * 0.85) for part, names in [(train, all_imgs[:split]), (val, all_imgs[split:])]: for img in names: stem os.path.splitext(img)[0] shutil.copy2(os.path.join(img_root, all, img), os.path.join(img_root, part, img)) shutil.copy2(os.path.join(label_root, all, stem .txt), os.path.join(label_root, part, stem .txt))random.seed(42) 让每次运行得到完全一致的划分这在对数据增强或损失函数做对比实验时非常关键。需要留意的是这里没有按类别比例分层抽样如果两种缺陷数量差异超过 3 倍建议改成先按类别分桶、再各自划分避免某一类在验证集里只剩个位数导致 mAP 波动过大。4. 用 YOLOv8 训练行李箱缺陷检测模型数据配置与训练参数格式就绪后进入用 YOLO 训练自己的数据集的真正阶段。这里以 YOLOv8 为例因为它的 CLI 配置足够简单且同一套数据目录和 data.yaml 可以直接沿用到 YOLO11差异只在个别参数名。650 张、2 类小样本有自己的训练策略迁移学习是刚需增强要适度验证集不能分太多。4.1 组织成 images / labels 目录结构YOLOv8 推荐把图片和标签放到同一个父目录下的 images 与 labels 两个平行目录train 与 val 子目录名称必须一一对应。上一章已经把文件复制到 datasets/luggage/images 和 labels 下最终目录结构应符合这种形态datasets/luggage/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml这里 labels 与 images 的子目录命名要一致名称不同会导致 yolo detect train 启动时报数据集路径不存在。常见错误是把标签放成labels/train、图片放成images/val数量对不上时训练不会启动但报错信息容易被忽略。4.2 编写 data.yaml 并保持类别顺序一致data.yaml 是 YOLO 训练的数据描述文件names 顺序必须与上一章转换脚本里的 classes 列表保持一致否则训练出来的模型类别编号会整体错位推理时看到的 label 和实际对象对不上号path: /home/user/luggage_defect/datasets/luggage train: images/train val: images/val names: 0: scratch 1: dentpath 建议写绝对路径不同版本对相对路径的解析基准不一样有的以当前工作目录为基准有的以配置文件所在目录为基准。写好后用一段简单命令验证 YAML 能被正确加载python -c import yaml; dyaml.safe_load(open(datasets/luggage/data.yaml)); print(d[names])常见的错误是把 names 写成 list 类型或漏掉序号这样训练虽不报错但日志里的类别显示和后续可视化都会错乱。4.3 训练命令与参数解读650张小样本训练命令的核心是 model 参数。650 张缺陷数据远不足以从零训练主干网络必须用 COCO 预训练权重做起点。yolov8s.pt 是速度和精度最均衡的选择常用训练命令如下yolo detect train \ modelyolov8s.pt \ datadatasets/luggage/data.yaml \ epochs120 \ imgsz640 \ batch8 \ patience20 \ optimizerAdamW \ lr00.001 \ device0参数含义epochs 给到 120 是留足收敛空间配合 patience20 早停batch8 对 650 张小样本既能提供稳定梯度又不会占满显存。imgsz640 是平衡值行李箱上的细小划痕属于小目标不建议降到 320否则缩放后特征容易丢失。device0 表示使用第一张 NVIDIA GPU没有独立显卡时可以不写 device让它自动落到 CPU但训练时间会明显拉长。训练开始后可以用下面的命令实时观察 yolo 损失函数中的 box_loss、cls_loss、dfl_loss 三个分量tail -f runs/detect/train/results.csvresults.csv 里每个 epoch 一行前几列是训练集三种 loss后续是验证集的 precision、recall、mAP50 与 mAP50-95。如果 box_loss 持续下降但 cls_loss 停滞优先怀疑类别不均衡如果三个 loss 都快速触底则要考虑是标注框太松导致模型学到的边界不紧。参数作用小样本推荐值model预训练权重起点yolov8s.pt 优先batch单次前向图片数显存允许时 8~16patience连续 n 个 epoch 无提升即停10~20lr0初始学习率0.001过大易早停imgsz输入图像边长640小缺陷不宜低于 4805. 训练完成后如何验证行李箱缺陷检测效果验证阶段不要只盯着终端打印的 mAP要拿模型预测结果与原始标注做一次反向比对。这里有个实用技巧把模型当作标注质量检测器用它的漏检和误检反向定位数据集本身的问题650 张的小样本尤其适用。5.1 用 val 命令输出 mAP 与每类指标验证集是 650 张里划分出的约 98 张图片运行以下命令即可得到整体与分类指标yolo detect val \ modelruns/detect/train/weights/best.pt \ datadatasets/luggage/data.yaml输出里 mAP50-95 比 mAP50 更有参考价值。mAP50 达到 0.9 但 mAP50-95 很低说明框的位置不够稳定某个类别 recall 明显低于另一类时先数一下该类在训练集里的样本数大概率是类别不平衡而不是网络结构问题。用 val 而不是 predict 的原因在于 val 会同时计算预测框与真实框的匹配关系输出带坐标的置信度矩阵后续排查不用再自己写匹配逻辑。5.2 用预测结果反查标注错误缺陷检测小数据集里标注质量直接决定模型精度上限。一个高效做法是把验证集预测结果导出为 txt再与原始标注目录做 diff 思路的比对yolo predict modelruns/detect/train/weights/best.pt \ sourcedatasets/luggage/images/val \ save_txtTrue save_confTruepredict 生成的 txt 位于 runs/detect/predict/labels 下文件名与输入图片同名。和验证集原始标注对比后凡是预测里有而原始标注没有的框大概率是当时漏标的缺陷这正是需要补标的样本。把新增补标写回 VOC 原始 XML重新跑一遍第三章的转换脚本再训练一轮这批 650 张行李箱缺陷数据集的可用性才算被真正释放。本文还有配套的精品资源点击获取