参考《OpenCV4.1中文官方文档》
理论
任何灰度图像都可以看作是一个地形表面,其中高强度表示山峰,低强度表示山谷。
你开始用不同颜色的水(标签)填充每个孤立的山谷(局部最小值)。
随着水位的上升,根据附近的山峰(坡度),来自不同山谷的水明显会开始合并,颜色也不同。
为了避免这种情况,你要在水融合的地方建造屏障。
你继续填满水,建造障碍,直到所有的山峰都在水下。
然后你创建的屏障将返回你的分割结果。
这种方法会由于图像中的噪声或其他不规则性而产生过度分割的结果。
因此OpenCV实现了一个基于标记的分水岭算法,
你可以指定哪些是要合并的山谷点,哪些不是。
我们所做的是给我们知道的对象赋予不同的标签。
用一种颜色(或强度)标记我们确定为前景或对象的区域,
用另一种颜色标记我们确定为背景或非对象的区域,
最后用 0 标记我们不确定的区域。
然后应用分水岭算法。然后我们的标记将使用我们给出的标签进行更新,对象的边界值将为 -1 。
代码
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:/project/test/test.png')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
#开运算 消除噪音
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2)
# 膨胀 确定背景区域
sure_bg = cv.dilate(opening,kernel,iterations=3)
# 寻找前景区域
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5) # 距离变换
ret, sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0) #相当于腐蚀
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)
ret, markers = cv.connectedComponents(sure_fg) #创建标记图
markers = markers+1
markers[unknown==255] = 0
marke=np.uint8(markers)
cv.imshow("markers", marke)
cv.waitKey(0)
markers = cv.watershed(img,markers)
#Watershed算法 第一个参数是图像,第二个参数是标记图
#会根据markers传入的轮廓作为种子(也就是所谓的注水点),
#对图像上其他的像素点根据分水岭算法规则进行判断,
#并对每个像素点的区域归属进行划定,直到处理完图像上所有像素点
#区域与区域之间的分界处的值被置为“-1”,以做区分。
img[markers == -1] = [255,0,0]
cv.imshow("ans", img)
cv.waitKey(0)