在這裏,問題直截了當。對於每一個像素,應用相同的閾值。若是像素值小於閾值,則將其設置爲0,不然將其設置爲最大值。函數cv.threshold用於應用閾值。第一個參數是源圖像,它應該是灰度圖像。第二個參數是閾值,用於對像素值進行分類。第三個參數是分配給超過閾值的像素值的最大值。OpenCV提供了不一樣類型的閾值,這由函數的第四個參數給出。經過使用cv.THRESH_BINARY類型。全部簡單的閾值類型爲:python
請經過類型的文檔來觀察區別。git
該方法返回兩個輸出。第一個是使用的閾值,第二個輸出是閾值後的圖像。算法
此代碼比較了不一樣的簡單閾值類型:函數
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread('gradient.png',0) ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY) ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV) ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC) ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO) ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV) titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV'] images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] for i in xrange(6): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
> 注意 爲了繪製多個圖像,咱們使用plt.subplot()
函數。請查看matplotlib文檔以獲取更多詳細信息。學習
該代碼產生如下結果:優化
在上一節中,咱們使用一個全局值做爲閾值。但這可能並不是在全部狀況下都很好,例如,若是圖像在不一樣區域具備不一樣的光照條件。在這種狀況下,自適應閾值閾值化能夠提供幫助。在此,算法基於像素周圍的小區域肯定像素的閾值。所以,對於同一圖像的不一樣區域,咱們得到了不一樣的閾值,這爲光照度變化的圖像提供了更好的結果。.net
除上述參數外,方法cv.adaptiveThreshold還包含三個輸入參數:code
該adaptiveMethod決定閾值是如何計算的:orm
cv.ADAPTIVE_THRESH_MEAN_C::閾值是鄰近區域的平均值減去常數C。 cv.ADAPTIVE_THRESH_GAUSSIAN_C:閾值是鄰域值的高斯加權總和減去常數C。blog
該BLOCKSIZE肯定附近區域的大小,C是從鄰域像素的平均或加權總和中減去的一個常數。
下面的代碼比較了光照變化的圖像的全局閾值和自適應閾值:
結果:
在全局閾值化中,咱們使用任意選擇的值做爲閾值。相反,Otsu的方法避免了必須選擇一個值並自動肯定它的狀況。
考慮僅具備兩個不一樣圖像值的圖像(雙峯圖像),其中直方圖將僅包含兩個峯。一個好的閾值應該在這兩個值的中間。相似地,Otsu的方法從圖像直方圖中肯定最佳全局閾值。
爲此,使用了cv.threshold做爲附加標誌傳遞。閾值能夠任意選擇。而後,算法找到最佳閾值,該閾值做爲第一輸出返回。
查看如下示例。輸入圖像爲噪點圖像。在第一種狀況下,採用值爲127的全局閾值。在第二種狀況下,直接採用Otsu閾值法。在第三種狀況下,首先使用5x5高斯覈對圖像進行濾波以去除噪聲,而後應用Otsu閾值處理。瞭解噪聲濾波如何改善結果。
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread('noisy2.png',0) # 全局閾值 ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY) # Otsu閾值 ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU) # 高斯濾波後再採用Otsu閾值 blur = cv.GaussianBlur(img,(5,5),0) ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU) # 繪製全部圖像及其直方圖 images = [img, 0, th1, img, 0, th2, blur, 0, th3] titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)', 'Original Noisy Image','Histogram',"Otsu's Thresholding", 'Gaussian filtered Image','Histogram',"Otsu's Thresholding"] for i in xrange(3): plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray') plt.title(titles[i*3]), plt.xticks([]), plt.yticks([]) plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256) plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([]) plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray') plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([]) plt.show()
結果:
本節演示了Otsu二值化的Python實現,以展現其實際工做方式。若是您不感興趣,能夠跳過此步驟。
因爲咱們正在處理雙峯圖像,所以Otsu的算法嘗試找到一個閾值(t),該閾值將由關係式給出的加權類內方差最小化:
$$ \sigma_w^2(t) = q_1(t)\sigma_1^2(t)+q_2(t)\sigma_2^2(t) $$
其中
$$ q_1(t) = \sum_{i=1}^{t} P(i) \quad & \quad q_2(t) = \sum_{i=t+1}^{I} P(i) $$
$$ \mu_1(t) = \sum_{i=1}^{t} \frac{iP(i)}{q_1(t)} \quad & \quad \mu_2(t) = \sum_{i=t+1}^{I} \frac{iP(i)}{q_2(t)} $$
$$ \sigma_1^2(t) = \sum_{i=1}^{t} [i-\mu_1(t)]^2 \frac{P(i)}{q_1(t)} \quad & \quad \sigma_2^2(t) = \sum_{i=t+1}^{I} [i-\mu_2(t)]^2 \frac{P(i)}{q_2(t)} $$
實際上,它找到位於兩個峯值之間的t值,以使兩個類別的差別最小。它能夠簡單地在Python中實現,以下所示:
img = cv.imread('noisy2.png',0) blur = cv.GaussianBlur(img,(5,5),0) # 尋找歸一化直方圖和對應的累積分佈函數 hist = cv.calcHist([blur],[0],None,[256],[0,256]) hist_norm = hist.ravel()/hist.max() Q = hist_norm.cumsum() bins = np.arange(256) fn_min = np.inf thresh = -1 for i in xrange(1,256): p1,p2 = np.hsplit(hist_norm,[i]) # 機率 q1,q2 = Q[i],Q[255]-Q[i] # 對類求和 b1,b2 = np.hsplit(bins,[i]) # 權重 # 尋找均值和方差 m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2 v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2 # 計算最小化函數 fn = v1*q1 + v2*q2 if fn < fn_min: fn_min = fn thresh = i # 使用OpenCV函數找到otsu的閾值 ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU) print( "{} {}".format(thresh,ret) )