Otsu 是針對整張圖片的二值化閥值選擇方法,但當圖像出現汙染時,Otsu 不容易得到好的結果,需要先對圖像進行預處理,再進行二值化閥值篩選。本文為系列文上篇,說明在圖像出現雜訊時,應先使用濾波器進行預處理;而圖像帶有陰影時,則使用自適應濾波器進行分割。下篇則會討論Otsu的應用實例以及自適應閥值與邊緣分割的關係。文末提供python程式碼。
目錄
- 前言 — 特殊情況下的二值化分割處理
- 帶有隨機雜訊圖像的閥值篩選
2.1 未加入雜訊
2.2 加入雜訊後
2.3 為什麼出現雜訊會影響二值化結果? - 陰影、漸層圖像的閥值篩選:局部、自適應閥值
- 小結
- Reference
1. 前言 — 特殊情況下的二值化分割處理
這篇文章說明圖像分割的進階應用,當圖片出現汙染或要進行細微亮點物件分割時,可以怎樣處理?包含圖片帶有雜訊或陰影的二值化處理,分別對應到濾波、自適應閥值及圖像梯度等技巧。本篇文章需要先對二值化算法有一些了解,如果不清楚可以先看這篇文章
2. 帶有隨機雜訊圖像的閥值篩選
2.1 未加入雜訊
隨機雜訊是一種常見的圖像汙染,我們用圖1(a) 來說明,加入雜訊對Otsu分割會有怎樣的影響。未加入雜訊前,如圖1 (b),將閥值定在200,可以乾淨的將圓形分割,分割結果如圖1 (c)。
2.2 加入雜訊後
圖2. (±50 灰度值) 、圖3. (±100 灰度值)列出加入不同雜訊的分割結果與分佈圖,可以看到圖2(c) 灰度值±50雜訊的分割結果邊緣輪廓不完整,但圖2(b) 分布圖仍可以找到一個不錯的分割點在180附近;但圖3(c) 灰度值±100的雜訊,因為雜訊的灰度值已經與中央的圓形重疊,所以圓外也有許多點被切割進去。
2.3 為什麼出現雜訊會影響二值化結果?
雜訊是否會影響分割結果,取決於加入的雜訊是否會影響數值的分布。從圖1 (b) ,可以看到圖像的灰度值分布在兩個區間,分別介於70–160,與240–250。兩分布差值約為90,表示當雜訊超過±45時,會讓兩個分佈的界線消失,無法準確分割出圖形。
上述隨機雜訊的問題,可以使用中值濾波器來處理,中值濾波可過濾範圍內的離群值,有效處理隨機雜訊。圖4.、圖5. 分別是對平均±50雜訊、平均±100雜訊圖像經過中值濾波處理的切割情形。與圖2. 未濾波前進行比較,可以看到濾波後的分布圖 (圖4(a)、圖5(a))更接近沒有雜訊的分布圖 (圖1(b)),切割結果也都獲得改善。
import cv2
import urllib
import urllib.request
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
req = urllib.request.urlopen('https://i.imgur.com/QP0rp2R.jpg')
arr = np.asarray(bytearray(req.read()), dtype=np.uint8)
img = cv2.imdecode(arr, -1)
img = cv2.resize(img, (500, 500))
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
### 幫圖片加個雜訊
noise_ar_100 = np.random.uniform(-100, 100, 500*500).reshape([500,500])
img_gray_addnoise100 = cv2.add(img_gray, noise_ar_100, dtype=cv2.CV_8UC3)
### 圖像分布情形
def image_hist_plot(image, n):
plt.imshow(image, cmap='gray', vmin=0, vmax=255);plt.axis('off')
plt.show()
plt.hist(image.ravel(), 256, [0,256])
plt.ylim(0, n)
plt.show()
image_hist_plot(img_gray_addnoise100, 2000)
### Otsu分割情形
def Otsu_result_plot(img):
ret, th = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
regions = np.digitize(img.astype(np.int), bins=np.array([ret]))
plt.imshow(regions, cmap='gray'); plt.axis('off')
plt.show()
Otsu_result_plot(img_gray_addnoise100)
3. 陰影、漸層圖像的閥值篩選:局部、自適應閥值
圖6(a) 是一個有陰影的漸層圓型,這個陰影的出現,讓Otsu切割的結果不完整,如圖6(b),當遇到這種圖片亮度不均的情形,就需要針對不同亮度的區域進行閥值篩選。
下面的範例將針對單一點進行閥值篩選,再套用至整張圖像。
例如,決定圖7(a) 紅色點 (x, y) 的閥值,需要經過下列步驟:
- 設定一個 kernel size,如圖7(a) 紅框的大小可以是3×3、5×5、7×7。
- 使用紅框內的灰度值,計算平均數mu(x, y) 作為基準值。此處平均數可以替換為其他統計量。
- 設定一個常數 c 作為閥值的偏移量
- 紅色點 (x, y) 的閥值 可以表示為 T(x, y) = mu(x, y) + c
- 比較紅色點 (x, y) 的灰度值是否超過 T(x, y),超過則定義為分割點
- 重複1–5步驟,掃過整張圖片,結果如圖7 (b)
### 讀入照片
req = urllib.request.urlopen('https://imgur.com/AX3EH43.jpg')
arr = np.asarray(bytearray(req.read()), dtype=np.uint8)
img = cv2.imdecode(arr, -1)
img = cv2.resize(img, (500, 500))
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
### 製作漸層圖像
n1, n2 = 50, 200
N = n2 - n1
linear_gradient_ar = np.array(list(range(n1, n2)) * N).reshape([N, N]).astype('float32')
linear_gradient_ar = cv2.resize(linear_gradient_ar, (500, 500))
plt.imshow(img_gray_addgrad, cmap='gray', vmin=0, vmax=255);plt.axis('off')
plt.show()
### Otsu分割
cut_value, th = cv2.threshold(img_gray_addgrad, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
### 繪製切割結果 => 結果不理想
ret, dst = cv2.threshold(img_gray_addgrad, cut_value, 255, cv2.THRESH_BINARY)
plt.imshow(dst, cmap='gray', vmin=0, vmax=255);plt.axis('off')
### 自適應閥值結果
def adaptiveThreshold_plot(para):
dst_mean = cv2.adaptiveThreshold(img_gray_addgrad, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, para ,3)
plt.imshow(dst_mean, cmap='gray', vmin=0, vmax=255);plt.axis('off')
plt.show()
### 比較不同參數下自適應閥值結果
adaptiveThreshold_plot(15)
adaptiveThreshold_plot(31)
adaptiveThreshold_plot(61)
adaptiveThreshold_plot(121)
4. 小結
本文說明了雜訊會對二值化分割帶來的影響,並說明使用中值濾波器時,能改善分割效果。而亮度在圖像上不均勻時,應該使用自適應閥值,對圖像上的每個位置設置各別的閥值。下篇將使用案例展示Otsu與自適應閥值的差異,並討論自適應閥值與sobel、Laplacian濾波器的差異。
5. Reference
- Gonzalez, Rafael C. Digital image processing. Pearson education india, 2018.
- OpenCV adaptiveThreshold