☰
布匹瑕疵检测数据集实战:从标注转换到YOLO训练避坑指南
2026/11/5 6:56:45 网站建设 项目流程

简介:这份资源是2019年天池布匹瑕疵检测赛题相关的图像数据集,面向从事工业质检、缺陷检测算法实践的开发者与学习者,可用于分类、目标检测等任务的训练与验证。压缩包共935个文件,以689张jpg瑕疵图像和245个xml标注文件为主,另附1个txt说明文件,整体约927.26MB,图像与标注基本配套,便于直接构建监督学习数据管线。数据覆盖32种布匹表面瑕疵类别,但各类别样本数量差异明显,部分类别图片偏少,作者也坦言整体质量不算高,更适合作为练手数据或补充样本使用。目前已有2919人学习下载,具备一定参考热度。读者可从中获得多类别瑕疵图像与对应标注,用于熟悉工业缺陷数据的组织方式、验证检测流程与模型鲁棒性,并据此评估类别不均衡对训练效果的影响。

1. 布匹瑕疵数据集:2019 天池赛题留下的那份“验布底料”到底怎么用

布匹瑕疵检测是工业质检里最典型的细粒度视觉任务之一,2019 年天池赛题放出的这份布匹瑕疵数据集,至今仍是不少从业者入门纺织质检、验证检测模型的第一份“底料”。它解决的核心问题很直接:给你一批带瑕疵标注的布面图像,让你把断经、破洞、油污、花毛这类缺陷从正常纹理里分出来。适合谁?做工业视觉的算法工程师、想复现缺陷检测流程的学生、以及需要快速搭一个验布 Demo 的团队。但这份数据不是“下载即用”的成品,标注格式、类别分布、图像分辨率都有它自己的脾气,直接喂给 YOLO 大概率翻车。下面按我实际拆包、清洗、转格式、训练的顺序讲清楚。

2. 先看清数据长什么样:类别、标注与目录结构

2.1 这份数据集的真实构成

拿到压缩包后别急着解压进训练目录,先花十分钟把结构摸清楚。这类赛题数据集常见的是“图像文件夹 + 标注文件”两段式,标注可能是 CSV、JSON 或每张图一个 txt。布匹瑕疵的类别通常围绕纺织厂实际会遇到的缺陷来定,常见的有断经、断纬、破洞、油污、花毛、结头、边不良等,具体类别数以你手上这份的标注文件为准,不要照搬别人的类别表。

我一般会先跑一段统计脚本,把类别分布、图像尺寸、标注框数量摸一遍。这一步不做,后面类别不均衡、空标注、尺寸异常全会在训练时集中爆发。

import os, json, collections from PIL import Image root = "cloth_defect" # 解压后的根目录 ann_dir = os.path.join(root, "annotations") img_dir = os.path.join(root, "images") cls_counter = collections.Counter() size_counter = collections.Counter() box_per_img = [] for fn in os.listdir(ann_dir): if not fn.endswith(".json"): continue with open(os.path.join(ann_dir, fn), "r", encoding="utf-8") as f: data = json.load(f) # 标注结构按实际字段调整,这里假设是 shapes/label 形式 shapes = data.get("shapes", []) box_per_img.append(len(shapes)) for s in shapes: cls_counter[s["label"]] += 1 for fn in os.listdir(img_dir)[:500]: # 抽样看尺寸即可 with Image.open(os.path.join(img_dir, fn)) as im: size_counter[im.size] += 1 print("类别分布:", cls_counter) print("尺寸分布(抽样):", size_counter) print("单图框数 min/max/avg:", min(box_per_img), max(box_per_img), sum(box_per_img)/len(box_per_img))

这段脚本干三件事:统计每个类别的框数量、抽样看图像分辨率是否统一、看单图平均框数。参数上唯一要改的是root和标注字段名,不同来源的标注键名可能是shapes、objects、annotations,打开一个文件看一眼就知道。跑完你会得到两个关键判断:类别是否严重长尾、图像尺寸是否杂乱。布匹数据里“正常布面”往往占大头,缺陷样本偏少,这个结论直接决定后面要不要做重采样。

2.2 为什么不能直接套通用检测配置

布匹瑕疵和 COCO 那类自然图像差别很大。第一,缺陷尺度极端:破洞可能只有十几个像素,油污可能铺满半张图,anchor 或特征层要兼顾。第二,背景是强纹理,布面的经纬纹理本身就是高频噪声,模型很容易把纹理当边缘。第三,类别间视觉差异小,断经和断纬在低分辨率下几乎一样。所以通用配置里的输入尺寸、anchor 比例、数据增强策略都得改。常见做法是把输入切到 640 以上、增强里加随机亮度与对比度扰动、慎用大角度旋转(布面纹理有方向性,转 90 度会把断经转成断纬的视觉特征)。这些判断不是拍脑袋,是上面统计结果推出来的。

3. 把标注转成训练格式:转换脚本与四个边界坑

3.1 转 YOLO 格式的完整脚本

多数人拿到这份数据是想快速跑通一个检测模型,YOLO 系列的 txt 标注是最省事的落点。转换的核心是把绝对像素坐标归一化到 0~1,并按类别 x_center y_center w h写行。下面这段脚本处理了坐标越界裁剪和空标注跳过。

import os, json root = "cloth_defect" ann_dir = os.path.join(root, "annotations") img_dir = os.path.join(root, "images") out_dir = os.path.join(root, "labels") os.makedirs(out_dir, exist_ok=True) classes = ["断经", "断纬", "破洞", "油污", "花毛"] # 按实际类别顺序改 cls2id = {c: i for i, c in enumerate(classes)} def clip(v, lo, hi): return max(lo, min(hi, v)) for fn in os.listdir(ann_dir): if not fn.endswith(".json"): continue stem = os.path.splitext(fn)[0] with open(os.path.join(ann_dir, fn), "r", encoding="utf-8") as f: data = json.load(f) W, H = data["imageWidth"], data["imageHeight"] lines = [] for s in data.get("shapes", []): label = s["label"] if label not in cls2id: continue pts = s["points"] xs = [p[0] for p in pts]; ys = [p[1] for p in pts] x1, x2 = clip(min(xs), 0, W), clip(max(xs), 0, W) y1, y2 = clip(min(ys), 0, H), clip(max(ys), 0, H) if x2 - x1 < 2 or y2 - y1 < 2: # 过滤退化框 continue cx = (x1 + x2) / 2 / W cy = (y1 + y2) / 2 / H bw = (x2 - x1) / W bh = (y2 - y1) / H lines.append(f"{cls2id[label]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") if lines: with open(os.path.join(out_dir, stem + ".txt"), "w") as f: f.write("\n".join(lines))

逻辑说明:clip处理标注点超出图像边界的脏数据,这在人工标注里很常见;x2-x1<2过滤掉几乎退化成点的框,否则训练时归一化宽高接近 0 会产生 NaN 损失;if lines保证空标注图不生成空 txt,避免训练器读空文件报错。参数上classes必须和你的标注 label 字符串完全一致,差一个字就整类丢失,这是最常见的静默失败。

3.2 划分训练验证集时别踩随机种子的坑

转换完别直接random.shuffle一刀切。布匹数据往往按批次或按布卷采集,同一卷布的图像高度相似,随机划分会导致验证集里混进训练集的“近亲”,指标虚高。我一般按文件名前缀或采集批次分组划分,保证同一批次的图只出现在一个集合里。

import os, random, shutil random.seed(42) img_dir = "cloth_defect/images" train_dir, val_dir = "dataset/images/train", "dataset/images/val" os.makedirs(train_dir, exist_ok=True); os.makedirs(val_dir, exist_ok=True) files = sorted(os.listdir(img_dir)) # 按前缀分组,假设文件名形如 batch01_0001.jpg groups = {} for f in files: key = f.split("_")[0] groups.setdefault(key, []).append(f) for key, fs in groups.items(): random.shuffle(fs) n_val = max(1, int(len(fs) * 0.2)) for i, f in enumerate(fs): dst = val_dir if i < n_val else train_dir shutil.copy(os.path.join(img_dir, f), os.path.join(dst, f))

seed=42保证可复现,0.2是验证比例,按组划分是关键。如果你的文件名没有批次信息,退而求其次用图像感知哈希聚类再分组,别偷懒用纯随机。

4. 训练配置与增强策略:参数怎么设才不白跑

4.1 输入尺寸与 anchor 的取舍

布匹缺陷小目标多,输入尺寸我一般从 640 起步,显存够就上 800 或 1024。小目标在 640 下可能只剩几个像素,检测头根本学不到。anchor 方面,如果用的是带自适应 anchor 的版本,让它自己聚类;如果是固定 anchor 的老版本,得用第 2 章的统计脚本导出所有框的宽高,跑一遍 k-means 重新生成,否则默认 anchor 全是自然图像的比例,对细长条状的断经完全不匹配。

# 以常见 YOLO 训练入口为例,参数按实际框架调整 python train.py \ --data cloth.yaml \ --img 800 \ --batch 8 \ --epochs 100 \ --hyp hyp_cloth.yaml \ --weights yolov5s.pt

--img 800是权衡显存后的选择,--batch 8在单卡 16G 上比较稳,--hyp指向自定义增强配置。cloth.yaml里要写对train、val路径和nc、names,nc必须等于类别数,写错会直接维度不匹配。

4.2 增强策略里哪些能用哪些要关

布面纹理有强方向性,增强要克制。随机亮度、对比度、轻微高斯噪声可以用,模拟车间光照波动;水平翻转慎用,断经翻过来视觉上接近断纬;大角度旋转直接关掉;Mosaic 可以用但要把概率调低,四图拼接会让小缺陷被压缩得更小。我一般把 Mosaic 概率从默认 1.0 降到 0.3~0.5,颜色抖动幅度控制在 ±20% 以内。

# hyp_cloth.yaml 关键片段 hsv_h: 0.0 # 色相不动,布面颜色是类别线索 hsv_s: 0.3 hsv_v: 0.3 flipud: 0.0 fliplr: 0.2 # 低概率水平翻转 mosaic: 0.4 scale: 0.3

hsv_h=0是因为色相变化可能把油污的颜色特征抹掉;fliplr=0.2留一点但不高;mosaic=0.4平衡小目标保留和增强多样性。这些值不是标准答案,是布匹场景下的经验起点,跑完看验证集上小类召回再微调。

5. 避坑与排查:五条血泪经验

5.1 类别名对不上导致整类丢失

现象:训练完发现某个类别从来没被预测出来,混淆矩阵里那一行全空。原因:转换脚本里的classes列表和标注文件里的 label 字符串不完全一致,比如标注写“断经”而你写“断经线”,cls2id查不到就continue跳过了,全程静默。解决:转换前先set出所有 label 打印一遍,和classes做差集,差集非空就停下来对齐。

5.2 空标注图引发损失 NaN

现象:训练几个 step 后 loss 变 NaN。原因:某些图没有缺陷(正常布面),转换时生成了空 txt 或没生成 txt,训练器读到空文件时 batch 内框数为 0,归一化除零。解决:转换脚本里if lines才写文件,同时在数据集配置里确认框架对空标注的处理方式,必要时把纯正常图单独放一个集合做负样本评估,不混进训练。

5.3 验证集指标虚高

现象:验证 mAP 0.9 以上,上线一测惨不忍睹。原因:按图随机划分,同批次近亲图同时进了训练和验证。解决:按批次前缀分组划分,或者用感知哈希去重后再分。这个坑最隐蔽,指标好看的时候反而要警惕。

5.4 小目标全漏检

现象:大块油污能检出,细小破洞全丢。原因:输入尺寸太小或 anchor 不匹配,小目标在特征图上不足一个像素。解决:输入提到 800 以上,检查 anchor 是否用本数据集聚类过,必要时加一个高分辨率检测头。

5.5 纹理被当成缺陷

现象:正常布面大量误检,框落在纹理交界处。原因:增强里对比度拉太猛,或者模型把经纬纹理的高频边缘学成了缺陷特征。解决:降低颜色抖动幅度,增加正常样本的负样本比例,训练后看误检框是否集中在纹理密集区,针对性加纹理抑制。

6. 验证与进阶:用混淆矩阵反推数据问题

训练跑完别只看一个 mAP 就收工。我习惯先导出混淆矩阵和每类的 PR 曲线,重点看两件事:哪两类互相混淆最多、小类召回是否被大类压制。布匹数据里断经和断纬混淆是常态,如果混淆矩阵显示这两类互相误判超过 30%,说明当前分辨率下方向特征没学出来,要么提分辨率,要么在增强里保留方向信息(别翻转)。

# 用验证集推理结果画混淆矩阵的简化流程 from sklearn.metrics import confusion_matrix import numpy as np # preds: [(gt_cls, pred_cls), ...] 由你的评估脚本导出 gt = [p[0] for p in preds] pd = [p[1] for p in preds] cm = confusion_matrix(gt, pd, labels=list(range(len(classes)))) print(cm) # 归一化后看每行最大值是否在对角线 cm_norm = cm / cm.sum(axis=1, keepdims=True) print(np.round(cm_norm, 2))

labels顺序必须和classes一致,cm_norm按行归一化后,对角线低于 0.6 的类别就是重点排查对象。另一个进阶技巧是做切片评估:把验证集按缺陷面积分成小、中、大三档,分别算召回,你会发现整体 mAP 不错但小目标档位可能只有 0.3,这个数字才是验布场景真正关心的。

还有个容易被忽略的点:这份数据集的标注框是矩形,但布匹缺陷很多是不规则形状,矩形框里混进大量背景。如果追求更高精度,可以尝试把矩形框转成掩码做分割任务,或者用带旋转框的检测器。我一般先用矩形框跑通基线,再决定要不要上分割,别一上来就堆复杂结构。

从那以后我每次拿到新的工业质检数据集,都强制先跑一遍类别统计和分组划分,再动训练脚本——这两步省下来的返工时间,比调参多得多。希望帮到你。

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

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

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

立即咨询