文章目錄
- 一、 原理
- 1. 分水嶺演算法原理
- 2. 距離變換
- 3. opencv有關函式的用法
- 二、基于距離的分水嶺分割流程
- 三、python代碼實作
一、 原理
1. 分水嶺演算法原理
- 任何一副灰度影像都可以被看成拓撲平面,灰度值高的區域可以被看成是山峰,灰度值低的區域可以被看成是山谷,我們向每一個山谷中灌不同顏色的水,隨著水的位的升高,不同山谷的水就會相遇匯合,為了防止不同山谷的水匯合,我們需要在水匯合的地方構建起堤壩,不停地灌水,不停地構建堤壩知道所有的山峰都被水淹沒,我們構建好的堤壩就是對影像的分割,這就是分水嶺演算法的背后原理,
- OpenCV 采用了基于掩模的分水嶺演算法,在這種演算法中我們要設定那些山谷點會匯合,那些不會,這是一種互動式的影像分割,我們要做的就是給我們已知的物件打上不同的標簽,如果某個區域肯定是前景或物件,就使用某個顏色(或灰度值)標簽標記它,如果某個區域肯定不是物件而是背景就使用另外一個顏色標簽標記,而剩下的不能確定是前景還是背景的區域就用 0 標記,這就是我們的標簽,然后實施分水嶺演算法,每一次灌水,我們的標簽就會被更新,當兩個不同顏色的標簽相遇時就構建堤壩,直到將所有山峰淹沒,最后我們得到的邊界物件(堤壩)的值為 -1,
2. 距離變換
- 距離變換的基本含義是計算一個影像中非零像素點到最近的零像素點的距離,也就是到零像素點的最短距離
- 最常見的距離變換演算法就是通過連續的腐蝕操作來實作,腐蝕操作的停止條件是所有前景像素都被完全
- 腐蝕,這樣根據腐蝕的先后順序,我們就得到各個前景像素點到前景中心骨架像素點的距離
- 根據各個像素點的距離值,設定為不同的灰度值,這樣就完成了二值影像的距離變換
3. opencv有關函式的用法
cv2.distanceTransform(src, distanceType, maskSize, dst=None, dstType=None)
- src:輸入二值影像
- distanceType:計算距離的方式
- maskSize:蒙板尺寸
cv2.connectedComponents(image, labels=None, connectivity=None, ltype=None)
- image:輸入8位單通道影像
- labels:輸出標簽地圖
- connectivity:連通性,默認8,還可以取4
- Itype:輸出標簽型別 ,默認 CV_32S, 還可以取CV_16U
cv2.watershed(image, markers)
- image:輸入影像
- markers:標記
二、基于距離的分水嶺分割流程

- 輸入影像,有噪聲的話,先進行去噪
- 轉成灰度影像
- 二值化處理、形態學操作
- 距離變換
- 尋找種子、生成marker
- 實施分水嶺演算法、輸出分割后的影像
三、python代碼實作
import cv2 as cv
import numpy as np
def watershed_algorithm(image):
# 邊緣保留濾波EPF 去噪
blur = cv.pyrMeanShiftFiltering(image,sp=10,sr=100)
# 轉成灰度影像
gray = cv.cvtColor(blur, cv.COLOR_BGR2GRAY)
# 得到二值影像 自適應閾值
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow('binary image', binary)
# 形態學操作 獲取結構元素 開操作
kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
opening = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel=kernel, iterations=2)
# 確定區域
sure_bg = cv.dilate(opening, kernel, iterations=3)
# cv.imshow('mor-opt', sure_bg)
# 距離變換
dist = cv.distanceTransform(opening, cv.DIST_L2, 3)
dist_out = cv.normalize(dist, 0, 1.0, cv.NORM_MINMAX)
# cv.imshow('distance-', dist_out * 50)
ret, surface = cv.threshold(dist_out, dist_out.max() * 0.6, 255, cv.THRESH_BINARY)
# cv.imshow('surface-markers', surface)
surface_fg = np.uint8(surface) # 轉成8位整型
unkonown = cv.subtract(sure_bg, surface_fg) # 找到位置區域
# Marker labelling
ret, markers = cv.connectedComponents(surface_fg) # 連通區域
print(ret)
# 分水嶺變換
# 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[unkonown == 255] = 0
# 實施分水嶺演算法了,標簽影像將會被修改,邊界區域的標記將變為 -1
markers = cv.watershed(image, markers=markers)
image[markers == -1] = [0, 0, 255] # 被標記的區域 設為紅色
cv.imshow('result', image)
src = cv.imread(r'./test/042.png')
src = cv.resize(src, None, fx=0.5, fy=0.5)
cv.imshow('input image', src)
watershed_algorithm(src)
cv.waitKey(0)
cv.destroyAllWindows()
運行效果如下:




轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/89549.html
標籤:其他
