PCB板缺陷检测是工业视觉检测中的重要应用。本文将详细介绍如何使用OpenCvSharp实现PCB板的缺陷检测,包括缺陷、断路、短路等问题的识别。
环境准备
// NuGet包引用
// Install-Package OpenCvSharp4
// Install-Package OpenCvSharp4.runtime.win
using OpenCvSharp;
using System;
完整代码实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
namespace AppPcb
{
    publicclass PCBDefectDetection
    {
        private Mat templateImage; // 模板图像
        private Mat testImage;     // 待测试图像
        public PCBDefectDetection(string templatePath, string testPath)
        {
            // 读取图像
            templateImage = Cv2.ImRead(templatePath, ImreadModes.Color);
            testImage = Cv2.ImRead(testPath, ImreadModes.Color);
            // 确保图像加载成功
            if (templateImage.Empty() || testImage.Empty())
            {
                thrownew Exception("图像加载失败!");
            }
        }
        public void DetectDefects()
        {
            // 1. 图像预处理
            Mat templateGray = PreprocessImage(templateImage);
            Mat testGray = PreprocessImage(testImage);
            // 2. 图像对齐
            Mat alignedTest = AlignImages(templateGray, testGray);
            // 3. 差异检测
            Mat diffMask = DetectDifferences(templateGray, alignedTest);
            // 4. 缺陷分析
            AnalyzeDefects(diffMask);
            // 5. 显示结果
            ShowResults(diffMask);
        }
        private Mat PreprocessImage(Mat input)
        {
            Mat result = new Mat();
            // 转换为灰度图
            Cv2.CvtColor(input, result, ColorConversionCodes.BGR2GRAY);
            // 高斯模糊去噪
            Cv2.GaussianBlur(result, result, new Size(7, 7), 0);
            // 自适应二值化
            Cv2.AdaptiveThreshold(
                result,
                result,
                255,
                AdaptiveThresholdTypes.GaussianC,
                ThresholdTypes.Binary,
                13,
                2
            );
            return result;
        }
        private Mat AlignImages(Mat template, Mat test)
        {
            // 特征点检测和匹配
            var orb = ORB.Create();
            // 检测关键点和描述符
            KeyPoint[] templateKeyPoints, testKeyPoints;
            Mat templateDescriptors = new Mat();
            Mat testDescriptors = new Mat();
            orb.DetectAndCompute(
                template,
                null,
                out templateKeyPoints,
                templateDescriptors
            );
            orb.DetectAndCompute(
                test,
                null,
                out testKeyPoints,
                testDescriptors
            );
            // 特征匹配
            var matcher = new BFMatcher(NormTypes.Hamming, crossCheck: true);
            DMatch[] matches = matcher.Match(templateDescriptors, testDescriptors);
            // 提取匹配点对
            Point2f[] templatePoints = matches.Select(m => templateKeyPoints[m.QueryIdx].Pt).ToArray();
            Point2f[] testPoints = matches.Select(m => testKeyPoints[m.TrainIdx].Pt).ToArray();
            // 计算变换矩阵
            Mat homography = Cv2.FindHomography(
                InputArray.Create(testPoints),
                InputArray.Create(templatePoints),
                HomographyMethods.Ransac
            );
            // 应用变换
            Mat alignedTest = new Mat();
            Cv2.WarpPerspective(
                test,
                alignedTest,
                homography,
                template.Size()
            );
            return alignedTest;
        }
        private Mat DetectDifferences(Mat template, Mat test)
        {
            Mat diff = new Mat();
            Mat diffMask = new Mat();
            // 计算差异
            Cv2.Absdiff(template, test, diff);
            // 二值化差异图
            Cv2.Threshold(
                diff,
                diffMask,
                30,
                255,
                ThresholdTypes.Binary
            );
            // 形态学操作,去除噪点
            Mat kernel = Cv2.GetStructuringElement(
                MorphShapes.Rect,
                new Size(3, 3)
            );
            Cv2.MorphologyEx(
                diffMask,
                diffMask,
                MorphTypes.Open,
                kernel,
                iterations: 2
            );
            return diffMask;
        }
        private void AnalyzeDefects(Mat diffMask)
        {
            // 查找轮廓
            Point[][] contours;
            HierarchyIndex[] hierarchy;
            Cv2.FindContours(
                diffMask,
                out contours,
                out hierarchy,
                RetrievalModes.External,
                ContourApproximationModes.ApproxSimple
            );
            // 分析每个缺陷区域
            foreach (var contour in contours)
            {
                // 计算缺陷面积
                double area = Cv2.ContourArea(contour);
                // 获取缺陷边界框
                Rect boundingRect = Cv2.BoundingRect(contour);
                // 判断缺陷类型
                if (area >= 20) // 面积阈值可调整
                {
                    Console.WriteLine($"发现缺陷:");
                    Console.WriteLine($"位置: X={boundingRect.X}, Y={boundingRect.Y}");
                    Console.WriteLine($"大小: {area:F2}平方像素");
                    // 在原图上标记缺陷
                    Cv2.Rectangle(
                        testImage,
                        boundingRect,
                        Scalar.Red,
                        2
                    );
                    Cv2.PutText(
                        testImage,
                        $"Defect: {area:F0}px",
                        new Point(boundingRect.X, boundingRect.Y - 5),
                        HersheyFonts.HersheySimplex,
                        0.5,
                        Scalar.Red,
                        1
                    );
                }
            }
        }
        private void ShowResults(Mat diffMask)
        {
            // 显示结果
            using (new Window("Template", templateImage))
            using (new Window("Test Image", testImage))
            using (new Window("Differences", diffMask))
            {
                Cv2.WaitKey();
            }
        }
    }
}
 
代码详解
图像预处理
高斯模糊(Gaussian Blur)
Cv2.GaussianBlur(result, result, new Size(7, 7), 0);
- result
- new Size(7, 7): 高斯核的大小,必须是正奇数(例如:3, 5, 7 等)。越大则模糊效果越明显。
- 0: 标准差 σ。在 X 和 Y 方向上的标准差。如果设为0,函数会根据核大小自动计算。
自适应二值化(Adaptive Thresholding)
Cv2.AdaptiveThreshold(
    result,
    result,
    255,
    AdaptiveThresholdTypes.GaussianC,
    ThresholdTypes.Binary,
    13,
    2
);
- result: 输入图像,经过二值化处理的输出图像(可覆盖)。
- 255: 阈值的最大值。当像素值超过该值时,将其赋为最大值。
- AdaptiveThresholdTypes.GaussianC: 自适应阈值的计算方法,这里使用“Gaussian”方法来计算局部阈值。
- ThresholdTypes.Binary: 阈值类型。在这里使用的是简单的二值化(黑和白)。
- 13: 领域大小,即考虑的邻域像素的尺寸(必须为奇数)。
- 2: 从每个计算得到的阈值中减去的常量C,用于调整二值化效果。
图像对齐
使用ORB特征检测和匹配实现图像对齐:
- 检测特征点和描述符
- 特征点匹配
- 计算单应性矩阵
- 透视变换实现图像对齐
差异检测
二值化差异图(Thresholding)
Cv2.Threshold(
    diff,
    diffMask,
    30,
    255,
    ThresholdTypes.Binary
);
- diff: 输入图像,通常是差异图(例如两张图像之间的差异)。
- diffMask
- 30: 阈值。像素值高于此值将被赋值为最大值(255),低于此值将被赋值为0。
- 255: 阈值的最大值。当像素值超过30时,输出结果将设为255。
- ThresholdTypes.Binary: 阈值类型。在这里使用的是简单的二值化,结果只有两种值(0和255)。
形态学操作(Morphological Operations)
Mat kernel = Cv2.GetStructuringElement(
    MorphShapes.Rect,
    new Size(3, 3)
);
- Cv2.GetStructuringElement
- MorphShapes.Rect: 指定结构元素的形状为矩形(也可使用其他形状,如椭圆或十字形)。
- new Size(3, 3)
Cv2.MorphologyEx(
    diffMask,
    diffMask,
    MorphTypes.Open,
    kernel,
    iterations: 2
);
- diffMask
- diffMask
- MorphTypes.Open
- kernel
- iterations: 2: 操作的迭代次数。设置为2意味着将在图像上执行两次开运算。
缺陷分析
分析检测到的缺陷:
- 轮廓检测
- 计算缺陷面积
- 获取缺陷位置
- 在原图上标记缺陷位置
总结
本文详细介绍了使用OpenCvSharp实现PCB板缺陷检测的完整流程。该方法具有以下特点:
- 自动化程度高
- 检测精度可靠
- 代码可扩展性强
- 实现相对简单
通过调整参数和优化算法,可以根据具体应用场景提高检测效果。
阅读原文:原文链接
该文章在 2025/3/3 16:00:11 编辑过