OpenCV-分水岭算法

分水岭算法

任何灰度图像都可以看作是一个地形表面,其中高强度表示山峰,低强度表示山谷。你开始用不同颜色的水(标签)填充每个孤立的山谷(局部最小值)。随着水位的上升,根据附近的山峰(坡度),来自不同山谷的水明显会开始合并,颜色也不同。为了避免这种情况,你要在水融合的地方建造屏障。你继续填满水,建造障碍,直到所有的山峰都在水下。然后你创建的屏障将返回你的分割结果。

但是这种方法会由于图像中的噪声或其他不规则性而产生过度分割的结果。因此OpenCV实现了一个基于标记的分水岭算法,你可以指定哪些是要合并的山谷点,哪些不是。这是一个交互式的图像分割。我们所做的是给我们知道的对象赋予不同的标签。用一种颜色(或强度)标记我们确定为前景或对象的区域,用另一种颜色标记我们确定为背景或非对象的区域,最后用0标记我们不确定的区域。这是我们的标记。然后应用分水岭算法。然后我们的标记将使用我们给出的标签进行更新,对象的边界值将为-1。

在这里插入图片描述

cv2.watershed

使用分水岭算法实现基于标记的图像分割

watershed(image, markers) -> markers
  • image:8位3通道图像
  • markers:标记(输入/输出的32位单通道图像,大小与图像一致)

示例

def watershed_image(image):
    """分水岭算法"""
    # 图像二值化
    blurred = cv.pyrMeanShiftFiltering(image, 10, 50)  # 均值迁移滤波
    gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)  # 转换成灰度图
    # cv.imshow("gray", gray)
    ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)  # 图像二值化
    # cv.imshow("binary", binary)

    # 去除噪声
    kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))  # 构造25×25的方形结构元素
    opening = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel=kernel, iterations=2)  # 开操作(需要去除图像中的任何白点噪声),迭代次数2
    # cv.imshow("noise removal", opening)

    # 确定背景区域sure_bg
    sure_bg = cv.dilate(opening, kernel, iterations=3)  # 腐蚀,迭代次数3,会去除边界像素
    cv.imshow("sure_bg", sure_bg)

    # 寻找前景区域sure_fg
    """ 距离变换的基本含义是计算一个图像中非零像素点到最近的零像素点的距离,也就是到零像素点的最短距离
    一个最常见的距离变换算法就是通过连续的腐蚀操作来实现,腐蚀操作的停止条件是所有前景像素都被完全腐蚀。
    这样根据腐蚀的先后顺序,我们就得到各个前景像素点到前景中心像素点的距离。根据各个像素点的距离值,设置
    为不同的灰度值。这样就完成了二值图像的距离变换。
    cv2.distanceTransform(src, distanceType, maskSize)
    distanceType为距离类型CV_DIST_L1, CV_DIST_L2 , CV_DIST_C;maskSize为距离转换掩码的大小
    """
    dist_transform = cv.distanceTransform(opening, cv.DIST_L2, 5)  # 距离变换
    dist_output = cv.normalize(dist_transform, 0, 1.0, cv.NORM_MINMAX)  # 矩阵归一化,主要是为了显示出dist_output
    cv.imshow("dist_transform", dist_output*50)  # dist_output不乘50看不出来
    ret, sure_fg = cv.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)  # 图像二值化
    cv.imshow("sure_fg", sure_fg)

    # 找到未知的区域unknown
    sure_fg = np.uint8(sure_fg)
    unknown = cv.subtract(sure_bg, sure_fg)  # 从sure_bg区域中减去sure_fg区域来获得unknown
    cv.imshow("unknown", unknown)

    # 类别标记
    ret, markers1 = cv.connectedComponents(sure_fg)
    print(ret)  # 计算数量,但此时会把图像边框也算进去,因此ret会多1
    # print(markers1)

    # 为所有的标记加1,保证背景是0而不是1
    markers = markers1 + 1
    # print(markers)
    
    # 现在让所有的未知区域为0
    markers[unknown == 255] = 0

    # 使用分水岭算法
    markers3 = cv.watershed(image, markers=markers)  # 边界区域将被修改标记为-1
    image[markers3 == -1] = [0, 0, 255]  # 边界区域画红色
    # print(markers3)

    cv.imshow("result", image)

结果:
在这里插入图片描述

参考链接:

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咬着棒棒糖闯天下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值