Open CV系列学习笔记(二十三)分水岭算法2021-02-15

Open CV系列学习笔记(二十三)分水岭算法

分水岭算法

所谓分水岭算法有好多种实现算法,拓扑学,形态学,浸水模拟和降水模拟等方式。分水岭算法(Watershed Algorithm),是根据分水岭的构成来考虑图像的分割。现实中我们可以或者说可以想象有山有湖的景象,那么那一定是水绕山,山围水的情形。当然在需要的时候,要人工构筑分水岭,以防集水盆之间的互相穿透。而区分高山(plateaus)与水的界线,以及湖与湖之间的间隔或 都是连通的关系,就是分水岭(watershed)。
分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
分水岭的计算过程是一个迭代标注过程。分水岭比较经典的计算方法是L. Vincent提出的。在该算法中,分水岭计算分两个步骤,一个是排序过程,一个是淹没过程。首先对每个像素的灰度级进行从低到高排序,然后在从低到高实现淹没过程中,对每一个局部极小值在h阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。
分水岭变换得到的是输入图像的集水盆图像,集水盆之间的边界点,即为分水岭。显然,分水岭表示的是输入图像极大值点。因此,为得到图像的边缘信息,通常把梯度图像作为输入图像,即
g(x,y)=grad(f(x,y))={[f(x,y)-f(x-1,y)]2[f(x,y)-f(x,y-1)]2}0.5
式中,f(x,y)表示原始图像,grad{.}表示梯度运算。
分水岭算法对微弱边缘具有良好的响应,图像中的噪声、物体表面细微的灰度变化,都会产生过度分割的现象。但同时应当看出,分水岭算法对微弱边缘具有良好的响应,是得到封闭连续边缘的保证的。另外,分水岭算法所得到的封闭的集水盆,为分析图像的区域特征提供了可能。
为消除分水岭算法产生的过度分割,通常可以采用两种处理方法,一是利用先验知识去除无关边缘信息。二是修改梯度函数使得集水盆只响应想要探测的目标。
为降低分水岭算法产生的过度分割,通常要对梯度函数进行修改,一个简单的方法是对梯度图像进行阈值处理,以消除灰度的微小变化产生的过度分割。即
g(x,y)=max(grad(f(x,y)),gθ)
式中,gθ表示阈值。

距离变换—Distance Transform

Open CV系列学习笔记(二十三)分水岭算法2021-02-15_第1张图片
Open CV系列学习笔记(二十三)分水岭算法2021-02-15_第2张图片
Open CV系列学习笔记(二十三)分水岭算法2021-02-15_第3张图片
代码:

def watershed_demo():
    #噪声太明显滤波
    print(src.shape)
    blurde = cv.pyrMeanShiftFiltering(src,10,100)
    #灰度,二值
    gray = cv.cvtColor(blurde,cv.COLOR_BGR2GRAY)
    ret,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY | cv.THRESH_OTSU)
    #cv.imshow("binary-image",binary)


    #形态学操作
    kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
    mb = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel,iterations=2)
    sure_bg = cv.dilate(mb,kernel,iterations=3)
    #cv.imshow("mor-opt",sure_bg)


    #距离变换
    dist = cv.distanceTransform(mb,cv.DIST_L2,3)
    dist_output = cv.normalize(dist ,0,1.0,cv.NORM_HAMMING)
    #cv.imshow("distance-t",dist_output*50)

    ret,surface = cv.threshold(dist,dist.max()*0.6,255,cv.THRESH_BINARY)
    #cv.imshow("surface-bin",surface)

    surface_fg = np.uint8(surface)
    unknow = cv.subtract(sure_bg,surface_fg)
    ret,markers = cv.connectedComponents(surface_fg)
    print(ret)

    #分水岭变换
    markers = markers + 1
    markers[unknow==255] = 0
    markers = cv.watershed(src,markers = markers)
    src[markers==-1] = [0,0,255]
    cv.imshow("result",src)


结果:
Open CV系列学习笔记(二十三)分水岭算法2021-02-15_第4张图片
Open CV系列学习笔记(二十三)分水岭算法2021-02-15_第5张图片
Open CV系列学习笔记(二十三)分水岭算法2021-02-15_第6张图片

完整代码:

import cv2 as cv
import numpy as np


def watershed_demo():
    #噪声太明显滤波
    print(src.shape)
    blurde = cv.pyrMeanShiftFiltering(src,10,100)
    #灰度,二值
    gray = cv.cvtColor(blurde,cv.COLOR_BGR2GRAY)
    ret,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY | cv.THRESH_OTSU)
    cv.imshow("binary-image",binary)


    #形态学操作
    kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
    mb = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel,iterations=2)
    sure_bg = cv.dilate(mb,kernel,iterations=3)
    cv.imshow("mor-opt",sure_bg)


    #距离变换
    dist = cv.distanceTransform(mb,cv.DIST_L2,3)
    dist_output = cv.normalize(dist ,0,1.0,cv.NORM_HAMMING)
    cv.imshow("distance-t",dist_output*50)

    ret,surface = cv.threshold(dist,dist.max()*0.6,255,cv.THRESH_BINARY)
    cv.imshow("surface-bin",surface)

    surface_fg = np.uint8(surface)
    unknow = cv.subtract(sure_bg,surface_fg)
    ret,markers = cv.connectedComponents(surface_fg)
    print(ret)

    #分水岭变换
    markers = markers + 1
    markers[unknow==255] = 0
    markers = cv.watershed(src,markers = markers)
    src[markers==-1] = [0,0,255]
    cv.imshow("result",src)


print("--------HEllow Python-------")
src = cv.imread("E:/picture/34.jpg")
cv.namedWindow("input image",cv.WINDOW_AUTOSIZE)
cv.imshow("input image",src)
watershed_demo()
cv.waitKey(0)


cv.destroyAllWindows()

你可能感兴趣的:(opencv学习笔记系列,算法,计算机视觉,opencv)