OpenCV 的分水岭图像分割算法

认识分水岭算法,并在 opencv 使用该算法

什么是分水岭算法?

  • 任何一副灰度图都可以看作是拓扑平面,灰度值高的区域被看成山峰,灰度值低的区域看作山谷
  • 向每个山谷灌不同颜色的水,随着水位的升高,不同山谷的水就会相遇,为了防止不同山谷的水汇合,在水汇合的地方构筑堤坝
  • 不停地灌水,不停地构筑堤坝,直至所有山峰被水淹没,我们构筑的堤坝就是对图像的分割

如何解决分水岭算法的过分割现象?

  • 使用传统的分水岭算法时,由于噪声的存在,图像上存在很多很小的局部极值点,会产生过分割的现象
  • 对图像进行高斯平滑操作,抹除很多小的最小值,这些小分区就会合并
  • 采用基于标记 (mark) 的图像分水岭算法,即不从最小值开始增长,可以将相对较高的灰度值像素作为起始点(需要用户手动标记),从标记处开始进行淹没,则很多小区域都会被合并为一个区域这也是 OpenCV 所采用的方法

基于 markers 的分水岭算法原理?

  • 手动标记后得到确定的前景、确定的背景、不确定区域,通过确定的前景及背景的水涨过程,逐渐去掉不确定区域,实现前景和背景的分割
  • 确定的前景、确定的背景、不确定区域存储在和原图大小一致的二值图 (markers) 上,即用大于 0 标记确定为前景或对象区域;(2) 用另一种颜色(大于 0)标记确定为背景或非对象区域;(3) 用 0 标记不确定任何内容的区域
  • 运行分水岭算法,对象的边界将具有 - 1 的值

基于 markers 的分水岭算法步骤?

  • 首先构造分水岭算法需要 markers,然后使用分水岭算法进行分割
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    # ---------------------1.图像预处理-----------------------------
    # 转为灰度图
    gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 自适应二值化
    ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

    # ---------------------2.构造种子-----------------------------
    # 开运算
    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)
    dist_output = cv2.normalize(dist_transform, 0, 1.0, cv2.NORM_MINMAX) #归一化
    ret, sure_fg = cv2.threshold(dist_output,0.7*dist_output.max(),255,0)

    # 确定的背景-确定的前景,剩下部分是未知区域
    sure_fg = np.uint8(sure_fg)
    unknown = cv2.subtract(sure_bg,sure_fg)
    show_images([sure_bg,sure_fg,unknown],closize=3)

    # 连通区域标记
    ret, markers = cv2.connectedComponents(sure_fg)
    #cv2.imwrite("markers.png",markers)

    # markers所有值+1,确保值从1开始
    markers = markers+1

    # 使用0值标记背景区域
    markers[unknown==255]=0
    # ---------------------3.使用分水岭算法-----------------------------
    markers = cv2.watershed(image,markers)

OpenCV 分水岭算法的缺点?

  • 标记一张图片后,无法将用法用于所有的图片
  • 图片必须有背景,并且有标注
  • 图片内所有连通的前景必须有标注,否则分水岭算法按背景处理