主要内容

本页采用了机器翻译。点击此处可查看英文原文。

利用 YOLOX 网络检测印刷电路板上的缺陷

本示例演示了如何使用 YOLOX 目标检测器检测、定位和分类印刷电路板 (PCB) 中的缺陷。

印刷电路板 (PCB) 包含各个电子元件及其连接。PCB 中的缺陷可能会导致性能不佳或产品故障。通过检测印刷电路板 (PCB) 中的缺陷,生产线可以剔除有缺陷的 PCB,从而确保电子设备的高质量。

下载预训练的 YOLOX 检测器

默认情况下,本示例使用 downloadTrainedNetwork 辅助函数下载 YOLOX 目标检测器的预训练版本 [1]。该辅助函数作为辅助文件附于本示例之后。您可以使用预训练网络直接运行整个示例,无需等待训练完成。

trainedPCBDefectDetectorNet_url = "https://ssd.mathworks.com/supportfiles/"+ ...
    "vision/data/trainedPCBDefectDetectorYOLOX.zip";
downloadTrainedNetwork(trainedPCBDefectDetectorNet_url,pwd);
load("trainedPCBDefectDetectorYOLOX.mat");

下载 PCB 缺陷数据集

本示例使用了 PCB 缺陷数据集 [2] [3]。该数据集包含 1,386 张带有合成缺陷的 PCB 元素图像。该数据包含六种缺陷类型:缺孔、鼠咬、开路、短路、刺状缺陷和异常铜。每个图像中都包含多个位于不同位置的同类缺陷。该数据集包含了每个图像中每个缺陷的边界框和坐标信息。该数据集的大小为 1.87 GB。

请将数据集的位置指定为 dataDir。使用 downloadPCBDefectData 辅助函数下载数据集。此函数作为支持文件包含在本示例中。

dataDir = fullfile(tempdir,"PCBDefects");
downloadPCBDefectData(dataDir)

执行目标检测

从数据集中读取一张示例图像。

sampleImage = imread(fullfile(dataDir,"PCB-DATASET-master","images", ...
    "Missing_hole","01_missing_hole_01.jpg"));
Warning: Division by zero when processing CompressedBitsPerPixel.  The value has been set to NaN.

使用 detect 函数预测每个边界框的边界框、标签以及特定类别的置信度得分。

[bboxes,scores,labels] = detect(detector,sampleImage);

显示结果。

imshow(sampleImage)
showShape("rectangle",bboxes,Label=labels);
title("Predicted Defects")

准备要训练的数据

创建一个用于读取和管理镜像数据的镜像数据存储。

imageDir = fullfile(dataDir,"PCB-DATASET-master","images");
imds = imageDatastore(imageDir,FileExtensions=".jpg",IncludeSubfolders=true);

创建一个数据存储文件,用于从 XML 文件中读取注解数据。指定一个自定义读取函数,用于解析 XML 文件并提取边界框信息。自定义读取函数 readPCBDefectAnnotations 作为辅助文件附在示例中。

annoDir = fullfile(dataDir,"PCB-DATASET-master","Annotations");
fds = fileDatastore(annoDir,ReadFcn=@readPCBDefectAnnotations, ...
    FileExtensions=".xml",IncludeSubfolders=true);

将标注的边界框数据保存为边界框标签数据存储。

annotations = readall(fds);
tbl = struct2table(vertcat(annotations{:}));
blds = boxLabelDatastore(tbl);

将目标类的名称作为分类向量获取。

classNames = categories(blds.LabelData{1,2})
classNames = 6×1 cell array
    "'missing_hole'"
    "'mouse_bite'"
    "'open_circuit'"
    "'short'"
    "'spur'"
    "'spurious_copper'"

将图像和框标注数据存储合并。

ds = combine(imds,blds);

分析目标类的分布情况

使用 countEachLabel 函数测量数据集中类标签的分布情况。该数据集中的类分布是均衡的。

countEachLabel(blds)
ans=6×3 table
       missing_hole    497    115
         mouse_bite    492    115
       open_circuit    482    116
              short    491    116
               spur    488    115
    spurious_copper    503    116

分区数据

在对数据进行分区之前,请将全局随机状态设置为默认状态,以确保结果具有更高的可重复性。

rng("default");

将数据集分成训练集、验证集和测试集。由于图像总数相对较少,因此将相对较大的比例 (70%) 的数据分配给训练。将 15% 的资源用于验证,其余部分用于测试。

numImages = ds.numpartitions;
numTrain = floor(0.7*numImages);
numVal = floor(0.15*numImages);

shuffledIndices = randperm(numImages);
dsTrain = subset(ds,shuffledIndices(1:numTrain));
dsVal = subset(ds,shuffledIndices(numTrain+1:numTrain+numVal));
dsTest = subset(ds,shuffledIndices(numTrain+numVal+1:end));

增强训练数据

通过使用 transform 函数,并结合由 augmentDataForPCBDefectDetection 辅助函数指定的自定义预处理操作,来扩充训练数据。此辅助函数作为支持文件包含在本示例中。augmentDataForPCBDefectDetection 函数对输入数据应用以下扩展操作:

  • 随机水平翻转(对称)

  • 按 [1, 1.1] 范围内的缩放因子进行随机缩放

  • 在 [-50, 50] 像素范围内进行水平和垂直方向的随机平移

dsTrain = transform(dsTrain,@augmentDataForPCBDefectDetection);

定义 YOLOX 目标检测器网络架构

使用 yoloxObjectDetector 函数创建 YOLOX 目标检测器。指定以 CSP-DarkNet-53 为基础网络、并在 COCO 数据集上训练而成的预训练网络 [1]。请指定类名和网络输入尺寸。

inputSize = [800 800 3];
detectorIn = yoloxObjectDetector("tiny-coco",classNames,InputSize=inputSize);

指定训练选项

使用 trainingOptions (Deep Learning Toolbox) 函数指定网络训练选项。使用 SGDM 求解器对目标检测器进行训练,最多 100 个 epoch。请将 ValidationData 名称-值参量指定为验证数据。将 OutputNetwork 设置为 "best-validation-loss",这样在训练结束时,即可获得训练过程中验证损失最低的网络。

options = trainingOptions("sgdm", ...
    InitialLearnRate=5e-4, ...
    LearnRateSchedule="piecewise", ...
    LearnRateDropFactor=0.99, ...
    LearnRateDropPeriod=1, ...   
    MiniBatchSize=20, ...
    MaxEpochs=100, ...
    ExecutionEnvironment="auto", ...
    Shuffle="every-epoch", ...
    VerboseFrequency=25, ...
    ValidationFrequency=100, ...
    ValidationData=dsVal, ...
    ResetInputNormalization=false, ...
    OutputNetwork="best-validation-loss", ...
    GradientThreshold=30, ...
    L2Regularization=5e-4);

训练检测器

要训练检测器,请将 doTraining 变量设置为 true。使用 trainYOLOXObjectDetector 函数对检测器进行训练。

如果可用,请在一块或多块 GPU 上进行训练。使用 GPU 需要 Parallel Computing Toolbox™ 许可证和支持 CUDA® 的 NVIDIA® GPU。有关详细信息,请参阅GPU 计算要求 (Parallel Computing Toolbox)。在配备 24 GB 内存的 NVIDIA Titan RTX™ 上,训练大约需要 7.5 小时。

doTraining = false;
if doTraining       
    [detector,info] = trainYOLOXObjectDetector(dsTrain,detectorIn,options,"FreezeSubNetwork","none");
    modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss"));
    save(fullfile(tempdir,"trainedPCBDefectDetectorYoloX"+modelDateTime+".mat"), ...
        "detector");
else
    load("trainedPCBDefectDetectorYOLOX.mat");
end

评估检测器

检测所有测试图像的边界框。将检测阈值设置为较低的值以检测到尽可能多的目标。这有助于您评估在整个检测分数值范围内检测性能的表现。

detectionResults = detect(detector,dsTest,Threshold=0.01);

使用 evaluateObjectDetection 函数,对测试集的检测结果计算目标检测度量。

metrics = evaluateObjectDetection(detectionResults,dsTest);

计算并显示每个类别的平均精确率 (AP) 得分。精确率量化了检测器正确分类目标的能力。

AP = averagePrecision(metrics);
table(classNames,AP)
ans=6×2 table
       'missing_hole'    0.9809
         'mouse_bite'    0.8571
       'open_circuit'    0.9432
              'short'    0.9735
               'spur'    0.9427
    'spurious_copper'    0.8899

计算每个缺陷目标检测的召回率和精确率值。召回率量化了检测器检测某类所有相关目标的能力。精确率-召回率 (PR) 曲线突显了在不同召回率水平下,检测器的精确程度。理想情况下,所有召回水平的精确率均为 1。绘制测试数据的 PR 曲线。

classNameStrings = metrics.ClassNames;
class = classNameStrings(4);
[precision, recall, ~] = precisionRecall(metrics,ClassName=class);
plot(recall{:},precision{:})
title(sprintf("Average Precision for '" + class + "' Defect: " + "%.2f",averagePrecision(metrics,ClassName=class)), interpreter="none")
xlabel("Recall")
ylabel("Precision")
grid on

评估基于目标大小的检测度量

使用 metricsByArea 函数研究目标大小对检测器性能的影响,该函数可计算特定目标大小范围内的目标检测度量。要评估基于大小的度量,您可以根据一组自定义范围来定义目标大小的范围。首先,根据测试集目标面积分布的第 33 百分位数和第 66 百分位数分界点,将测试图像的边界框尺寸划分为小、中、大三类目标尺寸。

绘制测试集中的目标尺寸分布图,其中边界框面积定义了目标尺寸。

testSetObjects = dsTest.UnderlyingDatastores{2};
objectLabels = readall(testSetObjects);
boxes = objectLabels(:,1);
boxes = vertcat(boxes{:});
boxArea = prod(boxes(:,3:4),2);
histogram(boxArea)
title("Bounding Box Area Distribution")
xlabel("Box Area");
ylabel("Count")

定义边界框的区域范围,然后使用 metricsByArea 对所定义的区域范围进行目标检测度量。经过训练的检测器的平均精度均值 (mAP) 度量在小、中、大三种目标尺寸下表现大致相当,其中对中型目标的检测性能略有提升。

boxPrctileBoundaries = prctile(boxArea,100*[1/3,2/3]);
metricsByArea(metrics,[0, boxPrctileBoundaries, inf])
ans=3×4 table
             0    3.5737e+03    144    0.8416    0.8416
    3.5737e+03          5107    145    0.9605    0.9605
          5107           Inf    144    0.8854    0.8854

参考资料

[1] Ge, Zheng, Songtao Liu, Feng Wang, Zeming Li, and Jian Sun."YOLOX:Exceeding YOLO Series in 2021", arXiv, August 6, 2021. https://arxiv.org/abs/2107.08430.

[2] Huang, Weibo, and Peng Wei."A PCB Dataset for Defects Detection and Classification."Preprint, submitted January 23, 2019. https://arxiv.org/abs/1901.08204.

[3] PCB-DATASET.Accessed December 20, 2022. https://github.com/Ironbrotherstyle/PCB-DATASET.

另请参阅

| | | | | (Deep Learning Toolbox) |

主题