摘要
本篇来用OpenCV实现Halcon中一个简单的网格缺陷检测实例。 Halcon中对应的例子为novelty_detection_dyn_threshold.hdev。并对二值化中的三种阈值处理进行介绍和比较:
- 全局阈值二值化(含OTSU方法)
-
自适应阈值二值化
- 双阈值二值化
阈值处理分析
1️⃣全局阈值二值化-threshold()
OpenCV的threshold函数一般是给定一个阈值,对超过或者低于这个阈值的像素进行处理,函数如下:
threshold( InputArray src, // 输入图像 OutputArray dst, // 输出图像 double thresh, // 阈值 double maxValue, // 最大值(对于三通道图像一般是255) int thresholdType // 阈值化操作的类型 ) 阈值化操作的类型常用两种: THRESH_BINARY //黑背景找白目标(即超过设定阈值的值置255,其他为0) THRESH_BINARY_INV //白背景找黑目标(即超过设定阈值的值置0,其他为255)
全局阈值类似一刀切的概念。对于整体图像来说,找到一个合适的阈值,将图像分为0(黑色)和255(白色)。
2️⃣自适应阈值二值化-adaptiveThreshold()
对于亮度分布差异较大的图像,因为常常无法找到一个合适的阈值。因此我们需要一种改进的阈值化算法,即自适应阈值化。
adaptiveThreshold( InputArray src, // 输入图像 OutputArray dst, // 输出图像 double maxValue, // 最大值 int adaptiveMethod, // 自适应方法,平均或高斯 int thresholdType // 阈值化类型 int blockSize, // 块大小(大小必须为奇数) double C // 常量(即偏移值调整量) ) //adaptiveThreshold()支持两种自适应方法: ADAPTIVE_THRESH_MEAN_C //平均:阈值是邻域的平均值
ADAPTIVE_THRESH_GAUSSIAN_C //高斯:阈值是邻域值的加权和,其中权重是高斯窗口
自适应阈值化能够根据图像不同区域亮度分布的,改变阈值。因此,我们针对同一图像的不同区域获得不同的阈值,并为具有不同照明的图像提供更好的结果。
3️⃣双阈值二值化
对于图像具有明显的双分界特征,可以使用双阈值法进行二值化操作,即实现Halcon中的threshold函数。
简单来说:
- Halcon的threshold函数是获取区间[a, b]之间的灰度值(双阈值)
- OpenCV的threshold只能针对大于或者小于a或者b的灰度值处理(单阈值)
因此我们可以预设两个特定的阈值量thresh1、thresh2,并且thresh1 < thresh2 。阈值化的过程就是,将在 (thresh1,thresh2) 这个区间内的灰度值设置为maxVal(255),将其余部分设置为0 。
const int maxVal = 255; //预设最大值 int low_threshold = 90; //较小的阈值量 int high_threshold = 190; //较大的阈值量 //小阈值对源灰度图像进行二进制阈值化操作 threshold(srcGray, dst1, low_threshold, maxVal, THRESH_BINARY); //大阈值对源灰度图像进行反二进制阈值化操作 threshold(srcGray, dst2, high_threshold, maxVal, THRESH_BINARY_INV); //矩阵"与运算"得到二值化结果 bitwise_and(dst1, dst2, dst); //对像素加和 imshow("双阈值二值化", dst);程序中主要还是用到了threshold()函数,对较小的阈值量进行二进制阈值化,而对较大的阈值量进行反二进制化操作,最后将所得的两幅图像进行与运算,得到最终效果。
网格缺陷检测
进入正题,本篇对网格缺陷检测的思路很简单:
- 动态阈值处理
- 面积筛选显示缺陷
opencv实现:
Mat src = imread("D:/opencv练习图片/网格缺陷检测1.png"); imshow("原图", src); cvtColor(src, gray, COLOR_RGB2GRAY); GaussianBlur(gray, gray, Size(3, 3), 1, 0); //双阈值方法 threshold(gray, binary1, 25, 255, THRESH_BINARY); threshold(gray, binary2, 80, 255, THRESH_BINARY_INV); bitwise_and(binary1, binary2, binary); imshow("双阈值二值化", binary); vector<vector<Point>>contours; findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point()); for (int i = 0; i < contours.size(); i++) { float area = contourArea(contours[i]); if (area>350) { drawContours(src, contours, i, Scalar(0, 0, 255), 2, 8); int baseline = 0; Size textSize = getTextSize("Mesh Not OK", FONT_HERSHEY_SIMPLEX, 1.0, 2, &baseline); rectangle(src, Rect(10, 10, textSize.width, textSize.height + baseline), Scalar(212, 233, 252), -1, 8); putText(src, "Mesh Not OK", Point(10, 5 + textSize.height + baseline), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8); } else { int baseline = 0; Size textSize = getTextSize("Mesh OK", FONT_HERSHEY_SIMPLEX, 1.0, 2, &baseline); rectangle(src, Rect(10, 10, textSize.width, textSize.height + baseline), Scalar(212, 233, 252), -1, 8); putText(src, "Mesh OK", Point(10, 5 + textSize.height + baseline), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8); } } imshow("缺陷", src);
这里采用的是双阈值处理。我们可以对比三种阈值处理的情况:
(1)全局阈值OTSU方法:
可以看到有部分正常孔洞和网格相连,会导致正常孔洞也被标记为缺陷。
(2)自适应阈值:
可以看到效果还不错。
(3)双阈值:
对比自适应阈值,可以看到分割的还是比较明显一点的。
参考博文:python-opencv函数总结之(一)threshold、adaptiveThreshold、Otsu 二值化_sinat_21258931的博客-CSDN博客