上一篇介紹了 Canny Edge Detection。
它可以幫助我們找到圖片中的邊緣。
那如果今天不是想知道物體的輪廓,而是想直接把物體和背景分開。
那還需要找邊緣嗎?
例如說:
有一張白紙,上面印著黑色文字。
如果只是想把文字擷取出來,其實我們根本不需要知道每個字的輪廓。
只需要知道 哪些 Pixel 是文字,哪些 Pixel 是背景。
這就是今天要介紹的 Threshold(二值化)。
先說明一下 Threshold 的概念。
其實非常簡單:
先設定一個數值。
例如:
Threshold = 128
接著逐一檢查圖片中的每個 Pixel。
如果亮度大於 128,就變成白色。
255
如果亮度小於或等於 128,就變成黑色。
0
最後整張圖片只剩下兩種顏色:黑和白。
因此也稱為 Binary Image(二值影像)。
假設有一排 Pixel:
30 55 90 130 180 220
如果設定:
Threshold = 128
結果就會變成:
0 0 0 255 255 255
可以看到圖片中的灰階資訊全部消失了。
留下來的,只有「屬於哪一邊」。
想像一張文件照片。
文字通常很暗,背景通常很亮。
例如:
文字:40
背景:220
只要 Threshold 選得夠好,黑色文字就會全部留下,白色背景也會全部保留。
因此,很多文件掃描、OCR(文字辨識)之前都會先做 Threshold。
OpenCV 提供了 cv2.threshold()。
import cv2
import matplotlib.pyplot as plt
img = cv2.imread("dog.jpeg", cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
其中:
128 是 Threshold。255 表示符合條件時輸出的值。THRESH_BINARY 表示使用一般二值化。接著比較結果。
plt.figure(figsize=(10,4))
plt.subplot(1,2,1)
plt.imshow(img, cmap="gray")
plt.title("Original")
plt.axis("off")
plt.subplot(1,2,2)
plt.imshow(binary, cmap="gray")
plt.title("Threshold")
plt.axis("off")
plt.tight_layout()
plt.show()
結果長這樣:

可以看到,原本的灰階圖片現在只剩下黑色和白色。
Threshold 其實有不只一種方式,在 OpenCV 中,他們提供了好幾種模式。
例如:
一般二值化:
cv2.THRESH_BINARY

黑白相反:
cv2.THRESH_BINARY_INV

只保留超過 Threshold 的值:
cv2.THRESH_TOZERO

以及其他不同的 Threshold 類型。
不同模式,其實只是決定超過 Threshold 之後,要如何處理 Pixel。
Threshold 最大的問題是 Threshold 該設多少?
例如如果設定:
Threshold = 80
很多背景可能也變成黑色。
如果設定:
Threshold = 200
有些真正的文字,反而會一起消失。
因此Threshold 的效果非常依賴選擇的門檻值。
既然門檻值很重要,那有沒有一種方法,可以讓電腦自己找到最適合的 Threshold?
答案是肯定的。
下一篇要介紹的 Otsu's Threshold 就是專門解決這個問題。
它會分析整張圖片的亮度分布並自動找出一個適合的 Threshold。
今天介紹了 Threshold。
和前面的邊緣偵測不同,Threshold 利用亮度,把圖片直接分成前景和背景。
這也是影像分割中最基本的方法之一。
不過,它最大的挑戰就是 Threshold 到底該設多少?
設的過高或過低,都有可能遺失部分資訊。
而有一種方法可以讓電腦自己尋找最適合的 Threshold,也就是 Otsu's Threshold。
下一篇,我們就來看看 Otsu 是如何讓電腦自己決定這個答案。