平均背景法的原理

《Learning OpenCV》中的第9中开篇介绍了一种建立背景模型的方法——平均背景法。 但书上只是简单地介绍了一下这种方法的思想,更多的原理蕴含在它给出的代码当中。

平均背景法是一种建立背景模型的方法。简单地说,就是从视频或摄像头中获取一系列的帧,然后取这些帧中的平均像素值来表征背景。然后再给这些平均像素值加上一定的阈值范围,这就构成了背景模型。在新加入的图像中,如果对应位置的像素超出了这个背景模型中对应位置像素的阈值范围,就把它当作前景。

应用的例子:举一个例子来说明背景模型的作用,比如说深夜里,摄像头检测是否有可疑人物进去,一旦建立了背景模型,摄像头所拍摄的图像将会与背景模型图像进行比较,如果图像中的像素变动很大,则说明前景发生了变化。如果有可疑人物进入摄像头的范围,这种变化就可以被捕捉到,可以进行报警等操作。

参考后面附上的代码,下面介绍平均背景法的建模和分割的步骤:

步骤1: 累积背景

从视频或摄像头中连续获取301帧图像,把这301帧图像分别标为I1I2 ... I300、 I301

 

l  从第2帧图像开始,把后面的300帧图像累加起来(这种累加是矩阵加法,下同)I2+…+ I300+I301 (因为考虑到后面的帧差需要从第2帧开始计算,所以这里也是从第2帧开始),得到图像IavgF

 

定义:帧差 = In-In-1

l  把300帧图像里面的每一个帧差累加起来[I2-I1+I3-I4+…+I299-I300+(I300-I301)]得到图像 Iscratch

 

步骤2: 创建基于统计学的背景模型

由步骤1得到了两个图像,IavgF 和Iscratch

l  计算出这300帧中每帧的平均值IavgF * (1.0/300) --> IavgF

l  从这300帧的帧差和中计算出平均帧差IdiffF * (1.0/300) --> IdiffF

 

l  计算阈值的上限:(这里以7.0作为一个计算阈值上限的参数)

IavgF + [(IdiffF + 1.0) * 7.0] --> IhiF

把IhiF这个3通道图分割成3个单通道图Ihi1, Ihi2, Ihi3分别代表B、G、R通道

 

l  计算阈值的下限:(这里以6.0作为一个计算阈值下限的参数)

IavgF - [(IdiffF + 1.0) * 6.0] --> IlowF

把IhiF这个3通道图分割成3个单通道图Ilow1, Ilow2, Ilow3分别代表B、G、R通道

 

注意:帧差越大的地方,计算出来的阈值范围就越大。最直观的感受是,变化得比较厉害的那些位置,拥有的阈值范围较大。

步骤3: 利用背景模型分割背景

继续从视频或摄像头中获取图像帧,把获取到的图像帧标为I

l  把I这个3通道图分割成3个单通道图Igray1, Igray2, Igray3分别代表B、G、R通道

l  对Igray1, Igray2,Igray3进行阈值处理:

对于Igray1,如果图像中的像素值在(low1, lhi1)的范围之内,则把该像素值设置为255,否则把该像素值设为0。对Igray2和Igray3作同样的处理。然后合并Igray1, Igray2, Igray3这3个单通道图为1个3通道图Imask。

l  作最后处理: 255 – Imask à Imask

 

结果是:

l  在建立背景模型Imask的过程中,像素值不常变化的地方,在最后得到的背景模型当中阈值范围较小,只要图像帧I中的像素在这些阈值范围较小的敏感地方,对应于背景模型图像Imask中的像素稍微出现一些变动,结果图像中那些变动的位置就会呈现出白色(因为通过计算所得到的像素值为255),而没有变动的地方则呈现黑色(因为通过计算所得到的像素值为0)。

l  然而,在建立背景模型Imask的过程中,在像素值经常变化的地方,在最后得到的背景模型当中阈值范围较大,所以这些区域就不那么敏感了,需要变化较大时才会显现白色。

参考代码

这篇博文对《Learning OpenCV》一书当中的代码进行了一点扩充,而且给出了一些有关背景模型的资料,我在学习这种方法的时候也是参考了这篇博文,通过代码来理解平均背景法的原理。这里引用一下博文里给出的代码,为了帮助理解代码,我同时给出了一个帮助理解这些代码的伪代码:

#include <cv.h>
#include <highgui.h>
#include <stdio.h>
IplImage *IavgF,*IdiffF,*IprevF,*IhiF,*IlowF;
IplImage *Iscratch,*Iscratch2;
IplImage * Igray1,*Igray2,*Igray3;
IplImage *Ilow1,*Ilow2 ,*Ilow3;
IplImage *Ihi1,*Ihi2,*Ihi3;
IplImage *Imaskt;
IplImage *Imask;
float Icount;
void AllocateImages(IplImage *I);
void accumulateBackground(IplImage * I);
void createModelsfromStats();
void setHighThreshold(float scale);
void setLowThreshold(float scale);
void backgroundDiff(IplImage *I,IplImage * Imask);
void DeallocateImages();
int main(int argc,char ** argv)
{
	CvCapture *capture=cvCreateCameraCapture(0);
	if (NULL==capture)
	{
		printf("没有找到摄像头装置!\n");
		return -1;
	}
	IplImage * Img=cvQueryFrame(capture);
	cvNamedWindow("原图",0);
	cvNamedWindow("检测图",0);
	cvShowImage("原图",Img);
	AllocateImages(Img);//初始化
	printf("开始统计背景模型\n");
	while (Icount<300)//统计背景模型
	{
		accumulateBackground(Img);
		Img=cvQueryFrame(capture);
		cvWaitKey(100);
		cvShowImage("原图",Img);
		printf(".");
	}
	createModelsfromStats();//就算每一个像素的均值和方差观测
	printf("\n统计背景模型结束.....\n");
	printf("按任意键开始分割图像.....\n");
	cvWaitKey(NULL);
	printf("开始分割图像.....\n");
	while (1)
	{
		Img=cvQueryFrame(capture);
		backgroundDiff(Img,Imask);
		cvShowImage("原图",Img);
		cvShowImage("检测图",Imask);
		if (27==cvWaitKey(100))
			break;          
	}
	cvDestroyAllWindows();
	//释放内存
	DeallocateImages();
	cvReleaseCapture(&capture);
	return 0;

}

void AllocateImages(IplImage *I)
{
	CvSize sz=cvGetSize(I);
	IavgF=cvCreateImage(sz,IPL_DEPTH_32F,3);
	IdiffF=cvCreateImage(sz,IPL_DEPTH_32F,3);
	IprevF=cvCreateImage(sz,IPL_DEPTH_32F,3);
	IhiF=cvCreateImage(sz,IPL_DEPTH_32F,3);
	IlowF=cvCreateImage(sz,IPL_DEPTH_32F,3);
	Ilow1=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Ilow2=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Ilow3=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Ihi1=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Ihi2=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Ihi3=cvCreateImage(sz,IPL_DEPTH_32F,1);
	cvZero(IavgF);
	cvZero(IdiffF);
	cvZero(IprevF);
	cvZero(IhiF);
	cvZero(IlowF);
	Icount=0.00001;
	Iscratch=cvCreateImage(sz,IPL_DEPTH_32F,3);
	Iscratch2=cvCreateImage(sz,IPL_DEPTH_32F,3);
	Igray1=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Igray2=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Igray3=cvCreateImage(sz,IPL_DEPTH_32F,1);
	Imaskt=cvCreateImage(sz,IPL_DEPTH_8U,1);
	Imask=cvCreateImage(sz,IPL_DEPTH_8U,1);
	cvZero(Iscratch);
	cvZero(Iscratch2);
	cvZero(Imask);
	cvZero(Imaskt);
}
void accumulateBackground(IplImage * I)
{
	static int first=1;
	cvCvtScale(I,Iscratch,1,0);
	if (!first)
	{
		cvAcc(Iscratch,IavgF);
		cvAbsDiff(Iscratch,IprevF,Iscratch2);
		cvAcc(Iscratch2,IdiffF);
		Icount+=1.0;
	}
	first=0;
	cvCopy(Iscratch,IprevF);
}
void createModelsfromStats()
{
	//通过除以输入图像积累的数目计算平均原始图像和绝对差分图像
	cvConvertScale(IavgF,IavgF,(double)(1.0/Icount));
	cvConvertScale(IdiffF,IdiffF,(double)(1.0/Icount));

	//make sure diff is slways something
	cvAddS(IdiffF,cvScalar(1.0,1.0,1.0),IdiffF);//确保平均差分图像的值最小是1
	setHighThreshold(7.0);//当计算前景和背景阈值以及避免前景阈值和背景阈值相等而出现的退化情况时,我们要缩放这个因素
	setLowThreshold(6.0);
}
void setHighThreshold(float scale)
{
	cvConvertScale(IdiffF,Iscratch,scale);
	cvAdd(Iscratch,IavgF,IhiF);
	cvSplit(IhiF,Ihi1,Ihi2,Ihi3,0);
}
void setLowThreshold(float scale)
{
	cvConvertScale(IdiffF,Iscratch,scale);
	cvSub(IavgF,Iscratch,IlowF);
	cvSplit(IlowF,Ilow1,Ilow2,Ilow3,0);
}
void backgroundDiff(IplImage *I,IplImage * Imask)
{
	cvConvertScale(I,Iscratch,1,0);
	cvSplit(Iscratch,Igray1,Igray2,Igray3,0);
	cvInRange(Igray1,Ilow1,Ihi1,Imask);
	cvInRange(Igray2,Ilow2,Ihi2,Imaskt);
	cvOr(Imask,Imaskt,Imask);
	cvInRange(Igray3,Ilow3,Ihi3,Imask);
	cvOr(Imask,Imaskt,Imask);
	cvSubRS(Imask,cvScalar(255),Imask);
}
void DeallocateImages()
{
	cvReleaseImage(&IavgF);
	cvReleaseImage(&IdiffF);
	cvReleaseImage(&IprevF);
	cvReleaseImage(&IhiF);
	cvReleaseImage(&IlowF);
	cvReleaseImage(&Ilow1);
	cvReleaseImage(&Ilow2);
	cvReleaseImage(&Ilow3);
	cvReleaseImage(&Ihi1);
	cvReleaseImage(&Ihi2);
	cvReleaseImage(&Ihi3);
	cvReleaseImage(&Iscratch);
	cvReleaseImage(&Iscratch2);
	cvReleaseImage(&Igray1);
	cvReleaseImage(&Igray2);
	cvReleaseImage(&Igray3);
	cvReleaseImage(&Imaskt);
	cvReleaseImage(&Imask);
}

 

对应的伪代码

 Step1 : Accumulate background 

 Icount = 300 times 

                  convert to float 
 I(a frame) ------------------>  Iscratch + IavgF-->  IavgF (do the loop for 300 times) 
                                                     - 
                                                  IprevF 
                                                     = 
                                                Iscratch2 

 Iscratch2 + IdiffF --> IdiffF 


Step2: Create model from statistics 

Icount = 300 

IavgF * (1.0/Icount) --> IavgF 
IdiffF * (1.0/Icount) --> IdiffF 

IavgF + [(IdiffF + 1.0) * 7.0] --> IhiF --> Ihi1, Ihi2, Ihi3 
IavgF - [(IdiffF + 1.0) * 6.0] --> IlowF --> Ilow1, Ilow2, Ilow3 


Step3: Segmentation 

                  convert to float 
I(a frame)  ------------------> Iscratch --> Igray1, Igray2, Igray3 

            (Ilow1,Ihi1)-> 255 otherwise -> 0 
Igray1 ------------------------------------> Imask 
     
             (Ilow1,Ihi1)-> 255 otherwise -> 0 
Igray2 ------------------------------------> Imaskt 

Imask | Imaskt --> Imask 

              (Ilow1,Ihi1)-> 255 otherwise -> 0 
Igray3 -------------------------------------> Imask 

Imask | Imaskt --> Imask 
255 - Imask  --> Imask 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值