Grabcut圖像切割

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

其中event是?CV_EVENT_*變量之一;?x和y是鼠標指針在圖像坐標系的坐標仰禀,并不是整個窗口的坐標照雁;??flags是CV_EVENT_FLAG的組合,??即表示所有的按鍵,一般情況下是固定的饺蚊;??param是用戶定義的傳遞到cvSetMouseCallback函數(shù)調用的參數(shù)萍诱,這通常在回調函數(shù)中都有類似這種功能的的參數(shù)。

Grabcut在opencv中核心算法函數(shù)為:

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保存初始化掩碼信息;在執(zhí)行分割的時候苗缩,也可以將用戶交互所設定的前景與背景保存到mask中饵蒂,然后再傳入grabCut函數(shù);在處理結束之后酱讶,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曹锨,函數(shù)內部會自動創(chuàng)建一個bgdModel;bgdModel必須是單通道浮點型(CV_32FC1)圖像剃允,且行數(shù)只能為1沛简,列數(shù)只能為13x5;

fgdModel——前景模型斥废,如果為null椒楣,函數(shù)內部會自動創(chuàng)建一個fgdModel;fgdModel必須是單通道浮點型(CV_32FC1)圖像牡肉,且行數(shù)只能為1捧灰,列數(shù)只能為13x5;

iterCount——迭代次數(shù)统锤,必須大于0毛俏;

mode——用于指示grabCut函數(shù)進行什么操作炭庙,可選的值有:

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

GC_INIT_WITH_MASK(=1)焕蹄,用掩碼圖像初始化GrabCut;

GC_EVAL(=2)阀溶,執(zhí)行分割腻脏。

前景點(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();

}

//負責顯示矩形框和畫的藍色區(qū)域

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);

//修改更改狀態(tài)

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;

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市掉弛,隨后出現(xiàn)的幾起案子症见,更是在濱河造成了極大的恐慌,老刑警劉巖殃饿,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谋作,死亡現(xiàn)場離奇詭異,居然都是意外死亡乎芳,警方通過查閱死者的電腦和手機遵蚜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奈惑,“玉大人吭净,你說我怎么就攤上這事‰鹊椋” “怎么了寂殉?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵缠诅,是天一觀的道長漆魔。 經常有香客問我,道長赚爵,這世上最難降的妖魔是什么庶柿? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任村怪,我火速辦了婚禮,結果婚禮上浮庐,老公的妹妹穿的比我還像新娘甚负。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布腊敲。 她就那樣靜靜地躺著击喂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碰辅。 梳的紋絲不亂的頭發(fā)上懂昂,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天,我揣著相機與錄音没宾,去河邊找鬼凌彬。 笑死,一個胖子當著我的面吹牛循衰,可吹牛的內容都是我干的铲敛。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼会钝,長吁一口氣:“原來是場噩夢啊……” “哼伐蒋!你這毒婦竟也來了?” 一聲冷哼從身側響起迁酸,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤先鱼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后奸鬓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焙畔,經...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年串远,在試婚紗的時候發(fā)現(xiàn)自己被綠了宏多。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡澡罚,死狀恐怖伸但,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情留搔,我是刑警寧澤砌烁,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站催式,受9級特大地震影響,放射性物質發(fā)生泄漏避归。R本人自食惡果不足惜荣月,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梳毙。 院中可真熱鬧哺窄,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至生年,卻和暖如春婴程,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抱婉。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工档叔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒸绩。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓衙四,卻偏偏與公主長得像,于是被迫代替她去往敵國和親患亿。 傳聞我的和親對象是個殘疾皇子传蹈,可洞房花燭夜當晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內容

  • 江南的天在梅雨季節(jié)灰蒙蒙地陰著〔脚海昏沉沉地回到家惦界,人像被砍去了負責感受的那一半般,機械地洗漱吃飯說話漱抓。午覺可能...
    九月桔香閱讀 138評論 0 0
  • 還不到煙花三月的時后表锻, 你便離開我遠去了揚洲。 我的哏眶不由自主地濕潤乞娄, 淚瞬逊,望著你漸行丶漸遠的身影流出。 不知什...
    黃泥村人閱讀 209評論 13 16
  • 我的一位年輕朋友經常拿自己和競爭對手作比較,說自己沒有良好的社會關系范删,沒有賞識自己才能的伯樂蕾域,沒有好的市場機...
    發(fā)芽的石頭就是我閱讀 556評論 0 3
  • 今天太累了,不想寫什么到旦。孩子自己做的作業(yè)旨巷,都沒有給他檢查,等明天一起給他看看添忘。 現(xiàn)在起風了采呐,真大。人家都說一心不能...
    2019影閱讀 92評論 0 2