☰
布匹缺陷检测数据集:VOC转YOLO格式与YOLOv8训练实战
2026/10/5 12:37:15 网站建设 项目流程

简介:这是一份面向工业视觉质检场景的布匹缺陷检测数据集,包含800张真实布匹图像,覆盖“损坏”“污渍”两类常见缺陷,适用于缺陷识别、目标检测、智能质检等方向的模型训练与效果验证。数据标注精确,VOC(xml)与YOLO(txt)两种标签格式同时提供,可免去格式转换步骤,直接接入主流检测框架使用;同时便于在模型训练前进行数据检查与增强实验。压缩包大小约988MB,共2401个文件,包括800张jpg原图、800个xml标注文件与801个txt标签文件,JPG图像与标签一一对应,目录结构清晰,方便按批次划分训练集与验证集。该数据集已有984人浏览学习,数据质量可靠,标注一致性较好,既适合工业现场快速验证缺陷检测算法,也可作为高校或培训机构的目标检测教学案例。

1. 布匹缺陷检测数据集:800张图能撑起什么样的质检模型

布匹出厂前的质检环节,至今大量工厂还靠人工在验布机前盯屏幕,眼睛盯两小时就开始漏检。这几年不少团队想用视觉模型替代人工初检,但卡在数据上:工业缺陷数据本身少,又不方便公开,能找到的公开布匹缺陷集大多是几百张、标注格式还五花八门。标题里这个800张的布匹缺陷检测数据集,胜在简单直接——包含损坏、污渍两类缺陷,同时给了VOC(XML)和YOLO(TXT)两种标签格式,省去了大部分入门者“拿到数据先折腾格式”的时间。它对两类人最有价值:一是刚接触工业视觉、想跑通全流程的算法工程师,二是已有检测经验、需要快速验证布匹场景可行性的团队。800张不大,但足以让你把数据解析、格式转换、模型训练、问题排查这条路完整走一遍,并且看清楚这类小数据量工业项目的真实边界。

2. VOC与YOLO标签格式差异:从损坏、污渍标注到坐标系的换算

2.1 两种标签格式的核心差别:XML绝对坐标与TXT归一化坐标

VOC格式是目标检测领域的老牌标准,每个图像对应一个同名XML文件,里面用<bndbox>记录目标的绝对像素坐标,<xmin>、<ymin>、<xmax>、<ymax>分别表示左上角和右下角的横纵坐标值。这种格式的优点是可读性强、不依赖具体图像尺寸,用标注工具打开就能直接看到坐标数值;缺点是同一张图在不同分辨率下坐标不能通用,训练前处理需要额外写解析逻辑。

YOLO格式完全不同,每个图像对应一个TXT文件,每行代表一个目标,格式为class_id cx cy w h,其中cx、cy是目标中心点的归一化坐标,w、h是归一化宽度和高度,取值范围都在0到1之间。归一化意味着坐标和原始图像宽高解耦了,无论图片是1920还是640,标注文件都能直接喂给YOLO系列训练框架。但这份“省事”是有代价的——人眼不好直接读懂归一化坐标,排查标签错误时通常得把坐标乘回图片宽高才能定位。

表格对比最直观:

对比项VOC格式YOLO格式
文件后缀.xml.txt
坐标形式绝对像素(xmin, ymin, xmax, ymax)归一化中心点+宽高(cx, cy, w, h)
可读性高,可直接阅读低,需换算回像素
依赖图像尺寸依赖不依赖
训练框架适配需先转成其它格式或写DataloaderYOLO系列直接读取

标题里说的“附voc与yolo格式标签”,本质就是同一批标注的两种不同表达。你需要搞清楚一件事:标注信息没有任何增加或减少,变的只是存储方式和数值含义。

2.2 两类缺陷的类别定义:损坏与污渍的标注边界

这本数据集设定为“损坏”和“污渍”两个类别。在工业布匹质检里,这两个类别的形态差异非常明显:损坏通常表现为破洞、撕裂、断纱,边缘锐利,形状不规则;污渍则是油渍、色斑、水渍,边界模糊,颜色和背景有渐变过渡。但标注边界经常出问题——一个破洞周围伴生油污痕迹,是标损坏还是污渍?一个深色色斑被误认为污渍但其实是织造色差,要不要标?

我处理这类数据集时会按“主缺陷优先”原则:如果一个区域同时有多个特征,按最先导致布匹降级的缺陷类别来标;如果两种缺陷形态同时存在且互不包含,就拆成两个框标注。换到具体操作上,标注损坏框时把边界贴紧撕裂/破洞边缘,标注污渍框时把边界画在颜色和正常布的过渡带上,不要收得太紧,否则训练时模型很难学到污渍的真实范围。

2.3 为什么工业数据集的标签格式必须“双轨制”

这不是读过剩的格式,而是你在实际项目里大概率要遇到的协作场景。标注员用labelImg这类工具产出VOC格式时是效率最高的,因为XML直观、方便人工校验;但到了训练阶段,YOLO系列检测框架读TXT更舒服,Ultralytics YOLO直接要求TXT和图像文件按目录配对,不需要写额外解析器。所以从标注到训练,中间必然有一道“换格式”工序。

你拿到800张数据集之后,第一件事不是急着训练,而是先验证标签文件是否真的和图片一一对应。做法很简单:写一个脚本随机抽几十张图,把XML/TXT里的框画出来叠加到原图上,肉眼确认框的位置、大小、类别ID是否符合实际缺陷。这个步骤也叫“标签可视化验证”,是我每次拿到数据集都先做的检查项,它能直接暴露90%的格式转换和数据匹配问题。

import cv2 import xml.etree.ElementTree as ET # 读取VOC标签并画框 def draw_voc_boxes(image_path, xml_path): img = cv2.imread(image_path) tree = ET.parse(xml_path) for obj in tree.findall('object'): name = obj.find('name').text 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) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) cv2.putText(img, name, (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow('voc_visualize', img) cv2.waitKey(0)

这段代码把VOC格式的标注框直接叠加到原图上,检查时有两点需要重点看:一是框的类别名是否拼接正确,二是框的坐标是否明显偏移。如果发现框整体偏上或偏下,那是标注工具的坐标系问题;如果框和缺陷边缘完全对不上,那可能是图片被压缩或裁剪过而XML里的坐标没有同步更新,这类数据在训练前要直接剔除,不要硬留。

3. 把VOC转成YOLO格式:坐标归一化与目录重组全流程

3.1 最小可用转换脚本:从XML提取坐标到生成TXT

拿到数据集后,你的首要任务是把VOC格式的XML标签全部转成YOLO格式的TXT,并且按YOLO训练要求的目录结构重新组织文件。这个转换过程并不复杂,核心就两步:读XML拿到绝对坐标,除以图片宽高得到归一化结果。

import os import xml.etree.ElementTree as ET # 类别名与ID的映射关系,顺序必须和训练时的data.yaml一致 CLASS_MAPPING = {'damage': 0, 'stain': 1} # 根据数据集实际类名调整 def convert_voc_to_yolo(xml_file, out_dir, img_width, img_height): tree = ET.parse(xml_file) root = tree.getroot() base_name = os.path.splitext(os.path.basename(xml_file))[0] out_txt = os.path.join(out_dir, base_name + '.txt') with open(out_txt, 'w') as f: for obj in root.findall('object'): name = obj.find('name').text if name not in CLASS_MAPPING: continue bndbox = obj.find('bndbox') xmin = float(bndbox.find('xmin').text) ymin = float(bndbox.find('ymin').text) xmax = float(bndbox.find('xmax').text) ymax = float(bndbox.find('ymax').text) # 计算中心点和宽高,再归一化到0~1 x_center = (xmin + xmax) / 2.0 / img_width y_center = (ymin + ymax) / 2.0 / img_height w = (xmax - xmin) / img_width h = (ymax - ymin) / img_height # 防止越界值,YOLO训练时对越界框容忍度低 x_center = min(max(x_center, 0.0), 1.0) y_center = min(max(y_center, 0.0), 1.0) w = min(max(w, 0.0), 1.0) h = min(max(h, 0.0), 1.0) f.write(f"{CLASS_MAPPING[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n")

这个脚本的关键参数有三个:CLASS_MAPPING决定了类别名到数字ID的映射,一旦定下来就不能随便改,因为训练时的data.yaml里类别顺序必须和这里完全一致;img_width和img_height必须从对应的JPG图片实际读取,不能想当然认为所有图片尺寸相同,800张数据里出现一两张不同分辨率的图是常事;最后的min/max限幅是为了防止标注坐标稍微越界导致归一化后出现负值或大于1的值,这类坏数据会让损失函数直接出现NaN。

3.2 图片尺寸的获取与批量处理:不要硬编码分辨率

最常见的翻车点就是这里。很多人图省事,随机打开一张图看了一眼尺寸就写死在脚本里,结果800张图里有30张长宽不一样,转换后的TXT坐标全部错位。正确的做法是逐张读取图片的实际分辨率再转换:

import cv2 import glob xml_dir = 'path/to/Annotations' out_dir = 'path/to/labels' img_dir = 'path/to/JPEGImages' os.makedirs(out_dir, exist_ok=True) for xml_path in glob.glob(os.path.join(xml_dir, '*.xml')): base_name = os.path.splitext(os.path.basename(xml_path))[0] img_path = os.path.join(img_dir, base_name + '.jpg') # 读取实际分辨率,不要硬编码 img = cv2.imread(img_path) if img is None: print(f"Warning: {img_path} 读取失败,跳过") continue h, w = img.shape[:2] convert_voc_to_yolo(xml_path, out_dir, w, h)

代码里加了一个图片读取失败的保护逻辑。别小看这个判断,工业数据集里偶尔会混入损坏的JPG文件,如果不对这种情况做处理,你会在训练到一半时突然被一个“找不到图片或标签”的报错打断。转换完成后,还需要检查一下TXT文件里是否所有行都合法:每行应当是5个由空格分隔的数值,类别ID是整数,四个坐标在0到1之间,如果有空文件或超长行就说明源XML有问题。

3.3 YOLO训练目录的标准结构:images与labels配对

YOLOv8和YOLOv5对数据目录的要求基本一致,都是images和labels两个目录分别存放图片和TXT标签,再通过data.yaml把训练集、验证集的路径指过去。常见的目录组织方式是这样的:

dataset/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ └── ... │ └── val/ │ ├── 0008.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 0001.txt │ │ └── ... │ └── val/ │ ├── 0008.txt │ └── ... └── data.yaml

划分数据时建议按8:2比例切分,而且要保证同一张图的图片和标签永远在同一边。我在做这一步时习惯先把所有文件名打乱,再用固定随机种子划分,确保每个人复现的结果一致。之所以强调固定随机种子,是因为数据划分对训练结果的影响比很多人想象的大——如果某次划分恰好把大部分污渍样本都分到验证集里,训练集里的污渍类别就几乎学不到。

3.4 转换后的三类校验证手段

转换完成不等于转换正确。我一般会做三重复核:

第一层是数量核对,统计XML文件数、TXT文件数、图片文件数三者是否一致,这是最基础的完整性检查。第二层是内容抽查,随机选10个TXT文件,把归一化坐标乘回图片宽高,再画出检测框叠加到图片上,肉眼确认类别和位置正确。第三层是训练框架自检,直接用YOLO的train模式跑一个最小epoch,观察加载过程是否有警告或跳过的样本——这一步能暴露“标签文件存在但内容为空”“图片存在但标签缺失”这类深层问题。

# 用yolo命令做数据完整性自检,跑1个epoch即可 yolo detect train model=yolov8n.pt data=dataset/data.yaml epochs=1 batch=8 imgsz=640

上面的自检命令跑完后,到训练日志里看一眼每个类别出现了多少次,如果某个类别的样本量为零,说明类别映射写错了或者标签里没有对应ID。这一步的价值在于把问题暴露在正式训练之前,而不是等到三小时训练结束看指标时才发觉数据有问题。

4. 用YOLOv8把数据集跑起来:配置文件与关键技术参数

4.1 编写data.yaml:类别顺序与路径的关键约定

YOLOv8训练走的是配置文件驱动,一份正确的data.yaml比模型代码更重要。配置很简单,但约定必须严格遵守:names列表的顺序,必须和转换标签时CLASS_MAPPING里定义的ID一一对应。

# dataset/data.yaml path: ./dataset # 数据集根目录,使用相对路径便于迁移 train: images/train # train目录相对path的路径 val: images/val # val目录相对path的路径 names: 0: damage # 损坏 1: stain # 污渍

这里的path字段指定数据集根目录,train和val填的是相对path的子目录路径,指向的是images目录而不是labels目录——YOLO会自动把images替换成labels来寻找对应标签文件,前提是两边的文件名一模一样且目录层级对应。names字典里的索引是类别ID,与TXT每行开头的数字一一对应,顺序不同会导致训练时类别错乱,直接影响AP指标。写完后检查一遍:标签TXT里类别ID最大值不能超过names长度减1,否则训练会直接报index越界。

4.2 训练命令与初期参数设置:从小模型、低分辨率开始

对于800张的小数据集,不建议一上来就用大模型和高分辨率。我的习惯是先用YOLOv8n(最大的就是它1秒能跑起来)加上512的输入尺寸跑一轮,把整个流程跑通,确认没有数据或配置错误,再换成大模型、加大分辨率追求精度。

yolo detect train \ model=yolov8n.pt \ data=dataset/data.yaml \ epochs=100 \ batch=16 \ imgsz=640 \ patience=20 \ project=runs/detect \ name=fabric_damage \ device=0 \ seed=42

这段命令的参数里,有几个值得谈:patience=20是早停策略,如果连续20个epoch验证集指标没有提升就直接结束训练,对总样本量只有800张的数据集来说可以避免过拟合阶段白白浪费算力;batch=16需要考虑显存大小,如果显存是8GB,建议降到8;device=0表示使用第一块GPU,没有GPU就改成device=cpu。seed=42固定随机种子,保证数据增强和初始化可复现——这在对比实验时非常重要。模型权重用yolov8n.pt是预训练权重,在ImageNet或COCO上预训练过的骨干网络对工业小数据集有明显帮助,比从随机初始化开始训练收敛快得多。

4.3 数据增强策略:小数据集靠增强拉高泛化

800张布匹图像本身不多,如果不做数据增强,模型很快会把训练集背下来。YOLOv8默认有一套增强策略,但对布匹这类纹理单调、缺陷占比小的图像,有两项值得重点调:一是亮度对比度调整,布匹在不同光照下色差明显,轻微调整亮度可以让模型更关注缺陷形状而不是依赖光照特征;二是轻微旋转和轻微缩放,模拟布匹在产线上的角度偏移。增强参数可以通过YOLO的hyp配置文件或训练命令里的覆盖参数控制。

# fabric_aug.yaml,在默认基础上做小幅调整 hsv_h: 0.01 # 色相增强幅度,保持较小避免颜色失真 hsv_s: 0.5 # 饱和度增强 hsv_v: 0.5 # 亮度增强 degrees: 5.0 # 旋转角度,布匹缺陷一般不会有大幅旋转 translate: 0.1 # 平移 scale: 0.3 # 缩放 fliplr: 0.5 # 水平翻转,布匹缺陷左右对称,可以用

使用自定义增强时,训练命令里加上hyp=fabric_aug.yaml即可。需要注意,增强是把双刃剑,旋转角度超过15度或缩放超过0.5倍时,小缺陷可能会被拉伸到无法识别的程度,反而降低验证集指标。布匹缺陷检测的增强核心思路是“模拟产线上的真实变化,而不是为了多样性而多样性”。

4.4 类别不平衡问题:当污渍样本占比不到三成

800张图里如果损坏占了大部分、污渍只有一小部分,模型会偏向学损坏类别。YOLOv8在检测头里天然带focal loss处理正负样本不平衡,但类别间的不平衡仍需要显式干预。最常见的做法是调整每个类别的损失权重,让少数类别贡献更大的梯度。

# 在训练后统计数据分布用的小工具 # 统计标签TXT里每个类别出现的次数 import glob label_dir = 'dataset/labels/train' class_count = {0: 0, 1: 0} for txt_path in glob.glob(label_dir + '/*.txt'): with open(txt_path) as f: for line in f: cls_id = int(line.split()[0]) class_count[cls_id] = class_count.get(cls_id, 0) + 1 print(class_count) ratio = class_count[1] / max(class_count[0], 1e-6) print(f'污渍/损坏比例: {ratio:.3f}')

如果比例小于0.3,说明少数类严重不足,此时有两类解法:一类是数据层面,对少数类样本做复制粘贴增强生成更多实例;另一类是损失层面,YOLOv8的class_loss_weight参数可以设置类别权重。不过对于两类缺陷任务,我建议优先从数据层面解决,因为类别权重调参比较玄学,不如直接让有效样本数变多来得稳当。

5. 布匹缺陷训练避坑手记:从标签错位到小目标丢失

5.1 标签文件与图片同名但不同步:验证时全部是空框

有人拿这份数据集训练时,把图片都放进images目录、TXT放进labels目录,训练过程不报错,但mAP一直是0。最后检查发现,一部分TXT文件内容为空——因为原始的VOC注释文件里存在一些没有<object>节点的XML,转换时直接生成了空文件。现象是训练正常跑完但精度为零。原因是空标签文件在YOLO里会被视为“背景图”,告诉模型这张图没有任何目标。解决方式是转换脚本里加一个判断:如果XML里没有目标对象,就不要生成对应TXT文件,同时把这张图从数据集里剔除,而不是留一张空标签图让模型学噪声。

5.2 归一化坐标用错了图片分辨率:模型学出一堆错位框

有次同事调这份数据报出过一个问题:模型输出的框整体偏在左上角,而且框的大小普遍偏大。排查后发现是转换脚本里用的img_width和img_height固定写成了从某张例图读到的数值,而这批图里有几十张分辨率不一样。现象是训练损耗能正常下降,但预测框和真实缺陷位置明显错位。原因是归一化坐标依赖原始分辨率,分母错了整个映射就错了。解决办法就是前面提到的逐张读取图片实际分辨率再转换,并且转换完成后用可视化脚本抽查不同分辨率图片的框位置。

5.3 小缺陷在resize后消失:原图很大但缺陷只有十几个像素

布匹图像常常分辨率很高,但缺陷区域可能不到整张图的2%。YOLO训练时默认把图像缩放到640大小,小缺陷在缩放后只剩几个像素,直接淹没在特征图里。现象是小目标类别的AP值极低,模型对小的损坏、污渍完全没有检出能力。原因是直接resize丢掉了细节。一个常用解决思路是切图训练(也叫tiling):把原始大图切成若干个640或512的小块,含缺陷的块保留标签,无缺陷的块作为负样本丢弃,然后用切出来的块做训练和推理。切图要设置重叠率,比如10%到20%,避免缺陷恰好落在切块边缘时被截断。

5.4 混淆矩阵看损伤与污渍交叠:模型总是把污渍判成损坏

训练结果显示污渍类别的recall很高但precision很低,打开混淆矩阵发现大量污渍被预测成损坏。原因是布匹上污渍和损坏常常伴生,标注时把边缘紧贴缺陷导致模型学到的特征空间重合。解决方法是回到标注层面重新核对:把两类缺陷框的重叠部分单独挑出来看,如果确实存在一个区域既是污渍又有撕裂,要么拆成两个框标,要么统一按主缺陷类别标,不给模型留模糊答案。同时可以适当放宽污渍框的范围,让模型更容易学到“渐变过渡”的特征。

5.5 验证集指标好但产线实测拉胯:训练集的“摆拍”数据太干净

800张数据集里的图像往往背景相对干净、光照均匀、缺陷角度单一,和产线实拍图有明显的域差距。现象是验证集mAP有0.85,但到现场测试时漏检率骤增。原因是训练数据的分布和真实场景不一致。解决思路有先后的:优先做数据增强模拟产线光照变化;其次考虑用少量现场图像做迁移微调,保留已有权重的基础上只花少量轮次更新知识;最后除非万不得已才放弃这套数据自己去采。这几个步骤里,微调的成本最低、见效最快,也是我拿到这类数据集后的标准动作。

6. 从跑通到可信:用验证集结果反向检验数据质量

模型训练完并不是终点,你还要回答一个问题:这个模型到底可不可信?这一步的常用做法是分析YOLO验证时的输出指标和图表,而不是只看训练日志里的总loss。YOLOv8在验证时会生成混淆矩阵、F1曲线、PR曲线和一批预测样例图,这些文件默认存放在runs/detect/exp/目录下。

# 对训练好的模型做验证集评估 yolo detect val \ model=runs/detect/fabric_damage/weights/best.pt \ data=dataset/data.yaml \ conf=0.25 \ iou=0.6 \ imgsz=640

验证后重点看三样东西:第一是混淆矩阵里的类别对角线和误判形态,前面讲的污渍误判成损坏的问题在混淆矩阵里一眼就能看出来;第二是PR曲线下面积和不同置信度下的精确率、召回率平衡点,如果置信度阈值设为0.25时召回率低于0.7,说明增强或训练轮次还得调;第三是预测样例图里被标错的目标——人工逐个翻,把模型预测错误的图片单独抽出来和原始标签对比,判断是标签标错了还是模型没学到特征。

最后一个我反复踩过的教训:这份数据集只有800张,模型的硬上限其实是由数据量决定的,不要指望靠调参让mAP高到0.95以上。更务实的做法是先争取让验证集mAP稳定在0.8以上,同时用几十张现场图片做一次小规模实测,确认模型的误差模式可以接受,再谈上线。我习惯把验证集里每个预测错误案例单独存成一个文件夹,隔几天再回头翻一遍,往往能发现当时急于调参时漏掉的数据标注问题。数据质量检查不是一锤子买卖,而是伴随着每次训练迭代持续做的动作——这个习惯帮你省下的时间比任何调参技巧都多,希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询