ARTICLE DETAIL

建站实战干货

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

VOC转YOLO实战:110张图训练YOLOv8皮带异物检测模型

2026/10/7 9:06:48 拓冰建站 浏览量
VOC转YOLO实战:110张图训练YOLOv8皮带异物检测模型 简介这份数据集定位为流水线皮带传送带场景下的异物检测训练资源面向计算机视觉初学者、工业质检算法开发者以及需要快速上手YOLO/VOC格式数据处理的用户。资源共110张jpg现场图像对每张图均提供Pascal VOC格式的xml标注和YOLO格式的txt标注类别唯一为anomaly框数总计191个全部由labelImg以矩形框形式精确标出便于直接替换进主流检测项目进行训练或验证。压缩包内共332个文件包括110张jpg、110个xml和112个txt其中110个为标注文件整包仅17.76MB体积小巧、结构清晰适合小样本实验和格式学习。目前已有391人学习下载。数据集不包含分割路径也未对模型精度作任何承诺但标注逻辑统一、类别单一可帮助读者高效完成数据准备、标注格式转换、模型迭代测试等环节是一份可直接上手的工业异物检测入门数据集。1. 流水线皮带异物检测数据集110张图能做什么不能做什么工业现场里皮带传送带上的异物检测是视觉落地的高频场景矿石里的铁块、食品线上的包装残片、物流分拣线上的掉件都要靠检测系统在高速运动时框出来。这个叫“流水线皮带传送带异物检测数据集VOCYOLO格式110张1类别.zip”的压缩包我第一眼看到的是数字——110张图、1个类别也就是工业界最常见也最尴尬的“稀缺样本”开局。它不是那种能直接喂给大模型炫耀精度的数据集而是用来验证“这条产线能不能用视觉检出来”的种子数据。它做对了三件事用VOC和YOLO双格式标注省去了自己写格式转换的麻烦标签只有1类对新手友好不用纠结多类别的平衡问题110张图虽然少但足以跑通“数据准备→训练→评估→设备部署”的完整链路。适合谁刚接手工业视觉项目、想在几天内出一个可讨论的方案、或者拿来做算法预研的工程师。对于已经有海量现场数据的老手它的价值更多是当作格式范本和训练基线。下面直接拆压缩包看看里面到底有什么以及怎么用最短路径把它变成能跑起来的检测模型。2. 拆开压缩包VOC与YOLO两套标注怎么对应文件结构先摸清2.1 VOC格式的目录约定与XML标注长什么样解压后你会看到典型的VOC数据集骨架。根目录下一般有三个文件夹JPEGImages放原始图片Annotations放XML标注文件ImageSets里通常会有一层子目录Main里面放着train.txt、val.txt这样的文件划分。110张图不多所以很有可能只有一个trainval.txt和test.txt甚至直接没有划分文件——这种需要自己按比例切。先别急着训练花十分钟把目录结构看清楚因为后面YOLO格式的数据集目录跟它是两套体系混着用会出大问题。打开任意一个XML文件核心内容长这样annotation folderJPEGImages/folder filenamebelt_001.jpg/filename size width1920/width height1080/height depth3/depth /size object nameforeign_body/name bndbox xmin385/xmin ymin260/ymin xmax712/xmax ymax489/ymax /bndbox /object /annotation这个XML里有三个关键信息图片文件名和尺寸、目标类别名foreign_body、以及边界框的左上角(xmin, ymin)和右下角(xmax, ymax)。注意这里的坐标都是像素绝对值基于原图分辨率。类别就一个但名字得记住后面YOLO的类别文件和训练配置里需要完全一致。如果多个XML里的类别名不一致比如有的写foreign_body有的写ForeignBody训练时会直接报错或者生成空标签这类坑非常常见。2.2 YOLO格式的txt标注与类别映射以及两者坐标换算再去看YOLO格式的目录常见的是images/train、images/val、labels/train、labels/val这样的划分。每张图片对应一个同名的txt文件里面每一行代表一个目标框格式为class_id x_center y_center width height。这里的坐标不是像素值而是除以图片宽高后的归一化值取值范围0到1。类别ID从0开始计所以如果只有1个类别那么那行永远以0开头。VOC和YOLO之间必须做一次坐标换算。假设XML里是xmin, ymin, xmax, ymax图片宽高是W, H换算公式为x_center ((xmin xmax) / 2) / Wy_center ((ymin ymax) / 2) / Hwidth (xmax - xmin) / Wheight (ymax - ymin) / H下面这段脚本可以把VOC的Annotations批量转成YOLO的labels顺便把图片划分也按比例切好import xml.etree.ElementTree as ET import os import random def voc_to_yolo(xml_path, class_names, output_dir): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bndbox obj.find(bndbox) xmin int(bndbox.find(xmin).text) ymin int(bndbox.find(ymin).text) xmax int(bndbox.find(xmax).text) ymax int(bndbox.find(ymax).text) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_path os.path.join(output_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(txt_path, w) as f: f.write(\n.join(lines)) class_names [foreign_body] # 必须与XML中的name一致 for xml_file in os.listdir(Annotations): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(Annotations, xml_file), class_names, labels)这个脚本逻辑很直白先读XML里的图像宽高再遍历所有object算出归一化中心坐标和宽高写进txt。注意class_names列表的顺序决定了类别ID后续训练配置里的names列表必须和这里完全一致否则预测出来类别名对不上。random导入在这里没用上实际切分图片时别忘了用random.seed(42)固定随机种子保证每次划分一致方便复现结果。2.3 一个坑VOC的ImageSets和YOLO的train/val目录怎么对齐VOC体系习惯用ImageSets/Main/train.txt这种记录图片名的文本划分而YOLO体系更多直接按目录划分images/train下的图就是训练集images/val下的就是验证集。两者逻辑不同。如果你的压缩包里VOC部分的train.txt写的是belt_001这种不带扩展名的名字而YOLO目录里已经有一套images/val和labels/val你得检查两套划分是否一致。不一致时以YOLO目录为准因为训练脚本只认目录。我一般会写个小脚本对照两边的文件名集合确保没有漏图、没有标签缺失。3. 从数据集到能跑的模型用YOLOv8在110张图上训练的最小流程3.1 准备数据集目录与YAML配置文件拿到双格式数据集后训练前必须整理成YOLO工具链能识别的目录树。常见做法是新建一个dataset文件夹里面放images/train、images/val和labels/train、labels/val注意必须是两个子目录YOLOv8不认单独的images和labels平级目录。然后把图片和txt标签按划分放进去。检查每张图都有对应的txt以及每个txt里的坐标范围都在0~1之间这一步别偷懒110张图也就几百个框手工抽查都来得及。接下来写一个数据集YAML这是训练时的地图。内容如下path: ./dataset train: images/train val: images/val names: 0: foreign_body注意path可以是相对路径相对于你执行训练命令的终端位置也可以是绝对路径但绝对路径换机器以后要记得改。train和val是相对于path的子路径写images/train而不是train/images。names从0开始和txt标签里的class_id一一对应。如果忘了写val训练脚本会默认从train里分一部分出来定向验证集就失效了mAP会虚高。3.2 训练命令与关键参数imgsz、epochs、batch、早停在命令行执行训练。用YOLOv8的CLI最简单的命令是yolo detect train databelt.yaml modelyolov8n.pt epochs100 imgsz640 batch16 patience20 project./runs namebelt_baseline说下每个参数的实际意义。imgsz640表示把输入图片缩放到640x640送入网络。你的原始图可能是1920x1080直接缩放会把小目标变小如果皮带上的异物本来就只有几十个像素建议用imgsz960或者更高但代价是显存和推理速度。epochs100对110张图来说绰绰有余因为模型大概率在30轮以内就过拟合了。batch16在显存允许的情况下尽量大小数据集上batch太小容易震荡但batch过大又会加剧过拟合16是折中。patience20是早停如果连续20轮验证集mAP不涨训练自动停止这几乎是小数据集的救命配置——不用傻等100轮跑完。如果你电脑显存只有6G把batch调到8或4imgsz调到640照样能跑只是慢一点。训练过程中重点看损失曲线box_loss和cls_loss应该持续下降而val/cls_loss如果下降后又反弹说明开始过拟合了早停会用得上。训练结束后模型权重存到了runs/belt_baseline/weights/best.pt。在验证集上看效果yolo val modelruns/belt_baseline/weights/best.pt databelt.yaml batch1 imgsz640这里batch1是为了看清每张图的具体预测结果如果直接跑默认batch输出的只是指标表。验证后会生成confusion_matrix.png和很多样例预测图多翻翻这些图别只看mAP数字。3.3 用训练好的模型跑一张测试图确认输出对味模型训练完先拿一张没参与训练的图跑推理确认输出格式和类别名没问题yolo predict modelruns/belt_baseline/weights/best.pt source./test_images/foreign_01.jpg conf0.5 saveTrueconf0.5表示置信度阈值低于这个阈值的框不画。现场用的阈值一般会调低到0.25甚至0.2因为漏检比误检的代价往往更高。saveTrue会把标注后的图存到runs/predict/下。如果预测框完全乱飘先别怀疑模型能力回头检查数据集不是更实在看看标签坐标有没有错位、类别名有没有大小写不一致、有没有一张图里框了背景区域。工业小数据集出问题八成是标签的问题。4. 小数据集的四个必踩坑过拟合、漏检、类别不平衡、标注噪声4.1 过拟合训练mAP很高验证mAP惨不忍睹现象训练集上mAP能到0.99验证集上只有0.5左右损失曲线里train/box_loss一路向下val/box_loss却掉头向上。原因很简单110张图反复看几十轮模型把训练图的背景纹理、光照分布都背下来了换个角度就认不出来了。解决的办法有几个按优先级排序第一扩大数据集哪怕用数据增强撑到三四百张第二给模型“减负”从yolov8n换成更小的yolov8n或者加上更强的dropoutYOLOv8自带部分正则第三把patience调小到15让早停更敏感别再硬练。另外可以试试cacheTrue让数据加载更快但跟过拟合本身无关。数据增强里最有用的是hsv_h0.015, hsv_s0.7, hsv_v0.4这组HLS扰动能模拟产线不同光照。还有随机平移和旋转degrees10就够了皮带上的异物不会倒着出现转太多反而失真。这些参数在YAML里就能配不用改代码。4.2 漏检小目标异物太小框被当噪声现象皮带上有很小的螺丝垫片宽度只有十几像素训练时模型没学出来推理时直接漏掉。原因有两个一是110张图里这种小目标数量太少正负样本不平衡二是imgsz640下小目标下采样后只剩下几个特征点特征图上的信息几乎丢失。解决路径把imgsz提高到960或1280虽然训练慢但小目标召回率会明显提升同时可以调整anchor虽然YOLOv8用的是anchor-free但特征层输出会受输入分辨率影响。还有一招是裁剪——把原图按皮带区域裁成几段每段单独训练相当于人为放大了异物尺寸。这个做法在工业现场很常见相当于给检测模型配了一个“聚焦区域”。4.3 类别不平衡只有1类但难例和易例失衡“类别不平衡”在单类别数据集里依然存在只不过不是类间不平衡而是“场景不平衡”。比如110张图里90张的异物都是黑色塑料块在深色皮带上对比度很低只有20张是白色金属块对比度高。模型会倾向于学会简单的白色块黑块漏检严重。对策是检查标注里是否覆盖了不同材质、不同颜色、不同遮挡程度的样本如果覆盖不够就去现场多拍。更实际的做法是用class weights或者给难样本做过采样把包含难例的图片在训练列表里重复几遍YOLOv8里可以通过修改数据集加载逻辑实现但更简单的是多复制那几张图到训练目录里并把对应的txt也复制一份。4.4 标注噪声边界框紧贴、遮挡、错标类别现象训练时损失不收敛验证时发现预测框总是比真实框大一圈或者小一圈。原因很可能是标注时的框打得不规范一类是框贴着物体外轮廓打得过紧导致模型学到的是“紧边”另一类是异物一半被皮带边缘遮挡标注员干脆把整个遮挡区域也框进去了还有的是把背景里的反光点当成异物标了。解决逐张检查XML或txt里的坐标用可视化脚本把框画出来看。代码如下import cv2 import numpy as np img_path dataset/images/train/belt_001.jpg label_path dataset/labels/train/belt_001.txt img cv2.imread(img_path) h, w, _ img.shape with open(label_path) as f: for line in f.readlines(): _, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw/2) * w) y1 int((yc - bh/2) * h) x2 int((xc bw/2) * w) y2 int((yc bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imwrite(check.jpg, img)把每张图的标注框画出来扫一遍框偏差一眼就能看出来。我遇到过最普遍的问题是“框打得太紧”同名异物在不同图里框出的宽高差异超过30%模型剪贴板学不出稳定的尺寸特征。这种修正没有捷径只能手动调整XML里那几个坐标值再重新生成txt。110张图不算多半天时间足够全部校正。5. 验证与上线前的事mAP怎么看、视频流实测注意什么训练结束后最忌讳只盯着mAP0.5一个数字。这个小数据集mAP高可能只是因为简单样本多。我一般先看mAP0.5:0.95它更敏感能反映框定位精度。如果前者高后者低说明框位置不准需要检查标注和缩放参数。然后看每类的准确率和召回率以及confusion_matrix.png里背景误检的比例。现场最常出现的情况是静态图测试全过一上视频全漏——因为皮带在运动模糊和动态遮挡让检测框忽大忽小。跑一段现场视频或RTSP流测试命令是yolo predict modelruns/belt_baseline/weights/best.pt sourcertsp://192.168.1.64:554/stream1 imgsz960 conf0.25 saveTrue如果现场机器有TensorRT可以先把模型导出成engine再推理速率能快一倍。但小数据集训练出的模型先别急着优化速度先把误检压下去。我见过一个案例模型在静态图上都很好一到视频就每隔几帧闪一个误检框原因是皮带上的接缝反光训练图里没出现过这种纹理。这种问题在数据层面解决最靠谱专门去现场截取几十帧含反光的背景图标为背景即不加标注框混进训练集里让模型学习“这是背景”。如果不想重新训练就用后处理做“时间平滑”同一位置的框连续出现3帧以上才触发报警能消掉一半闪烁误检。最后说个我的教训。第一次用这种110张的小数据集做交付我直接拿它去训了全分辨率模型结果现场跑得好好的一到换班时新换的灯光色温变了误检率暴增。后来我才养成习惯用HLS数据增强模拟不同光照并在验收单里明确写清楚“当前模型只在训练时的光照范围内有效”。模型的鲁棒性不是一个小数据集能解决的但你可以让这个边界清清楚楚至少不被现场的人指着鼻子说“这检测就是个摆设”。希望帮到你。本文还有配套的精品资源点击获取