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)
{//*编号1int 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-1for(int c = 0;c < width ;c++){//编号1-1-1int 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;}/****************************************/}/**********结束更新模型*************///第二步:分类classifysegmentation.at<uchar>(r,c) = this->BACK_GROUND;break;}}}//编号1-1-1-1if(count < this->minMatch)//classifysegmentation.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算法的改进

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

  1. OpenCV视频中的人脸标志检测

    OpenCV视频中的人脸标志检测 视频中的人脸标志检测 简介 命令参数说明 源代码 视频中的人脸标志检测 简介 此应用程序使您可以检测视频中检测到的面部的地标.此应用程序首先检测当前视频帧中的面部,然 ...

  2. 运动目标检测ViBe算法的numpy实现

    ViBe算法是一种优秀的运动目标检测,其背景建模是基于像素的邻域来建立,算法的具体原理我就不介绍了,网上可以搜到一大堆.网上提供的实现方法多是遍历图像中每个像素建立背景模型,有用c/c++实现的,也有 ...

  3. 运动目标检测ViBe算法

    一.运动目标检测简介   视频中的运动目标检测这一块现在的方法实在是太多了.运动目标检测的算法依照目标与摄像机之间的关系可以分为静态背景下运动检测和动态背景下运动检测.先简单从视频中的背景类型来讨论. ...

  4. 运动目标检测ViBe算法的armadillo实现

    接上篇,最近在学习c++矩阵库,顺便把vibe算法用c++矩阵库armadillo做了一遍.虽然效果不理想,但是借这个机会算是把armadillo.Eigen.numcpp等几个注明的矩阵库都大致学习 ...

  5. Opencv实战之混合高斯前景背景分割算法

    -不管三七二十一,先上代码 -读取需要掌握的函数 # 相关函数 # cv.VideoCapture() 初始化摄像头,0开启第一个摄像头,1开启第2个摄像头,返回摄像头对象,一般会自动打开摄像头 # ...

  6. 运动检测(前景检测)之(一)ViBe

    运动检测(前景检测)之(一)ViBe zouxy09@qq.com http://blog.csdn.net/zouxy09 因为监控发展的需求,目前前景检测的研究还是很多的,也出现了很多新的方法和思 ...

  7. ViBe(Visual Background extractor)背景建模或前景检测

    ViBe算法:ViBe - a powerful technique for background detection and subtraction in video sequences 算法官网: ...

  8. ViBe算法原理和代码解析

    ViBe - a powerful technique for background detection and subtraction in video sequences 算法官网:http:// ...

  9. 背景提取算法——帧间差分法、背景差分法、ViBe算法、ViBe+算法

    背景提取是在视频图像序列中提取出背景,背景就是场景中静止不动的景物.因为摄像机不动,因此图像中的每个像素点都有一个对应的背景值,在一段时间内,这个背景值是比较固定的.背景提取的目标就是根据视频图像序列 ...

最新文章

  1. 对象Equals相等性比较的通用实现
  2. [转]C语言茶余饭后之if...else PK switch...case
  3. 构建SpringBoot第一个Demo
  4. java 取字符串中的数字_java截取字符串中的数字
  5. python发动机悬置解耦计算-按重心处整车坐标系解耦
  6. 【Elasticsearch】es Elasticsearch HQ 介绍
  7. Spring源码解析 - BeanFactory接口体系解读
  8. laravel 图片
  9. Matlab数值计算差商与插值
  10. 零基础如何入门数学建模?
  11. A 股历年三大财务报表
  12. 计算机科学有科研,计算机科学领域科研合著网演化分析
  13. 苹果Mac延时摄影视频制作工具:Persecond
  14. gitlab-ce更新后reconfigure报错
  15. 读《七人分粥》悟管理之道
  16. 已注销主体的公众号迁移办理流程及方法
  17. sqlitebrowser
  18. 公众号文章里使用svg进行交互
  19. iphone6 适配和分辨率
  20. 从苏宁电器到卡巴斯基(后传)第05篇:聊聊我对WannaCry产生的感慨

热门文章

  1. OVS vswitchd启动(三十九)
  2. linux内核网络协议栈--数据包的接收过程(二十)
  3. 香蕉派安装64位linux,在香蕉派里安装配置archlinux到树莓派
  4. fabric.js 不同类型 不同控件_耐温灌封胶都哪几个类型?不同类型的灌封胶有哪些不同之处?...
  5. 【HDU4507】恨7不成妻
  6. python笔记22-literal_eval函数处理返回json中的单双引号
  7. 基于docker部署的微服务架构(四): 配置中心
  8. Android中全局Application的onCreate多次调用问题
  9. SSDT – Error SQL70001 This statement is not recognized in this context-摘自网络
  10. 使用图形工具管理Server Core上的账号和组