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