OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)
OpenCV與圖像處理學(xué)習(xí)七——傳統(tǒng)圖像分割之閾值法(固定閾值、自適應(yīng)閾值、大津閾值)
- 一、固定閾值圖像分割
 - 1.1 直方圖雙峰法
 - 1.2 OpenCV中的固定閾值分割
 
- 二、自動閾值圖像分割
 - 2.1 自適應(yīng)閾值法
 - 2.2 迭代法閾值分割
 - 2.3 Otsu大津閾值法
 
前面的筆記介紹了一些OpenCV基本的圖像處理,后面將學(xué)習(xí)使用OpenCV的傳統(tǒng)的圖像分割方法,這次筆記的內(nèi)容是閾值法進(jìn)行圖像分割。
圖像分割是指將圖像分成若干具有相似性質(zhì)的區(qū)域的過程,主要有基于閾值、基于區(qū)域、基于邊緣、基于聚類、基于圖論和基于深度學(xué)習(xí)的圖像分割方法等。圖像分割分為語義分割和實例分割。下圖是一個實例分割的例子,與語義分割不同的地方在于,它能將同類別的物體的每個個體都區(qū)分開,如下圖中每個人的輪廓都被分割開:
 分割的原則就是使劃分后的子圖在內(nèi)部保持相似度最大,而子圖之間的相似度最小,將G = (V, E) 分成兩個子集A,B,使得:
 
 
一、固定閾值圖像分割
即設(shè)定一個固定的閾值,整張圖片的每個像素的像素值都與該值進(jìn)行比較,若小于該閾值則將像素值改為一個固定的值(常用0),若大于該閾值則將像素值改為另一個固定的值(常用255),則可以將圖像進(jìn)行二值分割,得到一張二值圖。
1.1 直方圖雙峰法
六十年代中期提出的直方圖雙峰法(也稱mode法)是典型的全局單閾值分割方法。
 
 基本思想:假設(shè)圖像中有明顯的目標(biāo)和背景,則其灰度直方圖呈雙峰分布,當(dāng)灰度級直方圖具有雙峰特性時選取兩峰之間的谷對應(yīng)的灰度級作為閾值,大于閾值的作為前景,小于的作為背景。
缺點:對圖像的要求太高,很多圖像的直方圖并不滿足雙峰的分布。
1.2 OpenCV中的固定閾值分割
在OpenCV中的函數(shù):
retval, dst = cv2.threshold( src, thresh, maxval, type[, dst] )參數(shù):
這里也需要注意一下這個函數(shù)的輸出:
下面看幾個例子:
# 加載opencv和matplotlib import cv2 import matplotlib.pyplot as plt# 灰度圖讀入 img = cv2.imread('./image/thresh.png', 0) threshold = 127 # 閾值分割 ret, th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY) print(ret)cv2.imshow('Original', img) cv2.imshow('thresh', th) cv2.waitKey(0) cv2.destroyAllWindows()結(jié)果如下:
 
 返回的第一個參數(shù)就是設(shè)置的閾值:
即像素值低于127的都被賦予0,高于的都被賦予255,得到一張二值化的圖像。
再來看一下五種基本的閾值分割方法的區(qū)別:
# 導(dǎo)入第三方包 import cv2 from matplotlib import pyplot as plt # opencv讀取圖像 img = cv2.imread('./image/person.png',0) # 5種閾值法圖像分割 ret1, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) ret2, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) ret3, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC) ret4, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO) ret5, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] # 使用for循環(huán)進(jìn)行遍歷,matplotlib進(jìn)行顯示 for i in range(6):plt.subplot(2, 3, i+1)plt.imshow(images[i], cmap='gray')plt.xticks([])plt.yticks([])plt.suptitle('fixed threshold') plt.show()結(jié)果為:
 
 注意:除了前兩種方法是二值化圖像,后三種并不是,因為它們會保留一部分的原像素值。
如果這個圖不夠直觀,將輸入圖像換成上一個例子里的漸變灰度圖,結(jié)果如下:
 
二、自動閾值圖像分割
2.1 自適應(yīng)閾值法
很明顯,設(shè)置一個固定閾值對全局像素進(jìn)行分割是不合理的,如果這張圖片的光照角度不好,一邊比較亮一邊比較暗,但是我們想分割圖里的細(xì)節(jié),只用一個固定閾值的話很可能會出現(xiàn)下面的結(jié)果:
 
 而自適應(yīng)閾值分割則將圖像分成很多個小塊(region),對每個小塊單獨計算其閾值,然后用這個計算得到的閾值對該小塊進(jìn)行分割,這樣的好處是,即使受到光照影響,某一塊較暗或較亮,但是可以單獨計算這一塊的合理閾值來進(jìn)行分割而不用使用全局的固定閾值,換句話說,亮的小塊對應(yīng)的閾值較大,暗的小塊對應(yīng)的閾值較小,從而可以達(dá)到很好的分割效果。
函數(shù):
dst = cv2.adaptiveThreshold( src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst] )參數(shù):
看一下剛剛用固定閾值分割效果很差的那個圖用自適應(yīng)閾值來分割的效果:
# 自適應(yīng)閾值與固定閾值對比 import cv2 import matplotlib.pyplot as plt img = cv2.imread('./image/paper2.png', 0)# 固定閾值 ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 自適應(yīng)閾值 th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 4) th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 4) # 全局閾值,均值自適應(yīng),高斯加權(quán)自適應(yīng)對比 titles = ['Original', 'Global(v = 127)', 'Adaptive Mean', 'Adaptive Gaussian'] images = [img, th1, th2, th3] for i in range(4):plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')plt.title(titles[i], fontsize=8)plt.xticks([]), plt.yticks([]) plt.show()
 可以看出效果還是很好的,相比之下,高斯方法的自適應(yīng)閾值能獲得更好的效果,其噪點更少。
2.2 迭代法閾值分割
步驟:
其實迭代法就是將固定閾值分割里手動給定閾值改為了迭代計算閾值,可以適用的范圍更多一些,但是本質(zhì)還是固定閾值變換。
看個例子:
import cv2 import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cmdef best_thresh(img):# step 1: 設(shè)置初始閾值img_array = np.array(img).astype(np.float32) # 轉(zhuǎn)化成數(shù)組I = img_arrayzmax = np.max(I)zmin = np.min(I)tk = (zmax+zmin)/2# step 2: 根據(jù)閾值將圖像進(jìn)行分割為前景和背景,分別求出兩者的平均灰度zo和zbb = 1m, n = I.shape;while b == 0:ifg = 0ibg = 0fnum = 0bnum = 0for i in range(1, m):for j in range(1, n):tmp = I(i, j)if tmp >= tk:ifg = ifg + 1fnum = fnum + int(tmp) # 前景像素的個數(shù)以及像素值的總和else:ibg = ibg+1bnum = bnum + int(tmp) # 背景像素的個數(shù)以及像素值的總和# step 3: 計算前景和背景的新平均值zo = int(fnum / ifg)zb = int(bnum / ibg)# step 4: 比較tk是否等于新平均值if tk == int((zo+zb) / 2):b = 0else:tk = int((zo+zb)/2)# step 5: 返回的就是迭代計算后的閾值return tkimg = cv2.imread("./image/bird.png") img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) img = cv2.resize(gray, (200, 200)) # 大小 yvzhi = best_thresh(img) ret1, th1 = cv2.threshold(img, yvzhi, 255, cv2.THRESH_BINARY) print(ret1) plt.imshow(th1, cmap=cm.gray) plt.show()結(jié)果為:
 
2.3 Otsu大津閾值法
大津法:也叫最大類間方差法,1979年日本學(xué)者大津提出,是一種基于全局閾值的自適應(yīng)方法。
圖像分為前景和背景,當(dāng)取最佳閾值時,兩部分之間的差別應(yīng)該是最大的,衡量差別的方法為最大類間方差。
直方圖有兩個峰值的圖像,用大津法求得的閾值近似為谷底,如下圖所示,即代替了手動輸入閾值。
 
 大津法中類間方差是需要最大化的目標(biāo)函數(shù),那么它的定義如下圖所示:
 
 在OpenCV中大津閾值法只是在固定閾值法的函數(shù)cv2.threshold的閾值方法type這個參數(shù)后加上cv2.THRESH_OTSU,同時將參數(shù)2thresh忽視(設(shè)置多少無所謂,一般使用0),例如:
看一個例子:
import cv2 from matplotlib import pyplot as pltimg = cv2.imread('./image/noisy.png', 0) # 固定閾值法 ret1, th1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY) # Otsu閾值法 ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 先進(jìn)行高斯濾波,再使用Otsu閾值法 blur = cv2.GaussianBlur(img, (5, 5), 0) ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) images = [img, 0, th1, img, 0, th2, blur, 0, th3] titles = ['Original', 'Histogram', 'Global(v=100)','Original', 'Histogram', "Otsu's",'Gaussian filtered Image', 'Histogram', "Otsu's"]for i in range(3):# 繪制原圖plt.subplot(3, 3, i * 3 + 1)plt.imshow(images[i * 3], 'gray')plt.title(titles[i * 3], fontsize=8)plt.xticks([]), plt.yticks([])# 繪制直方圖plt.hist, ravel函數(shù)將數(shù)組降成一維plt.subplot(3, 3, i * 3 + 2)plt.hist(images[i * 3].ravel(), 256)plt.title(titles[i * 3 + 1], fontsize=8)plt.xticks([]), plt.yticks([])# 繪制閾值圖plt.subplot(3, 3, i * 3 + 3)plt.imshow(images[i * 3 + 2], 'gray')plt.title(titles[i * 3 + 2], fontsize=8)plt.xticks([]), plt.yticks([]) plt.show()這里比較了固定閾值法、大津閾值法和加上高斯濾波的大津閾值法,效果如下所示:
 
 相比之下,高斯濾波加大津閾值法的效果是最好的,也是實際使用中最常用的。
以上就是基于閾值的傳統(tǒng)圖像分割中常用的幾種閾值分割方法。
總結(jié)
以上是生活随笔為你收集整理的OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Qt中修改应用程序和标题栏的图标
 - 下一篇: 利用numpy生成各种波