- grabCut图像中对象交互抠图
setMouseCallback
onMouse(int event, int x, int y, int flags, void*param) - 输入图像、矩形输入、初始分类、GMM描述
- GMM训练分类、Graph Cut分类、最终分类、继续迭代/停止
- 图像中对象抠图
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat src;
const char* Window_Title = "Grabcuts";
bool init = false;
Rect rect;
Mat mask, fgModel, bgModel;//mask,前景,背景
void showImage();
void MouseEvent(int event, int x, int y, int flags, void* param);//鼠标回调函数
void maskROI();//为Grabcut创建mask区域
void GrabCuts();
int main(int argc, char** argv)
{
src = imread("../path.jpg");
if (src.empty())
{
cout << "could not load image..." << endl;
return -1;
}
mask.create(src.size(), CV_8UC1);//mask初始化
mask.setTo(Scalar::all(GC_BGD));//全部设置为背景GC_BGD
namedWindow(Window_Title, WINDOW_AUTOSIZE);
setMouseCallback(Window_Title, MouseEvent, 0);//设置鼠标事件
imshow(Window_Title, src);
while (true)
{
char c = (char)waitKey(0);
if (c == ' ')//按空格调用Grabcut
{
GrabCuts();
showImage();
}
if ((int)c == 27)
{
break;
}
}
waitKey(0);
return 0;
}
void showImage()//显示选择的前景区域
{
Mat dst,Mask;
Mask.create(mask.size(), CV_8UC1);
/
Mask = mask & 1;//&=操作符重载//进一步掩膜
if (init) //进一步抠出无效区域,鼠标按下,init变为false
{
src.copyTo(dst, Mask); //在位置(x, y)时,如果mask的像素值不等于0,则dst(x, y) = src(x, y);
}
else
{
src.copyTo(dst);
}
/
rectangle(dst, rect, Scalar(0, 0, 255), 2, 8);//在dst上绘制rect大小的矩形框
imshow(Window_Title, dst);//把dst绘制在窗口上
}
void MouseEvent(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;
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)//从左到右,从上到下
{
maskROI();//鼠标弹起的时候,调用mask函数
showImage();//鼠标弹起的时候,绘制
}
break;
default:
break;
}
return;
}
void maskROI()
{
mask.setTo(GC_BGD);//设置为Grabcut的背景色
//max min都是防止rect未初始化导致的差错
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));//rect区域设置为Grabcut的前景
// GC_FGD =1;//前景
// GC_BGD =0;//背景
// GC_PR_FGD = 3//可能的前景
// GC_PR_BGD = 2//可能的背景
return;
}
void GrabCuts()
{
if (rect.width < 2 || rect.height < 2) //rect太小则返回
{
return;
}
if (init)
{
//grabCut
grabCut(src, // 待分割图像,8bit,3通道
mask, // 如果没有手动标记 GC_BGD或GC_FGD ,那么结果只会有 GC_PR_BGD或GC_PR_FGD
rect, // 当 mode=GC_INIT_WITH_RECT时使用,rect外部的为GC_BGD,rect内部的为GC_FGD
bgModel, // 背景模型(内部使用)
fgModel, //前景模型(内部使用)
1);// 迭代次数,必须大于0
}
else
{
grabCut(src, mask, rect, bgModel, fgModel, 1, GC_INIT_WITH_RECT/*// GC_INIT_WITH_RECT表示用矩形框初始化Grabcut,GC_INIT_WITH_MASK表示用掩码图像初始化Grabcut, GC_EVAL表示执行分割*/);
init = true;
}
return;
}
src为:
按下鼠标左键,拉出如图的矩形框:
按下键盘空格,输出结果: