分水岭算法的逻辑:每一副灰度图像都可被当做拓扑平面,图像高灰度区域可以被看成是山峰,而低值区域是山谷,当向每一个山谷中灌不同颜色的水,当随着水位升高、不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合在一起,这时需要在水汇合的地方构建起堤坝,而构建堤坝的过程就是对图像的分割哲理和逻辑。我们可以通过下面的图像体会一下分水岭实现图像分割背后的逻辑:
我们先感受一下opencv官方的例子效果:
### 水平集分割:opencv 官方例子
ori_img = cv2.imread("img/1.jpg")
img = ori_img.copy()
gray = cv2.cvtColor(ori_img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
sure_bg = cv2.dilate(opening,kernel,iterations=1)
dist_transform = cv2.distanceTransform(opening,1,5)
ret, sure_fg = cv2.threshold(dist_transform,0.3*dist_transform.max(),255,0)
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
ret, markers1 = cv2.connectedComponents(sure_fg)
markers = markers1 1
markers[unknown==255] = 0
markers3 = cv2.watershed(ori_img,markers)
ori_img[markers3 == -1] = [255,0,0]
plt.figure(figsize=(20, 12))
plt.subplot(2,3,1), plt.imshow(img[:,:,::-1]),plt.title('原始图像'), plt.axis('off')
plt.subplot(2,3,2), plt.imshow(opening, 'gray'), plt.title('开运算结果'), plt.axis('off')
plt.subplot(2,3,3), plt.imshow(sure_bg, 'gray'), plt.title('sure_bg图像'), plt.axis('off')
plt.subplot(2,3,4), plt.imshow(dist_transform, 'gray'), plt.title('dist_transform图像'), plt.axis('off')
plt.subplot(2,3,5), plt.imshow(sure_fg, 'gray'), plt.title('sure_fg结果'), plt.axis('off')
plt.subplot(2,3,6), plt.imshow(ori_img, 'gray'), plt.title('ori_img结果'), plt.axis('off')
我们再试一下稍微复杂的图像:
效果并不理想。实际上,我们经常要对分割阈值进行动态的调整,这里会涉及到 能量泛函水平集分割。
能量泛函水平集分割:分割效果