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)

时间: 2024-08-07 04:20:06

via OpenCv 提取分离前景和背景的相关文章

OpenCV提取图像轮廓总结

OpenCV函数 cvFindContours提取轮廓 :点击打开链接  点击打开链接 点击打开链接 点击打开链接 提取元素的轮廓及形状描述子 点击打开链接 提取轮廓的点坐标 轮廓提取后,它是用关键点组成的,下面提取出这些关键点. 1.先输出所有关键点的个数cout<<"elements"<<contour->total<<endl; 2.for(int i=0;i<contour->total;++i) { CvPoint* p

Webpack 4 学习07(提取分离打包css)

前面讲过 将css文件引入到js文件中,然后一起打包成js文件,现在我们学习单独提取分离css并且打包. 安装插件min-css-extract-plugin npm install mini-css-extract-plugin --save-dev 配置webpack.config.js 引入插件 const MiniCssPlugin = require("mini-css-extract-plugin"); rules 设置 { test:/\.css$/, use:[Mini

opencv实现图像分割,分离前景和背景

简介 如题,本篇就是讲解和使用opencv函数grabcut,来实现图像前景与背景的分离. 函数原型 1.opencv官方介绍:opencv官方grabcut介绍 2.网上童鞋翻译解释:学习OpenCV--学习grabcut算法 3.大致内容如下: 函数原型: void grabCut(InputArray img, InputOutputArray mask, Rect rect, InputOutputArray bgdModel, InputOutputArray fgdModel, in

opencv实现图像分割,分离前景和背景(2)

简介 如题,本篇是在前一篇的基础上进一步讲解的第三个图像背景分离例子. 实例介绍 这个例子是在上一个加入鼠标操作实例的进一步操作. 本例:可以在鼠标选框完成之后,1.通过shift+鼠标右键来选择设置图像对应位置为前景. 2.通过ctrl +鼠标右键来选择设置图像对应位置为背景景. 3.按下键值'n',进行图像背景分离计算,并显示结果. 4.按下键值'esc',退出程序. 实例讲解 具体代码 #include "opencv2/highgui/highgui.hpp" #include

opencv提取surf特征点出现的错误

opencv实现surf特征的提取.本来是一个非常easy的代码,结果我执行时却出现了各种错误,以下来谈谈我出现的错误及问题的解决过程. 首先,我把提取surf特征的过程整合成了一个函数,我单独建立一个project读取两张图片,然后调用这个surf提取的函数时时不管是debug还是release模式下都是没有问题的.当我把这个函数加入到我如今已有的project代码里面的时候出现了各种奇葩错误.以下是我surf特征提取的函数 void surfdetect(IplImage *image1,I

OpenCV提取轮廓 去掉面积小的轮廓

转自:http://www.kaixuela.net/?p=23 #include <stdio.h> #include "cv.h" #include "cxcore.h" #include "highgui.h" #include <iostream> using namespace std; #pragma comment(lib,"cv.lib") #pragma comment(lib,&qu

OpenCV运动目标检测——帧间差,混合高斯模型方法

一.简单的帧间差方法 帧差法是在连续的图像序列中两个或三个相邻帧间采用基于像素的时间差分并且闽值化来提取图像中的运动区域. 代码: int _tmain(int argc, _TCHAR* argv[]) { VideoCapture capture("bike.avi"); if(!capture.isOpened()) return -1; double rate = capture.get(CV_CAP_PROP_FPS); int delay = 1000/rate; Mat

《Master Opencv...读书笔记》非刚性人脸跟踪 IV (终)

一.我们目前为止拥有什么 为了有一个连续完整的认识,在介绍最后一节前,先梳理下至今我们训练了哪些数据特征,并且训练它们的目的是什么. 1.      ft_data:利用手工标注工具,获取最原始的样本训练数据,包括以下内容: 图像名称集合imnames:表明在哪幅图像上标注特征点: 二维坐标集合points:手工标准点,后续更高级别特征均围绕这些特征点展开: 对称坐标索引集合symmetry:标注样本图像的镜像图像上的特征点,扩大样本库: 连接索引集合connections:描述手工标注的人脸特

[zt]OpenCV如何获取视频当前的一帧图像

(OpenCV读取视频.OpenCV提取视频每一帧.每一帧图片合成新的AVI视频)CvCapture 是视频获取结构 被用来作为视频获取函数的一个参数 比如 CvCapture* cap; IplImage* cvQueryFrame( cap ); 从摄像头或者文件中抓取并返回一帧———————————————————————— Opencv读取视频代码 #include "stdafx.h" #include"highgui.h" int main(int ar