OpenCV-Python 圖像閾值 | 十五

目標

  • 在本教程中,您將學習簡單閾值,自適應閾值和Otsu閾值。
  • 你將學習函數cv.thresholdcv.adaptiveThreshold

簡單閾值

在這裏,問題直截了當。對於每一個像素,應用相同的閾值。若是像素值小於閾值,則將其設置爲0,不然將其設置爲最大值。函數cv.threshold用於應用閾值。第一個參數是源圖像,它應該是灰度圖像。第二個參數是閾值,用於對像素值進行分類。第三個參數是分配給超過閾值的像素值的最大值。OpenCV提供了不一樣類型的閾值,這由函數的第四個參數給出。經過使用cv.THRESH_BINARY類型。全部簡單的閾值類型爲:python

  • cv.THRESH_BINARY
  • cv.THRESH_BINARY_INV
  • cv.THRESH_TRUNC
  • cv.THRESH_TOZERO
  • cv.THRESH_TOZERO_INV

請經過類型的文檔來觀察區別。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::閾值是鄰近區域的平均值減去常數Ccv.ADAPTIVE_THRESH_GAUSSIAN_C:閾值是鄰域值的高斯加權總和減去常數Cblog

BLOCKSIZE肯定附近區域的大小,C是從鄰域像素的平均或加權總和中減去的一個常數。

下面的代碼比較了光照變化的圖像的全局閾值和自適應閾值:

結果:

Otsu的二值化

在全局閾值化中,咱們使用任意選擇的值做爲閾值。相反,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的二值化如何實現?

本節演示了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) )

其餘資源

  1. Digital Image Processing, Rafael C. Gonzalez

練習題

  1. Otsu的二值化有一些優化。您能夠搜索並實現它。 磐創AI技術博客資源彙總站:http://docs.panchuang.net/ PyTorch官方中文教程站:http://pytorch.panchuang.net/ OpenCV中文官方文檔: http://woshicver.com/
相關文章
相關標籤/搜索