
简介布匹缺陷检测数据集共800张工业实拍图片聚焦“损坏”与“污渍”两类常见缺陷面向工业质检、智能巡检及目标检测算法开发人员可直接用于模型训练与效果验证。压缩包内图片按统一编号管理附vocxml与yolotxt两种主流标注格式另有jpg原图共2401个文件整体约988MB标签与图像一一对应无需额外转换即可接入常见检测框架。平台显示已有982人学习下载数据标注精确算法拟合表现可靠适合作为布匹表面缺陷检测项目的训练基座。读者可获得完整图片-标签配套数据便于开展数据划分、模型调参与缺陷识别实验同时可结合博主提供的更多介绍链接理解采集场景与标注规范降低实际项目落地门槛。1. 布匹缺陷检测数据集先解决标签格式再谈模型布匹表面的损坏、污渍这类缺陷在纺织产线质检里属于典型的小目标、低对比度问题。传统的机器视觉用灰度阈值、纹理滤波做一到花纹布、深色布就失效。深度学习的检测方案能把这些缺陷当目标框出来但前提是有一份干净、能直接喂给模型的标注数据。这题目里给的 800 张图、两种格式标答正好踩在大多数入坑者的痛点上不是不会跑 yolo而是被数据格式折腾到怀疑人生。VOC 的 XML 和 YOLO 的 Txt 各有各的坑坐标归一化、类别映射、train/val 划分一步错全盘崩。这篇就把这套数据集从目录结构、格式转换到训练验证的完整链路拆开讲适合已经跑通过一次检测流程、但被自定义数据集卡住的人。2. 数据集结构与两种标签格式的核心差异2.1 VOC 格式的目录树和 XML 解析逻辑VOC全称 PASCAL VOC是目标检测领域最老牌的数据组织方式。这套布匹缺陷数据集如果按 VOC 规范组织目录结构通常是这样的fabric_defect/ ├── JPEGImages/ # 原始图像jpg格式 ├── Annotations/ # 每张图对应的xml标注文件 ├── ImageSets/ │ └── Main/ │ ├── train.txt │ ├── val.txt │ └── test.txt每个 XML 文件的内容结构值得细看因为后面转 YOLO 格式要从里面抽信息annotation folderJPEGImages/folder filenamedefect_001.jpg/filename size width640/width height480/height depth3/depth /size object namedamage/name bndbox xmin120/xmin ymin85/ymin xmax300/xmax ymax240/ymax /bndbox /object object namestain/name bndbox xmin400/xmin ymin50/ymin xmax550/xmax ymax160/ymax /bndbox /object /annotation注意这里有几个关键点一是 size 节点里记录的是原图宽高不是缩放后的二是每个 object 代表一个目标框一张图可以有多个 object也可以有不同类型的缺陷混合三是 bndbox 四个值是像素坐标整数。转 YOLO 格式时xmin、ymin、xmax、ymax 必须除以 width 和 height 做归一化很多人在这里直接写死 640 或 416图一旦不是这个尺寸就全错了。实际用 Python 解析时我一般用 xml.etree.ElementTree不用 lxml因为标注文件不会复杂到需要 XPath 的程度标准库足够省得配环境:import xml.etree.ElementTree as ET tree ET.parse(Annotations/defect_001.xml) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) for obj in root.findall(object): label obj.find(name).text box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) print(label, xmin, ymin, xmax, ymax)这段代码的逻辑是按 XML 的树形结构逐层取节点先拿图宽高再遍历所有 object。size 节点在根元素下bndbox 在 object 下路径不能写错。如果发现解析出来的坐标有 0 值或者负数大概率是原始标注框贴着图像边缘后面训练时容易出 NaN建议直接过滤掉这些框或者做裁剪。2.2 YOLO 格式的归一化坐标与类别 id 映射YOLO 格式的目标检测标注是每张图一个同名 txt 文件放到 labels 目录里。每行一个目标格式是五列class_id、x_center、y_center、w、h。注意这四个值是归一化后的相对坐标不是像素值。归一化的计算方式是x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height这个目录结构是 YOLO 系列的通用约定fabric_defect_yolo/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/800 张图分 train 和 val 时每个 txt 文件名必须和对应 jpg 完全同名后缀不同否则训练时找不到标注界面会疯狂提示 warnings。类别映射也需要提前定好比如类别 id类别名中文含义0damage布匹损坏1stain污渍这套映射表在转格式脚本里要写死在 data.yaml 里也要写死两边不一致是训练时 mAP 为 0 的最常见原因没有之一。3. 把 VOC 转成 YOLO 的完整脚本与三个校验步骤3.1 最大化复用一个脚本跑通格式转换和数据集划分把 VOC 转 YOLO核心逻辑就是遍历 JPEGImages 里所有图片去找同名 XML提取坐标做归一化然后写入 labels 目录。这里给一份可以直接复制使用的脚本版本带 train/val 划分import os import random import xml.etree.ElementTree as ET # 配置 voc_root fabric_defect yolo_root fabric_defect_yolo class_names [damage, stain] train_ratio 0.8 random.seed(42) # 创建目录结构 for split in [train, val]: os.makedirs(f{yolo_root}/images/{split}, exist_okTrue) os.makedirs(f{yolo_root}/labels/{split}, exist_okTrue) # 收集所有标注文件 ann_dir os.path.join(voc_root, Annotations) all_anns [f for f in os.listdir(ann_dir) if f.endswith(.xml)] random.shuffle(all_anns) split_idx int(len(all_anns) * train_ratio) train_anns all_anns[:split_idx] val_anns all_anns[split_idx:] print(ftrain: {len(train_anns)}, val: {len(val_anns)}) def convert(ann_file, split): tree ET.parse(os.path.join(ann_dir, ann_file)) root tree.getroot() img_name root.find(filename).text img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) import shutil src_img os.path.join(voc_root, JPEGImages, img_name) dst_img os.path.join(yolo_root, images, split, img_name) shutil.copy(src_img, dst_img) yolo_lines [] for obj in root.findall(object): label obj.find(name).text if label not in class_names: continue class_id class_names.index(label) 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) # 坐标过滤 if xmax xmin or ymax ymin: print(fskip invalid box: {ann_file}, {label}) continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h yolo_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_name img_name.rsplit(., 1)[0] .txt with open(os.path.join(yolo_root, labels, split, txt_name), w) as f: f.write(\n.join(yolo_lines)) for split, anns in [(train, train_anns), (val, val_anns)]: for ann in anns: convert(ann, split) print(done)这个脚本看起来长但每个环节都有对应场景。img_w 和 img_h 必须从 XML 里读不能假设所有图一样大坐标过滤这一段是在防止后续训练时 loss 变成 nan框倒置或者面积为 0 的标注喂给任何检测头都会出问题写 yolo_lines 时统一保留 6 位小数避免浮点精度不足导致框偏移明显。划分比例按 0.8 给了 train、0.2 给 val800 张图分出来就是 640 和 160。如果缺陷类别分布不均需要在划分前按类别做分层抽样否则可能 val 里某类一个都没有。分层抽样也很简单先统计每个类别有多少张图然后按比例从每类里抽。3.2 转换后的三件事统计、复查、喂训练脚本跑完别急着开训。第一件该做的事是统计两个 split 的类别数量确认没有由于 XML 里类别名写错而被过滤掉导致某个 split 数据缺失。第二件是随机抽几张图把标注框画上去和原图对比看坐标是否对齐。这一步能用 Opencv 直接画矩形框或者用 LabelImg 打开图片和标签看。第三才是进入训练环节。这 800 张图里如果出现了某个类别只有 50 张而且其中 40 张都在 train、val 只有 10 张那么 val 的 mAP 波动会非常大。此时要么改划分比例要么做离线增强补齐稀缺类。离线增强可以是对原图做随机裁剪、旋转、亮度和对比度扰动。但增强后的图片必须重新生成标注框坐标不是简单复制一份图就行裁剪会改坐标、翻转也会改坐标这一块不能偷懒。import cv2 img cv2.imread(fabric_defect_yolo/images/train/defect_001.jpg) h, w img.shape[:2] with open(fabric_defect_yolo/labels/train/defect_001.txt) as f: for line in f: cls, xc, yc, bw, bh map(float, line.strip().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, 255, 0), 2) cv2.imwrite(check_visual.jpg, img)验证时注意YOLO 的 txt 存的是归一化中心点坐标和宽高画框之前要先换算回像素坐标中心点减宽高一半得到左上角加一半得到右下角。换算错了画出来框全偏到一边。4. 用 YOLOv8 起训练配置、命令与参数实测建议4.1 数据描述文件的写法路径必须和实际目录一致YOLO 系列用 YAML 文件描述数据集v5、v8、v11 写法一致path: /home/user/fabric_defect_yolo train: images/train val: images/val nc: 2 names: [damage, stain]这里的 path 是绝对路径train 和 val 相对于 path。最容易犯的错是写成train: /home/user/fabric_defect_yolo/images/train然后 path 也写了完整路径两个拼到一起直接找不到文件。train 和 val 字段写相对路径这是 YOLO 的默认解析逻辑。names 里的顺序和类别 id 必须和转换脚本里的 class_names 一致一个叫 [damage, stain]另一个写成了 [stain, damage]训练不会报错但模型输出的类别含义完全反了。800 张图的数据量不算大这个规模下直接选择 YOLOv8s 或者 YOLOv8m 比较合适不要一上来就 YOLOv8x 或者更大的模型小图数量下大模型容易过拟合val 的 mAP 反而往下走。官方 ataset 是 COCO 预训练权重自己的小数据集在它基础上微调收敛速度和效果都比从零训练好很多。4.2 常用命令和关键训练参数训练入口命令如下yolo detect train datafabric_defect.yaml modelyolov8s.pt epochs100 imgsz640 batch16 device0 projectruns_fabric namedefect_exp跑起来之后看输出日志关注四个指标。Box Loss 在第一个 epoch 结束应该在 1.5 以下不然就是学习率太大或者数据格式有问题Cls Loss 和 DFL Loss 会稳步下降正常情况不会出现剧烈震荡mAP50 在数据量小的时候可能前几个 epoch 都是 0这正常等第 20 个 epoch 左右再看趋势。我一般习惯把 Patience 参数留着它是早停策略val 的 mAP 连续 N 个 epoch 不涨就自动停。比如patience20如果 20 个 epoch 都没有提升说明模型已经收敛继续训练只会过拟合。参数名推荐值说明imgsz640输入分辨率缺陷是小目标可以试试 960batch8-32看显存8G 显存用 816G 用 16lr00.01预训练权重微调不用动mosaic1.0数据增强小数据集建议开启augmentTrue不关闭默认增强800 张图不增强很难训小目标缺陷和布纹纹理干扰是这个场景的两个主要难点。imgsz 从 640 提到 960 会显著增加对小目标的召回但显存占用和训练时间也上去了。如果损伤和污渍这类框的面积普遍小于 32x32 像素Imgsz 调到 960 的价值很大值得试一次对比。4.3 一次训练的最小验证回路训练结束后先别急着看 mAP直接跑一次 predict 能更快发现问题所在yolo detect predict modelruns_fabric/defect_exp/weights/best.pt sourcetest_images/ imgsz640 conf0.25 saveTruesource 可以是单张图、一个目录或者一段视频。如果 val 的 mAP50 很高但 predict 出来的框乱飘大概率是训练样本里长宽比极端的图太多模型泛化性差。这时候回看数据集布匹图片是不是都是同一朝向、同一光照如果是需要补充不同光线、不同角度和不同折痕背景的图片和对应标注。800 张图里如果有 100 张加入这些变化对模型鲁棒性的帮助远超再训 500 张同分布的数据。5. 从 800 张小数据集出发先调数据再调模型布匹缺陷检测这类实际产线场景最缺的往往是带标注数据本身。800 张图虽然听起来不大但配合预训练权重微调已经能跑出一个可用版本关键是先把这 800 张的利用率拉满。数据增强顺序上有讲究先做几何增强翻转、旋转、裁剪再做色彩增强亮度、对比度、饱和度。如果缺陷本身是颜色差异很小的污渍色彩增强的幅度别拉太大容易把正样本变没。一张布的缺陷区域往往占整幅图不到 5% 的面积mosaic 增强在这个场景里效果很好它会把四张图拼在一起强制模型在小感受野上学会缺陷和纹理的区分。调参之外一个容易被忽略的点是多尺度训练。在 YOLOv8 里开启scale0.5训练时每张图会随机缩放到原图的一半到 1.5 倍这等价于隐式增加了不同密度的布面纹理样本。800 张原图经过多尺度、mosaic 和翻转叠加实际喂给模型的样本方差会扩大数倍。如果你手头有 800 张之外更多的布匹图片但没标注上一轮训完的模型可以直接拿来预标注一半人工修正后再混合训练两个 epochs 就能再把 mAP 往上拉几个点。这个滚动式数据扩展比换大模型更经济也更符合布匹缺陷这类长尾分布数据的迭代方式。本文还有配套的精品资源点击获取