简介毛巾缺陷检测数据集包含435张毛巾缺陷训练图片及配套标签面向毕业设计、课程项目及目标检测入门者可直接用于缺陷识别相关模型训练与评估。压缩包共1307个文件大小约11.72MB其中jpg图片提供原始训练样本436个xml文件采用VOC格式标注436个txt文件采用YOLO格式标签文件两类标注可满足不同检测框架的需求省去手工标注环节。该数据集覆盖毛巾常见缺陷类别样本可供YOLO、Faster R-CNN等模型开展训练与验证若需扩充样本量可自行进行数据增强处理。目前已有421人学习下载适合需要快速获取标注数据完成毕设实验或算法对比的开发者。1. 毛巾缺陷检测数据集435张图能训出什么、训不出什么毛巾产线的质检环节大多数工厂还在靠人工在灯箱下翻布漏检率直接和当班师傅的疲劳程度挂钩这条线常被行业里吐槽是“玄学质检”。一个视觉方案能不能落地第一步就是数据从哪来。这个毛巾缺陷检测数据集提供了435张带标注的毛巾图片标签同时给了Pascal VOC格式的xml和YOLO格式的txt。它的价值在于你不用先折腾标注格式图片、xml、txt三套文件一一对应拿来就能喂给YOLO训练。它适合两类人一是刚入行缺陷检测、想拿真实工业图走通完整链路的新手二是已经在做纺织质检的工程师想验证小样本条件下mAP能到多少、算法选型合不合理。2. 两种标签格式拆解XML里的Pascal VOC和YOLO的txt差在哪2.1 数据集目录的常见组织方式拿到数据先别急着训练把目录结构看清楚。这个数据集的典型排布是三块内容平铺一个文件夹放jpg图片一个放同名xml另一个放同名txt。xml对应Pascal VOC标注格式txt对应YOLO格式文件名和图片一一对应只是扩展名不同。这样组织的好处是任何训练框架都能快速索引不会出现标签和图片对不上的问题。一个容易忽略的细节是图片文件如果有非jpg后缀比如png或bmp写遍历脚本时必须用glob去匹配后缀不能硬编码成.jpg。工业数据集经常掺着几种后缀统一转成jpg再做后续处理能省掉很多踩坑时间。另外xml的文件名和图片名偶尔会存在前后缀不一致的情况转换前先做一次名字对齐遍历时按图片名去索引xml缺xml的图直接跳过并记录到日志里而不是反过来拿着xml找图。2.2 XML里的Pascal VOC结构手把手读一遍用文本编辑器打开一个xml内容是这样一个骨架annotation folderJPEGImages/folder filenameimg_001.jpg/filename size width1280/width height720/height depth3/depth /size object namestain/name bndbox xmin320/xmin ymin180/ymin xmax450/xmax ymax230/ymax /bndbox /object /annotation这段结构里size下面的width和height是整张图片的宽高object可以重复出现多次每出现一次代表图中一个检测目标。name是类别名bndbox里是目标左上角和右下角的绝对像素坐标。如果一张图里有五个缺陷就会看到五个object块。新手最常见的困惑是拿记事本打开xml看到一堆挤在一行的内容就晕了。其实xml解析不需要靠肉眼直接用Python的ElementTree库遍历//object节点就行。另一个坑是标注工具偶尔会把name拼写写错比如少一个字母训练时类别数量对不上模型直接报错。所以拿到数据集第一步是统计name一共有几种、拼写是否一致用一行set(root.findall(object/name))就能查清楚。2.3 YOLO格式的归一化坐标从四个角到一个中心点YOLO的txt标签每一行代表一个目标格式是class_id x_center y_center width height五个数字用空格分隔后四个数值都相对图片宽高做了归一化。上面那段xml对应的YOLO行是0 0.300781 0.284722 0.101562 0.069444计算逻辑很简单x_center等于(xmin xmax) / 2 / width框宽等于(xmax - xmin) / width纵坐标同理。归一化之后不管原图是1280x720还是640x480标签都落在0到1之间模型训练时不用关心输入图片的绝对尺寸。这种设计的另一个好处是缩放、翻转等数据增强操作不需要重算坐标只要对坐标做同样的变换即可。2.4 XML转YOLO一个能直接跑起来的转换脚本实际工程里你拿到的数据不总是两种格式齐全常见情况是别人给你xml你自己要跑YOLO。这里写一个批量转换脚本把Pascal VOC的xml转成YOLO的txtimport xml.etree.ElementTree as ET import os def convert_xml_to_yolo(xml_path, out_dir, class_list): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_list: continue cls_id class_list.index(name) 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) # 坐标越界时钳制到图像边界防止训练时出现负数宽高 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines)) # 用法把xml文件夹路径、输出txt文件夹路径、类别列表按实际修改 convert_xml_to_yolo(annotations/xml, labels/txt, [stain, fold, hole])代码里值得注意的有两个点。一是root.find(size/width)这种路径写法前提是xml里没有嵌套命名空间如果根节点带了xmlns属性find会失效需要先剥掉命名空间再解析。二是坐标钳制这段不能省标注工具偶尔会画出超出图像边界的框不处理的话训练时YOLO会算出负的宽高loss直接变成NaN。转换完建议随机抽30个txt和原图做可视化比对确认框的位置对得上再做训练这一步虽然看起来多余却是整个流程里性价比最高的质检动作。3. 用YOLOv8训练这个数据集最小可复现的目录与命令3.1 labels和images必须按train/val分好而不是平铺把数据集直接扔给yolo detect train是不行的YOLOv8要求images和labels保持相同的子目录层级。常见做法是建四个目录images/train、images/val、labels/train、labels/val。划分比例按小样本惯例走训练集和验证集按9比1或8比2都可以但划分前先做一次shuffle避免原数据集里同类图片扎堆。写一个按文件名列表切分的脚本import os import random import shutil random.seed(42) img_files [f for f in os.listdir(images) if f.endswith(.jpg)] random.shuffle(img_files) val_ratio 0.2 val_count int(len(img_files) * val_ratio) for i, img in enumerate(img_files): sub val if i val_count else train label img.replace(.jpg, .txt) shutil.copy(fimages/{img}, fimages/{sub}/{img}) if os.path.exists(flabels/{label}): shutil.copy(flabels/{label}, flabels/{sub}/{label}) print(ftrain: {len(img_files) - val_count}, val: {val_count})这段脚本的逻辑是先shuffle再按固定比例切分seed固定保证每次复现结果一致。要注意的是图片和标签必须用同一份img_files列表不能分别shuffle两次否则标签和图片全错位了。现实中经常遇到的问题是少数类集中出现在某几张图上切完才发现验证集里全是某个类这种情况就需要按图片分组、按类别实例数做分层切分后面避坑章再展开说。3.2 data.yaml类别顺序必须和训练时一致在项目目录下建一个data.yaml内容如下train: images/train val: images/val nc: 3 names: [stain, fold, hole]nc的值必须和names列表长度一致names的顺序就是模型输出的类别顺序。这个顺序在转换标签、训练、推理三个阶段要保持一致。如果中途改过一次类别名比如把fold改成wrinkle旧模型的权重和新标签就对不上了推理结果会全部错位。建议把这份yaml提交到代码仓库里当成项目配置的一部分管理而不是每次训练前临时敲一遍。3.3 训练命令为什么小样本要选yolov8n而不是yolov8x执行训练的命令如下yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience30 \ save_dirruns/towel_defect参数说明modelyolov8n.pt是nano版本只有约315万参数适合小数据量imgsz640是输入分辨率毛巾缺陷往往是小目标如果显存允许可以提高到960或1280但batch要相应减半patience30表示30轮验证集mAP没提升就早停防止过拟合跑满100轮。数据量只有435张时不要选yolov8x这类大模型参数量上去了但数据喂不饱验证集mAP反而比nano低这不是玄学是欠拟合和过拟合同时存在的典型表现。提示imgsz提高后batch大小按显存余量调整8GB显存下imgsz1280建议batch4到8。435张图训100轮在单张消费级显卡上大约20到40分钟。训完去看runs/towel_defect/weights/下的best.pt和last.ptbest.pt是验证集上表现最好的权重部署时用它。3.4 训练日志里到底该看哪些指标训练结束不要只看一个mAP数字把results.png打开看五条曲线train/box_loss、train/cls_loss、val/box_loss、val/cls_loss、metrics/precision。小样本数据集最常见的现象是train的box_loss一路下降val的box_loss到某个点开始反弹这说明模型开始背训练集特有的纹理噪声了。yolo损失函数里box_loss是CIoU回归损失cls_loss是交叉熵分类损失两个指标要同时观察。只看分类不看框的位置或者只看框不看类别都会漏掉问题。毛巾这类目标框通常标得很准但几类缺陷在视觉上边界模糊分类比定位更容易翻车。训练中断也不需要从头再来yolo detect train加一个resumeTrue参数会自动从runs/towel_defect/weights/last.pt继续这对长训练任务特别有用能省掉大半重跑时间。4. 毛巾缺陷检测的5个避坑点小样本训练的翻车现场4.1 类别不平衡某类只有几十个实例模型直接忽略它现象训练结束后PR曲线里某个类别完全没有曲线或者precision和recall都是0。原因这个类别在整个数据集里只占百分之几yolo损失函数里的分类损失被多数类主导模型倾向于把所有目标都预测成出现频率最高的那类。解决按类别统计标注框数量发现严重不平衡时对少数类图片做过采样把少数类的图片多复制几份放进训练集或者对少数类单独做小幅旋转、平移、缩放扩充。复制样本会让模型多看几遍但不增加多样性所以优先配合增强一起用。4.2 小目标缺陷被resize抹平imgsz不能太小现象毛巾上的断纱、脏点这类缺陷只有二三十个像素imgsz设成640时下采样到80x80的特征图上连一个像素都不到网络根本没机会学。原因特征金字塔底层的小目标特征在连续下采样中被稀释。解决把imgsz调到1280并开启YOLOv8的scale多样本增强或者在推理阶段用SAHI做过切片再拼结果这个方案能明显提高小目标召回。有一个快速判断方法把训练集中缺陷框的像素宽度统计出来如果中位数小于32个像素imgsz640几乎必然漏检。4.3 EXIF旋转信息导致框全部错位现象用手机或部分工业相机拍摄的图片带EXIF旋转标记PIL读出来是原始像素方向而xml里的坐标是按旋转后视角标注的结果训练出来的模型在正常图片上框的位置全部偏移。原因对同一张图OpenCV和PIL读出的宽高可能正好相反一个按存储方向读一个按显示方向读。解决做数据集清洗时统一走一遍ImageOps.exif_transpose把图片物理旋转后再保存后续所有环节都不要再用带旋转标记的原图。这一步要放在标注和转换之前做否则坐标全部白转。4.4 验证集划分泄漏同一条毛巾的两个图分别进了train和val现象训练时val mAP到0.85以上看起来稳了一到真实产线上效果只有一半。原因同一个批次生产的毛巾纹理背景几乎一样模型记住的是“这种毛巾纹理出现缺陷”而不是“缺陷出现的位置和形态”验证集和训练集背景相似度高mAP被虚高了。解决划分数据时先按毛巾的批次或花纹分组保证同一条毛巾的所有图只进一个集合最简单的方式是在文件名里加批次前缀按前缀groupby后再切分。4.5 空标签和坐标越界转换脚本少了两行判断现象训练时报错Image is from train set but no labels found或者loss曲线在一开始就出现NaN。原因有少量标注文件为空或者标注框超出了图像边界转换脚本没有做拦截。解决转换脚本里跳过空标签的txt生成对所有坐标做clip训练前再写一个小脚本扫描labels里每一行数值凡是小于0或大于1的直接清理掉。这一步千万不要省略435张图里哪怕只有一张数据坏了训练就可能白跑几十分钟排查起来比一开始多写两行判断麻烦得多。5. 435张图为什么能训出能用的模型迁移学习与数据增强5.1 小样本能跑通的关键是预训练权重不是网络结构很多人第一次看到435张图会怀疑觉得必须攒几千张才能训练。实际上缺陷检测和自然场景通用数据集在底层视觉特征上是共享的。YOLOv8的预训练权重在COCO上见过大量边缘、纹理和光照变化微调阶段只需要让模型学会“毛巾上的脏点长什么样”就行。反过来说如果不用预训练权重从随机初始化开始训435张图连特征提取层都学不出来效果几乎必然不佳。正因为是微调训练参数也要跟着调整。常见做法是学习率比从零训练低一个量级比如lr00.005而不是默认的0.01冻结前10轮backbone让它先在分类头上收敛之后再放开全网络。这样能避免预训练特征在训练初期被大幅破坏小样本尤其需要保护这类先验特征。5.2 数据增强参数配置哪些该开、哪些该关YOLOv8的训练参数里和增强相关的几项对小数据集影响很大。mosaic1.0是把四张图拼成一张训练正常应该开着因为等效扩大了样本多样性但毛巾图如果纹理高度相似mosaic容易拼出假背景可以降到0.5。hsv_h0.015、hsv_s0.7、hsv_v0.4是色调、饱和度、明度的随机扰动毛巾颜色分布窄饱和度和明度扰动可以适当调小。flipud0.5随机上下翻转要慎重如果缺陷有明显的方向性比如水印、压痕开上下翻转会引入反方向样本反而误导模型。一个可用的起始配置是mosaic0.5、hsv_h0.01、hsv_s0.5、hsv_v0.2、flipud0.0、fliplr0.5。5.3 用albumentations做更针对性的增强亮度扰动与应用示例如果YOLO内置增强不够最常用的做法是训练前用albumentations离线增强一部分样本把数据翻到一千张以上再训练。毛巾缺陷检测里最该加的两类一是RandomBrightnessContrast模拟产线上灯箱亮度漂移这类波动在白天和夜班交接时非常明显二是GaussianBlur模拟相机对焦不好或毛巾运动时的模糊。其他像Rotate要限制小角度毛巾是规则的长方形旋转太多会产生不真实的几何形态。import albumentations as A import cv2 aug A.Compose([ A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.6), A.GaussianBlur(blur_limit(3, 7), p0.4), A.HorizontalFlip(p0.5), ], bbox_paramsA.BboxParams( formatyolo, label_fields[class_labels], min_visibility0.3 )) # 读取原图和对应txt标签生成增强后的图与标签 img cv2.imread(images/img_001.jpg) boxes, labels [], [] with open(labels/img_001.txt) as f: for line in f: parts line.strip().split() boxes.append([float(x) for x in parts[1:]]) labels.append(int(parts[0])) augmented aug(imageimg, bboxesboxes, class_labelslabels) aug_img augmented[image] aug_boxes augmented[bboxes]bbox_params里的formatyolo告诉albumentations输入已经是归一化中心点格式min_visibility0.3会把被裁剪掉大部分的目标过滤掉避免增强出的标签出现质量很差的框。离线增强的注意点是标签要像这样同步更新不能用增强后的图配原始的txt否则框的位置全是错的。增强后的新图建议命名加后缀比如img_001_aug1.jpg避免覆盖原始文件。5.4 一种快速判断数据够不够的方法看train和val的差距每次训练完直接对比train和val的loss曲线。如果train loss一直在降、val loss到了中间开始回升说明数据量不够模型在背训练集纹理优先加强增强强度如果train和val都降不下去说明特征学习能力不足应该换更大的预训练模型或者调大输入分辨率。435张图在这个环节的典型表现是train能降到很低、val压不住这时候不要盲目加数据先做困难样本分析把漏检的那几类单独挑出来看标注质量。很多时候漏检不是数据不够而是标注框本身就不齐模型学到的是一个含糊的目标边界。6. 验证阶段别只盯mAP用混淆矩阵找漏检类别再决定要不要补数据训练结束后跑一遍验证集预测然后看runs/towel_defect/confusion_matrix.png这张图。每一行代表真实类别每一列代表预测类别最直接看到的是某些缺陷类别大量被预测成背景。这才是模型上产线之前的真实风险而mAP只是把所有类别的表现平均成了一个数单类崩溃被掩盖掉了。我自己的习惯是部署前定一个验收标准每个类别的recall都到0.8以上且推理速度满足产线节拍。如果某类recall只有0.5优先找对应类别的漏检图片看是标注框太小、光照变化太大还是标注本身有噪声然后针对性补数据。从435张扩充到能上线的规模补什么也很讲究先补漏检最多的类别再补困难负样本也就是纹理接近缺陷但没有缺陷的图最后才补一般正常样本。负样本尤其重要它能直接压掉误检让模型不会把干净的毛巾误判成有缺陷。我第一次拿这个数据集训完mAP到0.88就以为完工了结果实测下来漏检全集中在压痕这一类上。返工查标注才发现那类框的边界本身就标得不齐重新清洗了一轮标注之后效果才真正稳定。花在验证和分析上的时间永远比盲目堆数据更有价值希望帮到你。本文还有配套的精品资源点击获取