简介:这份数据集源自2019年天池布匹瑕疵检测赛题,覆盖32种布匹表面缺陷类别,面向计算机视觉学习者、算法工程师及工业质检相关团队,可用于目标检测、图像分类等模型的训练与验证。压缩包共935个文件,包含689张JPG原图、245个XML标注文件及1个TXT说明文件;XML中保存了缺陷边界框,便于直接接入VOC格式检测流程。整个资源包约927MB,图片以缺陷类别和拍摄时间命名组织,便于按需筛选。目前已有2914人浏览学习。客观来看,各缺陷类别样本数量不均,部分类别图片较少,标注质量也非完美,建议使用前进行数据清洗或扩展增强。该数据集仍有助于初学者理解工业布料瑕疵的视觉特征与标注方式,也可作为算法效果对比的基准资料。 2019年天池上有一个后来被很多人拿来练手的工业视觉赛题:广东工业智造大数据创新大赛·布匹瑕疵检测。这个比赛配套开放的布匹瑕疵数据集,几乎成了缺陷检测方向绕不开的入门资料。我一直想把这套数据上踩过的坑和跑通的经验完整梳理一遍,包括数据格式、模型思路、训练细节和各类排查技巧。如果你正准备复现这个比赛、做毕业设计,或者公司里正好有布料、纺织类的质检项目需求,这套方法论基本可以无缝迁移过去。
1. 布匹瑕疵数据集到底长什么样
1.1 来自真实生产线的图像数据
先说背景。这个比赛聚焦的是纺织行业的质检环节,数据来自真实生产线上的工业相机拍摄,不是实验室里摆拍出来的“干净样本”。所以图像里有大量现实世界的复杂情况:布料褶皱、纹理差异、光照不均、飞絮灰尘等等。这一点非常关键,因为真实数据和学术数据集最大的区别就是噪声无处不在,你的模型最终要面对的也是这种脏数据。
公开的布匹瑕疵数据集一般会分成训练集、验证集和测试集。训练集量级大概在五千到九千张不等,不同比赛阶段会有调整。单张图片的分辨率特别高,常见尺寸接近4000×1696甚至更高。为什么这么高?因为工业相机视野要尽量覆盖整匹布的宽度,为了看清细微瑕疵就必须保留高分辨率。但这对算法工程师来说就是个麻烦,后面我会专门讲怎么处理。
1.2 标注格式与六类瑕疵
这个数据集里标注格式在不同版本里略有差异,但社区里流传的版本大多是PASCAL VOC风格的XML,或者是JSON转成的VOC格式。每一张图对应一个标注文件,里面用矩形框标出瑕疵的位置和类别。
瑕疵类别主流是六类,下面我用表格直接列出:
| 瑕疵类型 | 直观表现 | 检测难点 |
|---|---|---|
| 破洞 | 布料出现孔洞 | 边缘不规则,容易和深色纹理混淆 |
| 水渍 | 水痕浸染区域 | 边界模糊,对比度极低 |
| 油渍 | 反光的油污区域 | 颜色与布匹底色接近,反光不稳定 |
| 三丝 | 细线状异物 | 目标极细、长条形、长宽比夸张 |
| 结头 | 纱线接头或小坨 | 尺寸小,还可能密集出现 |
| 网纹 | 编织异常形成网纹状缺陷 | 面积大但轮廓模糊,视觉上很不“框” |
记得我刚开始做的时候,以为瑕疵检测就是普通目标检测,拿过来就跑YOLO,结果被“三丝”这类目标折磨得不轻。一条丝可能只有几个像素宽,横跨大半张图,普通anchor根本框不准。
1.3 和常见缺陷检测数据集有什么区别
很多人之前可能接触过NEU-DET,那是热轧钢带表面缺陷数据集,六类缺陷,共1800张图,目标相对居中且大小适中。但布匹瑕疵数据集完全不是这个难度,它的核心区别有三个。
第一,图像尺寸大了一个数量级,切片几乎是必须的。第二,瑕疵目标尺度极不均匀,既有大范围的网纹,也有几个像素的三丝。第三,类别分布严重不均衡,破洞、水渍这类样本很多,网纹、结头这类样本可能只有几十个。这些特性决定了你不能把一个常规目标检测pipeline原封不动搬过来,必须做针对性调整。
2. 为什么这批数据这么难搞
2.1 小目标、低对比度与长尾分布
很多人第一次跑这个数据集都会困惑:明明在COCO或者VOC上效果很好的模型,为什么到这里mAP直接崩盘?答案就藏在这三个难点里。
小目标问题最直接。以4000×1696的图为例,一个50×50像素的破洞,占整张图面积的比例不到万分之四。常规检测网络下采样32倍后,这个目标在特征图里只有不到2个像素,基本不可能被检测到。
低对比度问题更隐蔽。水渍这种瑕疵在布面上只是一块颜色稍微深一点的区域,人眼都得凑近了才看得清,模型提取特征就更是难上加难。处理这类目标时,光照归一化和对比度增强往往是提升效果的关键。
长尾分布则是数据层面的坑。我自己统计过一次,六类瑕疵里样本数最多的一类和最少的可能差几十倍。模型训练时会被多数类主导,少数类几乎学不到有效特征,推理时自然漏检严重。
2.2 检测框架选型的思路
框架选择没必要盲目追新。我在这个数据集上试过Faster R-CNN、YOLOv5和mmdetection系列,最后稳定使用的还是YOLOv5,原因有三个。
第一,YOLOv5对滑窗切片训练的兼容性非常好。切片之后每张小图相当于普通的自然图像,完全可以直接喂进去,不需要额外改网络结构。第二,社区生态成熟,遇到问题随便一搜就有答案,放工业项目里意味着迭代效率高。第三,YOLOv5的推理速度足够快,高分辨率图切片后虽然数量多,但整体吞吐量还是能满足产线需求。
如果你更习惯mmdetection,也可以用它跑通,只是配置和调参成本会高一些。Transformer系列检测器如DINO或CO-DETR我也试过,精度确实能涨一点,但对显存和训练时间要求更高,想要快速出结果的话还是先别碰。
2.3 不要直接拿整图训练
这是新手最容易犯的错误。把4000×1696的整图resize到640×640再去训练,结果就是所有目标都被压缩成了几个像素,模型能学到的东西非常有限。
直接拿整图训练还有三个实际问题:显存压力大,单张图可能就要占据几个G的显存,batch size根本提不上去;小目标经过多次下采样后完全丢失;正负样本比例极端失衡,背景占比太大,模型偏向于把所有区域都预测成背景。
所以业界通用的做法是“切块训练,切块推理”。简单说就是把大图切成若干个小块,比如1280×1280或者1024×1024,切成的小图作为训练样本。推理的时候也用同样的方式切分,最后把检测框映射回大图坐标再做去重合并。这个思路我下面会展开讲细节。
3. 从数据到模型:完整实操流程
3.1 先把标注转成模型能吃的格式
我习惯把数据集先整理成YOLO格式,也就是每张图对应一个txt文件,每一行是“类别 cx cy w h”,其中cx、cy是归一化后的中心点坐标,w、h是归一化后的宽高。
下面是VOC XML转YOLO txt的参考代码:
import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_txt_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] class_map = { 'hole': 0, 'water_stain': 1, 'oil_stain': 2, 'three_silk': 3, 'knot': 4, 'mesh': 5 } for obj in root.iter('object'): name = obj.find('name').text if name not in class_map: continue box = obj.find('bndbox') x1 = float(box.find('xmin').text) y1 = float(box.find('ymin').text) x2 = float(box.find('xmax').text) y2 = float(box.find('ymax').text) # 注意:有些版本的标注坐标从1开始,建议统一减1 x1, y1 = max(x1 - 1, 0), max(y1 - 1, 0) x2, y2 = min(x2 - 1, img_w - 1), min(y2 - 1, img_h - 1) cx = (x1 + x2) / 2 / img_w cy = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") with open(out_txt_path, 'w') as f: f.write('\n'.join(lines))这里有两个坑必须提醒。
一个是坐标偏移问题。PASCAL VOC的坐标是从1开始的,而像素坐标通常从0开始,如果不统一减1,所有框会系统性偏移一个像素。虽然单个框偏移一像素看起来不严重,但在细长目标上可能直接影响IoU计算。
另一个是类别映射表一定要固定。训练和推理阶段必须使用完全相同的类别ID顺序,否则模型输出和真实标签对不上,结果全部作废。
3.2 滑窗切片与坐标映射
切块这件事听起来简单,做起来有不少细节。我常用的参数是切片大小1280×1280,重叠率0.25。为什么不直接用640×640?因为切片尺寸越小,目标被切断的概率越高,而且每个切块里包含的上下文信息越少,模型识别大范围瑕疵时会吃亏。但切片尺寸太大又会增加显存压力,所以1280和三倍下采样后的中间特征图尺寸相对平衡。
重叠率的选择也很关键。重叠率太低,目标被切成两半的概率增加;重叠率太高,训练样本膨胀严重。0.2到0.3之间是比较常见的区间,我习惯用0.25,也就是步长取切片尺寸的75%。
切片后必须做一件事:把原始标注的绝对坐标转换成切块内部的相对坐标。这里最容易出错,因为一张大图切成多个切片后,如果坐标转换的偏移量算错,模型训练出来的框就会整体偏移。建议在代码里写一个调试函数,把切块图片和转换后的标注框画在一起,人工抽样检查几轮,确认无误再开始训练。
推理阶段同样用滑窗切块,得到所有检测框后映射回原图坐标,然后做NMS合并。跨切片重复检测是必然的,因为重叠区域的同一个目标可能被两个相邻切片都检测到,NMS可以很好地解决这个问题。如果你用了不同切片尺寸得到的多组结果,还可以尝试WBF(加权框融合),效果往往比单纯NMS更好。
3.3 训练配置、数据增强与评估
基础配置方面,我建议先用YOLOv5s跑通整个流程,确认没有数据格式问题后再换YOLOv5m或YOLOv5l提升精度。输入分辨率设成640×640起步,如果显存允许再调到1280×1280。一开始就上大分辨率的话,训练速度和调试成本都会高很多,不利于快速定位问题。
数据增强方面,YOLOv5自带的mosaic增强我建议保留,它对提升小目标的鲁棒性很有帮助。HSV颜色增强也建议开,但强度不要拉满,因为工业场景下颜色变化没有那么多,过度增强反而会让模型学到错误的颜色不变性。我还额外加了随机光照扰动,模拟生产线上不同时段的光照变化,这个操作在真实场景下涨点明显。
评估指标主要看mAP@0.5和mAP@0.5:0.95。但我要特别强调一句:工业项目里mAP@0.5通常更重要。瑕疵检测不需要非常精确的像素级框,框的位置差不多命中就够用了,mAP@0.5反而更贴近实际业务需求。另外每训练完一个阶段,一定要把badcase可视化图保存下来,盯着看一遍,比只看数字有用得多。
3.4 尾部类别的处理
针对网纹、结头这类样本数特别少的类别,我试过几种方法,效果排序大概是:复制粘贴增强大于重采样大于单纯调整损失权重。
复制粘贴增强的做法很简单:从训练集里找出包含目标类别的区域,把它们裁剪下来,粘贴到其他正常布匹图像的随机位置,同时生成对应的新标注。这个操作对“三丝”这类细长目标尤其有效,因为三丝本身占的面积小,粘贴进去不会太违和。但要注意粘贴位置尽量落在纹理均匀的区域,不要贴在已有的明显瑕疵上,否则标注会混乱。
重采样就是让模型每个batch里都能看到足够多的尾部类别样本,实现上可以在自定义Dataset里对样本数少的类别做上采样。这个方法逻辑最简单,但对训练速度有一定影响。损失权重调整我放在最后尝试,因为这需要比较多轮的实验来确定权重系数,投入产出比一般。
4. 训练和推理阶段的常见问题排查
4.1 mAP不涨,先怀疑标签而不是模型
我在这个数据集上最大的教训就是:模型效果差,大概率不是模型的问题,而是标签和预处理流程出了问题。
最常见的情况是坐标转换错误。XML转YOLO的时候,如果你忘了把坐标从1改为0,或者切片时偏移量算错,模型训练出来的框就会全部偏移。这类问题在loss曲线上往往看不出来,loss可能正常下降,但mAP就是上不去。
我自己的排查流程是:训练前随机抽取200张带标注的图片,把标签画回去,一张一张过目。不要嫌麻烦,这一步能省掉后面无数个小时的无效训练。只要看到框的位置和实际瑕疵对不上,立刻停下修代码,千万不要带着错误标签继续跑。
还有一个隐蔽问题是类别ID顺序。YOLO格式的txt文件里类别是用数字表示的,如果你训练和验证阶段用的类别映射表不一致,验证结果会乱成一锅粥。建议把class_map写成一个固定文件,所有脚本都从同一份文件读取,避免手动复制粘贴导致不一致。
4.2 漏检、误检和跨切片重复框
漏检的主要原因有两个:目标太小和训练样本不足。
目标太小靠提高输入分辨率解决。我测试过640和1280两种输入下的小目标AP,后者的mAP@0.5有明显提升。如果你的服务器显存不够,可以先在切块层面降低切片尺寸,比如从1280降到1024,这样至少能在batch size上保持可用。
样本不足靠增强和重采样,上一节已经提过,这里不重复。
误检则往往是因为背景太复杂。布料上的纹理、褶皱很容易被模型当成瑕疵。我的做法是增加一个后处理步骤:对每个检测框计算“瑕疵区域内部对比度”之类的特征,如果特征值太低就认为是背景误检。这个方法比较粗暴,但在某些类别上效果显著。
跨切片重复框是切块推理特有的问题,解决思路是在NMS之前把所有检测框统一映射到原图坐标,再做一次全局NMS。阈值可以适当调高,比如0.45到0.5左右,否则重叠区域的目标可能被保留多个框。
4.3 显存不足与训练时间失控
这套数据下显存不足太常见了。一块16G的V100,如果用1280×1280输入,batch size可能只能设到4甚至2,训练速度非常感人。
三个可行办法:开启AMP混合精度训练,显存能省接近一半;使用梯度累积,相当于用时间换显存;实在不行就降低输入分辨率,先跑通流程再优化精度。
另外提一句,如果训练时间实在不可控,优先检查数据加载是不是成了瓶颈。滑窗切片之后样本数量会膨胀很多,我的建议是提前把所有切片结果缓存成离线文件,而不是每次训练时实时切图。这样GUP利用率能明显提高,训练时间可以缩短一半以上。
我整理了一个高频问题速查表,方便你排查时参考:
| 现象 | 检查方向 | 解决办法 |
|---|---|---|
| loss不降或震荡 | 学习率、标签 | 调低学习率,检查坐标转换是否正确 |
| mAP@0.5低但loss正常 | 数据、输入分辨率 | 检查badcase图,提高切片分辨率 |
| 某个类别几乎不检出 | 样本量、增强 | 重采样、复制粘贴增强、降低该类置信度阈值 |
| 推理框明显偏移 | 坐标映射 | 检查切片到原图的坐标恢复逻辑 |
| 大量重复框 | NMS策略 | 映射到原图后统一NMS,尝试WBF |
| 训练集acc高验证低 | 过拟合 | 增大切片数量,加强增强,加早停 |
4.4 预处理一致性这类隐性坑
还有一个非常容易出现但经常被忽略的问题:训练和推理阶段的预处理必须完全一致。
比如我在训练时加了对比度增强和随机光照扰动,推理阶段如果不加,模型看到的图像分布和训练时不一致,效果自然会下降。尤其对水渍这种本来就低对比度的目标,预处理差异会直接导致漏检翻倍。
实际操作上,建议把预处理函数单独封装,训练脚本和推理脚本共用同一个函数。不要在两份代码里各写一份,哪怕复制粘贴都要避免,因为一旦改了其中一个忘了另一个,结果就是玄学。
5. 个人经验与后续扩展
5.1 一套可以直接复用的通用管线
做完整个比赛,我沉淀下来的通用管线其实很简洁:高分辨率滑窗切块、通用目标检测器训练、跨块结果合并。这套流程不仅适用于布匹瑕疵数据集,迁移到皮革表面缺陷、钢板表面缺陷、薄膜表面缺陷等场景,只需要改标注类别和切片参数。
建议你拿到一个新数据集后,先用最小的代价跑通一个baseline,把数据加载、训练、评估、可视化的完整链路确认没问题,再开始优化精度。我见过太多人一上来就堆Swin Transformer、各种trick,最后发现数据标签有问题,浪费了几天时间。
5.2 这个数据集还能怎么玩
如果你把检测做完了,这个数据集还有很多可扩展的方向。一个方向是把检测框转成分割mask,做像素级的瑕疵分割。因为矩形框对长条形的三丝和大范围的网纹表达不够精细,分割模型往往能拿到更好的业务效果。
另一个方向是无监督异常检测。只用正常布匹图像训练PatchCore这类模型,让模型学习“正常长什么样”,新样本只要偏离正常分布就视为瑕疵。这个思路对工业场景很有吸引力,因为实际产线上经常会出现数据集里没有的新瑕疵类型,监督学习模型对未知类别无能为力,无监督方案反而更灵活。
最后说一句我的真实体会:在这个数据集上刷分很容易让人上头,但真正有用的能力是能快速定位模型哪里不行、为什么不行。每跑完一轮实验,把预测错误的框画到图上,盯着看十分钟,比盲目调参有用得多。这也是我在布匹瑕疵数据集上最大的收获。
本文还有配套的精品资源,点击获取