#include#include using namespace std;#define CHANNELS 3typedef struct ce{ uchar learnHigh[CHANNELS]; //背景学习过程中当一个新像素来时用来判断是否在已有的码元中,是阈值的上界部分 uchar learnLow[CHANNELS]; //阈值的下界部分 uchar max[CHANNELS]; //背景学习过程中每个码元学习到的最大值 uchar min[CHANNELS]; //背景学习过程中每个码元学习到的最小值 int t_last_update; // 最后访问时间 int stale; //未被访问的最长时间} code_element;//码书结构typedef struct code_book{ code_element **cb; //指向码字的指针 int numEntries; //码书包含的码字数量 int t; //标识访问时间} codeBook;codeBook* TcodeBook; //所有像素的码书集合//在视频的前50帧用来更新码本int update_codebook(uchar* p, codeBook& c,unsigned* cbBounds, int numChannels ){ int high[3],low[3]; int n; if(c.numEntries==0) c.t=0; //设置当前访问时间为0 c.t=c.t+1; //每次更新 时间加1 for(n=0; n 255) high[n] = 255; low[n] = *(p+n)-*(cbBounds+n);//用来标识每个像素三个通道上的阈值下界 if(low[n] < 0) low[n] = 0; } int matchChannel; int i;//将当前像素各个通道上的像素亮度*(p+n)同每个码字的相应通道上的像素亮度记录进行匹配 for(i=0; i learnLow[n] <= *(p+n)) &&(*(p+n) <= c.cb[i]->learnHigh[n])) { matchChannel++;//匹配成功 } } if(matchChannel == numChannels) //如果三个通道都匹配成功 { c.cb[i]->t_last_update = c.t;//更新最后访问时间 //更新匹配码字的每个通道上的最大值和最小值 for(n=0; n max[n] < *(p+n)) { c.cb[i]->max[n] = *(p+n); } else if(c.cb[i]->min[n] > *(p+n)) { c.cb[i]->min[n] = *(p+n); } } break; } } //更新每个码字的最长未访问时间 for(int s=0; s t_last_update; if(c.cb[s]->stale < negRun) c.cb[s]->stale = negRun; } if(i == c.numEntries) //如果没有匹配的码字 添加一个新的码字 { code_element **foo = new code_element* [c.numEntries+1]; for(int ii=0; ii learnHigh[n] = high[n]; c.cb[c.numEntries]->learnLow[n] = low[n]; c.cb[c.numEntries]->max[n] = *(p+n); c.cb[c.numEntries]->min[n] = *(p+n); } c.cb[c.numEntries]->t_last_update = c.t; c.cb[c.numEntries]->stale = 0; c.numEntries += 1; }//对于匹配的码字或新添加的码字 更新每个通道上的亮度阈值 每次增加一度 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);} // 检查过期的码字 返回删除的码字数int clear_stale_entries(codeBook &c){ int staleThresh = c.t>>1; int *keep = new int [c.numEntries]; int keepCnt = 0; for(int i=0; i stale > staleThresh) keep[i] = 0; //标识用来清除 else { keep[i] = 1; //标识用来保存 keepCnt += 1; } } c.t = 0; //Full reset on stale tracking code_element **foo = new code_element* [keepCnt]; int k=0; for(int ii=0; ii t_last_update = 0; k++; } } delete [] keep; delete [] c.cb; c.cb = foo; int numCleared = c.numEntries - keepCnt; c.numEntries = keepCnt; return(numCleared);}uchar background_diff( uchar* p, codeBook& c, int numChannels, int* minMod, int* maxMod ){ int matchChannel; 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; //找到匹配的码字 则为背景像素 } } if(i >= c.numEntries) return(255); return(0); }IplImage* pFrame = NULL;IplImage* pFrameHSV = NULL;IplImage* pFrImg = NULL;CvCapture* pCapture = NULL;int nFrmNum = 0;unsigned cbBounds[3] = {10,10,10};int height,width;int nchannels;//用训练好的背景模型进行前景检测时用到,小于max[n] + maxMod[n]//并且大于min[n]-minMod[n])的像素点才被认为是背景像素int minMod[3]={35,8,8}, maxMod[3]={25,8,8};//和这两个值的选择有联系int main(){ cvNamedWindow("video", 1); cvNamedWindow("HSV空间图像",1); cvNamedWindow("foreground",1); //使窗口有序排列 cvMoveWindow("video", 30, 0); cvMoveWindow("HSV空间图像", 360, 0); cvMoveWindow("foreground", 690, 0); //打开视频文件 pCapture = cvCaptureFromFile("video.avi"); int j; while(pFrame = cvQueryFrame( pCapture )) { cvSmooth(pFrame,pFrame,CV_GAUSSIAN,3,3);//高斯滤波 nFrmNum++; cvShowImage("video", pFrame); if (nFrmNum == 1) { height = pFrame->height; width = pFrame->width; nchannels = pFrame->nChannels; pFrameHSV = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,3); pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1);//初始化码书 TcodeBook = new codeBook[width*height]; for(j = 0; j < width*height; j++) { TcodeBook[j].numEntries = 0; TcodeBook[j].t = 0; } } if (nFrmNum<50) { cvCvtColor(pFrame, pFrameHSV, CV_BGR2YCrCb);//色彩空间转化 //学习背景 for(j = 0; j < width*height; j++) { update_codebook((uchar*)pFrameHSV->imageData+j*nchannels, TcodeBook[j],cbBounds,3); } } else { cvCvtColor(pFrame, pFrameHSV, CV_BGR2YCrCb);//色彩空间转化//删除长久未访问的码字 if( nFrmNum == 50) { for(j = 0; j < width*height; j++) clear_stale_entries(TcodeBook[j]); } for(j = 0; j < width*height; j++) {//如果background_diff返回值不为NULL 则为前景像素 if(background_diff((uchar*)pFrameHSV->imageData+j*nchannels, TcodeBook[j],3,minMod,maxMod)) { pFrImg->imageData[j] = 255; } //否则 是背景像素 else { pFrImg->imageData[j] = 0; } } cvShowImage("foreground", pFrImg); cvShowImage("HSV空间图像", pFrameHSV); } if( cvWaitKey(22) >= 0 ) break; } // end of while-loop for(j = 0; j < width*height; j++) { if (!TcodeBook[j].cb) delete [] TcodeBook[j].cb; } if (!TcodeBook) delete [] TcodeBook; cvDestroyWindow("video"); cvDestroyWindow("HSV空间图像"); cvDestroyWindow("foreground"); return 0;}
程序运行结果: