前文傳送門:
「Python 影像處理 OpenCV (2):畫素處理與 Numpy 操作以及 Matplotlib 顯示影像」
「Python 影像處理 OpenCV (3):影像屬性、影像感興趣 ROI 區域及通道處理」
「Python 影像處理 OpenCV (4):影像算數運算以及修改顏色空間」
「Python 影像處理 OpenCV (5):影像的幾何變換」
影像的閾值
看到這個詞可能大家都很懵,為啥在影像處理裡面還會有閾值。
影像的閾值處理用大白話講就是將影像轉化為二值影像(黑白圖),目的是用來提取影像中的目標物體,將背景和噪聲區分開(可以近似的認為除了目標全是噪聲)。
通常會設定一個閾值 T ,通過 T 將影像的畫素劃分為兩類:大於 T 的畫素群和小於 T 的畫素群。
首先可以先將影像轉化為灰度影像,因為在灰度影像中,每個畫素都只有一個灰度值用來表示當前畫素的亮度。
接下來二值化處理可以將影像中的畫素劃分為兩類顏色,一種是大於閾值 T 的,另一種是小於閾值 T 的。
比如最常見的二值影像:
當灰度值小於閾值 T 的時候,可以將其畫素設定為 0 ,表示為黑色。
當灰度值大於閾值 T 的時候,可以將其畫素設定為 255 ,表示為白色。
在 OpenCV 中,為我們提供了閾值函式 threshold()
來幫助我們實現二值影像的處理。
函式如下:
retval, dst = threshold(src, thresh, maxval, type, dst=None)
- retval: 閾值
- dst: 處理後的影像
- src: 原影像
- thresh: 閾值
- maxval: 最大值
- type: 處理型別
常用的 5 中處理型別如下:
- cv.THRESH_BINARY: 二值處理
- cv.THRESH_BINARY_INV: 反二值處理
- cv.THRESH_TRUNC: 截斷閾值化
- cv.THRESH_TOZERO: 閾值化為 0
- cv.THRESH_TOZERO_INV: 反閾值化為 0
接下來這幾種處理型別有啥不同,我們一個一個來看。
二值處理
這種二值處理方式最開始需要選定一個閾值 T ,從 0 ~ 255 之間,我這裡選擇出於中間的那個數 127 。
接下來的處理規則就是這樣的:
- 大於等於 127 的畫素點的灰度值設定為最大值,也就是 255 白色
- 小於 127 的畫素點的灰度值設定為 0 ,也就是黑色
接下來開始寫程式碼,看我們的馬里奧同學(不知道你們還記不記得我們的馬里奧同學):
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 影像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值影像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY)
# 顯示影像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待顯示
cv.waitKey(0)
cv.destroyAllWindows()
反二值處理
這種方式和上面的二值處理非常相似,只是把處理規則給反了一下:
- 大於等於 127 的畫素點的灰度值設定為 0 ,也就是白色
- 小於 127 的畫素點的灰度值設定為最大值,也就是 255 白色
完整程式碼如下:
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 影像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值影像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY_INV)
# 顯示影像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待顯示
cv.waitKey(0)
cv.destroyAllWindows()
從影像上可以看到,顏色和上面的二值影像正好相反,大部分的位置都變成了白色。
截斷閾值化
這種方法還是需要先選定一個閾值 T ,影像中大於該閾值的畫素點被設定為該閾值,小於該閾值的保持不變。
完整程式碼如下:
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 影像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值影像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TRUNC)
# 顯示影像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待顯示
cv.waitKey(0)
cv.destroyAllWindows()
這種方式實際上是把圖片比較亮的畫素處理成為閾值,其他部分保持不變。
閾值化為 0
這種方式還是需要先選定一個閾值 T ,將小於 T 的畫素點設定為 0 黑色,其他的保持不變。
完整程式碼如下:
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 影像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值影像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO)
# 顯示影像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待顯示
cv.waitKey(0)
cv.destroyAllWindows()
這個方法是亮的部分不改,把比較暗的部分修改為 0 。
反閾值化為 0
這個和前面的反二值影像很像,同樣是反閾值化為 0 ,將大於等於 T 的畫素點變為 0 ,其餘保持不變。
完整程式碼如下:
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 影像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值影像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO_INV)
# 顯示影像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待顯示
cv.waitKey(0)
cv.destroyAllWindows()
這個方法是暗的部分不改,把比較亮的部分修改為 0 。
全家福
接下來還是給這幾種閾值處理後的影像來個全家福,讓大家能有一個直觀的感受,程式碼我也給出來,如下:
import cv2 as cv
import matplotlib.pyplot as plt
# 讀取影像
img=cv.imread('maliao.jpg')
lenna_img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
gray_img=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 閾值化處理
ret1, thresh1=cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY)
ret2, thresh2=cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY_INV)
ret3, thresh3=cv.threshold(gray_img, 127, 255, cv.THRESH_TRUNC)
ret4, thresh4=cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO)
ret5, thresh5=cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO_INV)
# 顯示結果
titles = ['Gray Img','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [gray_img, thresh1, thresh2, thresh3, thresh4, thresh5]
# matplotlib 繪圖
for i in range(6):
plt.subplot(2, 3, i+1), plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
示例程式碼
如果有需要獲取原始碼的同學可以在公眾號回覆「OpenCV」進行獲取。