高斯分布与背景建模的关系:图像中每一个像素点的颜色值作为一个随机过程X,并假设该点的像素值出现的概率服从高斯分布。令I(x,y,t)表示像素点(x,y,t)在t时刻的像素值,则有:
其中和
分别为t时刻该像素高斯分布的期望值和标准差。
算法流程:
1.用第一帧图像数据初始化背景模型,其中std_init通常设置为20。
2.检测前景与背景像素。
背景像素检测公式:
前景像素检测公式:
3.对、
、
背景值进行更新,更新公式如下:
4.返回到2直至停止。
#include <stdlib.h>
#include <stdio.h>
#include <highgui.h>
#include <cv.h>
#include <math.h>
#include <cxcore.h>
int main(int argc,char *argv[])
{
//新建窗口
cvNamedWindow("origin",CV_WINDOW_AUTOSIZE);
cvNamedWindow("background",CV_WINDOW_AUTOSIZE);
cvNamedWindow("foreground",CV_WINDOW_AUTOSIZE);
cvMoveWindow("origin", 1100, 400);
cvMoveWindow("background", 600, 400);
cvMoveWindow("foreground", 100, 400);
double alpha = 0.5;//背景建模alpha值
double std_int = 20;//初始化标准差
double var_int = std_int*std_int;//初始化方差
double lamda = 2.5*1.2;//背景更新参数
//视频文件
CvCapture *capture = NULL;
//读取视频文件
if(argc==1)
{
//从摄像头读入
capture = cvCreateCameraCapture(0);
}
else if(argc==2)
{
//从文件读入
capture = cvCreateFileCapture(argv[1]);
}
else
{
//读入错误
printf("input error\n");
return -1;
}
IplImage *frame = NULL;//原始图像
IplImage *frame_u=NULL;//期望图像(背景)
IplImage *frame_d=NULL;//前景图像
IplImage *frame_var=NULL;//方差图像
IplImage *frame_std=NULL;//标准差
CvScalar pixel={0};//像素原始值
CvScalar pixel_u={0};//像素期望值
CvScalar pixel_d={0};//像素前景
CvScalar pixel_var={0};//像素方差
CvScalar pixel_std={0};//像素标准差
//初始化frame_u,frame_d,frame_var,frame_std
frame=cvQueryFrame(capture);
frame_u=cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,3);
frame_d=cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,3);
frame_var=cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,3);
frame_std=cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,3);
for(int y=0;y<frame->height;++y)
{
for(int x=0;x<frame->width;++x)
{
pixel = cvGet2D(frame,y,x);
pixel_u.val[0]=pixel.val[0];
pixel_u.val[1]=pixel.val[1];
pixel_u.val[2]=pixel.val[2];
pixel_d.val[0]=0;
pixel_d.val[1]=0;
pixel_d.val[2]=0;
pixel_std.val[0]=std_int;
pixel_std.val[1]=std_int;
pixel_std.val[2]=std_int;
pixel_var.val[0]=var_int;
pixel_var.val[1]=var_int;
pixel_var.val[2]=var_int;
cvSet2D(frame_u,y,x,pixel_u);
cvSet2D(frame_d,y,x,pixel_d);
cvSet2D(frame_var,y,x,pixel_var);
cvSet2D(frame_std,y,x,pixel_std);
}
}
while(cvWaitKey(33)!=27)//按ESC键退出,帧率33ms
{
frame = cvQueryFrame(capture);
//视频结束退出
if(!frame)
{
break;
}
//单高斯背景更新
for(int y=0;y<frame->height;++y)
{
for(int x=0;x<frame->width;++x)
{
pixel=cvGet2D(frame,y,x);
pixel_u=cvGet2D(frame_u,y,x);
pixel_d=cvGet2D(frame_d,y,x);
pixel_std=cvGet2D(frame_std,y,x);
pixel_var=cvGet2D(frame_var,y,x);
//|I-u|<lamda*std时认为是背景,进行更新
if(fabs(pixel.val[0]-pixel_u.val[0])<lamda*pixel_std.val[0]&&
fabs(pixel.val[1]-pixel_u.val[1])<lamda*pixel_std.val[1]&&
fabs(pixel.val[2]-pixel_u.val[2])<lamda*pixel_std.val[2])
{
//更新期望u=(1-alpha)*u+alpha*I
pixel_u.val[0]=(1-alpha)*pixel_u.val[0]+alpha*pixel.val[0];
pixel_u.val[1]=(1-alpha)*pixel_u.val[1]+alpha*pixel.val[1];
pixel_u.val[2]=(1-alpha)*pixel_u.val[2]+alpha*pixel.val[2];
//更新方差var=(1-alpha)*var+alpha*(I-u)^2
pixel_var.val[0]=(1-alpha)*pixel_var.val[0]+
alpha*(pixel.val[0]-pixel_u.val[0])*(pixel.val[0]-pixel_u.val[0]);
pixel_var.val[1]=(1-alpha)*pixel_var.val[1]+
alpha*(pixel.val[1]-pixel_u.val[1])*(pixel.val[1]-pixel_u.val[1]);
pixel_var.val[2]=(1-alpha)*pixel_var.val[2]+
alpha*(pixel.val[2]-pixel_u.val[2])*(pixel.val[2]-pixel_u.val[2]);
//更新标准差
pixel_std.val[0]=sqrt(pixel_var.val[0]);
pixel_std.val[1]=sqrt(pixel_var.val[1]);
pixel_std.val[2]=sqrt(pixel_var.val[2]);
//写入矩阵
cvSet2D(frame_u,y,x,pixel_u);
cvSet2D(frame_var,y,x,pixel_var);
cvSet2D(frame_std,y,x,pixel_std);
}
else
{
pixel_d.val[0]=pixel.val[0]-pixel_u.val[0];
pixel_d.val[1]=pixel.val[1]-pixel_u.val[1];
pixel_d.val[2]=pixel.val[2]-pixel_u.val[2];
cvSet2D(frame_d,y,x,pixel_d);
}
}
}
//显示结果
frame_u->origin=1;
frame_u->origin=1;
cvShowImage("origin",frame);
cvShowImage("background",frame_u);
cvShowImage("foreground",frame_d);
}
//释放内存
cvReleaseCapture(&capture);
cvReleaseImage(&frame);
cvReleaseImage(&frame_u);
cvReleaseImage(&frame_var);
cvReleaseImage(&frame_std);
cvDestroyWindow("origin");
cvDestroyWindow("background");
cvDestroyWindow("foreground");
return 0;
}
从左到右,图片一次为前景,背景,原图。
备注:opencv使用的是1.0