摘要
本篇來用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);
網格缺陷檢測
進入正題,本篇對網格缺陷檢測的思路很簡單:
- 動態閾值處理
- 面積篩選顯示缺陷
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部落格