
简介这是一份面向工业视觉质检场景的布匹缺陷检测数据集包含800张真实布匹图像覆盖“损坏”“污渍”两类常见缺陷适用于缺陷识别、目标检测、智能质检等方向的模型训练与效果验证。数据标注精确VOCxml与YOLOtxt两种标签格式同时提供可免去格式转换步骤直接接入主流检测框架使用同时便于在模型训练前进行数据检查与增强实验。压缩包大小约988MB共2401个文件包括800张jpg原图、800个xml标注文件与801个txt标签文件JPG图像与标签一一对应目录结构清晰方便按批次划分训练集与验证集。该数据集已有984人浏览学习数据质量可靠标注一致性较好既适合工业现场快速验证缺陷检测算法也可作为高校或培训机构的目标检测教学案例。1. 布匹缺陷检测数据集800张图能撑起什么样的质检模型布匹出厂前的质检环节至今大量工厂还靠人工在验布机前盯屏幕眼睛盯两小时就开始漏检。这几年不少团队想用视觉模型替代人工初检但卡在数据上工业缺陷数据本身少又不方便公开能找到的公开布匹缺陷集大多是几百张、标注格式还五花八门。标题里这个800张的布匹缺陷检测数据集胜在简单直接——包含损坏、污渍两类缺陷同时给了VOCXML和YOLOTXT两种标签格式省去了大部分入门者“拿到数据先折腾格式”的时间。它对两类人最有价值一是刚接触工业视觉、想跑通全流程的算法工程师二是已有检测经验、需要快速验证布匹场景可行性的团队。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_okTrue) 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(fWarning: {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 modelyolov8n.pt datadataset/data.yaml epochs1 batch8 imgsz640上面的自检命令跑完后到训练日志里看一眼每个类别出现了多少次如果某个类别的样本量为零说明类别映射写错了或者标签里没有对应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 \ modelyolov8n.pt \ datadataset/data.yaml \ epochs100 \ batch16 \ imgsz640 \ patience20 \ projectruns/detect \ namefabric_damage \ device0 \ seed42这段命令的参数里有几个值得谈patience20是早停策略如果连续20个epoch验证集指标没有提升就直接结束训练对总样本量只有800张的数据集来说可以避免过拟合阶段白白浪费算力batch16需要考虑显存大小如果显存是8GB建议降到8device0表示使用第一块GPU没有GPU就改成devicecpu。seed42固定随机种子保证数据增强和初始化可复现——这在对比实验时非常重要。模型权重用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 # 水平翻转布匹缺陷左右对称可以用使用自定义增强时训练命令里加上hypfabric_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 \ modelruns/detect/fabric_damage/weights/best.pt \ datadataset/data.yaml \ conf0.25 \ iou0.6 \ imgsz640验证后重点看三样东西第一是混淆矩阵里的类别对角线和误判形态前面讲的污渍误判成损坏的问题在混淆矩阵里一眼就能看出来第二是PR曲线下面积和不同置信度下的精确率、召回率平衡点如果置信度阈值设为0.25时召回率低于0.7说明增强或训练轮次还得调第三是预测样例图里被标错的目标——人工逐个翻把模型预测错误的图片单独抽出来和原始标签对比判断是标签标错了还是模型没学到特征。最后一个我反复踩过的教训这份数据集只有800张模型的硬上限其实是由数据量决定的不要指望靠调参让mAP高到0.95以上。更务实的做法是先争取让验证集mAP稳定在0.8以上同时用几十张现场图片做一次小规模实测确认模型的误差模式可以接受再谈上线。我习惯把验证集里每个预测错误案例单独存成一个文件夹隔几天再回头翻一遍往往能发现当时急于调参时漏掉的数据标注问题。数据质量检查不是一锤子买卖而是伴随着每次训练迭代持续做的动作——这个习惯帮你省下的时间比任何调参技巧都多希望帮到你。本文还有配套的精品资源点击获取