分水岭算法
假设一副灰度图像看作是地势图,灰度值高的区域可以被看成是
山峰,灰度值低的区域可以被看成是山谷。开始向山谷中注入水,随着水位的升高,不同的山谷中的水将要汇聚,为了防止不同山谷的水汇合,我们需要在水汇合的区域构建堤坝。不停的灌水,不停的构建堤坝直至所有的山峰都被水淹没。我们构建好的堤坝就是对图像的分割。
代码
# 分水岭算法
import cv2
import numpy as np
from matplotlib import pyplot as plt
src = cv2.imread(r'F:\OPENCV\Opencv\coin.png')
if src is None:
print('image is empty')
img = src.copy()
img = cv2.medianBlur(img, 5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 阈值化
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
# 形态学操作, 去除噪声
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# 膨胀, 获取背景
sure_bg = cv2.dilate(opening, kernel, iterations=3)
# 距离变换
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret2, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)
# 获取未知区域
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg) # 不确定区域
# cv2.connectedComponents()将前景从1开始的正整数标记,背景标为0, 未知区域为-1
ret3, markers = cv2.connectedComponents(sure_fg) # 标签化
markers = markers + 1 # 所有标签+1, 目的是将背景标签从0变为1, 因为标签为0会被算法识别为背景区域
markers[unknown == 255] = 0 # 未知区域标记为0
# cv2.watershed() 开始注水
markers2 = cv2.watershed(img, markers)
img[markers2 == -1] = [0, 0, 255]
img[markers2 >= 1] = [255, 255, 255]
# 显示结果
titles = ['src', 'gray', 'sure_bg', 'dist_transform', 'sure_fg', 'unknown', "result"]
images = [src[:, :, ::-1], gray, sure_bg, dist_transform, sure_fg, unknown, img[:, :, ::-1]]
plt.figure(1)
for i in range(len(images)):
plt.subplot(3, 3, i + 1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.subplot(3, 3, 9)
plt.imshow(np.abs(markers), 'jet')
plt.title('markers')
plt.xticks([])
plt.yticks([])
plt.show()
结果显示