簡單閾值法
????????此方法是直截了當的。如果像素值大于閾值,則會被賦為一個值(可能為白色),否則會賦為另一個值(可能為黑色)。使用的函數是 cv.threshold。第一個參數是源圖像,它應該是灰度圖像。第二個參數是閾值,用于對像素值進行分類。第三個參數是 maxval,它表示像素值大于(有時小于)閾值時要給定的值。opencv 提供了不同類型的閾值,由函數的第四個參數決定。不同的類型有:
- cv.THRESH_BINARY
- cv.THRESH_BINARY_INV
- cv.THRESH_TRUNC
- cv.THRESH_TOZERO
- cv.THRESH_TOZERO_INV
獲得兩個輸出。第一個是 retval,稍后將解釋。第二個輸出是我們的閾值圖像。
代碼如下:
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()
結果如下所示:
?
自適應閾值
????????在前一節中,我們使用一個全局變量作為閾值。但在圖像在不同區域具有不同照明條件的條件下,這可能不是很好。在這種情況下,我們采用自適應閾值。在此,算法計算圖像的一個小區域的閾值。因此,我們得到了同一圖像不同區域的不同閾值,對于不同光照下的圖像,得到了更好的結果。
它有三個“特殊”輸入參數,只有一個輸出參數。
Adaptive Method-它決定如何計算閾值。
- cv.ADAPTIVE_THRESH_MEAN_C?閾值是指鄰近地區的平均值。
- cv.ADAPTIVE_THRESH_GAUSSIAN_C?閾值是權重為高斯窗的鄰域值的加權和。
Block Size-它決定了計算閾值的窗口區域的大小。
C-它只是一個常數,會從平均值或加權平均值中減去該值。
下面的代碼比較了具有不同照明的圖像的全局閾值和自適應閾值:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('sudoku.png',0)
img = cv.medianBlur(img,5)
ret,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\cv.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\cv.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)','Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in xrange(4):plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')plt.title(titles[i])plt.xticks([]),plt.yticks([])
plt.show()
結果如下所示:
?
Otsu 二值化
????????在全局閾值化中,我們使用一個任意的閾值,那么,我們如何知道我們選擇的值是好的還是不好的呢?答案是,試錯法。但是考慮一個雙峰圖像(簡單來說,雙峰圖像是一個直方圖有兩個峰值的圖像)。對于那個圖像,我們可以近似地取這些峰值中間的一個值作為閾值,這就是 Otsu 二值化所做的。所以簡單來說,它會自動從雙峰圖像的圖像直方圖中計算出閾值。(對于非雙峰圖像,二值化將不準確。)
????????為此,我們使用了?cv.threshold?函數,但傳遞了一個額外的符號?cv.THRESH_OTSU?。對于閾值,只需傳入零。然后,該算法找到最佳閾值,并作為第二個輸出返回 retval。如果不使用 otsu 閾值,則 retval 與你使用的閾值相同。
????????查看下面的示例。輸入圖像是噪聲圖像。在第一種情況下,我應用了值為 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()
結果如下:
?
?
?