分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征。
其他图像分割方法,如阈值,边缘检测等都不会考虑像素在空间关系上的相似性和封闭性这一概念,彼此像素间互相独立,没有统一性。分水岭算法较其他分割方法更具有思想性,更符合人眼对图像的印象。
任意的灰度图像可以被看做是地质学表面,高亮度的地方是山峰,低亮度的地方是山谷。给每个孤立的山谷(局部最小值)不同颜色的水(标签),当水涨起来,根据周围的山峰(梯度),不同的山谷也就是不同的颜色会开始合并,要避免这个,你可以在水要合并的地方建立障碍,直到所有山峰都被淹没。你所创建的障碍就是分割结果,这个就是分水岭的原理,但是这个方法会分割过度,因为有噪点,或者其他图像上的错误。所以OpenCV实现了一个基于掩模的分水岭算法,你可以指定哪些是要合并的点,哪些不是,这是一个交互式的图像分割,我们要做的是给不同的标签。给我们知道是前景或者是目标用一种颜色加上标签,给我们知道是背景或者非目标加上另一个颜色,最后不知道是什么的区域标记为0. 然后使用分水岭算法。
距离变换的基本含义是计算一个图像中非零像素点到最近的零像素点的距离,也就是到零像素点的最短距离个最常见的距离变换算法就是通过连续的腐蚀操作来实现,腐蚀操作的停止条件是所有前景像素都被完全腐蚀。这样根据腐蚀的先后顺序,我们就得到各个前景像素点到前景中心呗Ⅵ像素点的距离。根据各个像素点的距离值,设置为不同的灰度值。这样就完成了二值图像的距离变换
distanceType – 距离类型:cv2.DIST_L1
, cv2.DIST_L2
, cv2.DIST_C
maskSize – 距离变换蒙版的大小:取值3,5 或 CV_DIST_MASK_PRECISE(后一个选项仅由第一个函数支持)。在CV_DIST_L1或CV_DIST_C距离类型的情况下,参数被强制为3。
labels
– 可选输出2D标签数组(离散Voronoi图): 它的类型为CV_32SC1,大小与src相同
labelType
– 构建标签数组的类型:
若为 DIST_LABEL_CCOMP
,则src中每个连接的零组件(以及最接近连接组件的所有非零像素)将被分配相同的标签。
若为DIST_LABEL_PIXEL
,那么每个零像素(以及最接近它的所有非零像素)都会获得自己的标签。
img = np.zeros((400, 400), np.uint8)
cv2.circle(img, (150, 150), 100, 255, -1)
cv2.circle(img, (250, 250), 100, 255, -1)
dist = cv2.distanceTransform(img, cv2.cv.CV_DIST_L2, cv2.cv.CV_DIST_MASK_PRECISE)
dist3 = np.zeros((dist.shape[0], dist.shape[1], 3), dtype = np.uint8)
dist3[:, :, 0] = dist
dist3[:, :, 1] = dist
dist3[:, :, 2] = dist
markers = np.zeros(img.shape, np.int32)
markers[150,150] = 1 # seed for circle one
markers[250, 250] = 2 # seed for circle two
markers[50,50] = 3 # seeds for background
cv2.watershed(dist3, markers)
import numpy as np
import cv2
def watershed(imgpath):
img = cv2.imread(imgpath)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret0, thresh0 = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh0,cv2.MORPH_OPEN,kernel, iterations = 2)
# 确定背景区域
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# 确定前景区域
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret1, 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)
# 标记标签
ret2, markers1 = cv2.connectedComponents(sure_fg)
markers = markers1+1
markers[unknown==255] = 0
markers3 = cv2.watershed(img,markers)
img[markers3 == -1] = [0,255,0]
return thresh0,sure_bg,sure_fg,img
if __name__ == '__main__':
imgpath = './gggg/fenshuiling.png'
thresh0, sure_bg, sure_fg, img = watershed(imgpath)
cv2.imshow('thresh0',thresh0)
cv2.imshow('sure_bg', sure_bg)
cv2.imshow('sure_fg', sure_fg)
cv2.imshow('result_img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()