天天看點

【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摳圖

效果還是不錯。

繼續閱讀