上一篇介紹了 Otsu's Thresholding。
它會分析整張圖片的 Histogram,並自動找到一個適合的 Threshold。
不過,假設今天要處理的圖片是左半邊很亮,右半邊很暗的。
真的還能只用一個 Threshold 嗎?
答案是:不一定。
假設有一張文件照片,拍照時燈光剛好只照到右半邊。
可能會變成這樣:
左側(陰影) 右側(光線充足)
文字:60 文字:120
背景:150 背景:240
如果使用:
Threshold = 180
右半邊可能效果很好。
但是左半邊,背景和文字都低於 180。
最後整片都變黑。
如果改成:
Threshold = 100
左半邊正常了。
右半邊的背景卻可能和文字一起變白。
怎麼調都很難兼顧整張圖片。
上面提到的情況,真正的問題不是 Threshold 選錯。
而是整張圖片共用同一個 Threshold。
如果不同區域的亮度不同,也許每個地方都應該有自己的 Threshold。
而這就是 Adaptive Threshold 的想法。
一般 Threshold 只有一個門檻值:
整張圖片
↓
Threshold = 128
Adaptive Threshold 則會把圖片切成很多小區域。
每個 Pixel 都根據自己附近的亮度決定 Threshold。
每一小塊都有自己的 Threshold。
因此即使左邊很暗,右邊很亮,依然能正常分割。
OpenCV 提供兩種常見的方法:
第一種:
直接計算附近 Pixel 的平均值。
Threshold = 附近亮度平均值
如果附近整體比較亮,Threshold 就提高。
如果附近比較暗,Threshold 就降低。
在 OpenCV 中使用:
cv2.ADAPTIVE_THRESH_MEAN_C
第二種是使用 Gaussian 權重。
距離中心越近,影響越大。
距離越遠,影響越小。
在 OpenCV 中使用:
cv2.ADAPTIVE_THRESH_GAUSSIAN_C
這個概念,其實和 Day 10 的 Gaussian Filter 很像。
只是這次不是拿來平滑圖片,是拿來計算 Threshold。
import cv2
import matplotlib.pyplot as plt
img = cv2.imread("dog.jpeg", cv2.IMREAD_GRAYSCALE)
binary = cv2.adaptiveThreshold(
img,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
11,
2
)
這裡有兩個新的參數:
11
代表計算 Threshold 時。
會觀察附近 11 × 11 的區域。
而:
2
代表最後再減去一個常數,稍微調整 Threshold。
結果如下:

整理完之後,我們把三種方法放在一起。
| 方法 | Threshold 數量 | 適合情況 |
|---|---|---|
| Threshold | 自己設定一個 | 光線固定、環境簡單 |
| Otsu | 自動找一個 | 前景、背景亮度差異明顯 |
| Adaptive Threshold | 每個區域各自計算 | 光線不均、陰影很多 |
看到這裡,整個 Threshold 系列就大致介紹完了。
其實 Adaptive Threshold 並不是在每個場景都是最好的。
如果圖片本來光線就很平均,Adaptive Threshold 反而需要更多計算。
而且有時還可能因為局部亮度變化太大讓結果變得破碎。
所以它並不是萬用的方法,而是專門解決 光線不均勻 的問題。
今天介紹了 Adaptive Threshold。
和前面的 Threshold、Otsu 不同。
它根據每個 Pixel 周圍的亮度,自行決定適合的門檻值。
不再替整張圖片尋找唯一的 Threshold。
因此,即使圖片存在陰影、光照不均或局部過亮的情況。
也能得到比全域 Threshold 更穩定的結果。
到今天為止,我們已經學會了如何利用亮度,把圖片分成前景和背景。
但分割完成之後,圖片裡常常還會殘留一些小黑點、小白點,或是不完整的區域。
有沒有方法,可以把這些結果整理得更乾淨?
下一篇,我們就來介紹 Morphology(形態學)。
看看如何透過簡單的幾何操作,讓二值化結果變得更乾淨、更完整。