基于OpenCv的图像分割(分水岭算法)

图像分割

图像分割对于图像处理和计算机视觉领域非常重要,可以用于对象识别、图像分析、图像压缩等应用。
注意:通常我们把前景目标的灰度值设为255,即白色,背景的灰度值设为0,即黑色。所以定义中的非零像素点即为前景目标,零像素点即为背景。
所以图像中前景目标中的像素点距离背景越远,那么距离就越大,如果我们用这个距离值替换像素值,那么新生成的图像中这个点越亮。

distanceTransform()

cv.distanceTransform()是OpenCV中的一个函数,用于计算二值图像中每个像素与离它最近的零像素之间的距离。dst = cv.distanceTransform(src, distanceType, maskSize)

其中,参数src是输入的二值图像,distanceType是距离类型,maskSize是距离计算模板的大小,dst是输出的距离变换图像。 distanceType参数指定了距离计算的类型,包括以下几种取值:

  • cv.DIST_L1: L1距离,也称曼哈顿距离。
  • cv.DIST_L2: L2距离,也称欧几里得距离。
  • cv.DIST_C: C距离,也称棋盘距离。
  • cv.DIST_L12: L1-L2混合距离,即先使用L1距离,再使用L2距离。
  • cv.DIST_FAIR: Fair距离。
  • cv.DIST_WELSCH: Welsch距离。
  • cv.DIST_HUBER: Huber距离。 maskSize参数指定了距离计算模板的大小,可以取3、5或者cv.CV_DIST_MASK_PRECISE。当maskSize为3或者5时,距离计算模板为3x3或5x5的矩形;当maskSize为cv.CV_DIST_MASK_PRECISE时,距离计算模板为精确的距离变换模板。

connectedComponents()

cv.connectedComponents()是OpenCV中的一个函数,用于对二值图像进行连通组件标记。该函数会将具有相同像素值的像素标记为同一个连通组件,并给每个连通组件分配一个唯一的标记。
num_labels, labels = cv.connectedComponents(image[, connectivity[, ltype]])

其中,参数image是输入的二值图像,connectivity是连通性,ltype是输出标签图像的数据类型,num_labels是标签的数量,labels是输出的标签图像。 connectivity参数指定了像素之间的连通规则,可以取4或8。当connectivity为4时,只考虑上下左右四个方向的像素;当connectivity为8时,还考虑对角线方向的像素。 ltype参数指定了输出标签图像的数据类型,可以取cv.CV_32S或cv.CV_16U。当标签数量很大时,应该使用cv.CV_32S类型,否则可以使用cv.CV_16U类型。 使用cv.connectedComponents()函数可以对二值图像进行连通组件标记,得到每个连通组件的标签,并可以根据标签提取连通组件。这个函数在图像分割、对象检测和跟踪等领域中广泛应用。

watershed()

cv.watershed()是OpenCV中的一个函数,用于实现分水岭算法,即对图像进行分割。该函数将图像看做一个地形图,将图像中的亮度值看做高度,然后在该地形图上进行分水岭分割。
markers = cv.watershed(image, markers)

其中,参数image是输入的待分割图像,markers是标记图像,表示原始图像中的每个像素所属的初始标记。初始标记可以是-1或0,-1表示不确定区域,0表示背景区域。函数执行完毕后,markers会被更新为分割后的标记图像,即表示原始图像中每个像素所属的分割区域。 使用cv.watershed()函数实现分水岭算法需要经过以下几个步骤:

  1. 对原始图像进行预处理,如去噪、平滑等。
  2. 对预处理后的图像进行二值化处理,得到二值图像。
  3. 对二值图像进行距离变换,得到距离变换图像。
  4. 对距离变换图像进行阈值处理,得到初始标记图像。
  5. 对初始标记图像进行分水岭分割,得到分割结果。
  6. 对分割结果进行后处理,如去除小区域、填充空洞等。 cv.watershed()函数可以用于图像分割、对象检测和跟踪等领域中,该函数的分割结果通常比较准确,但是容易受到噪声的影响,需要经过一定的调试才能得到较好的分割结果。

基于OpenCV的图像分割流程图

在这里插入图片描述

完整代码:

import numpy as np
import cv2 as cv
import matplotlib.pyplot as  plt
img = cv.imread('img/iron.png')
print(img.shape)# (393, 320, 3)
# 对图像颜色进行转换
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 阈值分割,将图像分为黑白两部分
ret,thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
# 去除图像中的任何白点噪声,使用形态学扩张,
kernel = np.ones((3,3),np.uint8)# 定义卷积核
# 对图像进行开运算:先腐蚀,再膨胀
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel,iterations=2)
# 注意:靠近对象中心的区域是前景目标,而离对象中心很远的区域是背景
# 确定背景区域:对开运算的结果进行膨胀,得到大部分都是背景的区域
sure_bg = cv.dilate(opening,kernel,iterations=2)# 膨胀操
# 距离变换,寻找前景区域:通过distanceTransform
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
print(dist_transform.max())# 30个像素点
# 获取边界
ret,sure_fg = cv.threshold(dist_transform,0.5*dist_transform.max(),255,0)
# 获得边界区域,找到未知区域
sure_fg = np.uint8(sure_fg)
unknow = cv.subtract(sure_bg,sure_fg)
# 类别标记,将具有相同像素值的像素标记为同一个连通组件,ret对连同区域进行标号
ret,markers = cv.connectedComponents(sure_fg)
print(markers.shape)# (393, 320)
# 将图像矩阵保存为文件
np.savetxt("image.txt", markers)
print(markers==1)
# 为所有的标记加1,保证背景是0而不是1
markers = markers+1
# 让所有的未知区域为0
markers[unknow==255] = 0
# 实施分水岭算法,标签图将会被修改,边界区域的标记将变为-1
markers = cv.watershed(img,markers)
img[markers == -1] = [255,0,0]
# 图片展示
plt.subplot(241), plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB)),
plt.title('Original'), plt.axis('off')
plt.subplot(242), plt.imshow(thresh, cmap='gray'),
plt.title('Threshold'), plt.axis('off')
plt.subplot(243), plt.imshow(sure_bg, cmap='gray'),
plt.title('Dilate'), plt.axis('off')
plt.subplot(244), plt.imshow(dist_transform, cmap='gray'),
plt.title('Dist Transform'), plt.axis('off')
plt.subplot(245), plt.imshow(sure_fg, cmap='gray'),
plt.title('Threshold'), plt.axis('off')
plt.subplot(246), plt.imshow(unknow, cmap='gray'),
plt.title('Unknow'), plt.axis('off')
plt.subplot(247), plt.imshow(np.abs(markers), cmap='jet'),
plt.title('Markers'), plt.axis('off')
plt.subplot(248), plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB)),
plt.title('Result'), plt.axis('off')
plt.show()

在这里插入图片描述

查看图像的矩阵

方式一: 将图像矩阵保存为文件

# 将图像矩阵保存为文件
np.savetxt("image.txt", markers)

方式二:通过DeBug进行查看

在需要查看数据代码的左边,点击进行断点,然后点击debug模式启动,当运行完该行之后,需要的数据会出现在Variables中,我们可以点击查看,但是这样往往是从整体上进行查看,而不能查看具体的矩阵内容,我们可以进行如下操作:
在这里插入图片描述
操作后,可以看到VciView中可以看到
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值