3D缺陷检测(1)

1.简介

2D图像基础,滤波,直方图统计,模板匹配

3D处理基础,点云滤波,点云分割

基于c++开发,测试

2.缺陷检测概述

产品异常产生缺陷,缺陷检测检出产品是否存在缺陷,即使识别次品,避免造成更大的损失。

内部缺陷:气泡,孔洞,内部裂纹(检测技术:x射线,超声波,机械应力波)

外部缺陷:刮痕,破损,脏污(检测技术 2D(图像处理,滤波,分割),3D(点云,深度图))

例如:

环形3D结构光,不同高度具有不同的颜色,依赖光源的稳定性,均匀性等。

3D的数据,点云和深度图。主要以PCB检测为例,比如少锡、多锡、虚焊。通过检测爬锡高度,或者爬坡角度等指标,卡控不良。

 缺陷检测的准确率很重要,漏检容易造成很严重的问题。

由于精度高,一般是先对样品扫描,扫描过后再拼接,得到样品的全景图,再进行后续处理。可以检测元器件的偏移,管脚翘脚,少锡,虚焊,浮高等。

缺陷检测的任务可以分为检测,分类,分割。检测包含了分类和坐标回归。分割提取目标轮廓信息。计算复杂度:一般分割>检测>分类。

 

mobile-net,yolov5,ICNet。

有监督学习,分类

无监督学习,聚类,难度大

深度学习参数少,几乎能适应各种各样的缺陷检测,项目多,存在小样本,小目标问题。对于实时性的要求需要量化模型,进行剪枝,知识蒸馏,也可以采用清凉模型。

 3D成像系统 激光线扫,结构光扫描等。

 光源位置方向,相机位置方向,可以求得相交位置处像素点的空间位置。

 一般是条纹结构光,对拍摄的条纹结构进行解度可以得到物体表面的信息。

受物体材料朗伯反射性能影响。解码包裹相位,光栅序列,求得绝对相位,转换成点云。

点云可以分为有序点云和无序点云。

深度图灰度值表示高度值

mesh渲染,点云图并不连续的,而经过mesh渲染则可以看到连续的表面。

3.常用算法库及工具

PCL开源库

4.2D图像滤波方法

简述:

计算机视觉场景广泛,以深度学习为主,精度不高。图像处理算法。

机器视觉场景固定,以传统学习为主。图像采集,镜头控制,图像处理等算法。

操作系统 Linux,编程语言python,c++,图像处理基础知识,OpenCV使用。

1.2D滤波

去除噪声提升图像质量:均值,中值,高斯滤波

 高通:比如边缘提取 canny,sobel,laplacian等算子

 

 

 

 ·

 

还有自适应中值滤波:

OpenCV 中没有直接提供自适应中值滤波(Adaptive Median Filter)的函数,但你可以自己实现它。自适应中值滤波器与常规中值滤波器不同,它根据像素邻域内的特定条件调整窗口大小,这使得它在去除噪声(特别是“盐和胡椒”噪声)的同时更好地保护图像细节。

自适应中值滤波的基本步骤如下:

  1. 选择初始窗口大小:从一个小窗口(例如 3x3)开始。
  2. 计算窗口内的中值:计算当前窗口内像素的中值。
  3. 调整窗口大小:如果中值满足特定条件,则调整窗口大小。通常,如果中值接近窗口中的最大或最小值,则增大窗口。
  4. 检查是否满足停止条件:如果窗口大小超过预设的最大值或者已经找到了合适的中值,则停止调整。
  5. 应用中值:用找到的中值替换当前像素。

以下是一个简单的自适应中值滤波的实现示例:

#include <opencv2/opencv.hpp>
#include <algorithm>

using namespace cv;

void adaptiveMedianFilter(const Mat &src, Mat &dst, int maxSize) {
    dst = src.clone();
    for (int i = 0; i < src.rows; i++) {
        for (int j = 0; j < src.cols; j++) {
            int windowSize = 3;
            while (windowSize <= maxSize) {
                int halfSize = windowSize / 2;
                std::vector<uchar> neighbors;
                for (int x = -halfSize; x <= halfSize; x++) {
                    for (int y = -halfSize; y <= halfSize; y++) {
                        int ni = i + x, nj = j + y;
                        if (ni >= 0 && ni < src.rows && nj >= 0 && nj < src.cols) {
                            neighbors.push_back(src.at<uchar>(ni, nj));
                        }
                    }
                }
                std::nth_element(neighbors.begin(), neighbors.begin() + neighbors.size() / 2, neighbors.end());
                uchar median = neighbors[neighbors.size() / 2];

                // 判断中值是否是极端值
                auto minMax = std::minmax_element(neighbors.begin(), neighbors.end());
                if (median != *minMax.first && median != *minMax.second) {
                    dst.at<uchar>(i, j) = median;
                    break;
                }
                windowSize += 2; // 增加窗口大小
            }
        }
    }
}

int main() {
    Mat image = imread("path/to/your/image.jpg", IMREAD_GRAYSCALE);
    if(image.empty()) {
        std::cout << "Could not open or find the image" << std::endl;
        return -1;
    }

    Mat filteredImage;
    adaptiveMedianFilter(image, filteredImage, 7); // 使用最大窗口大小为 7

    imshow("Original Image", image);
    imshow("Adaptive Median Filtered Image", filteredImage);
    waitKey(0);
    destroyAllWindows();

    return 0;
}

 确保模板内元素和求平均为1。

深度图像
       深度图像(depth image)也被称为距离影像(range image),是指将从图像采集器到场景中各点的距离(深度)作为像素值的图像,它直接反映了景物可见表面的几何形状。

深度图像经过坐标转换可以计算为点云数据,有规则及必要信息的点云数据也可以反算为深度图像数据。
深度数据流所提供的图像帧中,每一个像素点代表的是在深度感应器的视野中,该特定的(x, y)坐标处物体到离摄像头平面最近的物体到该平面的距离(以毫米为单位).
 

图像深度
  图像深度 是指存储每个像素所用的位数,也用于量度图像的色彩分辨率。

  图像深度 确定彩色图像的每个像素可能有的颜色数,或者确定灰度图像的每个像素可能有的灰度级数。它决定了彩色图像中可出现的最多颜色数,或灰度图像中的最大灰度等级。比如一幅单色图像,若每个像素有8位,则最大灰度数目为2的8次方,即256。一幅彩色图像RGB三通道的像素位数分别为4,4,2,则最大颜色数目为2的4+4+2次方,即1024,就是说像素的深度为10位,每个像素可以是1024种颜色中的一种。
 

2.Blob分析

提取形状,数量统计

先是前景分割,再独立分割。

在计算机视觉和图像处理领域,Blob(二值图像区域)分析是一种常用的技术,主要用于识别和分析图像中的连续像素区域(称为blobs),这些区域在形状、大小、亮度等方面具有某种共同特征。Blob分析通常用于物体检测、追踪、形状分析等任务。以下是Blob分析的一些关键步骤和技术:

  1. 预处理:包括降噪、增强对比度等,以提高Blob检测的准确性。

  2. 二值化:将图像转换为黑白二值图像,通常使用阈值分割。

  3. Blob检测:在二值图像中识别连续的像素区域。常用方法包括连通区域标记(Connected Component Labeling)和轮廓检测(如OpenCV中的findContours)。

  4. Blob特征提取:计算每个Blob的特征,如面积、周长、重心位置、形状描述子等。

  5. 后处理:根据需要对Blobs进行分类、筛选或追踪。

 

在 C++ 中使用 OpenCV 库的 threshold 函数进行图像阈值处理是一个常见的操作。该函数主要用于将图像转换成二值图像,即将图像中的像素值根据给定的阈值分为两部分。以下是 OpenCV 的 threshold 函数在 C++ 中的使用方法:

double cv::threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)

参数说明

  • src: 输入图像,必须是单通道图像,例如灰度图。
  • dst: 输出图像,它是二值化后的图像。
  • thresh: 阈值。
  • maxval: 当像素值超过(或根据阈值类型,可能是低于)阈值时应该被赋予的最大值。
  • type: 阈值类型,常见的类型包括:
  • THRESH_BINARY:如果像素值大于阈值,则赋值为maxval,否则为0。
  • THRESH_BINARY_INV:如果像素值大于阈值,则赋值为0,否则为maxval
  • THRESH_TRUNC:如果像素值大于阈值,则赋值为阈值,否则保持不变。
  • THRESH_TOZERO:如果像素值小于阈值,则赋值为0,否则保持不变。
  • THRESH_TOZERO_INV:如果像素值大于阈值,则赋值为0,否则保持不变。

 threshhold使用:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;

int main() {
    // 读取图像
    Mat src = imread("path_to_image.jpg", IMREAD_GRAYSCALE);
    if (src.empty()) {
        std::cerr << "Error: Loading image" << std::endl;
        return -1;
    }

    Mat dst;
    double thresh = 100;  // 设定阈值
    double maxval = 255;  // 设定最大值

    // 应用阈值处理
    threshold(src, dst, thresh, maxval, THRESH_BINARY);

    // 显示图像
    imshow("Original Image", src);
    imshow("Thresholded Image", dst);

    waitKey(0);
    return 0;
}

 在 OpenCV 中,morphologyEx 函数用于执行各种形态学操作,如腐蚀、膨胀、开运算、闭运算等。这些操作通常用于图像预处理、特征提取或去除噪声。以下是如何在 C++ 中使用 morphologyEx 函数的简介:

void cv::morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue())
  • src: 输入图像。
  • dst: 输出图像,与输入图像具有相同的大小和类型。
  • op: 形态学操作类型,如 MORPH_OPENMORPH_CLOSEMORPH_GRADIENTMORPH_TOPHATMORPH_BLACKHAT 等。
  • kernel: 结构元素,用于定义操作的性质。可以使用 getStructuringElement 函数创建。
  • anchor: 锚点位置,默认值为元素的中心。
  • iterations: 操作的迭代次数。
  • borderType: 边界类型。
  • borderValue: 当 borderTypeBORDER_CONSTANT 时的边界值。

形态学例子:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;

int main() {
    // 读取图像
    Mat src = imread("path_to_image.jpg", IMREAD_GRAYSCALE);
    if (src.empty()) {
        std::cerr << "Error: Loading image" << std::endl;
        return -1;
    }

    Mat dst;
    int operation = MORPH_OPEN;  // 定义形态学操作类型
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));  // 创建结构元素

    // 应用形态学操作
    morphologyEx(src, dst, operation, element);

    // 显示图像
    imshow("Original Image", src);
    imshow("Morphology Operation Result", dst);

    waitKey(0);
    return 0;
}

在这个示例中,首先读取一幅图像。然后定义了一个形态学操作类型(这里使用的是开运算 MORPH_OPEN)和一个结构元素。使用 morphologyEx 函数对图像进行形态学处理。最后,展示原始图像和处理后的图像。

在 OpenCV 中,floodFill 函数用于执行洪水填充操作。这是一个非常强大的工具,通常用于分割图像的某些部分、改变图像中区域的颜色或者在图像分析中识别和标记连通区域。floodFill 函数基本上是从一个种子点开始,然后向四周扩展,直到满足某些停止条件。

函数原型:

int cv::floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, Rect* rect = 0, Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), int flags = 4)

参数说明:

  • image: 输入/输出图像,必须是 1 通道或 3 通道图像。
  • mask: 操作的掩码,更新填充区域的边界。大小必须是 (image.rows + 2) x (image.cols + 2),且为 8 位单通道。
  • seedPoint: 开始填充的种子点。
  • newVal: 填充区域的新值。
  • rect: 可选的输出参数,返回被填充区域的最小边界矩形。
  • loDiffupDiff: 填充颜色的下限和上限差值。
  • flags: 操作标志,定义填充模式和颜色选择方式。

例子:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;

int main() {
    // 读取图像
    Mat image = imread("path_to_image.jpg");
    if (image.empty()) {
        std::cerr << "Error: Loading image" << std::endl;
        return -1;
    }

    // 创建掩码
    Mat mask = Mat::zeros(image.rows + 2, image.cols + 2, CV_8UC1);

    // 种子点
    Point seedPoint = Point(50, 50);  // 需要根据实际情况选择合适的点

    // 洪水填充
    floodFill(image, mask, seedPoint, Scalar(255, 0, 0), 0, Scalar(10, 10, 10), Scalar(10, 10, 10), 4);

    // 显示图像
    imshow("Flood Fill Image", image);
    waitKey(0);
    return 0;
}

在这个例子中,首先读取了一幅图像。创建了一个稍大于原图的掩码。然后选择一个种子点,并使用 floodFill 函数进行填充。最后,显示填充后的图像。

3.直方图统计分析

根据灰度等级调整对比度,增强局部特征

 

应该是Otsu方法。

Otsu 图像分割是一种基于阈值的图像分割方法,旨在将图像中的前景和背景分开。这个方法的主要思想是找到一个阈值,将图像中的像素分为两个类别,使得这两个类别之间的类内方差最小,而类间方差最大。这个阈值将图像分成了前景和背景两个部分,从而实现了图像分割。

以下是使用Otsu算法进行图像分割的一般步骤:

  1. 首先,将彩色图像转换为灰度图像,因为Otsu算法通常用于灰度图像的分割。

  2. 计算每个灰度级别的像素在图像中的频数分布,可以得到直方图。

  3. 计算图像的总体平均灰度值和总体方差。

  4. 遍历可能的阈值(从0到最大灰度级别),对于每个阈值,将图像分成两个部分:低于阈值的像素属于一个类别,高于阈值的像素属于另一个类别。

  5. 对于每个阈值,计算两个类别的类内方差和类间方差。

  6. 使用Otsu的准则,选择使类间方差最大化的阈值。通常,这可以通过最大化以下公式来实现:

    类间方差 = 类间方差 / 类内方差

  7. 将选定的阈值应用于图像,将图像分割成前景和背景。

  8. 最终,你可以对分割后的前景和背景区域进行后续处理或分析。

Otsu算法是一种自适应的方法,不需要事先知道图像的特定阈值。它在很多情况下都表现良好,特别是对于具有双峰直方图的图像。然而,在一些情况下,它可能不够精确,因此需要根据具体应用的要求进行调整或使用其他图像分割方法。

例子:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取图像
    Mat img = imread("path_to_your_image.jpg", IMREAD_GRAYSCALE);
    if (img.empty()) {
        cout << "无法读取图像" << endl;
        return -1;
    }

    // 初始化直方图参数
    int histSize = 256;    // 直方图大小
    float range[] = { 0, 256 };
    const float* histRange = { range };

    Mat hist;
    calcHist(&img, 1, 0, Mat(), hist, 1, &histSize, &histRange);

    // 显示直方图
    int hist_w = 512, hist_h = 400;
    int bin_w = cvRound((double)hist_w / histSize);

    Mat histImage(hist_h, hist_w, CV_8UC1, Scalar(0, 0, 0));
    normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());

    for (int i = 1; i < histSize; i++) {
        line(histImage,
             Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),
             Point(bin_w * i, hist_h - cvRound(hist.at<float>(i))),
             Scalar(255, 0, 0), 2, 8, 0);
    }

    imshow("原始图像", img);
    imshow("直方图", histImage);

    waitKey(0);
    return 0;
}

在这个示例中,我们首先读取一个灰度图像,然后使用 calcHist 函数计算其直方图。直方图的每个“箱”(bin)的宽度通过图像的直方图大小来决定。接着,我们创建了一个用于显示直方图的空白图像,并使用 line 函数在其中绘制直方图。

请确保将 "path_to_your_image.jpg" 替换为你的图像文件的路径。

4.模板匹配

定位目标

 

 

 opencv自带函数。但不支持旋转,缩放。他的输出结果是映射图,图中的位置表示模板在图像的相对位置处的二者的相关性。

halcon,点击file,点击browse Hdevelop Example programs,在左边method里选择合适的匹配方法。在右边选择合适的例子,这里选择第3个find ncc。

 

在pogram window窗口就会出现代码,查看代码,前面是读进一幅图,选择合适区域作为模板。选择并按F5执行,程序会在stop处暂停,接着按F6步进查看每副图像匹配 。

6.深度图分割

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值