目标检测中背景建模方法
- Single Gaussian (单高斯模型)
Real-time tracking of the human body - 混合高斯模型(Mixture of Gaussian Model)
An improved adaptive background mixture model for real-time tracking with shadow detection - 滑动高斯平均(Running Gaussian average)—Single Gaussian
Real-time tracking of the human body
对于单高斯和混合高斯估计大家都熟悉,这里不再累述(混合高斯在现有的背景建模算法中应该算是比较好的,很多新的算法或改进的算法都是基于它的一些原理的不同变体,但混合高斯算法的缺点是计算量相对比较大,速度偏慢,对光照敏感); - 码本 (CodeBook)
Real-time foreground–background segmentation using codebook model
Real-time foreground-background segmentation using a modified codebook model
对与Codebook算法,曾经做过实验,效果还可以,后来也有多种变体,没有进一步的进行研究,但算法对光照也敏感); - 自组织背景检测( SOBS-Self-organization background subtraction)
A self-Organizing approach to background subtraction for+visual surveillance
对于自组织背景建模算法即SOBS算法,该算法对光照有一定的鲁棒性,但MAP的模型比输入图片大,计算量比较大,但是可以通过并行处理来解决算法的速度问题,可以进行尝试; - 样本一致性背景建模算法 (SACON)
A consensus-based method for tracking
A consensus-based method for tracking-Modelling background scenario and foreground appearance
SACON-Background subtraction based on a robust consensus method
SACON算法是基于统计的知识,代码实现过,并做过实验,效果还可以,但没有进一步的分析; - VIBE算法
vibe
ViBe-A Universal Background Subtraction
VIBE算法是B哥的一个大作,网上有现成的算法可用,但已申请了专利,用于做研究还是可以的,该算法速度非常快,计算量比较小,而且对噪声有一定的鲁棒性,检测效果不错; - 基于颜色信息的背景建模方法(Color)
A statistical approach for real-time robust background subtraction and shadow detection
基于颜色信息的背景建模方法,简称Color算法,该算法将像素点的差异分解成Chromaticity差异和Brightness差异,对光照具有很强的鲁棒性,并有比较好的效果,计算速度也比较快,基本可以满足实时性的要求,做了许多视频序列的检测,效果比较理想; - 统计平均法
- 中值滤波法( Temporal Median filter)
Automatic congestion detection system for underground platform
Detecting moving objects,ghost,and shadows in video streams
统计平均法和中值滤波法,对于这两个算法,只对统计平均法做了实现,并进行了测试,算法的应用具有很大的局限性,只能算是理论上的一个补充; - W4方法
W4.pdf
W4算法应该是最早被用于实际应用的一个算法,这个大家可以去查看相关的资料,这里不再细说; - 本征背景法
A Bayesian computer vision system for modeling human interactions
本征背景法没实现过,看很多文献有讲解,然后该算法又是基于贝叶斯框架,本人一直对贝叶斯框架不感冒,理论上很Perfect,实际应用就是Shit; - 核密度估计方法
Non-parametric model for background subtraction
最后就是核密度估计算法,该算法应该是一个比较鲁棒的算法,可以解决很多算法参数设置方面的问题,无需设置参数应该是算法的一大优势。
个人观点:SOBS、Color、VIBE、SACON、PDF等可以进行深入的了解,特别是近年来出现的Block-based或Region-Based、Features-Based、基于层次分类或层次训练器的算法可以进行深入的研究。
推荐一篇综述文章:Evaluation of Background Subtraction Techniques for Video Surveillance
VIBE算法如下
main.cpp
#include "vibe.h"
#include <cstdio>
using namespace cv;
using namespace std;
int main(int argc, char* argv[])
{
Mat frame, gray, mask;
VideoCapture capture;
capture.open("2.avi");
capture.set(CV_CAP_PROP_FRAME_WIDTH,640);
capture.set(CV_CAP_PROP_FRAME_HEIGHT,480);
if (!capture.isOpened())
{
cout<<"No camera or video input!\n"<<endl;
return -1;
}
ViBe_BGS Vibe_Bgs;
bool count =true;
while (1)
{
capture >> frame;
if (frame.empty())
continue;
cvtColor(frame, gray, CV_RGB2GRAY);
if (count)
{
Vibe_Bgs.init(gray);
Vibe_Bgs.processFirstFrame(gray);
cout<<" Training ViBe complete!"<<endl;
count=false;
}
else
{
Vibe_Bgs.testAndUpdate(gray);
mask = Vibe_Bgs.getMask();
morphologyEx(mask, mask, MORPH_OPEN, Mat());
imshow("mask", mask);
}
imshow("input", frame);
if ( cvWaitKey(30) == 27 )
break;
}
return 0;
}
vibe.h
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace cv;
using namespace std;
#define NUM_SAMPLES 20 //每个像素点的样本个数
#define MIN_MATCHES 2 //#min指数
#define RADIUS 20 //Sqthere半径
#define SUBSAMPLE_FACTOR 16 //子采样概率
class ViBe_BGS
{
public:
ViBe_BGS(void);
~ViBe_BGS(void);
void init(const Mat _image); //初始化
void processFirstFrame(const Mat _image);
void testAndUpdate(const Mat _image); //更新
Mat getMask(void){return m_mask;};
void deleteSamples(){delete samples;};
private:
unsigned char ***samples;
// float samples[1024][1024][NUM_SAMPLES+1];//保存每个像素点的样本值
/*
Mat m_samples[NUM_SAMPLES];
Mat m_foregroundMatchCount;*/
Mat m_mask;
};
vibe.cpp
#include "vibe.h"
using namespace std;
using namespace cv;
int c_xoff[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0}; //x的邻居点
int c_yoff[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0}; //y的邻居点
ViBe_BGS::ViBe_BGS(void)
{
}
ViBe_BGS::~ViBe_BGS(void)
{
}
/**************** Assign space and init ***************************/
void ViBe_BGS::init(const Mat _image)
{
//动态分配三维数组,samples[][][NUM_SAMPLES]存储前景被连续检测的次数
samples=new unsigned char **[_image.rows];
for (int i=0;i<_image.rows;i++)
{
samples[i]=new unsigned char *[1024];
for (int j=0;j<_image.cols;j++)
{
samples[i][j]=new unsigned char [NUM_SAMPLES+1];
for (int k=0;k<NUM_SAMPLES+1;k++)
{
samples[i][j][k]=0;
}
}
}
m_mask = Mat::zeros(_image.size(),CV_8UC1);
}
/**************** Init model from first frame ********************/
void ViBe_BGS::processFirstFrame(const Mat _image)
{
RNG rng;
int row, col;
for(int i = 0; i < _image.rows; i++)
{
for(int j = 0; j < _image.cols; j++)
{
for(int k = 0 ; k < NUM_SAMPLES; k++)
{
// Random pick up NUM_SAMPLES pixel in neighbourhood to construct the model
int random = rng.uniform(0, 9);
row = i + c_yoff[random];
if (row < 0)
row = 0;
if (row >= _image.rows)
row = _image.rows - 1;
col = j + c_xoff[random];
if (col < 0)
col = 0;
if (col >= _image.cols)
col = _image.cols - 1;
samples[i][j][k]=_image.at<uchar>(row, col);
}
}
}
}
/**************** Test a new frame and update model ********************/
void ViBe_BGS::testAndUpdate(const Mat _image)
{
RNG rng;
for(int i = 0; i < _image.rows; i++)
{
for(int j = 0; j < _image.cols; j++)
{
int matches(0), count(0);
int dist;
while(matches < MIN_MATCHES && count < NUM_SAMPLES)
{
dist = abs(samples[i][j][count] - _image.at<uchar>(i, j));
if (dist < RADIUS)
matches++;
count++;
}
if (matches >= MIN_MATCHES)
{
// It is a background pixel
samples[i][j][NUM_SAMPLES]=0;
// Set background pixel to 0
m_mask.at<uchar>(i, j) = 0;
// 如果一个像素是背景点,那么它有 1 / defaultSubsamplingFactor 的概率去更新自己的模型样本值
int random = rng.uniform(0, SUBSAMPLE_FACTOR);
if (random == 0)
{
random = rng.uniform(0, NUM_SAMPLES);
samples[i][j][random]=_image.at<uchar>(i, j);
}
// 同时也有 1 / defaultSubsamplingFactor 的概率去更新它的邻居点的模型样本值
random = rng.uniform(0, SUBSAMPLE_FACTOR);
if (random == 0)
{
int row, col;
random = rng.uniform(0, 9);
row = i + c_yoff[random];
if (row < 0)
row = 0;
if (row >= _image.rows)
row = _image.rows - 1;
random = rng.uniform(0, 9);
col = j + c_xoff[random];
if (col < 0)
col = 0;
if (col >= _image.cols)
col = _image.cols - 1;
random = rng.uniform(0, NUM_SAMPLES);
samples[i][j][random]=_image.at<uchar>(i, j);
}
}
else
{
// It is a foreground pixel
samples[i][j][NUM_SAMPLES]++;
// Set background pixel to 255
m_mask.at<uchar>(i, j) = 255;
//如果某个像素点连续N次被检测为前景,则认为一块静止区域被误判为运动,将其更新为背景点
if(samples[i][j][NUM_SAMPLES]>50)
{
int random = rng.uniform(0, NUM_SAMPLES);
if (random == 0)
{
random = rng.uniform(0, NUM_SAMPLES);
samples[i][j][random]=_image.at<uchar>(i, j);
}
}
}
}
}
}