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中的一個。
Rect rect:包含待分割目标的ROI區域。在該區域之外的像素被标記為"obvious background"。這個參數僅在mode==GC_INIT_WITH_RECT的時候使用。
InputOutputArray bgdModel:背景model的臨時數組。
InputOutputArray fgdModel:前景model的臨時數組。
int iterCount:算法疊代次數。
int mode = GC_EVAL:必須是枚舉類型GrabCutModes中的一種。
下面的代碼實作一個簡單的由滑鼠互動式選取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;
}
原圖:
紅色框的區域選取的ROI區域:
摳圖結果:
效果還是不錯。