原理
任何一副灰度圖像都可以被看成拓撲平面,灰度值高的區域可以被看成是山峰,灰度值低的區域可以被看成是山谷。我們向每一個山谷中灌不同顏色的水。隨著水的位的升高,不同山谷的水就會相遇匯合,為了防止不同山谷的水匯合,我們需要在水匯合的地方構建起堤壩。不停的灌水,不停的構建堤壩知道所有的山峰都被水淹沒。我們構建好的堤壩就是對圖像的分割。這就是分水嶺算法的背后哲理。
分水嶺算法是一種圖像分割算法,常用于分割具有重疊目標的圖像。它基于數學形態學的理念,將圖像看作地形圖,水流在圖像的低谷(目標邊界)聚集,形成分割線。OpenCV 提供了 cv2.watershed()
函數來執行分水嶺算法。
基本的語法如下:
cv2.watershed(image, markers)
參數說明:
image
: 輸入的圖像,通常是一個三通道彩色圖像。markers
: 標記圖像,用于指定分水嶺算法的初始標記。標記圖像應該是單通道灰度圖像,其中不同的標記值表示不同的區域。
cv2.watershed
函數會修改輸入圖像,將標記圖像中的區域分割開,并用不同的顏色標記不同的分割區域。分水嶺算法通常用于從預先標記的圖像中分割出目標區域。
以下是一個簡單的示例,演示如何使用分水嶺算法進行圖像分割:
import cv2
import numpy as np
import matplotlib.pyplot as plt# 讀取圖像
img = cv2.imread(r"C:\Users\mzd\Desktop\opencv\4.jpg")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 轉換為灰度圖像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 使用閾值分割獲取前景區域掩碼
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
#cv2.threshold 是 OpenCV 中用于圖像閾值化的函數。閾值化是一種將圖像分割成兩個區域的方法,通常用于目標檢測、邊緣檢測等應用。
# 對前景區域進行形態學操作,消除小的噪點
kernel = np.ones((3, 3), np.uint8)
#np.ones 是 NumPy 中的一個函數,用于創建一個指定形狀(shape)的數組,并將數組的所有元素初始化為 1
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)# 通過距離變換獲取圖像的距離變換結果
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)# 獲取背景區域掩碼
sure_bg = cv2.dilate(opening, kernel, iterations=3)# 標記不確定的區域
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)# 使用連通組件標記不確定區域
_, markers = cv2.connectedComponents(sure_fg)# 將標記+1作為不確定區域的標記,以避免與已知區域的標記沖突
markers = markers + 1
markers[unknown == 255] = 0 # 不確定區域的標記設為0# 應用分水嶺算法
cv2.watershed(img, markers)# 將分水嶺算法的標記區域設為紅色
img[markers == -1] = [255, 0, 0]# 顯示原始圖像、閾值分割結果和分水嶺算法結果
plt.figure(figsize=(12, 6))plt.subplot(131), plt.imshow(img_rgb)
plt.title('Original Image'), plt.axis('off')plt.subplot(132), plt.imshow(thresh, cmap='gray')
plt.title('Thresholded Image'), plt.axis('off')plt.subplot(133), plt.imshow(img)
plt.title('Watershed Result'), plt.axis('off')plt.show()
在這個示例中,我們首先讀取了一幅圖像,將其轉換為灰度圖像。然后,使用閾值分割獲取前景區域的掩碼,并通過形態學操作和距離變換對圖像進行預處理。接著,通過分水嶺算法將圖像分割為不同的區域,并在結果圖像上標記分割線。最后,通過 Matplotlib 顯示原始圖像、閾值分割結果和分水嶺算法的分割結果。