Enable aruco module
[platform/upstream/opencv.git] / samples / cpp / grabcut.cpp
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(char** argv)
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         <<  argv[0] << " <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| messi5.jpg |}");
280     help(argv);
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(samples::findFile(filename), IMREAD_COLOR);
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 }