简介:一套面向布匹质检场景的YOLO格式瑕疵检测数据集,适合计算机视觉开发者、算法研究人员以及纺织行业质检人员使用。数据严格按YOLOv5目录保存,训练集约550张图片及对应txt标注,测试集约140张图片,共定义46类瑕疵,涵盖擦洞、跳花、油渍、线印、扎洞、毛粒等常见缺陷,类别已写入classes文件,下载后可直接投入yolov5训练或基于该基线做改进实验。包内共1413个文件,其中706个jpg图像与706个txt标签一一对应,另有1个json配置文件,整体容量944.12MB,结构规整,免去自行划分数据集与格式转换的步骤。目前已有470人学习,借助该数据集可快速搭建布匹缺陷自动检测模型,用于产线质量监控、算法精度对比或YOLO系列改进研究。
1. 布匹瑕疵检测数据集:一份划分好的YOLO格式数据为什么值得用
一卷布以每分钟几十米的速度过机,质检员要在一瞬间判断某个斑点是不是瑕疵。这个岗位的疲劳度和漏检率,正是YOLO这类目标检测算法切入工业质检的天然理由。可真正开始训练,最卡人的往往不是模型结构,而是数据:网上的布匹瑕疵公开数据要么是VOC/COCO格式,要么没有划分好的训练集,更常见的是连类别清单都没有一份。这份标题里的数据集把两件脏活提前干完了——标注已经是YOLO的txt格式,train/val/test分得清清楚楚,class文件也单独给出。适合谁?想用YOLO快速跑通布匹瑕疵检测、不想从整理标注开始的人。
2. 拆解数据集结构:images、labels、class文件与目录划分
2.1 YOLO的目录约定:一张图片对应一个同名txt
拿到任何一份YOLO格式的数据集,第一步不是急着训练,而是把目录结构先摸清楚。常见组织方式是这样:图片放在images下分train、val、test三个子目录,标注txt放在labels下,同样拆成三份。每张图片对应一个同名txt,比如20230512_roll_001.jpg对应20230512_roll_001.txt。图片是jpg还是png无所谓,但txt的名字必须和图片去掉后缀后的文件名完全一致,否则训练时找不到标注,整张图会被当成背景跳过。
打开一个txt,内容是这样的几行数字:
0 0.5172 0.6825 0.0831 0.0417 2 0.3158 0.2409 0.1204 0.0556 1 0.7843 0.5021 0.0628 0.0329每行五个数,含义固定:第一个数是类别ID,从0开始;后面依次是瑕疵框中心点的x坐标、y坐标、框宽、框高。最关键的是,这四个值不是像素值,而是相对图片宽高的归一化比例,取值必须在0到1之间。YOLO从v5到v11,训练数据格式都认这一套,所以这份数据集以后做不同版本的对比实验也不用重新转换。
归一化约定有两个直接好处。第一,换输入分辨率不用改标注,训练时无论缩放到640还是1024,坐标都能直接参与运算;第二,模型输出的框本身就是0到1之间的比例坐标,推理后乘以原图尺寸就能还原像素位置,省掉一套坐标换算逻辑。
2.2 class文件决定类别ID:顺序错了全盘错位
class文件通常就是纯文本,一行一个类别名,比如:
hole oil_stain warp_break这里有一个新手最容易忽略的细节:txt标注里的类别ID数字,就是class文件的行号,从0开始计数。上面这个文件里,hole是第0行,所以所有破洞标注在txt里写0;oil_stain是第1行,统一写1;warp_break是第2行,统一写2。一旦你自己重写data.yaml时把names的顺序调换,整个模型的输出类别就全部错位,油污会被学到破洞上。
拿到class文件后,我习惯先把每个类别名对应到实际瑕疵照片上看一遍。布匹瑕疵的命名在不同工厂差异极大:有的按工艺分(破洞、断经、纬缩、油污、褶皱),有的按颜色深浅分,有的把飞花、竹节这类纱线瑕疵单独列出来。题目里提到的"类别class文件"其实就是这份映射表,它决定了模型到底在学哪几个缺陷类型,也直接决定训练配置文件里的nc参数。
如果想快速确认每个类别的标注数量分布,可以用下面这个脚本统计,避免某个类在训练集里样本太少:
import os from collections import Counter def count_classes(labels_dir): counter = Counter() for fname in os.listdir(labels_dir): if not fname.endswith('.txt'): continue path = os.path.join(labels_dir, fname) with open(path, 'r') as f: for line in f: line = line.strip() if line: cls = line.split()[0] counter[cls] += 1 return counter # labels_dir 换成实际路径,例如 'labels/train' print(count_classes('labels/train'))脚本逻辑不复杂:遍历指定目录下所有txt,读取每一行的第一个字段也就是类别ID,放进Counter里做累加。它能回答两个关键问题:各类别样本是否均衡,以及train/val/test里类别分布是否一致。如果某个瑕疵类别在训练集里有800个框,验证集里只有2个,那后续训练时这个类基本学不好。
2.3 划分好的train/val/test:为什么按布卷隔离比随机划分更可靠
很多公开数据集只是把图片打了标注,划分什么的不存在的。这份数据集既然把train/val/test分好了,你需要先理解它到底按什么原则切分,再决定能不能直接拿去训练。
对于布匹瑕疵来说,同一个布卷的连续帧在底纹、光照、瑕疵位置上高度相似。如果这些相似的帧被随机打散到train和val,模型在训练时等于提前见过验证集的内容,验证mAP会虚高。上了产线换一卷新布,模型立刻暴露出真实水平。所以可靠的数据集划分应该按布卷或拍摄时间段隔离,而不是简单地随机抽样。
拿到划分后可以做一个快速体检。用下面的命令统计每个集合的图片数和标注数,看在数量级上是否合理:
for split in train val test; do echo "=== $split ===" echo "images: $(ls images/$split | wc -l)" echo "labels: $(ls labels/$split | wc -l)" echo "boxes: $(cat labels/$split/*.txt | wc -l)" done这三行命令分别数出图片文件数、标注文件数和总框数。page一般train占70%到80%,val和test各占10%到15%。框数和图片数如果出现明显失衡,比如train图片2000张但框只有50个,就要警惕是不是大量图片没有标注,训练时会被全部当作背景,造成严重的正负样本不平衡。
3. 把VOC/COCO标注转换成YOLO格式:转换脚本与四个边界坑
3.1 坐标转换公式:从像素框到归一化中心点
如果拿到的布匹缺陷数据不是YOLO格式,而是一堆VOC的xml或者COCO的json,你需要先把坐标转换过来。VOC的xml里存的是xmin ymin xmax ymax,单位是像素;COCO存的是左上角x、左上角y、框宽、框高,同样是像素。YOLO要的是相对图片宽高的归一化中心点坐标。
转换公式是固定的四行:
x_center = (xmin + xmax) / 2 / img_width y_center = (ymin + ymax) / 2 / img_height w = (xmax - xmin) / img_width h = (ymax - ymin) / img_height分母必须用图片的实际宽高,而不是框所在区域宽高。有的转换代码写成用像素绝对值除以固定imgsz,这在训练时可能不报错,但所有标注都会整体偏移,导致检测框位置偏差。另外,xmin和xmax读出来是字符串,先用int()再参与计算,否则Python里会做字符串拼接,得到的结果全是错的。
3.2 一个可复用的VOC转YOLO的Python脚本
下面是一个可以直接跑通的转换脚本,我平时处理VOC格式布匹数据就是拿它改的。它遍历一个目录下所有xml,转成同名txt,并自动跳过不在类别清单里的目标:
import os import xml.etree.ElementTree as ET def voc2yolo(xml_path, txt_path, class_names, img_width, img_height): tree = ET.parse(xml_path) root = tree.getroot() with open(txt_path, 'w') as out: for obj in root.findall('object'): name = obj.find('name').text if name not in class_names: continue # 不在类别清单里的目标直接跳过 cls_id = class_names.index(name) bndbox = obj.find('bndbox') xmin = int(float(bndbox.find('xmin').text)) ymin = int(float(bndbox.find('ymin').text)) xmax = int(float(bndbox.find('xmax').text)) ymax = int(float(bndbox.find('ymax').text)) 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 # 防止越界导致训练崩溃 x_center = min(max(x_center, 0), 1) y_center = min(max(y_center, 0), 1) w = min(w, 1) h = min(h, 1) out.write(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n") # 使用示例 class_names = ['hole', 'oil_stain', 'warp_break'] img_width = 1024 img_height = 1024 for xml_file in os.listdir('voc_annotations'): if xml_file.endswith('.xml'): xml_path = os.path.join('voc_annotations', xml_file) txt_path = os.path.join('yolo_labels', xml_file.replace('.xml', '.txt')) voc2yolo(xml_path, txt_path, class_names, img_width, img_height)脚本的逻辑分三段:先解析xml拿到所有object节点,然后对每个目标做类别名匹配并读取边界框坐标,最后套用上面的公式完成归一化并写入txt。关键参数有两个:class_names的顺序必须和将来data.yaml里的names保持一致,img_width和img_height必须和图片真实尺寸一致,否则坐标全部变形。
实际使用中,建议把voc_annotations和yolo_labels换成你自己的目录路径。如果图片尺寸不统一,不要写死宽高,在脚本里额外用PIL或cv2读取每张图片的实际尺寸,这样最稳妥。
3.3 越界框、空标注、类别错位、灰度图:四个边界坑
第一个坑是越界框。部分标注工具在人工画框时允许框超出图片边缘,xmax会大于图片宽度,导致归一化后的w超过1。YOLO在算锚框匹配时遇到这种值会行为异常,轻则训练loss震荡,重则直接崩溃。解决方式就是脚本里那三行clip操作,把中心点和宽高全部限制在0到1之间。
第二个坑是空标注。一张布匹图片没有任何瑕疵时,xml里没有object节点,输出txt是空文件。有些脚本遇到这种情况就不生成txt,这是不行的。YOLO训练时如果图片没有对应txt,会把它判定为非标注图片跳过,导致实际参与训练的图片数和清单对不上。正确做法是仍然生成一个空txt,表示这张图存在且没有目标。
第三个坑是类别错位。xml里的标签名是hole,class_names列表里写的是hole,看起来一样,但如果class_names顺序和class文件顺序不一致,生成的类别ID就对不上。比如class文件里hole在第2行,而转换脚本的class_names把hole放第0行,那txt里的0实际是warp_break。转换前先核对class文件,这个步骤不能省。
第四个坑是灰度图。工业相机采到的布匹图像经常是单通道灰度,或者12bit深度图像,直接喂给YOLO会在数据加载阶段报通道数错误。常见做法是用CV2统一转成三通道8bit,顺带做一次归一化。转换代码里加一行img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),再把深度图用cv2.convertScaleAbs转到8bit,不过这步要放在坐标转换之前,且转换后的宽高不能变。
4. 用这份数据训练YOLOv8/v5:data.yaml与关键参数怎么设
4.1 data.yaml的组织方式:path、train、val、names一个都不能少
数据集目录确认没问题,下一步就是写训练配置。YOLOv8训练自己的数据集,统一靠一个data.yaml文件描述数据来源。最小可用配置长这样:
path: /data/fabric_defect train: images/train val: images/val test: images/test nc: 3 names: 0: hole 1: oil_stain 2: warp_breakpath字段是数据集根目录的绝对路径,train、val、test是相对这个根目录的路径。这里的nc要和onesnames里的类别数严格一致,写成3就表示模型输出三个类别。names的顺序必须和class文件一致,这一点我在前面的章节反复强调过,因为它直接决定了txt里数字ID的含义。
有一个常见翻车点:如果yaml里只写相对路径而不写path,YOLO会拿当前工作目录去拼,一旦你在别的目录下执行训练命令,就会报Dataset not found。所以我现在都习惯用绝对路径写死path,省得每次换机器都要改。另外Windows用户的路径分隔符是反斜杠,yaml解析容易出问题,统一用正斜杠最稳妥。
4.2 训练命令与超参:imgsz、batch、epochs怎么定
data.yaml就位后,最简单的训练命令是:
yolo train data=fabric.yaml model=yolov8s.pt epochs=300 imgsz=640 batch=16 device=0拆开看每个参数。model=yolov8s.pt表示从COCO预训练的yolov8s权重开始训练,这是迁移学习的标准做法。有人问coco80是什么,其实是指COCO数据集的80个类别,这个预训练权重已经见过大量自然图像特征,放到布匹瑕疵上能省下大量训练时间,也更容易收敛。
imgsz=640是输入分辨率。布匹瑕疵里破洞、线头这类目标往往只有几十个像素,640不一定够。我的经验是先把imgsz调到1024试一轮,如果显存不够或者训练速度不可接受,再退回736或896。这个参数对检测小目标的影响,比换任何模型结构都直接。
batch=16要看显存来定,显存只有8G就把batch降到8或4,显存充足可以开到32或64。建议先跑一次看显存占用,再决定batch大小。epochs=300对工业瑕疵检测来说不算多,配合早停机制,模型通常能在150到200轮左右收敛到稳定水平。早停和自适应学习率都是YOLO的默认行为,不需要额外配置。
跑起来之后要盯的不只是训练loss。loss会随着轮次下降,但更值得注意的是验证集上的mAP曲线。如果loss在降而mAP一直不动,大概率是数据划分或标注本身出了问题,先停下来检查数据,不要盲目加轮次。
4.3 布匹场景下的增强与损失函数参数:mosaic、类别不平衡、CIoU
布匹瑕疵检测和通用目标检测有个明显差异:布面本身有纹理、有光泽、有周期性图案,这些背景特征如果被增强策略放大,模型很容易把正常的织纹当成瑕疵。YOLOv8默认开启mosaic增强,把四张图拼成一张训练图,对通用检测是好事,但对布匹场景,拼接处会产生假的纹理边界,反而干扰模型。
常见做法是把mosaic概率调低,比如mosaic=0.5,甚至在数据增强里关闭它,改成只靠旋转、平移、翻转。怎么调看验证效果:如果训练中期mAP还在稳定上涨,说明mosaic没有坏事,保持默认;如果val mAP在epoch 100之后反而开始下降,优先怀疑增强策略过度。
类别不平衡也是布匹瑕疵的常态。油污可能占60%的标注框,破洞只占5%,模型会倾向于把不确定的区域判成油污。解决思路有两个:一是用训练脚本里默认的cls损失权重,给稀有类别提高权重;二是做简单的过采样,把破洞样本复制几份混进训练集。YOLOv8的损失由box、cls、dfl三部分组成,默认权重一般够用,但如果发现稀有类别召回率特别低,可以手动把cls权重从0.5提到0.7或1.0。
损失函数本身在YOLOv8里默认是CIoU,对大多数情况都适用。布匹瑕疵框有些是细长的,比如断经这种横向条状缺陷,长宽比极端,此时可以试试SIoU或WIoU这类更关注几何关系的损失。结论是别一上来就换损失函数,先用默认配置训一个baseline,确认瓶颈在模型还是在数据,再决定要不要动这些参数。
5. 避坑:布匹瑕疵检测数据集最常见的五个翻车现场
5.1 训练loss很低,验证mAP却上不去:数据泄漏
现象:训练集上loss降得很漂亮,准确率接近满分,但val mAP一直在0.5以下徘徊,检测出的框和真实标注对不上。
原因:数据划分没按布卷隔离。同一卷布的连续帧被随机分到train和val,模型在训练阶段已经把val的底纹学进特征里,真正跑验证时遇到没见过的布卷就崩。
解决:需要自行重新划分。按文件名的布卷ID或拍摄时间聚合,把同一个布卷的图片全部分到同一个集合。可以写个脚本按文件名前缀分组,再重新生成train/val/test目录,务必重新转换标注路径。
5.2 推理时把布面纹理当瑕疵:标注边界没定清
现象:模型在褶皱、阴影、金属光泽区域输出一堆置信度0.5以上的框,而真正的油污和破洞反而被漏掉。
原因:标注人员把布匹表面的正常编织纹理和光照反光也当成了目标。class文件里类名定义不清,导致不同标注员对"油污"的理解不一致,有的标了阴影,有的没有。
解决:回看class文件,给每个类写清楚定义边界。比如规定"油污必须是从外界沾上的深色污渍,面积小于一平方厘米,纯色阴影不算"。best做法是把定义和示例图贴在标注文档里,然后重新过一遍标注数据,删除那些模棱两可的框。
5.3 破洞、线头等小目标漏检:分辨率与下采样
现象:大瑕疵比如油污和褶皱基本能检测到,但破洞、线头这类只有十几像素的小目标,召回率不到50%。
原因:640的输入分辨率经过模型五层下采样后,小目标的特征在最后几层特征图里只剩下几个像素,丢失严重。
解决:先把imgsz提到1024,这一步通常立竿见影;如果显存不允许,至少到896。还可以用小模型yolov8n的更高分辨率版本,或者在推理时把原图切片成多块,分块检测后合并结果,这样不用改模型也能保小目标。
5.4 测试集指标高,换一块布就拉胯:背景过拟合
现象:测试集mAP有0.8,看似不错。一到工厂现场,换了一卷不同底色或纤维密度的布,检测效果直线下降。
原因:训练和测试数据来自同一个布卷或同一条产线,模型学到的是这块布的纹理背景,而不是瑕疵本身。布匹质检往往只采了几卷样本,背景多样性不足。
解决:收集不同底色、不同编织密度的布样重新扩数据。如果条件有限,可以先做数据增强模拟底色变化,用hsv_h、hsv_s扰动和亮度抖动,缓解背景过拟合,但这只是亡羊补牢,本质还需要真实多样性样本。
5.5 class文件中文乱码或类别错位:编码与顺序
现象:class.txt里直接写了中文类名,训练时控制台报编码错误,或者模型训练完了输出的类别名和对应关系全乱。
原因:Windows下文本文件默认可能是GBK编码,YOLO加载yaml和names时统一按UTF-8读,遇到GBK字符直接崩溃或乱码。
解决:用文本编辑器把class.txt另存为UTF-8无BOM格式。data.yaml里如果写了中文names,同样要保持UTF-8。更稳妥的做法是class文件和names全用英文或拼音,中文只在注释里解释,避免跨平台编码问题。
6. 验证与进阶:用mAP、混淆矩阵和可视化检查瑕疵模型
模型训练完成后,第一件事不是急着导出部署,而是用验证集做一次完整评估。跑以下命令:
yolo val model=runs/detect/train/weights/best.pt data=fabric.yaml输出里主要看几个指标:mAP50反映的是宽松IoU阈值下的整体水平,布匹瑕疵检测至少要到0.85才算合格;mAP50-95是严格阈值下的均值,小瑕疵多的地方通常只有0.5到0.7;Precision和Recall要在0.9左右,否则说明误检和漏检没有平衡好。
接下来打开训练输出目录里的混淆矩阵图confusion_matrix.png,重点看哪些类别互相混淆。比如油污被误判成深色织纹,破洞和线头分不清,这些都是数据标注质量问题,不是调参能解决的。看清楚之后再决定要不要合并类目、修正标注。
可视化这一步不要只看训练时保存的图片,我习惯单独挑十来张验证集里最难的图,用训练好的权重跑一次推理,肉眼检查检测框和置信度。框的位置准不准、有没有把纹理误检成瑕疵、置信度低于0.5的真实缺陷是不是完全没框出来,这些只看指标看不出来。
一切正常后,就可以导出ONNX部署到产线侧。命令很简单:
yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640导出后可以用ONNX Runtime在Jetson或者工控机上跑推理。如果瑕疵类别在视觉上高度重叠,比如油污包含了深浅程度不同的大量样本,纯目标检测会频繁漏检,可以换YOLOv8的实例分割权重做像素级分割,把瑕疵轮廓标出来,布匹质检的价值更高。
回到开头说的那份数据集,训练出合格模型的逻辑其实不复杂:先把目录和class文件理顺,再用划分严谨的数据训练,最后靠验证集和可视化把模型钉死。我有一个一直保留的习惯,任何数据集到手,先把val里最难的那几张图单独复制出来,训练完成后第一件事就是拿这几张图试推理。这个习惯帮我拦下了很多次"测试集指标很好、现场全崩"的翻车。希望帮到你。
本文还有配套的精品资源,点击获取