内容来自OpenCV-Python Tutorials 自己翻译整理
目标:
GrabCut算法原理与应用
创建交互式程序完成前景提取
原理:
首先用矩形将要选择的前景区域选定,其中前景区域应该完全包含在矩形框当中。然后算法进行迭代式分割,知道达到效果最佳。但是有时分割结果不好,例如前景当成背景,背景当成前景。测试需要用户修改。用户只需要在非前景区域用鼠标划一下即可。
如文档中的图片,运动员和足球被蓝色矩形保卫,其中有数个用白色标记修改的,表示前景区域,黑色表示背景区域。
首先,输入矩形框,矩形框外部区域都是背景。内部一定包含前景。
电脑对输入图像进行初始化,标记前景和背景的像素。
使用高斯混合模型(GMM)对前景和背景建模。
根据输入,GMM会学习并创建新的像素分布。对未知的像素(前景或背景不确定),根据他们与已知的分类像素关系进行分类。(类似聚类操作)
这样会根据像素的分布创建一幅图,图中节点是像素。除了像素点是节点以外,还有Source_node和Sink_node两个节点。所有的前景图像斗鱼Source_node相连。背景与Sink_node相连。
像素是否连接到Source_node/end_node依赖于权值,这个权值由像素属于同一类,也就是前景或者背景的概率来决定。如果像素的颜色有很大区别,那么他们之间的权重就很小。
使用mincut算法对图像进行分割。它会根据最小代价方程对图像分成source_node和sink_node。代价方程是指裁剪所有边上权重的和。裁剪完成后,所有连接到source_node的判定为前景,sink_node上的为背景。
继续此过程,直到分类收敛。
示例:
cv2.grabCut()函数参数
- img 输入图像
- mask 蒙板图像,确定前景区域,背景区域,不确定区域,可以设置为cv2.GC_BGD,cv2.GC_FGD,cv2.GC_PR_BGD,cv2.GC_PR_FGD,也可以输入0,1,2,3
- rect 前景的矩形,格式为(x,y,w,h),分别为左上角坐标和宽度,高度
- bdgModel, fgdModel 算法内部是用的数组,只需要创建两个大小为(1,65)np.float64的数组。
- iterCount 迭代次数
- mode cv2.GC_INIT_WITH_RECT 或 cv2.GC_INIT_WITH_MASK,使用矩阵模式还是蒙板模式。
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('1.jpg')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (50,50,450,290)#划定区域
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)#函数返回值为mask,bgdModel,fgdModel
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')#0和2做背景
img = img*mask2[:,:,np.newaxis]#使用蒙板来获取前景区域
cv2.imshow('p',img)
cv2.waitKey(0)
计算完成后mask里面值为0到3,其中0表示背景,1表示前景,2表示可能是背景,3表示可能是前景
代码中将0和2合并为背景 1和3合并为前景
样例中的图片结果如下
可以看到得到的结果并不准确,现在在这幅图片得到的蒙板上面添加标记,用白色(像素值为0)标记前景,黑色(像素值为255)标记背景。再次进行迭代计算,最后得到不错的结果
样例中的图片如下