opencv4學習筆記(1)-門檻值分割3種方法
文章結構:
1.三種分割方法:直接分割、自适應分割(平均值、高斯均值)
2.函數使用
3.程式例程 (C++)
4.效果展示
5.參數設定心得
三種分割方法
1.直接分割
直接分割即最簡單的分割方法,将圖檔轉換為灰階圖,設定一個灰階值界限,在界限内的像素點,我們就讓他變為白色,否則就變成黑色。
直接分割簡單粗暴,但是缺點也很明顯。如果一個圖檔某些地方暗,某些地方亮,這樣的話分割統一用一個标準的話的,分割效果就不好了。
這裡借用部落客隕落星雲的一張圖,讓大家看看直接分割的缺陷:

可以看到由于圖檔明暗不一緻,導緻分割的不理想。
2.自适應分割(平均值)
自适應分割就和前面的直接分割不同了。自适應分割會對圖像的每一個小區域單獨處理,這樣的話就達到了因地制宜的效果了。
相比于前面的直接分割需要直接設定門檻值(即灰階值界限),我們的自适應分割就不需要了。
自适應分割是如何判斷圖檔中的某一個像素點應該是黑色還是白色呢?這裡,自适應分割是通過将像素點和周圍特定範圍内像素點的灰階值的平均值做比較得出來的。
這裡大家可能會有一點聽不懂,那我們換一種說法。如果一個像素點,他比他周圍的像素點灰階的平均值大,那麼我們就把他變成白色。相反,我們就把他變成黑色。就像如果你的分數(灰階值)大于你們班級(指定的小範圍)的平均值,那麼你就是優等生(白色),否則你就是差等生(黑色)。
我麼用部落客Kyda的一張圖做例子:
如果我們要判斷中心點6的顔色應該是黑色還是白色,我麼以他為中心,取3x3的矩陣範圍(即深藍色範圍),計算矩陣的均值,得出來是5.4。然後我們看他原來值時6。比我們算出來的平均值大,是以他就會變成白色的像素點。類似其他點我們也可以同理算出來。
這裡我們通過平均值算出來該點為白色,我們有沒有别的辦法讓該點變成黑色呢。這裡我們還可以設定一個偏移量C,這樣的話,我們在将像素點的灰階值和平均值(mean)做比較的時候,平均值就會實作減去一個偏移量C,即mean = mean - C。這裡偏移量可以為正也可以為負。
這裡值得注意的是,以像素點為中心,他左邊的距離和右邊的距離必須相等,是以可以計算出矩形的長和寬必須是奇數,之後opencv提供的函數裡面就有這樣一個參數,如果設定為偶數,就會報錯。
還有一個問題是,如果大家仔細分析可以得出,如果矩形為3x3,那個圖像邊界上的那一圈像素點沒有辦法計算他的均值,這裡我們先放一放,不做深究,opencv的函數已經提前做好了邊界像素點的處理。
這裡總結一下,如果是自适應算法,需要我們使用者自己設定兩個參數,一個是我們範圍矩陣的大小,還有一個就是我們的偏移量C。
3.自适應分割(高斯均值)
高斯均值和我們前面的平均值大體原理來說是一樣的。不一樣的是,前面的平均值是計算範圍矩陣内所有像素點灰階的平均值。而我們的高斯均值并不是計算的平均值。而是引入了高斯分布函數,如果範圍矩陣内的像素點,離中心像素點越遠,那麼他的權重值就會越小。簡而言之,就是離中心像素點越的像素點,對我們的均值計算影響越小。
我們自适應分割的優勢很明顯,能夠針對圖形不同的區域進行處理,降低了圖檔的明暗不均對分割産生的影響。
函數使用
◆ threshold()//直接分割
double cv::threshold ( InputArray src,//輸入圖檔
OutputArray dst, //輸入圖像
double thresh, //門檻值
double maxval, //最大值,一般255
int type //門檻值類型,隻有兩個取值,分别為 THRESH_BINARY 和THRESH_BINARY_INV,不同的取值會導緻圖形黑白的反轉
)
Python:
retval, dst = cv.threshold( src, thresh, maxval, type[, dst] )
#include <opencv2/imgproc.hpp>
◆ adaptiveThreshold()//自适應分割
void cv::adaptiveThreshold ( InputArray src,//輸入圖檔
OutputArray dst, //輸出圖檔
double maxValue, //灰階值最大值,一般取255
int adaptiveMethod, //自适應的方法,ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C
int thresholdType, //門檻值類型,隻有兩個取值,分别為 THRESH_BINARY 和THRESH_BINARY_INV,不同的取值會導緻圖形黑白的反轉
int blockSize, //範圍矩陣的大小,注意取奇數
double C //偏移量
)
Python:
dst = cv.adaptiveThreshold( src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst] )
#include <opencv2/imgproc.hpp>//記得引入頭檔案
程式例程(C++)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/imgproc.hpp>
void Demo::threshold_demo()
{
namedWindow("src", WINDOW_NORMAL);//WINDOW_NORMAL 使用者可以改變src視窗大小
namedWindow("src_gray", WINDOW_NORMAL);
namedWindow("src_gray_median", WINDOW_NORMAL);
namedWindow("gray1", WINDOW_NORMAL);
namedWindow("gray2", WINDOW_NORMAL);
namedWindow("gray3", WINDOW_NORMAL);
Mat src = imread("D:/DESKTOP/picture/shadow.jpg");//讀入圖檔到src
imshow("src", src);//顯示圖檔到src視窗
Mat src_gray;//建立一個圖形,用來接下來原圖轉換為的灰階圖
cvtColor(src,src_gray, COLOR_BGR2GRAY);//opencv預設圖檔為BGR,進行門檻值分割前需轉為灰階圖
imshow("src_gray", src_gray);
Mat src_gray_median;
medianBlur(src_gray, src_gray_median, 5);//進行中值濾波,文字沒有說明,作用是除噪
imshow("src_gray_median", src_gray_median);
Mat gray1,gray2, gray3;
threshold(src_gray_median, gray1, 50, 255, THRESH_BINARY);//直接分割
imshow("gray1", gray1);
adaptiveThreshold(src_gray_median, gray2, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 21, 5);//自适應均值分割
imshow("gray2", gray2);
adaptiveThreshold(src_gray_median, gray3, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 21, 5);//自适應高斯分割
imshow("gray3", gray3);
waitKey(0);
}
效果展示
原圖:
中值濾波後灰階圖:
直接分割後(可以看到上面亮部位的分割不清晰):
自适應均值分割:(就比較均勻了)
自适應高斯分割:
參數設定心得
對于直接分割,就一直修改門檻值到效果最佳就可以了。
對于自适應濾波,如果範圍矩陣設定過小,會産生字裡面出現空洞,甚至字不清楚的效果,如圖:
如果出現如圖黑色過多的效果,可能是因為偏移量不夠,可以試着增大偏移量: