前景检测算法(七)--ViBe算法

原文:

http://blog.csdn.net/zouxy09/article/details/9622285


因为监控发展的需求,目前前景检测的研究还是很多的,也出现了很多新的方法和思路。个人了解的大概概括为以下一些:

       帧差、背景减除(GMM、CodeBook、 SOBS、 SACON、 VIBE、 W4、多帧平均……)、光流(稀疏光流、稠密光流)、运动竞争(Motion Competition)、运动模版(运动历史图像)、时间熵……等等。如果加上他们的改进版,那就是很大的一个家族了。

      对于上一些方法的一点简单的对比分析可以参考下:

http://www.cnblogs.com/ronny/archive/2012/04/12/2444053.html

       至于哪个最好,看使用环境吧,各有千秋,有一些适用的情况更多,有一些在某些情况下表现更好。这些都需要针对自己的使用情况作测试确定的。呵呵。

       推荐一个牛逼的库:http://code.google.com/p/bgslibrary/里面包含了各种背景减除的方法,可以让自己少做很多力气活。

       还有王先荣博客上存在不少的分析:

http://www.cnblogs.com/xrwang/archive/2010/02/21/ForegroundDetection.html

       下面的博客上转载王先荣的上面几篇,然后加上自己分析了两篇:

http://blog.csdn.net/stellar0

 

       本文主要关注其中的一种背景减除方法:ViBe。stellar0的博客上对ViBe进行了分析,我这里就不再啰嗦了,具体的理论可以参考:

http://www2.ulg.ac.be/telecom/research/vibe/

http://blog.csdn.net/stellar0/article/details/8777283

http://blog.csdn.net/yongshengsilingsa/article/details/6659859

http://www2.ulg.ac.be/telecom/research/vibe/download.html

http://www.cvchina.info/2011/12/25/vibe/



ViBe是一种像素级的背景建模、前景检测算法,该算法主要不同之处是背景模型的更新策略,随机选择需要替换的像素的样本,随机选择邻域像素进行更新。在无法确定像素变化的模型时,随机的更新策略,在一定程度上可以模拟像素变化的不确定性。

背景模型的初始化

  初始化是建立背景模型的过程,一般的检测算法需要一定长度的视频序列学习完成,影响了检测的实时性,而且当视频画面突然变化时,重新学习背景模型需要较长时间。

  ViBe算法主要是利用单帧视频序列初始化背景模型,对于一个像素点,结合相邻像素点拥有相近像素值的空间分布特性,随机的选择它的邻域点的像素值作为它的模型样本值

  优点:不仅减少了背景模型建立的过程,还可以处理背景突然变化的情况,当检测到背景突然变化明显时,只需要舍弃原始的模型,重新利用变化后的首帧图像建立背景模型。

  缺点:由于可能采用了运动物体的像素初始化样本集,容易引入拖影(Ghost)区域。

前景检测过程

  背景模型为每个背景点存储一个样本集,然后每个新的像素值和样本集比较判断是否属于背景。

  计算新像素值和样本集中每个样本值的距离,若距离小于阈值,则近似样本点数目增加。

  如果近似样本点数目大于阈值,则认为新的像素点为背景。

  检测过程主要由三个参数决定:样本集数目N,阈值#min和距离相近判定的阈值R,一般具体实现,参数设置为N=20,#min=2,R=20。

  前景检测算法(七)--ViBe算法_第1张图片

背景模型的更新策略

1).无记忆更新策略

  每次确定需要更新像素点的背景模型时,以新的像素值随机取代该像素点样本集的一个样本值。

2).时间取样更新策略

  并不是每处理一帧数据,都需要更新处理,而是按一定的更新率更新背景模型。当一个像素点被判定为背景时,它有1/rate的概率更新背景模型。rate是时间采样因子,一般取值为16。

3).空间邻域更新策略

  针对需要更新像素点,随机的选择一个该像素点邻域的背景模型,以新的像素点更新被选中的背景模型。

ViBe的改进

  前景检测算法(七)--ViBe算法_第2张图片

1).距离计算方法

  以圆椎模型代替原来的几何距离计算方法

 

  前景检测算法(七)--ViBe算法_第3张图片

  以自适应阈值代替原来固定的距离判定阈值,阈值大小与样本集的方差成正比,样本集方差越大,说明背景越复杂,判定阈值应该越大。

  

2).分离updating mask和segmentation mask

  引入目标整体的概念,弥补基于像素级前景检测的不足。针对updating mask和segmentation mask采用不同尺寸的形态学处理方法,提高检测准确率。

3).抑制邻域更新

  在updating mask里,计算像素点的梯度,根据梯度大小,确定是否需要更新邻域。梯度值越大,说明像素值变化越大,说明该像素值可能为前景,不应该更新。

4).检测闪烁像素点

  引入闪烁程度的概念,当一个像素点的updating label与前一帧的updating label不一样时,blinking level增加15,否则,减少1,然后根据blinking level的大小判断该像素点是否为闪烁点。闪烁像素主要出现在背景复杂的场景,如树叶、水纹等,这些场景会出现像素背景和前景的频繁变化,因而针对这些闪烁应该单独处理,可以作为全部作为背景。

5).增加更新因子

  ViBe算法中,默认的更新因子是16,当背景变化很快时,背景模型无法快速的更新,将会导致前景检测的较多的错误。因而,需要根据背景变化快慢程度,调整更新因子的大小,可将更新因子分多个等级,如rate = 16,rate = 5,rate = 1。


1)VIBE-A powerful random technique to estimatie the background in video sequences.

2) VIBE-A universal background subtraction algorithms for video sequences

   VIBE的头文件Vibe.hpp如下:

[cpp] view plain copy print ?
  1. #pragma once  
  2. #include "stdafx.h"  
  3. #define  WINSIZE 3  
  4.   
  5. class Vibe  
  6. {  
  7. public:  
  8.     Vibe(void);  
  9.     Vibe(IplImage *img);  
  10.     void SetMinMatch(int nthreshold){g_MinMatch=nthreshold;}  
  11.     void SetRadius(int radius){g_Radius=radius;}  
  12.     void SetSampleNum(int num){g_SampleNum=num;}  
  13.     void SetThreshold(double t){g_threshold=t;}  
  14.     IplImage* GetForeground(){return g_ForeImg;}  
  15.     IplImage* GetSegMask(){return g_SegementMask;}  
  16.     void Detect(IplImage *img);   
  17.     void ForegroundCombineEdge(); // 结合边缘信息  
  18.     void DeleteSmallAreaInForeground(double minArea=20);//删除小面积区域  
  19.     // 实现背景更新机制  
  20.     void Update();  
  21.     // 实现后处理,主要用形态学算子  
  22.     void PostProcess();  
  23.   
  24. public:  
  25.     ~Vibe(void);  
  26.   
  27. private:      
  28.     void ClearLongLifeForeground(int i_lifeLength=200); // 清除场景中存在时间较长的像素,i_lifeLength用于控制允许存在的最长时间  
  29.     double AreaDense(IplImage *pFr,int AI,int AJ,int W,int H); //计算(i,j)处邻域大小为W×H的密度  
  30.     int GetRandom(int istart,int iend); // 默认istart=0,iend=15  
  31.     int GetRandom(int random);  
  32.     int GetRandom();// 产生一个随机数  
  33.     // 计算两个像素之间的欧式距离  
  34.     double CalcPixelDist(CvScalar bkCs,CvScalar curCs);  
  35.     // 按照Kim的方法来计算颜色畸变  
  36.     double CalcuColorDist(CvScalar bkCs,CvScalar curCs);  
  37.     int g_SampleNum;// Sample number for the models,默认为20  
  38.     int g_MinMatch; // 当前像素与背景模型匹配的最少个数,默认为2  
  39.     int g_Height;  
  40.     int g_Width;  
  41.     int g_Radius;// 球体的半径,默认为20  
  42.     int g_offset; //边界的宽和高  
  43.     double g_threshold; // 距离度量的阈值  
  44.     unsigned char ***g_Model;// 保存背景模型    
  45.     IplImage *g_ForeImg;// 保存前景图  
  46.     IplImage *g_Edge;  
  47.   
  48.     IplConvKernel* element;  
  49.   
  50.     IplImage *g_SegementMask; //分割掩膜  
  51.     IplImage *g_UpdateMask; // 更新掩膜  
  52.     IplImage *g_Gray;  
  53.     int ** LifeLength; // 记录前景点的生命长度,如果前景点的生命长度到达一定的阈值,则将其融入背景中去,且要随机两次。      
  54. };  
#pragma once
#include "stdafx.h"
#define  WINSIZE 3

class Vibe
{
public:
	Vibe(void);
	Vibe(IplImage *img);
	void SetMinMatch(int nthreshold){g_MinMatch=nthreshold;}
	void SetRadius(int radius){g_Radius=radius;}
	void SetSampleNum(int num){g_SampleNum=num;}
	void SetThreshold(double t){g_threshold=t;}
	IplImage* GetForeground(){return g_ForeImg;}
	IplImage* GetSegMask(){return g_SegementMask;}
	void Detect(IplImage *img);	
	void ForegroundCombineEdge(); // 结合边缘信息
	void DeleteSmallAreaInForeground(double minArea=20);//删除小面积区域
	// 实现背景更新机制
	void Update();
	// 实现后处理,主要用形态学算子
	void PostProcess();

public:
	~Vibe(void);

private:	
	void ClearLongLifeForeground(int i_lifeLength=200); // 清除场景中存在时间较长的像素,i_lifeLength用于控制允许存在的最长时间
	double AreaDense(IplImage *pFr,int AI,int AJ,int W,int H); //计算(i,j)处邻域大小为W×H的密度
	int GetRandom(int istart,int iend); // 默认istart=0,iend=15
	int GetRandom(int random);
	int GetRandom();// 产生一个随机数
	// 计算两个像素之间的欧式距离
	double CalcPixelDist(CvScalar bkCs,CvScalar curCs);
	// 按照Kim的方法来计算颜色畸变
	double CalcuColorDist(CvScalar bkCs,CvScalar curCs);
	int g_SampleNum;// Sample number for the models,默认为20
	int g_MinMatch; // 当前像素与背景模型匹配的最少个数,默认为2
	int g_Height;
	int g_Width;
	int g_Radius;// 球体的半径,默认为20
	int g_offset; //边界的宽和高
	double g_threshold; // 距离度量的阈值
	unsigned char ***g_Model;// 保存背景模型	
	IplImage *g_ForeImg;// 保存前景图
	IplImage *g_Edge;

	IplConvKernel* element;

	IplImage *g_SegementMask; //分割掩膜
	IplImage *g_UpdateMask; // 更新掩膜
	IplImage *g_Gray;
	int ** LifeLength; // 记录前景点的生命长度,如果前景点的生命长度到达一定的阈值,则将其融入背景中去,且要随机两次。	
};


对应的实现文件如下Vibe.cpp所示:

[cpp] view plain copy print ?
  1. #include "StdAfx.h"  
  2. #include "Vibe.h"  
  3.   
  4. Vibe::Vibe(void)  
  5. {  
  6.     g_Radius=20;  
  7.     g_MinMatch=2;     
  8.     g_SampleNum=20;  
  9.     g_offset=(WINSIZE-1)/2;  
  10.   
  11. }  
  12.   
  13. Vibe::Vibe(IplImage *img)  
  14. {  
  15.     if (!img)  
  16.     {  
  17.         cout<<" The parameter referenced to NUll Pointer!"<
  18.         return;  
  19.     }  
  20.     this->g_Height=img->height;  
  21.     this->g_Width=img->width;  
  22.   
  23.     g_Radius=20;  
  24.     g_MinMatch=2;     
  25.     g_SampleNum=20;  
  26.     g_threshold=50;  
  27.     g_offset=(WINSIZE-1)/2;  
  28.   
  29.     g_ForeImg=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);  
  30.     g_Gray=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);  
  31.     g_Edge=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);  
  32.     g_SegementMask=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);  
  33.     g_UpdateMask=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);  
  34.   
  35.     element=cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,NULL);  
  36.   
  37.     cvCvtColor(img,g_Gray,CV_BGR2GRAY);  
  38.       
  39.     // 以上完成相关的初始化操作  
  40.     /**********************  以下实现第一帧在每个像素的8邻域内的采样功能,建立对应的背景模型*****************************/  
  41.       
  42.     int i=0,j=0,k=0;  
  43.     g_Model=new unsigned char**[g_SampleNum];  
  44.     for (k=0;k
  45.     {  
  46.         g_Model[k]=new unsigned char *[g_Height];  
  47.         for(i=0;i
  48.         {  
  49.             g_Model[k][i]=new unsigned char [g_Width];  
  50.             for (j=0;j
  51.             {  
  52.                 g_Model[k][i][j]=0;  
  53.             }  
  54.         }  
  55.     }  
  56.       
  57.     // 采样进行背景建模   
  58.     double dVal;  
  59.     int ri=0,rj=0; //随机采样的值  
  60.     for (i=g_offset;i
  61.     {  
  62.         for (j=g_offset;j
  63.         {  
  64.             // 周围3*3的邻域内进行采样  
  65.             for(k=0;k
  66.             {  
  67.                 ri=GetRandom(i);  
  68.                 rj=GetRandom(j);  
  69.                 dVal=cvGetReal2D(g_Gray,ri,rj);       
  70.                 g_Model[k][i][j]=dVal;                            
  71.             }  
  72.         }  
  73.     }  
  74.   
  75.     // 初始化前景点掩膜的生命长度  
  76.     LifeLength=new int *[g_Height];  
  77.     for (i=0;i
  78.     {  
  79.         LifeLength[i]=new int [g_Width];  
  80.         for(j=0;j
  81.         {  
  82.             LifeLength[i][j]=0;  
  83.         }  
  84.     }  
  85. }  
  86.   
  87.   
  88. void Vibe::Detect(IplImage *img)  
  89. {  
  90.     cvZero(g_ForeImg);    
  91.     cvCvtColor(img,g_Gray,CV_BGR2GRAY);  
  92.     int i=0,j=0,k=0;  
  93.     double dModVal,dCurrVal;  
  94.     int tmpCount=0;// 距离比较在阈值内的次数  
  95.     double tmpDist=0;     
  96.     int iR1,iR2;//产生随机数  
  97.     int Ri,Rj; // 产生邻域内X和Y的随机数  
  98.   
  99.     for (i=0;i
  100.     {         
  101.         for (j=0;j
  102.         {             
  103.             if( i < g_offset || j < g_offset || i> g_Height - g_offset || j> g_Width - g_offset )  
  104.             {  
  105.                 cvSetReal2D(g_ForeImg,i,j,0);  
  106.                 continue;  
  107.             }  
  108.             else  
  109.             {  
  110.                 tmpCount=0;  
  111.                 dCurrVal=cvGetReal2D(g_Gray,i,j);                 
  112.                 for (k=0;k
  113.                 {                     
  114.                     dModVal=g_Model[k][i][j];  
  115.                     //tmpDist=CalcPixelDist(dCurrVal,dModVal);  
  116.                     //tmpDist=CalcuColorDist(dCurrVal,dModVal);   
  117.                     tmpDist=fabs(dModVal-dCurrVal);                                   
  118.                     if (tmpDist
  119.                     {  
  120.                         tmpCount++;  
  121.                     }                     
  122.                 }  
  123.   
  124.                 //判断是否匹配上  
  125.                 if (tmpCount>=g_MinMatch)  
  126.                 {  
  127.                     cvSetReal2D(g_ForeImg,i,j,0);  
  128.                     // 背景模型的更新                    
  129.                     iR1=GetRandom(0,15);  
  130.                     if (iR1==0)  
  131.                     {  
  132.                         iR2=GetRandom();  
  133.                         g_Model[iR2][i][j]=dCurrVal;                          
  134.                     }  
  135.   
  136.                     //进一步更新邻域模型  
  137.                       
  138.                     iR1=GetRandom(0,15);  
  139.                     if (iR1==0)  
  140.                     {  
  141.                         Ri=GetRandom(i);  
  142.                         Rj=GetRandom(j);  
  143.                         iR2=GetRandom();  
  144.                         g_Model[iR2][Ri][Rj]=dCurrVal;                        
  145.                     }                         
  146.                 }  
  147.                 else  
  148.                 {  
  149.                     cvSetReal2D(g_ForeImg,i,j,255);  
  150.                 }  
  151.             }  
  152.         }         
  153.     }         
  154.       
  155.     //ForegroundCombineEdge();  
  156.     DeleteSmallAreaInForeground(80);  
  157.     ClearLongLifeForeground();  
  158.     //PostProcess();  
  159. }  
  160.   
  161. double Vibe::AreaDense(IplImage *pFr,int AI,int AJ,int W,int H)  
  162. {  
  163.     if (AI<=2 || AJ<=2 || AJ>=(g_Width-2) || AI>=(g_Height-2))  
  164.     {  
  165.         return 0;  
  166.     }  
  167.     int Num=0,i=0,j=0;  
  168.     double dVal=0,dense=0;  
  169.     int Total=(2*H+1)*(2*W+1);  
  170.     for (i=AI-H;i<=AI+H;i++)  
  171.     {  
  172.         for (j=AJ-W;j<=AJ+W;j++)  
  173.         {  
  174.             dVal=cvGetReal2D(pFr,i,j);  
  175.             if (dVal>200)  
  176.             {  
  177.                 Num++;  
  178.             }  
  179.         }  
  180.     }  
  181.     dense=(double)Num/(double)Total;  
  182.     return dense;  
  183. }  
  184.   
  185. void Vibe::ForegroundCombineEdge()  
  186. {     
  187.     cvZero(g_Edge);  
  188.     //cvZero(g_SegementMask);  
  189.     //cvCopy(g_ForeImg,g_SegementMask);  
  190.     cvCanny(g_Gray,g_Edge,30,200,3);  
  191.     int i=0,j=0;  
  192.     double dense;  
  193.     double dVal;  
  194.     for (i=g_offset;i
  195.     {  
  196.         for (j=g_offset;j
  197.         {  
  198.             dense=AreaDense(g_ForeImg,i,j,2,2);  
  199.             dVal=cvGetReal2D(g_Edge,i,j);  
  200.             if (dense>0.2 && dVal>200)  
  201.             {  
  202.                 cvSetReal2D(g_ForeImg,i,j,255);  
  203.             }  
  204.         }  
  205.     }  
  206.   
  207. }  
  208.   
  209.   
  210. void Vibe::DeleteSmallAreaInForeground(double minArea/* =20 */)  
  211. {  
  212.     //cvZero(g_SegementMask);  
  213.     //cvCopy(g_ForeImg,g_SegementMask);  
  214.     int region_count = 0;  
  215.     CvSeq *first_seq = NULL, *prev_seq = NULL, *seq = NULL;  
  216.     CvMemStorage*  storage = cvCreateMemStorage();  
  217.     cvClearMemStorage(storage);  
  218.     cvFindContours( g_ForeImg, storage, &first_seq, sizeof(CvContour), CV_RETR_LIST );  
  219.     for( seq = first_seq; seq; seq = seq->h_next )  
  220.     {  
  221.         CvContour* cnt = (CvContour*)seq;  
  222.         if( cnt->rect.width * cnt->rect.height < minArea )  
  223.         {  
  224.             prev_seq = seq->h_prev;  
  225.             if( prev_seq )  
  226.             {  
  227.                 prev_seq->h_next = seq->h_next;  
  228.                 if( seq->h_next ) seq->h_next->h_prev = prev_seq;  
  229.             }  
  230.             else  
  231.             {  
  232.                 first_seq = seq->h_next;  
  233.                 if( seq->h_next ) seq->h_next->h_prev = NULL;  
  234.             }  
  235.         }  
  236.         else  
  237.         {             
  238.             region_count++;  
  239.         }  
  240.     }              
  241.     cvZero(g_ForeImg);  
  242.     cvDrawContours(g_ForeImg, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);   
  243.   
  244.     /* 
  245.     CvContourScanner scanner = cvStartFindContours( g_ForeImg, storage,sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) ); 
  246.     CvSeq *contours=NULL,*c=NULL; 
  247.     int poly1Hull0=0; 
  248.     int nContours=0; 
  249.     double perimScale=100; 
  250.     while( (c = cvFindNextContour( scanner )) != 0 )  
  251.     { 
  252.         double len = cvContourPerimeter( c ); 
  253.         double q = (g_ForeImg->height + g_ForeImg->width)/perimScale; // calculate perimeter len threshold 
  254.         if( len < q ) //Get rid of blob if it's perimeter is too small 
  255.             cvSubstituteContour( scanner, 0 ); 
  256.         else //Smooth it's edges if it's large enough 
  257.         { 
  258.             CvSeq* newC; 
  259.             if( poly1Hull0 ) //Polygonal approximation of the segmentation  
  260.                 newC = cvApproxPoly( c, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 2, 0 );  
  261.             else //Convex Hull of the segmentation 
  262.                 newC = cvConvexHull2( c, storage, CV_CLOCKWISE, 1 ); 
  263.             cvSubstituteContour( scanner, newC ); 
  264.             nContours++; 
  265.         } 
  266.     } 
  267.     contours = cvEndFindContours( &scanner ); 
  268.     // paint the found regions back into the image 
  269.     cvZero( g_ForeImg ); 
  270.     for( c=contours; c != 0; c = c->h_next )  
  271.         cvDrawContours( g_ForeImg, c, cvScalarAll(255), cvScalarAll(0), -1, CV_FILLED, 8,cvPoint(0,0)); 
  272.     */  
  273.   
  274.     cvReleaseMemStorage(&storage);    
  275. }  
  276.   
  277. void Vibe::ClearLongLifeForeground(int i_lifeLength/* =200 */)  
  278. {  
  279.     int i=0,j=0;  
  280.     double dVal=0;  
  281.     double dLife=0;  
  282.     int iR1,iR2=0;  
  283.     double dCurrVal=0;  
  284.     for (i=g_offset;i
  285.     {  
  286.         for (j=g_offset;j
  287.         {  
  288.             dVal=cvGetReal2D(g_ForeImg,i,j);  
  289.             dLife=LifeLength[i][j];  
  290.             if (dLife>i_lifeLength)  
  291.             {  
  292.                 LifeLength[i][j]=0;  
  293.                 dCurrVal=cvGetReal2D(g_Gray,i,j);  
  294.                 // 更新背景模型  
  295.                 iR1=GetRandom();  
  296.                 iR2=GetRandom();  
  297.                 g_Model[iR1][i][j]=dCurrVal;  
  298.                 g_Model[iR2][i][j]=dCurrVal;  
  299.             }  
  300.             else  
  301.             {  
  302.                 LifeLength[i][j]=dLife+1;  
  303.             }  
  304.   
  305.         }  
  306.     }  
  307. }  
  308.   
  309. void Vibe::Update()  
  310. {  
  311.     cvZero(g_UpdateMask);     
  312.   
  313. }  
  314.   
  315. void Vibe::PostProcess()  
  316. {  
  317.     cvZero(g_SegementMask);  
  318.     cvMorphologyEx(g_ForeImg,g_SegementMask,NULL,element,CV_MOP_OPEN,1);  
  319.   
  320. }  
  321.   
  322. //算颜色畸变  
  323. double Vibe::CalcuColorDist(CvScalar bkCs,CvScalar curCs)  
  324. {  
  325.     double r,g,b,br,bg,bb;  
  326.     r=curCs.val[0];  
  327.     g=curCs.val[1];  
  328.     b=curCs.val[2];  
  329.   
  330.     br=bkCs.val[0];  
  331.     bg=bkCs.val[1];  
  332.     bb=bkCs.val[2];  
  333.   
  334.     double curDist=r*r+g*g*b*b;   
  335.     double bkDist=br*br+bg*bg+bb*bb;  
  336.   
  337.     double curBK=r*br+g*bg+b*bb;  
  338.     double curbkDist=curBK*curBK;  
  339.     double SquareP;  
  340.     if (bkDist==0.0)  
  341.     {  
  342.         SquareP=0;  
  343.     }  
  344.     else  
  345.     {  
  346.         SquareP=curbkDist/bkDist;  
  347.     }  
  348.     double dist=sqrtf(curDist-SquareP);  
  349.     return dist;      
  350. }  
  351.   
  352. double Vibe::CalcPixelDist(CvScalar bkCs,CvScalar curCs)  
  353. {  
  354.     double tmpDist=pow(bkCs.val[0]-curCs.val[0],2)+pow(bkCs.val[1]-curCs.val[1],2)+pow(bkCs.val[2]-curCs.val[2],2);  
  355.     return sqrtf(tmpDist);  
  356. }  
  357.   
  358. int Vibe::GetRandom()  
  359. {  
  360.     int val = g_SampleNum * 1.0 * rand() / RAND_MAX;      
  361.     if( val == g_SampleNum )  
  362.         return val - 1;  
  363.     else  
  364.         return val;  
  365. }  
  366.   
  367. int Vibe::GetRandom(int random)  
  368. {  
  369.     int val=random-g_offset+rand()%(2*g_offset);  
  370.     if (val
  371.     {  
  372.         val=random-g_offset;  
  373.     }  
  374.     if (val>random+g_offset)  
  375.     {  
  376.         val=random+g_offset;  
  377.     }     
  378.     return val;   
  379. }  
  380.   
  381. int Vibe::GetRandom(int istart,int iend)  
  382. {  
  383.     int val=istart+rand()%(iend-istart);  
  384.     return val;  
  385. }  
  386.   
  387.   
  388. Vibe::~Vibe(void)  
  389. {  
  390.     if (g_ForeImg)  
  391.     {  
  392.         cvReleaseImage(&g_ForeImg);  
  393.     }  
  394.     if (g_SegementMask)  
  395.     {  
  396.         cvReleaseImage(&g_SegementMask);  
  397.     }  
  398.     if (g_UpdateMask)  
  399.     {  
  400.         cvReleaseImage(&g_UpdateMask);  
  401.     }  
  402.     if (g_Gray)  
  403.     {  
  404.         cvReleaseImage(&g_Gray);  
  405.     }  
  406.   
  407.     if (g_Model!=NULL)  
  408.     {  
  409.         delete[]g_Model;  
  410.         g_Model=NULL;  
  411.     }  
  412. }  
#include "StdAfx.h"
#include "Vibe.h"

Vibe::Vibe(void)
{
	g_Radius=20;
	g_MinMatch=2;	
	g_SampleNum=20;
	g_offset=(WINSIZE-1)/2;

}

Vibe::Vibe(IplImage *img)
{
	if (!img)
	{
		cout<<" The parameter referenced to NUll Pointer!"<g_Height=img->height;
	this->g_Width=img->width;

	g_Radius=20;
	g_MinMatch=2;	
	g_SampleNum=20;
	g_threshold=50;
	g_offset=(WINSIZE-1)/2;

	g_ForeImg=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
	g_Gray=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
	g_Edge=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
	g_SegementMask=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
	g_UpdateMask=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);

	element=cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,NULL);

	cvCvtColor(img,g_Gray,CV_BGR2GRAY);
	
	// 以上完成相关的初始化操作
	/**********************  以下实现第一帧在每个像素的8邻域内的采样功能,建立对应的背景模型*****************************/
	
	int i=0,j=0,k=0;
	g_Model=new unsigned char**[g_SampleNum];
	for (k=0;k g_Height - g_offset || j> g_Width - g_offset )
			{
				cvSetReal2D(g_ForeImg,i,j,0);
				continue;
			}
			else
			{
				tmpCount=0;
				dCurrVal=cvGetReal2D(g_Gray,i,j);				
				for (k=0;k=g_MinMatch)
				{
					cvSetReal2D(g_ForeImg,i,j,0);
					// 背景模型的更新					
					iR1=GetRandom(0,15);
					if (iR1==0)
					{
						iR2=GetRandom();
						g_Model[iR2][i][j]=dCurrVal;						
					}

					//进一步更新邻域模型
					
					iR1=GetRandom(0,15);
					if (iR1==0)
					{
						Ri=GetRandom(i);
						Rj=GetRandom(j);
						iR2=GetRandom();
						g_Model[iR2][Ri][Rj]=dCurrVal;						
					}						
				}
				else
				{
					cvSetReal2D(g_ForeImg,i,j,255);
				}
			}
		}		
	}		
	
	//ForegroundCombineEdge();
	DeleteSmallAreaInForeground(80);
	ClearLongLifeForeground();
	//PostProcess();
}

double Vibe::AreaDense(IplImage *pFr,int AI,int AJ,int W,int H)
{
	if (AI<=2 || AJ<=2 || AJ>=(g_Width-2) || AI>=(g_Height-2))
	{
		return 0;
	}
	int Num=0,i=0,j=0;
	double dVal=0,dense=0;
	int Total=(2*H+1)*(2*W+1);
	for (i=AI-H;i<=AI+H;i++)
	{
		for (j=AJ-W;j<=AJ+W;j++)
		{
			dVal=cvGetReal2D(pFr,i,j);
			if (dVal>200)
			{
				Num++;
			}
		}
	}
	dense=(double)Num/(double)Total;
	return dense;
}

void Vibe::ForegroundCombineEdge()
{	
	cvZero(g_Edge);
	//cvZero(g_SegementMask);
	//cvCopy(g_ForeImg,g_SegementMask);
	cvCanny(g_Gray,g_Edge,30,200,3);
	int i=0,j=0;
	double dense;
	double dVal;
	for (i=g_offset;i0.2 && dVal>200)
			{
				cvSetReal2D(g_ForeImg,i,j,255);
			}
		}
	}

}


void Vibe::DeleteSmallAreaInForeground(double minArea/* =20 */)
{
	//cvZero(g_SegementMask);
	//cvCopy(g_ForeImg,g_SegementMask);
	int region_count = 0;
	CvSeq *first_seq = NULL, *prev_seq = NULL, *seq = NULL;
	CvMemStorage*  storage = cvCreateMemStorage();
	cvClearMemStorage(storage);
	cvFindContours( g_ForeImg, storage, &first_seq, sizeof(CvContour), CV_RETR_LIST );
	for( seq = first_seq; seq; seq = seq->h_next )
	{
		CvContour* cnt = (CvContour*)seq;
		if( cnt->rect.width * cnt->rect.height < minArea )
		{
			prev_seq = seq->h_prev;
			if( prev_seq )
			{
				prev_seq->h_next = seq->h_next;
				if( seq->h_next ) seq->h_next->h_prev = prev_seq;
			}
			else
			{
				first_seq = seq->h_next;
				if( seq->h_next ) seq->h_next->h_prev = NULL;
			}
		}
		else
		{			
			region_count++;
		}
	}			 
	cvZero(g_ForeImg);
	cvDrawContours(g_ForeImg, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);	

	/*
	CvContourScanner scanner = cvStartFindContours( g_ForeImg, storage,sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
	CvSeq *contours=NULL,*c=NULL;
	int poly1Hull0=0;
	int nContours=0;
	double perimScale=100;
	while( (c = cvFindNextContour( scanner )) != 0 ) 
	{
		double len = cvContourPerimeter( c );
		double q = (g_ForeImg->height + g_ForeImg->width)/perimScale; // calculate perimeter len threshold
		if( len < q ) //Get rid of blob if it's perimeter is too small
			cvSubstituteContour( scanner, 0 );
		else //Smooth it's edges if it's large enough
		{
			CvSeq* newC;
			if( poly1Hull0 ) //Polygonal approximation of the segmentation 
				newC = cvApproxPoly( c, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 2, 0 ); 
			else //Convex Hull of the segmentation
				newC = cvConvexHull2( c, storage, CV_CLOCKWISE, 1 );
			cvSubstituteContour( scanner, newC );
			nContours++;
		}
	}
	contours = cvEndFindContours( &scanner );
	// paint the found regions back into the image
	cvZero( g_ForeImg );
	for( c=contours; c != 0; c = c->h_next ) 
		cvDrawContours( g_ForeImg, c, cvScalarAll(255), cvScalarAll(0), -1, CV_FILLED, 8,cvPoint(0,0));
	*/

	cvReleaseMemStorage(&storage);	
}

void Vibe::ClearLongLifeForeground(int i_lifeLength/* =200 */)
{
	int i=0,j=0;
	double dVal=0;
	double dLife=0;
	int iR1,iR2=0;
	double dCurrVal=0;
	for (i=g_offset;ii_lifeLength)
			{
				LifeLength[i][j]=0;
				dCurrVal=cvGetReal2D(g_Gray,i,j);
				// 更新背景模型
				iR1=GetRandom();
				iR2=GetRandom();
				g_Model[iR1][i][j]=dCurrVal;
				g_Model[iR2][i][j]=dCurrVal;
			}
			else
			{
				LifeLength[i][j]=dLife+1;
			}

		}
	}
}

void Vibe::Update()
{
	cvZero(g_UpdateMask);	

}

void Vibe::PostProcess()
{
	cvZero(g_SegementMask);
	cvMorphologyEx(g_ForeImg,g_SegementMask,NULL,element,CV_MOP_OPEN,1);

}

//算颜色畸变
double Vibe::CalcuColorDist(CvScalar bkCs,CvScalar curCs)
{
	double r,g,b,br,bg,bb;
	r=curCs.val[0];
	g=curCs.val[1];
	b=curCs.val[2];

	br=bkCs.val[0];
	bg=bkCs.val[1];
	bb=bkCs.val[2];

	double curDist=r*r+g*g*b*b; 
	double bkDist=br*br+bg*bg+bb*bb;

	double curBK=r*br+g*bg+b*bb;
	double curbkDist=curBK*curBK;
	double SquareP;
	if (bkDist==0.0)
	{
		SquareP=0;
	}
	else
	{
		SquareP=curbkDist/bkDist;
	}
	double dist=sqrtf(curDist-SquareP);
	return dist;	
}

double Vibe::CalcPixelDist(CvScalar bkCs,CvScalar curCs)
{
	double tmpDist=pow(bkCs.val[0]-curCs.val[0],2)+pow(bkCs.val[1]-curCs.val[1],2)+pow(bkCs.val[2]-curCs.val[2],2);
	return sqrtf(tmpDist);
}

int Vibe::GetRandom()
{
	int val = g_SampleNum * 1.0 * rand() / RAND_MAX;	
	if( val == g_SampleNum )
		return val - 1;
	else
		return val;
}

int Vibe::GetRandom(int random)
{
	int val=random-g_offset+rand()%(2*g_offset);
	if (valrandom+g_offset)
	{
		val=random+g_offset;
	}	
	return val;	
}

int Vibe::GetRandom(int istart,int iend)
{
	int val=istart+rand()%(iend-istart);
	return val;
}


Vibe::~Vibe(void)
{
	if (g_ForeImg)
	{
		cvReleaseImage(&g_ForeImg);
	}
	if (g_SegementMask)
	{
		cvReleaseImage(&g_SegementMask);
	}
	if (g_UpdateMask)
	{
		cvReleaseImage(&g_UpdateMask);
	}
	if (g_Gray)
	{
		cvReleaseImage(&g_Gray);
	}

	if (g_Model!=NULL)
	{
		delete[]g_Model;
		g_Model=NULL;
	}
}

最后附上调用的main函数;

[cpp] view plain copy print ?
  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {     
  3.     CvCapture *capture=NULL;  
  4.     IplImage* frame=NULL;  
  5.     IplImage* pForeImg=NULL;  
  6.     IplImage* segImg=NULL;    
  7.   
  8.     char *file_path="E:\\testVideo\\VTS_01_4.avi";  // m1  test2 锦带河  VTS_01_4_2  head rear  VTS_01_6_2  VTS_01_4  
  9.     //const char* file_path="E:\\suntektechvideo\\锦带河.avi";  //test2  
  10.   
  11.     capture=cvCreateFileCapture(file_path);  
  12.     if (!capture)  
  13.     {  
  14.         //cout<<"Read Video File Error!"<  
  15.         return -1;  
  16.     }  
  17.     frame=cvQueryFrame(capture);  
  18.     frame=cvQueryFrame(capture);  
  19.   
  20.     cvNamedWindow("img",1);  
  21.     cvNamedWindow("foreN",1);  
  22.     //cvNamedWindow("seg",1);  
  23.   
  24.     Vibe* pV=new Vibe(frame);  
  25.   
  26.     while(frame=cvQueryFrame(capture))  
  27.     {  
  28.         pV->Detect(frame);  
  29.         pForeImg=pV->GetForeground();  
  30.         //segImg=pV->GetSegMask();  
  31.         //frame->origin=1;  
  32.         //pForeImg->origin=1;  
  33.         cvShowImage("img",frame);  
  34.         cvShowImage("foreN",pForeImg);  
  35.         //cvShowImage("seg",segImg);  
  36.         cvWaitKey(1);  
  37.     }  
  38.   
  39.     cvReleaseImage(&frame);  
  40.     cvReleaseImage(&pForeImg);  
  41.     cvReleaseCapture(&capture);  
  42.     return 0;     
  43. }  
int _tmain(int argc, _TCHAR* argv[])
{	
	CvCapture *capture=NULL;
	IplImage* frame=NULL;
	IplImage* pForeImg=NULL;
	IplImage* segImg=NULL;	

	char *file_path="E:\\testVideo\\VTS_01_4.avi";  // m1  test2 锦带河  VTS_01_4_2  head rear  VTS_01_6_2  VTS_01_4
	//const char* file_path="E:\\suntektechvideo\\锦带河.avi";  //test2

	capture=cvCreateFileCapture(file_path);
	if (!capture)
	{
		//cout<<"Read Video File Error!"<Detect(frame);
		pForeImg=pV->GetForeground();
		//segImg=pV->GetSegMask();
		//frame->origin=1;
		//pForeImg->origin=1;
		cvShowImage("img",frame);
		cvShowImage("foreN",pForeImg);
		//cvShowImage("seg",segImg);
		cvWaitKey(1);
	}

	cvReleaseImage(&frame);
	cvReleaseImage(&pForeImg);
	cvReleaseCapture(&capture);
	return 0;	
}

       代码没做过多的注释,但现有的注释应该对于理解代码足够了。另外,对于计算机视觉里的任何一种算法都不是万能的,VIBE也不例外,只能说VIBE相对其他算法有一定的优势,但是还是有相当的不足,其pixel-wise-based的灰度建模方式解决不了pixel-wise建模算法共有的问题,其他必要辅助信息的融合是必要的。





你可能感兴趣的:(目标检测)