ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

700张VOC传送带破损数据集:从标注转换到YOLOv8训练实践

2026/9/28 5:49:45 拓冰建站 浏览量
700张VOC传送带破损数据集:从标注转换到YOLOv8训练实践 简介一份基于PASCAL VOC格式的传送带皮带破损检测数据集面向计算机视觉与工业质检方向的学习者和研究者用于输送带表面裂纹、撕裂等缺陷的检测模型训练。资源包共1682个文件包含700张JPEG图像与700个对应XML标注文件另有备份及说明文件压缩后约68.16MB目前已有140人学习。数据集按7:2:1划分为训练集、验证集与测试集标注信息涵盖损伤区域边界框及类型特征可直接用于YOLO、SSD等目标检测算法的训练与评估。借助该数据训练的模型可提升皮带损伤识别的精度与响应速度降低人工巡检成本适合作为课程设计、毕业设计或工业视觉入门项目的实验数据。1. 为什么是 700 张 VOC 数据集传送带破损检测的落地起点传送带皮带出了裂纹、撕裂、孔洞靠人在现场巡检一个大长皮带廊走下来十几分钟眼睛看花也未必能发现细小的纵向划伤。做工业视觉的人拿到这类需求第一反应不是训练多大的模型而是手头有没有贴好标签的数据。这份基于 VOC 格式的 700 张传送带皮带破损检测数据集恰好补上这个缺口——它不追求样本数量夸张而是把工业现场常见的破损形态、光照变化和皮带纹理背景都收进去了适合个人学习完整目标检测流程也适合验证 YOLOv5、YOLOv8 在皮带表面缺陷检测上的可行性。对于正在学习 VOC 格式怎么转 YOLO 格式、数据集怎么划分、破损检测这类小目标和小样本问题怎么处理的人来说这套数据够你跑通全流程也能让你踩一遍真实标注数据常见的坑。它的数据标注形态是 Pascal VOC 的 XML 格式也就是每一张JPEGImages里的原图都能在Annotations目录下找到同名 XML 文件里面记录了破损目标的类别名称和矩形框坐标。后面用 YOLO 训练的时候需要把这套 VOC 标注转换成 YOLO 的归一化 txt 格式正好可以拿它练手转换脚本。下面直接拆开看这份数据集的内部结构。2. 拆开这份数据集目录结构、标注分布与样本质量2.1 VOC 目录结构JPEGImages、Annotations、ImageSets/Main 各装什么拿到手先看目录VOC 格式的数据集通常长这样这份皮带破损数据也不例外datasets/ ├── JPEGImages/ │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... ├── Annotations/ │ ├── 000001.xml │ ├── 000002.xml │ └── ... └── ImageSets/ └── Main/ ├── train.txt ├── val.txt └── test.txtJPEGImages存放原始图像命名一般是六位数字从 0 开始补零Annotations存放同名的 XML 标注文件里面用object标签逐个描述目标ImageSets/Main下的 txt 文件只写图片文件名不带扩展名用来划分训练集、验证集和测试集。XML 标注核心结构长这样annotation filename000125.jpg/filename size width1920/width height1080/height depth3/depth /size object nametear/name bndbox xmin640/xmin ymin312/ymin xmax702/xmax ymax374/ymax /bndbox /object /annotation注意size里的宽高是必须的字段后面写 VOC 转 YOLO 脚本时要靠它做归一化。如果发现某张图的 XML 缺失size或者宽高填了 0那这张图在转换阶段一定会生成错误的坐标属于需要先排查的病态样本。2.2 标注类别与目标尺寸分布破损检测为什么难浏览这 700 张图的标注会发现破损类别并不单一常见的有几类撕裂tear、划伤scratch、孔洞hole、磨损wear。如果你拿到的版本只标了一个defect大类那对做个人学习反而是好事——类别少模型好收敛先跑通流程比纠结多分类更重要。从目标框尺寸看破损检测的典型难点暴露得很明显大量标注框的宽高只有几十像素相对 1920×1080 的输入图像占比极小。比如一个高 62 像素、宽 92 像素的撕裂区域在整张图里只有 0.3% 左右的面积这给目标检测模型带来的问题是下采样倍数过大时小目标的特征可能在后几层 feature map 上只剩一个点。用 YOLOv8 默认的 640×640 输入训练时这些目标需要特别留意。标注质量方面工业数据集常见的毛病这里也能看到个别 XML 里目标框边缘明显比缺陷实际边界大一圈把皮带背景里的纹理噪点包了进来还有的图出现xmin大于xmax的倒置坐标这属于标注工具手误转换脚本里不预处理就会得到负数宽高。2.3 数据分布检查先统计再动手我拿到任何数据集的第一件事是先写一段脚本统计类别数量和框尺寸而不是直接开训。这一步能避免训练到一半才发现某个类别只有个位数样本。import os import xml.etree.ElementTree as ET ann_dir Annotations stats {} small_boxes 0 total_boxes 0 for xml_file in os.listdir(ann_dir): tree ET.parse(os.path.join(ann_dir, xml_file)) root tree.getroot() for obj in root.iter(object): name obj.find(name).text stats[name] stats.get(name, 0) 1 bbox obj.find(bndbox) w float(bbox.find(xmax).text) - float(bbox.find(xmin).text) h float(bbox.find(ymax).text) - float(bbox.find(ymin).text) total_boxes 1 if w * h 32 * 32: small_boxes 1 print(类别分布:, stats) print(小于32x32的框占比: {:.1%}.format(small_boxes / total_boxes))这段脚本把类别频次和小于 32×32 像素的小目标占比一起打印出来。如果某个类别占比极低需要在训练时考虑类别权重如果小目标占比超过一半那输入分辨率就不能设 512 以下。我一般会在这一步把明显异常的 XML 过滤掉比如宽高为负或 bndbox 缺失的宁可少几张图也不要让坏样本干扰训练。3. 把 VOC 转成 YOLO 格式一张图一个 txt 的完整脚本3.1 转换脚本XML 解析与归一化坐标计算VOC 的 XML 记录的是像素绝对坐标而 YOLO 需要的是一行class_id cx cy w h的归一化坐标中心点坐标和宽高都除以图像宽高。转换逻辑本身不难难在边界情况处理。下面是我常用的转换脚本import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, label_dir, classes): tree ET.parse(xml_path) root tree.getroot() filename root.find(filename).text img_w float(root.find(size/width).text) img_h float(root.find(size/height).text) out_lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) if xmax xmin or ymax ymin: print(f跳过异常框: {xml_path} - {name}) continue cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h bw (xmax - xmin) / img_w bh (ymax - ymin) / img_h out_lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) if out_lines: out_name os.path.splitext(filename)[0] .txt with open(os.path.join(label_dir, out_name), w) as f: f.write(\n.join(out_lines)) if __name__ __main__: ann_dir Annotations label_dir labels os.makedirs(label_dir, exist_okTrue) classes [tear, scratch, hole, wear] for xml_file in os.listdir(ann_dir): if xml_file.endswith(.xml): convert_voc_to_yolo( os.path.join(ann_dir, xml_file), label_dir, classes )逻辑说明classes列表的顺序决定最终生成的 class_id这个顺序必须和后面训练用的data.yaml里的names保持一致否则会出现模型的类别输出和实际语义错位。脚本里对xmax xmin或ymax ymin的异常框做了跳过处理这是工业标注数据里经常出现的脏数据。归一化公式是中心点横坐标(xmin xmax) / 2再除以图宽宽高同理保证输出值落在 0 到 1 之间。参数说明img_w和img_h必须从 XML 的size节点读取用实际图像尺寸而不是默认 1920×1080因为可能有部分图没有按统一分辨率采集。classes的顺序一旦确定就不要改动训练中断后续训时改了顺序之前的损失曲线和模型权重语义全部作废。3.2 数据划分随机打乱与按皮带分组是两种策略VOC 原版数据集通常用ImageSets/Main下的 train.txt、val.txt 直接指定哪些图参与训练、哪些图参与验证。如果你按文件顺序取前 70% 做训练、后 30% 做验证大概率会遇到验证集 mAP 虚高的问题。原因在于同一批传送带皮带拍摄的连续帧背景、光照、纹理高度相似相邻编号的图被同时分进训练集和验证集模型相当于“背题”了。import os import random import shutil image_dir JPEGImages label_dir labels train_ratio 0.8 random.seed(42) all_images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(all_images) split_idx int(len(all_images) * train_ratio) train_files all_images[:split_idx] val_files all_images[split_idx:] def write_split(file_list, split_name): with open(f{split_name}.txt, w) as f: for name in file_list: stem os.path.splitext(name)[0] f.write(stem \n) write_split(train_files, train_split) write_split(val_files, val_split)随机打乱能解决顺序偏差问题但不能解决数据泄露问题。如果你的数据源记录里能查到每张图属于哪一段皮带、哪一次拍摄更稳妥的做法是按皮带分组划分把同一条皮带的所有图片全部放进训练集或验证集避免同源数据同时出现在两端。这个细节在工业检测里是生死线因为现场测试时模型见到的是全新皮带不是训练集里出现过的纹理组合。3.3 转换后的验证标注可视化这一步不能省转完格式后别急着训练先用可视化脚本把 YOLO 的 txt 标注画回图上抽检 30 张左右。做法是把归一化坐标乘回图像宽高画矩形框肉眼确认框是否贴近缺陷边缘。这一步能发现两类问题坐标归一化除错了尺寸造成框整体偏移以及原始 XML 里类别名和图像内容对不上。我曾遇到过一份资料里 XML 标注的是整张皮带区域而不是破损位置转换时没查训练后模型学到的不是“破损长什么样”而是“皮带长什么样”结果自然完全失效。4. 训练配置与迁移学习把 YOLOv8 跑起来并看到 loss 下降4.1 准备 data.yaml 与超参数设置YOLOv8 训练自己的数据集需要一份data.yaml指向划分好的图片目录和类别定义。以这份 700 张皮带数据为例配置如下path: ./belt_datasets train: images/train val: images/val nc: 4 names: [tear, scratch, hole, wear]nc是类别总数names的顺序必须和转换脚本里的classes列表严格一致。如果只标了一类破损nc就写 1names写[defect]。训练命令yolo train modelyolov8n.pt datadata.yaml epochs100 imgsz640 batch8 device0参数说明yolov8n.pt是 Nano 版本的预训练权重在工业小数据集上先用小模型验证流程是划算的跑通后再换yolov8s或yolov8m提精度imgsz640是 YOLOv8 默认输入尺寸如果检测目标普遍小于 32×32 像素考虑提高到 800 或 960代价是显存占用上升batch根据显存调整8GB 显存跑yolov8n可以设 8 到 16跑yolov8m建议降到 4。训练中断后接续训练用resumeTrue它会自动读取runs/detect/train/weights/last.pt。4.2 训练过程监控loss 下降别只看数值训练开始后重点看三张曲线train/loss、val/loss和metrics/mAP50。如果train/loss前 30 轮持续下降但val/loss不降反升这是过拟合的信号需要提前终止而不是等满 100 轮。如果mAP50从一开始就是 0 且始终不动先去查标注转换是否出错常见情况是全部 txt 里只有 class_id 没有坐标或者图片路径没对上导致模型没读到任何标注。yolo train modelyolov8n.pt datadata.yaml epochs100 imgsz640 batch8 patience20patience20表示验证集指标连续 20 轮不提升就自动早停能省下不少时间。我一般会在 50 轮左右看一次验证集的可视化预测结果确认模型确实在学破损特征而不是在学背景纹理。4.3 断点续训与 fine-tune 策略如果训练在 87 轮中断了命令行加resumeTrue就能续上。需要注意的是断点续训后epochs是剩余轮数而非总轮数这一点容易混淆。对小数据集做 fine-tune 时还有个实用技巧先用较低分辨率预训练 50 轮让模型收敛再用高分辨率接着训练 30 轮做微调这比直接用高分辨率从头训练收敛更快且对小目标检测更友好。5. 避坑与常见问题训练破损检测时的五个翻车场景5.1 类别名大小写不一致导致标注全部丢失现象转换脚本运行无报错但生成的 labels 目录下大量 txt 文件为空。 原因原始 XML 中类别名有的写tear有的写Tearclasses列表里只有小写tear转换时name not in classes把大写样本全跳过了。 解决转换时统一转小写进行匹配。我写脚本时会在name obj.find(name).text.strip().lower()处理一次保证大小写不影响结果再在转换后统计一下有效 txt 数量和原始 XML 数量是否对得上。5.2 单类别占比失衡模型输出偏向多数类现象训练完成后 IoU 为 0.5 的 mAP 看起来不错但单独看撕裂类别的召回率只有 30%大量破损样本被漏检。 原因数据集中某一类别占到了 7 成以上模型学到了“大多数框是这一类”的捷径锚框分配时本就偏向多数类。 解决在data.yaml之外给训练加类别权重用yolov8n.pt做迁移学习时固定前几层特征提取器只微调检测头减少多数类的梯度主导效应。另外可以给少数类做离线复制粘贴增强把破损目标裁下来随机贴到空背景区域扩出更多有效样本。5.3 小目标被当成背景噪声现象255 轮训练loss收敛正常但验证集里小于 32×32 的破损目标基本检不出来。 原因破损区域面积占比过小在 640×640 输入下经过 5 次下采样特征图上的响应已经和噪声无法区分默认锚框尺寸对这样的小目标覆盖不足。 解决把imgsz提高到 960同时尝试把标签里的框按中心点切图——将 1920×1080 原图切成四个 960×960 区域分别训练和推理。这么做小目标在输入中的相对尺寸放大了 1.5 倍代价是需要处理切边处目标被截断的问题。5.4 训练集验证集划分不当导致指标虚高现象训练时mAP50轻松到 0.95现场用新拍的照片一测直接漏检一片。 原因按文件序号取前 70% 做训练同一皮带的连续帧在训练集和验证集里同时出现模型记住的是画面纹理而不是破损特征。 解决按皮带编号或拍摄时间分组做划分组为单位。优先用GroupShuffleSplit的做法保证同一个数据源的图像只出现在一个集合里。这也是工业视觉项目里最容易被忽略的划分原则反映出来的分数有误导性。5.5 标注框过松边界模糊目标学不准确现象训练 loss 抖动大测试时预测框比缺陷实际边界大一圈重叠度算不高。 原因标注破损边界时不同标注者对“哪个像素算破损边缘”理解不一致导致同一类目标框的形状方差大模型难以收敛到稳定边界。 解决做法是统一标注规范——横纵撕裂以裂纹末端为边界磨损以颜色突变处为边界孔洞以连通区域外接矩形为准。如果你是使用者注意这份数据集里如果标注松弛就需要在训练时加强数据增强里的随机缩放和裁剪让模型对边界模糊不那么敏感。6. 验证与进阶用混淆矩阵和现场样本做最终检验训练完成后先不要急着拿着best.pt去答辩或上线。第一步是看runs/detect/train目录下的confusion_matrix.png和results.png混淆矩阵能直接告诉你哪两类容易互相误判——在皮带破损检测场景里划伤和撕裂经常混在一起因为从二维图像看细长的撕裂和深划伤的外形几乎一致。如果误判集中在相邻类别上且现场允许可以把这两个类别合并成一个defect类重新训练换来更高的整体召回率。第二步是用验证集之外的样本做推理验证命令很简单yolo predict modelruns/detect/train/weights/best.pt source./test_images imgsz640 save_txtTrue我习惯把现场拍的夜间光照照片和不同角度照片单独放一个文件夹跑推理而不是只在验证集上报告数字。工业检测模型过不过关不看训练集指标看的是从没见过的工作环境照片能不能稳定检出。如果现场光照偏暗推理时把对比度做一次自适应增强再送进模型比重新训练便宜得多。进阶用法上如果你最终要把这套流程放到实际工况里考虑用 TensorRT 把best.pt导出成 engine 格式做推理加速单张 1920×1080 图的耗时能从 12 毫秒降到 4 毫秒左右导出时注意imgsz要和训练时的输入尺寸一致否则目标检测框的坐标会整体偏移。从那以后我每次拿到一份 VOC 数据集都强制自己按这套流程走一遍先统计类别分布再写转换脚本划分时按数据源分组训练前用可视化抽检三个环节确认标注没问题最后用现场照片收尾验证。你按这个顺序操作这份 700 张皮带破损数据集的价值就能完整发挥出来希望帮到你。本文还有配套的精品资源点击获取