基于OpenCV的图像分割(分水岭算法和GrabCut)

目录

一、分水岭算法

二、GrabCut


一、分水岭算法

res = cv.watershed(image,markers)

参数: 

  • image: 输入图像,必须是8位的3通道彩色图像
  • marker: 标记图像,32位单通道图像,它包括种子点信息,使用轮廓信息作为种子点。在进行分水岭算法之前,必须设置好marker信息,它包含不同区域的轮廓,每个轮廓有唯一的编号,使用findCountours方法确定轮廓位置,不同区域的交界位置为-1

返回:

  • res: 图像分割之后的结果

自动分割的步骤:

  1. 对原图像进行灰度化处理,并进行边缘检测或二值化
  2. 查找轮廓,并且把轮廓信息按不同的编号绘制在标记图像上,即标记种子点,将其传给marker参数
  3. 进行分水岭算法检测
  4. 绘制分割出来的区域,使用随机颜色进行填充

代码:

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

# 1.读入图片
img = cv.imread(r"D:\Desktop\00aa\1.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

# 2.canny边缘检测
canny = cv.Canny(gray_img, 80, 150)

# 3.轮廓检测并设置标记图像
# 寻找图像轮廓 返回修改后的图像 图像的轮廓 以及它们的层次
contours, hierarchy = cv.findContours(canny, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 32位有符号整数类型,
marks = np.zeros(img.shape[:2], np.int32)
# findContours检测到的轮廓
imageContours = np.zeros(img.shape[:2], np.uint8)

# 轮廓颜色
compCount = 0
index = 0
# 绘制每一个轮廓
for index in range(len(contours)):
    # 对marks进行标记,对不同区域的轮廓使用不同的亮度绘制,相当于设置注水点,有多少个轮廓,就有多少个轮廓
    # 图像上不同线条的灰度值是不同的,底部略暗,越往上灰度越高
    marks = cv.drawContours(marks, contours, index, (index, index, index), 1, 8, hierarchy)
    # 绘制轮廓,亮度一样
    imageContours = cv.drawContours(imageContours, contours, index, (255, 255, 255), 1, 8, hierarchy)

# 4 使用分水岭算法,并给不同的区域随机填色
# cv.watershed(输入图像,标记图像)
marks = cv.watershed(img, marks)
# 实现图像增强等相关操作的快速运算  图像sobely转换回灰度
afterWatershed = cv.convertScaleAbs(marks)

# 生成随机颜色
colorTab = np.zeros((np.max(marks) + 1, 3))
# 生成0~255之间的随机数
for i in range(len(colorTab)):
    aa = np.random.uniform(0, 255)
    bb = np.random.uniform(0, 255)
    cc = np.random.uniform(0, 255)
    colorTab[i] = np.array([aa, bb, cc], np.uint8)

bgrImage = np.zeros(img.shape, np.uint8)

# 遍历marks每一个元素值,对每一个区域进行颜色填充
for i in range(marks.shape[0]):
    for j in range(marks.shape[1]):
        # index值一样的像素表示在一个区域
        index = marks[i][j]
        # 判断是不是区域与区域之间的分界,如果是边界(-1),则使用白色显示
        if index == -1:
            bgrImage[i][j] = np.array([255, 255, 255])
        else:
            bgrImage[i][j] = colorTab[index]
# 5 图像显示
plt.imshow(bgrImage[:, :, ::-1])
plt.title('result')
plt.xticks([]), plt.yticks([])
plt.show()

分割结果如下所示:

二、GrabCut

GrabCut算法的优势是:

1、只需要在目标外面画一个框,把目标框住,它就可以完成良好的分割:

2、如果增加额外的用户交互(由用户指定一些像素属于目标),那么效果会更好

3、它的边界处理技术会使目标分割边界更加自然和柔和:

该算法也有不完美的地方,如果背景比较复杂或者背景和目标的相似度很大,那分割效果就不好,而且该算法的速度较慢。

在openCV中实现GrabCut算法,使用的API:

grabCut(img,mask,rect,bgdModel,fgdModel,iterCount,mode )

参数:

  • img:输入图像,必须是8位的3通道彩色图像
  • mask: 掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:
  • rect:用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理
  • bgdModel:背景模型,如果为None,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5
  • fgdModel:前景模型,如果为None,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5
  • iterCount:迭代次数,必须大于0
  • mode:指明grabcut函数进行哪种操作,如下所示: ​​​​​​

代码演示:

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

# 1. 读取图片
img = cv.imread(r"D:\Desktop\00aa\1.png")

# 2. 掩码图像
mask = np.zeros(img.shape[:2], np.uint8)

print(img.shape[:2])
# 3.矩形窗口(x,y,w,h);
rect = [0, 0, 389, 582] # 全图显示,但是要注意,利用img.shape[:2]获取图像大小后,维度都需要减1才能填入w,h。利用shape获取的维度是h,w

# 4.物体分割
cv.grabCut(img, mask, tuple(rect), None, None, 5, cv.GC_INIT_WITH_RECT)

# 5.抠取图像
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img_show = img * mask2[:, :, np.newaxis]

# 将矩形框绘制在图像上
cv.rectangle(img, (0, 0), (389, 582), (0, 255, 0), 3)

# # 6.图像显示
plt.figure(figsize=(10, 8), dpi=100)
plt.subplot(121), plt.imshow(img[:, :, ::-1]), plt.title('矩形框选位置')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(img_show[:, :, ::-1]), plt.title('抠取结果')
plt.xticks([]), plt.yticks([])
plt.show()

参考:图像分割

  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 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
发出的红包

打赏作者

清纯世纪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值