• codebook法分割前景目标


    利用codebook法训练得到背景模型后,对背景差分得到的掩模图像去噪声并找到较大连通域。相对于平均背景法,它的分割效果更好些。当然,分割效果和背景模型训练的帧数有很大关系,适当调整一些参数会得到更好的效果。

      1 #include "stdafx.h"
      2 #include "cv.h"
      3 #include "highgui.h"
      4 
      5 #define CHANNELS 3
      6 typedef struct ce{
      7     uchar learnHigh[CHANNELS];
      8     uchar learnLow[CHANNELS];
      9     uchar max[CHANNELS];
     10     uchar min[CHANNELS];
     11     int t_last_update;
     12     int stale;
     13 }code_element;
     14 
     15 typedef struct code_book{
     16     code_element **cb;
     17     int numEntries;
     18     int t;
     19 }codeBook;
     20 
     21 #define CVCONTOUR_APPROX_LEVEL 2   // Approx.threshold - the bigger it is, the simpler is the boundary  
     22 #define CVCLOSE_ITR 1                // How many iterations of erosion and/or dialation there should be  
     23 
     24 #define CV_CVX_WHITE    CV_RGB(0xff,0xff,0xff)  
     25 #define CV_CVX_BLACK    CV_RGB(0x00,0x00,0x00) 
     26 
     27 ///////////////////////////////////////////////////////////////////////////////////  
     28 // int updateCodeBook(uchar *p, codeBook &c, unsigned cbBounds)  
     29 // Updates the codebook entry with a new data point  
     30 //  
     31 // p            Pointer to a YUV pixel  
     32 // c            Codebook for this pixel  
     33 // cbBounds     Learning bounds for codebook (Rule of thumb: 10)  
     34 // numChannels  Number of color channels we're learning  
     35 //  
     36 // NOTES:  
     37 //      cvBounds must be of size cvBounds[numChannels]  
     38 //  
     39 // RETURN  
     40 //  codebook index    更新码本
     41 int update_codebook(uchar* p,codeBook &c, unsigned* cbBounds, int numChannels)
     42 {
     43     if(c.numEntries==0)c.t=0;    //码本中码元数为0初始化时间为0
     44     c.t+=1;                        //每调用一次时间数加1
     45 
     46     int n;
     47     unsigned int high[3],low[3];
     48     for(n=0;n<numChannels;n++)
     49     {
     50         //加减cbBonds作为此像素阈值上下界
     51         high[n]=*(p+n) + *(cbBounds+n);        //直接使用指针操作更快  
     52         if(high[n]>255)    high[n]=255;
     53         low[n]=*(p+n)-*(cbBounds+n);
     54         if(low[n]<0)  low[n]=0;
     55     }
     56 
     57     int matchChannel;
     58     int i;
     59     for(i=0;i<c.numEntries;i++)
     60     {
     61         matchChannel=0;
     62         for(n=0;n<numChannels;n++)
     63         {
     64             if((c.cb[i]->learnLow[n]<=*(p+n)) && (*(p+n)<=c.cb[i]->learnHigh[n]))
     65             {
     66                 matchChannel++;
     67             }
     68         }
     69         if(matchChannel==numChannels)
     70         {
     71             c.cb[i]->t_last_update=c.t;      //更新码元时间 
     72             for(n=0;n<numChannels;n++)         //调整码元各通道最大最小值
     73             {
     74                 if(c.cb[i]->max[n]<*(p+n))
     75                     c.cb[i]->max[n]=*(p+n);
     76                 else if(c.cb[i]->min[n]>*(p+n))
     77                     c.cb[i]->min[n]=*(p+n);
     78             }
     79             break;
     80         }    
     81     }
     82 
     83     //像素p不满足码本中任何一个码元,创建一个新码元
     84     if(i == c.numEntries)
     85     {
     86         code_element **foo=new code_element*[c.numEntries+1];
     87         for(int ii=0;ii<c.numEntries;ii++)
     88             foo[ii]=c.cb[ii];
     89         foo[c.numEntries]=new code_element;
     90         if(c.numEntries)delete[]c.cb;
     91         c.cb=foo;
     92         for(n=0;n<numChannels;n++)
     93         {
     94             c.cb[c.numEntries]->learnHigh[n]=high[n];
     95             c.cb[c.numEntries]->learnLow[n]=low[n];
     96             c.cb[c.numEntries]->max[n]=*(p+n);
     97             c.cb[c.numEntries]->min[n]=*(p+n);
     98         }
     99         c.cb[c.numEntries]->t_last_update = c.t;  
    100         c.cb[c.numEntries]->stale = 0;  
    101         c.numEntries += 1; 
    102     }
    103 
    104     //计算码元上次更新到现在的时间
    105     for(int s=0; s<c.numEntries; s++)
    106     {
    107         int negRun=c.t-c.cb[s]->t_last_update;
    108         if(c.cb[s]->stale < negRun)
    109             c.cb[s]->stale = negRun;
    110     }
    111 
    112     //如果像素通道值在高低阈值内,但在码元阈值之外,则缓慢调整此码元学习界限(max,min相当于外墙,粗调;learnHigh,learnLow相当于内墙,细调)
    113     for(n=0; n<numChannels; n++)
    114     {
    115         if(c.cb[i]->learnHigh[n]<high[n])
    116             c.cb[i]->learnHigh[n]+=1;
    117         if(c.cb[i]->learnLow[n]>low[n])
    118             c.cb[i]->learnLow[n]-=1;
    119     }
    120 
    121     return i;
    122 }
    123 
    124 // 删除一定时间内未访问的码元,避免学习噪声的codebook
    125 int cvclearStaleEntries(codeBook &c)
    126 {
    127     int staleThresh=c.t>>1;                //设定刷新时间
    128     int *keep=new int[c.numEntries];    
    129     int keepCnt=0;                        //记录不删除码元码元数目
    130     for(int i=0; i<c.numEntries; i++)
    131     {
    132         if(c.cb[i]->stale > staleThresh)
    133             keep[i]=0;        //保留标志符
    134         else
    135         {
    136             keep[i]=1;        //删除标志符
    137             keepCnt+=1;
    138         }
    139     }
    140 
    141     c.t=0;
    142     code_element **foo=new code_element*[keepCnt];
    143     int k=0;
    144     for(int ii=0; ii<c.numEntries; ii++)
    145     {
    146         if(keep[ii])
    147         {
    148             foo[k]=c.cb[ii];
    149             foo[k]->stale=0;    //We have to refresh these entries for next clearStale    
    150             foo[k]->t_last_update=0;
    151             k++;
    152         }
    153     }
    154 
    155     delete[] keep;
    156     delete[] c.cb;
    157     c.cb=foo;
    158     int numCleared=c.numEntries-keepCnt;
    159     c.numEntries=keepCnt;
    160     return numCleared;      //返回删除的码元
    161 }
    162 
    163 ///////////////////////////////////////////////////////////////////////////////////  
    164 // uchar cvbackgroundDiff(uchar *p, codeBook &c, int minMod, int maxMod)  
    165 // Given a pixel and a code book, determine if the pixel is covered by the codebook  
    166 //  
    167 // p        pixel pointer (YUV interleaved)  
    168 // c        codebook reference  
    169 // numChannels  Number of channels we are testing  
    170 // maxMod   Add this (possibly negative) number onto max level when code_element determining if new pixel is foreground  
    171 // minMod   Subract this (possible negative) number from min level code_element when determining if pixel is foreground  
    172 //  
    173 // NOTES:  
    174 // minMod and maxMod must have length numChannels, e.g. 3 channels => minMod[3], maxMod[3].  
    175 //  
    176 // Return  
    177 // 0 => background, 255 => foreground  背景差分,寻找前景目标
    178 uchar cvbackgroundDiff(uchar *p, codeBook &c, int numChannels, int *minMod, int *maxMod)
    179 {
    180     int matchChannel;
    181     int i;
    182     for(i=0; i<c.numEntries; i++)
    183     {
    184         matchChannel=0;
    185         for(int n=0; n<numChannels; n++)
    186         {
    187             if((c.cb[i]->min[n]-minMod[n]<=*(p+n)) && (*(p+n)<=c.cb[i]->max[n]+maxMod[n]))
    188                 matchChannel++;
    189             else
    190                 break;
    191         }
    192         if(matchChannel==numChannels)
    193             break;
    194     }
    195     if(i==c.numEntries)        //像素p各通道值不满足所有的码元,则为前景,返回白色
    196         return 255;
    197     return 0;                //匹配到一个码元时,则为背景,返回黑色
    198 }
    199 
    200 ///////////////////////////////////////////////////////////////////////////////////////////  
    201 //void cvconnectedComponents(IplImage *mask, int poly1_hull0, float perimScale, int *num, CvRect *bbs, CvPoint *centers)  
    202 // This cleans up the foreground segmentation mask derived from calls to cvbackgroundDiff  
    203 //  
    204 // mask         Is a grayscale (8 bit depth) "raw" mask image which will be cleaned up  
    205 //  
    206 // OPTIONAL PARAMETERS:  
    207 // poly1_hull0  If set, approximate connected component by (DEFAULT) polygon, or else convex hull (0)  
    208 // perimScale   Len = image (width+height)/perimScale.  If contour len < this, delete that contour (DEFAULT: 4)  
    209 void cvconnectedComponents(IplImage *mask, int poly1_hull0, float perimScale)  
    210 {
    211     static CvMemStorage* mem_storage=NULL;
    212     static CvSeq* contours=NULL;
    213     cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_OPEN, CVCLOSE_ITR);
    214     cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_CLOSE, CVCLOSE_ITR);  
    215 
    216     if(mem_storage==NULL)
    217         mem_storage=cvCreateMemStorage(0);
    218     else
    219         cvClearMemStorage(mem_storage);
    220 
    221     CvContourScanner scanner=cvStartFindContours(mask,mem_storage,sizeof(CvContour),CV_RETR_EXTERNAL);
    222     CvSeq* c;
    223     int numCont=0;                //轮廓数
    224     while((c=cvFindNextContour(scanner))!=NULL)
    225     {
    226         double len=cvContourPerimeter(c);
    227         double q=(mask->height+mask->width)/perimScale;   //轮廓长度阀值设定
    228         if(len<q)
    229             cvSubstituteContour(scanner,NULL);           //删除太短轮廓
    230         else
    231         {
    232             CvSeq* c_new;
    233             if(poly1_hull0)                                  //用多边形拟合轮廓                                    
    234                 c_new = cvApproxPoly(c, sizeof(CvContour), mem_storage,  
    235                     CV_POLY_APPROX_DP, CVCONTOUR_APPROX_LEVEL);
    236             else                                        //计算轮廓Hu矩
    237                 c_new = cvConvexHull2(c,mem_storage, CV_CLOCKWISE, 1);
    238 
    239             cvSubstituteContour(scanner,c_new);            //替换拟合后的多边形轮廓
    240             numCont++;
    241         }
    242     }
    243     contours = cvEndFindContours(&scanner);  //结束扫描,并返回最高层的第一个轮廓指针
    244 
    245     cvZero(mask);
    246     for(c=contours; c!=NULL; c=c->h_next)
    247         cvDrawContours(mask,c,CV_CVX_WHITE, CV_CVX_BLACK,-1,CV_FILLED,8);
    248 }
    249 
    250 int main()
    251 {
    252     ///////////////////////////////////////  
    253     // 需要使用的变量  
    254     CvCapture* capture=NULL;
    255     IplImage*  rawImage=NULL;            //视频的每一帧原图像
    256     IplImage*  yuvImage=NULL;            //比经验角度看绝大部分背景中的变化倾向于沿亮度轴,而不是颜色轴,故YUV颜色空间效果更好
    257     IplImage* ImaskCodeBook=NULL;        //掩模图像
    258     IplImage* ImaskCodeBookCC=NULL;        //清除噪声后并采用多边形法拟合轮廓连通域的掩模图像
    259 
    260     codeBook* cB=NULL;
    261     unsigned cbBounds[CHANNELS];
    262     uchar* pColor=NULL;                    //yuvImage像素指针
    263     int imageLen=0;
    264     int nChannels=CHANNELS;
    265     int minMod[CHANNELS];
    266     int maxMod[CHANNELS];
    267 
    268     //////////////////////////////////////////////////////////////////////////  
    269     // 初始化各变量  
    270     cvNamedWindow("原图");
    271     cvNamedWindow("掩模图像");
    272     cvNamedWindow("连通域掩模图像");
    273 
    274     //capture = cvCreateFileCapture("C:/Users/shark/Desktop/eagle.flv");
    275     capture=cvCreateCameraCapture(0);
    276     if(!capture)
    277     {
    278         printf("Couldn't open the capture!");  
    279         return -1;  
    280     }
    281     
    282     rawImage=cvQueryFrame(capture);
    283     int width=(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH);
    284     int height=(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT);
    285     CvSize size=cvSize(width,height);
    286     yuvImage=cvCreateImage(size,8,3);
    287     ImaskCodeBook = cvCreateImage(size, IPL_DEPTH_8U, 1);
    288     ImaskCodeBookCC = cvCreateImage(size, IPL_DEPTH_8U, 1);
    289     cvSet(ImaskCodeBook,cvScalar(255));
    290 
    291     imageLen=width*height;
    292     cB=new codeBook[imageLen];    //得到与图像像素数目长度一样的一组码本,以便对每个像素进行处理
    293 
    294     for(int i=0;i<imageLen;i++)
    295         cB[i].numEntries=0;
    296     for(int i=0;i<nChannels;i++)
    297     {
    298         cbBounds[i]=10;
    299         minMod[i]=20;        //用于背景差分函数中
    300         maxMod[i]=20;        //调整其值以达到最好的分割
    301     }
    302 
    303     //////////////////////////////////////////////////////////////////////////  
    304     // 开始处理视频每一帧图像 
    305     for(int i=0;;i++)
    306     {
    307         if(!(rawImage=cvQueryFrame(capture)))
    308             break;
    309         cvCvtColor(rawImage, yuvImage, CV_BGR2YCrCb); 
    310         // 色彩空间转换,将rawImage 转换到YUV色彩空间,输出到yuvImage  
    311         // 即使不转换效果依然很好  
    312         // yuvImage = cvCloneImage(rawImage);
    313 
    314         if(i<=30)            //前30帧进行背景学习
    315         {
    316             pColor=(uchar*)yuvImage->imageData;
    317             for(int c=0; c<imageLen; c++)
    318             {
    319                 update_codebook(pColor, cB[c], cbBounds, nChannels);   //对每个像素调用此函数
    320                 pColor+=3;
    321             }
    322             if(i==30)
    323             {
    324                 for(int c=0;c<imageLen;c++)
    325                 {
    326                     cvclearStaleEntries(cB[c]);            //第30时帧时,删除每个像素码本中陈旧的码元                
    327                 }
    328             }
    329         }
    330         else
    331         {
    332             uchar maskPixel;    
    333             pColor=(uchar*)yuvImage->imageData;
    334             uchar* pMask=(uchar*)ImaskCodeBook->imageData;
    335             for(int c=0;c<imageLen;c++)
    336             {
    337                 maskPixel=cvbackgroundDiff(pColor,cB[c],nChannels,minMod,maxMod);
    338                 *pMask++=maskPixel;
    339                 pColor+=3;
    340             }
    341             cvCopy(ImaskCodeBook,ImaskCodeBookCC);
    342             cvconnectedComponents(ImaskCodeBookCC,1,4.0);
    343             cvShowImage("掩模图像",ImaskCodeBook);
    344             cvShowImage("连通域掩模图像",ImaskCodeBookCC);
    345         }
    346         cvShowImage("原图",rawImage);
    347         if (cvWaitKey(30) == 27)  
    348             break; 
    349     }
    350 
    351     cvReleaseCapture(&capture);  
    352     if (yuvImage)  
    353         cvReleaseImage(&yuvImage);  
    354     if(ImaskCodeBook)   
    355         cvReleaseImage(&ImaskCodeBook);  
    356     cvDestroyAllWindows();  
    357     delete [] cB;  
    358 
    359     return 0;  
    360 
    361 }
  • 相关阅读:
    Windows PowerShell 2.0之进程管理
    PowerShell 2.0远程管理之交互式远程线程
    PowerShell 2.0解析、格式化及显示远程输出
    PowerShell 2.0语言远程管理之理解线程配置
    PowerShell 2.0远程管理之隐式远程管理
    PowerShell 2.0如何将远程线程保存在本地
    Windows PowerShell 2.0之服务管理
    PowerShell 2.0远程管理开发使用CredSSP处理多跳授权
    通过PowerShell操作事件日志
    (译)Silverlight教程第七部分: 使用控件模板定制控件的观感
  • 原文地址:https://www.cnblogs.com/luckyboylch/p/5003661.html
Copyright © 2020-2023  润新知