opencv图像分割 --- Grabcut图像分割

         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;
	}

	
}

结果展示:

 

 

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值