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

随着当今世界的发展,计算机视觉技术的应用越来越广泛。伴随着硬件设备的不断升级,构造复杂的计算机视觉应用变得越来越容易了。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的像素点)的距离关系。通常情况下:

  • 如果前景对象的中心距离值为0的像素点距离较远,会得到一个较大的值。
  • 如果前景对象的边缘距离值为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()

运行之后,效果如下:

左上为原图

右上为原图膨胀后得到的图像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。效果如下:

实战分水岭算法

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

  • 通过形态学开运算对原始图像0进行去噪
  • 通过腐蚀操作获取“确定背景B”。需要注意,这里得到“原始图像-确定背景”即可
  • 利用距离变换函数对原始图像进行运算,并对其进行阈值处理,得到“确定前景F”
  • 计算未知区域UN(UN=0-B-F)
  • 利用函数cv2.connectedComponents()对原始图像0进行标注
  • 对函数cv2.connectedComponents()的标注结果进行修正
  • 使用分水岭函数完成图像分割

完整代码如下:

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图像分割与提取内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python用opencv完成图像分割并进行目标物的提取

    运行平台: Windows Python版本: Python3.x IDE: Spyder 今天我们想实现的功能是对单个目标图片的提取如图所示: 图片读取 ###############头文件 import matplotlib.pyplot as plt import os import cv2 import numpy as np from PIL import Image #from skimage import io import random from PIL import Image

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

    随着当今世界的发展,计算机视觉技术的应用越来越广泛.伴随着硬件设备的不断升级,构造复杂的计算机视觉应用变得越来越容易了.OpenCV像是一个黑盒,让我们专注于视觉应用的开发,而不必过多的关注基础图象处理的具体细节. 图像分割 了解分水岭算法之前,我们需要了解什么是图像的分割. 在图像的处理过程中,经常需要从图像中将前景对象作为目标图像分割或者提取出来.例如,在视频监控中,观测到的是固定背景下的视频内容,而我们对背景本身并无兴趣,感兴趣的是背景中出现的车辆,行人或者其他对象.我们希望将这些对象从视

  • OpenCV图像分割之分水岭算法与图像金字塔算法详解

    目录 前言 一.使用分水岭算法分割图像 1.cv2.distanceTransform()函数 2.cv2.connectedComponents()函数 3.cv2.watershed()函数 二.图像金字塔 1.高斯金字塔向下采样 2.高斯金字塔向上采样 3.拉普拉斯金字塔 4.应用图像金字塔实现图像的分割和融合 前言 主要介绍OpenCV中的分水岭算法.图像金字塔对图像进行分割的方法. 一.使用分水岭算法分割图像 分水岭算法的基本原理为:将任意的灰度图像视为地形图表面,其中灰度值高的部分表

  • C++中实现OpenCV图像分割与分水岭算法

    分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征. API介绍 void watershed( InputArray image, InputOutputArray markers ); 参数说明: image: 必须是一个8bit 3通道彩色图像矩阵序列 markers: 在执行分水岭函数watershed之前,必须对第二个参数markers

  • Python基于分水岭算法解决走迷宫游戏示例

    本文实例讲述了Python基于分水岭算法解决走迷宫游戏.分享给大家供大家参考,具体如下: #Solving maze with morphological transformation """ usage:Solving maze with morphological transformation needed module:cv2/numpy/sys ref: 1.http://www.mazegenerator.net/ 2.http://blog.leanote.com

  • opencv+python实现鼠标点击图像,输出该点的RGB和HSV值

    我就废话不多说了,大家还是直接看代码吧! import cv2 # 读取图片并缩放方便显示 img = cv2.imread('D:/6.jpg') height, width = img.shape[:2] size = (int(width * 0.2), int(height * 0.2)) # 缩放 img = cv2.resize(img, size, interpolation=cv2.INTER_AREA) # BGR转化为HSV HSV = cv2.cvtColor(img, c

  • python数字图像处理之骨架提取与分水岭算法

    骨架提取与分水岭算法也属于形态学处理范畴,都放在morphology子模块内. 1.骨架提取 骨架提取,也叫二值图像细化.这种算法能将一个连通区域细化成一个像素的宽度,用于特征提取和目标拓扑表示. morphology子模块提供了两个函数用于骨架提取,分别是Skeletonize()函数和medial_axis()函数.我们先来看Skeletonize()函数. 格式为:skimage.morphology.skeletonize(image) 输入和输出都是一幅二值图像. 例1: from s

  • OpenCV半小时掌握基本操作之分水岭算法

    [OpenCV]⚠️高手勿入! 半小时学会基本操作 ⚠️ 分水岭算法 概述 OpenCV 是一个跨平台的计算机视觉库, 支持多语言, 功能强大. 今天小白就带大家一起携手走进 OpenCV 的世界. 分水岭算法 分水岭算法 (Watershed Algorithm) 是一种图像区域分割算法. 在分割的过程中, 分水岭算法会把跟临近像素间的相似性作为重要的根据. 分水岭分割流程: 读取图片 转换成灰度图 二值化 距离变换 寻找种子 生成 Marker 分水岭变换 距离变换 距离变换 (Distan

  • C++实现分水岭算法(Watershed Algorithm)

    分水岭分割方法(Watershed Segmentation),是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭.分水岭的概念和形成可以通过模拟浸入过程来说明.在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭. 分水岭的计算过程是一个迭代标

  • python opencv之分水岭算法示例

    本文介绍了python opencv之分水岭算法示例,分享给大家,具体如下: 目标 使用分水岭算法对基于标记的图像进行分割 使用函数cv2.watershed() 原理: 灰度图像可以被看成拓扑平面,灰度值高的区域可以看出山峰,灰度值低的区域可以看成是山谷.向每一个山谷当中灌不同颜色的水.水位升高,不同山谷的水会汇合,为防止不同山谷的水汇合,小在汇合处建立起堤坝.然后继续灌水,然后再建立堤坝,直到山峰都掩模.构建好的堤坝就是图像的分割. 此方法通常会得到过渡分割的结果,因为图像中的噪声以及其他因

  • Python OpenCV基于霍夫圈变换算法检测图像中的圆形

    目录 第一章:霍夫变换检测圆 ① 实例演示1 ② 实例演示2 ③ 霍夫变换函数解析 第二章:Python + opencv 完整检测代码 ① 源代码 ② 运行效果图 第一章:霍夫变换检测圆 ① 实例演示1 这个是设定半径范围 0-50 后的效果. ② 实例演示2 这个是设定半径范围 50-70 后的效果,因为原图稍微大一点,半径也大了一些. ③ 霍夫变换函数解析 cv.HoughCircles() 方法 参数分别为:image.method.dp.minDist.param1.param2.mi

随机推荐