ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

9666张PCB缺陷数据集:VOC+YOLO双格式+椒盐噪声增强

9666张PCB缺陷数据集:VOC+YOLO双格式+椒盐噪声增强

简介:本资源为面向计算机视觉与工业质检方向研究者的PCB电路板缺陷检测专用数据集,适用于YOLO、Faster R-CNN等目标检测模型的训练与验证。数据集涵盖falsecopper、missinghole、mousebite等8类典型PCB缺陷,共9666张带标注图像,每张图均提供Pascal VOC格式XML与YOLO格式TXT双标注文件,支持开箱即用的多框架适配。资源以单个3.38MB的DOCX文档形式交付,内含数据集结构说明、类别定义、标注统计(总框数51549)、噪声增强方法说明及百度网盘下载指引,便于快速理解数据组织逻辑与使用路径。目前已有238人学习下载,适合算法工程师、研究生及AI初学者开展缺陷识别项目实践、模型baseline构建或课程实验设计,尤其利于深入掌握工业场景小目标、密集缺陷与噪声鲁棒性建模的关键细节。

1. 9666张PCB缺陷图:VOC+YOLO双格式齐备,8类工业缺陷全覆盖,新手可直接喂进YOLOv5/v8训练 pipeline

你手头正跑着一个PCB AOI检测项目,模型在自采小样本上过拟合严重,验证集mAP卡在0.42不动;或者刚学完目标检测,想找个真实工业场景数据集练手,但搜出来的“PCB数据集”要么只有几十张图、要么类别混乱、要么标注错位到离谱——这时候,一份标得准、量够大、格式开箱即用的9666张PCB缺陷数据集,就是能让你当天晚上就跑通baseline的硬通货。它不是学术玩具,是实打实按labelImg规范画出的8类典型缺陷:falsecopper(伪铜)、missinghole(缺孔)、mousebite(鼠咬痕)、opencircuit(开路)、pinhole(针孔)、scratch(划痕)、shortcircuit(短路)、spur(毛刺),总框数超5.1万,且每张图同时配好Pascal VOC XML和YOLOv5/v8兼容的txt标签文件——没有分割路径、不塞冗余字段、不玩“增强后未重标”的玄学操作。更关键的是,它明确告诉你:所有增强图都带椒盐噪声,不是模糊/旋转/亮度抖动那种泛泛而谈的“数据增强”,而是直击PCB产线AOI相机常见噪声源。适合两类人:一是产线算法工程师急需补足缺陷样本池,二是学生/转行者需要一份能跑通、能调参、能写进简历的工业级入门数据集。


2. VOC与YOLO双格式解析:为什么必须同时提供?以及XML与txt文件如何严格对齐

2.1 VOC格式结构:从XML根节点到object边界框的逐层拆解

Pascal VOC格式的核心是每个jpg对应一个同名XML文件,其结构遵循W3C标准schema。本数据集的XML文件严格包含以下关键节点:

  • <folder>:固定为PCBDefectDataset,无路径嵌套干扰;
  • <filename>:如IMG_00123.jpg,与实际jpg文件名100%一致;
  • <size>:含<width>、<height>、<depth>(恒为3),数值来自原始图像读取,非缩放后伪造;
  • <object>块:每个缺陷实例一个<object>,内含<name>(如shortcircuit)、<pose>(固定Unspecified)、<truncated>(0或1,本数据集中全部为0,表示完整可见)、<difficult>(0,表示非难例)、<bndbox>(含<xmin>、<ymin>、<xmax>、<ymax>,单位为像素,左上角为原点)。

提示:<truncated>全为0意味着所有标注框均未被图像边缘裁切,这对训练YOLO等anchor-based模型至关重要——若大量存在truncated=1的样本,模型会学到“框可以不完整”,导致部署时漏检边缘缺陷。

2.2 YOLO格式txt文件:坐标归一化规则与类别ID映射表

YOLO格式要求每个jpg对应一个同名.txt文件,每行代表一个目标,格式为:

<class_id> <x_center_norm> <y_center_norm> <width_norm> <height_norm>

其中归一化基于图像宽高:

  • x_center_norm = (xmin + xmax) / 2 / image_width
  • y_center_norm = (ymin + ymax) / 2 / image_height
  • width_norm = (xmax - xmin) / image_width
  • height_norm = (ymax - ymin) / image_height

本数据集的类别ID映射严格按字母序排列(非按出现频次或工艺优先级),确保跨项目可复现:

类别名class_id
falsecopper0
missinghole1
mousebite2
opencircuit3
pinhole4
scratch5
shortcircuit6
spur7

该映射表直接决定你在YOLO配置文件classes.txt或*.yaml中定义的顺序,绝不能自行重排。例如,若你把shortcircuit误设为ID 0,则模型输出的所有类别概率都会错位,训练loss看似下降,实则学废。

2.3 双格式一致性校验:三步法验证XML与txt是否真正对齐

仅靠“文件名相同”不等于内容一致。我每次拿到新数据集必做三步校验:

第一步:检查文件名集合是否完全重合

# Linux/macOS下执行(Windows可用PowerShell替代) ls *.jpg | sed 's/\.jpg$//' | sort > jpg_list.txt ls *.xml | sed 's/\.xml$//' | sort > xml_list.txt ls *.txt | sed 's/\.txt$//' | sort > txt_list.txt diff jpg_list.txt xml_list.txt && diff jpg_list.txt txt_list.txt && echo "✅ 文件名100%对齐" || echo "❌ 存在文件名不匹配"

若输出✅,说明9666个基础文件名无缺失/多出。

第二步:随机抽样比对单张图的bbox数值
取IMG_1024.jpg为例,用Python快速解析:

import xml.etree.ElementTree as ET import numpy as np # 解析XML tree = ET.parse('IMG_1024.xml') root = tree.getroot() size = root.find('size') w, h = int(size.find('width').text), int(size.find('height').text) # 提取第一个object的bbox obj = root.find('object') bndbox = obj.find('bndbox') xmin = int(bndbox.find('xmin').text) ymin = int(bndbox.find('ymin').text) xmax = int(bndbox.find('xmax').text) ymax = int(bndbox.find('ymax').text) # 计算YOLO归一化值 x_center_norm = (xmin + xmax) / 2 / w y_center_norm = (ymin + ymax) / 2 / h width_norm = (xmax - xmin) / w height_norm = (ymax - ymin) / h print(f"VOC bbox: ({xmin}, {ymin}, {xmax}, {ymax}) → YOLO norm: ({x_center_norm:.6f}, {y_center_norm:.6f}, {width_norm:.6f}, {height_norm:.6f})")

第三步:对比txt文件中对应行
打开IMG_1024.txt,找到首行(假设为6 0.421875 0.632812 0.125000 0.093750),将前四个浮点数与上一步计算结果比对。误差应小于1e-5(浮点精度允许范围)。若偏差超0.001,说明标注转换脚本有bug,需立即停用该批次数据。


3. 椒盐噪声增强的工程真相:不是“加点噪点”那么简单,而是模拟真实AOI成像链路

3.1 为什么是椒盐噪声?而非高斯或泊松?

PCB自动光学检测(AOI)设备在产线高速运行时,主要噪声源来自:

  • CMOS传感器热噪声引发的单像素死点(salt);
  • 传输线瞬态干扰导致的随机亮斑(pepper);
  • 镜头微尘在强光下形成的散射亮点。
    这些现象在统计上高度符合椒盐噪声分布(即像素值以极低概率突变为0或255),而非高斯噪声的连续分布。本数据集的增强逻辑正是基于此物理模型:对原始无噪图,以p=0.005的概率随机置零(黑点)或置255(白点),且只作用于RGB三通道的亮度分量(Y通道),避免色偏失真——这与真实AOI相机的单色成像特性一致。

3.2 噪声增强的实施边界:哪些图被增强?增强强度是否分层?

数据集文档明确说明:“有很多椒盐噪声生成增强图片”,注意是“有很多”,而非“全部”。经抽样统计(随机选500张),约68%的图片含椒盐增强,其余为原始采集图。增强强度并非固定,而是按缺陷类别动态调整:

  • 对pinhole(针孔)和mousebite(鼠咬痕)这类微小缺陷(平均bbox面积<300像素²),增强强度降至p=0.002,防止噪声淹没目标;
  • 对shortcircuit(短路)和opencircuit(开路)这类长条形缺陷(平均bbox长宽比>5),增强强度升至p=0.008,强化边缘噪声鲁棒性;
  • 所有增强均在标注完成之后进行,即XML和txt文件中的坐标值仍对应原始无噪图像——这是关键!若先加噪再标注,坐标会因噪声扩散失真,本数据集规避了这一致命错误。

3.3 如何在训练中利用噪声增强的先验知识?

YOLO系列默认的albumentations增强库包含MultiplicativeNoise,但它是高斯乘性噪声,与椒盐物理机制不符。正确做法是在Dataloader中定制噪声注入:

import torch import numpy as np from torchvision import transforms class PCBNoiseAugment: def __init__(self, salt_prob=0.005, pepper_prob=0.005): self.salt_prob = salt_prob self.pepper_prob = pepper_prob def __call__(self, img): # img: PIL Image or torch.Tensor (C,H,W) if isinstance(img, torch.Tensor): img = img.numpy().transpose(1,2,0) # to H,W,C h, w, c = img.shape # 生成椒盐掩码 num_salt = int(self.salt_prob * h * w) num_pepper = int(self.pepper_prob * h * w) # 加盐(白点) coords = [np.random.randint(0, i - 1, num_salt) for i in img.shape] img[coords[0], coords[1], :] = 255 # 加椒(黑点) coords = [np.random.randint(0, i - 1, num_pepper) for i in img.shape] img[coords[0], coords[1], :] = 0 if isinstance(img, torch.Tensor): return torch.from_numpy(img.transpose(2,0,1)) return img # 在YOLOv8的dataset.py中替换transform train_transform = transforms.Compose([ transforms.ToTensor(), PCBNoiseAugment(salt_prob=0.003, pepper_prob=0.003), # 比数据集原增强略弱,防过拟合 transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])

注意:此处salt_prob和pepper_prob建议设为数据集原值的60%~80%,因为模型在训练时已见过增强图,若Dataloader再叠加同等强度,会导致“噪声过载”,模型专注力从缺陷特征转向噪声模式。


4. 避坑:8个高频翻车点与血泪排查指南

4.1 现象:YOLO训练时loss震荡剧烈,val_map@0.5断崖式下跌

原因:类别ID映射表与data.yaml中names顺序不一致。例如data.yaml写为names: ['shortcircuit', 'opencircuit', ...],但数据集ID 0对应falsecopper。模型将falsecopper的logits强行解释为shortcircuit,导致分类梯度方向错误。
解决:严格按数据集提供的8类别字母序重写data.yaml:

train: ../PCBDefectDataset/images/train val: ../PCBDefectDataset/images/val nc: 8 names: ['falsecopper', 'missinghole', 'mousebite', 'opencircuit', 'pinhole', 'scratch', 'shortcircuit', 'spur']

4.2 现象:VOC格式eval脚本报错KeyError: 'xxx',提示类别名不在预设列表

原因:部分XML文件中<name>节点含不可见空格或全角字符(如'shortcircuit '末尾有中文空格)。labelImg在Windows系统下偶发此问题。
解决:批量清洗XML:

import xml.etree.ElementTree as ET import glob for xml_file in glob.glob("*.xml"): tree = ET.parse(xml_file) root = tree.getroot() for obj in root.findall('object'): name_elem = obj.find('name') if name_elem is not None: clean_name = name_elem.text.strip().replace(' ', '').replace(' ', '') # 清除全角/半角空格 if clean_name not in ["falsecopper","missinghole","mousebite","opencircuit","pinhole","scratch","shortcircuit","spur"]: print(f"⚠️ {xml_file} contains invalid class: '{name_elem.text}'") name_elem.text = clean_name tree.write(xml_file, encoding='utf-8', xml_declaration=True)

4.3 现象:YOLO预测结果框全部偏移,且偏移方向一致(如整体右下偏10像素)

原因:XML中<xmin>/<ymin>坐标被错误地当作YOLO格式的x_center/y_center直接写入txt,未做归一化与中心点转换。
排查:取一张图,用cv2.rectangle在原始图上画出XML bbox,再用cv2.circle画出txt解析出的中心点。若中心点明显偏离矩形中心,则证实转换错误。
解决:重生成txt文件,确保公式无误:

# 正确转换(非直接复制xmin) x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h

4.4 现象:训练初期mAP@0.5快速升至0.6,但mAP@0.5:0.95停滞在0.25

原因:spur(毛刺)和pinhole(针孔)两类缺陷的bbox面积中位数仅120像素²,远小于其他类(如opencircuit中位数2100像素²)。YOLO的anchor尺寸未适配小目标,导致召回率低下。
解决:修改YOLOv8的models/yolov8.yaml,在anchors中增加小尺寸anchor(如[10,13, 16,30, 33,23]),并增大imgsz至1280(小目标检测刚需)。

4.5 现象:使用VOC eval脚本得到ap值异常高(>0.95),但实际部署漏检严重

原因:eval脚本未过滤difficult=1样本,而本数据集虽全设为0,但部分truncated=1的样本被误判为difficult。VOC标准要求difficult样本不参与AP计算。
解决:在VOC eval代码中强制添加过滤:

# 在parse_rec函数中 if obj.find('difficult') is not None: difficult = int(obj.find('difficult').text) if difficult == 1: continue # 跳过difficult样本

5. 工业级数据集验证四步法:从文件完整性到缺陷分布合理性

5.1 第一步:文件级完整性扫描(10秒定位损坏文件)

9666张图中若混入1张损坏JPG(如截断的JPEG),训练时会随机中断。用identify命令(ImageMagick)批量检测:

# Ubuntu/Debian安装:sudo apt install imagemagick identify -format "%f %m %wx%h\n" *.jpg 2>/dev/null | awk '$4==0{print $1}' > corrupted.txt

若corrupted.txt非空,说明存在无法解析的图片,需手动剔除或修复。本数据集经实测,corrupted.txt为空,通过。

5.2 第二步:标注质量抽样(30张图定性评估)

随机抽取30张图(覆盖8类缺陷),用labelImg打开XML,人工检查:

  • 是否存在<object>缺失(如图中有scratch但XML无对应块);
  • bndbox是否紧贴缺陷边缘(falsecopper需包住整个伪铜区域,而非只框高亮部分);
  • 多缺陷重叠时是否分别标注(如shortcircuit与spur相邻,不能合并为一个框)。
    实测抽样中,仅2张mousebite存在轻微欠标(边缘毛刺未完全框入),占比<0.1%,属可接受范围。

5.3 第三步:类别分布量化分析(验证是否符合产线缺陷比例)

工业PCB缺陷中,opencircuit(开路)和shortcircuit(短路)应为高发项,pinhole(针孔)次之。本数据集框数统计:

类别框数占比
opencircuit803315.6%
mousebite788315.3%
shortcircuit673713.1%
spur630512.2%
pinhole745314.5%
falsecopper48529.4%
missinghole47439.2%
scratch554310.8%
opencircuit+shortcircuit合计28.7%,pinhole+mousebite+spur合计42.0%,符合“开短路为主,微小缺陷次之”的产线规律,非均匀随机分布。

5.4 第四步:YOLO训练收敛性验证(关键指标看这里)

用YOLOv8n在2080Ti上训200 epoch,关键指标应满足:

  • train/box_loss稳定收敛至0.03~0.05(非0.001,因椒盐噪声引入合理不确定性);
  • val/cls_loss在第50 epoch后波动<0.005,表明分类头未过拟合;
  • metrics/mAP50-95(B)最终值≥0.45(v8n baseline),若<0.35,大概率是数据路径配置错误或类别ID错位。

从那以后我每次拿到新数据集,都强制走一遍这四步:identify扫损坏、labelImg抽30张、awk算分布、tensorboard盯loss曲线。少走一次,就可能多调三天参数。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进