图像分割python代码_分水岭算法图像分割(OpenCV+Python)

本文内容是对Opencv官方文档的学习笔记

原理

我们可以将任何一幅灰度图都看作是拓扑平面,灰度图中灰度值高的看作是山峰,灰度值地的看作是山谷,我们向每一个山谷中灌入不同颜色的水,随着水位的升高,不同山谷的水就会相遇汇合,而为了防止不同山谷的水汇合,我们需要在水汇合的地方建起堤坝,随着不停地灌水,不停地构建堤坝,直到所有的山峰都被水淹没。我们构建好的堤坝就是对图像的分割。

但是由于噪声或者图像中其他不规律的因素,这种方法可能会得到过度分割的效果。为减少这种影响,OpenCV采用基于掩模的分水岭算法,在这种算法中我们要设置哪些山谷点会汇合,哪些不会。这是一种交互式的图像分割。我们要做的就是给我们已知的对象打上不同的标签。如果某个区域肯定是前景或对象,就使用某个颜色(或灰度值)标签标记它。如果某个区域肯定不是对象而是背景就使用另外一个颜色标签标记。而剩下的不能确定是前景还是背景的区域就用 0 标记。这就是我们的标签。然后实施分水岭算法。每一次灌水,我们的标签就会被更新,当两个不同颜色的标签相遇时就构建堤坝,直到将所有山峰淹没,最后我们得到的边界对象(堤坝)的值为 -1。

代码示例

在下面的例子中,我们将距离变换和分水岭算法紧挨在一起的对象进行分割。

看一下原图:

original

先从硬币的近似估值开始,我们先使用二值化cv2.threshold函数对图像进行处理

import numpy as np

import cv2

from matplotlib import pyplot as plt

img = cv2.imread('water_coins.jpg')

gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#转化成灰度图

ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

#二值化

#看一下图像

cv2.imshow('orig',img)

cv2.imshow('thresh',thresh)

cv2.waitKey(0)

cv2.destroyAllWindows()

现在我们开运算去除图像中的所有白噪声。靠近对象中心的区域肯定是前景,而远离对象中心的区域肯定是背景,不能确定的部分为硬币之间的边界。

我们要提取肯定是硬币的区域,距离变换+合适的阈值。接下来我们要找肯定不是硬币的区域,用膨胀操作,膨胀操作将对象的边界延伸到了背景中,所以由于边界区域被处理,我们就能知道哪些区域是前景,哪些区域是背景。剩下的区域就是我们不知道该如何区分了,而这就是分水岭算法要做的,前景与背景的交界处就是边界,从肯定是不是背景的区域中减去肯定是前景的区域就是边界区域。

#去除噪音

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,1,5)

ret,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)

cv2.imshow('sure_bg',sure_bg)

cv2.imshow('sure_fg',sure_fg)

cv2.imshow('unknow',unknow)

cv2.waitKey(0)

cv2.destroyAllWindows()

2

在上面的代码中,阈值化以后得到的就是硬币的区域。

现在知道了哪些是背景,哪些是硬币,我们就可以创建标签了(一个与原图像大小相同,数据类型为int32的数组)并标记其中的区域,用函数 cv2.connectedComponents()来操作,将背景标记为0,其他对象使用从1开始的正整数标记。对不确定的区域(函数cv2.connectedComponents()输出的结果使用unknown定义未知区域)标记为0。

# Marker labelling

ret,markers1=cv2.connectedComponents(sure_fg)

# Add one to all labels so that sure background is not 0, but 1

markers = markers1+1

# Now, mark the region of unknown with zero

markers[unknown==255] = 0

深蓝色区域为未知区域。肯定是硬币的区域

使用不同的颜色标记。其余区域就是用浅蓝色标记的背景了。

现在标签准备好了。到最后一步:实施分水岭算法了。标签图像将会被修改,边界区域的标记将变为 -1.

markers3 = cv2.watershed(img,markers)

img[markers3 == -1] = [255,0,0]

cv2.imshow('img',img)

cv2.waitKey(0)

cv2.destroyAllWindows()

结果如下。有些硬币的边界被分割的很好,也有一些硬币之间的边界分割的不好。

3

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于分水岭算法图像分割是一种常用的图像处理技术,可以将图像分割成多个区域,每个区域内的像素具有相似的特征。在 OpenCV 中,可以使用 cv2.watershed() 函数实现基于分水岭算法图像分割。 下面是一个简单的 Python 示例,演示如何使用基于分水岭算法图像分割: ```python import cv2 import numpy as np # 读取图像 img = cv2.imread('image.jpg') # 转换为灰度图像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 阈值分割 ret, thresh = cv2.threshold(gray, 0, 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) # 距离变换 dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5) ret, sure_fg = cv2.threshold(dist_transform,0.1*dist_transform.max(),255,0) # 背景区域 sure_bg = cv2.dilate(opening,kernel,iterations=3) # 不确定区域 sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg,sure_fg) # 标记连通区域 ret, markers = cv2.connectedComponents(sure_fg) markers = markers + 1 markers[unknown==255] = 0 # 应用分水岭算法 markers = cv2.watershed(img,markers) img[markers == -1] = [255,0,0] # 显示结果 cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在上面的示例中,首先读取一张图像,并将其转换为灰度图像。然后使用阈值分割算法将图像二值化。接下来,进行形态学操作,以去除图像中的噪声。然后使用距离变换算法计算前景区域,并将其阈值化。接着,使用形态学操作计算背景区域。最后,使用 cv2.connectedComponents() 函数计算不确定区域,并使用标记连通区域的方法生成分水岭算法的输入标记图像。最后,应用 cv2.watershed() 函数进行图像分割,并在窗口中显示结果。 需要注意的是,分水岭算法的结果依赖于输入标记图像的质量,因此需要根据具体情况进行调整,比如阈值分割的参数、形态学操作的参数、距离变换的参数等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值