1 #include "opencv2/highgui/highgui.hpp"
2 #include "opencv2/imgproc/imgproc.hpp"
11 cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n"
12 "and then grabcut will attempt to segment it out.\n"
14 "./grabcut <image_name>\n"
15 "\nSelect a rectangular area around the object you want to segment\n" <<
17 "\tESC - quit the program\n"
18 "\tr - restore the original image\n"
19 "\tn - next iteration\n"
21 "\tleft mouse button - set rectangle\n"
23 "\tCTRL+left mouse button - set GC_BGD pixels\n"
24 "\tSHIFT+left mouse button - set CG_FGD pixels\n"
26 "\tCTRL+right mouse button - set GC_PR_BGD pixels\n"
27 "\tSHIFT+right mouse button - set CG_PR_FGD pixels\n" << endl;
30 const Scalar RED = Scalar(0,0,255);
31 const Scalar PINK = Scalar(230,130,255);
32 const Scalar BLUE = Scalar(255,0,0);
33 const Scalar LIGHTBLUE = Scalar(255,255,160);
34 const Scalar GREEN = Scalar(0,255,0);
36 const int BGD_KEY = EVENT_FLAG_CTRLKEY;
37 const int FGD_KEY = EVENT_FLAG_SHIFTKEY;
39 static void getBinMask( const Mat& comMask, Mat& binMask )
41 if( comMask.empty() || comMask.type()!=CV_8UC1 )
42 CV_Error( Error::StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );
43 if( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )
44 binMask.create( comMask.size(), CV_8UC1 );
45 binMask = comMask & 1;
51 enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };
52 static const int radius = 2;
53 static const int thickness = -1;
56 void setImageAndWinName( const Mat& _image, const string& _winName );
57 void showImage() const;
58 void mouseClick( int event, int x, int y, int flags, void* param );
60 int getIterCount() const { return iterCount; }
63 void setLblsInMask( int flags, Point p, bool isPr );
65 const string* winName;
68 Mat bgdModel, fgdModel;
70 uchar rectState, lblsState, prLblsState;
74 vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;
78 void GCApplication::reset()
81 mask.setTo(Scalar::all(GC_BGD));
82 bgdPxls.clear(); fgdPxls.clear();
83 prBgdPxls.clear(); prFgdPxls.clear();
85 isInitialized = false;
88 prLblsState = NOT_SET;
92 void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName )
94 if( _image.empty() || _winName.empty() )
98 mask.create( image->size(), CV_8UC1);
102 void GCApplication::showImage() const
104 if( image->empty() || winName->empty() )
110 image->copyTo( res );
113 getBinMask( mask, binMask );
114 image->copyTo( res, binMask );
117 vector<Point>::const_iterator it;
118 for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )
119 circle( res, *it, radius, BLUE, thickness );
120 for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )
121 circle( res, *it, radius, RED, thickness );
122 for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )
123 circle( res, *it, radius, LIGHTBLUE, thickness );
124 for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )
125 circle( res, *it, radius, PINK, thickness );
127 if( rectState == IN_PROCESS || rectState == SET )
128 rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);
130 imshow( *winName, res );
133 void GCApplication::setRectInMask()
135 CV_Assert( !mask.empty() );
136 mask.setTo( GC_BGD );
137 rect.x = max(0, rect.x);
138 rect.y = max(0, rect.y);
139 rect.width = min(rect.width, image->cols-rect.x);
140 rect.height = min(rect.height, image->rows-rect.y);
141 (mask(rect)).setTo( Scalar(GC_PR_FGD) );
144 void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
146 vector<Point> *bpxls, *fpxls;
147 uchar bvalue, fvalue;
162 if( flags & BGD_KEY )
165 circle( mask, p, radius, bvalue, thickness );
167 if( flags & FGD_KEY )
170 circle( mask, p, radius, fvalue, thickness );
174 void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
176 // TODO add bad args check
179 case EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels
181 bool isb = (flags & BGD_KEY) != 0,
182 isf = (flags & FGD_KEY) != 0;
183 if( rectState == NOT_SET && !isb && !isf )
185 rectState = IN_PROCESS;
186 rect = Rect( x, y, 1, 1 );
188 if ( (isb || isf) && rectState == SET )
189 lblsState = IN_PROCESS;
192 case EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels
194 bool isb = (flags & BGD_KEY) != 0,
195 isf = (flags & FGD_KEY) != 0;
196 if ( (isb || isf) && rectState == SET )
197 prLblsState = IN_PROCESS;
200 case EVENT_LBUTTONUP:
201 if( rectState == IN_PROCESS )
203 rect = Rect( Point(rect.x, rect.y), Point(x,y) );
206 CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
209 if( lblsState == IN_PROCESS )
211 setLblsInMask(flags, Point(x,y), false);
216 case EVENT_RBUTTONUP:
217 if( prLblsState == IN_PROCESS )
219 setLblsInMask(flags, Point(x,y), true);
224 case EVENT_MOUSEMOVE:
225 if( rectState == IN_PROCESS )
227 rect = Rect( Point(rect.x, rect.y), Point(x,y) );
228 CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
231 else if( lblsState == IN_PROCESS )
233 setLblsInMask(flags, Point(x,y), false);
236 else if( prLblsState == IN_PROCESS )
238 setLblsInMask(flags, Point(x,y), true);
245 int GCApplication::nextIter()
248 grabCut( *image, mask, rect, bgdModel, fgdModel, 1 );
251 if( rectState != SET )
254 if( lblsState == SET || prLblsState == SET )
255 grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK );
257 grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT );
259 isInitialized = true;
263 bgdPxls.clear(); fgdPxls.clear();
264 prBgdPxls.clear(); prFgdPxls.clear();
271 static void on_mouse( int event, int x, int y, int flags, void* param )
273 gcapp.mouseClick( event, x, y, flags, param );
276 int main( int argc, char** argv )
283 string filename = argv[1];
284 if( filename.empty() )
286 cout << "\nDurn, couldn't read in " << argv[1] << endl;
289 Mat image = imread( filename, 1 );
292 cout << "\n Durn, couldn't read image filename " << filename << endl;
298 const string winName = "image";
299 namedWindow( winName, WINDOW_AUTOSIZE );
300 setMouseCallback( winName, on_mouse, 0 );
302 gcapp.setImageAndWinName( image, winName );
311 cout << "Exiting ..." << endl;
319 int iterCount = gcapp.getIterCount();
320 cout << "<" << iterCount << "... ";
321 int newIterCount = gcapp.nextIter();
322 if( newIterCount > iterCount )
325 cout << iterCount << ">" << endl;
328 cout << "rect must be determined>" << endl;
334 destroyWindow( winName );