【图像处理】——图像的二值化操作及阈值化操作(固定阈值法(全局阈值法——大津法OTSU和三角法TRIANGLE)和自适应阈值法(局部阈值法——均值和高斯法))
目錄
一、二值化的概念(實際上就是一個閾值化操作)
1、概念:
2、實現方法
3、常用方法
二、閾值類型
1、常見閾值類型(主要有五種類型)
(1)公式描述
(2)圖表描述
2、兩種特殊的閾值算法(OSTU和TRIANGLE)——主要是為了得到合理化的閾值
(1)大津法OSTU閾值類型——適用于雙峰直方圖
前背景方差的計算公式:
問題1:參數該怎么求解(主要依靠灰度直方圖進行求解)?
(2)三角法TRIANGLE閾值類型——適用于單峰直方圖
相關理論知識
步驟(根據以下可以進行編程)
(3)大津法和三角法的比較
三、固定閾值法(threshold)——全局閾值法
1、threshold()函數
2、threshold()參數
3、常用閾值類型下的二值化(含代碼和結果)
(1)代碼
(2)結果對比
4、OTSU法和TRIANGLE法(代碼+結果)
(1)代碼
(2)結果
四、自適應閾值法(adaptiveThreshold)——局部閾值法
1、adaptiveThreshold()函數及其參數講解
(1)adaptiveThreshold()函數
(2)adaptiveThreshold()參數
問題2:參數blocksize和C對二值化圖像的影響
(3)注意點——閾值類型是指定的、塊大小長寬只能為奇數
2、自適應閾值算法
(1)平均值法(ADAPTIVE_THRESH_MEAN_C)
(2)高斯法(ADAPTIVE_THRESH_GAUSSIAN_C)
(3)代碼
(4)結果
五、固定閾值和自適應閾值法比較
六、自定義閾值法
一、二值化的概念(實際上就是一個閾值化操作)
1、概念:
二值化就是將圖像的灰度值轉換為只有0和255,即非黑即白,黑色(0)作為背景(暗域),白色(255)作為前景即目標區域(亮域)。
2、實現方法
一般是通過設定一個閾值,大于閾值的像素全設置為0或者255,小于閾值的像素全設置為了一個灰度,這樣就完成了對圖像的二值化處理。
3、常用方法
常用的方法大致可以分為兩類:固定閾值法和自適應閾值法
固定閾值法:表示根據閾值類型設置好一個閾值,然后進行二值化
自適應閾值法:表示根據每一個像素點的鄰域模塊的像素進行均值或者加權進行設置閾值,然后進行二值化
二、閾值類型
1、常見閾值類型(主要有五種類型)
(1)公式描述
(2)圖表描述
2、兩種特殊的閾值算法(OSTU和TRIANGLE)——主要是為了得到合理化的閾值
?????? 這兩種閾值算法比較特殊,閾值的選擇上比較合理化,這兩種閾值都是基于圖像灰度直方圖來進行設置的。先獲得灰度直方圖,直方圖的繪制可以參考:《【圖像處理】——圖像灰度直方圖的繪制(直接調用函數和自定義函數)》
????? 這兩種算法的使用一般與上述五種算法進行搭配使用,閾值的取值按照這兩種算法進行取值,而二值化的規則則按照常用的來進行。
(1)大津法OSTU閾值類型——適用于雙峰直方圖
OTSU算法也稱最大類間差法,有時也稱之為大津算法,由大津于1979年提出,被認為是圖像分割中閾值選取的最佳算法,計算簡單,不受圖像亮度和對比度的影響,因此在數字圖像處理上得到了廣泛的應用。
它是按圖像的灰度特性,將圖像分成背景和前景兩部分。因方差是灰度分布均勻性的一種度量,背景和前景之間的類間方差越大,說明構成圖像的兩部分的差別越大,利于后續的圖像分割,當部分前景錯分為背景或部分背景錯分為前景都會導致兩部分差別變小。因此,使類間方差最大的分割意味著錯分概率最小。
前背景方差的計算公式:
參考:https://blog.csdn.net/guduruyu/article/details/68059450
?
問題1:參數該怎么求解(主要依靠灰度直方圖進行求解)?
答:上述方差公式也可以寫成:
參考:https://blog.csdn.net/u013066730/article/details/92787627
參數的求解(根據以下的公式就可以進行編程):
公式證明推導可參考:https://www.cnblogs.com/guopengfei/p/4759569.html
T:前景與背景的分割閾值,這是待確定參數,一般采用遍歷法進行求解,通常是從中間段灰度級別進行取值,向兩邊展開取值
w0:前景(大于閾值,亮域)點數占圖像比例,在假設的閾值下,所有大于閾值的像素點個數除以圖像像素點總數
u0:平均灰度,大于閾值的像素點的灰度值總和除以大于閾值的像素點數目
w1:背景(小于閾值,暗域)點數占圖像比例,在假設的閾值下,所有小于閾值的像素點個數除以圖像像素點總數,也可以這樣通過w1=1-w0計算
u1:平均灰度,小于閾值的像素點的灰度值總和除以小于閾值的像素點數目
u:圖像的總平均灰度,所有像素點的灰度值總和除以圖像像素點總數,也可以通過u = w0*u0 + w1*u1進行計算
g:前景和背景圖象的方差
w0+w1=1?
?
在opencv中已經幫我們封裝好了這個算法:cv.THRESH_OTSU
?
(2)三角法TRIANGLE閾值類型——適用于單峰直方圖
相關理論知識
參考:https://blog.csdn.net/jia20003/article/details/53954092
?????? 三角法求閾值最早見于Zack的論文《Automatic measurement of sister chromatid exchange frequency》主要是用于染色體的研究,該方法是使用直方圖數據,基于純幾何方法來尋找最佳閾值,它的成立條件是假設直方圖最大波峰在靠近最亮的一側,然后通過三角形求得最大直線距離,根據最大直線距離對應的直方圖灰度等級即為分割閾值
在直方圖上從最高峰處bmx到最暗對應直方圖bmin(p=0)%構造一條直線,從bmin處開始計算每個對應的直方圖b到直線的垂直距離,知道bmax為止,其中最大距離dmax對應的直方圖位置即為圖像二值化對應的閾值T=dmax。
有時候最大波峰對應位置不在直方圖最亮一側,而在暗的一側,這樣就需要翻轉直方圖,翻轉之后求得值,用255減去即得到為閾值T,即T=255-dmax。擴展情況的直方圖表示如下:
步驟(根據以下可以進行編程)
1. 圖像轉灰度
2. 計算圖像灰度直方圖
3. 尋找直方圖中兩側邊界(通過sort函數對直方圖數據進行排序即可求得)
4. 尋找直方圖最大值(通過max函數可以求得出現的最高頻率,再通過該值進行索引的獲取)
5. 檢測是否最大波峰在亮的一側,否則翻轉(翻轉可以使用flip函數)
6. 求解到直線的最大值(關鍵)
設灰度級別為L,頻率為α,當頻率αmax最大的時候設L=L_αmax,當Lmin時,α=α_Lmin
(1)求解直線方程:根據點(Lmin,α_Lmin)和點(L_αmax,αmax)可以確定直線l的方程,斜率以及截距可得
(2)求解各點到直線的距離:各點(L,α)到直線l的距離,根據點到直線的距離公式可以求得,用一個列表去存放所有的距離d,然后利用max函數即可求得dmax
7. 計算閾值得到閾值T=dmax,如果翻轉則T=255-dmax
在opencv中已經幫我們封裝好了這個算法:cv.THRESH_TRIANGLE
(3)大津法和三角法的比較
相同點:不用自己指定thresh值,系統會進行計算并且作為返回值返回。
不同點:
THRESH_OTSU最適用于雙波峰。
THRESH_TRIANGLE最適用于單個波峰,最開始用于醫學分割細胞等。
三、固定閾值法(threshold)——全局閾值法
固定閾值法就是給定一個閾值進行二值化,比較特殊的就是utsu和triangle法,通過一定的計算得到的閾值
1、threshold()函數
thresold(src,thresh,maxvalue,type) #返回ret(閾值)和dst(二值化化的圖像矩陣)對于大津法和三角法,第二個參數thresh就沒有意義了,因為會自動計算閾值并且優先被設置為閾值
cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)#大律法,全局自適應閾值,第二個參數值0可改為任意數字但不起作用。 cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) #TRIANGLE法,全局自適應閾值,第二個參數值0可改為任意數字但不起作用,適用于單個波峰。?以上是和固定閾值類型進行結合使用的,也可以單獨使用大津法和三角法
2、threshold()參數
src參數:表示輸入圖像(多通道,8位或32位浮點)。thresh參數:表示閾值。maxval參數:表示與THRESH_BINARY和THRESH_BINARY_INV閾值類型一起使用設置的最大值。type參數:表示閾值類型。ret參數:表示返回的閾值。若是全局固定閾值算法,則返回thresh參數值。若是全局自適應閾值算法,則返回自適應計算得出的合適閾值。dst參數:表示輸出與src相同大小和類型以及相同通道數的圖像。3、常用閾值類型下的二值化(含代碼和結果)
(1)代碼
import cv2 from image_gray.image_gray_methods import gray_mean_rgb#固定閾值#cv2.THRESH_BINARY def threshold_binary(thresh,imagepath):#需要給定的參數為閾值和圖片地址gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化#直接閾值化是對輸入的單通道矩陣逐像素進行閾值分割。ret, binary = cv2.threshold(gray, thresh, 255, cv2.THRESH_BINARY )#返回閾值以及二值化后的圖像矩陣result = {"threshold":ret,"binary":binary}return resultdef threshold_binary_inv(thresh,imagepath):#需要給定的參數為閾值和圖片地址gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化#直接閾值化是對輸入的單通道矩陣逐像素進行閾值分割。ret, binary = cv2.threshold(gray, thresh, 255, cv2.THRESH_BINARY_INV )#返回閾值以及二值化后的圖像矩陣return {"threshold":ret,"binary":binary}def threshold_trunc(thresh,imagepath):#需要給定的參數為閾值和圖片地址gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化#直接閾值化是對輸入的單通道矩陣逐像素進行閾值分割。ret, binary = cv2.threshold(gray, thresh, 255, cv2.THRESH_TRUNC )#返回閾值以及二值化后的圖像矩陣return {"threshold":ret,"binary":binary}def threshold_tozero(thresh,imagepath):#需要給定的參數為閾值和圖片地址gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化#直接閾值化是對輸入的單通道矩陣逐像素進行閾值分割。ret, binary = cv2.threshold(gray, thresh, 255, cv2.THRESH_TOZERO )#返回閾值以及二值化后的圖像矩陣return {"threshold":ret,"binary":binary}def threshold_tozero_inv(thresh,imagepath):#需要給定的參數為閾值和圖片地址gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化#直接閾值化是對輸入的單通道矩陣逐像素進行閾值分割。ret, binary = cv2.threshold(gray, thresh, 255, cv2.THRESH_TOZERO_INV )#返回閾值以及二值化后的圖像矩陣return {"threshold":ret,"binary":binary}if __name__ == '__main__':imagepath = 'colorful_lena.jpg'#給定圖片thresh = 120#給定閾值result = threshold_tozero_inv(120,imagepath)#接受得到的字典,含閾值和二值化圖像矩陣信息thresh_value = result["threshold"]binary = result["binary"]#取值cv2.namedWindow("binary0", cv2.WINDOW_NORMAL)#cv2.WINDOW_NORMAL,使得窗口可以進行縮放cv2.imshow("binary0", binary)cv2.waitKey(0)cv2.destroyAllWindows()上述代碼可以合并為:
import cv2 from image_gray.image_gray_methods import gray_mean_rgb#全局固定閾值#需要給定的參數為閾值、閾值類型標簽號和圖片地址 #thresh:閾值 #flags:閾值類型標簽號,0——cv2.THRESH_BINARY,1——cv2.THRESH_BINARY_INV,2——cv2.THRESH_TRUNC, # 3——cv2.THRESH_TOZERO,4——cv2.THRESH_TOZERO_INV def threshold_methods(thresh,flags,imagepath):thresh_type = [cv2.THRESH_BINARY,cv2.THRESH_BINARY_INV,cv2.THRESH_TRUNC,cv2.THRESH_TOZERO,cv2.THRESH_TOZERO_INV]gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化#直接閾值化是對輸入的單通道矩陣逐像素進行閾值分割。ret, binary = cv2.threshold(gray, thresh, 255, thresh_type[flags] )#返回閾值以及二值化后的圖像矩陣result = {"threshold":ret,"binary":binary}return resultif __name__ == '__main__':thresh = 100imagepath = "colorful_lena.jpg"for i in range(5):flags = ibinary = threshold_methods(thresh,flags,imagepath)["binary"]cv2.namedWindow('thresh_type_0{}'.format(flags))cv2.imshow('thresh_type_0{}'.format(flags),binary)cv2.imwrite('thresh_type_0{}.jpg'.format(flags),binary)cv2.waitKey(0)cv2.destroyAllWindows()(2)結果對比
以下圖像均是在閾值為100的前提下進行的
?不同的圖片可以根據圖片自身的特點進行選取
4、OTSU法和TRIANGLE法(代碼+結果)
(1)代碼
import cv2 from image_gray.image_gray_methods import gray_mean_rgb#全局固定閾值#需要給定的參數為閾值算法標簽號和圖片地址 #flags:閾值類型標簽號,0——cv2.THRESH_OTSU,1——cv2.THRESH_TRIANGLE def threshold_methods(flags,imagepath):thresh_type = [cv2.THRESH_OTSU,cv2.THRESH_TRIANGLE]gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化#直接閾值化是對輸入的單通道矩陣逐像素進行閾值分割。ret, binary = cv2.threshold(gray, 0, 255, thresh_type[flags] )#返回自適應計算的閾值以及二值化后的圖像矩陣result = {"threshold":ret,"binary":binary}return resultif __name__ == '__main__':imagepath = "colorful_lena.jpg"for i in range(2):flags = ibinary = threshold_methods(flags,imagepath)["binary"]cv2.namedWindow('ostu_triangle_0{}'.format(flags))cv2.imshow('ostu_triangle_0{}'.format(flags),binary)cv2.imwrite('ostu_triangle_0{}.jpg'.format(flags),binary)cv2.waitKey(0)cv2.destroyAllWindows()(2)結果
可見三角法二值化的結果好一些,細節都保留了
四、自適應閾值法(adaptiveThreshold)——局部閾值法
自適應閾值法也叫做局部閾值法,對每一個像素點的閾值都是不一樣的,計算量比較大,基本的思路就是以目標像素點為中心選擇一個塊,然后對塊區域里面的像素點進行高斯或者均值計算,將得到的平均值或者高斯值作為目標像素點的閾值,以此來對目標像素格進行二值化。對圖像每一個像素格進行如此操作就完成了對整個圖像的二值化處理。
1、adaptiveThreshold()函數及其參數講解
(1)adaptiveThreshold()函數
adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C) #返回二值化后的圖像矩陣-> dst(2)adaptiveThreshold()參數
src參數:表示輸入圖像(8位單通道圖像)。maxValue參數:表示使用 THRESH_BINARY 和 THRESH_BINARY_INV 的最大值.adaptiveMethod參數:表示自適應閾值算法,平均 (ADAPTIVE_THRESH_MEAN_C)或高斯(ADAPTIVE_THRESH_GAUSSIAN_C)。thresholdType參數:表示閾值類型,必須為THRESH_BINARY或THRESH_BINARY_INV的閾值類型。blockSize參數:表示塊大小(奇數且大于1,比如3,5,7........ )。C參數:常數,表示從平均值或加權平均值中減去的數。 通常情況下,這是正值,但也可能為零或負值。問題2:參數blocksize和C對二值化圖像的影響
答:這里參數注意有blocksize和C。
一般情況下blocksize過大會導致圖像細節的丟失,過小雖然保存了圖像細節,但是也使得運行的時間大幅增加,因此需要進行權衡。
一般情況參數C是大于0的,C越大說明最后的閾值就會越小,這樣導致的結果就是圖像的大部分像素會被轉換為亮域,即更多的像素點的灰度值大于閾值,被轉化為255亮域。C越小時則恰恰相反
?
(3)注意點——閾值類型是指定的、塊大小長寬只能為奇數
閾值類型固定為:THRESH_BINARY 和 THRESH_BINARY_INV
塊大小一定為奇數:必須保證目標像素點在塊的中心
2、自適應閾值算法
2021年01月30日補充:
自適應閾值可以忽略光照的影響而得到目標物,但是這個只是適用于目標較為均勻地分布在圖像上才可,比如在整個圖像上只有一個小目標,利用這種方法會導致在其他沒目標的地方也會產生閾值劃分(即二值),這樣就會導致二值化后會有更多的干擾因素,當然如果效果好的話,可以通過形態學進行處理得到,但是大部分情況是糟糕的。
如檢測缺陷:左邊圖像是自適應閾值,右邊是全局二值化,效果顯然右邊好。
準確來說,自適應閾值法主要是用來尋找輪廓
############################################################################################################################################
在使用平均和高斯兩種算法情況下,通過計算每個像素周圍blockSize?x blockSize大小像素塊的加權均值并減去常量C即可得到自適應閾值
(1)平均值法(ADAPTIVE_THRESH_MEAN_C)
平均值法就是對目標像素點的周圍取一定size(為奇數)的塊區域,將該區域的像素點灰度值的平均值再減去參數C的值得到的值作為閾值
(2)高斯法(ADAPTIVE_THRESH_GAUSSIAN_C)
使用高斯的方法,則每個像素周圍像素的權值則根據其到中心點的距離通過高斯方程得到,然后閾值就會等于各像素點權值乘以灰度值的積的累加再減去C,權值的和為1
(3)代碼
import cv2 from image_gray.image_gray_methods import gray_mean_rgb#全局固定閾值#需要給定的參數為閾值算法標簽號、自適應閾值算法標簽號、塊的尺寸、參數C和圖片地址 #flags:閾值類型標簽號,0——cv2.THRESH_BINARY,1——cv2.THRESH_BINARY_INV #tags:自適應閾值算法標簽號,0——cv2.ADAPTIVE_THRESH_MEAN_C,1——cv2.ADAPTIVE_THRESH_GAUSSIAN_C #blocksize:塊的尺寸,為奇數 def threshold_local_methods(tags,flags,blocksize,C,imagepath):thresh_type = [cv2.THRESH_BINARY,cv2.THRESH_BINARY_INV]thresh_methods = [cv2.ADAPTIVE_THRESH_MEAN_C,cv2.ADAPTIVE_THRESH_GAUSSIAN_C]gray = gray_mean_rgb(imagepath) #把輸入圖像灰度化binary = cv2.adaptiveThreshold(gray, 255, thresh_methods[tags],thresh_type[flags],blocksize,C )#返回二值化后的圖像矩陣return binaryif __name__ == '__main__':imagepath = "colorful_lena.jpg" #以下是進行循環使用閾值類型和自適應閾值算法搭配,共四種for i in range(2):for j in range(2):tags = iflags = jbinary = threshold_local_methods(tags,flags,blocksize=5,C=1,imagepath=imagepath)cv2.namedWindow('mean_gauss{}{}'.format(tags,flags))cv2.imshow('mean_gauss{}{}'.format(tags,flags),binary)cv2.imwrite('mean_gauss{}{}.jpg'.format(tags,flags),binary)cv2.waitKey(0)cv2.destroyAllWindows()(4)結果
參數為:C=1,blocksize=5
五、固定閾值和自適應閾值法比較
固定閾值的二值化效果一般比較差,尤其是在處理亮度差別很大的圖像,它是對整個圖像進行閾值操作,圖像比較平滑,細節較少
自適應閾值法則是圍繞目標像素點的一小塊區域進行閾值化操作,效果會更好,圖像的細紋都保留了下來,即圖像細節得到了保存。
?
?
因此在以后二值化時選用自適應閾值法是優選
六、自定義閾值法
直接上代碼不再贅述
這里是將全局平均值作為閾值進行二值化
import cv2 import numpy as np #用戶自己計算閾值 def custom_threshold(imagepath):img = cv2.imread(imagepath)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #把輸入圖像灰度化h, w =gray.shape[:2]#獲得灰度化后圖像矩陣的高以及寬m = np.reshape(gray, [1,w*h])#將灰度化的矩陣reshape成一維的便于計算累和mean = m.sum()/(w*h)#求整個矩陣的元素的總和的平均值ret, binary = cv2.threshold(gray, mean, 255, cv2.THRESH_BINARY)result = {"threshold":ret,"binary":binary}return resultif __name__ == '__main__':imagepath = "colorful_lena.jpg"binary = custom_threshold(imagepath)["binary"]cv2.namedWindow("binary2", cv2.WINDOW_NORMAL)cv2.imshow("binary2", binary)cv2.waitKey(0)cv2.destroyAllWindows()?
?
總結
以上是生活随笔為你收集整理的【图像处理】——图像的二值化操作及阈值化操作(固定阈值法(全局阈值法——大津法OTSU和三角法TRIANGLE)和自适应阈值法(局部阈值法——均值和高斯法))的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【机器学习】逻辑回归(LogisticR
- 下一篇: React Native 开发环境搭建