Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/cpp/grabcut.cpp
16337 views
1
#include "opencv2/imgcodecs.hpp"
2
#include "opencv2/highgui.hpp"
3
#include "opencv2/imgproc.hpp"
4
5
#include <iostream>
6
7
using namespace std;
8
using namespace cv;
9
10
static void help()
11
{
12
cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n"
13
"and then grabcut will attempt to segment it out.\n"
14
"Call:\n"
15
"./grabcut <image_name>\n"
16
"\nSelect a rectangular area around the object you want to segment\n" <<
17
"\nHot keys: \n"
18
"\tESC - quit the program\n"
19
"\tr - restore the original image\n"
20
"\tn - next iteration\n"
21
"\n"
22
"\tleft mouse button - set rectangle\n"
23
"\n"
24
"\tCTRL+left mouse button - set GC_BGD pixels\n"
25
"\tSHIFT+left mouse button - set GC_FGD pixels\n"
26
"\n"
27
"\tCTRL+right mouse button - set GC_PR_BGD pixels\n"
28
"\tSHIFT+right mouse button - set GC_PR_FGD pixels\n" << endl;
29
}
30
31
const Scalar RED = Scalar(0,0,255);
32
const Scalar PINK = Scalar(230,130,255);
33
const Scalar BLUE = Scalar(255,0,0);
34
const Scalar LIGHTBLUE = Scalar(255,255,160);
35
const Scalar GREEN = Scalar(0,255,0);
36
37
const int BGD_KEY = EVENT_FLAG_CTRLKEY;
38
const int FGD_KEY = EVENT_FLAG_SHIFTKEY;
39
40
static void getBinMask( const Mat& comMask, Mat& binMask )
41
{
42
if( comMask.empty() || comMask.type()!=CV_8UC1 )
43
CV_Error( Error::StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );
44
if( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )
45
binMask.create( comMask.size(), CV_8UC1 );
46
binMask = comMask & 1;
47
}
48
49
class GCApplication
50
{
51
public:
52
enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };
53
static const int radius = 2;
54
static const int thickness = -1;
55
56
void reset();
57
void setImageAndWinName( const Mat& _image, const string& _winName );
58
void showImage() const;
59
void mouseClick( int event, int x, int y, int flags, void* param );
60
int nextIter();
61
int getIterCount() const { return iterCount; }
62
private:
63
void setRectInMask();
64
void setLblsInMask( int flags, Point p, bool isPr );
65
66
const string* winName;
67
const Mat* image;
68
Mat mask;
69
Mat bgdModel, fgdModel;
70
71
uchar rectState, lblsState, prLblsState;
72
bool isInitialized;
73
74
Rect rect;
75
vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;
76
int iterCount;
77
};
78
79
void GCApplication::reset()
80
{
81
if( !mask.empty() )
82
mask.setTo(Scalar::all(GC_BGD));
83
bgdPxls.clear(); fgdPxls.clear();
84
prBgdPxls.clear(); prFgdPxls.clear();
85
86
isInitialized = false;
87
rectState = NOT_SET;
88
lblsState = NOT_SET;
89
prLblsState = NOT_SET;
90
iterCount = 0;
91
}
92
93
void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName )
94
{
95
if( _image.empty() || _winName.empty() )
96
return;
97
image = &_image;
98
winName = &_winName;
99
mask.create( image->size(), CV_8UC1);
100
reset();
101
}
102
103
void GCApplication::showImage() const
104
{
105
if( image->empty() || winName->empty() )
106
return;
107
108
Mat res;
109
Mat binMask;
110
if( !isInitialized )
111
image->copyTo( res );
112
else
113
{
114
getBinMask( mask, binMask );
115
image->copyTo( res, binMask );
116
}
117
118
vector<Point>::const_iterator it;
119
for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )
120
circle( res, *it, radius, BLUE, thickness );
121
for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )
122
circle( res, *it, radius, RED, thickness );
123
for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )
124
circle( res, *it, radius, LIGHTBLUE, thickness );
125
for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )
126
circle( res, *it, radius, PINK, thickness );
127
128
if( rectState == IN_PROCESS || rectState == SET )
129
rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);
130
131
imshow( *winName, res );
132
}
133
134
void GCApplication::setRectInMask()
135
{
136
CV_Assert( !mask.empty() );
137
mask.setTo( GC_BGD );
138
rect.x = max(0, rect.x);
139
rect.y = max(0, rect.y);
140
rect.width = min(rect.width, image->cols-rect.x);
141
rect.height = min(rect.height, image->rows-rect.y);
142
(mask(rect)).setTo( Scalar(GC_PR_FGD) );
143
}
144
145
void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
146
{
147
vector<Point> *bpxls, *fpxls;
148
uchar bvalue, fvalue;
149
if( !isPr )
150
{
151
bpxls = &bgdPxls;
152
fpxls = &fgdPxls;
153
bvalue = GC_BGD;
154
fvalue = GC_FGD;
155
}
156
else
157
{
158
bpxls = &prBgdPxls;
159
fpxls = &prFgdPxls;
160
bvalue = GC_PR_BGD;
161
fvalue = GC_PR_FGD;
162
}
163
if( flags & BGD_KEY )
164
{
165
bpxls->push_back(p);
166
circle( mask, p, radius, bvalue, thickness );
167
}
168
if( flags & FGD_KEY )
169
{
170
fpxls->push_back(p);
171
circle( mask, p, radius, fvalue, thickness );
172
}
173
}
174
175
void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
176
{
177
// TODO add bad args check
178
switch( event )
179
{
180
case EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels
181
{
182
bool isb = (flags & BGD_KEY) != 0,
183
isf = (flags & FGD_KEY) != 0;
184
if( rectState == NOT_SET && !isb && !isf )
185
{
186
rectState = IN_PROCESS;
187
rect = Rect( x, y, 1, 1 );
188
}
189
if ( (isb || isf) && rectState == SET )
190
lblsState = IN_PROCESS;
191
}
192
break;
193
case EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels
194
{
195
bool isb = (flags & BGD_KEY) != 0,
196
isf = (flags & FGD_KEY) != 0;
197
if ( (isb || isf) && rectState == SET )
198
prLblsState = IN_PROCESS;
199
}
200
break;
201
case EVENT_LBUTTONUP:
202
if( rectState == IN_PROCESS )
203
{
204
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
205
rectState = SET;
206
setRectInMask();
207
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
208
showImage();
209
}
210
if( lblsState == IN_PROCESS )
211
{
212
setLblsInMask(flags, Point(x,y), false);
213
lblsState = SET;
214
showImage();
215
}
216
break;
217
case EVENT_RBUTTONUP:
218
if( prLblsState == IN_PROCESS )
219
{
220
setLblsInMask(flags, Point(x,y), true);
221
prLblsState = SET;
222
showImage();
223
}
224
break;
225
case EVENT_MOUSEMOVE:
226
if( rectState == IN_PROCESS )
227
{
228
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
229
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
230
showImage();
231
}
232
else if( lblsState == IN_PROCESS )
233
{
234
setLblsInMask(flags, Point(x,y), false);
235
showImage();
236
}
237
else if( prLblsState == IN_PROCESS )
238
{
239
setLblsInMask(flags, Point(x,y), true);
240
showImage();
241
}
242
break;
243
}
244
}
245
246
int GCApplication::nextIter()
247
{
248
if( isInitialized )
249
grabCut( *image, mask, rect, bgdModel, fgdModel, 1 );
250
else
251
{
252
if( rectState != SET )
253
return iterCount;
254
255
if( lblsState == SET || prLblsState == SET )
256
grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK );
257
else
258
grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT );
259
260
isInitialized = true;
261
}
262
iterCount++;
263
264
bgdPxls.clear(); fgdPxls.clear();
265
prBgdPxls.clear(); prFgdPxls.clear();
266
267
return iterCount;
268
}
269
270
GCApplication gcapp;
271
272
static void on_mouse( int event, int x, int y, int flags, void* param )
273
{
274
gcapp.mouseClick( event, x, y, flags, param );
275
}
276
277
int main( int argc, char** argv )
278
{
279
cv::CommandLineParser parser(argc, argv, "{@input| ../data/messi5.jpg |}");
280
help();
281
282
string filename = parser.get<string>("@input");
283
if( filename.empty() )
284
{
285
cout << "\nDurn, empty filename" << endl;
286
return 1;
287
}
288
Mat image = imread( filename, 1 );
289
if( image.empty() )
290
{
291
cout << "\n Durn, couldn't read image filename " << filename << endl;
292
return 1;
293
}
294
295
const string winName = "image";
296
namedWindow( winName, WINDOW_AUTOSIZE );
297
setMouseCallback( winName, on_mouse, 0 );
298
299
gcapp.setImageAndWinName( image, winName );
300
gcapp.showImage();
301
302
for(;;)
303
{
304
char c = (char)waitKey(0);
305
switch( c )
306
{
307
case '\x1b':
308
cout << "Exiting ..." << endl;
309
goto exit_main;
310
case 'r':
311
cout << endl;
312
gcapp.reset();
313
gcapp.showImage();
314
break;
315
case 'n':
316
int iterCount = gcapp.getIterCount();
317
cout << "<" << iterCount << "... ";
318
int newIterCount = gcapp.nextIter();
319
if( newIterCount > iterCount )
320
{
321
gcapp.showImage();
322
cout << iterCount << ">" << endl;
323
}
324
else
325
cout << "rect must be determined>" << endl;
326
break;
327
}
328
}
329
330
exit_main:
331
destroyWindow( winName );
332
return 0;
333
}
334
335