Vibe算法简介、优缺点、代码

ViBe检测方法

    • 算法简介
      • 1.背景模型的初始化
      • 2.前景检测过程
      • 3.背景模型的更新方法
    • Vibe算法优缺点
    • Vibe代码

算法简介

  该算法采用邻域像素来创建背景模型,通过比对背景模型和当前输入像素值来检测前景。总的来说就是先背景建模,后前景检测的背景差方法。
  具体的思想就是为每个像素点存储了一个样本集,样本集中采样值就是该像素点过去的像素值和其邻居点的像素值,然后将每一个新的像素值和样本集进行比较来判断是否属于背景点。
  vibe算法可以分为以下三个步骤。

1.背景模型的初始化

  初始化单帧图像中每个像素点的背景模型。假设每一个像素和其邻域像素的像素值在空域上有相似的分布。基于这种假设,每一个像素模型都可以用其邻域中的像素来表示。为了保证背景模型符合统计学规律,邻域的范围要足够大。
  对于一个像素点,随机的选择它的邻居点的像素值作为它的模型样本值。M0(x)=v0(y|y∈NG(x)),t=0初始时刻,NG(x)即为邻居点 。

2.前景检测过程

  对后续的图像序列进行前景目标分割操作。
背景模型为每个背景点存储一个样本集,然后每个新的像素值和样本集比较判断是否属于背景。像素点(x,y)的背景模型为在这里插入图片描述,像素值为在这里插入图片描述。按照下面判断该像素值是否为前景。
在这里插入图片描述

这里上标r是随机选的;T是预先设置好的阈值。当  在这里插入图片描述
满足符合背景#N次时,我们认为像素点    在这里插入图片描述为背景,否则为前景。

3.背景模型的更新方法

  ViBe算法的更新在时间和空间上都具有随机性。时间上的随机性。在N个背景模型中随机抽取一个,设为图像Pg。当我们得到新的一帧图像Pt时,如果图像Pt中的x位置对应的像素Pt(x)被判断为背景,则Pg需要被更新。这个抽取的过程体现了时间上的随机性。空间上的随机性。在Pg(x)的八邻域中随机抽取一个像素Pg(r),用Pg(r)的来替换掉Pg(x),这体现了模型更新空间上的随机性。

Vibe算法优缺点

优点:

  • 内存占用少,一个像素需要作一次比较,占用一个字节的内存;
  • 无参数法;
  • 性能优于混合高斯,参数化方法,SACON等;
  • 像素级算法,视频处理中的预处理关键步骤;
  • 背景模型及时初始化;
  • 具有较好的抗噪能力;
  • 在背景建模中,不仅减少了背景模型建立的过程,还可以处理背景突然变化的情况,当检测到背景突然变化明显时,只需要舍弃原始的模型,重新利用变化后的首帧图像建立背景模型;
  • 在背景模型更新中,采用八邻域更新的方法,可以去除由于获取的视频细微抖动(摄像机抖动、目标微动)而产生的重影和误差,让检测目标更加准确。

缺点:

  • 在背景建模中,由于可能采用了运动物体的像素初始化样本集,容易引入Ghost区域(拖影、鬼影区域)(Ghost区域也就是指当一个原本静止的物体开始运动,背景差检测算法可能会将原来该物体所覆盖的区域错误的检测为运动的,这块区域就成为Ghost,当然原来运动的物体变为静止的也会引入Ghost区域,Ghost区域在检测中必须被尽快的消除。)Ghost区域如下图所示;
    Vibe算法简介、优缺点、代码_第1张图片

  • 静止目标问题,长时间驻留未运动,该人物运动目标逐渐被背景吸收。
    Vibe算法简介、优缺点、代码_第2张图片

  • 阴影前景问题,阴影的存在导致检测出来的运动目标形状不准确,影响后续目标分类、跟踪、识别和分析等其他智能视频处理模块。产生阴影前景问题的根源是:光线被运动目标前景遮挡,投射阴影区的颜色比背景的颜色暗,即阴影和背景颜色值的距离相差较大,背景差分后被误检为运动目标前景。
    Vibe算法简介、优缺点、代码_第3张图片

  • 运动目标不完整问题,运动目标通常可分为非刚性物体和刚性物体,人属于非刚性物体,车属于刚性物体,这两种常见检测对象的检测结果都出现了不完整现象。
    Vibe算法简介、优缺点、代码_第4张图片

Vibe代码

Vibe.h头文件

#include
#include
#include

using namespace cv;
using namespace std;

//每个像素点的样本个数默认值
#define DEFAULT_NUM_SAMPLES 20

//#min指数默认值
#define DEFAULT_MIN_MATCHES 2

//Sqthere半径默认值
#define DEFAULT_RADIUS 20

//子采样概率默认值
#define DEFAULT_RANDOM_SAMPLE 16

class ViBe
{
public:
	ViBe(int num_sam = DEFAULT_NUM_SAMPLES,
		int min_match = DEFAULT_MIN_MATCHES,
		int r = DEFAULT_RADIUS,
		int rand_sam = DEFAULT_RANDOM_SAMPLE);
	~ViBe(void);

	//背景模型初始化
	void init(Mat img);

	//处理第一帧图像
	void ProcessFirstFrame(Mat img);

	//运行ViBe算法,提取前景区域并更新背景模型样本库
	void Run(Mat img);

	//获取前景模型二值图像
	Mat getFGModel();

	//删除样本库
	void deleteSamples();

	//x的邻居点
	int c_xoff[9];

	//y的邻居点
	int c_yoff[9];

private:
	//样本库
	unsigned char*** samples;

	//前景模型二值图像
	Mat FGModel;

	//每个像素点的样本个数
	int num_samples;

	//#min指数
	int num_min_matches;

	//Sqthere半径
	int radius;

	//子采样概率
	int random_sample;

};
#pragma once

Vibe.cpp

#include"Vibe.h"
/*
构造函数ViBe
参数:
int num_sam:每个像素点的样本个数
int min_match:#min 指数
int r: Sqthere 半径
int rand_sam:子采样概率
*/

ViBe::ViBe(int num_sam, int min_match, int r, int rand_sam)
{
	num_samples = num_sam;
	num_min_matches = min_match;
	radius = r;
	random_sample = rand_sam;
	int c_off[9] = { -1,0,1,-1,1,-1,0,1,0 };
	for (int i = 0; i < 9; i++) {
		c_xoff[i] = c_yoff[i] = c_off[i];
	}
}

/*析构函数:~ViBe
  说明:释放样本库内存
*/

ViBe::~ViBe(void)
{
	deleteSamples();
}

/*
  函数名init
  说明:背景模型初始化
	   为样本库分配空间

  参数:Mat img:源图像
  返回值:void
*/

void ViBe::init(Mat img)
{
	//动态分配三维数组,samples[][][num_samples]存储前景被连续检测的次数
	//
	samples = new unsigned char**[img.rows];
	for (int i = 0; i < img.rows; i++)
	{
		samples[i] = new uchar *[img.cols];
		for (int j = 0; j < img.cols; j++)
		{
			//数组中,在num_samples之外多增加的一个值,用于统计该像素点连续成为前景的次数
			samples[i][j] = new uchar[num_samples + 1];
			for (int k = 0; k < num_samples + 1; k++)
			{
				//创建样本库是,所有样本全部初始化为0
				samples[i][j][k] = 0;

			}
		}

	}

	FGModel = Mat::zeros(img.size(), CV_8UC1);
}
/*
	 函数名 ProcessFirstFrame
	 说明:处理第一帧图像
		 读取视频序列第一帧,并随机选取像素点邻域内像素填充样本库,初始化背景模型
	参数:
	Mat img:源图像
	返回值:void

	 */

void ViBe::ProcessFirstFrame(Mat img)
{
	RNG rng;
	int row, col;

	for (int i = 0; i < img.rows; i++)
	{
		for (int j = 0; j < img.cols; j++)
		{
			for (int k = 0; k < num_samples; k++)
			{
				//随机选择num_samples个邻域像素点,构建背景模型
				int random;
				random = rng.uniform(0, 9); row = i + c_yoff[random];
				random = rng.uniform(0, 9); col = j + c_xoff[random];

				//防止选取的像素点越界
				if (row < 0)
					row = 0;
				if (row >= img.rows)
					row = img.rows - 1;
				if (col < 0)
					col = 0;
				if (col >= img.cols)
					col = img.cols - 1;

				//为样本库赋值随机值
				samples[i][j][k] = img.at<uchar>(row, col);
			}
		}
	}
}

/*
函数名:Run
说明:运行ViBe算法,提取前景区域并更新背景模型样本库
参数:
Mat img 源图像
返回值:void

*/

void ViBe::Run(Mat img)
{
	RNG rng;
	int k = 0, dist = 0, matches = 0;
	for (int i = 0; i < img.rows; i++)
	{
		for (int j = 0; j < img.cols; j++)
		{
			//前景提取
			//说明:计算当前像素值与样本库的匹配情况
			//参数:
			//int matches:当前像素值与   样本库中值之差小于阈值范围RADIUS的个数
			//int count: 遍历样本库的缓存变量
			for (k = 0, matches = 0; matches < num_min_matches && k < num_samples; k++)
			{
				dist = abs(samples[i][j][k] - img.at<uchar>(i, j));
				if (dist < radius)
					matches++;

			}
			//说明:当前像素值与样本库中值匹配次数较高,则认为是背景像素点;
			//此时更新前景统计次数、更新前景模型、更新该像素模型样本值、更新该像素点邻域像素点
			if (matches >= num_min_matches)
			{
				//已经认为是背景像素,故该像素前景统计次数置0
				samples[i][j][num_samples] = 0;

				//该像素点的前景模型像素值置0
				FGModel.at<uchar>(i, j) = 0;
			}
			//说明:当前像素值与样本库中值匹配次数较低,则认为是前景像素点
			//此时需要更新前景统计次数,判断更新前景模型
			else {
				//已经认为是前景像素,故该像素的前景统计次数+1
				samples[i][j][num_samples]++;

				//该像素点的前景模型像素值置255
				FGModel.at<uchar>(i, j) = 255;

				//如果某个像素点连续50次被检测为前景,则认为一块静止区域被误判为运动,将其更新为背景点
				if (samples[i][j][num_samples] > 50)
				{
					int random = rng.uniform(0, num_samples);
					samples[i][j][random] = img.at<uchar>(i, j);
				}
			}
			//更新模型样本库
			if (matches >= num_min_matches)
			{
				//已经认为该像素是背景像素,那么它有1/φ的概率去更新自己的模型样本值
				int random = rng.uniform(0, random_sample);
				if (random == 0)
				{
					random = rng.uniform(0, num_samples);
					samples[i][j][random] = img.at<uchar>(i, j);
				}
				//同时也有1/φ的概率去更新它的邻居点的模型样本值
				random = rng.uniform(0, random_sample);
				if (random == 0)
				{
					int row, col;
					random = rng.uniform(0, 9); row = i + c_yoff[random];
					random = rng.uniform(0, 9); col = j + c_xoff[random];

					//防止选取的像素点越界
					if (row < 0)
						row = 0;
					if (row >= img.rows)
						row = img.rows - 1;
					if (col < 0)
						col = 0;
					if (col >= img.cols)
						col = img.cols - 1;

					//为样本库赋值随机值

					random = rng.uniform(0, num_samples);
					samples[row][col][random] = img.at<uchar>(i, j);


				}
			}
		}

	}
}

/*
函数名 :getFGModel
说明:获取前景模型二值图像
返回值:Mat

*/

Mat ViBe::getFGModel()
{
	return FGModel;
}

/*
函数名:deletesamples
说明:删除样本库
返回值:void

*/

void ViBe::deleteSamples()
{
	delete samples;
}

main.cpp


#include
#include
#include
#include 
#include 
#include "Vibe.h"

int main(int argc, char* argv[])
{
	Mat frame, gray, FGModel;
	VideoCapture capture;
	capture = VideoCapture("E:/material/yaoyongde/test.avi");
	if (!capture.isOpened())
	{
		capture = VideoCapture("E:/material/yaoyongde/test.avi");
		if (!capture.isOpened())
		{
			capture = VideoCapture("E:/material/yaoyongde/test.avi");
			if (!capture.isOpened())
			{
				cout << "ERROR:Didn't find thid video!" << endl;
				return 0;
			}
		}
	}

	capture.set(CAP_PROP_FRAME_WIDTH, 160);
	capture.set(CAP_PROP_FRAME_HEIGHT, 120);

	if (!capture.isOpened())
	{
		cout << "No camera or video input!" << endl;
		return -1;
	}

	//程序运行时间统计变量
	double time;
	double start;

	ViBe vibe;
	bool count = true;
	while (1)
	{
		capture >> frame;
		if (frame.empty())
			continue;

		cvtColor(frame, gray, CV_RGB2GRAY);
		if (count)
		{
			vibe.init(gray);
			vibe.ProcessFirstFrame(gray);
			cout << "Training ViBe Success." << endl;
			count = false;
		}
		else
		{
			start = static_cast<double>(getTickCount());
			vibe.Run(gray);
			time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
			cout << "Time of Update ViBe BAckground:" << time << "ms" << endl << endl;

			FGModel = vibe.getFGModel();
			imshow("FGModel", FGModel);
		}
		imshow("input", frame);

		if (waitKey(25) == 27)
			break;
	}
	return 0;
}

你可能感兴趣的:(计算机视觉,算法,机器学习,目标检测)