简介:这份钢铁表面缺陷检测图像数据集面向从事机器视觉、工业质检与深度学习缺陷识别的研究人员和工程师,尤其适合需要监督学习样本训练分类或检测模型的场景。数据集共1800张钢铁表面图像,每张均配有对应标签,覆盖裂纹、锈蚀、凹坑、划痕等多种缺陷类型,可用于图像预处理、特征提取、CNN分类与模型调优等环节的训练与验证。压缩包为zip格式,内含2000个文件,以1800张jpg图像为主体,辅以1400余个xml标注文件、1400余个txt标签文件及data、names等配置说明,整体约26.81MB,目录结构便于按训练集与验证集划分检索。目前已有2450人学习下载。借助图像与标签的完整对应关系,读者可快速搭建缺陷检测流程,评估精度、召回率与F1分数,为钢铁制造自动化质检提供可复用的数据基础。
1. 钢铁表面缺陷检测图像数据集:1800张图带标签,到底能不能直接跑
产线上做钢铁表面质检的兄弟,大概率都经历过这个阶段:算法框架搭好了,模型结构也调通了,结果卡在数据上——要么样本太少,要么标签格式对不上,要么缺陷类别定义跟现场完全两回事。这次拆的这份钢铁表面缺陷检测图像数据集,1800张图像、带标签,属于那种拿到手就能先跑一轮baseline的资源。它解决的不是"有没有模型"的问题,而是"有没有干净、带标注、能直接喂给检测网络"的问题。适合谁?做工业质检方向的学生、刚接手缺陷检测项目的算法工程师、以及想验证自己训练流程是否跑得通的人。不适合指望它覆盖所有钢种和所有缺陷类型的人——1800张的体量,注定它是一个起点,不是终点。下面按我实际拆包的顺序,把这份资源从结构、用法到坑,一层层讲清楚。
2. 数据集结构与标签格式:先看清目录再动手
2.1 目录组织与文件命名逻辑
拿到一个缺陷检测数据集,我第一件事不是写训练脚本,而是先tree一遍看结构。这份钢铁表面缺陷检测图像数据集的组织方式比较典型:图像和标签分离存放,按类别或按批次分文件夹。常见做法是images/放原图,labels/放同名标注文件,扩展名可能是.txt(YOLO格式)、.xml(PASCAL VOC格式)或.json(COCO格式)。先确认这一点,决定了你后面用哪套解析代码。
# 先看整体结构,别急着写代码 find ./steel_defect_dataset -maxdepth 2 -type d | sort # 统计图像数量,确认是否真的是1800张 find ./steel_defect_dataset -name "*.jpg" -o -name "*.png" | wc -l # 看标签文件扩展名分布 find ./steel_defect_dataset -name "*.txt" -o -name "*.xml" -o -name "*.json" | sed 's/.*\.//' | sort | uniq -c上面三条命令分别解决三个问题:目录层级是否规整、图像总数是否与描述一致、标签格式是哪一种。逻辑说明:-maxdepth 2避免递归太深刷屏,sed 's/.*\.//'提取扩展名后uniq -c统计数量。参数上,如果你的数据集图像和标签混在同一目录,find的结果会翻倍,这时候要按文件名前缀区分。我一般会额外跑一条检查图像与标签是否一一对应:
import os img_dir = "./steel_defect_dataset/images" lbl_dir = "./steel_defect_dataset/labels" imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir)} lbls = {os.path.splitext(f)[0] for f in os.listdir(lbl_dir)} print("图像数:", len(imgs)) print("标签数:", len(lbls)) print("有图无标签:", imgs - lbls) print("有标签无图:", lbls - imgs)这段代码的核心是集合差集运算。imgs - lbls找出那些有图但没标注的样本,这类样本在训练时必须剔除,否则要么报错要么被当成负样本,直接影响召回率。参数说明:os.path.splitext去掉扩展名,保证.jpg和.txt同名也能匹配上。如果差集不为空,先别急着训练,回去核对数据采集环节是不是漏标了。
2.2 标签格式解析与类别映射
确认格式之后,要搞清楚每个标签文件里到底存了什么。YOLO格式的.txt每行是class_id x_center y_center width height,坐标是归一化到 0~1 的;VOC的.xml存的是绝对像素坐标的xmin ymin xmax ymax;COCO的.json则是统一的大文件。这三种格式互转是绕不开的活。
# 以YOLO格式为例,解析一个标签文件并统计类别分布 from collections import Counter def parse_yolo_label(label_path): boxes = [] with open(label_path, "r") as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue # 跳过异常行 cls_id, x, y, w, h = parts boxes.append({ "cls": int(cls_id), "x": float(x), "y": float(y), "w": float(w), "h": float(h) }) return boxes # 统计整个数据集的类别分布 counter = Counter() for f in os.listdir(lbl_dir): if f.endswith(".txt"): for b in parse_yolo_label(os.path.join(lbl_dir, f)): counter[b["cls"]] += 1 print("类别分布:", counter)逻辑说明:逐行读取,split()后判断字段数是否为5,不是就跳过——这一步能过滤掉空行和格式错乱的行。Counter统计每个类别出现的次数,用来判断数据集是否类别不平衡。参数说明:cls_id从0开始还是从1开始,不同数据集不一样,训练前必须确认,否则类别全错位。如果发现某个类别只有几十个框,而另一个类别上千个,那训练时就得考虑加权采样或者数据增强补齐。
提示:类别映射表(class_id 到缺陷名称)通常单独放在一个
classes.txt或data.yaml里,别硬编码到代码里,换数据集时容易忘。
3. 从零跑通训练:YOLO格式转换与baseline搭建
3.1 格式转换脚本与配置文件
假设你拿到的标签是VOC格式,而你想用YOLO系列训练,那就得转。转换的核心是把绝对坐标归一化,并且按图像宽高做除法。这一步翻车的点在于:有人忘了归一化,直接把像素坐标写进去,训练loss直接爆炸。
import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_path, img_path, class_map): tree = ET.parse(xml_path) root = tree.getroot() img = Image.open(img_path) iw, ih = img.size lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text if cls_name not in class_map: continue cls_id = class_map[cls_name] bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 归一化并转为中心点+宽高 x_center = (xmin + xmax) / 2.0 / iw y_center = (ymin + ymax) / 2.0 / ih w = (xmax - xmin) / iw h = (ymax - ymin) / ih lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") return lines逻辑说明:ET.parse解析XML,遍历所有object节点。class_map是一个字典,把缺陷名称映射到整数ID。归一化用图像实际宽高iw, ih做除数,保留6位小数足够。参数说明:如果class_map里没有某个缺陷名,continue跳过——这是防止未知类别污染训练集。转换完记得抽查几个文件,用可视化脚本画框确认坐标没偏。
配置文件方面,YOLO系一般需要一个data.yaml:
path: ./steel_defect_dataset train: images/train val: images/val nc: 4 names: ["crack", "inclusion", "scratch", "rolled_in_scale"]nc是类别数,names顺序必须和class_map一致。常见错误是nc写错,导致训练时索引越界。
3.2 训练命令与关键参数
配置好了就可以起训练。以YOLOv8为例,一条命令能跑起来,但参数得按你的显存调。
yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=./runs/steel \ name=exp1逻辑说明:model=yolov8n.pt用预训练权重做迁移学习,小数据集上比从头训收敛快得多。imgsz=640是输入分辨率,钢铁缺陷通常比较细,分辨率太低小缺陷直接消失。batch=16按显存调,8G显存跑640大概能到16,不够就降到8。patience=20是早停,20轮验证集不提升就停,省时间。参数说明:lr0初始学习率,0.01是默认值,小数据集可以降到0.001更稳。project和name决定权重保存路径,别用默认的,不然跑几次就找不着了。
训练起来之后盯两个东西:box_loss是否稳定下降,mAP50是否在涨。如果loss震荡厉害,先把batch调小或者学习率降一个量级。如果mAP一直不动,回去检查标签格式和类别映射。
注意:1800张图按8:1:1切分,验证集只有180张左右,mAP波动会比较大,别拿单次结果下结论,多跑几个seed取平均。
4. 数据增强与类别不平衡:1800张怎么榨出更多价值
4.1 针对钢铁缺陷的增强策略
1800张在深度学习里算小数据集,直接训容易过拟合。数据增强是标配,但钢铁表面缺陷有它的特殊性:缺陷形态多样但背景相对单一,所以增强不能瞎用。翻转、旋转、亮度调整是安全的;但颜色抖动要谨慎,因为钢铁表面的颜色变化本身可能就代表缺陷类型(比如氧化色)。常见做法是用Albumentations搭一套增强pipeline:
import albumentations as A transform = A.Compose([ A.HorizontalFlip(p=0.5), A.VerticalFlip(p=0.3), A.RandomRotate90(p=0.3), A.RandomBrightnessContrast(p=0.4), A.GaussNoise(p=0.2), A.CLAHE(p=0.3), # 限制对比度自适应直方图均衡,增强纹理 ], bbox_params=A.BboxParams(format="yolo", label_fields=["class_labels"]))逻辑说明:HorizontalFlip和VerticalFlip对钢铁表面缺陷是安全的,因为缺陷方向没有语义上的固定性。CLAHE增强局部对比度,对细小裂纹特别有用。bbox_params指定格式为yolo,保证框跟着图像一起变换。参数说明:p是触发概率,别全设1.0,那样每个epoch看到的都一样。GaussNoise模拟传感器噪声,工业相机在暗场下确实有噪声,加上更贴近实际。
4.2 类别不平衡的处理
如果统计出来某个缺陷类别特别少,比如"rolled_in_scale"只有50个框,而"scratch"有800个,那模型会偏向多数类。处理方式有几种:过采样少数类、在loss里加权、或者用focal loss。YOLO本身支持通过复制少数类样本来平衡,但更干净的做法是在数据集层面做。
# 按类别统计并生成过采样列表 import random def balance_dataset(label_dir, target_per_class=300): class_files = {} for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f)) as fh: classes = {line.split()[0] for line in fh if line.strip()} for c in classes: class_files.setdefault(c, []).append(f) oversample = [] for c, files in class_files.items(): if len(files) < target_per_class: need = target_per_class - len(files) oversample.extend(random.choices(files, k=need)) return oversample逻辑说明:先按类别归集文件,然后对数量不足的类别随机重复采样到目标数量。random.choices允许重复抽取同一个文件。参数说明:target_per_class设成多少合适?一般取中位数类别的数量,别直接拉到最大类,否则过拟合风险高。过采样之后记得在训练配置里把这些文件加进去,或者单独生成一个增强版数据集目录。
提示:过采样会让某些图像在每个epoch出现多次,如果这些图本身标注质量差,错误会被放大。过采样前先人工抽查一遍少数类样本。
5. 避坑与排查:这份数据集最容易翻车的五个点
5.1 图像与标签不对应,训练时报索引错
现象:训练启动几秒后报IndexError或KeyError,提示找不到某个文件。原因:图像目录和标签目录里有文件名不一致的情况,比如图像是img_001.jpg,标签是img_001.png.txt或者干脆漏了一个。解决:跑一遍前面2.1节的集合差集检查,把有图无标签和有标签无图的都列出来,手动核对或直接剔除。我一般会在数据加载前加一个断言,不通过就不让训练开始。
5.2 坐标归一化搞反,框全跑到左上角
现象:训练loss不降,可视化预测框全部挤在图像左上角一小块。原因:VOC转YOLO时忘了除以图像宽高,或者除反了(宽除了高)。解决:转换后随机抽10张图,用OpenCV把框画出来看一眼。归一化后的坐标应该在0~1之间,如果出现大于1的值,说明没除或者除错了。
5.3 类别ID从1开始,训练时越界
现象:报AssertionError或类别预测全错。原因:有些标注工具默认类别从1开始编号,而YOLO要求从0开始。解决:检查标签文件里出现的最大class_id,如果等于类别数,说明是从1开始的,统一减1。这个坑很隐蔽,因为不报错的时候模型也能训,只是学出来的类别是错的。
5.4 验证集划分不合理,mAP虚高
现象:验证集mAP很高,但拿新图一测就崩。原因:划分时没有按图像来源或批次分层,验证集里混入了和训练集同一批采集、几乎一样的图。解决:如果数据有批次信息,按批次划分;没有的话,至少保证随机划分前先shuffle,并且固定随机种子。1800张的体量,验证集180张,如果这180张和训练集高度相似,指标没有参考价值。
5.5 图像分辨率与模型输入不匹配,小缺陷丢失
现象:大缺陷检测没问题,细裂纹全部漏检。原因:原图可能是2000×2000,直接resize到640,细裂纹被插值抹掉了。解决:要么提高输入分辨率到1024或1280,要么用切图推理(把大图切成小块分别检测再合并)。切图方案对小缺陷更友好,但后处理合并框的逻辑要写对,否则同一个缺陷会被重复检测。
6. 进阶技巧:用切图推理把细缺陷召回拉上来
前面提到分辨率是细缺陷检测的命门。1800张图里如果原图分辨率高,直接resize到640训练,模型学到的是"缩小版"的缺陷,推理时也按640跑,小目标基本没戏。我一般会走切图这条路:训练时就把大图切成512×512或640×640的patch,每个patch单独标注,推理时也切,最后把结果映射回原图坐标。
def slice_image(img_path, slice_size=640, overlap=128): img = cv2.imread(img_path) h, w = img.shape[:2] slices = [] step = slice_size - overlap for y in range(0, h, step): for x in range(0, w, step): x2 = min(x + slice_size, w) y2 = min(y + slice_size, h) patch = img[y:y2, x:x2] # 边缘不足slice_size的补零 if patch.shape[0] < slice_size or patch.shape[1] < slice_size: patch = cv2.copyMakeBorder( patch, 0, slice_size - patch.shape[0], 0, slice_size - patch.shape[1], cv2.BORDER_CONSTANT, value=(0, 0, 0)) slices.append((patch, x, y)) return slices逻辑说明:step = slice_size - overlap控制滑动步长,overlap保证相邻patch有重叠区域,避免缺陷正好被切在边界上。每个patch记录左上角坐标(x, y),推理完把框坐标加回去就是原图坐标。参数说明:overlap一般取slice_size的1/5到1/4,太小会漏边界缺陷,太大计算量翻倍。切图之后,1800张原图可能变成上万张patch,训练集瞬间够用了,但要注意patch里大量是纯背景,得过滤掉没有标注的patch,否则正负样本比例失衡。
推理合并的时候,重叠区域的框要用NMS再压一遍,不然同一个缺陷会被检出好几次。我习惯把IoU阈值设到0.5,比训练时的NMS稍微松一点,因为切图边缘的框坐标有偏移。
从那以后我每次拿到新的缺陷检测数据集,都强制走一遍"结构检查→格式转换→可视化抽查→切图评估"这四步,不跳过任何一步。这份钢铁表面缺陷检测图像数据集1800张带标签,体量不大但足够把整条流程跑通,适合拿来练手和验证pipeline。希望帮到你。
本文还有配套的精品资源,点击获取