分水嶺算法watershed
- 進行圖像分割
- 基本的步驟
- 通過形態學開運算對原圖像O去噪
- 通過膨脹操作獲取“確定背景B”
- 利用距離變換函數對圖像進行運算,並對其進行閾值處理,得到“確定前景F”
- 計算未知區域UN(UN = O – B – F )
- 利用函數connecedComponents對原圖像O進行標註
- 對函數connecedComponents的標註結果進行修正
- 使用分水嶺函數watershed完成對圖像的分割
操作小記
代碼
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('coins.jpg')
if img is None:
print('Could not open or find the image ')
exit(0)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
# cv.imshow("threshold", thresh) #閾值處理後會有緊挨着(粘連)的情況
# 去噪處理
kernel = np.ones((3, 3), np.uint8)
opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=2) # 開運算
# sure background area
sure_bg = cv.dilate(opening, kernel, iterations=3) # 膨脹操作
# Finding sure foreground area
dist_transform = cv.distanceTransform(opening, cv.DIST_L2, 5)
ret, sure_fg = cv.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0) # 距離背景點足夠遠的點認爲是確定前景
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg, sure_fg) # 確定未知區域:減法運算
# Marker labelling
ret, markers = cv.connectedComponents(sure_fg) # 設定壩來阻止水匯聚
# Add one to all labels so that sure background is not 0, but 1
markers = markers + 1
# Now, mark the region of unknown with zero
markers[unknown == 255] = 0
markers = cv.watershed(img, markers)
img[markers == -1] = [255, 0, 0]
plt.imshow(img)
plt.show()
效果
代碼分析
ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
- 函數的作用是進行閾值處理
- 返回參數:
ret
爲閾值,thresh
爲處理結果 - 輸入參數中
gray
爲輸入圖像的灰度圖像,上一行代碼cv.cvtColor(img, cv.COLOR_BGR2GRAY)
進行了色彩空間的轉換0
爲設定的閾值255
代表圖像最大值cv.THRESH_BINARY_INV + cv.THRESH_OTSU
二值化的方式,THRESH_BINARY_INV
表示採取反二進制閾值化,而`THRESH_OTSU``代表採用自適應圖像的二值化Otsu- 說明:反二進制閾值化:小於閾值取255,大於閾值取0
- 測試中,
ret=162.0
,thresh
的效果如下:
- 相關資料:濾波和卷積
opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=2)
- 函數的作用是去除噪聲
- 輸入參數中
thresh
是進行閾值處理的圖像cv.MORPH_OPEN
表示進行開運算,開運算就是對圖像先腐蝕再膨脹kernel
爲形態學運算的內核,上一句kernel = np.ones((3, 3), np.uint8)
設置了參考點位於中心3x3的核iterations
用於設置迭代次數,這裏設置爲2
opening
爲處理結果,效果如下:
- 相關資料:More Morphology Transformations
sure_bg = cv.dilate(opening, kernel, iterations=3)
- 作用:用膨脹的方式獲取背景
sure_bg
爲得到的結果,效果如下:
# Finding sure foreground area
dist_transform = cv.distanceTransform(opening, cv.DIST_L2, 5)
ret, sure_fg = cv.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0) # 距離背景點足夠遠的點認爲是確定前景
通過函數cv.distanceTransform(...)
進行距離變換,cv.DIST_L2
代表採用歐幾里得的距離計算公式,5
代表掩膜尺寸,用來確定前景,然後通過閾值處理得到核心的區域,超過最大值的70%才留下來
dst = cv2.distanceTransform( src, distanceType, maskSize[, dst[, dstType]] )
src
:8位單通道的二值圖像distanceType
:距離類型參數maskSize
:掩膜尺寸dstType
:目標圖像的類型,默認值爲cv_32Fdst
:計算得到的目標圖像
distanceType
取值
maskSize
說明
得到效果如下:
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg, sure_fg) # 確定未知區域:減法運算
通過做減法來獲取到未知區域,確定的背景-確定的前景
確定未知區域
對確定的前景F進行膨脹放大——得到——確定的背景B
未知區域UN = 原圖像O—確定的背景B — 確定前景F
減法運算
unknown
的效果如下:
# # Marker labelling
ret, markers = cv.connectedComponents(sure_fg) # 設定壩來阻止水匯聚
# Add one to all labels so that sure background is not 0, but 1
markers = markers + 1
# Now, mark the region of unknown with zero
markers[unknown == 255] = 0
用來設置水壩來分割背景和前景,將未知區域標記爲0
標註確定的前景圖像函數
connectedComponents
cv2. connectedComponents中:背景標註爲0,其他對象使用從1開始的正整數標註
- 數值0代表背景區域
- 從數值1開始的值,代表不同的前景區域
在分水嶺算法中,標註0代表未知區域,所以需要對上面的標註結果進行調整
- 數值1代表背景區域
- 從數值2開始的值,代表不同的前景區域
- 對未知區域進行標註,將計算出的未知區域標註爲0
retval, labels = cv2. connectedComponents(image )
- images:8位單通道的待標註圖像
- retval :返回標註的數量
- labels :標註的結果圖像
markers = cv.watershed(img, markers)
進行分水嶺算法
Markers = cv2.watershed(image, markers )
Image: 輸入圖像,必須是一個8bit 3通道的圖像
- 在處理之前必須用正數大致勾畫出圖像中的期望分割區域。
- 每個區域被標註爲1、2、3等。對尚未確定的區域,需要將它們標註爲0.
- 標註區域可以理解爲進行分水嶺算法分割的“種子”區域
markers: 32位單通道的標註結果,和image大小相同
- 算法會根據markers傳入的輪廓作爲種子(也就是所謂的注水點),對圖像上其他的像素點根據分水嶺算法規則進行判斷,並對每個像素點的區域歸屬進行劃定
- 每個像素要麼要麼被設置爲初期的“種子值”,要麼爲“-1”
- 區域與區域之間的分界處的值被置爲“-1”
附錄
官方文檔:The Watershed Transformation
距離變換函數distanceTransform
提取前景對象
如果前景對象的中心(質心)距離值爲0的像素點距離較遠,會得到一個較大的值
如果前景對象的邊緣距離值爲0的像素點較近,會得到一個較小的值