资讯详情 钢铁表面缺陷检测图像数据集:1800张带标签图像实战指南
📅 2026/10/25 11:37:36
简介这份钢铁表面缺陷检测图像数据集面向从事机器视觉与工业质检研究的开发者、学生及算法工程师用于训练和评估裂纹、锈蚀、凹坑、划痕等表面缺陷的识别模型可作为监督学习与深度学习实验的样本来源。压缩包为zip格式共约2000个文件主体是1800张jpg缺陷图像并配有1400余个xml标注文件与1400余个txt说明文件另有少量data、names等配置类文件整体约26.81MB体量轻便、便于快速下载与本地部署。目前已有2453人学习下载具备一定的使用参考基础。图像与标签一一对应读者可直接据此划分训练集与验证集开展CNN等模型的训练、超参数调优与精度、召回率、F1分数评估也能用于数据扩增和预处理流程的对比实验为钢铁产线自动化缺陷检测方案的搭建提供现成数据支撑。1. 钢铁表面缺陷检测图像数据集1800 张带标签图像到底能撑起什么产线上跑视觉检测的同行大概都遇到过这种局面算法框架搭好了推理脚本也跑通了卡在最后一步——手里没有一批带标注的钢铁表面图像。钢铁表面缺陷检测图像数据集1800 张图像含标签这个标题说的就是这类小而精的工业质检数据资产图像来自热轧或冷轧带钢表面缺陷类型通常覆盖划痕、麻点、夹杂、氧化皮压入、边裂等几类标签以检测框或分割掩码的形式给出。1800 张不算大但对一个想快速验证缺陷检测方案、跑通从标注格式转换到模型训练再到指标评估全链路的团队来说这个量级恰好够用——既不至于训练不动也不至于一眼过拟合。适合谁适合做工业视觉落地的算法工程师、想复现缺陷检测 baseline 的学生、以及需要给产线质检做可行性验证的技术负责人。下面按“先看清数据、再跑通流程、最后避开坑”的顺序拆开讲。2. 先摸清 1800 张钢铁缺陷图像的底细类别分布与标注格式拿到一批工业图像最忌讳上来就train.py一把梭。钢铁表面缺陷有个特点类间极不均衡划痕可能几百张边裂可能只有几十张而且同一张图上可能同时出现多种缺陷。不先把这些摸清楚后面训练出来的模型大概率是“只会认划痕”的偏科生。这一章的目标是让你在动手训练前用几段脚本把数据的类别分布、标注格式、图像尺寸、缺陷面积占比全部量化出来。2.1 为什么钢铁缺陷数据必须先做分布统计钢铁表面的缺陷形态和自然图像里的目标检测任务差别很大。自然图像里一只猫占画面比例通常不小而钢铁表面的麻点可能只有十几个像素边裂则是细长条。如果直接套用 COCO 预训练模型的默认 anchor 尺寸小目标召回率会非常难看。更现实的问题是1800 张图像如果按 8:1:1 划分验证集只有 180 张某些稀有类别在验证集里可能一张都没有这时候算出来的 mAP 是失真的。我一般会先做三件事统计每类缺陷的实例数、统计每张图的缺陷数量分布、统计缺陷框的宽高比分布。前两个决定你要不要做重采样或类别加权第三个决定 anchor 怎么设或者要不要改用无锚框的检测头。这三项统计用一段 Python 就能跑完不需要任何深度学习框架。import os import json from collections import Counter, defaultdict # 假设标签是 COCO 格式的 annotations.json with open(annotations.json, r, encodingutf-8) as f: coco json.load(f) # 类别 id - 名称 cat_map {c[id]: c[name] for c in coco[categories]} inst_counter Counter() img_defect_count defaultdict(int) aspect_ratios [] for ann in coco[annotations]: cid ann[category_id] inst_counter[cat_map[cid]] 1 img_defect_count[ann[image_id]] 1 x, y, w, h ann[bbox] if h 0: aspect_ratios.append(w / h) print(各类别实例数, dict(inst_counter)) print(单图缺陷数分布, Counter(img_defect_count.values())) print(宽高比 min/max, min(aspect_ratios), max(aspect_ratios))这段脚本的关键在三个输出。inst_counter直接告诉你哪几类样本少得可怜少于总实例数 5% 的类别就要考虑过采样或者 focal loss。img_defect_count的分布如果大量集中在 1说明多数图是单缺陷模型不容易学到多缺陷共存的特征训练时可以适当做 mosaic 增强。aspect_ratios的极值如果超过 10 或者低于 0.1说明存在极端细长目标默认 anchor 的 1:1、1:2、2:1 三组比例覆盖不住需要补 1:4、4:1 这类比例。提示统计脚本跑完先别急着删把结果存成 CSV后面调参时回头对照能省很多“为什么这轮 mAP 掉了”的排查时间。2.2 标注格式转换从 COCO 到 YOLO 的四个边界坑钢铁缺陷数据集的标签常见两种COCO 的 JSON 和 Pascal VOC 的 XML。如果你打算用 YOLO 系列做 baseline就得转成每张图一个 txt、每行class cx cy w h的归一化格式。这个转换看着简单实际有四个坑会让你的训练直接翻车。第一个坑是坐标越界。钢铁图像在标注时标注员可能把框拖到了图像边缘外一两个像素转成归一化坐标后出现负数或大于 1 的值YOLO 读取时会直接跳过这个框等于悄悄丢样本。第二个坑是宽高为 0 的退化框通常来自标注工具的误操作。第三个坑是类别 id 从 1 开始还是从 0 开始COCO 习惯从 1 开始YOLO 要求从 0 开始差一位就是全部标签错位。第四个坑是图像文件名和标签文件名的对应关系有些数据集图像叫img_001.jpg标签却叫001.txt直接按同名匹配会漏掉一半。import json import os with open(annotations.json, r, encodingutf-8) as f: coco json.load(f) # COCO 类别 id 可能不连续重映射成 0 起始 cat_ids sorted({c[id] for c in coco[categories]}) id2idx {cid: i for i, cid in enumerate(cat_ids)} img_info {img[id]: img for img in coco[images]} os.makedirs(labels, exist_okTrue) skipped 0 for ann in coco[annotations]: img img_info[ann[image_id]] W, H img[width], img[height] x, y, w, h ann[bbox] # 边界裁剪把框限制在图像内 x1, y1 max(0, x), max(0, y) x2, y2 min(W, x w), min(H, y h) w, h x2 - x1, y2 - y1 if w 1 or h 1: # 退化框直接丢弃并计数 skipped 1 continue cx, cy (x1 w / 2) / W, (y1 h / 2) / H nw, nh w / W, h / H cls id2idx[ann[category_id]] stem os.path.splitext(img[file_name])[0] with open(flabels/{stem}.txt, a, encodingutf-8) as out: out.write(f{cls} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}\n) print(丢弃退化框数量, skipped)转换逻辑里有几个参数值得说清楚。max(0, x)和min(W, xw)这一对裁剪是必须的宁可把框裁小也不能让它越界因为越界框在 YOLO 的 dataloader 里会被静默过滤你看到的 loss 曲线正常但实际参与训练的样本少了。w 1 or h 1这个阈值不是随便定的钢铁缺陷里小于 1 像素的框没有检测意义留着只会污染训练。归一化保留 6 位小数是为了避免累积误差尤其是小目标框小数点后少两位可能就差出好几个像素。最后用a追加模式写文件是因为一张图可能有多个缺陷框每次追加一行天然支持多标签。转换完一定要做一次反向校验随机抽 20 张图把 txt 里的框画回原图上看位置对不对。这一步花五分钟能避免训练两天后发现标签全错位的血泪教训。2.3 图像尺寸与缺陷面积占比决定输入分辨率的关键数字钢铁缺陷检测的输入分辨率不能拍脑袋定 640。缺陷面积占比这个指标直接决定你该用多大的输入。如果麻点类缺陷平均只占图像的 0.1%在 640 输入下经过 backbone 的 32 倍下采样特征图上就剩不到一个像素神仙也检不出来。import json with open(annotations.json, r, encodingutf-8) as f: coco json.load(f) img_info {img[id]: img for img in coco[images]} ratios [] for ann in coco[annotations]: img img_info[ann[image_id]] area ann[bbox][2] * ann[bbox][3] ratios.append(area / (img[width] * img[height])) ratios.sort() n len(ratios) print(面积占比 P10/P50/P90, ratios[int(n*0.1)], ratios[int(n*0.5)], ratios[int(n*0.9)])如果 P10 低于 0.001我的建议是把输入提到 1024 甚至 1280或者改用带高分辨率特征的检测头比如在 stride 8 的特征图上做小目标分支。如果 P50 在 0.05 以上640 完全够用再大只是浪费显存。这个数字还影响数据增强策略小目标占比高的数据集随机缩放的下限不能设太低否则增强完目标直接消失。3. 用 1800 张图跑通缺陷检测 baseline训练配置与增强策略数据摸清楚了接下来是把它喂给模型。1800 张图做检测训练属于“小数据”范畴直接从头训大概率欠拟合或过拟合二选一。这一章讲清楚三件事为什么选迁移学习而不是从头训、钢铁缺陷场景下哪些增强有用哪些有害、以及一套能复现的训练配置。3.1 小样本钢铁缺陷检测为什么必须走迁移学习1800 张图按类别分下来每类可能就两三百个实例。检测模型 backbone 动辄几千万参数从头训需要的数据量是万级起步。迁移学习的逻辑是backbone 在自然图像上学到的边缘、纹理、角点特征在钢铁表面同样存在——划痕是边缘麻点是局部纹理突变氧化皮是区域纹理。所以加载在通用数据上预训练的权重只微调检测头加部分 backbone 层收敛速度和最终精度都会好很多。具体怎么冻结有讲究。我一般分三段backbone 的前 2/3 冻结后 1/3 用较小学习率base_lr 的 0.1 倍微调检测头用正常学习率。原因是浅层特征太通用钢铁数据量不足以撼动它们强行训反而破坏深层特征和任务相关必须放开。如果显存紧张也可以只训检测头但 mAP 通常会低 3 到 5 个点属于用精度换资源的取舍。# 以常见检测框架的配置思路示意非特定框架 API model build_detector(num_classeslen(cat_ids), pretrainedTrue) # 分段设置学习率backbone 浅层冻结深层小学习率head 正常 for name, param in model.backbone.named_parameters(): if stage1 in name or stage2 in name: param.requires_grad False else: param.requires_grad True optimizer build_optimizer([ {params: model.backbone.parameters(), lr: 1e-4}, # 深层微调 {params: model.head.parameters(), lr: 1e-3}, # 检测头 ])这里1e-4和1e-3的十倍关系不是玄学是经验值深层特征已经比较接近任务学习率大了容易把预训练权重冲垮检测头是随机初始化的需要正常学习率快速收敛。如果你的框架不支持分组学习率退而求其次用全局1e-4加 cosine 衰减也能跑只是前期收敛慢一些。3.2 钢铁缺陷场景下哪些数据增强真有用数据增强在工业质检里是个双刃剑。自然图像那套随机裁剪、大角度旋转、颜色抖动搬到钢铁表面可能直接制造出“不存在的缺陷”或者“抹掉真实缺陷”。我按有用程度排个序。第一梯队是水平翻转和垂直翻转。钢铁表面的划痕、边裂没有方向语义翻转后仍然是合理的缺陷形态等于把数据量翻倍几乎零风险。第二梯队是亮度对比度微调幅度控制在 ±15% 以内模拟产线光照波动这个很实用因为实际部署时打光不可能和训练集完全一致。第三梯队是小角度旋转±10 度以内模拟相机安装偏差超过这个角度缺陷形态就开始失真。要慎用的是随机裁剪和 mosaic。随机裁剪可能把边裂这种贴边缺陷裁掉一半标签框跟着变形模型学到的是残缺特征。Mosaic 把四张图拼一起对小目标检测有好处但钢铁缺陷里如果缺陷本身就在图像边缘拼接后边界处的缺陷会被切断产生大量噪声标签。我的做法是 mosaic 只在训练前 80% 的 epoch 开最后 20% 关掉让模型在干净数据上收尾。# 增强配置示意翻转必开亮度小幅旋转小角度mosaic 后期关闭 train_pipeline [ dict(typeRandomFlip, directionhorizontal, prob0.5), dict(typeRandomFlip, directionvertical, prob0.5), dict(typeRandomBrightnessContrast, brightness_limit0.15, contrast_limit0.15, prob0.5), dict(typeRandomRotate, max_angle10, prob0.3), dict(typeMosaic, prob0.8, close_epoch_ratio0.8), # 后 20% 关闭 ]close_epoch_ratio0.8这个参数的意思是训练进度到 80% 后自动关掉 mosaic不同框架写法不同核心思想是“前期靠增强提泛化后期靠干净数据收精度”。prob值也别全设 1.0翻转 0.5 是为了保留一半原始朝向的样本避免模型对翻转产生依赖。3.3 一套可复现的训练超参配置把上面这些拼起来给一套我实际用过、在 1800 张量级上比较稳的配置。输入 1024×1024依据 2.3 的面积占比统计调整batch size 8单卡 24G 显存下的安全值初始学习率 1e-3 配 cosine 衰减warmup 500 iter总 epoch 100优化器用 SGD momentum 0.9。类别不均衡用类别加权交叉熵权重按实例数的倒数归一化。参数取值说明输入分辨率1024×1024小目标占比高时上调batch size824G 显存安全值初始学习率1e-3检测头backbone 深层 1e-4学习率策略cosine warmup 500 iter小数据防早期震荡总 epoch100配合 mosaic 后 20% 关闭类别权重实例数倒数归一化缓解长尾NMS IoU 阈值0.5钢铁缺陷重叠少可略低训练过程中重点盯两个信号一是分类 loss 和回归 loss 的比例如果分类 loss 远大于回归 loss说明类别不均衡严重加权系数要调二是验证集上小目标类别的 AP如果它一直不涨而大目标涨得快回头检查输入分辨率和 anchor 配置。100 个 epoch 在单卡上大概几小时跑完先看整体 mAP再看每类 AP偏科严重的类别单独分析。4. 钢铁缺陷检测的避坑清单五条踩过的真实教训这一章不讲新东西专门讲翻车。下面五条都是我在类似量级的钢铁缺陷数据上真实遇到过的每条按“现象、原因、解决”写你对照自己的训练日志看大概率能提前避开。4.1 训练 loss 正常但验证 mAP 极低现象训练集 loss 稳定下降验证集 mAP 卡在 0.1 左右不动模型在验证图上几乎框不出东西。原因八成是标签坐标系搞错了。钢铁数据集的标注有时用左上角原点有时用左下角原点某些标注工具导出习惯转换脚本没做 y 轴翻转训练时模型学的是上下颠倒的框验证时自然全错。解决抽一张图把标签框画上去肉眼确认框是否套在缺陷上这一步比看任何指标都直接。4.2 稀有类别 AP 始终为 0现象划痕类 AP 能到 0.6边裂类一直是 0验证集里明明有边裂样本。原因通常是验证集里该类实例太少按 8:1:1 随机划分时边裂全被分进了训练集。解决改用分层划分保证每个类别在训练、验证、测试集里都有最低数量比如每类验证集至少 10 个实例稀有类别可以适当提高验证集比例。这个改动看着小但对指标可信度影响极大。4.3 模型在产线新图上大面积误检现象验证集指标不错换一批新拍的钢铁图像背景里的氧化皮纹理被大量误检成麻点。原因是训练集和测试集的拍摄条件不一致训练集可能来自单一产线、单一光照模型把某种背景纹理当成了缺陷的判别特征。解决训练时加强亮度对比度增强同时在负样本里显式加入“无缺陷但纹理复杂”的背景图让模型学会区分纹理和缺陷。如果条件允许新数据先做一轮小样本微调再上线。4.4 转换后的标签文件数量对不上图像数量现象图像 1800 张转出来的 txt 只有 1200 个训练时大量样本没有标签被当成背景。原因是文件名匹配逻辑写死了后缀图像是.jpg而标签生成时用了.png的 stem或者图像有子目录而标签全平铺在一层。解决转换脚本里统一用os.path.splitext取 stem并且转换后做一次集合比对打印出“有图无标签”和“有标签无图”的清单数量不为零就不往下走。4.5 增大输入分辨率后显存爆了但精度没涨现象按小目标统计把输入从 640 提到 1280显存直接 OOM降 batch size 勉强跑起来mAP 只涨了不到 1 个点。原因是单纯提分辨率小目标在特征图上的绝对像素确实多了但如果 anchor 尺寸没跟着调匹配上的正样本反而变少。解决提分辨率的同时重算 anchor用 2.1 里统计的宽高比分布做 k-means 聚类生成新 anchor或者干脆换无锚框检测头省掉这层调参。另外提分辨率前先确认小目标类别的 AP 是不是真的低如果低的是大目标类别提分辨率纯属浪费。5. 从 1800 张到可交付模型验证方法与一个提点技巧数据跑通、坑也避了最后一章讲怎么判断这个模型到底能不能交付以及一个在钢铁缺陷场景下实测有效的提点技巧。工业质检和学术刷榜最大的区别是mAP 高不代表能用漏检率和误检率在产线语境下比 mAP 更有说服力。5.1 用漏检率和误检率替代 mAP 做交付判断mAP 是 IoU 阈值下的平均精度它把定位质量和分类质量混在一起。产线关心的是两件事真缺陷有没有漏掉漏检率好产品有没有被误判误检率。这两个指标要单独算而且阈值要按业务定。# 按置信度阈值统计漏检与误检需先跑推理拿到预测结果 def eval_miss_false(preds, gts, conf_thr0.3, iou_thr0.5): miss, false_pos, total_gt 0, 0, 0 for img_id, gt_boxes in gts.items(): total_gt len(gt_boxes) pred_boxes [p for p in preds.get(img_id, []) if p[score] conf_thr] matched set() for pb in pred_boxes: hit False for i, gb in enumerate(gt_boxes): if i in matched: continue if iou(pb[box], gb) iou_thr: matched.add(i); hit True; break if not hit: false_pos 1 # 预测了但没有对应真值 miss len(gt_boxes) - len(matched) return miss / total_gt, false_pos / max(1, len(preds))conf_thr是业务阈值产线通常宁可误检不可漏检所以这个值会设得偏低0.2 到 0.3代价是误检率上升需要后处理或人工复检兜底。iou_thr在钢铁缺陷里可以设 0.5因为缺陷框通常比较规整不像自然图像里遮挡严重。算出来的漏检率如果超过 5%这个模型就不能直接上线得回去补数据或者调阈值。5.2 一个提点技巧难例挖掘加针对性补标1800 张的量级模型能力上限很大程度被数据卡住。与其反复调参榨那 1 个点不如做一轮难例挖掘。具体做法用当前模型在验证集和一批未标注的新图上跑推理把漏检的、误检置信度高的图挑出来人工复核后补标或修正标签再混进训练集重训。这一轮下来通常能带来 3 到 5 个点的实际提升比换 backbone 划算得多。我一般会按“漏检优先、误检次之”的顺序挑图每次补 100 到 200 张重训一轮看指标有效就继续两轮没动静就说明数据分布已经覆盖住了该考虑模型结构了。这个循环里最容易被忽略的是修正错误标签——有些“漏检”其实是标注漏了模型检出来是对的这种样本补上标签比任何增强都值钱。说个我自己的习惯每次重训前把上一版的漏检清单存一份新版本跑完对比看漏的是不是同一批图。如果是说明这类缺陷模型根本没学会得从数据侧找原因如果不是说明模型在泛化继续补数据就行。这个对比习惯帮我省过好几次盲目调参的时间。希望帮到你。本文还有配套的精品资源点击获取