当前位置: 首页 > 编程日记 > 正文

前景检测算法_3(GMM)

摘要

本文通过opencv来实现一种前景检测算法——GMM,算法采用的思想来自论文[1][2][4]。在进行前景检测前,先对背景进行训练,对图像中每个背景采用一个混合高斯模型进行模拟,每个背景的混合高斯的个数可以自适应。然后在测试阶段,对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为是背景,否则认为是前景。由于整个过程GMM模型在不断更新学习中,所以对动态背景有一定的鲁棒性。最后通过对一个有树枝摇摆的动态背景进行前景检测,取得了较好的效果。

关键字:GMM,opencv,前景检测

前言

前景检测主要分为帧差法,平均背景法,光流法,前景建模法,背景非参数估计,背景建模法等。而本文要实现的方法属于背景建模法中的一种——GMM,也称混合高斯模型。

混合高斯模型最早在计算机视觉中的应用是Stauffer et al.[1],作者将其用来做前景检测,主要是用于视频监控领域,这个系统和稳定且有自学能力,能在户外环境跑16个多月。KaewTraKulPong et al.[2]将GMM的训练过程做了改进,将训练过程分为2步进行,前L帧采用EM算法进行权值,均值,方差更新,后面的过程就采用[1]中的方法进行更新,取得了更好的检测效果。Zivkovic et al.在[3]中对GMM理论做了全面的论述,使得GMM理论的使用不仅金限于计算机视觉领域。并且该作者在[4]将该理论进一步具体到背景减图的前景检测中来,即加入了参数估计的先验知识,取得了很好的效果和稳定性。

最近几年陆续有学者对GMM的背景见图中的应用做了更深一步的研究,其代表性贡献见论文[5][6]。

  实现过程

本文中主要是根据[2][4]中提出的算法,采用其中的更新方差来根性模型中的3个参数,最后结合用opencv基本底层函数实现该算法。其主要过程如下:

  1. 首先将每个高斯的均值,方差,权值都设置为0,即初始化个模型矩阵参数。
  2. 采用视频中的T帧用来训练GMM模型。对每一个像素而言,建立其模型个数最大GMM_MAX_COMPONT个高斯的GMM模型。当第一个像素来,单独为其在程序中设置好其固定的初始均值,方差,并且权值设置为1。
  3. 非第一帧训练过程中,当后面来的像素值时,与前面已有的高斯的均值比较,如果该像素点的值与其模型均值差在3倍的方差内,则任务属于该高斯。此时用如下方程进行更新:

4. 当到达训练的帧数T后,进行不同像素点GMM个数自适应的选择。首先用权值除以方差对各个高斯进行从大到小排序,然后选取最前面B个高斯,使

这样就可以很好的消除训练过程中的噪声点。

5. 在测试阶段,对新来像素点的值与B个高斯中的每一个均值进行比较,如果其差值在2倍的方差之间的话,则认为是背景,否则认为是前景。并且只要其中有一个高斯分量满足该条件就认为是前景。前景赋值为255,背景赋值为0。这样就形成了一副前景二值图。

6. 由于前景二值图中含有很多噪声,所以采用了形态学的开操作将噪声缩减到0,紧接着用闭操作重建由于开操作丢失的边缘部分的信息。消除了不连通的小噪声点。

上面是该算法实现的大概流程,但是当我们在具体编程时,还是有很多细节的地方需要注意,比如有些参数值的选择。在这里,经过试验将一些常用的参数值声明如下:

  1. 3个参数的值的更新方差中,取其中的学习率为0.005。也就是说T等于200。
  2. 定义每个像素的最大混合高斯个数取7。
  3. 取视频的前200帧进行训练。
  4. 取Cf为0.3。即满足权值大于0.7的个数为自适应高斯的个数B。
  5. 训练过程中,需要新建立一个高斯时,其权值取值设为与学习率大小值相等,即0.005。
  6. 训练过程中,需要新建立一个高斯时,取该高斯的均值为该输入像素值大小本身。方差为15。
  7. 训练过程中,需要新建立一个高斯时,取该高斯的方差15。

   程序流程框图

该工程的流程框图如下图所示:

试验结果

 

本次试验的数据为摇摆的树枝作为背景,Waving Trees,其来源网址为:http://research.microsoft.com/en-us/um/people/jckrumm/WallFlower/TestImages.htm 由于该数据是286张bmp格式的图片,所以用的前200张图片来作为GMM参数训练,后186张作为测试。训练的过程中树枝被很大幅度的摆动,测试过程中有行人走动,该行人是需要迁就检测的部分。

下图为训练过程中动态背景截图

由上图可以看出,树枝在不断摇摆,可见其背景是动态的。

没有形态学处理的结果如下:

下图是有简单形态学的试验结果:

(上面2幅图的左边为测试原始图片,右图为检测效果图)

总结

通过本次试验,不仅学习到了GMM的相关理论知识,以及背景减图法在前景检测中的应用。更重要的时,对opencv在计算机视觉中的使用更进一步的熟悉了。另外对于目标检测的难点有了更深一层的理解。

参考文献

  1. Stauffer, C. and W. E. L. Grimson (1999). Adaptive background mixture models for real-time tracking, IEEE.
  2. KaewTraKulPong, P. and R. Bowden (2001). An improved adaptive background mixture model for real-time tracking with shadow detection.
  3. Zivkovic, Z. and F. van der Heijden (2004). "Recursive unsupervised learning of finite mixture models." Pattern Analysis and Machine Intelligence, IEEE Transactions on 26(5): 651-656.
  4. Zivkovic, Z. and F. van der Heijden (2006). "Efficient adaptive density estimation per image pixel for the task of background subtraction." Pattern recognition letters 27(7): 773-780.
  5. Bouzerdoum, A., A. Beghdadi, et al. (2010). "On the analysis of background subtraction techniques using Gaussian mixture models."
  6. Lin, H. H., J. H. Chuang, et al. (2011). "Regularized background adaptation: a novel learning rate control scheme for Gaussian mixture modeling." Image Processing, IEEE Transactions on 20(3): 822-836.

   后续改进

需要改进的地方:

1. 程序运行的速度太慢,很多参数都是浮点数,每个像素都要建立一个gmm,gmm个数本身比较多,所以训练过程中速度比较慢,代码需要优化。

2. 最后生成的前景图需要用连通域处理算法进行修整,即需要形态学操作,然后找出连通域大小满足要求的轮廓,用多边形拟合来进行处理。这种算法在《learning opencv》一书中有提到。后续有时间加入该算法,效果会好很多的。

附录:工程代码

  1  //gmm.cpp : 定义控制台应用程序的入口点。
  2 #include "stdafx.h"
  3 #include "cv.h"
  4 #include "highgui.h"
  5 #include <opencv2/imgproc/imgproc.hpp>
  6 #include <opencv2/core/core.hpp>
  7 #include <opencv2/highgui/highgui.hpp>
  8 #include <stdio.h>
  9 #include <iostream>
 10 
 11 using namespace cv;
 12 using namespace std;
 13 
 14 //定义gmm模型用到的变量
 15 #define GMM_MAX_COMPONT 6
 16 #define GMM_LEARN_ALPHA 0.005    //该学习率越大的话,学习速度太快,效果不好
 17 #define GMM_THRESHOD_SUMW 0.7    //如果取值太大了的话,则更多树的部分都被检测出来了
 18 #define END_FRAME 200
 19 
 20 bool pause=false;
 21 
 22 Mat w[GMM_MAX_COMPONT];
 23 Mat u[GMM_MAX_COMPONT];
 24 Mat sigma[GMM_MAX_COMPONT];
 25 Mat fit_num,gmask,foreground;
 26 vector<Mat> output_m;
 27 Mat output_img;
 28 
 29 float temp_w,temp_sigma;
 30 unsigned char temp_u;
 31 int i=-1;
 32 
 33 
 34 //For connected components:
 35 int CVCONTOUR_APPROX_LEVEL = 2;   // Approx.threshold - the bigger it is, the simpler is the boundary
 36 int CVCLOSE_ITR = 1;        
 37 
 38 //Just some convienience macros
 39 #define CV_CVX_WHITE    CV_RGB(0xff,0xff,0xff)
 40 #define CV_CVX_BLACK    CV_RGB(0x00,0x00,0x00)
 41 
 42 //gmm整体初始化函数声明
 43 void gmm_init(Mat img);
 44 
 45 //gmm第一帧初始化函数声明
 46 void gmm_first_frame(Mat img);
 47 
 48 //gmm训练过程函数声明
 49 void gmm_train(Mat img);
 50 
 51 //对输入图像每个像素gmm选择合适的个数函数声明
 52 void gmm_fit_num(Mat img);
 53 
 54 //gmm测试函数的实现
 55 void gmm_test(Mat img);
 56 
 57 //连通域去噪函数声明
 58 void find_connected_components(Mat img);
 59 //void cvconnectedComponents(IplImage *mask, int poly1_hull0, float perimScale, int *num, CvRect *bbs, CvPoint *centers);
 60 
 61 int main(int argc, const char* argv[])
 62 {
 63     Mat img,img_gray;
 64     char str_num[5];
 65 
 66 
 67 //    char *str_num;//why does this definition not work?
 68     String str="WavingTrees/b00";//string,the 's' can be a captial or lower-caseletters
 69 
 70     /****read the first image,and reset the array w,u,sigma****/
 71     img=imread("WavingTrees/b00000.bmp");
 72     if(img.empty())                
 73         return -1;
 74 
 75     output_img=Mat::zeros(img.size(),img.type());
 76     cvtColor(img,img_gray,CV_BGR2GRAY);//covert the colorful image to the corresponding gray-level image
 77 
 78     /****initialization the three parameters ****/
 79     gmm_init(img_gray);
 80     fit_num=Mat(img.size(),CV_8UC1,-1);//初始化为1
 81     gmask=Mat(img.size(),CV_8UC1,-1);
 82     foreground=img.clone();
 83     split(img,output_m);
 84 
 85     output_m[0]=Mat::zeros(img.size(),output_m[0].type());
 86     output_m[1]=Mat::zeros(img.size(),output_m[0].type());
 87     output_m[2]=Mat::zeros(img.size(),output_m[0].type());
 88 
 89     namedWindow("src",WINDOW_AUTOSIZE);
 90     namedWindow("gmask",WINDOW_AUTOSIZE);
 91     
 92     //在定义视频输出对象时,文件名一定后面要加后缀,比如这里的.avi,否则是输出不了视频的!并且这里只能是avi格式的,当参数为('P','I','M','1')时
 93     VideoWriter output_src("src.avi",CV_FOURCC('P','I','M','1'),20,Size(160,120),1);//c++版本的opencv用Size函数即可,c版本的用cvSize()函数
 94     //VideoWriter output_src("src.avi",CV_FOURCC('M','J','P','G'),5,Size(160,120),1);//c++版本的opencv用Size函数即可,c版本的用cvSize()函数
 95     VideoWriter output_dst("dst.avi",CV_FOURCC('P','I','M','1'),20,Size(160,120),1);//这样输出的是3个通道的数据
 96     while(1)
 97 
 98     {
 99         if(!pause)
100         {
101             /****read image from WavingTrees****/
102             i++;
103             _itoa_s(i,str_num,10);//the latest name is _itoa_s or _itoa,not the itoa,although iota can be used,deprecated
104             if(i<10)
105                 str+="00";
106             else if(i<100)
107                 str+="0";
108             else if(i>285)//we used the first 285 frames to learn the gmm model
109                 i=-1;
110             str+=str_num;
111             str+=".bmp";
112 
113             img=imread(str);
114             if(img.empty())
115                 break;
116             str="WavingTrees/b00";//after read,str must be reseted ;
117 
118             cvtColor(img,img_gray,CV_BGR2GRAY);//covert the colorful image to the corresponding gray-level image
119 
120             /****when it is the first frame,set the default parameter****/
121             if(1==i)
122             {
123                 gmm_first_frame(img_gray);
124             }
125 
126             //the train of gmm phase
127             //if(1<i&&i<5&&i!=3)//由此可知当i大于等于3以后,就会一直出现错误,且错误在内部排序的部分
128             if(1<i<END_FRAME)
129             {
130                 gmm_train(img_gray);
131             }//end the train phase
132         
133             cout<<i<<endl;
134             /****chose the fitting number of component in gmm****/
135             if(END_FRAME==i)
136             {
137                 gmm_fit_num(img_gray);
138         //        cout<<fit_num<<endl;//其输出值有4个高斯的,但也有0个高斯的,why?照理说不可能的啊!
139             }
140 
141             /****start the test phase****/
142             if(i>=END_FRAME)
143             {
144                  output_src<<img;
145                  gmm_test(img_gray);
146                  find_connected_components(img_gray);
147 
148                  output_m[0]=gmask.clone();
149                  output_m[1]=gmask.clone();
150                  output_m[2]=gmask.clone();
151 
152                  merge(output_m,output_img);
153                  output_dst<<output_img;
154             }
155             if(285==i)
156             {
157                 return 0;
158             }
159             imshow("src",img);
160             imshow("gmask",gmask);
161         }
162         
163         char c=(char)waitKey(1);
164         if(c==27)//if press the ESC key,the exit the proggram
165             break;
166         if(c==' ')
167         //    pause=~pause;//if use '~',then the pause key cannot work,why?
168             pause=!pause;        
169     }
170     return 0;
171 }
172 
173 
174 //gmm初始化函数实现
175 void gmm_init(Mat img)
176 {
177     /****initialization the three parameters ****/
178     for(int j=0;j<GMM_MAX_COMPONT;j++)
179     {
180         w[j]=Mat(img.size(),CV_32FC1,0.0);//CV_32FC1本身体现了正负符号
181         u[j]=Mat(img.size(),CV_8UC1,-1);//为什么这里赋值为0时,后面的就一直出错?暂时还不知道原因,先赋值-1,其实内部存储的也是0
182         sigma[j]=Mat(img.size(),CV_32FC1,0.0);//float类型
183     }
184 
185     //为什么一下语句不能放在这个函数里面呢
186 //    output_m[0]=Mat(img.size(),CV_8UC1,0);
187 //    output_m[1]=Mat(img.size(),CV_8UC1,0);
188 //    output_m[2]=Mat(img.size(),CV_8UC1,0);
189 }
190 
191 
192 //gmm第一帧初始化函数实现
193 void gmm_first_frame(Mat img)
194 {
195     for(int m=0;m<img.rows;m++)
196         for(int n=0;n<img.cols;n++)        
197         {
198             w[0].at<float>(m,n)=1.0;
199 
200             //if the pixvel is gray-clever,then we should use unsigned char,not the unsigned int
201             u[0].at<unsigned char>(m,n)=img.at<unsigned char>(m,n);// 一定要注意其类型转换,否则会得不得预期的结果
202             sigma[0].at<float>(m,n)=15.0;//opencv 自带的gmm代码中用的是15.0
203 
204             for(int k=1;k<GMM_MAX_COMPONT;k++)    
205             {
206                 /****when assigment this,we must be very carefully****/
207                 w[k].at<float>(m,n)=0.0;
208                 u[k].at<unsigned char>(m,n)=-1;
209                 sigma[k].at<float>(m,n)=15.0;//防止后面排序时有分母为0的情况
210             }
211         }        
212 }
213 
214 
215 //gmm训练过程函数实现
216 void gmm_train(Mat img)
217 {
218     for(int m=0;m<img.rows;m++)
219         for(int n=0;n<img.cols;n++)
220         {
221             int k=0;
222             int nfit=0;
223             for(;k<GMM_MAX_COMPONT;k++)
224             {
225                 //    if(w[k].at<float>(m,n)!=0)//只有在权值不为0的情况下才进行比较
226                 //    {
227                 int delam=abs(img.at<unsigned char>(m,n)-u[k].at<unsigned char>(m,n));//防止溢出
228                 long dis=delam*delam;
229                 if(dis<3.0*sigma[k].at<float>(m,n))//the present pixpel is fit the component
230                 {
231                     /****update the weight****/
232                     w[k].at<float>(m,n)=w[k].at<float>(m,n)+GMM_LEARN_ALPHA*(1-w[k].at<float>(m,n));
233 
234                     /****update the average****/
235                     u[k].at<unsigned char>(m,n)=u[k].at<unsigned char>(m,n)+(GMM_LEARN_ALPHA/w[k].at<float>(m,n))*delam;
236 
237                     /****update the variance****/
238                     sigma[k].at<float>(m,n)=sigma[k].at<float>(m,n)+(GMM_LEARN_ALPHA/w[k].at<float>(m,n))*(dis-sigma[k].at<float>(m,n));
239 
240     //                break;
241                 }
242                 else{
243                     w[k].at<float>(m,n)=w[k].at<float>(m,n)+GMM_LEARN_ALPHA*(0-w[k].at<float>(m,n));
244                     nfit++;
245                 }        
246                 //        }
247             }
248 
249             ////训练过程加速算法
250             //for(int bb=k+1;bb<GMM_MAX_COMPONT;bb++)
251             //{
252             //    w[bb].at<float>(m,n)=w[bb].at<float>(m,n)+GMM_LEARN_ALPHA*(0-w[bb].at<float>(m,n));
253             //    nfit++;
254             //}
255 
256             //对gmm各个高斯进行排序,从大到小排序,排序依据为w/sigma
257             for(int kk=0;kk<GMM_MAX_COMPONT;kk++)
258             {
259                 for(int rr=kk;rr<GMM_MAX_COMPONT;rr++)
260                 {
261                     //怎样才能做到gmm结构体整体排序呢?
262                     if(w[rr].at<float>(m,n)/sigma[rr].at<float>(m,n)>w[kk].at<float>(m,n)/sigma[kk].at<float>(m,n))
263                     {
264                         //权值交换
265                         temp_w=w[rr].at<float>(m,n);
266                         w[rr].at<float>(m,n)=w[kk].at<float>(m,n);
267                         w[kk].at<float>(m,n)=temp_w;
268 
269                         //均值交换
270                         temp_u=u[rr].at<unsigned char>(m,n);
271                         u[rr].at<unsigned char>(m,n)=u[kk].at<unsigned char>(m,n);
272                         u[kk].at<unsigned char>(m,n)=temp_u;
273 
274                         //方差交换
275                         temp_sigma=sigma[rr].at<float>(m,n);
276                         sigma[rr].at<float>(m,n)=sigma[kk].at<float>(m,n);
277                         sigma[kk].at<float>(m,n)=temp_sigma;
278                     }
279                 }
280             }
281 
282             //****如果没有满足条件的高斯,则重新开始算一个高斯分布****/
283             if(nfit==GMM_MAX_COMPONT&&0==w[GMM_MAX_COMPONT-1].at<float>(m,n))//if there is no exit component fit,then start a new componen
284             {
285                 //不能写为for(int h=0;h<MAX_GMM_COMPONT&&((0==w[h].at<float>(m,n)));h++),因为这样明显h不会每次都加1
286                 for(int h=0;h<GMM_MAX_COMPONT;h++)
287                 {
288                     if((0==w[h].at<float>(m,n)))
289                     {
290                         w[h].at<float>(m,n)=GMM_LEARN_ALPHA;//按照论文的参数来的
291                         u[h].at<unsigned char>(m,n)=(unsigned char)img.at<unsigned char>(m,n);
292                         sigma[h].at<float>(m,n)=15.0;//the opencv library code is 15.0
293                         for(int q=0;q<GMM_MAX_COMPONT&&q!=h;q++)
294                         {
295                             /****update the other unfit's weight,u and sigma remain unchanged****/
296                             w[q].at<float>(m,n)*=1-GMM_LEARN_ALPHA;//normalization the weight,let they sum to 1
297                         }
298                         break;//找到第一个权值不为0的即可
299                     }                            
300                 }
301             }
302             //如果GMM_MAX_COMPONT都曾经赋值过,则用新来的高斯代替权值最弱的高斯,权值不变,只更新均值和方差
303             else if(nfit==GMM_MAX_COMPONT&&w[GMM_MAX_COMPONT-1].at<float>(m,n)!=0)
304             {
305                 u[GMM_MAX_COMPONT-1].at<unsigned char>(m,n)=(unsigned char)img.at<unsigned char>(m,n);
306                 sigma[GMM_MAX_COMPONT-1].at<float>(m,n)=15.0;//the opencv library code is 15.0
307             }
308 
309             
310         }
311 }//end the train phase
312 
313 
314 //对输入图像每个像素gmm选择合适的个数
315 void gmm_fit_num(Mat img)
316 {
317     //float sum_w=0.0;//重新赋值为0,给下一个像素做累积
318     for(int m=0;m<img.rows;m++)
319         for(int n=0;n<img.cols;n++)
320         {
321             float sum_w=0.0;//重新赋值为0,给下一个像素做累积
322             //chose the fittest number fit_num
323             for(unsigned char a=0;a<GMM_MAX_COMPONT;a++)
324             {
325                 //cout<<w[a].at<float>(m,n)<<endl;
326                 sum_w+=w[a].at<float>(m,n);
327                 if(sum_w>=GMM_THRESHOD_SUMW)//如果这里THRESHOD_SUMW=0.6的话,那么得到的高斯数目都为1,因为每个像素都有一个权值接近1
328                 {
329                     fit_num.at<unsigned char>(m,n)=a+1;
330                     break;
331                 }
332             }
333         }
334 }
335 
336 
337 //gmm测试函数的实现
338 void gmm_test(Mat img)
339 {
340     for(int m=0;m<img.rows;m++)
341         for(int n=0;n<img.cols;n++)
342         {
343             unsigned char a=0;
344             for(;a<fit_num.at<unsigned char>(m,n);a++)
345             {
346                 //如果对sigma取根号的话,树枝当做前景的概率会更大,不过人被检测出来的效果也更好些;用2相乘,不用开根号效果还不错
347         //        if(abs(img.at<unsigned char>(m,n)-u[a].at<unsigned char>(m,n))<(unsigned char)(2*(sigma[a].at<float>(m,n))))
348                 if(abs(img.at<unsigned char>(m,n)-u[a].at<unsigned char>(m,n))<(unsigned char)(2.5*(sigma[a].at<float>(m,n))))
349                 {
350                     gmask.at<unsigned char>(m,n)=1;//背景
351                     break;
352                 }
353             }
354             if(a==fit_num.at<unsigned char>(m,n))
355                 gmask.at<unsigned char>(m,n)=255;//前景
356         }
357 }
358 
359 //连通域去噪函数实现
360 void find_connected_components(Mat img)
361 {
362     morphologyEx(gmask,gmask,MORPH_OPEN,Mat());
363 //    morphologyEx(gmask,gmask,MORPH_CLOSE,Mat());
364 }
365 
366 ////连通域去噪函数实现
367 //void find_connected_components(Mat img)
368 //{
369 //    morphologyEx(gmask,gmask,MORPH_OPEN,Mat());
370 //    morphologyEx(gmask,gmask,MORPH_CLOSE,Mat());
371 ////    erode(gmask,gmask,Mat());//只腐蚀是不行的,人来了也被腐蚀掉了
372 //
373 //    vector<vector<Point>> contours;//由点向量组成的向量,所以有2个层次结构
374 //    vector<Vec4i> hierarchy;//typedef Vec<int,4>Vec4i;即由4个整数组成的向量
375 //    
376 //    //找到gmask的轮廓,存储在contours中,其拓扑结构存储在hierarchy中,且仅仅找出最外面的轮廓,用压缩算法只存储水平,垂直,斜对角线的端点
377 //    //其中暗含了hierarchy[i][2]=hierarchy[3]=-1,即其父轮廓和嵌套轮廓不用考虑
378 //    findContours(gmask,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
379 //    if(contours.size()==0)
380 //        return;
381 //
382 //    int idex=0;
383 //    for(idex=0;idex<contours.size();idex++)
384 //    {
385 //        const vector<Point>& c=contours[idex];
386 ////        const vector<Point>& cnull::zeros();
387 //        double len=arcLength(c,false);//求出轮廓的周长,并不一定要求其是封闭的
388 //        double q=(img.rows+img.cols)/4;
389 //        if(q>=len)
390 //        {
391 //            const vector<Point> &cnew=contours[idex];
392 //    //        Mat mcnew=Mat(cnew);
393 //    //        Mat mcnew;
394 //    //        approxPolyDP(Mat(c),mcnew,CVCONTOUR_APPROX_LEVEL,false);//多边形曲线拟合,并不一定要求其轮廓闭合
395 //    //        approxPolyDP(Mat(c),Mat(cnew),CVCONTOUR_APPROX_LEVEL,false);//多边形曲线拟合,并不一定要求其轮廓闭合
396 //            approxPolyDP(Mat(c),cnew,CVCONTOUR_APPROX_LEVEL,false);//多边形曲线拟合,并不一定要求其轮廓闭合
397 //    //        cnew=vector<Point>(mcnew);
398 //    //        contours[idex]=cnew;
399 //        }
400 ////        else contours[idex]=vector<Point(0,0,0)>;
401 //    }    
402 //
403 //}
404 
405 ///
406 //void cvconnectedComponents(IplImage *mask, int poly1_hull0, float perimScale, int *num, CvRect *bbs, CvPoint *centers)
407 // This cleans up the forground segmentation mask derived from calls to cvbackgroundDiff
408 //
409 // mask            Is a grayscale (8 bit depth) "raw" mask image which will be cleaned up
410 //
411 // OPTIONAL PARAMETERS:
412 // poly1_hull0    If set, approximate connected component by (DEFAULT) polygon, or else convex hull (0)
413 // perimScale     Len = image (width+height)/perimScale.  If contour len < this, delete that contour (DEFAULT: 4)
414 // num            Maximum number of rectangles and/or centers to return, on return, will contain number filled (DEFAULT: NULL)
415 // bbs            Pointer to bounding box rectangle vector of length num.  (DEFAULT SETTING: NULL)
416 // centers        Pointer to contour centers vectore of length num (DEFULT: NULL)
417 //
418 //void cvconnectedComponents(IplImage *mask, int poly1_hull0, float perimScale, int *num, CvRect *bbs, CvPoint *centers)
419 //{
420 //    static CvMemStorage*    mem_storage    = NULL;
421 //    static CvSeq*            contours    = NULL;
422 ////    static CvSeq**            firstContour;
423 //
424 //    //CLEAN UP RAW MASK
425 //    //开运算作用:平滑轮廓,去掉细节,断开缺口
426 //    cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_OPEN, CVCLOSE_ITR );//对输入mask进行开操作,CVCLOSE_ITR为开操作的次数,输出为mask图像
427 //    //闭运算作用:平滑轮廓,连接缺口
428 //    cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_CLOSE, CVCLOSE_ITR );//对输入mask进行闭操作,CVCLOSE_ITR为闭操作的次数,输出为mask图像
429 //
430 //    //FIND CONTOURS AROUND ONLY BIGGER REGIONS
431 //    if( mem_storage==NULL ) mem_storage = cvCreateMemStorage(0);
432 //    else cvClearMemStorage(mem_storage);
433 //
434 //    //CV_RETR_EXTERNAL=0是在types_c.h中定义的,CV_CHAIN_APPROX_SIMPLE=2也是在该文件中定义的
435 //    CvContourScanner scanner = cvStartFindContours(mask,mem_storage,sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
436 ////    CvContourScanner scanner = cvFindContours(mask,mem_storage,firstContour,sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
437 //    CvSeq* c;
438 //    int numCont = 0;
439 //    while( (c = cvFindNextContour( scanner )) != NULL )
440 //    {
441 //        double len = cvContourPerimeter( c );
442 //        double q = (mask->height + mask->width) /perimScale;   //calculate perimeter len threshold
443 //        if( len < q ) //Get rid of blob if it's perimeter is too small
444 //        {
445 //            cvSubstituteContour( scanner, NULL );
446 //        }
447 //        else //Smooth it's edges if it's large enough
448 //        {
449 //            CvSeq* c_new;
450 //            if(poly1_hull0) //Polygonal approximation of the segmentation
451 //                c_new = cvApproxPoly(c,sizeof(CvContour),mem_storage,CV_POLY_APPROX_DP, CVCONTOUR_APPROX_LEVEL,0);
452 //            else //Convex Hull of the segmentation
453 //                c_new = cvConvexHull2(c,mem_storage,CV_CLOCKWISE,1);
454 //            cvSubstituteContour( scanner, c_new );
455 //            numCont++;
456 //        }
457 //    }
458 //    contours = cvEndFindContours( &scanner );
459 //
460 //    // PAINT THE FOUND REGIONS BACK INTO THE IMAGE
461 //    cvZero( mask );
462 //    IplImage *maskTemp;
463 //    //CALC CENTER OF MASS AND OR BOUNDING RECTANGLES
464 //    if(num != NULL)
465 //    {
466 //        int N = *num, numFilled = 0, i=0;
467 //        CvMoments moments;
468 //        double M00, M01, M10;
469 //        maskTemp = cvCloneImage(mask);
470 //        for(i=0, c=contours; c != NULL; c = c->h_next,i++ )
471 //        {
472 //            if(i < N) //Only process up to *num of them
473 //            {
474 //                cvDrawContours(maskTemp,c,CV_CVX_WHITE, CV_CVX_WHITE,-1,CV_FILLED,8);
475 //                //Find the center of each contour
476 //                if(centers != NULL)
477 //                {
478 //                    cvMoments(maskTemp,&moments,1);
479 //                    M00 = cvGetSpatialMoment(&moments,0,0);
480 //                    M10 = cvGetSpatialMoment(&moments,1,0);
481 //                    M01 = cvGetSpatialMoment(&moments,0,1);
482 //                    centers[i].x = (int)(M10/M00);
483 //                    centers[i].y = (int)(M01/M00);
484 //                }
485 //                //Bounding rectangles around blobs
486 //                if(bbs != NULL)
487 //                {
488 //                    bbs[i] = cvBoundingRect(c);
489 //                }
490 //                cvZero(maskTemp);
491 //                numFilled++;
492 //            }
493 //            //Draw filled contours into mask
494 //            cvDrawContours(mask,c,CV_CVX_WHITE,CV_CVX_WHITE,-1,CV_FILLED,8); //draw to central mask
495 //        } //end looping over contours
496 //        *num = numFilled;
497 //        cvReleaseImage( &maskTemp);
498 //    }
499 //    //ELSE JUST DRAW PROCESSED CONTOURS INTO THE MASK
500 //    else
501 //    {
502 //        for( c=contours; c != NULL; c = c->h_next )
503 //        {
504 //            cvDrawContours(mask,c,CV_CVX_WHITE, CV_CVX_BLACK,-1,CV_FILLED,8);
505 //        }
506 //    }
507 //}

转载于:https://www.cnblogs.com/tornadomeet/archive/2012/06/02/2531565.html

相关文章:

ADO.Net五个对象

Connection Command DataAdapter DataSet DataReader 取5个单词的首字母CCDDD&#xff0c;在拼音输入法里面打一下&#xff0c;出来5个字&#xff0c;然后就记忆为曹操打豆豆。 制作了一张图片&#xff0c;用来帮助记忆曹操打豆豆。 Reference ADO.NET的五大对象转载于:ht…

XPath与多线程爬虫

一. Xpath的介绍与配置 1. XPath是什么 XPath是一门语言 XPath可以在XML文档中查找信息 XPath支持HTML XPath通过元素和属性进行导航 总结&#xff1a; XPath可以用来提取信息&#xff08;和正则表达式类似&#xff09; XPath比正则表达式更加厉害 XPath比正则表…

html无序列表空心圆_列表样式的使用CSS入门基础(018)

今天我们分享关于列表样式的内容。列表项list-sytle-type&#xff1a;在HTML学习中&#xff0c;我们知道有有序列表和无序列表&#xff0c;都是使用type属性来定义的。1、有序列表有序列表 有序列表 有序列表 属性值type&#xff1a;1&#xff0c;数字1、2、3……&#xff1b…

2013年3月百度之星B题

Sigma Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65535/32768K (Java/Other) Problem Description 小H是一个程序员。他很喜欢做各种各样的数学题&#xff0c;尤其喜欢做《水泥数学》。 在看了《水泥数学》的2.5章后&#xff0c;小H终于会用9种计算 1^22^2...n^…

TCP/IP 10.1集成IS-IS协议

樱桃小小的 软软的甜甜的好吃哈&#xff01;感谢上帝 &#xff0c; 恩呢 &#xff0c; 让我吃的这么满足&#xff0c;开心&#xff01;第十章 集成IS-IS协议建议在学习ISIS的时候联系2个<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office"…

运维基础-文件权限管理

Linux是一个多用户操作系统&#xff0c;在多用户操作系统上我们需要一种方法来允许或者拒绝访问特定的文件和目录。文件有所属人和相关的单个组。我们可以设置所属人或者租的权限&#xff0c;以及所有其他人的权限。 文件只具有三个应用权限的用户类别。文件的所有者&#xff0…

android帧动画实现方法之一

好多动画离不开帧动画的使用&#xff0c;下面就实现帧动画的制作方式之一&#xff0c;以后会推出其他方法。 上面是文件存放位置。 a.xml文件的代码如下&#xff1a; <?xml version"1.0" encoding"utf-8"?> <animation-list xmlns:android"…

python技术晨讲_python系列教程14

声明&#xff1a;在人工智能技术教学期间&#xff0c;不少学生向我提一些python相关的问题&#xff0c;所以为了让同学们掌握更多扩展知识更好的理解人工智能技术&#xff0c;我让助理负责分享这套python系列教程&#xff0c;希望能帮到大家&#xff01;好了&#xff0c;是开始…

三字母词和转义字符

1. 三字母词 在C语言中有一种三字母词的说法&#xff0c;trigraph sequences&#xff0c;目前为止有九种三字母词&#xff0c;如下 ?? # ??) ] ??! | ??( [ …

写了个Python脚本监控nginx进程

写了个Python脚本监控nginx进程 Xiaoxia[PG]写了个Python脚本监控nginx进程接上一文用iptables让SSH服务对陌生人说不。还是有点担心这个学期内&#xff0c;nginx可能会因为系统各种原因而出现异常退出&#xff0c;导致Web服务暂停。所以&#xff0c;又来了一个方案。view pla…

Linux shell 脚本报错:/bin/bash^M: bad interpreter: No such file or directory

今天遇到一个很诡异的问题&#xff0c;一直运行很正常的shell脚本失败了&#xff0c;只是昨天增加了一个参数而已。 报错信息&#xff1a; /bin/bash^M: bad interpreter: No such file or directory 后来发现root cause, 昨天修改文件的时候在windows中修改保存&#xff0c;然…

C语言volatile关键字详解

volatile提醒编译器它后面所定义的变量随时都有可能改变&#xff0c;因此编译后的程序每次需要存储或读取这个变量的时候&#xff0c;都会直接从变量地址中读取数据。如果没有volatile关键字&#xff0c;则编译器可能优化读取和存储&#xff0c;可能暂时使用寄存器中的值&#…

python储存数据的容器_Python基础四容器类数据

一、上周内容回顾int bool str 之间的互相转换int str:str(int)int(str) #字符串必须是数字组成int bool:bool(int):非零即TrueTrue --->1 Fasle --->0bool str:str-->bool #非空即Truestr&#xff1a;BIF自己去背吧二、列表why&#xff1a;1.取值费劲。2.对字符串…

Android 清单文件 详解

转载于:https://www.cnblogs.com/mohe/archive/2013/03/31/2991642.html

android屏幕分辨率详解 ldpi mdpi hdpi 程序UI自适应 《官方翻译》

2019独角兽企业重金招聘Python工程师标准>>> 看世界杯的空闲 时间&#xff0c;翻译一下 官方文档。分辨率 问题是大家都很关心的&#xff08;720480会不会悲剧&#xff09;&#xff0c;而关于这个问题&#xff0c;android官方的文档无疑最有说服力。由于不是所有的人…

010 并发的三个特性

一 . 概述 在之前,我们使用synchronized关键词解决了原子性的操作,本节我们分析一个JVM内存模型导致的另外的两个问题. 二 . 可见性 为了加速线程的运行的速度,JVM的内存模型中设置了线程栈中的缓存,当一个线程使用了堆内存的数据的时候,首先会将这个数据缓存到线程栈之中, 当这…

LeetCode: Longest Consecutive Sequence

想到map了&#xff0c;可惜没想到用erase来节省空间&#xff0c;看了网上答案 1 class Solution {2 public:3 int longestConsecutive(vector<int> &num) {4 // Start typing your C/C solution below5 // DO NOT write int main() function6 …

python做测试书籍推荐_学习pytest应该观看的书籍?

这本书有中文版了pytest是动态编程语言Python专用的测试框架&#xff0c;它具有易于上手、功能强大、第三方插件丰富、效率高、可扩展性好、兼容性强等特点。《pytest测试实战》深入浅出地讲解了pytest的使用方法&#xff0c;尤其是具有特色的fixture的用法。作者通过丰富的测试…

路由器、路由与路由表

2019独角兽企业重金招聘Python工程师标准>>> 路由器、路由与路由表 路由器就是一台网络设备&#xff0c;它配备多个网络接口卡(NIC)&#xff0c;能利用它的网络知识正确转发入口流量。 决定一个入口封包应当送给本地主机还是转发所需要的信息&#xff0c;以及在转发…

Hadoop虚拟机的jdk版本和本地eclipse的版本不一致怎么办

在本周学习Hadoop遇到了一个问题&#xff0c;困扰了半天&#xff0c;本人在安装Hadoop时是按照视频来的&#xff0c;结果发现Hadoop上的jdk版本和本地eclipse的版本不一致&#xff0c;导致本地的程序到处jar包传到虚拟机上运用Hadoop不能正常运行&#xff0c;如果你遇到相同的问…

操作符和表达式

一. 操作符 1. 算术操作符 - * / % 除了%之外其余的几个操作符既可以用于计算整型也可以用于计算浮点型数据&#xff0c;%只能计算整型数据&#xff0c;得到的结果是余数 2. 移位操作符 << 左移位操作符 >> 右移位操作符 <<左移…

kuayu react_react跨域解决方案

react跨域解决方案1.开发环境:reactaxioselement2.电脑系统:windows10专业版3.在使用react开发的过程中,我们总是会遇到跨域的问题,下面我来分享一下,在react中跨域处理方法!4.我使用的是axios向后台发送请求,安装axios:npm i axios --save5.安装代理中间件(http-proxy-middlew…

HDU 1429 胜利大逃亡(续) (BFS+位压缩)

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1429 胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3309 Accepted Submission(s): 1063 Problem DescriptionIgnatius再次…

Ext fucionchart插件

http://code.google.com/p/uxmedia/downloads/list转载于:https://www.cnblogs.com/jerome-rong/archive/2012/06/09/2543565.html

前端 ----jQuery的动画效果

03-jQuery动画效果 jQuery提供的一组网页中常见的动画效果&#xff0c;这些动画是标准的、有规律的效果&#xff1b;同时还提供给我们了自定义动画的功能。 显示动画 方式一&#xff1a; $("div").show(); 解释&#xff1a;无参数&#xff0c;表示让指定的元素直接显…

结构和联合--结构体内存和位段内存开辟规则

一. 结构的基本知识 聚合数据类型能够存储多个数据&#xff0c;C语言提供了两种类型的聚合数据类型&#xff0c;数组和结构。数组是相同的数据&#xff0c;结构是不同类型的数据聚合。结构也是一些值得集合&#xff0c;这些值成为它的成员&#xff0c;每个结构都有它的名字&a…

antd自定义分页器_自定义分页器

classPagination(object):def __init__(self, current_page, all_count, per_page_num10, pager_count11):"""封装分页相关数据:param current_page: 当前页:param all_count: 数据库中的数据总条数:param per_page_num: 每页显示的数据条数:param pager_count:…

.net实现跨页面传值

//一般用于向php&#xff0c;jsp等传值&#xff0c;因为跨语言session等不能共用&#xff0c;只有通过post提交 //下面演示的是服务器端控件提交 PostBackUrl"WebForm3.aspx"//这个页面只需要修改控件属性就能把值传给下一页面 protected void Page_Load(object send…

进程的同步、互斥以及PV原语

在处理进程间的同步与互斥问题时&#xff0c;我们离不开信号量和PV原语&#xff0c;使用这两个工具的目的在于打造一段不可分割不可中断的程序。应当注意的是&#xff0c;信号量和PV原语是解决进程间同步与互斥问题的一种机制&#xff0c;但并不是唯一的机制。 信号量&#xff…

ListT中,Remove和RemoveAt区别

Remove删除的是匹配的第一项。比如你的list里面有2个相同的项。那么就删除第一个。后面的不删除&#xff0c;找不到元素和删除失败都返回falseRemoveAt是删除索引下的项 转载于:https://www.cnblogs.com/mcyushao/p/9526208.html