您现在的位置是:首页 >学无止境 >基于OpenCv的图像分割(分水岭算法)网站首页学无止境

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

WH_Deng 2023-05-13 12:00:02
简介基于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中可以看到
在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。