Grabcut图像分割与GMM、KMeans、分水岭分割的区别在于, Grabcut图像分割是用户可以
选择目标图像,然后将剩余的作为背景,目标作为前景进行分割,这样可以把目标提取出来,也就
是抠图;而其他三个分割算法是不需要人为干预的,通过算法将图像分割为几个部分。
Grabcut 算法主要运用于计算机视觉中的前背景分割,立体视觉和抠图等。该算法利用了图像
中的纹理(颜色)信息和边界(反差)信息,只要少量的用户交互操作即可得到比较好的分割结
果.
1. Grabcut 的目标和背景的模型是RGB三通道的混合高斯模型GMM;
2. Grab Cut为一个不断进行分割估计和模型参数学习的交互迭代过程;
3. Grab Cut只需要提供背景区域的像素集就可以了。也就是说你只需要框选目标,那么在方框外的像素全部当成背景,这时候就可以对GMM进行建模和完成良好的分割了。即Grab Cut允许不完全的标注.
opencv中Grabcut的相关API:
void grabCut( InputArray img, //输入图像,必须是8位3通道图像,在处理过程中不会被修改
InputOutputArray mask, //掩码图像,用来确定哪些区域是背景,前景,可能是背景,
可能是前景等
//mask既可以作为输入也可以作为输出。作为输入时,mode要
选择GC_INIT_WITH_MASK (=1);
GCD_BGD (=0), 背景; GCD_FGD (=1),前景;
GCD_PR_BGD (=2),可能是背景; GCD_PR_FGD(=3),可能是前景
如果没有手工标记GCD_BGD 或者GCD_FGD,那么结果只会有 GCD_PR_BGD 和 GCD_PR_FGD
Rect rect, //包含前景的矩形,格式为(x, y, w, h)
InputOutputArray bgdModel,//算法内部使用的数组,只需要创建大小为(1,65),
数据类型为np.float64的数组
InputOutputArray fgdModel,//同上
int iterCount, //算法迭代的次数
int mode = GC_EVAL //用来指示grabCut函数进行什么操作
// GC_INIT_WITH_RECT (=0),用矩形窗初始化GrabCut;
// GC_INIT_WITH_MASK (=1),用掩码图像初始化GrabCut
);
有关鼠标操作的两个函数:
void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata=0)
winname:窗口的名字
onMouse:鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。 这个函数的原型应该为void on_Mouse(int event, int x, int y, int flags, void* param);
userdate:传给回调函数的参数
void OnMouseAction( int event, // 代表了鼠标的各种操作
int x, // 代表鼠标位于窗口的(x,y)坐标位置,即Point(x,y)
int y,
int flags, // 代表鼠标的拖拽事件,以及键盘鼠标联合事件
void *ustc // 标识了所响应的事件函数
);
int event:
#define CV_EVENT_MOUSEMOVE 0 //滑动
#define CV_EVENT_LBUTTONDOWN 1 //左键点击
#define CV_EVENT_RBUTTONDOWN 2 //右键点击
#define CV_EVENT_MBUTTONDOWN 3 //中键点击
#define CV_EVENT_LBUTTONUP 4 //左键放开
#define CV_EVENT_RBUTTONUP 5 //右键放开
#define CV_EVENT_MBUTTONUP 6 //中键放开
#define CV_EVENT_LBUTTONDBLCLK 7 //左键双击
#define CV_EVENT_RBUTTONDBLCLK 8 //右键双击
#define CV_EVENT_MBUTTONDBLCLK 9 //中键双击
int flags:
#define CV_EVENT_FLAG_LBUTTON 1 //左鍵拖曳
#define CV_EVENT_FLAG_RBUTTON 2 //右鍵拖曳
#define CV_EVENT_FLAG_MBUTTON 4 //中鍵拖曳
#define CV_EVENT_FLAG_CTRLKEY 8 //(8~15)按Ctrl不放事件
#define CV_EVENT_FLAG_SHIFTKEY 16 //(16~31)按Shift不放事件
#define CV_EVENT_FLAG_ALTKEY 32 //(32~39)按Alt不放事件
Grabcut 算法的代码展示:
/*
图像中对象抠图
*/
#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace std;
using namespace cv;
Mat src, image;
Mat mask, bgModel, fgModel;
const char* winTitle = "input image";
Rect rect;
int num; //迭代的次数
bool init = false;
void onMouse(int event, int x, int y, int flags, void* param);
void showImage();
void setROIMask();
void runGrabCut();
int main(int argc, char** argv)
{
src = imread("E:/技能学习/opencv图像分割/flower.jpg");
if (src.empty())
{
cout << "could not load image!" << endl;
return -1;
}
mask.create(src.size(), CV_8UC1);
mask.setTo(Scalar::all(GC_BGD)); //将mask设置为背景
namedWindow(winTitle, WINDOW_AUTOSIZE);
setMouseCallback(winTitle, onMouse, 0); //鼠标回调函数
imshow(winTitle, src);
while (true)
{
char c = (char)waitKey(0);
if (c == ' ') //选中矩形框后,按空格键执行grabcut分割
{
runGrabCut(); //开始图割算法,运行一次为迭代一次
num++; //迭代次数加1
showImage(); //显示图像
//imshow("背景", bgModel);
//imshow("前景", fgModel);
cout << "current iteative time:" << num << endl;
}
if ((int)c == 27) //检测是否按下esc键
{
break;
}
}
waitKey(0);
destroyAllWindows();
return 0;
}
void onMouse(int event, int x, int y, int flags, void* param)
{
switch (event)
{
case EVENT_LBUTTONDOWN: //鼠标按下
rect.x = x;
rect.y = y;
rect.width = 1;
rect.height = 1;
init = false;
num = 0;
break;
case EVENT_MOUSEMOVE: //鼠标移动
if (flags & EVENT_FLAG_LBUTTON) //鼠标左键拖拽
{
rect = Rect(Point(rect.x, rect.y), Point(x, y));
showImage();
}
break;
case EVENT_LBUTTONUP: //鼠标左边弹起
if (rect.width > 1 && rect.height > 1)
{
showImage();
}
break;
}
}
void showImage()
{
Mat result,binMask;
binMask.create(mask.size(), CV_8UC1);
binMask = mask & 1; //进一步掩膜
if (init) //进一步抠出无效区域。鼠标按下,init变为false
{
src.copyTo(result, binMask);
}
else
{
src.copyTo(result);
}
rectangle(result, rect, Scalar(0, 0, 255), 2, 8);
imshow(winTitle, result);
}
void setROIMask()
{
/*
GC_BGD = 0:表示明确属于背景的像素
GC_FGD = 1:表示明确属于前景的像素
GC_PR_BGD = 2:表示可能属于背景的像素
GC_PR_FGD = 3:表示可能属于前景的像素
*/
mask.setTo(GC_BGD); //全部设置为背景
//防止出界
rect.x = max(0, rect.x);
rect.y = max(0, rect.y);
rect.width = min(rect.width, src.cols - rect.x);
rect.height = min(rect.height, src.rows - rect.y);
//从mask中截取选中的矩形,将其作为可能的前景
mask(rect).setTo(Scalar(GC_PR_FGD));
}
void runGrabCut()
{
if (rect.width < 2 && rect.height < 2)
{
return;
}
if (init) //鼠标按下,init变为false
{
grabCut(src, mask, rect, bgModel, fgModel, 1); //第二次迭代,用mask初始化grabcut
}
else
{
grabCut(src, mask, rect, bgModel, fgModel, 1, GC_INIT_WITH_RECT);//用矩形框初始化grabcut
init = true;
}
}
结果展示: