ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PCB缺陷识别实战:400张图从VOC标注到YOLOv8训练

PCB缺陷识别实战:400张图从VOC标注到YOLOv8训练 简介面向计算机视觉与工业质检领域的开发者资源包内含四百张原始PCB图像与对应的四百个Pascal VOC格式XML标注文件图像与标注一一对应可支撑YOLO、Faster R-CNN等主流目标检测模型的训练、验证与评估。根据资源描述使用该数据集进行PCB缺陷识别检测平均正确率可达百分之九十五点九具备较强的工程参考价值可作为算法选型与模型优化的基准。压缩包共八百个文件JPG图像用于模型输入XML文件记录缺陷类别与边框位置整体大小约九十五点一七MB目录结构清晰适合直接解压后开展训练流程。目前已有四百一十二人学习下载适合从事电子制造质检、自动化视觉检测项目的研究者或工程师用于算法调优、数据集划分、缺陷类型分析及识别方案改进等场景。1. PCB缺陷识别为何拿 400 张图就敢谈 95.9% 正确率PCB印制电路板缺陷识别检测数据集平均识别正确率在95.9%对400张原始图进行PASCAL VOC XML格式标注——这个标题信息量其实很足数据规模不大类别定义明确标注格式是目标检测领域最通用的VOC XML之一。它面向的是产线质检场景需要用目标检测模型在固定光照、固定工位的成像条件下把缺孔、短路、断路、毛刺这类缺陷框出来并分类。400张图能做到95.9%的平均识别正确率说明数据采集环境稳定、类别相对集中、标注质量在可控范围内。适合做这件事的人是手里有工业相机、想用深度学习替代人工目检的视觉工程师或者刚接触检测框架、想在真实标注数据上跑通YOLO流程的开发者。需要提前说清的是95.9%不是所有场景都能复现的数字。它在单一产线、单一相机参数、固定光源下是有参考价值的但换到不同板卡、不同光照指标要重新打。这篇笔记会从标注格式拆起讲清400张图怎么从XML标注一路走到训练和评估再把这套流程里我踩过的坑摊开讲。2. PASCAL VOC XML 标注格式一张缺陷图里到底存了什么2.1 一个XML文件里的必备字段与结构先纠正一个细节标题里的“pasical voc”是PASCAL VOC的常见手误正规叫法是PASCAL VOC全称是Pattern Analysis, Statistical Modelling and Computational Learning Visual Object Classes。这个数据集里的每张缺陷图对应一个同名的XML文件记录这张图里所有缺陷的位置和类别。很多人拿到XML文件后第一反应是问用什么打开其实VS Code、Notepad都能看但400张图不可能逐个人工翻更实用的方式是写脚本批量读取。拿一个具体的XML文件来说annotation folderJPEGImages/folder filenamepcb_001.jpg/filename source databasePCB Defect Dataset/database /source size width1024/width height768/height depth3/depth /size segmented0/segmented object nameshort/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin312/xmin ymin245/ymin xmax367/xmax ymax273/ymax /bndbox /object /annotation这段XML描述的信息很直接文件名pcb_001.jpg图像宽1024高768图里有一个类别为short的缺陷框的左上角坐标是(312,245)右下角是(367,273)。坐标单位是像素不是归一化数值解析时直接取整数。训练框架在读到VOC格式时默认走这个坐标系。关键字段的作用如下表字段含义常见问题filename对应图片文件名容易与实际文件名大小写不一致size/width、height图像宽高缩放原图后未同步修改导致坐标错位object/name缺陷类别类别拼写不统一比如short和shorts混用bndbox/xmin、ymin、xmax、ymax缺陷框左上右下像素坐标坐标越界或xmin大于xmaxdifficult是否难识别样本训练时默认过滤difficult1的框2.2 XML解析与格式校验拿到数据集的第一道工序我拿到任何VOC格式数据集第一件事不是训练而是写脚本把XML里的filename、size、bndbox抽出来和实际图片核对一遍。这个习惯来自一次翻车一个公开数据集里十几张图的XML filename写成了带目录前缀的相对路径训练时一半图片找不到调了一天没查出原因。用ElementTree解析能把问题暴露干净这里的xml解析方式也是处理VOC数据最常用的手段。import os import xml.etree.ElementTree as ET xml_path Annotations/pcb_001.xml root ET.parse(xml_path).getroot() print(filename:, root.findtext(filename)) print(image size:, root.findtext(size/width), root.findtext(size/height)) for obj in root.findall(object): name obj.findtext(name) box obj.find(bndbox) xmin int(box.findtext(xmin)) ymin int(box.findtext(ymin)) xmax int(box.findtext(xmax)) ymax int(box.findtext(ymax)) print(f{name}: ({xmin}, {ymin}, {xmax}, {ymax}))参数说明findtext比find加text更省事取不到内容时返回None而不是抛异常findall(object)能拿到全部缺陷目标适合一张图多个缺陷的情况。解析时xmin、ymin、xmax、ymax一定要转成int因为XML里是字符串后面做坐标计算时如果不转类型结果会变成字符串拼接而不是数值运算缺陷框全部错位。2.3 为什么选VOC XML而不是COCO JSON或YOLO txtVOC XML、COCO JSON、YOLO txt是目标检测数据最常见的三种承载格式。VOC格式的优势在于单张图对应一个XML文件文件自包含能看到目标类别、坐标和图像尺寸配合LabelImg就能直接编辑。COCO JSON把整个数据集的标注装进一个JSON文件字段多、层级深手工改容易把images和annotations的id对应关系改乱。YOLO txt是训练友好格式一行一个目标类别用数字索引代替但对人来说可读性差框画歪了用肉眼几乎看不出。格式文件组织坐标类型可读性常见工具VOC XML每图一个XML绝对像素坐标高LabelImgCOCO JSON整个数据集一个JSON绝对像素坐标低labelme、CVAT导出YOLO txt每图一个txt归一化坐标低LabelImg切换模式对400张图的规模来说VOC XML的“每图一个文件”特性特别友好标注、修正、增删样本时不需要维护一个巨大的全局索引。后续要喂给YOLOv8或MMDetection再写脚本转换成对应格式即可数据源头不丢信息。2.4 400张图的标准目录组织与划分文件常见的数据集目录组织是三个子目录加一个划分目录。JPEGImages放原始图Annotations放同名XMLImageSets/Main放train.txt、val.txt、test.txt。划分文件里只写不带后缀的文件名每行一个# ImageSets/Main/train.txt 内容示例 pcb_001 pcb_002 pcb_010 ...这种组织方式被YOLO和MMDetection的VOC解析器默认支持。公开下载的数据集大多按这个约定组织但train和val的划分文件经常不一起给需要自己从头划分。我的建议是划分时固定随机种子否则每次训练前重新随机换一个划分方式指标能差好几个点95.9%这种数字就成了黑匣子。3. 用LabelImg标注PCB缺陷从原图到可用数据集的完整流程3.1 安装LabelImg的正确姿势与常用设置LabelImg是VOC XML标注最顺手的工具但它在Python 3.9以上环境安装容易踩坑最典型的是pyqt5依赖装不上或启动闪退。我一般固定用Python 3.8环境安装顺序是先装pyqt5再装labelImg反了就会报找不到Qt插件。conda create -n labelimg python3.8 -y conda activate labelimg pip install pyqt5 lxml labelImg安装完成后在终端输入labelImg即可打开。启动后先在界面上确认保存格式是PascalVOC默认就是该格式生成的标注文件是XML。快捷键需要记几个W是画框CtrlS是保存D是切到下一张图。画框时建议把图片放大到200%以上再标PCB上的开路和毛刺往往只有十几个像素不放大很容易把框画偏。一张图熟练后一到两分钟就能标注完成。3.2 缺陷类别定义与边界框绘制标准PCB缺陷检测的类别通常按IPC标准结合产线实际定义最常见的是缺孔、短路、断路、毛刺、余铜、锡珠这六类。标题里的数据集没有列出具体类别但从平均识别正确率95.9%这个数字推断类别不会超过八个超过八个在400张图上很难训到这个水平。边界框绘制有两条铁律。第一框要紧贴缺陷的最小外接矩形不要为了省事把整条走线或整个焊盘框进去。比如断路缺陷只需要框住断口处框太大时模型学到的特征会被无缺陷区域稀释。第二多个独立缺陷要分别画框一个框同时圈住两个缺陷在训练时等于让模型学习一个不存在的目标。实际标注时容易把短路和余铜搞混短路是两处不该连接的铜箔连在一起余铜是在不该保留铜的区域残留铜箔两者形态相似但位置意义不同标注前最好先做几轮标准图示范。3.3 类别统计与一致性检查脚本标注完成不等于数据集可用。我每次标注完都会跑两个脚本。第一个是类别统计确认每个类别的样本数量避免训练时才发现少数类几乎没有样本。import xml.etree.ElementTree as ET from collections import Counter import os xml_dir Annotations counter Counter() for xml_name in os.listdir(xml_dir): root ET.parse(os.path.join(xml_dir, xml_name)).getroot() for obj in root.findall(.//object): counter[obj.findtext(name)] 1 print(counter)第二个是坐标越界检查把所有bndbox和图像尺寸比对坐标超出图像范围的直接打印出来。这两个脚本都是几十行的量级但能省下后面训练排错的大把时间。类别统计结果对后续训练策略影响很大如果某一类只有个位数样本就要考虑数据增强或类别权重否则模型会直接把这类缺陷学成背景。4. 用VOC格式训练YOLOv8实现PCB缺陷检测命令、参数与实际效果4.1 VOC转YOLO的转换脚本与数据目录重建YOLOv8默认读取的是YOLO txt格式标签不是VOC XML所以拿到VOC数据后要先转换。转换核心逻辑是把像素坐标的xmin、ymin、xmax、ymax换算成归一化的中心点坐标和宽高同时把类别名映射成数字id这一步非常容易错。网上有些转换脚本类别名顺序和训练配置不一致转换出来的标签看似正常训练时错误率飙升。import xml.etree.ElementTree as ET import os xml_dir Annotations label_dir labels class_names [missing_hole, short, open, burr, copper, solder] def convert_voc_to_yolo(xml_path): root ET.parse(xml_path).getroot() w int(root.findtext(size/width)) h int(root.findtext(size/height)) lines [] for obj in root.findall(.//object): name obj.findtext(name) if name not in class_names: continue class_id class_names.index(name) box obj.find(bndbox) xmin int(box.findtext(xmin)) ymin int(box.findtext(ymin)) xmax int(box.findtext(xmax)) ymax int(box.findtext(ymax)) x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) return \n.join(lines) for xml_name in os.listdir(xml_dir): xml_path os.path.join(xml_dir, xml_name) label_name xml_name.replace(.xml, .txt) with open(os.path.join(label_dir, label_name), w) as f: f.write(convert_voc_to_yolo(xml_path))参数说明class_names列表必须与后面data.yaml里的names保持一致顺序不同会导致类别错位。我习惯把类别名维护在一个全局列表里标注、转换、训练三处引用同一个列表能避免最低级的错误。转换时遇到XML里出现未在列表中的类别直接continue保留下次检查的线索。目录重建也比较机械用bash命令把图片和标签按train、val、test分开mkdir -p images/train images/val images/test mkdir -p labels/train labels/val labels/test # 按 ImageSets/Main 里的划分文件复制对应文件 while read f; do cp JPEGImages/$f.jpg images/train/ cp labels/$f.txt labels/train/ done ImageSets/Main/train.txt4.2 data.yaml与训练命令960输入尺寸与小batch的取舍转换完成后需要写一份data.yaml指定路径和类别表。400张图的划分我建议用7:2:1而不是8:1:1。训练集280张对6类缺陷够用验证集80张能让评估结果更稳定测试集40张只在全部训练结束后用一次避免反复看测试集导致过拟合。下面是data.yaml内容path: /path/to/PCB_defect_dataset train: images/train val: images/val test: images/test names: 0: missing_hole 1: short 2: open 3: burr 4: copper 5: solder训练命令yolo detect train datapcb.yaml modelyolov8n.pt imgsz960 epochs150 batch8 patience20 projectruns/pcb几个关键参数要特别说明。imgsz960是个取舍PCB缺陷属于典型小目标640输入下很多十几像素的缺陷会被压成几个像素检测器基本学不到特征拉到960后小目标特征保留更好代价是显存占用变大显存不够时可以先降到800看效果。batch8在280张训练图下偏保守但小数据集上batch过大容易提前过拟合我宁可用小batch加早停。patience20表示验证集指标连续20个epoch不提升就停400张图规模下150轮基本等不到早停能让训练早一点收敛。4.3 评估指标解读95.9%正确率与mAP之间差多少标题里的95.9%需要先弄清口径。如果它指逐张图的识别正确率即预测框和真值框IoU大于0.5且类别正确算检对在单一线体下并不夸张。如果它指mAP0.5那就更常见。还有一种是按框计算的precision或者accuracy三种口径数值能差出十个百分点。看评估结果时我只信任两个数验证集上的mAP0.5和mAP0.5:0.95。mAP0.5主要看能不能找到目标mAP0.5:0.95对框的贴合度更敏感。同一份数据mAP0.5跑到90%容易mAP0.5:0.95跑到50%以上已经算不错。所以复现95.9%时先问对方用的是哪个指标再对照自己的训练日志。YOLOv8训练结束后会自动在runs/pcb/exp目录下生成results.csv和混淆矩阵直接看val/0.5m就是mAP0.5。5. 避坑PCB缺陷检测的5个典型翻车现场与排查方法5.1 现象文件名对不上XML训练报FileNotFoundError数据集解压后直接训练报错提示某个txt标签对应的图片不在训练目录。原因通常是XML里的filename字段和实际文件名不一致比如大小写不同、多了路径前缀或者后缀是.png但实际是.jpg。转换脚本如果拿filename字段直接拼路径就会生成错误的标签文件名YOLO训练时找不到对应图片。解决在转换前以JPEGImages目录下的实际文件名为基准逐一比对XML里的filename。不一致时优先改XML里的filename因为坐标信息有效只是文件名对不上。批量修正用Python写个循环替换即可改完跑一遍校验脚本确认全部对应。5.2 现象数据增强后坐标错位缺陷框乱飞做了翻转、旋转、缩放增强后模型在验证集上性能不升反降画出来的预测框位置明显不对。原因是增强时只处理了图片没有同步修改XML或txt里的坐标。用OpenCV的rotate或flip后框的坐标必须跟着变换没有变换就把错位框喂进了训练。解决如果用的YOLOv8它内置的Mosaic、随机翻转会在训练时同步处理标签不需要额外处理。如果是自己写增强流程就把图像和坐标一起包进一个transform函数增强后立刻画框回显确认位置贴合。我在自写增强时会把图像和坐标绑在同一个函数里再加单元测试验证变换后框是否还在目标上。5.3 现象小缺陷漏检率高大缺陷几乎全对漏检的基本是几像素到十几像素的开路、毛刺而短路这种较大的缺陷几乎全对。原因是输入缩放时小目标被压缩到很小尺寸特征丢失。400张图里如果小目标样本又只有几十个模型基本学不到。解决先统计标注框的宽高分布确认有多少框小于16像素。若占比高训练时直接调imgsz到960或1024。另外可以用切片推理推理时把大图切成若干小块分别检测再合并结果对小缺陷提升明显。如果不想引入新库至少要在训练时关闭或调低Mosaic的缩放下限避免小缺陷被进一步缩小。5.4 现象类别分布极度不均衡少数类AP等于0六类缺陷里短路和开路占八成标注框毛刺、余铜只有个位数样本结果模型对多数类过拟合少数类几乎不预测。这是PCB数据集的通病产线上一类缺陷天然稀少但模型不能直接放弃。解决三个方向同时做。第一在损失函数或训练参数里给少数类加权YOLOv8里可以对样本少的类单独调整loss权重。第二对少数类做针对性增强比如复制粘贴被截断的缺陷样本、局部放大后贴回原图。第三把验证集采样改成按类别分层采样至少保证每类在验证集里有出现否则指标上少数类AP永远显示为0看不出模型潜力。5.5 现象反光与遮挡样本缺失正确率虚高验证集里全是背景均匀、光照充足的图模型在实验室环境下看着很好到了产线换上强反光板、元器件阴影环境漏检率飙升。原因不是模型泛化差是训练数据里没有覆盖反光、眩光、元件遮挡这些真实变化。解决在数据准备阶段就引入光照增强把亮度、对比度、灰度均值的扰动加进训练增强。同时在原图叠加深色背景和元件做成负样本让模型学习“看不到目标时不要乱报”。这一步往往比调网络结构收益更大PCB缺陷检测的主要成本是误报导致的人工复核。6. 从95.9%到更高针对PCB缺陷的进阶调优技巧6.1 分层拆分验证集先让95.9%可复现小数据集的性能数字很容易被拆分随机性左右。同一份数据随机拆出来的验证集不同mAP高低能差好几个点。我比较习惯的做法是进入训练前先固定随机种子用sklearn的StratifiedGroupKFold做一次分层拆分保证每个类别在训练集和验证集中的比例接近总体比例同时把同一张原图上的多个缺陷尽量放在同一侧避免数据串扰。这样得到的95.9%才有复现基础而不是换一次拆分就掉下去。from sklearn.model_selection import StratifiedGroupKFold import numpy as np # X 是样本索引y 是主类别标签groups 是图片ID X np.arange(len(samples)) y np.array([sample[primary_class] for sample in samples]) groups np.array([sample[image_id] for sample in samples]) sgkf StratifiedGroupKFold(n_splits5, shuffleTrue, random_state42) for train_idx, val_idx in sgkf.split(X, y, groups): print(len(train_idx), len(val_idx))参数说明random_state固定后重复执行结果一致这是可复现的关键。n_splits5代表5折交叉验证对小数据集可以把5折的结果均值当作最终指标比单次拆分的说服力强得多。6.2 小目标推理输入尺寸与切片检测的取舍训练时用960输入能提升小缺陷召回但产线实时推理时960分辨率会压帧率。另一种做法是保持推理图幅不变针对缺陷尺度区间单独优化。YOLOv8是anchor-free结构不需要手工调anchor但还是要确认最低层特征图没有因为输入尺寸太小而丢失小缺陷信息。我会先跑一批样本统计误检目标的最大边长再决定imgsz用800保帧率还是继续升1024保准确率。若两类都不满足再引入切片推理把一帧图切成四块分别检测再合并小缺陷漏检能降一半以上。6.3 用弱模型预标注把标注效率提上去400张图人工标注一到两天能完成但4000张就不行了。更现实的做法是用已标注的几百张训一个弱模型对剩余原图做预标注导出批量生成的XML或txt再在LabelImg里只复核模型标出的框人工做修正和类别调整。预标注把框先画到大致位置人工只需要拖动边界和改类别效率通常提升三倍左右。这个流程对PCB缺陷这种形态相对固定的场景特别适用模型见过的缺陷样式一旦够多预标注准确率会超预期。最后说一个习惯每次拿到这类数据集验证性训练只当参考真要上线前一定会在产线现场重新采集一批真实光照下的图连同模型预测结果一起做一次误检率评审。数据集的数字再好看也比不上现场十个坏板子里能挑出九个。希望这篇笔记对你在PCB缺陷检测数据集的使用和模型落地上有帮助。本文还有配套的精品资源点击获取
返回列表