via OpenCv 提取分离前景和背景

  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 << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n"  
 12         "and then grabcut will attempt to segment it out.\n"  
 13         "Call:\n"  
 14         "./grabcut <image_name>\n"  
 15         "\nSelect a rectangular area around the object you want to segment\n" <<  
 16         "\nHot keys: \n"  
 17         "\tESC - quit the program\n"  
 18         "\tr - restore the original image\n"  
 19         "\tn - next iteration\n"  
 20         "\n"  
 21         "\tleft mouse button - set rectangle\n"  
 22         "\n"  
 23         "\tCTRL+left mouse button - set GC_BGD pixels\n"  
 24         "\tSHIFT+left mouse button - set CG_FGD pixels\n"  
 25         "\n"  
 26         "\tCTRL+right mouse button - set GC_PR_BGD pixels\n"  
 27         "\tSHIFT+right mouse button - set CG_PR_FGD pixels\n" << 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( "Lena.jpg", 1 );  
291     if( image.empty() )  
292     {  
293         cout << "\n 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     
336     cvDestroyWindow( winName.c_str() );  
337     return 0;  
338 }  
GrabCut

 

设置snake算法使用的参数:
     alpha代表点相互靠拢的权值(0-1.0)
     beta表示弯曲能量(越小越容易弯曲)(0-1.0),
     gamma表示整体能量(0-1.0)

posted @ 2016-08-19 20:02  Henry2017  阅读(417)  评论(0编辑  收藏  举报