1. 程式人生 > >python+OpenCV影象處理(五)影象的閾值分割

python+OpenCV影象處理(五)影象的閾值分割

影象的閾值處理

      一幅影象包括目標物體、背景還有噪聲,要想從多值的數字影象中直接提取出目標物體,常用的方法就是設定一個閾值T,用T將影象的資料分成兩部分:大於T的畫素群和小於T的畫素群。這是研究灰度變換的最特殊的方法,稱為影象的二值化(Binarization)。

       閾值分割法的特點是:適用於目標與背景灰度有較強對比的情況,重要的是背景或物體的灰度比較單一,而且總可以得到封閉且連通區域的邊界。

(一)簡單閾值

選取一個全域性閾值,然後就把整幅影象分成非黑即白的二值影象。

函式為cv2.threshold( )

這個函式有四個引數,第一個是原影象矩陣,第二個是進行分類的閾值,第三個是高於(低於)閾值時賦予的新值,第四個是一個方法選擇引數,常用的有:

  • cv2.THRESH_BINARY(黑白二值)
  • cv2.THRESH_BINARY_INV(黑白二值翻轉)
  • cv2.THRESH_TRUNC(得到額影象為多畫素值)
  • cv2.THRESH_TOZERO(當畫素高於閾值時畫素設定為自己提供的畫素值,低於閾值時不作處理)
  • cv2.THRESH_TOZERO_INV(當畫素低於閾值時設定為自己提供的畫素值,高於閾值時不作處理)

這個函式返回兩個值,第一個值為閾值,第二個就是閾值處理後的影象矩陣。

img = cv2.imread('4.jpg', 0)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)  # binary (黑白二值)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)  # (黑白二值反轉)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)  # 得到的影象為多畫素值
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)  # 高於閾值時畫素設定為255,低於閾值時不作處理
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)  # 低於閾值時設定為255,高於閾值時不作處理

print(ret)

cv2.imshow('thresh1', thresh1)
cv2.imshow('thresh2', thresh2)
cv2.imshow('thresh3', thresh3)
cv2.imshow('thresh4', thresh4)
cv2.imshow('thresh5', thresh5)
cv2.imshow('grey-map', img)
cv2.waitKey(0)
cv2.destroyAllWindows()


(二)自適應閾值

一中的簡單閾值是一種全域性性的閾值,只需要設定一個閾值,整個影象都和這個閾值比較。而自適應閾值可以看成一種區域性性的閾值,通過設定一個區域大小,比較這個點與區域大小裡面畫素點 的平均值(或者其他特徵)的大小關係確定這個畫素點的情況。使用的函式為:

# 第一個引數為原始影象矩陣,第二個引數為畫素值上限,第三個是自適應方法(adaptive method):
#                                              -----cv2.ADAPTIVE_THRESH_MEAN_C:領域內均值
#                                              -----cv2.ADAPTIVE_THRESH_GAUSSIAN_C:領域內畫素點加權和,權重為一個高斯視窗
# 第四個值的賦值方法:只有cv2.THRESH_BINARY和cv2.THRESH_BINARY_INV
# 第五個Block size:設定領域大小(一個正方形的領域)
# 第六個引數C,閾值等於均值或者加權值減去這個常數(為0相當於閾值,就是求得領域內均值或者加權值)

# 這種方法理論上得到的效果更好,相當於在動態自適應的調整屬於自己畫素點的閾值,而不是整幅圖都用一個閾值

img = cv2.imread('4.jpg', 0)
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 第一個引數為原始影象矩陣,第二個引數為畫素值上限,第三個是自適應方法(adaptive method):
#                                              -----cv2.ADAPTIVE_THRESH_MEAN_C:領域內均值
#                                              -----cv2.ADAPTIVE_THRESH_GAUSSIAN_C:領域內畫素點加權和,權重為一個高斯視窗
# 第四個值的賦值方法:只有cv2.THRESH_BINARY和cv2.THRESH_BINARY_INV
# 第五個Block size:設定領域大小(一個正方形的領域)
# 第六個引數C,閾值等於均值或者加權值減去這個常數(為0相當於閾值,就是求得領域內均值或者加權值)
# 這種方法理論上得到的效果更好,相當於在動態自適應的調整屬於自己畫素點的閾值,而不是整幅圖都用一個閾值
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 2)
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
th4 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
cv2.imshow('img', img)
cv2.imshow('th1', th1)
cv2.imshow('th2', th2)
cv2.imshow('th3', th3)
cv2.imshow('th4', th4)
cv2.waitKey(0)
cv2.destroyAllWindows()


對於第五個引數的視窗越來越小時,發現得到的影象越來越細了,可以設想,如果把視窗設定的足夠大的話(不能超過影象大小),那麼得到的結果可能就和第二幅影象的相同了。

(三)Otsu's二值化

cv2.threshold( )函式有兩個返回值,一個是閾值,第二個是處理後的影象矩陣。

前面對於閾值的設定上,我們選擇的閾值都是127,在實際情況中,有的影象閾值不是127得到的影象效果更好。那麼這裡就需要演算法自己去尋找一個閾值,而Otsu's就可以自己找到一個認為最好的閾值。並且Otsu's非常適合於影象灰度直方圖(只有灰度影象才有)具有雙峰的情況。他會在雙峰之間找到一個值作為閾值,對於非雙峰影象,可能並不是很好用。那麼經過Otsu's得到的那個閾值就是函式cv2.threshold的第一個引數了。因為Otsu's方法會產生一個閾值,那麼函式cv2.threshold( )的第二個引數(設定閾值)就是0了,並且在cv2.threshold的方法引數中還得加上語句cv2.THRESH_OTSU.

在下列這些程式和圖片中大家會有鮮明的體會:

img = cv2.imread('2.jpg', 0)
ret1, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)  # 簡單濾波
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)  # Otsu 濾波
print(ret2)
cv2.imshow('img', img)
cv2.imshow('th1', th1)
cv2.imshow('th2', th2)
# 用於解決matplotlib中顯示影象的中文亂碼問題
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.hist(img.ravel(), 256)
plt.title('灰度直方圖')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()