ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

瓷砖表面9类缺陷检测:YOLO数据集划分与训练避坑指南

瓷砖表面9类缺陷检测:YOLO数据集划分与训练避坑指南 简介面向瓷砖质检场景的 YOLO 格式缺陷检测数据集覆盖划伤、边角异常、色点/色块瑕疵、光圈瑕疵等 9 类问题标注统一为 classes、x_centre、y_centre、w、h 相对坐标可直接用于 YOLOv5 系列模型训练。数据按训练集 3552 张、验证集 888 张划分图片与 txt 标签一一对应目录结构紧凑压缩包约 63.81 MB共 2000 个文件其中 1999 个为标注标签另含 show.py 可视化脚本无需调整参数即可绘制边界框便于抽样核对标注效果。配套专栏给出 YOLOv5 改进实战参考已吸引 181 人学习适合需要表面缺陷检测数据做迁移训练、模型评估或论文实验的开发者与研究人员也可配合专栏中的改进思路进一步复现和验证模型性能。1. 瓷砖表面瑕疵检测遇到 9 类缺陷时数据集划分比模型结构更关键做瓷砖表面质检的人容易产生一个误会只要把裂纹、色差、针孔这些缺陷都标清楚模型就一定能学会。真正上过产线的人才知道9 类瑕疵的难度极不均匀角损和边损长得像釉裂和跳釉换一种打光就分不开。标得再整齐的数据集只要 class 文件的顺序、txt 里的坐标、train/val 的划分错一样YOLO 训练过程不报错最后给出的 mAP 却全不可信。这篇文章就来拆一套划分好的瓷砖表面瑕疵 YOLO 数据集9 个类怎么命名不打架YOLO txt 的归一化坐标怎么换算目录怎么组织class 文件写在哪里数据可视化脚本到底在验证什么然后给一套能照跑的 YOLOv8 训练与排查路线。适合手头正拿着瓷砖照片准备转 YOLO 格式的视觉工程师也适合刚拿到公开数据集想跑通全流程的算法同学。2. 把瓷砖原图整理成 YOLO 数据集9 类命名、坐标换算和目录结构拿到一批瓷砖照片比较稳健的做法不是马上打开标注工具而是先把类别清单、标注规范和划分方式定下来。类别的排序或命名一旦中途发生变化所有 YOLO 数据集的 txt 文件都要跟着重导很多试错成本是在这一步付出去的。2.1 先把 9 类缺陷的边界定死class 文件才不会越写越乱YOLO 在训练时只认 txt 里的整型索引显示标签时才用到 class 文件里的名字。如果两类缺陷边界模糊同一个标注人员会在两个类别之间反复犹豫整个数据集的标注噪声会被拉高。常见的 9 类分割方式是按“视觉可分辨性”划分而不是按工艺工序划分因为算法学的是像素上下文不是窑炉里的物理成因。class_id类名典型判据容易混淆的类0crack线状裂纹形状细长长度明显大于宽度glaze_crack1edge_chip边缘局部崩缺不延及角部corner_chip2corner_chip角部崩缺常带两条边同时受伤edge_chip3pinhole针尖大小的孔洞直径通常小于 1mmbubble4bubble釉面鼓起的气泡直径比针孔大且带弧度pinhole5stain表面污渍、斑点边缘模糊color_abnormal6color_abnormal大范围色差或偏色边界不规则stain7bulge釉面凸起侧光下有明显阴影bubble8glaze_crack釉层浅裂纹比裂痕细多在局部网纹crack这张表不一定等同你手头项目的 9 类但建表思路是通用的每个类必须有肉眼可区分的判据且写明“不要和哪一类混”。class 文件一旦按这种顺序写死后面所有划分好的数据集、可视化脚本和混淆矩阵都围绕它运行。中途增删类别是最容易翻车的操作因为它会让旧 txt 里的历史索引全部失效所以我一般会先把类别名冻结再进入批量标注。2.2 YOLO 标注 txt 的归一化坐标一个乘除位置算错整套标注就作废YOLO txt 每一行的格式是class_id x_center y_center width height后四个值全部是相对原图的 0 到 1 小数而不是像素坐标。这样设计是为了换分辨率不重新标注。转换成 YOLO 格式时最常见的错误是直接从标注工具导出像素坐标忘记做除法。下面这段脚本把标注工具导出的 json 转成 YOLO txt适合矩形或多边形的最小外接框import json from PIL import Image def json_to_yolo(annotation_file, output_txt, class_mapping): 将标注工具导出的单张图 json 转成 YOLO txt。 class_mapping: 类名到 class_id 的字典必须和 class 文件顺序一致。 with open(annotation_file, encodingutf-8) as f: ann json.load(f) # 宽高必须从原图读取不能用缩略图尺寸 with Image.open(ann[imagePath]) as img: img_w, img_h img.size lines [] for shape in ann[shapes]: cls_name shape[label] cls_id class_mapping[cls_name] # 取多边形的外接框 xs [pt[0] for pt in shape[points]] ys [pt[1] for pt in shape[points]] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 归一化中心点和宽高全部除以原图宽高 x_center ((x_min x_max) / 2) / img_w y_center ((y_min y_max) / 2) / img_h box_w (x_max - x_min) / img_w box_h (y_max - y_min) / img_h lines.append( f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f} ) with open(output_txt, w, encodingutf-8) as f: f.write(\n.join(lines))逻辑说明先读原图宽高再遍历标注形状把多边形外接框的左上右下坐标换算成中心点加宽高最后同时除以宽和高。参数说明里最要紧的是class_mapping它决定了 json 里的类名映射到哪一个整数索引如果 class 文件顺序改过这个字典必须同步改否则 txt 里存的 class_id 会用错。提示矩形标注工具通常也返回多边形形式即使只有两个点min/max 求外接框的方法同样适用。写成函数后最好在单独一张图上跑一次再把 y 坐标对调这种低概率错误排除掉。2.3 划分好的数据集目录结构、class 文件和 data.yaml 三件套一个能直接训练的 YOLO 数据集目录结构固定到不需要看说明文档就能猜出用途tile_yolo/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── classes.txt ├── data.yaml └── scripts/ └── visualize.pyimages 和 labels 是平行目录只有文件名主体相同、后缀不同训练器才会把图和标注配对。分割点必须在图片和标注之间一致不能出现 train 有图但 val 有对应 txt 这类情况。文件作用需要注意的地方classes.txt按行列出 9 个类名顺序就是 class_id删改会让历史标注报废data.yaml告诉训练器路径和类别数量names 的顺序必须和 classes.txt 完全一致scripts/visualize.py把标注画回原图做质量检查类名索引也依赖同一个顺序data.yaml 是 Ultralytics YOLO 的主入口内容很薄# data.yaml 里 train/val 写目录路径不写具体文件 train: images/train val: images/val nc: 9 names: 0: crack 1: edge_chip 2: corner_chip 3: pinhole 4: bubble 5: stain 6: color_abnormal 7: bulge 8: glaze_crack这里有个容易踩的坑如果项目包同时给了 classes.txt 和 data.yaml应该以 classes.txt 为唯一基准写脚本时直接从 class 文件读类名而不是在代码里手抄一份。手抄意味着多一个维护点一旦两侧顺序错位训练不报错可视化脚本的文字却会对不上框。目录结构确认无误后再用一个数据可视化脚本把随机几十张图叠上标注框确认没有漏标和错位。这一步做扎实后面的训练环节才不会在数据质量上反复返工。3. 用 YOLOv8 训练自己的瓷砖瑕疵数据集最小命令和赛前必调的三个参数数据集划分好之后训练环节其实比想象中机械。真正需要认真对待的是环境对齐、预训练权重选择和三个关键参数。很多人在这一步急着调网络结构但瓷砖瑕疵属于纹理密集场景网络结构的收益远不如数据质量和输入分辨率来得大。3.1 环境装到能训练ultralytics 与 torch 的版本先对齐训练用 PyTorch 和 ultralytics 两套东西就够了不需要手动把 YOLO 论文里的模块复制一遍。先把环境检查好再安装依赖python -m pip install ultralytics python -c import torch; print(torch.cuda.is_available(), torch.__version__)这段命令先安装 ultralytics再检查当前 torch 是否能看到 GPU。如果输出False说明装的是 CPU 版 torch训练会慢到让人失去耐心。常见做法是先按 CUDA 版本装对应的 torch再装 ultralytics版本之间会自动对齐。CPU 机器也可以用来跑数据可视化脚本和短验证但正式训练还是找一张 8G 以上显存的卡更稳妥V100 这类卡在 batch 上会从容很多。3.2 一次能跑起来的最小训练命令确定好 batch、imgsz、patience拿到划分好的数据集后我一般先用最小配置验证 yolov8n 能不能正常收敛再换更大的预训练模型yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs200 \ batch16 \ imgsz640 \ patience40逻辑说明detect train是 Ultralytics 的统一训练入口modelyolov8n.pt表示用 COCO 预训练权重初始化。第一次运行会去仓库下载预训练模型不确定网络条件时也可以把权重文件提前放到运行目录下让命令自动识别。瓷砖这类纹理密集场景yolov8n 只适合用来建立基线后面要换yolov8s.pt或yolov8m.pt。参数说明里最值得盯的是三个batch、imgsz、patience。batch 由显存决定16 是 12G 显存常见起点显存不够就降到 8 或 4但 batch 太小会让 BN 统计不稳定所以宁可用更小的模型也不要用极小的 batch。imgsz 默认 640如果针孔类缺陷在图上只有十几个像素训练时会被降采样成几乎看不见保守做法是把 imgsz 提到 1280同时把 batch 折半。patience 是早停轮数40 意味着连续 40 轮验证集没有提升就停能避免后期过拟合浪费算力。如果训练到中途断了不需要从头开始yolo detect train resumeTrue这条命令会自动找最近一次 runs/detect 目录里的权重继续训练。resume 是保命选项训练中断时它比任何 checkpoint 手动管理都省事。提示如果你的场景是开放式类别可以考虑 YOLO-World 系列的预训练模型权重但对瓷砖这批封闭 9 类缺陷来说普通检测权重的稳定性和速度更好开放词表反而带来控制变量上的麻烦。3.3 loss 曲线和验证集结果怎么看不要只盯 mAP训练结束后runs/detect 目录下会留下 weights/best.pt、last.pt 和 results.png。results.png 里有训练 loss、验证 loss 和 mAP 曲线真正要关心的是它是否同步下降。如果训练 loss 一路走低验证 mAP 却迟迟不涨优先怀疑类别不平衡、train/val 重复或 imgsz 太小而不是模型结构。瓷砖表面缺陷多为小目标验证集上最常见的现象是 crack 这种大目标很准pinhole 这类小目标几乎不报。此时应该先去看混淆矩阵而不是盲目加训练轮数。同一轮训练结束后还会生成 confusion_matrix.png这张图比 mAP 更有信息量。mAP 是一个综合数字混淆矩阵却能看到哪两类缺陷互相吞并。把它留好后面做 hard set 回归测试会用到。4. 数据可视化脚本与混淆矩阵检查 9 类瓷砖瑕疵标没标歪、漏没漏标数据可视化脚本经常被当成一个可有可无的辅助文件实际恰恰相反。它是整个 YOLO 数据集里负责“把错误暴露给人眼”的工具。训练中绝大多数不报错的诡异问题最终都是靠画框叠加图翻出来的。4.1 可视化脚本的两个职责框叠加和类别分布第一个职责是叠加框把 YOLO txt 里的坐标反算回像素画在原图上人眼可以立刻发现三类问题。框整体偏移说明坐标换算错误某个类完全没画框说明 txt 文件名对不上框里的类名文字错位说明 class 文件顺序和 data.yaml 不一致。第二个职责是统计类别分布9 类各自的样本数量差异过大时训练器会对少数类完全学不到。可视化脚本里顺便输出一张柱状图比事后看混淆矩阵更快发现问题。4.2 一个可改的 YOLO 数据可视化脚本画框、写类名、输出检查图下面这段脚本读取一张原图和对应的 YOLO txt输出叠加标注框后的检查图import cv2 import os def draw_yolo_boxes(image_path, label_path, class_names, output_path): img cv2.imread(image_path) if img is None: print(图片读取失败:, image_path) return h, w img.shape[:2] # 原图宽高反归一化必须用它 with open(label_path, r, encodingutf-8) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) 5: continue cls_id, xc, yc, bw, bh map(float, parts[:5]) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) # 类名从 class 文件读入索引必须和 txt 里的 int 一致 label class_names[int(cls_id)] color (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, label, (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(output_path, img) # 示例遍历 val 目录每张图生成一张 overlay 图 import glob class_names open(classes.txt, encodingutf-8).read().splitlines() for label_txt in glob.glob(labels/val/*.txt): base os.path.splitext(os.path.basename(label_txt))[0] image_file fimages/val/{base}.jpg out_file foverlay_val/{base}.jpg draw_yolo_boxes(image_file, label_txt, class_names, out_file)逻辑说明draw_yolo_boxes先读原图宽高然后把归一化中心点和宽高换算回像素坐标。换算公式里最容易错的地方是减半x1 用的是中心点减半宽y2 用的是中心点加半高顺序反了框就会偏到缺陷的另一侧。class_names必须直接读 class 文件因为脚本里的索引顺序一旦和 class 文件不一致画出来的框位置正确但标签文字错误这种错位用肉眼很难一次看出。参数说明output_path 建议放到单独的 overlay 目录不要覆盖原图glob 路径里的文件名要和 txt 完全一致如果原图是 png把后缀改成 png 再跑。脚本跑完随机翻 30 张图确认过才算数据质量这一关通过。4.3 混淆矩阵里的“总和不唯一”先查归一化方式再质疑模型用训练好的权重跑一次验证yolo detect val datadata.yaml modelruns/detect/train/weights/best.pt输出目录里的 confusion_matrix.png 是 9 类加上背景的矩阵。如果你发现每一行加起来的百分比不是 100%先不要怀疑模型翻车因为 YOLO 自带矩阵有时是按样本数归一化有时按预测概率归一化还包含背景类。先看图例和归一化方式再去读对角线。真正要读的是那些非对角线上的亮点。比如 pinhole 和 bubble 互相误判严重说明这两类在 640 分辨率下像素尺寸太小特征被降采样抹平了crack 和 glaze_crack 互相误判说明标注时边界分类没有遵守“线状裂痕归 crack、釉面细纹归 glaze_crack”的标准。解决顺序是先修标注口径再提分辨率最后才想到换网络结构。5. 瓷砖表面 9 类瑕疵检测的 4 个避坑记录现象、原因、解决下面的四个问题来自标注、可视化、训练和评估四个环节都是我见过好几次的真实情况。每条都按现象、原因、解决三个步骤处理可以直接对号入座。5.1 训练不报错但可视化脚本画出的类名总是错位现象训练过程一切正常验证 loss 也在下降但把预测结果画回原图时某个框上的文字显示的类名和框的物理位置明显不对比如明明是边损文字却写着釉泡。原因class 文件顺序和 data.yaml 里的 names 顺序不一致。YOLO 学习时用的是 txt 里的整数索引推理时用它去 names 里查字面名两侧顺序一旦错位模型没有错文字层全乱了。解决先把数据集里所有 txt 的最大 class_id 找出来确认它小于 ncfor f in labels/train/*.txt labels/val/*.txt; do awk {print $1} $f done | sort -nu | tail -1这条命令统计所有标注里出现的最大索引。如果输出大于等于 9说明有 txt 越界如果小于 9说明某些类没有样本或标丢了。然后把 classes.txt 和 data.yaml 的 names 按行 diff确认完全一致后再跑一次 4.2 的可视化脚本用肉眼复核。5.2 可视化脚本画出的框整体往右下偏现象叠加框和真实缺陷看起来贴合但放大后每一帧都偏离缺陷越大偏移越明显小缺陷在视觉上反而容易被忽略。原因反归一化时用错了参考宽高或者把中心点加半宽误当成右下角。比如在缩略图上做了标注却用原图宽高去反算所有坐标都会成比例漂移。另一种常见错法是x2 (xc bw) * w忘了减半宽。解决统一按 **x1(xc - bw/2)wx2(xc bw/2)w计算并且宽高从原图读取不要写死在代码里。你先挑一张只有一个框的图手动算一遍真实像素再跑脚本对比这一步能把 90% 的换算问题拦住。5.3 换产线打光后训练到一半 loss 变成 NaNBN 崩溃现象模型在同一批瓷砖图上收敛正常换产线灯光后从某个 epoch 开始 loss 突然变成 nan之后权重输出一片空白。原因不同产线的亮度方差太大默认学习率下 BN 层统计被带崩。瓷砖表面纹理本身空白区域多一旦某 batch 里过曝或过暗样本集中批量归一化计算的均值和方差出现极端值训练过程就会在几个 step 内崩溃。解决把学习率从默认值往下调例如lr00.005同时给训练集加入亮度、对比度、噪点的增强。Ultralytics 里可以通过hsv_h、hsv_s这类参数控制颜色扰动但亮度的真实波动范围还是得靠数据增强脚本补齐。如果已经崩了用 last.pt 以更低学习率继续训练不要从 nan 的日志里强行恢复。5.4 验证集 mAP 很高现场新瓷砖却漏检严重现象训练时验证集 mAP 达到 0.95一上线换全新批次瓷砖漏检率肉眼可见尤其是边损和角损。原因train/val 划分不是按批次而是按文件名随机切。同一批瓷砖的不同图同时出现在训练集和验证集纹理背景被模型记住了验证指标变成“开卷考试”。真正陌生的纹理一到泛化能力立刻露馅。解决划分好的数据集里如果按文件名随机切分建议重新按产品批次或窑车编号分组保证同一批原图只在 train 或只在 val 出现。再加一步图像哈希去重剔除完全相同或几乎相同的图避免序列帧跨集合重复。这一步会让 mAP 表面上降低但换来的才是现场可用的指标。6. 把最容易混的两类单独拉成 hard set放进每次回归验证mAP 0.95 是给人看的产线更关心的是哪些缺陷会互相漏掉。我不太相信全局指标更信任一张专门收集难样本的验证集。这个做法不复杂却能在每次改模型后稳定地回答“新权重有没有把老问题带回来”。6.1 hard set 怎么构造回归门槛怎么定从第 4 章的混淆矩阵里找到最容易互相误判的两对类别比如 pinhole 和 bubble把 val 标注目录里同时包含这两类的图片单独刨出来组成一个很小的 hard setmkdir -p hard_set/{images,labels} for f in labels/val/*.txt; do if grep -E ^(3|4) $f /dev/null; then cp $f hard_set/labels/ cp images/val/$(basename $f .txt).jpg hard_set/images/ fi done这段命令把任何含 pinholeclass_id3或 bubbleclass_id4的验证样本复制到独立目录。逻辑是只留下最难判别的样本把大量背景简单的图排除在回归集之外。跑完后用训练好的 best.pt 做一次推理再用 4.2 的脚本把所有预测叠加出来人工核对两件事hard set 的召回率有没有到 95% 以上以及两类之间的互相误判率有没有被压下来。我自己的习惯是每次训练完第一件事不是看排行榜而是把 hard set 跑一遍。很多模型在常规验证集上很漂亮一到这种边角场景就翻车。把 hard set 固化下来等于给以后的自己留了一颗后悔药也顺便解决“改完 A 缺陷又把 B 缺陷改没了”这个经典难题。这个流程看起来只多花半小时回到陌生现场时却能省下一整天的血泪排查希望帮到你。本文还有配套的精品资源点击获取
返回列表