《学习OpenCV》平均距离法实现背景建模(page300)

本文档介绍了如何利用OpenCV库通过平均背景法来实现视频背景建模,适用于室内静态场景,避免了移动背景成分的影响。通过累积图像、计算背景和差值,设置阈值,从而实现背景减除并分割出前景物体。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/********************************************************************************************
	Averaging Background Method
	平均背景法
	We’ve just seen a simple method of learning background scenes and segmenting fore-
	ground objects. It will work well only with scenes that do not contain moving background 
	components (like a waving curtain or waving trees). It also assumes that the lighting 
	remains fairly constant (as in indoor static scenes). 
*********************************************************************************************/

#include "cv.h"
#include "highgui.h"

/*******************************************************/
//全局变量:我们为需要的不同临时图像和统计属性的图像创建指针
/*Float 3-channel images*/
IplImage* IavgF, *IdiffF, *IprevF, *IhiF, *IlowF;
IplImage* Iscratch, *Iscratch2;

/*Float 1-channel images*/
IplImage* Igray1, *Igray2, *Igray3;
IplImage* Ilow1, *Ilow2, *Ilow3;
IplImage* Ihi1, *Ihi2, *Ihi3;

/*Byte 1-channel image*/
IplImage* Imaskt;
IplImage* Imask;

float Icount;
/******************************************************/


/*该函数为需要的所有临时图像分配内存,传入来自视频的首帧图像作为大小参考*/
void AllocateImages( IplImage* I )
{
	CvSize sz = cvGetSize(I);
	/*Float 3-channel images*/
	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);
	cvZero(IavgF);
	cvZero(IdiffF);
	cvZero(IprevF);
	cvZero(IhiF);
	cvZero(IlowF);

	/*Float 1-channel images*/
	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);
	
	Icount = 1e-5;

	/*Float 3-channel images*/
	Iscratch	= cvCreateImage(sz, IPL_DEPTH_32F, 3);
	Iscratch2	= cvCreateImage(sz, IPL_DEPTH_32F, 3);
	cvZero(Iscratch);
	cvZero(Iscratch2);

	/*Float 1-channel images*/
	Igray1	= cvCreateImage(sz, IPL_DEPTH_32F, 1);
	Igray2	= cvCreateImage(sz, IPL_DEPTH_32F, 1);
	Igray3	= cvCreateImage(sz, IPL_DEPTH_32F, 1);

	/*Byte 1-channel image*/
	Imaskt	= cvCreateImage(sz, IPL_DEPTH_8U, 1);
	Imask	= cvCreateImage(sz, IPL_DEPTH_8U, 1);
}


/*累积背景图像和前后帧图像差值的绝对值,当累积够一定数量后就将其转换成一个背景统计模型*/
void accumulateBackground( IplImage* I )
{
	static int first = 1;							//局部静态变量,只初始化一次,意思就是第一次被赋值为1
	cvCvtScale( I, Iscratch, 1, 0 );				//即cvConvertScale,dst(I)=src(I)*scale+(shift,shift,...),此处Iscratch(I)=I(I)*1+0
													//将I指向的图像复制给Iscratch 不能用cvCopy,因为像素的位深度不同
	if (!first)										//刚开始,first=1,不进入
	{
		cvAcc( Iscratch, IavgF );					//累积原始的浮点图像到IIavgF
		cvAbsDiff( Iscratch, IprevF, Iscratch2 );	//计算前后帧图像绝对差图像到Iscratch2
		cvAcc( Iscratch2, IdiffF );					//将前后帧差值图像累加到IdiffF中	
		Icount += 1.0;								//记录累加的次数用于背景统计时计算均值		
	}
	first = 0;										//first为局部静态变量,,以后调用该函数将不再初始化为1,
													//意思就是除了第一次,以后调用该函数均进入if语句
	cvCopy(Iscratch, IprevF);						//IprevF用来保存前一帧图像
}


/*设置高阈值*/
void setHighThreshold( float scale )
{
	cvConvertScale( IdiffF, Iscratch, scale );		//将统计的绝对差分图像值放大scale倍赋给Iscratch:Iscratch = IdiffF*scale
	cvAdd( Iscratch, IavgF, IhiF );					//IhiF = Iscratch + IavgF
	cvSplit( IhiF, Ihi1, Ihi2, Ihi3, 0 );			//将阀值上限分割为多通道	
}


/*设置低阈值*/
void setLowThreshold( float scale )
{
	cvConvertScale( IdiffF, Iscratch, scale );		//Iscratch = IdiffF*scale
	cvSub( IavgF, Iscratch, IlowF );				//IlowF = IavgF - Iscratch
	cvSplit( IlowF, Ilow1, Ilow2, Ilow3, 0 );		//将阀值下限分割为多通道
	
}

/*当累积足够多的帧图后,就将其转化成一个背景统计模型,该函数用于计算每个像素的均值和平均绝对差分*/
void createModelsfromStats()
{
	cvConvertScale( IavgF, IavgF, (double)(1.0/Icount));	//计算平均原始图像到 IavgF:IavgF = IavgF/Icount
	cvConvertScale( IdiffF, IdiffF, (double)(1.0/Icount));	//计算绝对差分图像到 IdiffF:IdiffF = IdiffF/Icount
	cvAddS( IdiffF, cvScalar(1.0, 1.0, 1.0), IdiffF );		//使得到的绝对差分图像每个像素值均不为空,故每个像素加上1
	setHighThreshold(7.0);									//根据统计的背景模型设定一个阀值上限和下限	
	setLowThreshold(6.0);									//如果 IlowF<=Temp<IhiF 时认为其为背景,否则为视频中出现的运动目标物体	
}

void backgroundDiff( IplImage* I )
{
	cvCvtScale( I, Iscratch, 1, 0 );				//将I指向的图像复制给Iscratch 不能用cvCopy, 因为像素的位深度不同
	cvSplit( Iscratch, Igray1, Igray2, Igray3, 0 );	//得到的当前帧分割成3个单通道图像	
	cvInRange( Igray1, Ilow1, Ihi1, Imask );		//检查这些单通道图像是否在平均背景像素高低阀值之间
	//         src     lower  upper dst				  如果src(I)在范围内(lower<=src<upper),dst(I)被设置为0xff(每一位都是'1')否则置0
	cvInRange( Igray2, Ilow2, Ihi2, Imaskt );
	cvOr( Imask, Imaskt, Imask );					//计算两个数组每个元素的按位或值赋值给第三个参数
	cvInRange( Igray3, Ilow3, Ihi3, Imaskt );
	cvOr( Imask, Imaskt, Imask );					//最后Imask为分离出的前景二值图
	cvSubRS(Imask, cvScalar(255), Imask);			//计算数量和数组之间的差,将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 ); 
}

int main()
{
	CvCapture* capture = cvCreateCameraCapture(0);	//初始化从摄像机中获取视频
	if (!capture)
	{
		printf( "Couldn't Open the file." );
		return -1;
	}
	cvNamedWindow( "raw" );
	cvNamedWindow( "avg" );

	IplImage* rawImage = cvQueryFrame(capture);		//这个函数仅仅是函数cvGrabFrame和函数cvRetrieveFrame在一起调用的组合
	cvShowImage( "raw", rawImage );					//显示第一帧图像
	AllocateImages( rawImage );						//以第一帧图像分配图像大小		
	
	for (int i=0; ; i++)
	{
		if (i<=30) 
		{
			accumulateBackground( rawImage );		//前30帧用于累积计算背景图像	
			if (i==30)								//将前30帧转换成一个背景统计模型	
				createModelsfromStats();
		}
		else 
			backgroundDiff( rawImage );				//建立好背景模型后调用此函数进行图像分割
		cvShowImage( "avg", Imask );					//播放分割后的目标图像结果

		if (cvWaitKey(33)==27)						//每33ms 播放一帧		
			break;
		if (!(rawImage = cvQueryFrame(capture)))
			break;
		cvShowImage( "raw", rawImage );				//显示源图像
	}	
	DeallocateImages();								//撤销内存

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值