基于Vibe算法的运动目标检测

        初学习opencv一段时间了,把自己学的东西在博客上做个总结,也有助于自己的知识的巩固。

这段时间主要是在研究运动目标的检测,目前,关于运动目标检测的方法有很多。比如,平均背景法,帧差法,光流法,ViBe算法等等。那么在对上述方法的学习和了解后,发现ViBe算法相对而言,具有更高的鲁棒性。

        首先,介绍ViBe算法。ViBe算法即视觉背景提取算法,是在2009年提出来的用于进行快速背景提取和运动目标检测的算法,具有较高的实时性和鲁棒性。ViBe算法根据图像像素的空间一致性,使用视频序列第一帧来初始化背景模型。采用目标像素的八领域进行背景建模和背景更新。不足之处,对光照和阴影方面处理不足。

      这个博客http://blog.csdn.net/brilliantstone/article/details/18085235 对ViBe算法的原理解释的比较清楚。

      在理解了算法的基本原理后,下面贴出ViBe的算法。


ViBe.hpp 头文件

#include "stdafx.h"  
#include   
#include   
  
using namespace std;  
using namespace cv;  
  
#define defaultNbSamples 20     //每个像素点的样本个数  
#define defaultReqMatches 2     //#min指数  
#define defaultRadius 20        //Sqthere半径  
#define defaultSubsamplingFactor 16 //子采样概率  
#define background 0        //背景像素  
#define foreground 255      //前景像素  
  
void Initialize(CvMat* pFrameMat,RNG rng);//初始化  
void update(CvMat* pFrameMat,CvMat* segMat,RNG rng,int nFrmNum);//更新


ViBe.cpp 实现文件

    #include "Vibe.h"  
    #include   
    #include   
      
    using namespace std;  
    using namespace cv;       
      
    float samples[1024][1024][defaultNbSamples+1];//保存每个像素点的样本值  
      
      
    //初始化  
    void Initialize(CvMat* pFrameMat,RNG rng){  
      
        //记录随机生成的 行(r) 和 列(c)  
        int rand,r,c;  
      
        //对每个像素样本进行初始化  
        for(int y=0;yrows;y++){//Height  
            for(int x=0;xcols;x++){//Width  
                for(int k=0;k=pFrameMat->rows) r=pFrameMat->rows-1;   //行  
                    rand = rng.uniform(-1,1);  
                    c= x+rand; if(c<0) c=0; if(c>=pFrameMat->cols) c=pFrameMat->cols-1; //列  
      
                    //存储像素样本值  
                    samples[y][x][k]=CV_MAT_ELEM(*pFrameMat,float,r,c);  
                }  
                samples[y][x][defaultNbSamples]=0;  
            }  
        }  
    }  
      
      
    //更新函数  
    void update(CvMat* pFrameMat,CvMat* segMat,RNG rng,int nFrmNum){  
      
        for(int y=0;yrows;y++){   //Height  
            for(int x=0;xcols;x++){   //Width  
      
                //用于判断一个点是否是背景点,index记录已比较的样本个数,count表示匹配的样本个数  
                int count=0,index=0;float dist=0;  
                //  
                while((count=defaultReqMatches){  
      
                    //判断为背景像素,只有背景点才能被用来传播和更新存储样本值  
                    samples[y][x][defaultNbSamples]=0;//??????????  
      
                    *((float *)CV_MAT_ELEM_PTR(*segMat,y,x))=background;  
      
                    int rand=rng.uniform(0,defaultSubsamplingFactor-1);  
                    if(rand==0){  
                        rand=rng.uniform(0,defaultNbSamples-1);///////////////  
                        samples[y][x][rand]=CV_MAT_ELEM(*pFrameMat,float,y,x);  
                    }  
                    rand=rng.uniform(0,defaultSubsamplingFactor-1);  
                    if(rand==0){  
                        int xN,yN;  
                        rand = rng.uniform(-1,1);yN = y+rand ;if(yN<0) yN=0; if(yN>=pFrameMat->rows) yN=pFrameMat->rows-1;  
                        rand = rng.uniform(-1,1);xN = x+rand ;if(xN<0) xN=0; if(xN>=pFrameMat->cols) xN=pFrameMat->cols-1;  
                        rand=rng.uniform(0,defaultNbSamples-1);  
                        samples[yN][xN][rand]=CV_MAT_ELEM(*pFrameMat,float,y,x);  
                    }   
                }  
                else {  
                    //判断为前景像素  
                    *((float *)CV_MAT_ELEM_PTR(*segMat,y,x))=foreground;  
      
                    samples[y][x][defaultNbSamples]++;  
                    if(samples[y][x][defaultNbSamples]>50){  
                        int rand=rng.uniform(0,defaultNbSamples);  
                        if(rand==0){  
                            rand=rng.uniform(0,defaultNbSamples);  
                            samples[y][x][rand]=CV_MAT_ELEM(*pFrameMat,float,y,x);  
                        }  
                    }  
                }  
            }  
        }  
    }  

main函数

    #include "stdafx.h"  
    #include   
    #include "Vibe.h"  
    #include   
      
    using namespace std;  
    using namespace cv;  
      
    int nFrmNum = 0;//记录帧数  

    //对运动目标用绿色矩形圈出
	void find_connected_components(IplImage* pFrame,IplImage* mask,float perimScale)
	{
		CvMemStorage* mem_storage = NULL;
		CvSeq* contours = NULL;

		IplImage* mask_temp = cvCreateImage(cvGetSize(mask),8,1);
		cvCopy(mask,mask_temp);

		mem_storage = cvCreateMemStorage(0);

		CvContourScanner scanner = cvStartFindContours(mask_temp,mem_storage,
			sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);

		CvSeq* c;
		int numCont = 0;
		while( ( c = cvFindNextContour(scanner)) != NULL )
		{
			double len = cvContourPerimeter(c);//计算轮廓的直径
			double q = (mask_temp->height + mask_temp->width) / perimScale;

			if( lenh_next)
		{
			//cvDrawContours(pFrame, c,  CV_RGB(255,0,0), CV_RGB(255, 0, 0),  2, 1.8, 8, cvPoint(0,0));
			CvRect rect = cvBoundingRect(c,0);  //根据序列,返回轮廓外围矩形;  

			CvPoint pt1,pt2;  
			pt1.x=rect.x;  
			pt1.y=rect.y;  
			pt2.x=rect.x+rect.width;  
			pt2.y=rect.y+rect.height;  
  
			cvDrawContours(pFrame,c,CV_RGB(255,0,0),CV_RGB(255,0,0),0);//画轮廓
			cvRectangle(pFrame,pt1,pt2,CV_RGB(0,255,0),2);//画矩形     
		}

		cvReleaseImage(&mask_temp);
		cvReleaseMemStorage(&mem_storage);
	}

    int main(int argc, char* argv[])  
    {  
        IplImage* pFrame=NULL;CvMat* pFrameMat = NULL;//pFrame对象  
        IplImage* pAfter=NULL;CvMat* pAfterMat=NULL;//保存pFrame对应的灰度图像  
        IplImage* segMap=NULL;CvMat* segMat=NULL;//保存处理后的信息  
      
        //要读取的视频文件和保存的视频文件路径  
        char* openfile="1.avi";  
      
        //打开视频文件  
        CvCapture* pCapture=cvCreateFileCapture(openfile);  
   
        if(pCapture==NULL) 
		{  
            cout<<"video file open error!"<width, pFrame->height),  IPL_DEPTH_8U,1);  
                segMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);  
                pAfter=cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1);  
                pAfterMat=cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);  
    
                //转化成单通道图像再处理  
                cvCvtColor(pFrame, pAfter, CV_BGR2GRAY);  
                cvConvert(pAfter, pAfterMat);  
                Initialize(pAfterMat,rng);  
            }  
            else 
			{  
                cvCvtColor(pFrame,pAfter,CV_BGR2GRAY);  
                cvConvert(pAfter,pAfterMat);  
                update(pAfterMat,segMat,rng,nFrmNum);//更新背景  
                cvConvert(segMat,segMap);  
				
				cvMorphologyEx(segMap,segMap,0,0,CV_MOP_OPEN,1);
				cvMorphologyEx(segMap,segMap,0,0,CV_MOP_CLOSE,1);

				//draw(pFrame,segMap);
				find_connected_components(pFrame,segMap,20);

				cvShowImage("src",pFrame);  
				cvShowImage("out",segMap);
				char c = cvWaitKey(40);
				if(c==' ')
					cvWaitKey();
            }  
        }  
        cvReleaseImage(&pFrame);cvReleaseMat(&pFrameMat);  
        cvReleaseImage(&pAfter);cvReleaseMat(&pAfterMat);  
        cvReleaseImage(&segMap);cvReleaseMat(&segMat);  

        cvReleaseImage(&dstImg);  
        cvDestroyWindow("src");  
		cvDestroyWindow("out");
        return 0;  
    }  

运行结果:

基于Vibe算法的运动目标检测_第1张图片

基本实现了对运动目标的检测与跟踪

基于Vibe算法的运动目标检测_第2张图片

但是,对于运动目标靠的太近,会错误的检测为同一个目标,后期想办法改进~~

你可能感兴趣的:(Opencv)