vibe在opencv的2.3.1以上,但在2.4.6下有提供这个算法。但是必须在gpu模式下,这个比较蛋疼。首先要重新编译opencv,再就要学习c++接口的opencv,原始的c接口的是不行的。显卡的配置也要求比较高,我测试时是使用GeForce 660测试的,再低端的显卡不知道可不可以。效率跟vibe算法作者提供的程序类似。如果项目的预算不是很高,可以使用我改的程序,预算高的话可以使用opencv的gpu_vibe函数。
vibe官方提供的程序的运行截图:
命令的输入简图:
由于网上可以找到vibe的实现的源代码,所以我也下了一个。
原始的代码地址在:http://pan.baidu.com/share/link?shareid=409860&uk=3373051938 但是在网友“开心每一天的提醒下,发现代码有问题,于是我自己改了改。,原来的代码的随机数产生有问题。
更改后的代码如下:
//vibe.cpp
#include "stdafx.h"
#include "hanshu.h"
#include <opencv2/opencv.hpp>
#include "highgui.h"
#include <math.h>
#include <iostream>
using namespace std;
using namespace cv;
static int c_xoff[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0};//x的邻居点
static int c_yoff[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0};//y的邻居点
float samples[1024][1024][defaultNbSamples+1];//保存每个像素点的样本值
//初始化
void Initialize(CvMat* pFrameMat,RNG rng){
//记录随机生成的 行(r) 和 列(c)
int rand,r,c;
//对每个像素样本进行初始化
for(int y=0;y<pFrameMat->rows;y++){//Height
for(int x=0;x<pFrameMat->cols;x++){//Width
for(int k=0;k<defaultNbSamples;k++){
//随机获取像素样本值
//rand=rng.uniform( 0, 9 );
//r=y+c_yoff[rand]; if(r<0) r=0; if(r>=pFrameMat->rows) r=pFrameMat->rows-1; //行
//c=x+c_xoff[rand]; if(c<0) c=0; if(c>=pFrameMat->cols) c=pFrameMat->cols-1; //列
rand = rng.uniform(-1,1);
r=y+rand;if(r<0) r=0; if(r>=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;y<pFrameMat->rows;y++){ //Height
for(int x=0;x<pFrameMat->cols;x++){ //Width
//用于判断一个点是否是背景点,index记录已比较的样本个数,count表示匹配的样本个数
int count=0,index=0;float dist=0;
//
while((count<defaultReqMatches) && (index<defaultNbSamples)){
dist=CV_MAT_ELEM(*pFrameMat,float,y,x)-samples[y][x][index];
if(dist<0) dist=-dist;
if(dist<defaultRadius) count++;
index++;
}
if(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(0,9);yN=y+c_yoff[rand];if(yN<0) yN=0; if(yN>=pFrameMat->rows) yN=pFrameMat->rows-1;
//rand=rng.uniform(0,9);xN=x+c_xoff[rand];if(xN<0) xN=0; if(xN>=pFrameMat->cols) xN=pFrameMat->cols-1;
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);
}
}
}
}
}
}
vibe的头文件没有更改,和原来的一样。
vibe.h
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include "highgui.h"
#include <iostream>
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);//更新
测试用的main函数如下:
//main.cpp
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include "highgui.h"
#include "hanshu.h"
#include <iostream>
using namespace std;
using namespace cv;
int nFrmNum = 0;//记录帧数
int Width;//记录帧的宽度
int Height;//记录帧的高度
int FrameRate;//记录视频帧率
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="video.avi";
char* outfile="out.avi";
char filename[100];//保存的图像位置和名称
//打开视频文件
CvCapture* pCapture=cvCreateFileCapture(openfile);
//CvCapture* pCapture= cvCreateCameraCapture(-1);
if(pCapture==NULL) {
cout<<"video file open error!"<<endl;
return -1;
}
int array_cross[] ={ 0, 0xff, 0,
0xff,0xff, 0xff,
0 ,0xff, 0
};
IplConvKernel * rectCross= cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,array_cross);
//获取视频相关信息,帧率和大小
double fps=cvGetCaptureProperty(pCapture,CV_CAP_PROP_FPS);
CvSize size=cvSize((int)cvGetCaptureProperty(pCapture,CV_CAP_PROP_FRAME_WIDTH)*3,
(int)cvGetCaptureProperty(pCapture,CV_CAP_PROP_FRAME_HEIGHT));
//创建输出视频文件
CvVideoWriter* Save_result=NULL;
Save_result=cvCreateVideoWriter(outfile,CV_FOURCC('X','V','I','D'),fps,size,1);
IplImage* dstImg=cvCreateImage(size,IPL_DEPTH_8U,3);//创建要保存的图像
//创建窗口
cvNamedWindow("video",CV_WINDOW_AUTOSIZE);
//创建一个随机数生成器
RNG rng(0xFFFFFFFF);
//定义字体
CvFont font;
cvInitFont( &font, CV_FONT_HERSHEY_COMPLEX_SMALL ,1, 1, 0, 1, 8);
//逐帧读取视频并进行处理
while(pFrame = cvQueryFrame( pCapture )){
nFrmNum++;
//如果是第一帧,申请内存并进行初始化
if(nFrmNum==1){
segMap = cvCreateImage(cvSize(pFrame->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);
//载入原图像到目标图像
cvSetImageROI(dstImg, cvRect(0, 0, pFrame->width, pFrame->height));
cvCopy(pFrame, dstImg);
cvResetImageROI(dstImg);
//将segMap转换成三通道图像存在pFrame中
cvCvtColor(segMap,pFrame,CV_GRAY2BGR);
//载入检测后的图像到目标图像
cvSetImageROI(dstImg, cvRect(pFrame->width, 0, pFrame->width, pFrame->height));
cvCopy(pFrame, dstImg);
cvResetImageROI(dstImg);
cvSetImageROI(dstImg,cvRect(pFrame->width*2,0,pFrame->width,pFrame->height));
cvDilate(pFrame,dstImg);
cvResetImageROI(dstImg);
//显示提示文字
cvPutText(dstImg, "Origin Video", cvPoint(0,pFrame->height-5), &font,CV_RGB(255,255,0));
cvPutText(dstImg, "ViBe Video", cvPoint(pFrame->width,pFrame->height-5),&font,CV_RGB(255,255,0));
//保存视频和输出
//cvWriteFrame(Save_result,dstImg);
/*输出图片
if(nFrmNum<11)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\000%d_Vibe.jpg",nFrmNum-1);
else if(nFrmNum<101)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\00%d_Vibe.jpg",nFrmNum-1);
else if(nFrmNum<1001)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\0%d_Vibe.jpg",nFrmNum-1);
else if(nFrmNum<10001)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\%d_Vibe.jpg",nFrmNum-1);
cvSaveImage(filename,dstImg);
*/
cvShowImage("video",dstImg);
cvWaitKey();
//if(cvWaitKey(5)>=0) break;
}
}
cvReleaseImage(&pFrame);cvReleaseMat(&pFrameMat);
cvReleaseImage(&pAfter);cvReleaseMat(&pAfterMat);
cvReleaseImage(&segMap);cvReleaseMat(&segMat);
cvReleaseVideoWriter(&Save_result);
cvReleaseImage(&dstImg);
cvDestroyWindow("video");
return 0;
}
运行的结果如下: