天天看点

【OpenCV】grabcut抠图

 OpenCV中的grabcut函数实现的是论文《"GrabCut": interactive foreground extraction using iterated graph cuts》的算法。

OpenCV的函数原型如下:

void cv::grabCut(InputArray img,
InputOutputArray mask,
Rect rect,
InputOutputArray bgdModel,
InputOutputArray fgdModel,
int iterCount,
int mode = GC_EVAL 
)	
           

参数:

InputArray img:输入8位3通道图像。

InputOutputArray mask:输入\输出8位单通道掩码。如果函数设置mode= GC_INIT_WITH_RECT,则该掩码由函数来初始化。它的元素值是GrabCutClasses中的一个。

【OpenCV】grabcut抠图

Rect rect:包含待分割目标的ROI区域。在该区域之外的像素被标记为"obvious background"。这个参数仅在mode==GC_INIT_WITH_RECT的时候使用。

InputOutputArray bgdModel:背景model的临时数组。

InputOutputArray fgdModel:前景model的临时数组。

int iterCount:算法迭代次数。

int mode = GC_EVAL:必须是枚举类型GrabCutModes中的一种。

【OpenCV】grabcut抠图

下面的代码实现一个简单的由鼠标交互式选取ROI区域进行抠图的代码。

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

using namespace std;

cv::Rect rect;
cv::Point2i cursor;

void onMouse(int event, int x, int y, int flags, void *param)
{
	cv::Mat* src = (cv::Mat*)param;
	cv::Mat roiImg;
	src->copyTo(roiImg);
	switch (event)
	{
	case CV_EVENT_LBUTTONDOWN:
		cursor = cv::Point2i(x, y);
		rect = cv::Rect(x, y, 1, 1);
		std::cout << "CV_EVENT_LBUTTONDOWN==" << rect << std::endl;
		break;
    
	case CV_EVENT_LBUTTONUP:
		rect.width = abs(x - rect.x);
		rect.height = abs(y - rect.y);
		std::cout << "CV_EVENT_LBUTTONUP==" << rect << std::endl;
		cv::rectangle(roiImg, rect, cv::Scalar(0, 0, 255), 2);
		cv::imshow("input", roiImg);
		break;

	case CV_EVENT_MOUSEMOVE:
		if (flags && CV_EVENT_FLAG_LBUTTON) {
			rect.x = MIN(x, cursor.x);
			rect.y = MIN(y, cursor.y);
			rect.width = abs(cursor.x - x);
			rect.height = abs(cursor.y - y);
		}
		break;
	}
}

int main()
{
	cv::Mat img = cv::imread("boldt.jpg");
	if (img.empty())
	{
		cout << "error";
		return -1;
	}

	cv::namedWindow("SrcImage");
	cv::setMouseCallback("SrcImage", onMouse, &img);
	cv::imshow("SrcImage", img);
	cv::waitKey();


	cv::Mat result;
	cv::Mat bgModel, fgModel;
	cv::grabCut(img, result, rect, bgModel, fgModel, 5, cv::GC_INIT_WITH_RECT);
	cv::compare(result, cv::GC_PR_FGD, result, cv::CMP_EQ);

	cv::Mat foreground(img.size(), CV_8UC3, cv::Scalar(255, 255, 255));
	img.copyTo(foreground, result);

	cv::namedWindow("Foreground");
	cv::imshow("Foreground", foreground);
	cv::waitKey();
	return 0;
}
           

原图:

【OpenCV】grabcut抠图

红色框的区域选取的ROI区域:

【OpenCV】grabcut抠图

抠图结果:

【OpenCV】grabcut抠图

效果还是不错。

继续阅读