博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
paper 83:前景检测算法_1(codebook和平均背景法)
阅读量:5869 次
发布时间:2019-06-19

本文共 24934 字,大约阅读时间需要 83 分钟。

前景分割中一个非常重要的研究方向就是背景减图法,因为背景减图的方法简单,原理容易被想到,且在智能视频监控领域中,摄像机很多情况下是固定的,且背景也是基本不变或者是缓慢变换的,在这种场合背景减图法的应用驱使了其不少科研人员去研究它。

      但是背景减图获得前景图像的方法缺点也很多:比如说光照因素,遮挡因素,动态周期背景,且背景非周期背景,且一般情况下我们考虑的是每个像素点之间独立,这对实际应用留下了很大的隐患。

      这一小讲主要是讲简单背景减图法和codebook法。

 

一、简单背景减图法的工作原理。

      在视频对背景进行建模的过程中,每2帧图像之间对应像素点灰度值算出一个误差值,在背景建模时间内算出该像素点的平均值,误差平均值,然后在平均差值的基础上+-误差平均值的常数(这个系数需要手动调整)倍作为背景图像的阈值范围,所以当进行前景检测时,当相应点位置来了一个像素时,如果来的这个像素的每个通道的灰度值都在这个阈值范围内,则认为是背景用0表示,否则认为是前景用255表示。

      下面的一个工程是learning opencv一书中作者提供的源代码,关于简单背景减图的代码和注释如下:

     avg_background.h文件:

///// Accumulate average and ~std (really absolute difference) image and use this to detect background and foreground//// Typical way of using this is to://     AllocateImages();loop for N images to accumulate background differences//    accumulateBackground();When done, turn this into our avg and std model with high and low bounds//    createModelsfromStats();Then use the function to return background in a mask (255 == foreground, 0 == background)//    backgroundDiff(IplImage *I,IplImage *Imask, int num);Then tune the high and low difference from average image background acceptance thresholds//    float scalehigh,scalelow; //Set these, defaults are 7 and 6. Note: scalelow is how many average differences below average//    scaleHigh(scalehigh);//    scaleLow(scalelow);That is, change the scale high and low bounds for what should be background to make it work.Then continue detecting foreground in the mask image//    backgroundDiff(IplImage *I,IplImage *Imask, int num);////NOTES: num is camera number which varies from 0 ... NUM_CAMERAS - 1.  Typically you only have one camera, but this routine allows//          you to index many.//#ifndef AVGSEG_#define AVGSEG_#include "cv.h"                // define all of the opencv classes etc.#include "highgui.h"#include "cxcore.h"//IMPORTANT DEFINES:#define NUM_CAMERAS   1              //This function can handle an array of cameras#define HIGH_SCALE_NUM 7.0            //How many average differences from average image on the high side == background#define LOW_SCALE_NUM 6.0        //How many average differences from average image on the low side == backgroundvoid AllocateImages(IplImage *I);void DeallocateImages();void accumulateBackground(IplImage *I, int number=0);void scaleHigh(float scale = HIGH_SCALE_NUM, int num = 0);void scaleLow(float scale = LOW_SCALE_NUM, int num = 0);void createModelsfromStats();void backgroundDiff(IplImage *I,IplImage *Imask, int num = 0);#endif

   avg_background.cpp文件:

// avg_background.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "avg_background.h"//GLOBALSIplImage *IavgF[NUM_CAMERAS],*IdiffF[NUM_CAMERAS], *IprevF[NUM_CAMERAS], *IhiF[NUM_CAMERAS], *IlowF[NUM_CAMERAS];IplImage *Iscratch,*Iscratch2,*Igray1,*Igray2,*Igray3,*Imaskt;IplImage *Ilow1[NUM_CAMERAS],*Ilow2[NUM_CAMERAS],*Ilow3[NUM_CAMERAS],*Ihi1[NUM_CAMERAS],*Ihi2[NUM_CAMERAS],*Ihi3[NUM_CAMERAS];float Icount[NUM_CAMERAS];void AllocateImages(IplImage *I)  //I is just a sample for allocation purposes{    for(int i = 0; i

  

二、codebook算法工作原理

     考虑到简单背景减图法无法对动态的背景建模,有学者就提出了codebook算法。

     该算法为图像中每一个像素点建立一个码本,每个码本可以包括多个码元,每个码元有它的学习时最大最小阈值,检测时的最大最小阈值等成员。在背景建模期间,每当来了一幅新图片,对每个像素点进行码本匹配,也就是说如果该像素值在码本中某个码元的学习阈值内,则认为它离过去该对应点出现过的历史情况偏离不大,通过一定的像素值比较,如果满足条件,此时还可以更新对应点的学习阈值和检测阈值。如果新来的像素值对码本中每个码元都不匹配,则有可能是由于背景是动态的,所以我们需要为其建立一个新的码元,并且设置相应的码元成员变量。因此,在背景学习的过程中,每个像素点可以对应多个码元,这样就可以学到复杂的动态背景。

     关于codebook算法的代码和注释如下:

     cv_yuv_codebook.h文件:

///// Accumulate average and ~std (really absolute difference) image and use this to detect background and foreground//// Typical way of using this is to://     AllocateImages();loop for N images to accumulate background differences//    accumulateBackground();When done, turn this into our avg and std model with high and low bounds//    createModelsfromStats();Then use the function to return background in a mask (255 == foreground, 0 == background)//    backgroundDiff(IplImage *I,IplImage *Imask, int num);Then tune the high and low difference from average image background acceptance thresholds//    float scalehigh,scalelow; //Set these, defaults are 7 and 6. Note: scalelow is how many average differences below average//    scaleHigh(scalehigh);//    scaleLow(scalelow);That is, change the scale high and low bounds for what should be background to make it work.Then continue detecting foreground in the mask image//    backgroundDiff(IplImage *I,IplImage *Imask, int num);////NOTES: num is camera number which varies from 0 ... NUM_CAMERAS - 1.  Typically you only have one camera, but this routine allows//          you to index many.//#ifndef AVGSEG_#define AVGSEG_#include "cv.h"                // define all of the opencv classes etc.#include "highgui.h"#include "cxcore.h"//IMPORTANT DEFINES:#define NUM_CAMERAS   1              //This function can handle an array of cameras#define HIGH_SCALE_NUM 7.0            //How many average differences from average image on the high side == background#define LOW_SCALE_NUM 6.0        //How many average differences from average image on the low side == backgroundvoid AllocateImages(IplImage *I);void DeallocateImages();void accumulateBackground(IplImage *I, int number=0);void scaleHigh(float scale = HIGH_SCALE_NUM, int num = 0);void scaleLow(float scale = LOW_SCALE_NUM, int num = 0);void createModelsfromStats();void backgroundDiff(IplImage *I,IplImage *Imask, int num = 0);#endif

   cv_yuv_codebook.cpp文件:

YUV CODEBOOK// Gary Bradski, July 14, 2005#include "stdafx.h"#include "cv_yuv_codebook.h"//GLOBALS FOR ALL CAMERA MODELS//For connected components:int CVCONTOUR_APPROX_LEVEL = 2;   // Approx.threshold - the bigger it is, the simpler is the boundaryint CVCLOSE_ITR = 1;                // How many iterations of erosion and/or dialation there should be//#define CVPERIMSCALE 4            // image (width+height)/PERIMSCALE.  If contour lenght < this, delete that contour//For learning background//Just some convienience macros#define CV_CVX_WHITE    CV_RGB(0xff,0xff,0xff)#define CV_CVX_BLACK    CV_RGB(0x00,0x00,0x00)///// int updateCodeBook(uchar *p, codeBook &c, unsigned cbBounds)// Updates the codebook entry with a new data point//// p            Pointer to a YUV pixel// c            Codebook for this pixel// cbBounds        Learning bounds for codebook (Rule of thumb: 10)// numChannels    Number of color channels we're learning//// NOTES://        cvBounds must be of size cvBounds[numChannels]//// RETURN//    codebook indexint cvupdateCodeBook(uchar *p, codeBook &c, unsigned *cbBounds, int numChannels){    if(c.numEntries == 0) c.t = 0;//说明每个像素如果遍历了的话至少对应一个码元    c.t += 1;        //Record learning event,遍历该像素点的次数加1//SET HIGH AND LOW BOUNDS    int n;    unsigned int high[3],low[3];    for(n=0; n
255) high[n] = 255; low[n] = *(p+n)-*(cbBounds+n); if(low[n] < 0) low[n] = 0; } int matchChannel; //SEE IF THIS FITS AN EXISTING CODEWORD int i; for(i=0; i
min[n]-c.cb[i]->learnLow[n] <= *(p+n)) && (*(p+n) <= c.cb[i]->max[n]+c.cb[i]->learnHigh[n]))//原因是因为在每次建立一个新码元的时候,learnHigh[n]和learnLow[n]的范围就在max[n]和min[n]上扩展了cbBounds[n],所以说//learnHigh[n]和learnLow[n]的变化范围实际上比max[n]和min[n]的大 if((c.cb[i]->learnLow[n] <= *(p+n)) && (*(p+n) <= c.cb[i]->learnHigh[n])) //Found an entry for this channel { matchChannel++; } } if(matchChannel == numChannels) //If an entry was found over all channels,找到了该元素此刻对应的码元 { c.cb[i]->t_last_update = c.t; //adjust this codeword for the first channel//更新每个码元的最大最小阈值,因为这2个阈值在后面的前景分离过程要用到 for(n=0; n
max[n] < *(p+n))//用该点的像素值更新该码元的最大值,所以max[n]保存的是实际上历史出现过的最大像素值 { c.cb[i]->max[n] = *(p+n);//因为这个for语句是在匹配成功了的条件阈值下的,所以一般来说改变后的max[n]和min[n]//也不会过学习的高低阈值,并且学习的高低阈值也一直在缓慢变化 } else if(c.cb[i]->min[n] > *(p+n))//用该点的像素值更新该码元的最小值,所以min[n]保存的是实际上历史出现过的最小像素值 { c.cb[i]->min[n] = *(p+n); } } break;//一旦找到了该像素的一个码元后就不用继续往后找了,加快算法速度。因为最多只有一个码元与之对应 } } //OVERHEAD TO TRACK POTENTIAL STALE ENTRIES for(int s=0; s
t_last_update;//negRun表示码元没有更新的时间间隔 if(c.cb[s]->stale < negRun) c.cb[s]->stale = negRun;//更新每个码元的statle } //ENTER A NEW CODE WORD IF NEEDED if(i == c.numEntries) //No existing code word found, make a new one,只有当该像素码本中的所有码元都不符合要求时才满足if条件 { code_element **foo = new code_element* [c.numEntries+1];//创建一个新的码元序列 for(int ii=0; ii
learnHigh[n] = high[n];//当建立一个新码元时,用当前值附近cbBounds范围作为码元box的学习阈值 c.cb[c.numEntries]->learnLow[n] = low[n]; c.cb[c.numEntries]->max[n] = *(p+n);//当建立一个新码元时,用当前值作为码元box的最大最小边界值 c.cb[c.numEntries]->min[n] = *(p+n); } c.cb[c.numEntries]->t_last_update = c.t; c.cb[c.numEntries]->stale = 0;//因为刚建立,所有为0 c.numEntries += 1;//码元的个数加1 } //SLOWLY ADJUST LEARNING BOUNDS for(n=0; n
learnHigh[n] < high[n]) c.cb[i]->learnHigh[n] += 1; if(c.cb[i]->learnLow[n] > low[n]) c.cb[i]->learnLow[n] -= 1; } return(i);//返回所找到码本中码元的索引}///// uchar cvbackgroundDiff(uchar *p, codeBook &c, int minMod, int maxMod)// Given a pixel and a code book, determine if the pixel is covered by the codebook//// p pixel pointer (YUV interleaved)// c codebook reference// numChannels Number of channels we are testing// maxMod Add this (possibly negative) number onto max level when code_element determining if new pixel is foreground// minMod Subract this (possible negative) number from min level code_element when determining if pixel is foreground//// NOTES:// minMod and maxMod must have length numChannels, e.g. 3 channels => minMod[3], maxMod[3].//// Return// 0 => background, 255 => foregrounduchar cvbackgroundDiff(uchar *p, codeBook &c, int numChannels, int *minMod, int *maxMod){ int matchChannel; //SEE IF THIS FITS AN EXISTING CODEWORD int i; for(i=0; i
min[n] - minMod[n] <= *(p+n)) && (*(p+n) <= c.cb[i]->max[n] + maxMod[n])) { matchChannel++; //Found an entry for this channel } else { break;//加快速度,当一个通道不满足时提前结束 } } if(matchChannel == numChannels) { break; //Found an entry that matched all channels,加快速度,当一个码元找到时,提前结束 } } if(i >= c.numEntries) return(255);//255代表前景,因为所有的码元都不满足条件 return(0);//0代表背景,因为至少有一个码元满足条件}//UTILITES////int clearStaleEntries(codeBook &c)// After you've learned for some period of time, periodically call this to clear out stale codebook entries////c Codebook to clean up//// Return// number of entries clearedint cvclearStaleEntries(codeBook &c)//对每一个码本进行检查{ int staleThresh = c.t>>1;//阈值设置为访问该码元的次数的一半,经验值 int *keep = new int [c.numEntries]; int keepCnt = 0; //SEE WHICH CODEBOOK ENTRIES ARE TOO STALE for(int i=0; i
stale > staleThresh)//当在背景建模期间有一半的时间内,codebook的码元条目没有被访问,则该条目将被删除 keep[i] = 0; //Mark for destruction else { keep[i] = 1; //Mark to keep,为1时,该码本的条目将被保留 keepCnt += 1;//keepCnt记录了要保持的codebook的数目 } } //KEEP ONLY THE GOOD c.t = 0; //Full reset on stale tracking code_element **foo = new code_element* [keepCnt];//重新建立一个码本的双指针 int k=0; for(int ii=0; ii
stale = 0; //We have to refresh these entries for next clearStale,不被访问的累加器stale重新赋值0 foo[k]->t_last_update = 0;// k++; } } //CLEAN UP delete [] keep; delete [] c.cb; c.cb = foo; int numCleared = c.numEntries - keepCnt;//numCleared中保存的是被删除码元的个数 c.numEntries = keepCnt;//最后新的码元数为保存下来码元的个数 return(numCleared);//返回被删除的码元个数}///int countSegmentation(codeBook *c, IplImage *I)////Count how many pixels are detected as foreground// c Codebook// I Image (yuv, 24 bits)// numChannels Number of channels we are testing// maxMod Add this (possibly negative) number onto max level when code_element determining if new pixel is foreground// minMod Subract this (possible negative) number from min level code_element when determining if pixel is foreground//// NOTES:// minMod and maxMod must have length numChannels, e.g. 3 channels => minMod[3], maxMod[3].////Return// Count of fg pixels//int cvcountSegmentation(codeBook *c, IplImage *I, int numChannels, int *minMod, int *maxMod){ int count = 0,i; uchar *pColor; int imageLen = I->width * I->height; //GET BASELINE NUMBER OF FG PIXELS FOR Iraw pColor = (uchar *)((I)->imageData); for(i=0; i
height + mask->width) /perimScale; //calculate perimeter len threshold if( len < q ) //Get rid of blob if it's perimeter is too small { cvSubstituteContour( scanner, NULL ); } else //Smooth it's edges if it's large enough { CvSeq* c_new; if(poly1_hull0) //Polygonal approximation of the segmentation c_new = cvApproxPoly(c,sizeof(CvContour),mem_storage,CV_POLY_APPROX_DP, CVCONTOUR_APPROX_LEVEL,0); else //Convex Hull of the segmentation c_new = cvConvexHull2(c,mem_storage,CV_CLOCKWISE,1); cvSubstituteContour( scanner, c_new ); numCont++; } } contours = cvEndFindContours( &scanner );// PAINT THE FOUND REGIONS BACK INTO THE IMAGE cvZero( mask ); IplImage *maskTemp; //CALC CENTER OF MASS AND OR BOUNDING RECTANGLES if(num != NULL) { int N = *num, numFilled = 0, i=0; CvMoments moments; double M00, M01, M10; maskTemp = cvCloneImage(mask); for(i=0, c=contours; c != NULL; c = c->h_next,i++ ) { if(i < N) //Only process up to *num of them { cvDrawContours(maskTemp,c,CV_CVX_WHITE, CV_CVX_WHITE,-1,CV_FILLED,8); //Find the center of each contour if(centers != NULL) { cvMoments(maskTemp,&moments,1); M00 = cvGetSpatialMoment(&moments,0,0); M10 = cvGetSpatialMoment(&moments,1,0); M01 = cvGetSpatialMoment(&moments,0,1); centers[i].x = (int)(M10/M00); centers[i].y = (int)(M01/M00); } //Bounding rectangles around blobs if(bbs != NULL) { bbs[i] = cvBoundingRect(c); } cvZero(maskTemp); numFilled++; } //Draw filled contours into mask cvDrawContours(mask,c,CV_CVX_WHITE,CV_CVX_WHITE,-1,CV_FILLED,8); //draw to central mask } //end looping over contours *num = numFilled; cvReleaseImage( &maskTemp); } //ELSE JUST DRAW PROCESSED CONTOURS INTO THE MASK else { for( c=contours; c != NULL; c = c->h_next ) { cvDrawContours(mask,c,CV_CVX_WHITE, CV_CVX_BLACK,-1,CV_FILLED,8); } }}

  

三、2种算法进行对比。

     Learning Opencv的作者将这两种算法做了下对比,用的视频是有风吹动树枝的动态背景,一段时间过后的前景是视频中移动的手。

     当然在这个工程中,作者除了体现上述简单背景差法和codobook算法的一些原理外,还引入了很多细节来优化前景分割效果。比如说误差计算时的方差和协方差计算加速方法,消除像素点内长时间没有被访问过的码元,对检测到的粗糙原始前景图用连通域分析法清楚噪声,其中引入了形态学中的几种操作,使用多边形拟合前景轮廓等细节处理。

     在看作者代码前,最好先看下下面几个变量的物理含义。

     maxMod[n]:用训练好的背景模型进行前景检测时用到,判断点是否小于max[n] + maxMod[n])。

     minMod[n]:用训练好的背景模型进行前景检测时用到,判断点是否小于min[n] -minMod[n])。

     cbBounds*:训练背景模型时用到,可以手动输入该参数,这个数主要是配合high[n]和low[n]来用的。

     learnHigh[n]:背景学习过程中当一个新像素来时用来判断是否在已有的码元中,是阈值的上界部分。

     learnLow[n]:背景学习过程中当一个新像素来时用来判断是否在已有的码元中,是阈值的下界部分。

     max[n]: 背景学习过程中每个码元学习到的最大值,在前景分割时配合maxMod[n]用的。

     min[n]: 背景学习过程中每个码元学习到的最小值,在前景分割时配合minMod[n]用的。

     high[n]:背景学习过程中用来调整learnHigh[n]的,如果learnHigh[n]<high[n],则learnHigh[n]缓慢加1

     low[n]: 背景学习过程中用来调整learnLow[n]的,如果learnLow[n]>Low[n],则learnLow[缓慢减1

     该工程带主函数部分代码和注释如下:

#include "stdafx.h"#include "cv.h"#include "highgui.h"#include 
#include
#include
#include "avg_background.h"#include "cv_yuv_codebook.h"//VARIABLES for CODEBOOK METHOD:codeBook *cB; //This will be our linear model of the image, a vector //of lengh = height*widthint maxMod[CHANNELS]; //Add these (possibly negative) number onto max // level when code_element determining if new pixel is foregroundint minMod[CHANNELS]; //Subract these (possible negative) number from min //level code_element when determining if pixel is foregroundunsigned cbBounds[CHANNELS]; //Code Book bounds for learningbool ch[CHANNELS]; //This sets what channels should be adjusted for background boundsint nChannels = CHANNELS;int imageLen = 0;uchar *pColor; //YUV pointervoid help() { printf("\nLearn background and find foreground using simple average and average difference learning method:\n" "\nUSAGE:\n ch9_background startFrameCollection# endFrameCollection# [movie filename, else from camera]\n" "If from AVI, then optionally add HighAvg, LowAvg, HighCB_Y LowCB_Y HighCB_U LowCB_U HighCB_V LowCB_V\n\n" "***Keep the focus on the video windows, NOT the consol***\n\n" "INTERACTIVE PARAMETERS:\n" "\tESC,q,Q - quit the program\n" "\th - print this help\n" "\tp - pause toggle\n" "\ts - single step\n" "\tr - run mode (single step off)\n" "=== AVG PARAMS ===\n" "\t- - bump high threshold UP by 0.25\n" "\t= - bump high threshold DOWN by 0.25\n" "\t[ - bump low threshold UP by 0.25\n" "\t] - bump low threshold DOWN by 0.25\n" "=== CODEBOOK PARAMS ===\n" "\ty,u,v- only adjust channel 0(y) or 1(u) or 2(v) respectively\n" "\ta - adjust all 3 channels at once\n" "\tb - adjust both 2 and 3 at once\n" "\ti,o - bump upper threshold up,down by 1\n" "\tk,l - bump lower threshold up,down by 1\n" );}////USAGE: ch9_background startFrameCollection# endFrameCollection# [movie filename, else from camera]//If from AVI, then optionally add HighAvg, LowAvg, HighCB_Y LowCB_Y HighCB_U LowCB_U HighCB_V LowCB_V//int main(int argc, char** argv){ IplImage* rawImage = 0, *yuvImage = 0; //yuvImage is for codebook method IplImage *ImaskAVG = 0,*ImaskAVGCC = 0; IplImage *ImaskCodeBook = 0,*ImaskCodeBookCC = 0; CvCapture* capture = 0; int startcapture = 1; int endcapture = 30; int c,n; maxMod[0] = 3; //Set color thresholds to default values minMod[0] = 10; maxMod[1] = 1; minMod[1] = 1; maxMod[2] = 1; minMod[2] = 1; float scalehigh = HIGH_SCALE_NUM;//默认值为6 float scalelow = LOW_SCALE_NUM;//默认值为7 if(argc < 3) {//只有1个参数或者没有参数时,输出错误,并提示help信息,因为该程序本身就算进去了一个参数 printf("ERROR: Too few parameters\n"); help(); }else{//至少有2个参数才算正确 if(argc == 3){//输入为2个参数的情形是从摄像头输入数据 printf("Capture from Camera\n"); capture = cvCaptureFromCAM( 0 ); } else {//输入大于2个参数时是从文件中读入视频数据 printf("Capture from file %s\n",argv[3]);//第三个参数是读入视频文件的文件名 // capture = cvCaptureFromFile( argv[3] ); capture = cvCreateFileCapture( argv[3] ); if(!capture) { printf("Couldn't open %s\n",argv[3]); return -1;}//读入视频文件失败 } if(isdigit(argv[1][0])) { //Start from of background capture startcapture = atoi(argv[1]);//第一个参数表示视频开始的背景训练时的帧,默认是1 printf("startcapture = %d\n",startcapture); } if(isdigit(argv[2][0])) { //End frame of background capture endcapture = atoi(argv[2]);//第二个参数表示的结束背景训练时的,默认为30 printf("endcapture = %d\n"); } if(argc > 4){ //See if parameters are set from command line,输入多于4个参数表示后面的算法中用到的参数在这里直接输入 //FOR AVG MODEL if(argc >= 5){ if(isdigit(argv[4][0])){ scalehigh = (float)atoi(argv[4]); } } if(argc >= 6){ if(isdigit(argv[5][0])){ scalelow = (float)atoi(argv[5]); } } //FOR CODEBOOK MODEL, CHANNEL 0 if(argc >= 7){ if(isdigit(argv[6][0])){ maxMod[0] = atoi(argv[6]); } } if(argc >= 8){ if(isdigit(argv[7][0])){ minMod[0] = atoi(argv[7]); } } //Channel 1 if(argc >= 9){ if(isdigit(argv[8][0])){ maxMod[1] = atoi(argv[8]); } } if(argc >= 10){ if(isdigit(argv[9][0])){ minMod[1] = atoi(argv[9]); } } //Channel 2 if(argc >= 11){ if(isdigit(argv[10][0])){ maxMod[2] = atoi(argv[10]); } } if(argc >= 12){ if(isdigit(argv[11][0])){ minMod[2] = atoi(argv[11]); } } } } //MAIN PROCESSING LOOP: bool pause = false; bool singlestep = false; if( capture ) { cvNamedWindow( "Raw", 1 );//原始视频图像 cvNamedWindow( "AVG_ConnectComp",1);//平均法连通区域分析后的图像 cvNamedWindow( "ForegroundCodeBook",1);//codebook法后图像 cvNamedWindow( "CodeBook_ConnectComp",1);//codebook法连通区域分析后的图像 cvNamedWindow( "ForegroundAVG",1);//平均法后图像 int i = -1; for(;;) { if(!pause){// if( !cvGrabFrame( capture ))// break;// rawImage = cvRetrieveFrame( capture ); rawImage = cvQueryFrame( capture ); ++i;//count it// printf("%d\n",i); if(!rawImage) break; //REMOVE THIS FOR GENERAL OPERATION, JUST A CONVIENIENCE WHEN RUNNING WITH THE SMALL tree.avi file if(i == 56){//程序开始运行几十帧后自动暂停,以便后面好手动调整参数 pause = 1; printf("\n\nVideo paused for your convienience at frame 50 to work with demo\n" "You may adjust parameters, single step or continue running\n\n"); help(); } } if(singlestep){ pause = true; } //First time: if(0 == i) { printf("\n . . . wait for it . . .\n"); //Just in case you wonder why the image is white at first //AVG METHOD ALLOCATION AllocateImages(rawImage);//为算法的使用分配内存 scaleHigh(scalehigh);//设定背景建模时的高阈值函数 scaleLow(scalelow);//设定背景建模时的低阈值函数 ImaskAVG = cvCreateImage( cvGetSize(rawImage), IPL_DEPTH_8U, 1 ); ImaskAVGCC = cvCreateImage( cvGetSize(rawImage), IPL_DEPTH_8U, 1 ); cvSet(ImaskAVG,cvScalar(255)); //CODEBOOK METHOD ALLOCATION: yuvImage = cvCloneImage(rawImage); ImaskCodeBook = cvCreateImage( cvGetSize(rawImage), IPL_DEPTH_8U, 1 );//用来装前景背景图的,当然只要一个通道的图像即可 ImaskCodeBookCC = cvCreateImage( cvGetSize(rawImage), IPL_DEPTH_8U, 1 ); cvSet(ImaskCodeBook,cvScalar(255)); imageLen = rawImage->width*rawImage->height; cB = new codeBook [imageLen];//创建一个码本cB数组,每个像素对应一个码本 for(int f = 0; f
= startcapture && i < endcapture ){ //LEARNING THE AVERAGE AND AVG DIFF BACKGROUND accumulateBackground(rawImage);//平均法累加过程 //LEARNING THE CODEBOOK BACKGROUND pColor = (uchar *)((yuvImage)->imageData);//yuvImage矩阵的首位置 for(int c=0; c
= endcapture) {//endcapture帧后开始检测前景 //FIND FOREGROUND BY AVG METHOD: backgroundDiff(rawImage,ImaskAVG); cvCopy(ImaskAVG,ImaskAVGCC); cvconnectedComponents(ImaskAVGCC);//平均法中的前景清除 //FIND FOREGROUND BY CODEBOOK METHOD uchar maskPixelCodeBook; pColor = (uchar *)((yuvImage)->imageData); //3 channel yuv image uchar *pMask = (uchar *)((ImaskCodeBook)->imageData); //1 channel image for(int c=0; c
endcapture){ scalehigh += 0.25; printf("AVG scalehigh=%f\n",scalehigh); scaleHigh(scalehigh); } break; case '='://scalehigh减少2.5是增加其影响力 if(i > endcapture){ scalehigh -= 0.25; printf("AVG scalehigh=%f\n",scalehigh); scaleHigh(scalehigh); } break; case '[': if(i > endcapture){//设置设定背景建模时的低阈值函数,同上 scalelow += 0.25; printf("AVG scalelow=%f\n",scalelow); scaleLow(scalelow); } break; case ']': if(i > endcapture){ scalelow -= 0.25; printf("AVG scalelow=%f\n",scalelow); scaleLow(scalelow); } break; //CODEBOOK PARAMS case 'y': case '0'://激活y通道 ch[0] = 1; ch[1] = 0; ch[2] = 0; printf("CodeBook YUV Channels active: "); for(n=0; n

  

 运行结果截图如下:

     训练过程视频原图截图:

  测试过程视频原图截图:

 前景检测过程截图:

 

可以看到左边2幅截图的对比,codebook算法的效果明显比简单减图法要好,手型比较清晰些。

 

 四、参考文献

      Bradski, G. and A. Kaehler (2008). Learning OpenCV: Computer vision with the OpenCV library, O'Reilly Media.

 

转载于:https://www.cnblogs.com/molakejin/p/5684882.html

你可能感兴趣的文章
来自小姐姐的入门推荐:7个基本机器学习算法Python实现
查看>>
整理Exchange数据库空白空间
查看>>
有关台式机不能被开启的故障诊断步骤
查看>>
生活?
查看>>
oracle导出结果集为xml格式文件
查看>>
Kubernetes 1.5安装
查看>>
Python Split函数的用法总结
查看>>
linux中开启和关闭防火墙
查看>>
HBuilder学习与使用
查看>>
脑机交互有多难?五位顶级科学家剖析马斯克的Neuralink
查看>>
gitlab 完整部署实例
查看>>
maven 教程入门 maven 配置管理 编译java程序
查看>>
GNS关于IPS&ASA&PIX&Junos的配置
查看>>
走马观花: Linux 系统调用 open 七日游(五)
查看>>
MongoDB用户管理
查看>>
mysql字符集与校对规则---大小写敏感那点事
查看>>
记一次服务器宕机处理过程
查看>>
dns处理模块dnspython
查看>>
备份MySQL数据库的命令
查看>>
Python 方法
查看>>