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

发布时间:2024年01月22日

目录

一、分水岭算法

二、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()

参考:图像分割

文章来源:https://blog.csdn.net/qq_45100200/article/details/135749193
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。