opencv2 学习第8天 提取分离前景和背景

http://blog.csdn.net/zhouzhouzf/article/details/9281327

GrabCut

代码来自于http://www.cnblogs.com/tornadomeet/archive/2012/11/09/2763271.html

[cpp] view plain copy
 
  1. #include <opencv2/highgui/highgui.hpp>  
  2. #include <opencv2/core/core.hpp>  
  3. #include <vector>  
  4. #include <iostream>  
  5. #include <opencv2/imgproc/imgproc.hpp>  
  6. //#include "../../../../../Downloads/colourhistogram.h"  
  7. using namespace std;  
  8. using namespace cv;  
  9. static void help()  
  10. {  
  11.     cout << " This program demonstrates GrabCut segmentation -- select an object in a region "  
  12.         "and then grabcut will attempt to segment it out. "  
  13.         "Call: "  
  14.         "./grabcut <image_name> "  
  15.         " Select a rectangular area around the object you want to segment " <<  
  16.         " Hot keys:  "  
  17.         " ESC - quit the program "  
  18.         " r - restore the original image "  
  19.         " n - next iteration "  
  20.         " "  
  21.         " left mouse button - set rectangle "  
  22.         " "  
  23.         " CTRL+left mouse button - set GC_BGD pixels "  
  24.         " SHIFT+left mouse button - set CG_FGD pixels "  
  25.         " "  
  26.         " CTRL+right mouse button - set GC_PR_BGD pixels "  
  27.         " SHIFT+right mouse button - set CG_PR_FGD pixels " << endl;  
  28. }  
  29.   
  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);  
  35.   
  36. const int BGD_KEY = CV_EVENT_FLAG_CTRLKEY;  //Ctrl键  
  37. const int FGD_KEY = CV_EVENT_FLAG_SHIFTKEY; //Shift键  
  38.   
  39. static void getBinMask( const Mat& comMask, Mat& binMask )  
  40. {  
  41.     if( comMask.empty() || comMask.type()!=CV_8UC1 )  
  42.         CV_Error( CV_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;  //得到mask的最低位,实际上是只保留确定的或者有可能的前景点当做mask  
  46. }  
  47.   
  48. class GCApplication  
  49. {  
  50. public:  
  51.     enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };  
  52.     static const int radius = 2;  
  53.     static const int thickness = -1;  
  54.   
  55.     void reset();  
  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 );  
  59.     int nextIter();  
  60.     int getIterCount() const { return iterCount; }  
  61. private:  
  62.     void setRectInMask();  
  63.     void setLblsInMask( int flags, Point p, bool isPr );  
  64.   
  65.     const string* winName;  
  66.     const Mat* image;  
  67.     Mat mask;  
  68.     Mat bgdModel, fgdModel;  
  69.   
  70.     uchar rectState, lblsState, prLblsState;  
  71.     bool isInitialized;  
  72.   
  73.     Rect rect;  
  74.     vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;  
  75.     int iterCount;  
  76. };  
  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;    //NOT_SET == 0  
  88.     lblsState = NOT_SET;  
  89.     prLblsState = NOT_SET;  
  90.     iterCount = 0;  
  91. }  
  92.   
  93. /*给类的成员变量赋值而已*/  
  94. void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName  )  
  95. {  
  96.     if( _image.empty() || _winName.empty() )  
  97.         return;  
  98.     image = &_image;  
  99.     winName = &_winName;  
  100.     mask.create( image->size(), CV_8UC1);  
  101.     reset();  
  102. }  
  103.   
  104. /*显示4个点,一个矩形和图像内容,因为后面的步骤很多地方都要用到这个函数,所以单独拿出来*/  
  105. void GCApplication::showImage() const  
  106. {  
  107.     if( image->empty() || winName->empty() )  
  108.         return;  
  109.   
  110.     Mat res;  
  111.     Mat binMask;  
  112.     if( !isInitialized )  
  113.         image->copyTo( res );  
  114.     else  
  115.     {  
  116.         getBinMask( mask, binMask );  
  117.         image->copyTo( res, binMask );  //按照最低位是0还是1来复制,只保留跟前景有关的图像,比如说可能的前景,可能的背景  
  118.     }  
  119.   
  120.     vector<Point>::const_iterator it;  
  121.     /*下面4句代码是将选中的4个点用不同的颜色显示出来*/  
  122.     for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )  //迭代器可以看成是一个指针  
  123.         circle( res, *it, radius, BLUE, thickness );  
  124.     for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )  //确定的前景用红色表示  
  125.         circle( res, *it, radius, RED, thickness );  
  126.     for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )  
  127.         circle( res, *it, radius, LIGHTBLUE, thickness );  
  128.     for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )  
  129.         circle( res, *it, radius, PINK, thickness );  
  130.   
  131.     /*画矩形*/  
  132.     if( rectState == IN_PROCESS || rectState == SET )  
  133.         rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);  
  134.   
  135.     imshow( *winName, res );  
  136. }  
  137.   
  138. /*该步骤完成后,mask图像中rect内部是3,外面全是0*/  
  139. void GCApplication::setRectInMask()  
  140. {  
  141.     assert( !mask.empty() );  
  142.     mask.setTo( GC_BGD );   //GC_BGD == 0  
  143.     rect.x = max(0, rect.x);  
  144.     rect.y = max(0, rect.y);  
  145.     rect.width = min(rect.width, image->cols-rect.x);  
  146.     rect.height = min(rect.height, image->rows-rect.y);  
  147.     (mask(rect)).setTo( Scalar(GC_PR_FGD) );    //GC_PR_FGD == 3,矩形内部,为可能的前景点  
  148. }  
  149.   
  150. void GCApplication::setLblsInMask( int flags, Point p, bool isPr )  
  151. {  
  152.     vector<Point> *bpxls, *fpxls;  
  153.     uchar bvalue, fvalue;  
  154.     if( !isPr ) //确定的点  
  155.     {  
  156.         bpxls = &bgdPxls;  
  157.         fpxls = &fgdPxls;  
  158.         bvalue = GC_BGD;    //0  
  159.         fvalue = GC_FGD;    //1  
  160.     }  
  161.     else    //概率点  
  162.     {  
  163.         bpxls = &prBgdPxls;  
  164.         fpxls = &prFgdPxls;  
  165.         bvalue = GC_PR_BGD; //2  
  166.         fvalue = GC_PR_FGD; //3  
  167.     }  
  168.     if( flags & BGD_KEY )  
  169.     {  
  170.         bpxls->push_back(p);  
  171.         circle( mask, p, radius, bvalue, thickness );   //该点处为2  
  172.     }  
  173.     if( flags & FGD_KEY )  
  174.     {  
  175.         fpxls->push_back(p);  
  176.         circle( mask, p, radius, fvalue, thickness );   //该点处为3  
  177.     }  
  178. }  
  179.   
  180. /*鼠标响应函数,参数flags为CV_EVENT_FLAG的组合*/  
  181. void GCApplication::mouseClick( int event, int x, int y, int flags, void* )  
  182. {  
  183.     // TODO add bad args check  
  184.     switch( event )  
  185.     {  
  186.     case CV_EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels  
  187.         {  
  188.             bool isb = (flags & BGD_KEY) != 0,  
  189.                 isf = (flags & FGD_KEY) != 0;  
  190.             if( rectState == NOT_SET && !isb && !isf )//只有左键按下时  
  191.             {  
  192.                 rectState = IN_PROCESS; //表示正在画矩形  
  193.                 rect = Rect( x, y, 1, 1 );  
  194.             }  
  195.             if ( (isb || isf) && rectState == SET ) //按下了alt键或者shift键,且画好了矩形,表示正在画前景背景点  
  196.                 lblsState = IN_PROCESS;  
  197.         }  
  198.         break;  
  199.     case CV_EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels  
  200.         {  
  201.             bool isb = (flags & BGD_KEY) != 0,  
  202.                 isf = (flags & FGD_KEY) != 0;  
  203.             if ( (isb || isf) && rectState == SET ) //正在画可能的前景背景点  
  204.                 prLblsState = IN_PROCESS;  
  205.         }  
  206.         break;  
  207.     case CV_EVENT_LBUTTONUP:  
  208.         if( rectState == IN_PROCESS )  
  209.         {  
  210.             rect = Rect( Point(rect.x, rect.y), Point(x,y) );   //矩形结束  
  211.             rectState = SET;  
  212.             setRectInMask();  
  213.             assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );  
  214.             showImage();  
  215.         }  
  216.         if( lblsState == IN_PROCESS )   //已画了前后景点  
  217.         {  
  218.             setLblsInMask(flags, Point(x,y), false);    //画出前景点  
  219.             lblsState = SET;  
  220.             showImage();  
  221.         }  
  222.         break;  
  223.     case CV_EVENT_RBUTTONUP:  
  224.         if( prLblsState == IN_PROCESS )  
  225.         {  
  226.             setLblsInMask(flags, Point(x,y), true); //画出背景点  
  227.             prLblsState = SET;  
  228.             showImage();  
  229.         }  
  230.         break;  
  231.     case CV_EVENT_MOUSEMOVE:  
  232.         if( rectState == IN_PROCESS )  
  233.         {  
  234.             rect = Rect( Point(rect.x, rect.y), Point(x,y) );  
  235.             assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );  
  236.             showImage();    //不断的显示图片  
  237.         }  
  238.         else if( lblsState == IN_PROCESS )  
  239.         {  
  240.             setLblsInMask(flags, Point(x,y), false);  
  241.             showImage();  
  242.         }  
  243.         else if( prLblsState == IN_PROCESS )  
  244.         {  
  245.             setLblsInMask(flags, Point(x,y), true);  
  246.             showImage();  
  247.         }  
  248.         break;  
  249.     }  
  250. }  
  251.   
  252. /*该函数进行grabcut算法,并且返回算法运行迭代的次数*/  
  253. int GCApplication::nextIter()  
  254. {  
  255.     if( isInitialized )  
  256.         //使用grab算法进行一次迭代,参数2为mask,里面存的mask位是:矩形内部除掉那些可能是背景或者已经确定是背景后的所有的点,且mask同时也为输出  
  257.         //保存的是分割后的前景图像  
  258.         grabCut( *image, mask, rect, bgdModel, fgdModel, 1 );  
  259.     else  
  260.     {  
  261.         if( rectState != SET )  
  262.             return iterCount;  
  263.   
  264.         if( lblsState == SET || prLblsState == SET )  
  265.             grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK );  
  266.         else  
  267.             grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT );  
  268.   
  269.         isInitialized = true;  
  270.     }  
  271.     iterCount++;  
  272.   
  273.     bgdPxls.clear(); fgdPxls.clear();  
  274.     prBgdPxls.clear(); prFgdPxls.clear();  
  275.   
  276.     return iterCount;  
  277. }  
  278.   
  279. GCApplication gcapp;  
  280.   
  281. static void on_mouse( int event, int x, int y, int flags, void* param )  
  282. {  
  283.     gcapp.mouseClick( event, x, y, flags, param );  
  284. }  
  285.   
  286. int main( int argc, char** argv )  
  287. {  
  288.   
  289.     string filename = "D:\images\dog.jpg";  
  290.     Mat image = imread( filename, 1 );  
  291.     if( image.empty() )  
  292.     {  
  293.         cout << "  Durn, couldn't read image filename " << filename << endl;  
  294.         return 1;  
  295.     }  
  296.   
  297.     help();  
  298.   
  299.     const string winName = "image";  
  300.     cvNamedWindow( winName.c_str(), CV_WINDOW_AUTOSIZE );  
  301.     cvSetMouseCallback( winName.c_str(), on_mouse, 0 );  
  302.   
  303.     gcapp.setImageAndWinName( image, winName );  
  304.     gcapp.showImage();  
  305.   
  306.     for(;;)  
  307.     {  
  308.         int c = cvWaitKey(0);  
  309.         switch( (char) c )  
  310.         {  
  311.         case 'x1b':  
  312.             cout << "Exiting ..." << endl;  
  313.             goto exit_main;  
  314.         case 'r':  
  315.             cout << endl;  
  316.             gcapp.reset();  
  317.             gcapp.showImage();  
  318.             break;  
  319.         case 'n':  
  320.             int iterCount = gcapp.getIterCount();  
  321.             cout << "<" << iterCount << "... ";  
  322.             int newIterCount = gcapp.nextIter();  
  323.             if( newIterCount > iterCount )  
  324.             {  
  325.                 gcapp.showImage();  
  326.                 cout << iterCount << ">" << endl;  
  327.             }  
  328.             else  
  329.                 cout << "rect must be determined>" << endl;  
  330.             break;  
  331.         }  
  332.     }  
  333.   
  334. exit_main:  
  335.     cvDestroyWindow( winName.c_str() );  
  336.     return 0;  
  337. }  

opencv2 书本上给的GrabCut的方法代码,实现起来速度也是不太能够忍受
[cpp] view plain copy
 
  1. class WatershedSegmenter {  
  2.   
  3. private:  
  4.   
  5.     cv::Mat markers;  
  6.   
  7. public:  
  8.   
  9.     void setMarkers(const cv::Mat& markerImage) {  
  10.   
  11.         // Convert to image of ints  
  12.         markerImage.convertTo(markers,CV_32S);  
  13.     }  
  14.   
  15.     cv::Mat process(const cv::Mat &image) {  
  16.   
  17.         // Apply watershed  
  18.         cv::watershed(image,markers);  
  19.   
  20.         return markers;  
  21.     }  
  22.   
  23.     // Return result in the form of an image  
  24.     cv::Mat getSegmentation() {  
  25.   
  26.         cv::Mat tmp;  
  27.         // all segment with label higher than 255  
  28.         // will be assigned value 255  
  29.         markers.convertTo(tmp,CV_8U);  
  30.   
  31.         return tmp;  
  32.     }  
  33.   
  34.     // Return watershed in the form of an image  
  35.     cv::Mat getWatersheds() {  
  36.   
  37.         cv::Mat tmp;  
  38.         markers.convertTo(tmp,CV_8U,255,255);  
  39.   
  40.         return tmp;  
  41.     }  
  42. };  
  43. int main()  
  44. {  
  45.     using namespace cv;  
  46.     // Open another image  
  47.     Mat image= cv::imread("D:\images\tower.jpg");  
  48.   
  49.     // define bounding rectangle  
  50.     cv::Rect rectangle(50,70,image.cols-150,image.rows-180);  
  51.   
  52.     cv::Mat result; // segmentation result (4 possible values)  
  53.     cv::Mat bgModel,fgModel; // the models (internally used)  
  54.     // GrabCut segmentation  
  55.     cv::grabCut(image,    // input image  
  56.         result,   // segmentation result  
  57.         rectangle,// rectangle containing foreground  
  58.         bgModel,fgModel, // models  
  59.         1,        // number of iterations  
  60.         cv::GC_INIT_WITH_RECT); // use rectangle  
  61.   
  62.     // Get the pixels marked as likely foreground  
  63.     cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);  
  64.     // Generate output image  
  65.     cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));  
  66.     image.copyTo(foreground,result); // bg pixels not copied  
  67.   
  68.     // draw rectangle on original image  
  69.     cv::rectangle(image, rectangle, cv::Scalar(255,255,255),1);  
  70.     cv::namedWindow("Image");  
  71.     cv::imshow("Image",image);  
  72.   
  73.     // display result  
  74.     cv::namedWindow("Segmented Image");  
  75.     cv::imshow("Segmented Image",foreground);  
  76.   
  77.     // Open another image  
  78.     image= cv::imread("D:\images\tower.jpg");  
  79.   
  80.     // define bounding rectangle  
  81.     cv::Rect rectangle2(10,100,380,180);  
  82.   
  83.     cv::Mat bkgModel,fgrModel; // the models (internally used)  
  84.     // GrabCut segmentation  
  85.     cv::grabCut(image,  // input image  
  86.         result, // segmentation result  
  87.         rectangle2,bkgModel,fgrModel,5,cv::GC_INIT_WITH_RECT);  
  88.     // Get the pixels marked as likely foreground  
  89.     //      cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);  
  90.     result= result&1;  
  91.     foreground.create(image.size(),CV_8UC3);  
  92.     foreground.setTo(cv::Scalar(255,255,255));  
  93.     image.copyTo(foreground,result); // bg pixels not copied  
  94.   
  95.     // draw rectangle on original image  
  96.     cv::rectangle(image, rectangle2, cv::Scalar(255,255,255),1);  
  97.     cv::namedWindow("Image 2");  
  98.     cv::imshow("Image 2",image);  
  99.   
  100.     // display result  
  101.     cv::namedWindow("Foreground objects");  
  102.     cv::imshow("Foreground objects",foreground);  
  103.     waitKey(0);  
  104.     system("pause");  
  105.     return 0;  
  106. }  


 
0
原文地址:https://www.cnblogs.com/jukan/p/7245075.html