文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

OpenCV-Python使用分水岭算法实现图像的分割与提取

2024-04-02 19:55

关注

随着当今世界的发展,计算机视觉技术的应用越来越广泛。伴随着硬件设备的不断升级,构造复杂的计算机视觉应用变得越来越容易了。OpenCV像是一个黑盒,让我们专注于视觉应用的开发,而不必过多的关注基础图象处理的具体细节。

图像分割

了解分水岭算法之前,我们需要了解什么是图像的分割。

在图像的处理过程中,经常需要从图像中将前景对象作为目标图像分割或者提取出来。例如,在视频监控中,观测到的是固定背景下的视频内容,而我们对背景本身并无兴趣,感兴趣的是背景中出现的车辆,行人或者其他对象。我们希望将这些对象从视频中提取出来,而忽略那些没有对象进入背景的视频内容。

分水岭算法

图像分割是图像处理过程中一种非常重要的操作。分水岭算法将图像形象地比喻为地理学上的地形表面,实现图像分割,该算法非常有用。

下面,博主对分水岭算法的相关内容做简单的介绍。(详细可以参考冈萨雷斯的《数字图像处理》一书)

任何一副灰度图像,都可以被看作是地理学上的地形表面,灰度值越高的区域可以被看成是山峰,灰度值越低的区域可以被看成是山谷。

如果我们向每个山谷中灌注不同颜色的水。那么随着水位的不断升高,不同山谷的水就汇聚到一起。在这个过程中,为了防止不同山谷的水交汇,我们需要在水流可能汇合的地方构建堤坝。该过程将图像分为两个不同的集合:集水盆地和分水岭线。我们构建的堤坝就是分水岭线,也即对原始图像的分割。这就是分水岭算法的原理。

不过,一般的图像都存在着噪声,采用分水岭算法时,会经常得到过度分割的结果。为了改善图像分割的效果,人们提出了基于掩摸的改进的分水岭算法。改进的分水岭算法允许用户将它认为是同一个分割区域的部分标注出来。这样,分水岭算法在处理时,就会将标注的部分处理为同一个分割区域。

如果对于该理论不怎么了解,可以使用软件PowerPoint中的“删除背景”功能进行观察配合理解。

waterShed函数

在OpenCV中,可以使用函数cv2.watershed()函数实现分水岭算法。不过,具体实现的过程,还需要借助形态学函数,距离变换函数cv2.distanceTransform(),cv2.connectedComponents()来完成图像分割。

形态学分割

在使用分水岭算法之前,我们需要对图像进行简单的形态学处理。一般情况下,我们都是使用形态学中的开运算,因为开运算是先腐蚀后膨胀的操作,能够去除图像内的噪声。


import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("36.jpg")
k=np.ones((5,5),dtype=np.uint8)
e=cv2.erode(img,k)
result=cv2.subtract(img,e)

plt.subplot(131)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(132)
plt.imshow(e, cmap="gray")
plt.axis('off')

plt.subplot(133)
plt.imshow(result, cmap="gray")
plt.axis('off')
plt.show()

回顾一下,我们前面的开运算函数为cv2.erode(),这里我们首先经过开运算去除噪声。然后减法运算cv2.subtract()获取图像边界。运行之后,效果如下:

效果

distanceTransform函数

当图像内的各个子图没有连接时,可以直接使用形态学的腐蚀操作确定前景对象,但是如果图像内的子图连接在一起时,就很难确定前景对象了。这个时候,就需要借助变换函数cv2.distanceTransform()方便地将前景对象提取出来。

cv2.distanceTransform()反应了各个像素点与背景(值为0的像素点)的距离关系。通常情况下:

下面,我们来使用该函数确定一副图像的前景,并观察效果。


import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("36.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
k = np.ones((5, 5), dtype=np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, k, iterations=2)
distTransform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, fore = cv2.threshold(distTransform, 0.7 * distTransform.max(), 255, 0)

plt.subplot(131)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(132)
plt.imshow(distTransform, cmap="gray")
plt.axis('off')

plt.subplot(133)
plt.imshow(fore, cmap="gray")
plt.axis('off')
plt.show()

这里,我们使用cv2.morphologyEx函数进行开运算,同时使用cv2.distanceTransform得到距离图像,最后在通过cv2.threshold对距离图像进行阈值处理,确定前景。运行之后,效果如下:

前景

确定未知区域

通过距离函数,我们获取到了图像的“中心”,也就是“确定前景”。为了方便后续的讲解,我们将确定前景称为F。

图像中有了确定前景F和确定背景B,剩下的区域就是未知区域UN了。这部分区域正是分水岭算法要进一步明确的区域。

针对一副图像0,通过以下关系能够得到未知区域UN:

未知区域UN=图像0-确定背景B-确定前景F

由上述公式变换得到:

未知区域UN=(图像0-确定背景B)-确定前景F

其中(图像0-确定背景B)就是我们开始的减法操作,通过形态学膨胀得到。也只需要将上面的代码添加4行并更改显示的代码内容:


bg=cv2.dilate(opening,k,iterations=3)
fore=np.uint8(fore)
un=cv2.subtract(bg,fore)

plt.subplot(221)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(222)
plt.imshow(bg, cmap="gray")
plt.axis('off')

plt.subplot(223)
plt.imshow(fore, cmap="gray")
plt.axis('off')

plt.subplot(224)
plt.imshow(un, cmap="gray")
plt.axis('off')
plt.show()

运行之后,效果如下:

4附图

左上为原图

右上为原图膨胀后得到的图像bg,其背景图像是确定背景B。前景图像是“原始图像0-确定背景B”

左下为确定前景图像fore

右下为未知区域图像UN

ConnectedComponents函数

明确了确定前景后,就可以对确定前景进行标注了。在OpenCV中,它提供了cv2.ConnectedComponents()函数进行标注。

该函数会将背景标注为0,将其他的对象使用从1开始的正整数标注。它只有一个参数8位单通道的待标注图像。

返回值有两个:retval为返回的标注数量,labels为标注的结果图像。

下面,我们来使用该函数进行标注。代码如下(同样更改上面bg下面代码就行):


bg = cv2.dilate(opening, k, iterations=3)
fore = np.uint8(fore)
ret, markets = cv2.connectedComponents(fore)
unknown=cv2.subtract(bg,fore)
markets=markets+1
markets[unknown==255]=0

plt.subplot(131)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(132)
plt.imshow(fore, cmap="gray")
plt.axis('off')

plt.subplot(133)
plt.imshow(markets, cmap="gray")
plt.axis('off')
plt.show()

修改上面fore = np.uint8(fore)的代码,并修改输出内容。运行之后,我们会得到原图,前景图像的中心点图像fore以及标注后的结果图像markets。效果如下:

效果图

实战分水岭算法

经过前文的介绍,我们了解了使用分水岭算法进行图像分割的基本步骤:

完整代码如下:


import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("36.jpg")
plt.subplot(121)
plt.imshow(img, cmap="gray")
plt.axis('off')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
k = np.ones((5, 5), dtype=np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, k, iterations=2)
distTransform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, fore = cv2.threshold(distTransform, 0.2 * distTransform.max(), 255, 0)
bg = cv2.dilate(opening, k, iterations=3)
fore = np.uint8(fore)
ret, markets = cv2.connectedComponents(fore)
unknown = cv2.subtract(bg, fore)
markets = markets + 1
markets[unknown == 255] = 0
markets = cv2.watershed(img, markets)
img[markets == -1] = [255, 0, 0]

plt.subplot(122)
plt.imshow(img, cmap="gray")
plt.axis('off')
plt.show()

运行之后,我们就可以得到分割的图像:

参数

当然,参数可以调整,可以看到大致的硬币被完整的分割出来了。

到此这篇关于OpenCV-Python使用分水岭算法实现图像的分割与提取的文章就介绍到这了,更多相关OpenCV图像分割与提取内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯