grabcut.cpp 9.6 KB

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