码迷,mamicode.com
首页 > 其他好文 > 详细

codebook法分割前景目标

时间:2015-11-28 23:15:09      阅读:626      评论:0      收藏:0      [点我收藏+]

标签:

利用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 }

 

codebook法分割前景目标

标签:

原文地址:http://www.cnblogs.com/luckyboylch/p/5003661.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!