当前位置:   article > 正文

3D缺陷检测(1)

3d缺陷检测

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. 应用中值:用找到的中值替换当前像素。

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

  1. #include <opencv2/opencv.hpp>
  2. #include <algorithm>
  3. using namespace cv;
  4. void adaptiveMedianFilter(const Mat &src, Mat &dst, int maxSize) {
  5. dst = src.clone();
  6. for (int i = 0; i < src.rows; i++) {
  7. for (int j = 0; j < src.cols; j++) {
  8. int windowSize = 3;
  9. while (windowSize <= maxSize) {
  10. int halfSize = windowSize / 2;
  11. std::vector<uchar> neighbors;
  12. for (int x = -halfSize; x <= halfSize; x++) {
  13. for (int y = -halfSize; y <= halfSize; y++) {
  14. int ni = i + x, nj = j + y;
  15. if (ni >= 0 && ni < src.rows && nj >= 0 && nj < src.cols) {
  16. neighbors.push_back(src.at<uchar>(ni, nj));
  17. }
  18. }
  19. }
  20. std::nth_element(neighbors.begin(), neighbors.begin() + neighbors.size() / 2, neighbors.end());
  21. uchar median = neighbors[neighbors.size() / 2];
  22. // 判断中值是否是极端值
  23. auto minMax = std::minmax_element(neighbors.begin(), neighbors.end());
  24. if (median != *minMax.first && median != *minMax.second) {
  25. dst.at<uchar>(i, j) = median;
  26. break;
  27. }
  28. windowSize += 2; // 增加窗口大小
  29. }
  30. }
  31. }
  32. }
  33. int main() {
  34. Mat image = imread("path/to/your/image.jpg", IMREAD_GRAYSCALE);
  35. if(image.empty()) {
  36. std::cout << "Could not open or find the image" << std::endl;
  37. return -1;
  38. }
  39. Mat filteredImage;
  40. adaptiveMedianFilter(image, filteredImage, 7); // 使用最大窗口大小为 7
  41. imshow("Original Image", image);
  42. imshow("Adaptive Median Filtered Image", filteredImage);
  43. waitKey(0);
  44. destroyAllWindows();
  45. return 0;
  46. }

 确保模板内元素和求平均为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使用:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. int main() {
  5. // 读取图像
  6. Mat src = imread("path_to_image.jpg", IMREAD_GRAYSCALE);
  7. if (src.empty()) {
  8. std::cerr << "Error: Loading image" << std::endl;
  9. return -1;
  10. }
  11. Mat dst;
  12. double thresh = 100; // 设定阈值
  13. double maxval = 255; // 设定最大值
  14. // 应用阈值处理
  15. threshold(src, dst, thresh, maxval, THRESH_BINARY);
  16. // 显示图像
  17. imshow("Original Image", src);
  18. imshow("Thresholded Image", dst);
  19. waitKey(0);
  20. return 0;
  21. }

 在 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 时的边界值。

形态学例子:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. int main() {
  5. // 读取图像
  6. Mat src = imread("path_to_image.jpg", IMREAD_GRAYSCALE);
  7. if (src.empty()) {
  8. std::cerr << "Error: Loading image" << std::endl;
  9. return -1;
  10. }
  11. Mat dst;
  12. int operation = MORPH_OPEN; // 定义形态学操作类型
  13. Mat element = getStructuringElement(MORPH_RECT, Size(5, 5)); // 创建结构元素
  14. // 应用形态学操作
  15. morphologyEx(src, dst, operation, element);
  16. // 显示图像
  17. imshow("Original Image", src);
  18. imshow("Morphology Operation Result", dst);
  19. waitKey(0);
  20. return 0;
  21. }

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

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

函数原型:

  1. 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: 操作标志,定义填充模式和颜色选择方式。

例子:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. int main() {
  5. // 读取图像
  6. Mat image = imread("path_to_image.jpg");
  7. if (image.empty()) {
  8. std::cerr << "Error: Loading image" << std::endl;
  9. return -1;
  10. }
  11. // 创建掩码
  12. Mat mask = Mat::zeros(image.rows + 2, image.cols + 2, CV_8UC1);
  13. // 种子点
  14. Point seedPoint = Point(50, 50); // 需要根据实际情况选择合适的点
  15. // 洪水填充
  16. floodFill(image, mask, seedPoint, Scalar(255, 0, 0), 0, Scalar(10, 10, 10), Scalar(10, 10, 10), 4);
  17. // 显示图像
  18. imshow("Flood Fill Image", image);
  19. waitKey(0);
  20. return 0;
  21. }

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

3.直方图统计分析

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

 

应该是Otsu方法。

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

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

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

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

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

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

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

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

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

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

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

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

例子:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main() {
  6. // 读取图像
  7. Mat img = imread("path_to_your_image.jpg", IMREAD_GRAYSCALE);
  8. if (img.empty()) {
  9. cout << "无法读取图像" << endl;
  10. return -1;
  11. }
  12. // 初始化直方图参数
  13. int histSize = 256; // 直方图大小
  14. float range[] = { 0, 256 };
  15. const float* histRange = { range };
  16. Mat hist;
  17. calcHist(&img, 1, 0, Mat(), hist, 1, &histSize, &histRange);
  18. // 显示直方图
  19. int hist_w = 512, hist_h = 400;
  20. int bin_w = cvRound((double)hist_w / histSize);
  21. Mat histImage(hist_h, hist_w, CV_8UC1, Scalar(0, 0, 0));
  22. normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
  23. for (int i = 1; i < histSize; i++) {
  24. line(histImage,
  25. Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),
  26. Point(bin_w * i, hist_h - cvRound(hist.at<float>(i))),
  27. Scalar(255, 0, 0), 2, 8, 0);
  28. }
  29. imshow("原始图像", img);
  30. imshow("直方图", histImage);
  31. waitKey(0);
  32. return 0;
  33. }

在这个示例中,我们首先读取一个灰度图像,然后使用 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.深度图分割

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/606290
推荐阅读
相关标签
  

闽ICP备14008679号