Opencv实现用于图像分割分水岭算法
目标 • 使用分水岭算法基于掩模的图像分割 原理 任何一幅灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷。我们向每一个山谷中灌不同颜色的水,随着水的位的升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝。不停的灌水,不停的构建堤坝直到所有的山峰都被水淹没。我们构建好的堤坝就是对图像的分割。这就是分水岭算法的背后哲理。 代码 下面的例子中我们将就和距离变换和分水岭算法对紧挨在一起的对象进行分割。 我们从找到这些硬币的近似估计值开始,我们使用Otsu's二值化。 import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('image/coins.png') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(gray,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,iterations=3) dist_transform =cv2.distanceTransform(opening,1,5) ret,sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),0) sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg,sure_fg) 如结果所示,在阈值化之后的图像中,我们得到了肯定是硬币的区域,而且硬币之间也被分割开了。(有些情况下你可能只需要对前景进行分割,而不需要将紧挨在一起的对象分开,此时就没有必要使用距离变换了,腐蚀就足够了当然腐蚀也可以用来提取肯定是前景的区域。) 现在知道了哪些是背景哪些是硬币了,那我们就可以创建标签(一个与原图像大小相同,数据类型为 in32 的数组),并标记其中的区域了。对我们已经确定分类的区域(无论是前景还是背景)使用不同的正整数标记,对我们不确定的区域使用 0 标记。我们可以使用函数 cv2.connectedComponents()来做这件事。它会把将背景标记为 0,其他的对象使用从 1 开始的正整数标记。 ret,markers1 = cv2.connectedComponents(sure_fg) markers = markers1 + 1 markers[unknown == 255] = 0 结果使用 JET 颜色地图表示。深蓝色区域为未知区域。肯定是硬币的区域使用不同的颜色标记。其余区域就是用浅蓝色标记的背景了。现在标签准备好了。 markers3 = cv2.watershed(img,markers) img[markers3 == -1] = [255,0] 结果如下,有些硬币的边界被分割的很好,也有一些硬币之间的边界分割的不好。 参考:Opencv官方教程中文版(For Python) 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。 您可能感兴趣的文章:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |