本文实现的检测效果跟原文基本相同,但是在实现上做了很多改进,检测速度比原算法快,而且是纯C语言实现
本文实现的算法也跟VIBE原算法和混合高斯背景建模一样,对光照突变都很敏感,在后续的文章中会提出自适应阈值的方法来改进VIBE算法,使其能适应运动物体接近摄像头时引起的图像亮度变化,同时不会影响算法的速度。
废话不多说先上三张检测图:
ViBe.h
#ifndef _VIBE_H
#define _VIBE_H
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/video/video.hpp"
using namespace std;
using namespace cv;
#define VIBE_WIDTH (360) //图像宽
#define VIBE_HEIGHT (240)//图像高
#define VIBE_FORGROUND_COUNT (100) //统计前景出现次数,如果超过此次数则把前景更新到背景中
void VibeForground(unsigned char *CurImg,unsigned char* ForImg);
#endif //_VIBE_H
ViBe.cpp
#include "ViBe.h"
const int gVibeRandom = 127; //vibe子采样率为16
const int gVibeSize = VIBE_WIDTH*VIBE_HEIGHT;
const int gRadius = 20; //当前帧与背景模型作差的阈值(可变)
const int gMin = 3; //当前帧与背景模型做差其差值小于20的个数,如果大于等于3则认为是当前像素是背景
const int gVibeSimpleSize = 8; //背景模型保存的帧数
const int gVibeModelLastSize =VIBE_WIDTH*VIBE_HEIGHT*8;
static unsigned char VibeModelData[VIBE_WIDTH*VIBE_HEIGHT*9]; //用于存储样本集
static unsigned char BackGroundData[VIBE_WIDTH*VIBE_HEIGHT];
//---------------------------------------------------------------------------
// Function name:InitialModelData()
// Function :init VibeModelData by first Frame ,
// Parameter:pImgData
//---------------------------------------------------------------------------
void InitialModelData(const unsigned char *pImgData)
{
int i=0;
for (i=0;i<gVibeSimpleSize;i++)
{
memcpy(VibeModelData +gVibeSize * i, pImgData, gVibeSize);
}
}
//---------------------------------------------------------------------------
// Function name:VibeForground()
// Function:The current frame and background model are compared,
// and the foreground of the current frame is obtained.
// Parameter:CurImgPyr
// Reference:<<A universal background subtraction algorithm for video sequences>>
// 201507--zhangbin
//---------------------------------------------------------------------------
void VibeForground(unsigned char *CurImg,unsigned char* ForImg)
{
unsigned int nOffset = 0;//偏移量
short nDiff = 0; //保存当前帧与背景模型中某帧的差值
unsigned int j = 0;
unsigned int i = 0;
unsigned char nCount = 0;
unsigned char *pSrcImg = 0;
unsigned char *pDstImg = 0;
static unsigned char CountNum=0; //此数除以16,127为一个循环,共能够取0-7之前的随机数(模拟VIBE子采样概率生成的随机数)
static int g_IsModelInit = 1;
pSrcImg = CurImg;
pDstImg = ForImg;
CountNum++;
if (CountNum>gVibeRandom)
{
CountNum=0;
}
if(g_IsModelInit)
{
InitialModelData(pSrcImg); //当前帧初始化背景
memcpy(BackGroundData, pSrcImg, gVibeSize);
g_IsModelInit = 0;
return;
}
nOffset = (CountNum>>4) * gVibeSize; //CountNum>>4相当于16帧生成一个数
for(i = 0; i < gVibeSize; i++)
{
nCount = 0;
pDstImg[i] = 255;
for(j = 0; j < gVibeSimpleSize; j++) //遍历背景模型
{
nDiff = VibeModelData[j*gVibeSize+i]-pSrcImg[i];
if(nDiff <= gRadius )
{
if (nDiff >= -gRadius)
{
nCount++;
}
}
if(nCount >= gMin) //当前帧像素值小于gRadius的次数大于gMin(论文中为#min = 2)则此点判断为背景
{
pDstImg[i] = 0;
break;
}
}
}
for(i = 0; i < gVibeSize; i++)
{
if (pDstImg[i]) //如果此点为前景则累加此点为前景的次数
{
VibeModelData[gVibeModelLastSize+i]++;
if ( VibeModelData[gVibeModelLastSize+i]>100)
{
VibeModelData[nOffset+i] = pSrcImg[i];
}
}else//如果此点为背景,则对背景模型进行更新
{
VibeModelData[nOffset+i] = pSrcImg[i];
BackGroundData[i] = pSrcImg[i];
VibeModelData[gVibeModelLastSize+i] = 0;
}
}
}
main.cpp
#include "ViBe.h"
#include <stdio.h>
char *VideoName = "E:\\ViBe\\测试视频\\seq-320-240.avi";
int main(int argc, char* argv[])
{
//声明IplImage指针
IplImage* pFrame = NULL;
IplImage* pResizeFrame = NULL;
IplImage* pGrayImg = NULL;
IplImage* pFrImg = NULL;
CvCapture* pCapture = NULL;
double t;
int nFrmNum = 0;
int otsuThresh = 0;
//创建窗口
cvNamedWindow("video", 1);
cvNamedWindow("foreground",1);
//使窗口有序排列
cvMoveWindow("video", 300, 50);
cvMoveWindow("foreground", 680, 50);
if( !(pCapture = cvCaptureFromAVI(VideoName)))
{
fprintf(stderr, "Can not open video.\n");
return -2;
}
//逐帧读取视频
while(pFrame = cvQueryFrame( pCapture ))
{
nFrmNum++;
//如果是第一帧,需要申请内存,并初始化
if(nFrmNum == 1)
{
pResizeFrame = cvCreateImage(cvSize(VIBE_WIDTH, VIBE_HEIGHT), IPL_DEPTH_8U,3);
pFrImg = cvCreateImage(cvSize(VIBE_WIDTH, VIBE_HEIGHT), IPL_DEPTH_8U,1);
pGrayImg = cvCreateImage(cvSize(VIBE_WIDTH, VIBE_HEIGHT), IPL_DEPTH_8U,1);
cvResize(pFrame,pResizeFrame,1);
cvCvtColor(pResizeFrame, pGrayImg, CV_BGR2GRAY);
VibeForground((unsigned char*)pGrayImg->imageData,(unsigned char*)pFrImg->imageData);
}
else
{
cvResize(pFrame,pResizeFrame,1);
cvCvtColor(pResizeFrame, pGrayImg, CV_BGR2GRAY);
t = (double)cvGetTickCount();
VibeForground((unsigned char*)pGrayImg->imageData,(unsigned char*)pFrImg->imageData);
t = (double)cvGetTickCount() - t;
printf( "exec time = %gms\n", t/(cvGetTickFrequency()*1000));
//显示图像
cvShowImage("video", pGrayImg);
cvShowImage("foreground", pFrImg);
char c=cvWaitKey(5);//显示每一帧之间有5毫秒的间隔
if(c==27)break;//如果在这间隔期间用户触发Esc按键 循环就退出 否则继续执行循环
}
}
//销毁窗口
cvDestroyWindow("video");
cvDestroyWindow("foreground");
//释放图像和矩阵
cvReleaseImage(&pFrImg);
cvReleaseImage(&pGrayImg);
cvReleaseImage(&pResizeFrame);
cvReleaseCapture(&pCapture);
system("pause");
return 0;
}
http://blog.youkuaiyun.com/huiguixian/article/details/17334195