opencv关于图像前景提取

时间:2024-04-04 09:17:59

在图像处理的过程中,会经常碰到需要提取选定区域的前景,以下就使用grabCut算法进行图像前景的提取(我是用的opencv版本为opencv2.4.3.)。这是我的第一篇博客,有什么不足的地方欢迎大家指导。

关于grabCut算法的原理,我也是参照大牛写的文章,[这里]就是原文地址。(https://blog.csdn.net/zouxy09/article/details/8534954)

在进行图像前景的提取操作之前,我们需要先设置鼠标点击事件,这里我是用的是opencv的鼠标回调事件,首先定义回调函数,代码如下所示:void onMouse(int event,int x,int y,int flags,void* param);
回调函数定义完成之后需要将我们的回调函数设置在固定窗口之中,这里使用opencv的highgui模块中的setMouseCallback函数,代码如下所示:setMouseCallback(winTitle,onMouse); //设置鼠标回调函数
其中winTitle为窗口的名称,onMouse为之前定义的是鼠标回调函数。

将鼠标回调函数设置完毕之后,需要写回调函数中的具体内容了,废话不多说,直接上代码。

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;
		numRun=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)
		{
			setROIMask();	//设置矩形区域为可能前景区域
			showImage();
		}
		break;
	default:
		break;
	}
}

在这个函数中,我仅仅是对鼠标左键进行设置,包括左键按下事件,鼠标移动事件以及左键松开事件。

做完以上步骤之后,可以运行一下,看看是否达到我们要求的效果,我的运行结果如下图所示:
opencv关于图像前景提取

完成了以上步骤之后,就可以设置图片的前景与后景了。在鼠标回调函数中,可以看到我使用了自定义的setROIMask函数,该函数的主要目的是将框选的区域设置为可能的前景,代码如下所示:

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(rect).setTo(Scalar(GC_PR_FGD));	//将矩形区域设置为可能的前景
}

这里有必要对setTo中的参数进行说明,正如上面代码所说,GC_BGD表示设置为背景,GC_FGD表示设置为前景,GC_PR_BGD表示设置为疑似背景GC_PR_FGD表示设置为疑似前景。在鼠标左键松开事件中调用的setROIMask函数,就是将鼠标框选的矩形区域设置为疑似前景部分。

在完成对图像的前后景设置之后,就可以使用grabCut算法进行前景的分割提取了。

grabCut(src,mask,rect,bgModel,fgModel,1);	//图割算法

以下是使用grabCut算法得到的图片前景,如果觉得效果不太理想,可以进行多次迭代,以达到更好的效果。
opencv关于图像前景提取

关于opencv的图像分割算法到这里就差不多了,好的,哪下面就放上完整代码,以供大家参考并指正。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;

int numRun=0;
Rect rect;
bool init=false;
Mat src,image;
Mat mask,bgModel,fgModel;
const char* winTitle="input image";

void onMouse(int event,int x,int y,int flags,void* param);
void setROIMask();
void showImage();
void runGrabCut();

int main()
{
	src=imread("flower.jpg",1);
	if(src.empty())
	{
		cout<<"could not load image..."<<endl;
		return -1;
	}
	mask.create(src.size(),CV_8UC1);
	mask.setTo(Scalar::all(GC_BGD));	//将整个图片设置为背景

	namedWindow(winTitle,CV_WINDOW_AUTOSIZE);	//声明窗口
	setMouseCallback(winTitle,onMouse);		//设置鼠标回调函数
	imshow(winTitle,src);	//展示原图

	while(true)
	{
		char c=(char)waitKey(0);	//获取用户 按下的按键
		if(c=='n')
		{
			runGrabCut();	//开始图割算法,运行一次为迭代一次
			numRun++;		//迭代次数加1
			showImage();	//显示图像
			//imshow("背景",bgModel);
			//imshow("前景",fgModel);
			cout<<"迭代次数为:"<<numRun<<endl;
		}
		if((int)c==27)
		{
			break;	//检测到按下esc按钮
		}
	}
	waitKey(0);
	return 0;
}

void showImage()
{
	Mat result,binMask;
	binMask.create(mask.size(),CV_8UC1);
	binMask=mask & 1;
	if(init)
	{
		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(rect).setTo(Scalar(GC_PR_FGD));	//将矩形区域设置为可能的前景
}

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;
		numRun=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)
		{
			setROIMask();	//设置矩形区域为可能前景区域
			showImage();
		}
		break;
	default:
		break;
	}
}

void runGrabCut()
{
	if(rect.width<2 || rect.height<2)
		return;
	if(init)
	{
		grabCut(src,mask,rect,bgModel,fgModel,1);	//图割算法
	}{
		grabCut(src,mask,rect,bgModel,fgModel,1,GC_INIT_WITH_RECT);
		init=true;
	}
}

关于图片和完整工程,大家可以去这里下载。