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 }
时间: 2024-08-17 01:17:35

codebook法分割前景目标的相关文章

前景目标检测1(总结)

运动前景对象检测一直是国内外视觉监控领域研究的难点和热点之一,其目的是从序列图像中将变化区域从背景图像中提取出来,运动前景对象的有效检测对于对象跟踪.目标分类.行为理解等后期处理至关重要,那么区分前景对象,非常关键的一个问题是确定一个非常合适的背景,背景从象素的角度来理解,每一个象素就是有可能是前景点,也有可能是背景点,那么我们就要防止背景中误进入原属于前景点的对象,目前有几种常用的方法,但分别有利弊. 主要可以分为:背景建模,帧差法,光流法 1: Single Gaussian(单高斯模型)

前景目标检测(总结)

运动前景对象检测一直是国内外视觉监控领域研究的难点和热点之一,其目的是从序列图像中将变化区域从背景图像中提取出来,运动前景对象的有效检测对于对象跟踪.目标分类.行为理解等后期处理至关重要,那么区分前景对象,非常关键的一个问题是确定一个非常合适的背景,背景从象素的角度来理解,每一个象素就是有可能是前景点,也有可能是背景点,那么我们就要防止背景中误进入原属于前景点的对象,目前有几种常用的方法,但分别有利弊. 主要可以分为:背景建模,帧差法,光流法 1: Single Gaussian(单高斯模型)

利用IFA法进行个人目标实现

利用IFA法目标实现(一) 一.背景 1)主要原因:一直以来,我觉得计划对于实现目标非常重要.但是从2010年至今,我的各种各样计划几乎都全部流产,以至于我丧失了对于制定计划的兴趣.当然,抛弃制定计划之后,我仍然没有能够完成我的目标.翻看往年计划本,2013年2月份的时候,我的计划中就提到了问题的根源可能在于在于制定计划的方法上,但是没有加以重视. 2)催化剂:今年两次考GRE以及一次托福考试,匆匆忙忙的抢考位.预定酒店.车票和机票,然后临时抱佛脚,最后结果也十分不理想.仔细分析,解决问题的方法

paper 83:前景检测算法_1(codebook和平均背景法)

前景分割中一个非常重要的研究方向就是背景减图法,因为背景减图的方法简单,原理容易被想到,且在智能视频监控领域中,摄像机很多情况下是固定的,且背景也是基本不变或者是缓慢变换的,在这种场合背景减图法的应用驱使了其不少科研人员去研究它. 但是背景减图获得前景图像的方法缺点也很多:比如说光照因素,遮挡因素,动态周期背景,且背景非周期背景,且一般情况下我们考虑的是每个像素点之间独立,这对实际应用留下了很大的隐患. 这一小讲主要是讲简单背景减图法和codebook法. 一.简单背景减图法的工作原理. 在视频

【数字图像处理】帧差法与Kirsch边缘检测实现运动目标识别与分割

本文链接:https://blog.csdn.net/qq_18234121/article/details/82763385 作者:冻人的蓝鲸梁思成 视频分割算法可以从时域和空域两个角度考虑.时域分割算法利用视频流时域连续性,通过 相邻帧的时域变化来检测运动目标.在摄像头静止的情况下,常用的方法有帧差法和减背景法. 帧差法比较直观实用,对光照的变化干扰不敏感,但是 对目标的检测不准确,对于缓慢运动的目标甚至可 能无法提取出目标边界,对于快速运动的目标提取 出的目标区域又过大.减背景法容易得到目

目标跟踪之光流法---光流法简单介绍

光流的概念是Gibson在1950年首先提出来的.它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法.一般而言,光流是由于场景中前景目标本身的移动.相机的运动,或者两者的共同运动所产生的.其计算方法可以分为三类: (1)基于区域或者基于特征的匹配方法: (2)基于频域的方法: (3)基于梯度的方法: 简单来说,光流是空间运动物体在观测成像平面上的像素运

教学目标的表述方式──行为目标的ABCD表述法

教学目标应规定学生在教学活动结束后能表现出什么样的学业行为,并限定学生学习过程中知识.技能的获得和情感态度发展的层次.范围.方式及变化效果的量度.对每节课教学目标的准确表述,可以充分发挥教学目标在教学活动中的指向.评估和激励作用.表述教学目标时一般要慎重考虑以下四个因素. 1.行为主体.根据新课程的有关理念,在编写教学目标时,无论是一般的行为目标或是具体的行为目标,在描写时都应写成学生的学习行为而不是教师的教学行为,一般不用来描述教师的教学程序或活动的安排,如“使学生……”“让学生……”“提高学

opencv-视频处理-实时前景检测-阈值法

阈值法: 对每一帧进行阈值处理,取较低的一个阈值进行二值化处理.假设以下为视频流中的任意一帧 代表任意一点处的亮度值(灰度空间),代表一个固定的阈值,对当前帧做以下二值化处理: 该算法比较适合运动物体的亮度大于周围环境的情况,如夜晚的汽车前灯.尾灯等. 下面基于阈值法的前景检测,完成夜晚视频中车辆的检测.跟踪和计数: [算法的步骤] 1.首先画出感兴趣区域,步骤再此博文已详细描述:视频中画出感兴趣区域 2.对进入感兴趣区域的车辆进行前灯的检测,跟踪和计数 代码如下: #include<iostr

OpenCV &mdash;&mdash; 图像局部与部分分割(一)

背景减除 一旦背景模型建立,将背景模型和当前的图像进行比较,然后减去这些已知的背景信息,则剩下的目标物大致就是所求的前景目标了 缺点 -- 该方法基于一个不长成立的假设:所有像素点是独立的 场景建模 新的前景(物体移动的新位置) -- 旧的前景 (物体离开后留下的"空洞")-- 背景 cvInitLineIterator()  和  CV_NEXT_LINE_POINT() 对任意直线上的像素进行采样 // 从视频的一行中读出所有像素的RGB值,收集这些数值并将其分成三个文件 #inc