• 码本模型


    #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;
    }
    

    程序运行结果:

  • 相关阅读:
    nginx 点播mp4方法
    NGINX 添加MP4、FLV视频支持模块
    用nginx搭建基于rtmp或者http的flv、mp4流媒体服务器
    obs nginx-rtmp-module搭建流媒体服务器实现直播 ding
    利用nginx搭建RTMP视频点播、直播、HLS服务器
    使用nginx搭建媒体点播服务器
    nginx支持flv MP4 扩展nginx_mod_h264_streaming,nginx-rtmp-module-master,yamdi
    the odb manual
    Zookeeper——启动闪退
    Zookeeper之启动常见错误及解决方法
  • 原文地址:https://www.cnblogs.com/juaner767/p/3678819.html
Copyright © 2020-2023  润新知