钢板表面缺陷检测:混合DAGM与铝型材数据及高斯噪声扩充实践 简介面向缺陷检测与目标检测任务这份钢板表面缺陷数据集涵盖划伤、孔洞、焊缝三类典型表面缺陷由铝型材数据集与德国DAGM数据集混合制作并引入高斯噪声扩充样本适合用于训练YOLO、SSD、Faster R-CNN等常见检测模型。该分卷压缩包共2000个文件以1690张jpg原图、1690个xml标签及1个说明txt为主体图片与标签一一对应压缩包约754.93MB目录已整理接入常见目标检测框架即可直接训练。部分标签为人工标注质量较有保障提供的是分卷上传的第一部分完整数据集共4282组图片与标注按需下载后续部分即可获得全套数据适合需要开展缺陷检测算法对比、模型调优或工业质检落地的学生与工程师。已有1023人学习浏览可用于模型训练、算法验证与实验分析省去自行爬取、清洗和标注数据的成本。1. 这块钢板表面缺陷数据集为什么值得自己混一份出来做过钢板表面缺陷检测的工程师都有同感模型跑通不难难的是手里没有够用的数据。德国DAGM数据集是工业视觉检测领域的标准纹理缺陷库铝型材缺陷数据集则是贴近真实产线的表面瑕疵样本把这两个数据源混合制作再用高斯噪声扩充数据集是很多工业视觉项目起步阶段最务实的打法。这套方案能解决两个具体问题一是让模型同时见过实验室级纹理和现场级表面状态二是把原本几百张的原始样本扩到能让 YOLO、MMRotate 这类模型真正收敛的量级。适合手头缺陷图不足千张、又不想一上来就依赖生成模型造数据的团队。我下面按自己完整跑过的流程把混合逻辑、标注转换、噪声参数和踩过的坑逐一拆开。2. 两个数据源谁是谁为什么德国 DAGM 能和铝型材缺陷混在一起训练2.1 DAGM 的十类纹理与弱监督标注格式DAGM 指的是德国 DAGM 协会当年为工业光学检测举办的竞赛所发布的数据集全称里带“Weakly Supervised Learning”字样意思是它鼓励用尽量少的强标注做缺陷检测。实际用下来它的标注并没有那么弱每个缺陷区域在配套的文本文件里由一组多边形顶点坐标描述像素是绝对坐标图像本身是灰度 TIF。十类背景纹理差异很典型。有的是规则颗粒状织构缺陷是细小划痕有的是均匀细纹理缺陷是暗斑或孔洞还有的是周期性的条纹背景缺陷往往是小块隆起或凹坑。这些纹理的共同点是周期性明显、灰度分布集中和真实钢板表面的轧制纹理在视觉结构上非常接近。每类样本量不大带缺陷的大概百张量级无缺陷的负样本会多一些。这个规模直接训练检测模型是不够的但它胜在缺陷类型干净、标注坐标准确作为混合数据集里的“标准成分”非常合适。2.2 铝型材缺陷数据集的形态与 VOC 标注铝型材表面缺陷数据集是国内某次工业算法竞赛里放出来的图像就是真实的铝型材阳极氧化表面缺陷类别常见的有擦花、桔皮、漏底、杂色、喷流、脏点等不同版本类别数在六到十几类之间浮动。图像通常是 JPG 彩色图常见分辨率是 256×256标注格式是 PASCAL VOC 的 XML每个缺陷一个 bndbox 框。铝型材表面和钢板表面的相似度比想象中高都有细长的拉痕、团状的氧化斑、局部底色不均的“漏底”。这些缺陷边界模糊对比度低和 DAGM 里那种清晰的小斑点相比难度更高。也正因为如此用铝型材数据去补充 DAGM能让模型从“实验室检测员”变成“产线质检员”泛化性提升一截。2.3 混合的依据缺陷尺度重叠与背景纹理互补把两个数据源混在一起不是拍脑袋而是看三个维度是否匹配。第一个维度是缺陷尺度。DAGM 的缺陷大多在几十像素量级铝型材的擦花、脏点也在这个范围网络输入分辨率统一后锚框分布不需要大改。第二个维度是前景背景关系。两个数据源都是“复杂纹理背景 小面积缺陷”模型学习的是缺陷与局部背景的灰度差异而不是某一类特定纹理。这决定了混合训练时特征提取层不会互相打架。第三个维度是背景互补性。DAGM 的背景是规则的周期性纹理铝型材背景是相对均匀的金属表面。混合之后模型见过的背景类型更宽对实际钢板产线的光照变化、表面反光更有抵抗力。2.4 高斯噪声在这里的定位不是凑数是模拟传感器噪声很多人看到“高斯噪声扩充数据集”会下意识觉得这是简单的数据增强。实际上在金属表面缺陷场景里高斯噪声的角色更像是模拟工业相机在低照度、高增益下的传感器噪声。真实产线的 CMOS 相机输出图像总带有高斯型噪声方差随增益升高而增大。如果训练集全是干净的实验室图像模型会对这种噪声非常敏感容易出现“背景灰度抖动一点点就被判定为缺陷”的误检。所以这个扩充手段的本质是让模型对灰度小幅扰动变得不敏感把注意力放在缺陷区域的空域连续性上。后面第 4 章会讲到噪声参数一旦选大了缺陷本身的对比度被淹没模型就开始学噪声本身这一步需要精确控制。维度DAGM铝型材数据集图像类型灰度 TIF彩色 JPG典型分辨率512×512256×256标注格式文本多边形坐标VOC XML 矩形框缺陷特征小面积、边界清晰细长划痕、低对比度团块背景纹理规则周期纹理阳极氧化金属表面适合承担的角色标准缺陷形态基准贴近产线的真实样本3. 把两个数据源统一成 YOLO 能吃的格式目录规划与坐标转换脚本3.1 目录结构与划分策略拿到两个原始数据集后第一步不是写代码而是先把目录结构定好。我习惯按 YOLO 系列的通用布局来组织这样后面用 YOLOv5、YOLOv8 或者 MMRotate 训练自己的数据集时都不用再改路径。dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── class_names.txt └── data.yaml划分策略上先按缺陷类别做分层抽样保证 DAGM 的十个类和铝型材的每个类在训练和验证集里的比例一致。这里有一个容易被忽视的点先划分数据集再做高斯噪声扩充。如果先扩充再划分同一张原图的不同噪声版本可能同时出现在训练集和验证集后面验证集指标会虚高。3.2 DAGM 多边形文本转 YOLO txt 的转换脚本DAGM 的标注文本每行对应一个缺陷区域内容是 n 个点的绝对像素坐标形如“x1 y1 x2 y2 x3 y3 ...”。YOLO 格式要求每行是一个目标的分类和归一化中心点宽高格式是“class cx cy w h”。转换时先取多边形外接矩形再做归一化。import os import numpy as np def dagm_txt_to_yolo(txt_path, save_path, img_w, img_h, class_id0): 将 DAGM 多边形坐标文本转成 YOLO 格式 txt。 每个缺陷区域取外接矩形再归一化到图像宽高。 boxes [] with open(txt_path, r, encodingutf-8) as f: for line in f: coords line.strip().split() if len(coords) 6: continue pts np.array(coords, dtypenp.float32).reshape(-1, 2) x_min, y_min pts[:, 0].min(), pts[:, 1].min() x_max, y_max pts[:, 0].max(), pts[:, 1].max() boxes.append((x_min, y_min, x_max, y_max)) with open(save_path, w, encodingutf-8) as f: for x_min, y_min, x_max, y_max in boxes: cx ((x_min x_max) / 2) / img_w cy ((y_min y_max) / 2) / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h f.write(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n)逻辑说明这里的class_id需要根据你最终合并的类别列表自行映射。比如你决定“划痕0斑点1擦花2……”DAGM 的十类如果合并成“划痕、斑点”两类就把相应文本都映射到同一个 id。np.reshape(-1, 2)是把一维坐标序列解析成点列表必须保证坐标对数量是偶数否则说明标注文件有缺项。参数说明img_w和img_h传入 DAGM 图像的真实宽高通常是 512。如果你打算后续把图 resize 到其他尺寸不要在这里改参数应该让归一化基准始终是原始宽高resize 后的坐标变化放到数据加载阶段处理。3.3 VOC XML 转 YOLO txt 的转换脚本铝型材数据集的 VOC XML 标注转 YOLO 格式用 Python 标准库 xml.etree.ElementTree 就能解析不需要引入额外依赖。import xml.etree.ElementTree as ET def voc_xml_to_yolo(xml_path, save_path, img_w, img_h, class_map): 将 VOC XML 转成 YOLO 格式 txt。 class_map 是类别名称到整数 id 的字典。 tree ET.parse(xml_path) root tree.getroot() with open(save_path, w, encodingutf-8) as f: for obj in root.iter(object): name obj.findtext(name) if name not in class_map: print(f未知类别: {name}, 跳过) continue class_id class_map[name] bndbox obj.find(bndbox) x_min float(bndbox.findtext(xmin)) y_min float(bndbox.findtext(ymin)) x_max float(bndbox.findtext(xmax)) y_max float(bndbox.findtext(ymax)) cx ((x_min x_max) / 2) / img_w cy ((y_min y_max) / 2) / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h f.write(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n)逻辑说明VOC 的 bndbox 四个值都是绝对像素坐标转换逻辑和 DAGM 完全一致。关键在class_map的维护。铝型材里同一个语义缺陷在不同版本数据集里可能叫法不一致比如有的版本叫“擦花”有的版本叫“划伤”合并前必须人工核对把语义相同的类别映射到同一个 id否则模型会把同一类缺陷当成两个类别去学AP 直接被砍半。参数说明img_w和img_h从 XML 根节点的 size 字段读取更稳妥不要硬编码 256。部分版本的数据集图像尺寸有出入用实际尺寸做归一化基准才准确。3.4 坐标转换的边界坑外接矩形与大图切片DAGM 的缺陷标注是多边形而 YOLO 检测框是矩形取外接矩形意味着框内必然混入大量背景纹理。如果缺陷是细长划痕外接矩形里背景占比可能超过百分之六七十训练时模型会频繁看到“框里大部分是背景”的样本收敛速度明显变慢。常见做法有两种。第一种是直接用外接矩形训练但把这类缺陷的置信度阈值调低一点因为矩形 IoU 天然偏低是预期内的。第二种是改用 YOLOv8 的 segmentation 模式或 MMRotate 的旋转框这时 DAGM 的多边形坐标就能直接作为分割标签或旋转框的输入反而省掉取外接矩形的信息损失。我个人建议如果目标是检测 mAP用第一种如果目标是给产线输出缺陷位置和形状用第二种。还有一个边界坑是图像尺寸不一致。DAGM 是 512×512铝型材是 256×256混合训练时不能简单 resize 成同一个尺寸。我会把两者都先缩放到 512×512 短边填充或者统一用 640 输入但保持原始宽高比做 letterbox。直接在代码里改标签归一化基准只能解决“坐标对不对”的问题解决不了“小缺陷被 resize 抹掉”的问题后者在避坑章里单独说。4. 高斯噪声扩充的实测参数从 σ10 到 σ50缺陷可见度怎么取舍4.1 三种加噪方式固定方差、信噪比控制、局部背景加噪高斯噪声扩充看起来是“图像加噪声”一句话的事但不同加噪方式对训练结果影响很大。我常用三种方式按场景选择。第一种是固定方差加噪直接对整图加高斯白噪声噪声强度固定不变。import numpy as np import cv2 def gauss_noise_fixed(img, mean0.0, std25.0): 整图叠加固定方差的高斯噪声。 std 是高斯分布标准差0-255 灰度范围内取值。 典型区间 10-35超过 50 会严重破坏纹理结构。 noise np.random.normal(mean, std, img.shape) out img.astype(np.float32) noise return np.clip(out, 0, 255).astype(np.uint8)逻辑说明mean0表示加噪后图像整体亮度不偏移。std25.0表示噪声的灰度扰动幅度大约在 ±75 灰度级范围内肉眼可见明显噪点但仍能辨认缺陷轮廓。注意输入如果是 BGR 三通道彩色图np.random.normal生成的三通道噪声是独立的这符合真实传感器各通道噪声独立的情况。第二种是信噪比控制方式不直接设方差而是设期望的信噪比让算法根据图像本身的信号强度反推噪声功率更接近真实相机的噪声模型。def gauss_noise_snr(img, snr_db30.0): 按信噪比(dB)控制噪声强度。 snr_db 越大噪声越弱。工业图像常用 25-35dB。 img_f img.astype(np.float32) signal_power np.mean(img_f ** 2) noise_power signal_power / (10 ** (snr_db / 10.0)) noise np.sqrt(noise_power) * np.random.randn(*img.shape) out img_f noise return np.clip(out, 0, 255).astype(np.uint8)逻辑说明信噪比方式的好处是当图像本身偏暗时噪声会自动减弱不会出现暗部被噪声完全覆盖的情况。数值上30dB 表示噪声功率是信号功率的千分之一对 512×512 的灰度图来说视觉效果类似清晰图像加了轻微颗粒感。25dB 开始噪声肉眼可见20dB 以下就能明显看到雪花点不太建议用在低对比度缺陷上。第三种是局部背景加噪只对缺陷区域之外的背景加噪缺陷区域保持原样。这种方式适合缺陷对比度极低、加整图噪声会淹没缺陷的场景。def gauss_noise_background(img, mask, std20.0): 仅对背景区域加噪缺陷区域用 mask 保护。 mask: 和 img 同尺寸的二值图缺陷像素为 1背景为 0。 noise np.random.normal(0, std, img.shape) out img.astype(np.float32).copy() out[mask 0] noise[mask 0] return np.clip(out, 0, 255).astype(np.uint8)逻辑说明局部加噪的问题在于会引入“缺陷区域干净、背景带噪”的伪特征模型可能学到“干净区域就是缺陷区域”。所以这种方式我一般只在缺陷对比度低于 10 个灰度级时用并且搭配随机抖动缺陷区域的灰度值避免形成固定规律。4.2 用代码量化加噪后的缺陷可见度很多工程问题其实不是参数不会调而是缺少一个量化标准来判断参数合不合适。我会用下面这段代码统计“缺陷区域与局部背景的平均灰度差”对比加噪前后的差异。def defect_contrast(img, mask): 计算缺陷区域与周边背景的灰度差均值。 img: 灰度图或彩色图转灰度 mask: 缺陷区域二值 mask if len(img.shape) 3: gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) else: gray img defect_pixels gray[mask 0] background_pixels gray[mask 0] defect_mean np.mean(defect_pixels) background_mean np.mean(background_pixels) return abs(defect_mean - background_mean)逻辑说明对比度低于 8 个灰度级的缺陷人眼已经很难辨认模型也基本学不到。加噪之后对比度会被进一步拉低因为噪声同时叠加在缺陷和背景上拉低幅度约等于噪声 std 的 30% 到 50%。所以一个经验判据是加噪后缺陷对比度仍应大于 10 个灰度级否则这个噪声版本应该丢弃。使用建议对 DAGM 的细纹理类对比度普遍在 15-30 之间噪声 std 上限建议 20铝型材的擦花类对比度可能到 40 以上可以承受 std 30 的噪声。按这个逻辑给不同类设定不同噪声强度比全数据集统一一个参数要稳得多。4.3 铝型材与 DAGM 的差异化噪声配置两个数据源背景纹理不同噪声参数不能统一。DAGM 的规则周期纹理对噪声非常敏感。周期纹理一旦被强噪声破坏模型会误以为“纹理断裂处”是缺陷。实测 std 超过 25DAGM 的误检率明显上升。所以 DAGM 那部分我用 SNR 方式控制取值 30-35dB等效于很轻的传感器噪声。铝型材表面是不规则金属光泽本身灰度起伏就比较大对噪声的容忍度更高。我用固定方差方式std 取 20-35。偏暗的漏底类缺陷会自动被 SNR 方式压住噪声所以这类我改用 SNR 28dB 而不是固定方差。数据来源推荐方式参数区间选择理由DAGM 规则纹理类gauss_noise_snr30-35dB保护周期性纹理结构DAGM 粗颗粒纹理类gauss_noise_fixedstd 10-20粗纹理对噪声容忍度稍高铝型材细划伤类gauss_noise_fixedstd 20-30细长缺陷对比度高可承受更强噪声铝型材低对比度团块类gauss_noise_snr25-28dB防止噪声淹没本来就模糊的缺陷全部来源的负样本gauss_noise_fixedstd 10-15负样本只需轻微扰动增强背景鲁棒性4.4 扩充倍率与类别平衡的关系扩充倍率不是越大越好。高斯噪声的多样性有限同一张原图生成 10 个噪声版本第 6 个开始对模型几乎没有新增信息量。我一般让每个缺陷样本生成 2 到 3 个噪声版本总扩增 3 倍左右。真正该用扩充去解决的是类别不平衡。DAGM 十个类缺陷数量相差可能到 5 倍铝型材各类也不均衡。先统计每个类的样本数给少数类多配几个噪声版本给多数类少配甚至不配让各类比例拉平到 1.5 倍以内。这一步的效果比盲目扩充整体数据明显得多因为模型在类别均衡的数据上收敛快AP 也更稳。5. 数据集翻车实录五个必须提前处理的常见问题5.1 灰度图与彩色图混用第一个 epoch 就报维度错误现象DAGM 是灰度 TIF铝型材是彩色 JPG直接放进 DataLoader训练还没开始就报通道数不匹配。原因DAGM 单通道和铝型材三通道混在一个数据集里模型输入层固定是三通道灰度图必须复制成三通道或统一转灰度。解决预处理里统一转成三通道灰度复制保留灰度信息但满足网络输入维度。注意不要用 cv2.imread 默认的 BGR 读 DAGM显式指定cv2.IMREAD_GRAYSCALE读取再cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)。5.2 小缺陷被 resize 抹掉mAP 一直卡在 0.3现象原始缺陷只有 10×10 像素训练时图像 resize 到 416×416缺陷缩到 6×6 像素模型头几层下采样后特征图上一个点都覆盖不到。原因DAGM 缺陷本来就不大铝型材数据集的细划痕也只有 2-3 像素宽。无脑 resize 到小输入尺寸等于主动丢掉小目标。解决输入尺寸至少 512×512铝型材的 256×256 图不要直接拉伸到 640而是用 letterbox 保持宽高比并补齐灰边。更激进的做法是用 MMRotate 这类支持多尺度训练的框架训练时随机在 512-768 之间采样输入尺度让小缺陷至少在某些 epoch 里有足够像素。5.3 验证集泄漏扩充前没先划分数据现象加了高斯噪声扩充后训练验证集 mAP0.5 到了 0.97但到现场测试掉到 0.6。查了半天发现训练集和验证集里出现了同一张图的不同噪声版本。原因先混合两个数据集、再扩充、最后随机划分是最常见的顺序错误。同一缺陷原图加上不同噪声后看起来是不同样本但其实内容完全相同模型记住的是这张图的纹理细节而不是缺陷本质。解决严格按“先划分、再扩充”的顺序。划分时还要按缺陷 ID 分组保证同一缺陷的多个裁剪块不会跨集合。验证集和测试集永远用原始未加噪的图像噪声版本只进训练集。这样验证指标才是可信的。5.4 坐标转换“差一个像素”归一化基准不一致现象训练后预测框整体偏移看起来像是完全没对齐。逐项检查发现 DAGM 的坐标归一化用的是原始 512 宽高铝型材用 resize 后的 640两个数据集的标签基准不一样。原因混合数据集里两类标签的归一化基准不一致模型在训练时同一尺度下看到的框位置错误。这个问题不出在转换代码出在流程设计是典型的“单看每部分都对合起来就错”。解决统一归一化基准。所有标签都以原始图像宽高为准后续 letterbox 和 resize 的坐标变换由数据加载阶段统一处理不在写入标签时改基准。做一张表记录每个来源的原始尺寸转换脚本里显式读取图像实际宽高而不是硬编码。5.5 噪声淹没缺陷模型学到的不是缺陷而是噪点现象扩充后训练 loss 正常下降但验证集缺陷全漏检反而把背景纹理误报成缺陷。查看送入训练的样本发现噪声版本里缺陷已经和背景融为一体。原因高斯噪声强度超过了缺陷对比度模型无法在噪声中找到稳定的缺陷边界只能退而求其次学习背景纹理的局部异常形成错误特征。解决加噪前先跑一遍 4.2 里的对比度统计筛掉加噪后对比度低于 10 的样本。另一个补救措施是加噪时降低透明度也就是把原图和噪声图按比例融合比如 0.7 原图加 0.3 噪声既保留缺陷结构又引入扰动。这是把噪声强度和缺陷可见度的平衡控制住的有效手段。6. 用混淆矩阵和 Bad Case 验证这个数据集值不值得投入6.1 在 YOLOv8 上跑一轮对比实验混合数据集做完后先用 YOLOv8 跑一个快速验证判断这套数据到底值不值得继续投入。yolov8 detect train \ data/path/to/dataset/data.yaml \ modelyolov8n.pt \ imgsz512 \ epochs100 \ batch16 \ projectsteel_defect_exp \ namebaseline逻辑说明先用 nano 模型验证数据质量耗时短、显存占用小如果 nano 模型的 mAP 能到 0.7 以上再做放大版本的实验。如果 nano 本身就低于 0.5问题大概率出在数据而不是模型容量。data.yaml 里的类别顺序必须和转换脚本里 class_map 的 id 完全一致这个不一致是跑通后最容易翻车的点。6.2 混淆矩阵与漏检样本的归属分析训练完成后看三样东西混淆矩阵、PR 曲线、验证集 Bad Case。混淆矩阵能直接暴露类别混淆——比如 DAGM 的“暗斑”被大规模预测成铝型材的“脏点”说明这两个类在灰度特征上高度重叠要么合并成一个语义类要么需要更多差异化的噪声样本来拉开边界。Bad Case 分析时把漏检样本按来源打标签统计每个来源的漏检率。如果漏检集中在 DAGM 的某个纹理类说明该类的缺陷对比度太低需要给这类单独提高噪声版本里原图的占比如果漏检集中在铝型材的低对比度团块说明噪声参数还是偏大回退到 SNR 28dB 以上。6.3 后续还能怎么扩展这块数据这个混合方案跑通后可以平滑迁移到两个方向。一是旋转框检测铝型材的擦花和 DAGM 的细长划痕都有明显方向性参考 mmrotate 训练 DOTA 数据集的做法把标签从矩形框改成旋转框能显著降低细长缺陷的背景干扰。二是半监督迭代先用这套数据训练一个初版模型到产线采集真实钢板缺陷图后用模型预测加人工确认的方式批量生成伪标签再微调一轮这是从“实验室数据”过渡到“现场数据”最平滑的路径。我自己现在的习惯是每次混数据前先花半小时统计缺陷对比度分布和类别数量再决定噪声参数和扩充倍率。这块数据跑出来的模型在实际钢板样本上能省下不少返工时间。希望帮到你。本文还有配套的精品资源点击获取