1. GC10-DET数据集深度解析
GC10-DET是工业质检领域一个非常实用的金属表面缺陷数据集,我在多个实际项目中都使用过它。这个数据集最大的特点就是真实——所有样本都来自真实的钢铁生产线,包含了十种常见的表面缺陷类型,总共3570张灰度图像。对于刚接触工业质检的新手来说,这个数据集规模适中,既不会太小导致无法训练模型,也不会太大让数据处理变得困难。
数据集中的十种缺陷类型各有特点:
- 冲孔(Pu):机械故障导致的非预期孔洞
- 焊缝(Wl):带钢焊接处产生的痕迹
- 新月形缝隙(Cg):切割过程中产生的半圆形缺陷
- 水斑(Ws)和油斑(Os):表面污染类缺陷,但视觉特征差异明显
- 丝斑(Ss):辊压不均匀导致的波浪状斑块
- 夹杂物(In):金属表面嵌入的异物
- 轧坑(Rp):周期性隆起或凹陷
- 折痕(Cr)和腰部折痕(Wf):材料变形导致的褶皱
我第一次接触这个数据集时,发现它的组织方式比较特别——图片按缺陷类型存放在10个子文件夹中,而标签则是统一的XML文件。这种结构在实际使用时需要特别注意,因为有些图片可能没有对应的标签文件,这也是我们后续数据清洗的重点之一。
2. 数据清洗全流程实战
2.1 去除无标签样本
在实际项目中,我遇到过不少数据质量问题,其中最常见的就是标签缺失。GC10-DET数据集也不例外,经过检查发现部分图片没有对应的标注文件。这种情况如果不处理,训练时就会遇到麻烦。
我的处理流程是这样的:
- 首先创建一个新的项目文件夹,比如
GC10-DET_processed - 在里面建立
images和annotations两个子目录 - 遍历原始标签文件夹,把所有有效的标签文件名记录下来
- 然后逐个检查原始图片,只保留那些有对应标签的图片
这里有个实用技巧:我通常会先用Python的os.listdir()快速检查标签和图片的数量是否匹配。如果发现明显差异,就要仔细检查了。下面是我实际用过的代码:
import os from tqdm import tqdm # 原始数据路径 raw_img_dir = "path/to/raw/images" raw_ann_dir = "path/to/raw/annotations" # 处理后的路径 processed_img_dir = "path/to/processed/images" processed_ann_dir = "path/to/processed/annotations" # 获取所有有效标注文件 valid_files = [f.split('.')[0] for f in os.listdir(raw_ann_dir) if f.endswith('.xml')] # 复制有标注的图片到新目录 for img_file in tqdm(os.listdir(raw_img_dir)): img_name = img_file.split('.')[0] if img_name in valid_files: # 复制图片 shutil.copy( os.path.join(raw_img_dir, img_file), os.path.join(processed_img_dir, img_file) ) # 复制标注 shutil.copy( os.path.join(raw_ann_dir, f"{img_name}.xml"), os.path.join(processed_ann_dir, f"{img_name}.xml") )2.2 修正错误标签
在数据清洗过程中,我发现GC10-DET存在一些标签错误问题。最常见的是标签名称拼写不一致,比如"10_yaozhe"有时被写成"10_yaozhed"。这类问题看似不大,但在训练时会导致模型无法正确识别这些样本。
我的修正方法是:
- 先用
grep或Python的glob模块找出所有包含错误标签的XML文件 - 然后批量替换错误的标签名
- 最后再随机抽查几个文件确认修改是否正确
这里有个坑要注意:Windows和Linux系统的路径表示方法不同,在写脚本时要考虑跨平台兼容性。下面是我使用的修正脚本:
import xml.etree.ElementTree as ET from pathlib import Path def fix_incorrect_labels(xml_dir, wrong_label, correct_label): xml_files = Path(xml_dir).glob("*.xml") for xml_file in xml_files: tree = ET.parse(xml_file) root = tree.getroot() # 查找并修正错误标签 for obj in root.findall("object"): name = obj.find("name") if name.text == wrong_label: name.text = correct_label # 保存修改后的文件 tree.write(xml_file)3. 数据预处理技巧
3.1 统一数据格式
GC10-DET原始数据是灰度图像,但在实际应用中,我们可能需要将其转换为RGB格式以适应某些预训练模型。我的经验是,简单的灰度转RGB效果往往不如保持原始单通道输入。
我通常会做以下处理:
- 图像尺寸标准化:将所有图像调整为相同尺寸
- 像素值归一化:将0-255的像素值缩放到0-1范围
- 数据增强:针对工业缺陷特点,使用旋转、翻转等增强方式
import cv2 import numpy as np def preprocess_image(img_path, target_size=(512, 512)): # 读取灰度图像 img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) # 调整尺寸 img = cv2.resize(img, target_size) # 归一化 img = img.astype(np.float32) / 255.0 # 可选:转换为3通道 # img = np.stack([img]*3, axis=-1) return img3.2 数据集划分策略
工业质检数据集的一个特点是样本分布不均衡,某些缺陷类型可能样本很少。我在处理GC10-DET时采用了分层抽样方法,确保每个类别在训练集和验证集中都有代表。
具体步骤:
- 按缺陷类别统计样本数量
- 对每个类别单独划分训练/验证/测试集
- 保持各类别在划分后数据集中的比例一致
from sklearn.model_selection import train_test_split def split_dataset(image_paths, test_size=0.2, random_state=42): # 假设image_paths是包含所有图片路径的列表 # 这里可以根据实际情况获取每个图片的标签 # 分层划分 train_files, val_files = train_test_split( image_paths, test_size=test_size, stratify=labels, # 按标签分层 random_state=random_state ) return train_files, val_files4. 工程化实践建议
4.1 高效数据加载方案
当数据集较大时,如何高效加载数据是个关键问题。我推荐使用tf.data或PyTorch的DataLoader,它们可以并行加载数据,显著提高训练效率。
这里分享一个我在实际项目中使用的PyTorch数据加载器实现:
from torch.utils.data import Dataset, DataLoader import torch class SteelDefectDataset(Dataset): def __init__(self, img_dir, ann_dir, transform=None): self.img_dir = img_dir self.ann_dir = ann_dir self.transform = transform self.img_files = [f for f in os.listdir(img_dir) if f.endswith('.jpg')] def __len__(self): return len(self.img_files) def __getitem__(self, idx): img_name = self.img_files[idx] img_path = os.path.join(self.img_dir, img_name) # 读取并预处理图像 image = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) image = cv2.resize(image, (512, 512)) image = image.astype(np.float32) / 255.0 image = torch.from_numpy(image).unsqueeze(0) # 增加通道维度 # 读取并解析XML标注 ann_path = os.path.join(self.ann_dir, img_name.replace('.jpg', '.xml')) boxes, labels = parse_xml(ann_path) # 需要实现XML解析函数 if self.transform: image = self.transform(image) return image, {'boxes': boxes, 'labels': labels} # 使用示例 dataset = SteelDefectDataset(img_dir, ann_dir) dataloader = DataLoader(dataset, batch_size=8, shuffle=True, num_workers=4)4.2 常见问题排查
在数据处理过程中,我遇到过几个典型问题:
- 内存不足:处理大尺寸图像时容易发生。解决方案是使用生成器或分块处理。
- 标注框越界:有些标注框的坐标可能超出图像范围。需要添加边界检查。
- 标签不一致:同一种缺陷可能有不同名称。需要建立统一的标签映射表。
针对标注框越界问题,我通常会添加这样的检查代码:
def check_bbox_validity(box, img_width, img_height): xmin, ymin, xmax, ymax = box # 检查坐标是否在合理范围内 xmin = max(0, min(xmin, img_width - 1)) xmax = max(0, min(xmax, img_width - 1)) ymin = max(0, min(ymin, img_height - 1)) ymax = max(0, min(ymax, img_height - 1)) # 检查是否有效框 if xmin >= xmax or ymin >= ymax: return None return [xmin, ymin, xmax, ymax]5. 模型训练前的最后检查
在完成所有数据清洗和预处理后,我强烈建议进行以下检查:
- 随机可视化一些样本和对应的标注,确认标注正确
- 检查训练集和验证集的类别分布是否相似
- 确认所有图像都能正常加载,没有损坏文件
- 检查数据增强效果是否符合预期
这里分享一个简单的可视化检查代码:
import matplotlib.pyplot as plt import matplotlib.patches as patches def visualize_sample(img_path, ann_path): img = cv2.imread(img_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) fig, ax = plt.subplots(1, figsize=(12, 8)) ax.imshow(img) # 解析标注并绘制边界框 boxes, labels = parse_xml(ann_path) for box, label in zip(boxes, labels): xmin, ymin, xmax, ymax = box rect = patches.Rectangle( (xmin, ymin), xmax-xmin, ymax-ymin, linewidth=2, edgecolor='r', facecolor='none' ) ax.add_patch(rect) ax.text(xmin, ymin, label, color='white', backgroundcolor='red') plt.show() # 随机检查几个样本 for _ in range(5): idx = np.random.randint(len(dataset)) img_path = os.path.join(img_dir, dataset.img_files[idx]) ann_path = os.path.join(ann_dir, dataset.img_files[idx].replace('.jpg', '.xml')) visualize_sample(img_path, ann_path)