任何灰度图像都可以看作是一个地形表面,其中高强度表示山峰和丘陵,而低强度表示山谷。用不同颜色的水(标签)填充每个孤立的山谷(局部极小值)。当水上升时,根据附近的峰(梯度),不同山谷不同的颜色的水,显然会开始融合。为了避免这种情况,你在水就要融合的地方及时增加屏障(增高水坝)。你继续填满水,建造屏障,直到所有的山峰都被淹没。最后,创建的屏障会给出分割结果。这就是分水岭算法的通俗原理。你可以访问分水岭的CMM网页(http://www.cmm.mines-paristech.fr/~beucher/wtshed.html),里面有动画帮助理解。
但是这种方法会由于图像中的噪声或其他不规则性因素而导致过度分割的结果。OpenCV实现了一种基于标记的分水岭算法,你可以指定哪些是要合并的谷点,哪些不是。我们所做的是给我们所知道的对象赋予不同的标签(marker)。用一种颜色(或强度)标记我们确定的为前景或对象的区域,用另一种颜色标记我们确定为背景或非对象的区域,最后用0标记我们不确定的区域。然后应用分水岭算法,其将使用我们给出的标签进行更新(填水),对象的边界值将为-1。
下面是示例代码,用于对金鱼图片进行分割:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread("2.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#使用Otsu的二值化方法把图片变成二值图像
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)
#找到确定的背景区域
sure_bg = cv2.dilate(opening, kernel, iterations = 3)
#背景之外是可能的前景,距离变换(“膨胀”)确定的背景的边界线得到确定的前景
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
alpha =0.3 #距离变换系数,越小则会找到更多的前景区域
ret, sure_fg = cv2.threshold(dist_transform, alpha*dist_transform.max(), 255, 0) #用距离变换找到确定的前景区域
sure_fg = np.uint8(sure_fg)
得到了确定的背景和 确定的前景
#确定分割边界
unknown = cv2.subtract(sure_bg, sure_fg)
ret, markers = cv2. connectedComponents(sure_fg)
markers += 1
markers[unknown==255] =0
markers = cv2.watershed(img, markers)
img1 = cv2.cvtColor(img.copy(), cv2.COLOR_BGR2RGB)
img1[markers ==-1] = [0, 0, 255] #分水岭 标蓝色
#使用matplotlib 绘图
plt.subplot(221)
plt.imshow(img1)
plt.title("watershed")
plt.xticks([]); plt.yticks([]) #不显示坐标轴刻度
plt.subplot(222)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title("original")
plt.xticks([]); plt.yticks([])
plt.subplot(223)
sure_fg_ = np.where(sure_fg>128, 1, 0) #转2值图
sure_fg_ = sure_fg_.repeat(3,axis=1).reshape(img.shape) * cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#plt.imshow(sure_fg, cmap ="gray")
plt.imshow(sure_fg_)
plt.title("sure foreground")
plt.xticks([]); plt.yticks([])
plt.subplot(224)
sure_bg_ =255- sure_bg #黑白颠倒
sure_bg_ = np.where(sure_bg_>128, 1, 0) #转2值图
sure_bg_ = sure_bg_.repeat(3,axis=1).reshape(img.shape) * cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#plt.imshow(sure_bg, cmap ="gray_r")
plt.imshow(sure_bg_)
plt.title("sure background")
plt.xticks([]); plt.yticks([])
plt.show()
本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!