作业目标
算法简介
- 算法主要基于以下知识:
k均值聚类
高斯混合模型建模(GMM)
max flow/min cut
- GrabCut算法的实现步骤:
- 在图片中定义(一个或者多个)包含物体的矩形。 矩形外的区域被自动认为是背景。
- 对于用户定义的矩形区域,可用背景中的数据来区分它里面的前景和背景区域。
- 用高斯混合模型(GMM)来对背景和前景建模,并将未定义的像素标记为可能的前景或者背景。
- 图像中的每一个像素都被看做通过虚拟边与周围像素相连接,而每条边都有一个属于前景或者背景的概率,这是基于它与周边像素颜色上的相似性。
- 每一个像素(即算法中的节点)会与一个前景或背景节点连接。
- 在节点完成连接后(可能与背景或前景连接),若节点之间的边属于不同终端(即一个节点属于前景,另一个节点属于背景),则会切断他们之间的边,这就能将图像各部分分割出来。
实现代码
import numpy as npimport cv2# 鼠标事件的回调函数def on_mouse(event, x, y, flag, param):global rectglobal leftButtonDowmglobal leftButtonUp# 鼠标左键按下if event == cv2.EVENT_LBUTTONDOWN:rect[0] = xrect[2] = xrect[1] = yrect[3] = yleftButtonDowm = TrueleftButtonUp = False# 移动鼠标事件if event == cv2.EVENT_MOUSEMOVE:if leftButtonDowm and not leftButtonUp:rect[2] = xrect[3] = y# 鼠标左键松开if event == cv2.EVENT_LBUTTONUP:if leftButtonDowm and not leftButtonUp:x_min = min(rect[0], rect[2])y_min = min(rect[1], rect[3])x_max = max(rect[0], rect[2])y_max = max(rect[1], rect[3])rect[0] = x_minrect[1] = y_minrect[2] = x_maxrect[3] = y_maxleftButtonDowm = FalseleftButtonUp = True# 读入图片img = cv2.imread('messi5.jpg')# 掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果mask = np.zeros(img.shape[:2], np.uint8)# 背景模型,如果为None,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5;bgdModel = np.zeros((1, 65), np.float64)# fgdModel——前景模型,如果为None,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5;fgdModel = np.zeros((1, 65), np.float64)# 用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;rect = [0, 0, 0, 0]# 鼠标左键按下leftButtonDowm = False# 鼠标左键松开leftButtonUp = True# 指定窗口名来创建窗口cv2.namedWindow('img')# 设置鼠标事件回调函数 来获取鼠标输入cv2.setMouseCallback('img', on_mouse)# 显示图片cv2.imshow('img', img)while cv2.waitKey(2) == -1:# 左键按下,画矩阵if leftButtonDowm and not leftButtonUp:img_copy = img.copy()# 在img图像上,绘制矩形 线条颜色为green 线宽为2cv2.rectangle(img_copy, (rect[0], rect[1]), (rect[2], rect[3]), (0, 255, 0), 2)# 显示图片cv2.imshow('img', img_copy)# 左键松开,矩形画好elif not leftButtonDowm and leftButtonUp and rect[2] - rect[0] != 0 and rect[3] - rect[1] != 0:# 转换为宽度高度rect[2] = rect[2] - rect[0]rect[3] = rect[3] - rect[1]rect_copy = tuple(rect.copy())rect = [0, 0, 0, 0]# 物体分割cv2.grabCut(img, mask, rect_copy, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')img_show = img * mask2[:, :, np.newaxis]# 显示图片分割后结果cv2.imshow('grabcut', img_show)# 显示原图cv2.imshow('img', img)cv2.waitKey(0)cv2.destroyAllWindows()
实验结果
| 原始图片 | ![]() |
|---|---|
| 勾选目标区域 | ![]() |
| 分割结果 | ![]() |
分割出来的效果不是很好,脚下的草可以通过写代码去掉特定的颜色,但是考虑到这样做没有普遍性,换一张图就完全行不通了,于是没有写。



