码本模型

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;

#define CHANNELS 3

typedef 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<numChannels; n++)
 {
  high[n] = *(p+n)+*(cbBounds+n);//用来标识每个像素三个通道上亮度的阈值上界
  if(high[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<c.numEntries; i++)
 {
  matchChannel = 0;
  for(n=0; n<numChannels; n++)
  {
    if((c.cb[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<numChannels; n++)
   {
    if(c.cb[i]->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<c.numEntries; s++)
 {
  int negRun = c.t - c.cb[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<c.numEntries; ii++)
  {
   foo[ii] = c.cb[ii];
  }
  foo[c.numEntries] = new code_element;
  if(c.numEntries) delete [] c.cb;
  c.cb = foo;
  for(n=0; n<numChannels; n++)
  {
   c.cb[c.numEntries]->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<numChannels; n++)
 {
  if(c.cb[i]->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<c.numEntries; i++)
{
  if(c.cb[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<c.numEntries; ii++){
  if(keep[ii])
  {
   foo[k] = c.cb[ii];
   //We have to refresh these entries for next clearStale
   foo[k]->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<c.numEntries; i++) 
{
  matchChannel = 0;
  for(int n=0; n<numChannels; n++) 
{
   if((c.cb[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;
}

程序运行结果:

原文地址:https://www.cnblogs.com/juaner767/p/3678819.html