1、圖像閾值與二值化
閾值是一種簡單的圖像分割方法,一幅圖像包括目標物體(前景)、背景還有噪聲,要想從數字圖像中直接提取出目標物體,可以設定一個像素值即閾值,然后用圖像的每一個像素點和閾值做比較,給出判定結果。
二值化是特殊的閾值分割方法,把圖像分為兩部分,以閾值T為分割線,大於T的像素群和小於T的像素群,這樣圖像就變為黑白二色圖像。通過設定一個標准如果大於這個標准就設為白,如果小於這個標准就設為黑,而這個標准就是閾值。
2、OpenCV閾值threshold
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type);
(1)第一個參數,InputArray 類型的 src,源圖像。單通道,8 或 32位浮點數類型的深度。
(2)第二個參數,OutputArray 類型的 dst,輸出圖像。
(3)第三個參數,double 類型的 thresh,選取的閾值。
(4)第四個參數,double 類型的 maxval。
(5)第五個參數,int 類型的 type。閾值類型。如下所示:
type類型如下:
enum cv::ThresholdTypes { cv::THRESH_BINARY = 0, cv::THRESH_BINARY_INV = 1, cv::THRESH_TRUNC = 2, cv::THRESH_TOZERO = 3, cv::THRESH_TOZERO_INV = 4, cv::THRESH_MASK = 7, cv::THRESH_OTSU = 8, cv::THRESH_TRIANGLE = 16 }
不同的閾值方法生成關系如下圖。
Mat img = Mat::zeros(6, 6, CV_8UC1); randu(img, 0, 255); int th = 100; Mat threshold1, threshold2, threshold3, threshold4, threshold5, threshold6, threshold7, threshold8; threshold(img, threshold1, th, 200, THRESH_BINARY); threshold(img, threshold2, th, 200, THRESH_BINARY_INV); threshold(img, threshold3, th, 200, THRESH_TRUNC); cout << "raw=\r\n"<<img << "\r\n" << endl; cout << "THRESH_BINARY=\r\n" << threshold1 << "\r\n" << endl; cout << "THRESH_BINARY_INV=\r\n" << threshold2 << "\r\n" << endl; cout << "THRESH_TRUNC=\r\n" << threshold3 << "\r\n" << endl;
上面代碼中randu(img, 0, 255)作用是產出隨機數填充img矩陣。輸出結果如下。
raw= [ 91, 2, 79, 179, 52, 205; 236, 8, 181, 239, 26, 248; 207, 218, 45, 183, 158, 101; 102, 18, 118, 68, 210, 139; 198, 207, 211, 181, 162, 197; 191, 196, 40, 7, 243, 230] THRESH_BINARY= [ 0, 0, 0, 200, 0, 200; 200, 0, 200, 200, 0, 200; 200, 200, 0, 200, 200, 200; 200, 0, 200, 0, 200, 200; 200, 200, 200, 200, 200, 200; 200, 200, 0, 0, 200, 200] THRESH_BINARY_INV= [200, 200, 200, 0, 200, 0; 0, 200, 0, 0, 200, 0; 0, 0, 200, 0, 0, 0; 0, 200, 0, 200, 0, 0; 0, 0, 0, 0, 0, 0; 0, 0, 200, 200, 0, 0] THRESH_TRUNC= [ 91, 2, 79, 100, 52, 100; 100, 8, 100, 100, 26, 100; 100, 100, 45, 100, 100, 100; 100, 18, 100, 68, 100, 100; 100, 100, 100, 100, 100, 100; 100, 100, 40, 7, 100, 100]
THRESH_BINARY,thresh=100,maxval=200,大於閾值限定為200,小於閾值清零。
THRESH_BINARY_INV的作用和THRESH_BINARY 相反,小於閾值置200,大於閾值清。
THRESH_TRUNC的作用是對大於閾值的數據進行截斷,其余值保留原值不變。
圖像閾值例子如下。
Mat img = imread("D:/WORK/5.OpenCV/LeanOpenCV/pic_src/pic6.bmp", IMREAD_GRAYSCALE); int th = 100; Mat threshold1, threshold2, threshold3, threshold4, threshold5, threshold6, threshold7, threshold8; threshold(img, threshold1, th, 200, THRESH_BINARY); threshold(img, threshold2, th, 200, THRESH_BINARY_INV); threshold(img, threshold3, th, 200, THRESH_TRUNC); imshow("raw pic",img); imshow("THRESH_BINARY", threshold1); imshow("THRESH_BINARY_INV", threshold2); imshow("THRESH_TRUNC", threshold3);
3、自動閾值—大津法OTSU
最大類間方差是由日本學者大津(Nobuyuki Otsu)於1979年提出,是一種自適應的閾值確定方法。算法假設圖像像素能夠根據閾值,被分成背景[background]和目標[objects]兩部分。然后,計算該最佳閾值來區分這兩類像素,使得兩類像素區分度最大。
算法原理為:
設圖像Img長寬尺寸為M*N, T為二值化的閾值;
N0為灰度小於T的像素的個數,N0的平均灰度為μ0。
N1 為灰度大於T的像素的個數,N1的平均灰度為μ1。
ω0=N0/ M×N (1) //落在N0的概率
ω1=N1/ M×N (2) //落在N1的概率
N0+N1=M×N (3)
ω0+ω1=1 (4)
μ=ω0*μ0+ω1*μ1 (5) //平均灰度乘以概率 再相加
g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6) //類間方差
將式(5)代入式(6),得到等價公式: g=ω0ω1(μ0-μ1)^2 (7)
OpenCV自帶了OSTU算法。
Mat img = imread("D:/WORK/5.OpenCV/LeanOpenCV/pic_src/pic2.bmp", IMREAD_GRAYSCALE); int th = 100; Mat threshold1, threshold2, threshold3; threshold(img, threshold1, th, 255, THRESH_BINARY); threshold(img, threshold2, th, 255, THRESH_TRUNC); threshold(img, threshold3, th, 255, THRESH_OTSU); // 閾值隨意設置即可 imshow("raw pic",img); imshow("THRESH_BINARY", threshold1); imshow("THRESH_TRUNC", threshold2); imshow("THRESH_OTSU", threshold3);
使用大津法時閾值可以不設置或隨意設置,函數會自動計算最合適的閾值,輸出圖像如下。
大津法相比其他二值化方法,能很好的篩選出前景圖和背景圖,讓圖像分類后黑白區分度最大。
4、參考文獻
1、《學習OpenCV》,清華大學出版社,Gary Bradski, Adrian kaehler著
2、Miscellaneous Image Transformations
https://docs.opencv.org/3.1.0/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57
3、OpenCV threshold函數詳解
https://blog.csdn.net/weixin_42296411/article/details/80901080
4、詳細及易讀懂的 大津法(OTSU)原理 和 算法實現
https://blog.csdn.net/u012198575/article/details/81128799
尊重原創技術文章,轉載請注明。