Grabcut图像切割

mouseClick (int event, int x, int y, int flags, void* param);

其中event是 CV_EVENT_*变量之一; x和y是鼠标指针在图像坐标系的坐标,并不是整个窗口的坐标;  flags是CV_EVENT_FLAG的组合,  即表示所有的按键,一般情况下是固定的;  param是用户定义的传递到cvSetMouseCallback函数调用的参数,这通常在回调函数中都有类似这种功能的的参数。

Grabcut在opencv中核心算法函数为:

void cv::grabCut( const Mat& img, Mat& mask, Rect rect, Mat& bgdModel, Mat& fgdModel, int iterCount, int mode )

img——待分割的源图像,必须是8位3通道(CV_8UC3)图像,在处理的过程中不会被修改;

mask——掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:

GCD_BGD(=0),背景;

GCD_FGD(=1),前景;

GCD_PR_BGD(=2),可能的背景;

GCD_PR_FGD(=3),可能的前景。

如果没有手工标记GCD_BGD或者GCD_FGD,那么结果只会有GCD_PR_BGD或GCD_PR_FGD;

rect——用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;

bgdModel——背景模型,如果为null,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;

fgdModel——前景模型,如果为null,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;

iterCount——迭代次数,必须大于0;

mode——用于指示grabCut函数进行什么操作,可选的值有:

GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;

GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;

GC_EVAL(=2),执行分割。

前景点(Shift+鼠标左键+鼠标移动)和一些背景点(Ctrl+鼠标左键+鼠标移动)

#include"opencv2/imgcodecs.hpp"

#include"opencv2/highgui.hpp"

#include"opencv2/imgproc.hpp"

#include

usingnamespacestd;

usingnamespacecv;

staticvoidhelp()

{

cout<<"\nThis program demonstrates GrabCut segmentation -- select an object in a region\n"

"and then grabcut will attempt to segment it out.\n"

"Call:\n"

"./grabcut \n"

"\nSelect a rectangular area around the object you want to segment\n"<<

"\nHot keys: \n"

"\tESC - quit the program\n"

"\tr - restore the original image\n"

"\tn - next iteration\n"

"\n"

"\tleft mouse button - set rectangle\n"

"\n"

"\tCTRL+left mouse button - set GC_BGD pixels\n"

"\tSHIFT+left mouse button - set GC_FGD pixels\n"

"\n"

"\tCTRL+right mouse button - set GC_PR_BGD pixels\n"

"\tSHIFT+right mouse button - set GC_PR_FGD pixels\n"<

}

constScalarRED =Scalar(0, 0, 255);

constScalarPINK =Scalar(230, 130, 255);

constScalarBLUE =Scalar(255, 0, 0);

constScalarLIGHTBLUE =Scalar(255, 255, 160);

constScalarGREEN =Scalar(0, 255, 0);

constintBGD_KEY =CV_EVENT_FLAG_CTRLKEY;

constintFGD_KEY =CV_EVENT_FLAG_SHIFTKEY;

staticvoidgetBinMask(constMat&comMask,Mat&binMask)

{

cout<<"getBinMask"<

if(comMask.empty() ||comMask.type() !=CV_8UC1)

CV_Error(CV_StsBadArg,"comMask is empty or has incorrect type (not CV_8UC1)");

if(binMask.empty() ||binMask.rows !=comMask.rows ||binMask.cols !=comMask.cols)

binMask.create(comMask.size(),CV_8UC1);

binMask=comMask&1;

}

classGCApplication

{

public:

enum{NOT_SET= 0,IN_PROCESS= 1,SET= 2 };

staticconstintradius = 2;

staticconstintthickness = -1;

voidreset();

voidsetImageAndWinName(constMat&_image,conststring&_winName);

voidshowImage()const;

voidmouseClick(intevent,intx,inty,intflags,void*param);

intnextIter();

intgetIterCount()const{returniterCount; }

private:

voidsetRectInMask();

voidsetLblsInMask(intflags,Pointp,boolisPr);

conststring* winName;

constMat* image;

Matmask;

MatbgdModel, fgdModel;

ucharrectState, lblsState, prLblsState;

boolisInitialized;

Rectrect;

vector fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;

intiterCount;

};

//清空点集合

voidGCApplication::reset()

{

cout<<"reset"<

if(!mask.empty())

mask.setTo(Scalar::all(GC_BGD));

bgdPxls.clear(); fgdPxls.clear();

prBgdPxls.clear();  prFgdPxls.clear();

isInitialized =false;

rectState =NOT_SET;

lblsState =NOT_SET;

prLblsState =NOT_SET;

iterCount = 0;

}

//初始化,把image赋给全局变量image

voidGCApplication::setImageAndWinName(constMat&_image,conststring&_winName)

{

cout<<"setImageAndWinName"<

if(_image.empty() ||_winName.empty())

return;

image = &_image;

winName = &_winName;

mask.create(image->size(),CV_8UC1);

reset();

}

//负责显示矩形框和画的蓝色区域

voidGCApplication::showImage()const

{

cout<<"showImage"<

if(image->empty() || winName->empty())

return;

Matres;

MatbinMask;

if(!isInitialized) {

cout<<"!isInitialized"<

image->copyTo(res);

}

else

{

cout<<"isInitialized"<

getBinMask(mask, binMask);

image->copyTo(res, binMask);

}

vector::const_iteratorit;

for(it=bgdPxls.begin(); it!=bgdPxls.end();++it) {

//进的这里

//cout << "bgdPxls" << bgdPxls << endl;

//就是为了把以前的点也保存下来,所以不能直接打印

//所以说 bgdPxls

//这里就是为了显示,而真正的mask没在这里

circle(res,*it, radius, BLUE, thickness);

}

for(it=fgdPxls.begin(); it!=fgdPxls.end();++it) {

cout<<"fgdPxls"<

circle(res,*it, radius, RED, thickness);

}

for(it=prBgdPxls.begin(); it!=prBgdPxls.end();++it) {

cout<<"prBgdPxls"<

circle(res,*it, radius, LIGHTBLUE, thickness);

}

for(it=prFgdPxls.begin(); it!=prFgdPxls.end();++it) {

cout<<"prFgdPxls"<

circle(res,*it, radius, PINK, thickness);

}

if(rectState ==IN_PROCESS|| rectState ==SET) {

//意思是无论如何,都要显示出来绿色的矩形框

cout<<"IN_PROCESS || SET"<

rectangle(res,Point(rect.x, rect.y),Point(rect.x + rect.width, rect.y + rect.height), GREEN, 2);

}

//把res显示出来

imshow(*winName, res);

}

voidGCApplication::setRectInMask()

{

cout<<"setRectInMask"<

assert(!mask.empty());

mask.setTo(GC_BGD);

rect.x = max(0, rect.x);

rect.y = max(0, rect.y);

rect.width = min(rect.width, image->cols - rect.x);

rect.height = min(rect.height, image->rows - rect.y);

(mask(rect)).setTo(Scalar(GC_PR_FGD));

}

voidGCApplication::setLblsInMask(intflags,Pointp,boolisPr)

{

cout<<"setLblsInMask"<

vector *bpxls, *fpxls;

ucharbvalue, fvalue;

if(!isPr)

{

cout<<"这里";

bpxls = &bgdPxls;

fpxls = &fgdPxls;

bvalue =GC_BGD;

fvalue =GC_FGD;

}

else

{

bpxls = &prBgdPxls;

fpxls = &prFgdPxls;

bvalue =GC_PR_BGD;

fvalue =GC_PR_FGD;

}

if(flags& BGD_KEY)

{

//Add element at the end

//bpxls相当于直接对bgdPxls操作

bpxls->push_back(p);

//cout << "bpxls" << *bpxls;

//cout << "bgdPxls" << bgdPxls;

circle(mask,p, radius, bvalue, thickness);

}

if(flags& FGD_KEY)

{

fpxls->push_back(p);

circle(mask,p, radius, fvalue, thickness);

}

}

//传入x,y的点坐标,然后直接赋值给Point (x,y)——》然后进setLblsInMask

voidGCApplication::mouseClick(intevent,intx,inty,intflags,void*)

{

// TODO add bad args check

switch(event)

{

caseCV_EVENT_LBUTTONDOWN:// set rect or GC_BGD(GC_FGD) labels

{

boolisb = (flags& BGD_KEY) != 0,

isf = (flags& FGD_KEY) != 0;

if(rectState ==NOT_SET&& !isb && !isf)

{

//这里不是按住ctrl的

rectState =IN_PROCESS;

rect=Rect(x,y, 1, 1);

}

if((isb || isf) && rectState ==SET) {

//cout << "这里是按住ctrl之后的CV_EVENT_LBUTTONDOWN" << endl;

lblsState =IN_PROCESS;

}

}

break;

/*

case CV_EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels

{

cout << "CV_EVENT_RBUTTONDOWN"<

bool isb = (flags & BGD_KEY) != 0,

isf = (flags & FGD_KEY) != 0;

if ((isb || isf) && rectState == SET)

prLblsState = IN_PROCESS;

}

break;

*/

caseCV_EVENT_LBUTTONUP:

if(rectState ==IN_PROCESS)

{

//这里不是按住ctrl的

rect=Rect(Point(rect.x, rect.y),Point(x,y));

rectState =SET;

setRectInMask();

assert(bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty());

showImage();

}

if(lblsState ==IN_PROCESS)

{

//cout << "这里是按住ctrl之后的CV_EVENT_LBUTTONUP" << endl;

//把点给mask设置上去

setLblsInMask(flags,Point(x,y),false);

//修改更改状态

lblsState =SET;

//显示image

showImage();

//TODO 这里打印一下mask的值

//cout << " mask" << mask;

}

break;

/*

case CV_EVENT_RBUTTONUP:

cout << "CV_EVENT_RBUTTONUP" << endl;

if (prLblsState == IN_PROCESS)

{

setLblsInMask(flags, Point(x, y), true);

prLblsState = SET;

showImage();

}

break;

*/

caseCV_EVENT_MOUSEMOVE:

//cout << "CV_EVENT_MOUSEMOVE" << endl;

if(rectState ==IN_PROCESS)

{

//没有按住的

//cout << "画矩形画的点" << endl;

rect=Rect(Point(rect.x, rect.y),Point(x,y));

assert(bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty());

showImage();

}

elseif(lblsState ==IN_PROCESS)

{

//按住crtl之后移动的点

cout<<"crtl移动"<

setLblsInMask(flags,Point(x,y),false);

showImage();

}

/*

else if (prLblsState == IN_PROCESS)

{

setLblsInMask(flags, Point(x, y), true);

showImage();

}

*/

break;

}

}

intGCApplication::nextIter()

{

if(isInitialized) {

cout<<"init";

grabCut(*image, mask, rect, bgdModel, fgdModel, 1);

}

else

{

if(rectState !=SET)

returniterCount;

if(lblsState ==SET|| prLblsState ==SET)

{

cout<<"MASK"<<"rect"<

grabCut(*image, mask, rect, bgdModel, fgdModel, 1,GC_INIT_WITH_MASK);

}

else

{

cout<<"RECT";

grabCut(*image, mask, rect, bgdModel, fgdModel, 1,GC_INIT_WITH_RECT);

}

isInitialized =true;

}

iterCount++;

bgdPxls.clear(); fgdPxls.clear();

prBgdPxls.clear(); prFgdPxls.clear();

returniterCount;

}

GCApplicationgcapp;

staticvoidon_mouse(intevent,intx,inty,intflags,void*param)

{

gcapp.mouseClick(event,x,y,flags,param);

}

intmain(intargc,char**argv)

{

stringfilename ="C:\\Users\\Mz\\Desktop\\0327\\1.jpg";

Matimage = imread(filename, 1);

if(image.empty())

{

cout<<"\n Durn, couldn't read image filename "<

return1;

}

help();

conststringwinName ="image";

namedWindow(winName, 2);

setMouseCallback(winName, on_mouse, 0);

gcapp.setImageAndWinName(image, winName);

gcapp.showImage();

for(;;)

{

intc = waitKey(0);

switch((char)c)

{

case'\x1b':

cout<<"Exiting ..."<

gotoexit_main;

case'r':

cout<

gcapp.reset();

gcapp.showImage();

break;

case'n':

intiterCount = gcapp.getIterCount();

cout<<"<"<

intnewIterCount = gcapp.nextIter();

if(newIterCount > iterCount)

{

gcapp.showImage();

cout<"<

}

else

cout<<"rect must be determined>"<

break;

}

}

exit_main:

destroyWindow(winName);

return0;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 江南的天在梅雨季节灰蒙蒙地阴着。昏沉沉地回到家,人像被砍去了负责感受的那一半般,机械地洗漱吃饭说话。午觉可能...
    九月桔香阅读 138评论 0 0
  • 还不到烟花三月的时后, 你便离开我远去了揚洲。 我的哏眶不由自主地湿润, 泪,望着你渐行丶渐远的身影流出。 不知什...
    黄泥村人阅读 209评论 13 16
  • 我的一位年轻朋友经常拿自己和竞争对手作比较,说自己没有良好的社会关系,没有赏识自己才能的伯乐,没有好的市场机...
    发芽的石头就是我阅读 556评论 0 3
  • 今天太累了,不想写什么。孩子自己做的作业,都没有给他检查,等明天一起给他看看。 现在起风了,真大。人家都说一心不能...
    2019影阅读 92评论 0 2