opencv-视频处理-实时的前景检测-Vibe算法

vibe算法是一种像素级的前景检测算法,实时性高,内存占有率低,前景检测准确率高。但是会出现“鬼影”,当然基于对鬼影的处理,也会有相应的对vibe算法的改进。

把下面三篇文章看明白,基本就会掌握vibe算法的过程:

 ViBe: a powerful random technique to estimate the background in video sequences

Background Subtraction: Experiments and Improvements for ViBe 

ViBe: A universal background subtraction algorithm for video sequences

该算法的原文链接地址,作者已经给出了C代码。

以下用opencv复现一遍。

原理:

技术分享

论文中以(x,y)为中心,取3x3的区域,即(x,y)处的8-领域。也可以选择5x5区域,即24-领域

技术分享


对(x,y)处的8-领域,按平均分布 随机抽样numberSamples次,论文中给出的是numberSamples=20,假设以下为随机取样的结果:

技术分享

做为下一帧(x,y)处的背景模型。

问题1:怎么判断视频流中的下一帧坐标(x,y)处是背景点还是前景点?

技术分享


对于上述的结果,如果“1”的数量大于某一阈值minMatch(文章中设为2),则视为背景点,并更新背景模型,否则,为前景点。

问题2:更新背景模型的策略

文中给出了伪码,主要基于均匀随机抽样的方法。把背景点,按照一定的概率更新到背景模型中。

代码:(opencv实现)

class OriginalVibe{
public:
	//构造函数
	OriginalVibe(int _numberSamples, int _minMatch,int _distanceThreshold,int _updateFactor,int _neighborWidth,int _neighborHeight):numberSamples(_numberSamples),minMatch(_minMatch),distanceThreshold(_distanceThreshold),updateFactor(_updateFactor),neighborWidth(_neighborWidth),neighborHeight(_neighborHeight){};	
	~OriginalVibe(){};
	//操作成员变量
	void setUpdateFactor(int _updateFactor);
	//灰度图像
	void originalVibe_Init_GRAY(const Mat &firstFrame);
	void originalVibe_ClassifyAndUpdate_GRAY(const Mat &frame,OutputArray &_segmentation);
	//RGB三通道
	void originalVibe_Init_BGR(const Mat & firstFrame);
	void originalVibe_ClassifyAndUpdate_BGR(const Mat &frame,OutputArray &_segmentation);

private:
	//背景模型
	const int numberSamples;
	std::vector<Mat>  backgroundModel;
	//像素点的分类判断的参数
	const int minMatch;
	int distanceThreshold;
	//背景模型更新概率
	 int updateFactor;
	//8-领域(3 x 3)
	const int neighborWidth;
	const int neighborHeight;
	//前景和背景分割
	const static  unsigned char BACK_GROUND;
	const static  unsigned char FORE_GROUND;	
	//BGR的距离计算
	int distanceL1(const  Vec3b &src1,const  Vec3b &src2);
	float distanceL2(const Vec3b &src1,const	Vec3b &src2);
};


#include"originalVibe.h"
#include<iostream>
const unsigned char OriginalVibe::BACK_GROUND = 0;
const unsigned char OriginalVibe::FORE_GROUND = 255;

//操作成员变量
void OriginalVibe::setUpdateFactor(int _updateFactor)
{
	this->updateFactor = _updateFactor;
}
//第一种方法:最原始的vibe灰度通道
void OriginalVibe::originalVibe_Init_GRAY(const Mat &firstFrame)
{
	int height = firstFrame.rows;
	int width = firstFrame.cols;
	//背景模型分配内存
	backgroundModel.clear();
	for(int index = 0;index < this->numberSamples;index++)
	{
		backgroundModel.push_back(Mat::zeros(height,width,CV_8UC1));
	}
	//随机数
	RNG rng;
	int cshift;
	int rshift;
	for(int r = 0;r < height ;r++)
	{
		for(int c = 0;c < width ; c++)
		{
			if( c < neighborWidth/2 || c > width - neighborWidth/2 -1|| r < neighborHeight/2 || r > height - neighborHeight/2 -1)
			{
				/*随机数的生成方式有很多种*/
				/*
				cshift = randu<int>()%neighborWidth - neighborWidth/2;
				rshift = randu<int>()%neighborHeight - neighborHeight/2;
				*/	
				cshift = rand()%neighborWidth - neighborWidth/2;
				rshift = rand()%neighborHeight - neighborHeight/2;
				
				for(std::vector<Mat>::iterator it = backgroundModel.begin();it != backgroundModel.end();it++)
				{
					for(;;)
					{
						/*
						cshift = rng.uniform(-neighborWidth/2,neighborWidth/2 + 1);
						rshift = rng.uniform(-neighborHeight/2,neighborHeight/2 +1 );
						*/		
						cshift = abs(randu<int>()%neighborWidth) - neighborWidth/2;
						rshift = abs(randu<int>()%neighborHeight) - neighborHeight/2;
						
						if(!(cshift == 0 && rshift==0))
							break;
					}	
					if(c + cshift < 0 || c + cshift >=width)
						cshift *= -1;
					if(r + rshift < 0 || r + rshift >= height)
						rshift *= -1;
					(*it).at<uchar>(r,c) = firstFrame.at<uchar>(r+rshift,c+cshift);
				}
			}
			else
			{
				for(std::vector<Mat>::iterator it = backgroundModel.begin();it != backgroundModel.end();it++)
				{
					for(;;)
					{
						/*
						cshift = rng.uniform(-neighborWidth/2,neighborWidth/2 + 1);
						rshift = rng.uniform(-neighborHeight/2,neighborHeight/2 +1 );
						*/
						cshift = abs(randu<int>()%neighborWidth) - neighborWidth/2;
						rshift = abs(randu<int>()%neighborHeight) - neighborHeight/2;
						if(!(cshift == 0 && rshift == 0))
							break;
					}
					(*it).at<uchar>(r,c) = firstFrame.at<uchar>(r+rshift,c+cshift);
				}
			}
		}
	}
}
void OriginalVibe::originalVibe_ClassifyAndUpdate_GRAY(const Mat &frame,OutputArray &_segmentation)
{
	int width = frame.cols;
	int height = frame.rows;
	int rshift;
	int cshift;
	_segmentation.create(frame.size(),CV_8UC1);
	Mat segmentation = _segmentation.getMat();
	
	RNG rng;
	for(int r = 0; r < height;r++)
	{
		for(int c = 0;c < width ;c++)
		{
			int count = 0;
			unsigned char pixel = frame.at<uchar>(r,c);
			//让pixel和背景模板中backgroundModel进行比较
			for(std::vector<Mat>::iterator it = backgroundModel.begin();it != backgroundModel.end();it++)
			{
				if( abs( int(pixel) - int( (*it).at<uchar>(r,c)) )  < (this->distanceThreshold) )
				{
					count++;
					//循环到一定阶段,判断count的值是否大于 minMatch,更新背景模型
					if( count >= this->minMatch)
					{
						int random = rng.uniform(0,this->updateFactor);
						if(random == 0)
						{
							int updateIndex = rng.uniform(0,this->numberSamples);
							backgroundModel[updateIndex].at<uchar>(r,c) = pixel;
						}
						random = rng.uniform(0,this->updateFactor);
						if(random == 0)
						{
							if(c < neighborWidth/2 || c > width - neighborWidth/2-1 || r < neighborHeight/2 || r > height - neighborHeight/2-1)
							{
								for(;;)
								{
									/*
									 cshift = rng.uniform(-neighborWidth/2,neighborWidth/2 + 1);
									 rshift = rng.uniform(-neighborHeight/2,neighborHeight/2 +1 );
									*/
									cshift = abs(randu<int>()%neighborWidth) - neighborWidth/2;
									rshift = abs(randu<int>()%neighborHeight) - neighborHeight/2;
									if(!(cshift == 0 && rshift ==0))
										break;
								}		
								if(c + cshift < 0 || c + cshift >=width)
									cshift *= -1;
								if(r + rshift < 0 || r + rshift >= height)
									rshift *= -1;
								int updateIndex = rng.uniform(0,this->numberSamples);
								backgroundModel[updateIndex].at<uchar>(r+rshift,c+cshift) = pixel;
							}
							else
							{
								for(;;)
								{
									/*
									cshift = rng.uniform(-neighborWidth/2,neighborWidth/2 + 1);
									rshift = rng.uniform(-neighborHeight/2,neighborHeight/2 +1 );
									*/
									cshift = abs(randu<int>()%neighborWidth) - neighborWidth/2;
									rshift = abs(randu<int>()%neighborHeight) - neighborHeight/2;
									if(!(cshift == 0 && rshift==0))
										break;
								}								
								int updateIndex = rng.uniform(0,this->numberSamples);
								backgroundModel[updateIndex].at<uchar>(r+rshift,c+cshift) = pixel;
							}						
						}
						segmentation.at<uchar>(r,c) = this ->BACK_GROUND;
						break;
					}
				}
			}
			if( count < this->minMatch)	
			segmentation.at<uchar>(r,c) = this->FORE_GROUND;
		}
	}
}


//第三种方法:BGR通道
void OriginalVibe::originalVibe_Init_BGR(const Mat & fristFrame)
{
	int height = fristFrame.rows;
	int width = fristFrame.cols;
	//背景模型分配内存
	backgroundModel.clear();
	for(int index = 0;index  < this->numberSamples;index++)
	{
		backgroundModel.push_back( Mat::zeros(height,width,CV_8UC3) );
	}
	//随机数
	RNG rng;
	int cshift;
	int rshift;
	for(int r =0 ; r < height; r++)
	{
		for(int c = 0;c < width ;c++)
		{
			if( c < neighborWidth/2 || c > width - neighborWidth/2 -1|| r < neighborHeight/2 || r > height - neighborHeight/2 -1 )
			{
				/*
				 初始化背景模型:开始
				*/
				for(vector<Mat>::iterator iter = backgroundModel.begin(); iter != backgroundModel.end();iter++)
				{
					for(;;)
					{
						cshift = abs(randu<int>()%neighborWidth) - neighborWidth/2;
						rshift = abs(randu<int>()%neighborHeight) - neighborHeight/2;
						if(!(cshift == 0 && rshift==0))
							break;
					}
					if(c + cshift < 0 || c + cshift >=width)
						cshift *= -1;
					if(r + rshift < 0 || r + rshift >= height)
						rshift *=-1;
					(*iter).at<Vec3b>(r,c) = fristFrame.at<Vec3b>(r+rshift,c+cshift);
				}
			}
			/*初始化背景模型:结束*/
			else
			{
				/*******初始化背景模型:开始******/
				for(vector<Mat>::iterator iter = backgroundModel.begin(); iter != backgroundModel.end();iter++)
				{
					for(;;)
					{
						cshift = abs(randu<int>()%neighborWidth) - neighborWidth/2;
						rshift = abs(randu<int>()%neighborHeight) - neighborHeight/2;
						if( !(cshift == 0 && rshift==0) )
							break;
					}
					(*iter).at<Vec3b>(r,c) = fristFrame.at<Vec3b>(r+rshift,c+cshift);
				}
				/*****初始化背景模型:结束 ******/
			}	
		}
	}
}

float OriginalVibe::distanceL2(const Vec3b & src1,const  Vec3b& src2)
{
	return pow( pow(src1[0]-src2[0],2.0) +pow(src1[1]-src2[1],2.0) + pow(src1[2] - src2[2],2.0),0.5);
}
int OriginalVibe::distanceL1(const Vec3b & src1,const  Vec3b& src2)
{
	return abs(src1[0]-src2[0])+abs(src1[1] - src2[1])+abs(src1[2]-src2[2]) ;
}

void OriginalVibe::originalVibe_ClassifyAndUpdate_BGR(const Mat &frame,OutputArray &_segmentation)
{//*编号1
	int height = frame.rows;
	int width = frame.cols;
	int cshift;
	int rshift;
	_segmentation.create(frame.size(),CV_8UC1);
	Mat segmentation = _segmentation.getMat();

	RNG rng;

	for(int r =0 ;r < height; r++)
	{//编号1-1
		for(int c = 0;c < width ;c++)
		{//编号1-1-1
			int count = 0;
			Vec3b pixel = frame.at<Vec3b>(r,c);
			for( vector<Mat>::iterator iter = backgroundModel.begin() ;iter != backgroundModel.end(); iter++)
			{//编号1-1-1-1
				//
				//
				if( distanceL1(pixel,(*iter).at<Vec3b>(r,c)) < 4.5*this->distanceThreshold )
				{
					count++;
					if(count >= this->minMatch)
					{
						//第一步:更新模型update
						/**********开始更新模型*************/
						int random = rng.uniform(0,this->updateFactor);
						if(random == 0)
						{
							int updateIndex = rng.uniform(0,this->numberSamples);
							backgroundModel[updateIndex].at<Vec3b>(r,c) = pixel;
						}

						random = rng.uniform(0,this->updateFactor);
						if(random == 0)
						{
							/****************************************/
							if( c < neighborWidth/2 || c > width - neighborWidth/2-1 || r < neighborHeight/2 || r > height - neighborHeight/2-1 )
							{
								for(;;)
								{
									cshift = abs(randu<int>()%neighborWidth) - neighborWidth/2;
									rshift = abs(randu<int>()%neighborHeight) - neighborHeight/2;
									if(!(cshift == 0 && rshift==0))
										break;
								}
								if(c + cshift < 0 || c + cshift >=width)
									cshift*=-1;
								if(r + rshift < 0 || r + rshift >= height)
									rshift*=-1;
								int updateIndex = rng.uniform(0,this->numberSamples);
								backgroundModel[updateIndex].at<Vec3b>(r+rshift,c+cshift) = pixel;
							}
							else
							{
								for(;;)
								{
									cshift = abs(rand()%neighborWidth) - neighborWidth/2;
									rshift = abs(rand()%neighborHeight) - neighborHeight/2;
									if(!(cshift == 0 && rshift==0))
										break;
								}
								int updateIndex = rng.uniform(0,this->numberSamples);
								backgroundModel[updateIndex].at<Vec3b>(r+rshift,c+cshift) = pixel;
							}
							/****************************************/
						}
						/*
						*********结束更新模型************
						*/
						//第二步:分类classify
						segmentation.at<uchar>(r,c) = this->BACK_GROUND;
						break;
					}
				}
			}//编号1-1-1-1
			if(count < this->minMatch)//classify
				segmentation.at<uchar>(r,c) = this->FORE_GROUND;
		}//编号1-1-1
	}//编号1-1

}//*编号1



vibe前景检测算法的结果:(可以和帧差法做一比较)

技术分享

技术分享

【结论】

可以看到vibe算法,并没有像帧差法那样,产生大的空洞,但是会有鬼影出现


《Background Subtraction: Experiments and Improvements for ViBe》对上述原始的vibe算法,做了很多改进:

1、把固定的距离阈值,变为自适应的阈值

2、距离的计算方法改为codebook算法中距离计算公式

3、针对闪光点的判断

等等。下面还得接着研究对vide算法的改进

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值