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) {//*編號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算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 背景建模--Vibe 算法优缺点分析
- 下一篇: 百度机器人对战人类最强大脑,赢在了小数点