Update the changelog
[opencv] / cvaux / src / vs / enteringblobdetection.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //
12 // Copyright (C) 2000, Intel Corporation, all rights reserved.
13 // Third party copyrights are property of their respective owners.
14 //
15 // Redistribution and use in source and binary forms, with or without modification,
16 // are permitted provided that the following conditions are met:
17 //
18 //   * Redistribution's of source code must retain the above copyright notice,
19 //     this list of conditions and the following disclaimer.
20 //
21 //   * Redistribution's in binary form must reproduce the above copyright notice,
22 //     this list of conditions and the following disclaimer in the documentation
23 //     and/or other materials provided with the distribution.
24 //
25 //   * The name of Intel Corporation may not be used to endorse or promote products
26 //     derived from this software without specific prior written permission.
27 //
28 // This software is provided by the copyright holders and contributors "as is" and
29 // any express or implied warranties, including, but not limited to, the implied
30 // warranties of merchantability and fitness for a particular purpose are disclaimed.
31 // In no event shall the Intel Corporation or contributors be liable for any direct,
32 // indirect, incidental, special, exemplary, or consequential damages
33 // (including, but not limited to, procurement of substitute goods or services;
34 // loss of use, data, or profits; or business interruption) however caused
35 // and on any theory of liability, whether in contract, strict liability,
36 // or tort (including negligence or otherwise) arising in any way out of
37 // the use of this software, even if advised of the possibility of such damage.
38 //
39 //M*/
40
41 /*
42 This file implements the virtual interface defined as "CvBlobDetector".
43 This implementation based on a simple algorithm:
44 A new blob is detected when several successive frames contains connected components 
45 which have uniform motion not at an unreasonably high speed.
46 Separation from border and already tracked blobs are also considered.
47
48 For an entrypoint into the literature see:
49
50      Appearance Models for Occlusion Handling
51      Andrew Senior &t al, 8p 2001
52      http://www.research.ibm.com/peoplevision/PETS2001.pdf
53
54 */
55
56 //#define USE_OBJECT_DETECTOR
57
58 #include "_cvaux.h"
59
60 static int CompareContour(const void* a, const void* b, void* )
61 {
62     float           dx,dy;
63     float           h,w,ht,wt;
64     CvPoint2D32f    pa,pb;
65     CvRect          ra,rb;
66     CvSeq*          pCA = *(CvSeq**)a;
67     CvSeq*          pCB = *(CvSeq**)b;
68     ra = ((CvContour*)pCA)->rect;
69     rb = ((CvContour*)pCB)->rect;
70     pa.x = ra.x + ra.width*0.5f;
71     pa.y = ra.y + ra.height*0.5f;
72     pb.x = rb.x + rb.width*0.5f;
73     pb.y = rb.y + rb.height*0.5f;
74     w = (ra.width+rb.width)*0.5f;
75     h = (ra.height+rb.height)*0.5f;
76
77     dx = (float)(fabs(pa.x - pb.x)-w);
78     dy = (float)(fabs(pa.y - pb.y)-h);
79
80     //wt = MAX(ra.width,rb.width)*0.1f;
81     wt = 0;
82     ht = MAX(ra.height,rb.height)*0.3f;
83     return (dx < wt && dy < ht);
84 }
85
86 void cvFindBlobsByCCClasters(IplImage* pFG, CvBlobSeq* pBlobs, CvMemStorage* storage)
87 {   /* Create contours: */
88     IplImage*       pIB = NULL;
89     CvSeq*          cnt = NULL;
90     CvSeq*          cnt_list = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvSeq*), storage );
91     CvSeq*          clasters = NULL;
92     int             claster_cur, claster_num;
93
94     pIB = cvCloneImage(pFG);
95     cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
96     cvFindContours(pIB,storage, &cnt, sizeof(CvContour), CV_RETR_EXTERNAL);
97     cvReleaseImage(&pIB);
98
99     /* Create cnt_list.      */
100     /* Process each contour: */
101     for(; cnt; cnt=cnt->h_next)
102     {
103         cvSeqPush( cnt_list, &cnt);
104     }
105
106     claster_num = cvSeqPartition( cnt_list, storage, &clasters, CompareContour, NULL );
107
108     for(claster_cur=0; claster_cur<claster_num; ++claster_cur)
109     {
110         int         cnt_cur;
111         CvBlob      NewBlob;
112         double      M00,X,Y,XX,YY; /* image moments */
113         CvMoments   m;
114         CvRect      rect_res = cvRect(-1,-1,-1,-1);
115         CvMat       mat;
116
117         for(cnt_cur=0; cnt_cur<clasters->total; ++cnt_cur)
118         {
119             CvRect  rect;
120             CvSeq*  cnt;
121             int k = *(int*)cvGetSeqElem( clasters, cnt_cur );
122             if(k!=claster_cur) continue;
123             cnt = *(CvSeq**)cvGetSeqElem( cnt_list, cnt_cur );
124             rect = ((CvContour*)cnt)->rect;
125
126             if(rect_res.height<0)
127             {
128                 rect_res = rect;
129             }
130             else
131             {   /* Unite rects: */
132                 int x0,x1,y0,y1;
133                 x0 = MIN(rect_res.x,rect.x);
134                 y0 = MIN(rect_res.y,rect.y);
135                 x1 = MAX(rect_res.x+rect_res.width,rect.x+rect.width);
136                 y1 = MAX(rect_res.y+rect_res.height,rect.y+rect.height);
137                 rect_res.x = x0;
138                 rect_res.y = y0;
139                 rect_res.width = x1-x0;
140                 rect_res.height = y1-y0;
141             }
142         }
143
144         if(rect_res.height < 1 || rect_res.width < 1)
145         {
146             X = 0;
147             Y = 0;
148             XX = 0;
149             YY = 0;
150         }
151         else
152         {
153             cvMoments( cvGetSubRect(pFG,&mat,rect_res), &m, 0 );
154             M00 = cvGetSpatialMoment( &m, 0, 0 );
155             if(M00 <= 0 ) continue;
156             X = cvGetSpatialMoment( &m, 1, 0 )/M00;
157             Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
158             XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
159             YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
160         }
161         NewBlob = cvBlob(rect_res.x+(float)X,rect_res.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
162         pBlobs->AddBlob(&NewBlob);
163
164     }   /* Next cluster. */
165
166     #if 0
167     {   // Debug info:
168         IplImage* pI = cvCreateImage(cvSize(pFG->width,pFG->height),IPL_DEPTH_8U,3);
169         cvZero(pI);
170         for(claster_cur=0; claster_cur<claster_num; ++claster_cur)
171         {
172             int         cnt_cur;
173             CvScalar    color = CV_RGB(rand()%256,rand()%256,rand()%256);
174
175             for(cnt_cur=0; cnt_cur<clasters->total; ++cnt_cur)
176             {
177                 CvSeq*  cnt;
178                 int k = *(int*)cvGetSeqElem( clasters, cnt_cur );
179                 if(k!=claster_cur) continue;
180                 cnt = *(CvSeq**)cvGetSeqElem( cnt_list, cnt_cur );
181                 cvDrawContours( pI, cnt, color, color, 0, 1, 8);
182             }
183
184             CvBlob* pB = pBlobs->GetBlob(claster_cur);
185             int x = cvRound(CV_BLOB_RX(pB)), y = cvRound(CV_BLOB_RY(pB));
186             cvEllipse( pI, 
187                 cvPointFrom32f(CV_BLOB_CENTER(pB)),
188                 cvSize(MAX(1,x), MAX(1,y)),
189                 0, 0, 360, 
190                 color, 1 );
191         }
192
193         cvNamedWindow( "Clusters", 0);
194         cvShowImage( "Clusters",pI );
195
196         cvReleaseImage(&pI);
197
198     }   /* Debug info. */
199     #endif
200
201 }   /* cvFindBlobsByCCClasters */
202
203 /* Simple blob detector.  */
204 /* Number of successive frame to analyse: */
205 #define EBD_FRAME_NUM   5
206 class CvBlobDetectorSimple:public CvBlobDetector
207 {
208 public:
209     CvBlobDetectorSimple();
210    ~CvBlobDetectorSimple();
211     int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
212     void Release(){delete this;};
213
214 protected:
215     IplImage*       m_pMaskBlobNew;
216     IplImage*       m_pMaskBlobExist;
217     /* Lists of connected components detected on previous frames: */
218     CvBlobSeq*      m_pBlobLists[EBD_FRAME_NUM]; 
219 };
220
221 /* Blob detector creator (sole interface function for this file) */
222 CvBlobDetector* cvCreateBlobDetectorSimple(){return new CvBlobDetectorSimple;};
223
224 /* Constructor of BlobDetector: */
225 CvBlobDetectorSimple::CvBlobDetectorSimple()
226 {
227     int i = 0;
228     m_pMaskBlobNew = NULL;
229     m_pMaskBlobExist = NULL;
230     for(i=0;i<EBD_FRAME_NUM;++i)m_pBlobLists[i] = NULL;
231 }
232
233 /* Destructor of BlobDetector: */
234 CvBlobDetectorSimple::~CvBlobDetectorSimple()
235 {
236     int i;
237     if(m_pMaskBlobExist) cvReleaseImage(&m_pMaskBlobExist);
238     if(m_pMaskBlobNew) cvReleaseImage(&m_pMaskBlobNew);
239     for(i=0; i<EBD_FRAME_NUM; ++i) delete m_pBlobLists[i];
240 }   /* cvReleaseBlobDetector */
241
242 /* cvDetectNewBlobs
243  * return 1 and fill blob pNewBlob by blob parameters
244  * if new blob is detected:
245  */
246 int CvBlobDetectorSimple::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
247 {
248     int         result = 0;
249     CvSize      S = cvSize(pFGMask->width,pFGMask->height);
250     if(m_pMaskBlobNew == NULL ) m_pMaskBlobNew = cvCreateImage(S,IPL_DEPTH_8U,1);
251     if(m_pMaskBlobExist == NULL ) m_pMaskBlobExist = cvCreateImage(S,IPL_DEPTH_8U,1);
252     
253     /* Shift blob list: */
254     {
255         int     i;
256         if(m_pBlobLists[0]) delete m_pBlobLists[0];
257         for(i=1;i<EBD_FRAME_NUM;++i)m_pBlobLists[i-1]=m_pBlobLists[i];
258         m_pBlobLists[EBD_FRAME_NUM-1] = new CvBlobSeq;
259     }   /* Shift blob list. */
260     
261     /* Create exist blob mask: */
262     cvCopy(pFGMask, m_pMaskBlobNew);
263
264     /* Create contours and add new blobs to blob list: */
265     {   /* Create blobs: */
266         CvBlobSeq       Blobs;
267         CvMemStorage*   storage = cvCreateMemStorage();
268
269 #if 1
270         {   /* Glue contours: */
271             cvFindBlobsByCCClasters(m_pMaskBlobNew, &Blobs, storage );
272         }   /* Glue contours. */
273 #else
274         {   /**/
275             IplImage*       pIB = cvCloneImage(m_pMaskBlobNew);
276             CvSeq*          cnts = NULL;
277             CvSeq*          cnt = NULL;
278             cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
279             cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
280
281             /* Process each contour: */
282             for(cnt = cnts; cnt; cnt=cnt->h_next)
283             {
284                 CvBlob  NewBlob;
285
286                 /* Image moments: */
287                 double      M00,X,Y,XX,YY;
288                 CvMoments   m;
289                 CvRect      r = ((CvContour*)cnt)->rect;
290                 CvMat       mat;
291
292                 if(r.height < S.height*0.02 || r.width < S.width*0.02) continue;
293
294                 cvMoments( cvGetSubRect(m_pMaskBlobNew,&mat,r), &m, 0 );
295                 M00 = cvGetSpatialMoment( &m, 0, 0 );
296
297                 if(M00 <= 0 ) continue;
298
299                 X  = cvGetSpatialMoment( &m, 1, 0 )/M00;
300                 Y  = cvGetSpatialMoment( &m, 0, 1 )/M00;
301
302                 XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
303                 YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
304
305                 NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
306
307                 Blobs.AddBlob(&NewBlob);
308
309             }   /* Next contour. */
310
311             cvReleaseImage(&pIB);
312
313         }   /* One contour - one blob. */
314 #endif
315
316         {   /* Delete small and intersected blobs: */
317             int i;
318             for(i=Blobs.GetBlobNum(); i>0; i--)
319             {
320                 CvBlob* pB = Blobs.GetBlob(i-1);
321
322                 if(pB->h < S.height*0.02 || pB->w < S.width*0.02)
323                 {
324                     Blobs.DelBlob(i-1);
325                     continue;
326                 }
327                 if(pOldBlobList)
328                 {
329                     int j;
330                     for(j=pOldBlobList->GetBlobNum(); j>0; j--)
331                     {
332                         CvBlob* pBOld = pOldBlobList->GetBlob(j-1);
333                         if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) &&
334                            (fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB))))
335                         {   /* Intersection is present, so delete blob from list: */
336                             Blobs.DelBlob(i-1);
337                             break;
338                         }
339                     }   /* Check next old blob. */
340                 }   /*  if pOldBlobList */
341             }   /* Check next blob. */
342         }   /*  Delete small and intersected blobs. */
343
344         {   /* Bubble-sort blobs by size: */
345             int N = Blobs.GetBlobNum();
346             int i,j;
347             for(i=1; i<N; ++i)
348             {
349                 for(j=i; j>0; --j)
350                 {
351                     CvBlob  temp;
352                     float   AreaP, AreaN;
353                     CvBlob* pP = Blobs.GetBlob(j-1);
354                     CvBlob* pN = Blobs.GetBlob(j);
355                     AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP);
356                     AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN);
357                     if(AreaN < AreaP)break;
358                     temp = pN[0];
359                     pN[0] = pP[0];
360                     pP[0] = temp;
361                 }
362             }
363
364             /* Copy only first 10 blobs: */
365             for(i=0; i<MIN(N,10); ++i)
366             {
367                 m_pBlobLists[EBD_FRAME_NUM-1]->AddBlob(Blobs.GetBlob(i));
368             }
369
370         }   /* Sort blobs by size. */
371
372         cvReleaseMemStorage(&storage);
373
374     }   /* Create blobs. */
375
376     /* Analyze blob list to find best blob trajectory: */
377     {
378         int     Count = 0;
379         int     pBLIndex[EBD_FRAME_NUM];
380         int     pBL_BEST[EBD_FRAME_NUM];
381         int     i;
382         int     finish = 0;
383         double  BestError = -1;
384         int     Good = 1;
385
386         for(i=0; i<EBD_FRAME_NUM; ++i)
387         {
388             pBLIndex[i] = 0;
389             pBL_BEST[i] = 0;
390         }
391         
392         /* Check configuration exist: */
393         for(i=0; Good && (i<EBD_FRAME_NUM); ++i)
394             if(m_pBlobLists[i] == NULL || m_pBlobLists[i]->GetBlobNum() == 0)
395                 Good = 0;
396
397         if(Good)
398         do{ /* For each configuration: */
399             CvBlob* pBL[EBD_FRAME_NUM];
400             int     Good = 1;
401             double  Error = 0;
402             CvBlob* pBNew = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBLIndex[EBD_FRAME_NUM-1]);
403
404             for(i=0; i<EBD_FRAME_NUM; ++i)  pBL[i] = m_pBlobLists[i]->GetBlob(pBLIndex[i]);
405
406             Count++;
407
408             /* Check intersection last blob with existed: */
409             if(Good && pOldBlobList)
410             {   /* Check intersection last blob with existed: */
411                 int     k;
412                 for(k=pOldBlobList->GetBlobNum(); k>0; --k)
413                 {
414                     CvBlob* pBOld = pOldBlobList->GetBlob(k-1);
415                     if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) &&
416                        (fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew))))
417                         Good = 0;
418                 }
419             }   /* Check intersection last blob with existed. */
420
421             /* Check distance to image border: */
422             if(Good)
423             {   /* Check distance to image border: */
424                 CvBlob*  pB = pBNew;
425                 float    dx = MIN(pB->x,S.width-pB->x)/CV_BLOB_RX(pB);
426                 float    dy = MIN(pB->y,S.height-pB->y)/CV_BLOB_RY(pB);
427
428                 if(dx < 1.1 || dy < 1.1) Good = 0;
429             }   /* Check distance to image border. */
430
431             /* Check uniform motion: */
432             if(Good)
433             {
434                 int     N = EBD_FRAME_NUM;
435                 float   sum[2] = {0,0};   
436                 float   jsum[2] = {0,0};
437                 float   a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/
438
439                 int j;
440                 for(j=0; j<N; ++j)
441                 {
442                     float   x = pBL[j]->x;
443                     float   y = pBL[j]->y;
444                     sum[0] += x;
445                     jsum[0] += j*x;
446                     sum[1] += y;
447                     jsum[1] += j*y;
448                 }
449
450                 a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1));
451                 b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1));
452                 a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1));
453                 b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1));
454
455                 for(j=0; j<N; ++j)
456                 {
457                     Error += 
458                         pow(a[0]*j+b[0]-pBL[j]->x,2)+
459                         pow(a[1]*j+b[1]-pBL[j]->y,2);
460                 }
461
462                 Error = sqrt(Error/N);
463
464                 if( Error > S.width*0.01 || 
465                     fabs(a[0])>S.width*0.1 ||
466                     fabs(a[1])>S.height*0.1)
467                     Good = 0;
468
469             }   /* Check configuration. */
470
471             
472             /* New best trajectory: */
473             if(Good && (BestError == -1 || BestError > Error))
474             {
475                 for(i=0; i<EBD_FRAME_NUM; ++i)
476                 {
477                     pBL_BEST[i] = pBLIndex[i];
478                 }
479                 BestError = Error;
480             }   /* New best trajectory. */
481
482             /* Set next configuration: */
483             for(i=0; i<EBD_FRAME_NUM; ++i)
484             {
485                 pBLIndex[i]++;
486                 if(pBLIndex[i] != m_pBlobLists[i]->GetBlobNum()) break;
487                 pBLIndex[i]=0;
488             }   /* Next time shift. */
489
490             if(i==EBD_FRAME_NUM)finish=1;
491
492         } while(!finish);       /* Check next time configuration of connected components. */
493
494         #if 0
495         {/**/
496             printf("BlobDetector configurations = %d [",Count);
497             int i;
498             for(i=0; i<EBD_FRAME_NUM; ++i)
499             {
500                 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
501             }
502             printf("]\n");
503         
504         }
505         #endif
506
507         if(BestError != -1)
508         {   /* Write new blob to output and delete from blob list: */
509             CvBlob* pNewBlob = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBL_BEST[EBD_FRAME_NUM-1]);
510             pNewBlobList->AddBlob(pNewBlob);
511
512             for(i=0; i<EBD_FRAME_NUM; ++i)
513             {   /* Remove blob from each list: */
514                 m_pBlobLists[i]->DelBlob(pBL_BEST[i]);
515             }   /* Remove blob from each list. */
516
517             result = 1;
518
519         }   /* Write new blob to output and delete from blob list. */
520     }   /*  Analyze blob list to find best blob trajectory. */
521
522     return result;
523
524 }   /* cvDetectNewBlob */
525
526
527
528
529 /* Simple blob detector2.  */
530 /* Number of successive frames to analyse: */
531 #define SEQ_SIZE_MAX    30
532 #define SEQ_NUM         1000
533 typedef struct
534 {
535     int     size;
536     CvBlob* pBlobs[SEQ_SIZE_MAX];
537 } DefSeq;
538
539 class CvBlobDetectorCC:public CvBlobDetector
540 {
541 public:
542     CvBlobDetectorCC();
543    ~CvBlobDetectorCC();
544     int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
545     void Release(){delete this;};
546
547     virtual void ParamUpdate()
548     {
549         if(SEQ_SIZE<1)SEQ_SIZE=1;
550         if(SEQ_SIZE>SEQ_SIZE_MAX)SEQ_SIZE=SEQ_SIZE_MAX;
551
552 #ifdef USE_OBJECT_DETECTOR
553         if( m_param_split_detector_file_name )
554         {
555             m_split_detector = new CvObjectDetector();
556             if( !m_split_detector->Load( m_param_split_detector_file_name ) )
557             {
558                 delete m_split_detector;
559                 m_split_detector = 0;
560             }
561             else
562             {
563                 m_min_window_size = m_split_detector->GetMinWindowSize();
564                 m_max_border = m_split_detector->GetMaxBorderSize();
565             }
566         }
567 #endif
568     }
569
570 private:
571     /* Lists of connected components detected on previous frames: */
572     CvBlobSeq*      m_pBlobLists[SEQ_SIZE_MAX];
573     DefSeq          m_TrackSeq[SEQ_NUM];
574     int             m_TrackNum;
575     float           m_HMin;
576     float           m_WMin;
577     float           m_MinDistToBorder;
578     int             m_Clastering;
579     int             SEQ_SIZE;
580
581     /* If not 0 then the detector is loaded from the specified file
582      * and it is applied for splitting blobs which actually correspond
583      * to groups of objects:
584      */ 
585     char*           m_param_split_detector_file_name;
586     float           m_param_roi_scale;
587     int             m_param_only_roi;
588
589     CvObjectDetector* m_split_detector;
590     CvSize          m_min_window_size;
591     int             m_max_border;
592
593     CvBlobSeq       m_detected_blob_seq;
594     CvSeq*          m_roi_seq;
595
596     CvBlobSeq       m_debug_blob_seq;
597 };
598
599 /* Blob detector creator (sole interface function for this file): */
600 CvBlobDetector* cvCreateBlobDetectorCC(){return new CvBlobDetectorCC;}
601
602 /* Constructor for BlobDetector: */
603 CvBlobDetectorCC::CvBlobDetectorCC() :
604     m_split_detector(0),
605     m_detected_blob_seq(sizeof(CvDetectedBlob)),
606     m_roi_seq(0),
607     m_debug_blob_seq(sizeof(CvDetectedBlob))
608 {
609     /*CvDrawShape shapes[] = 
610     {
611         { CvDrawShape::RECT,    {{255,255,255}} },
612         { CvDrawShape::RECT,    {{0,0,255}} },
613         { CvDrawShape::ELLIPSE, {{0,255,0}} }
614     };
615     int num_shapes = sizeof(shapes) / sizeof(shapes[0]);*/
616
617     int i = 0;
618     SEQ_SIZE = 10;
619     AddParam("Latency",&SEQ_SIZE);
620     for(i=0;i<SEQ_SIZE_MAX;++i)m_pBlobLists[i] = NULL;
621     for(i=0;i<SEQ_NUM;++i)m_TrackSeq[i].size = 0;
622     m_TrackNum = 0;
623
624     m_HMin = 0.02f;
625     m_WMin = 0.01f;
626     AddParam("HMin",&m_HMin);
627     AddParam("WMin",&m_WMin);
628     m_MinDistToBorder = 1.1f;
629     AddParam("MinDistToBorder",&m_MinDistToBorder);
630     CommentParam("MinDistToBorder","Minimal allowed distance from blob center to image border in blob sizes");
631     
632     m_Clastering=1;
633     AddParam("Clastering",&m_Clastering);
634     CommentParam("Clastering","Minimal allowed distance from blob center to image border in blob sizes");
635
636     m_param_split_detector_file_name = 0;
637 #ifdef USE_OBJECT_DETECTOR
638     AddParam("Detector", &m_param_split_detector_file_name);
639     CommentParam("Detector", "Detector file name");
640 #endif
641
642     m_param_roi_scale = 1.5F;
643     AddParam("ROIScale", &m_param_roi_scale);
644     CommentParam("ROIScale", "Determines the size of search window around a blob");
645
646     m_param_only_roi = 1;
647     AddParam("OnlyROI", &m_param_only_roi);
648     CommentParam("OnlyROI", "Shows the whole debug image (0) or only ROIs where the detector was applied (1)");
649
650     m_min_window_size = cvSize(0,0);
651     m_max_border = 0;
652     m_roi_seq = cvCreateSeq( 0, sizeof(*m_roi_seq), sizeof(CvRect), cvCreateMemStorage() );
653     
654     SetModuleName("BD_CC");
655 }
656
657 /* Destructor for BlobDetector: */
658 CvBlobDetectorCC::~CvBlobDetectorCC()
659 {
660     int i;
661     for(i=0; i<SEQ_SIZE_MAX; ++i)
662     {
663         if(m_pBlobLists[i]) 
664             delete m_pBlobLists[i];
665     }
666
667     if( m_roi_seq )
668     {
669         cvReleaseMemStorage( &m_roi_seq->storage );
670         m_roi_seq = 0;
671     }
672     //cvDestroyWindow( "EnteringBlobDetectionDebug" );
673 }   /* cvReleaseBlobDetector */
674
675
676 /* cvDetectNewBlobs
677  * Return 1 and fill blob pNewBlob  with
678  * blob parameters if new blob is detected:
679  */
680 int CvBlobDetectorCC::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
681 {
682     int         result = 0;
683     CvSize      S = cvSize(pFGMask->width,pFGMask->height);
684     
685     /* Shift blob list: */
686     {
687         int     i;
688         if(m_pBlobLists[SEQ_SIZE-1]) delete m_pBlobLists[SEQ_SIZE-1];
689
690         for(i=SEQ_SIZE-1; i>0; --i)  m_pBlobLists[i] = m_pBlobLists[i-1];
691
692         m_pBlobLists[0] = new CvBlobSeq;
693
694     }   /* Shift blob list. */
695     
696     /* Create contours and add new blobs to blob list: */
697     {   /* Create blobs: */
698         CvBlobSeq       Blobs;
699         CvMemStorage*   storage = cvCreateMemStorage();
700
701         if(m_Clastering)
702         {   /* Glue contours: */
703             cvFindBlobsByCCClasters(pFGMask, &Blobs, storage );
704         }   /* Glue contours. */
705         else
706         { /**/
707             IplImage*       pIB = cvCloneImage(pFGMask);
708             CvSeq*          cnts = NULL;
709             CvSeq*          cnt = NULL;
710             cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
711             cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
712
713             /* Process each contour: */
714             for(cnt = cnts; cnt; cnt=cnt->h_next)
715             {
716                 CvBlob  NewBlob;
717                 /* Image moments: */
718                 double      M00,X,Y,XX,YY;
719                 CvMoments   m;
720                 CvRect      r = ((CvContour*)cnt)->rect;
721                 CvMat       mat;
722                 if(r.height < S.height*m_HMin || r.width < S.width*m_WMin) continue;
723                 cvMoments( cvGetSubRect(pFGMask,&mat,r), &m, 0 );
724                 M00 = cvGetSpatialMoment( &m, 0, 0 );
725                 if(M00 <= 0 ) continue;
726                 X = cvGetSpatialMoment( &m, 1, 0 )/M00;
727                 Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
728                 XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
729                 YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
730                 NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
731                 Blobs.AddBlob(&NewBlob);
732
733             }   /* Next contour. */
734
735             cvReleaseImage(&pIB);
736
737         }   /* One contour - one blob. */
738
739         {   /* Delete small and intersected blobs: */
740             int i;
741             for(i=Blobs.GetBlobNum(); i>0; i--)
742             {
743                 CvBlob* pB = Blobs.GetBlob(i-1);
744
745                 if(pB->h < S.height*m_HMin || pB->w < S.width*m_WMin)
746                 {
747                     Blobs.DelBlob(i-1);
748                     continue;
749                 }
750
751                 if(pOldBlobList)
752                 {
753                     int j;
754                     for(j=pOldBlobList->GetBlobNum(); j>0; j--)
755                     {
756                         CvBlob* pBOld = pOldBlobList->GetBlob(j-1);
757                         if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) &&
758                            (fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB))))
759                         {   /* Intersection detected, delete blob from list: */
760                             Blobs.DelBlob(i-1);
761                             break;
762                         }
763                     }   /* Check next old blob. */
764                 }   /*  if pOldBlobList. */
765             }   /*  Check next blob. */
766         }   /*  Delete small and intersected blobs. */
767
768         {   /* Bubble-sort blobs by size: */
769             int N = Blobs.GetBlobNum();
770             int i,j;
771             for(i=1; i<N; ++i)
772             {
773                 for(j=i; j>0; --j)
774                 {
775                     CvBlob  temp;
776                     float   AreaP, AreaN;
777                     CvBlob* pP = Blobs.GetBlob(j-1);
778                     CvBlob* pN = Blobs.GetBlob(j);
779                     AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP);
780                     AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN);
781                     if(AreaN < AreaP)break;
782                     temp = pN[0];
783                     pN[0] = pP[0];
784                     pP[0] = temp;
785                 }
786             }
787
788             /* Copy only first 10 blobs: */
789             for(i=0; i<MIN(N,10); ++i)
790             {
791                 m_pBlobLists[0]->AddBlob(Blobs.GetBlob(i));
792             }
793
794         }   /* Sort blobs by size. */
795
796         cvReleaseMemStorage(&storage);
797
798     }   /* Create blobs. */
799
800     {   /* Shift each track: */
801         int j;
802         for(j=0; j<m_TrackNum; ++j)
803         {
804             int     i;
805             DefSeq* pTrack = m_TrackSeq+j;
806
807             for(i=SEQ_SIZE-1; i>0; --i)
808                 pTrack->pBlobs[i] = pTrack->pBlobs[i-1];
809
810             pTrack->pBlobs[0] = NULL;
811             if(pTrack->size == SEQ_SIZE)pTrack->size--;
812         }
813     }   /* Shift each track. */
814
815     /* Analyze blob list to find best blob trajectory: */
816     {
817         double      BestError = -1;
818         int         BestTrack = -1;;
819         CvBlobSeq*  pNewBlobs = m_pBlobLists[0];
820         int         i;
821         int         NewTrackNum = 0;
822         for(i=pNewBlobs->GetBlobNum(); i>0; --i)
823         {
824             CvBlob* pBNew = pNewBlobs->GetBlob(i-1);
825             int     j;
826             int     AsignedTrack = 0;
827             for(j=0; j<m_TrackNum; ++j)
828             {
829                 double  dx,dy;
830                 DefSeq* pTrack = m_TrackSeq+j;
831                 CvBlob* pLastBlob = pTrack->size>0?pTrack->pBlobs[1]:NULL;
832                 if(pLastBlob == NULL) continue;
833                 dx = fabs(CV_BLOB_X(pLastBlob)-CV_BLOB_X(pBNew));
834                 dy = fabs(CV_BLOB_Y(pLastBlob)-CV_BLOB_Y(pBNew));
835                 if(dx > 2*CV_BLOB_WX(pLastBlob) || dy > 2*CV_BLOB_WY(pLastBlob)) continue;
836                 AsignedTrack++;
837
838                 if(pTrack->pBlobs[0]==NULL)
839                 {   /* Fill existed track: */
840                     pTrack->pBlobs[0] = pBNew;
841                     pTrack->size++;
842                 }
843                 else if((m_TrackNum+NewTrackNum)<SEQ_NUM)
844                 {   /* Duplicate existed track: */
845                     m_TrackSeq[m_TrackNum+NewTrackNum] = pTrack[0];
846                     m_TrackSeq[m_TrackNum+NewTrackNum].pBlobs[0] = pBNew;
847                     NewTrackNum++;
848                 }
849             }   /* Next track. */
850
851             if(AsignedTrack==0 && (m_TrackNum+NewTrackNum)<SEQ_NUM )
852             {   /* Initialize new track: */
853                 m_TrackSeq[m_TrackNum+NewTrackNum].size = 1;
854                 m_TrackSeq[m_TrackNum+NewTrackNum].pBlobs[0] = pBNew;
855                 NewTrackNum++;
856             }
857         }   /* Next new blob. */
858
859         m_TrackNum += NewTrackNum;
860
861         /* Check each track: */
862         for(i=0; i<m_TrackNum; ++i)
863         {
864             int     Good = 1;
865             DefSeq* pTrack = m_TrackSeq+i;
866             CvBlob* pBNew = pTrack->pBlobs[0];
867             if(pTrack->size != SEQ_SIZE) continue;
868             if(pBNew == NULL ) continue;
869             
870             /* Check intersection last blob with existed: */
871             if(Good && pOldBlobList)
872             {
873                 int k;
874                 for(k=pOldBlobList->GetBlobNum(); k>0; --k)
875                 {
876                     CvBlob* pBOld = pOldBlobList->GetBlob(k-1);
877                     if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) &&
878                        (fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew))))
879                         Good = 0;
880                 }
881             }   /* Check intersection last blob with existed. */
882
883             /* Check distance to image border: */
884             if(Good)
885             {   /* Check distance to image border: */
886                 float    dx = MIN(pBNew->x,S.width-pBNew->x)/CV_BLOB_RX(pBNew);
887                 float    dy = MIN(pBNew->y,S.height-pBNew->y)/CV_BLOB_RY(pBNew);
888                 if(dx < m_MinDistToBorder || dy < m_MinDistToBorder) Good = 0;
889             }   /* Check distance to image border. */
890
891             /* Check uniform motion: */
892             if(Good)
893             {   /* Check uniform motion: */
894                 double      Error = 0;
895                 int         N = pTrack->size;
896                 CvBlob**    pBL = pTrack->pBlobs;
897                 float       sum[2] = {0,0};   
898                 float       jsum[2] = {0,0};
899                 float       a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/
900                 int         j;
901                 
902                 for(j=0; j<N; ++j)
903                 {
904                     float   x = pBL[j]->x;
905                     float   y = pBL[j]->y;
906                     sum[0] += x;
907                     jsum[0] += j*x;
908                     sum[1] += y;
909                     jsum[1] += j*y;
910                 }
911
912                 a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1));
913                 b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1));
914                 a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1));
915                 b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1));
916
917                 for(j=0; j<N; ++j)
918                 {
919                     Error += 
920                         pow(a[0]*j+b[0]-pBL[j]->x,2)+
921                         pow(a[1]*j+b[1]-pBL[j]->y,2);
922                 }
923
924                 Error = sqrt(Error/N);
925
926                 if( Error > S.width*0.01 || 
927                     fabs(a[0])>S.width*0.1 ||
928                     fabs(a[1])>S.height*0.1)
929                     Good = 0;
930                 
931                 /* New best trajectory: */
932                 if(Good && (BestError == -1 || BestError > Error))
933                 {   /* New best trajectory: */
934                     BestTrack = i;
935                     BestError = Error;
936                 }   /* New best trajectory. */
937             }   /*  Check uniform motion. */
938         }   /*  Next track. */
939
940         #if 0
941         {   /**/
942             printf("BlobDetector configurations = %d [",m_TrackNum);
943             int i;
944             for(i=0; i<SEQ_SIZE; ++i)
945             {
946                 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
947             }
948             printf("]\n");
949         }
950         #endif
951
952         if(BestTrack >= 0)
953         {   /* Put new blob to output and delete from blob list: */
954             assert(m_TrackSeq[BestTrack].size == SEQ_SIZE);
955             assert(m_TrackSeq[BestTrack].pBlobs[0]);
956             pNewBlobList->AddBlob(m_TrackSeq[BestTrack].pBlobs[0]);
957             m_TrackSeq[BestTrack].pBlobs[0] = NULL;
958             m_TrackSeq[BestTrack].size--;
959             result = 1;
960         }   /* Put new blob to output and mark in blob list to delete. */
961     }   /*  Analyze blod list to find best blob trajectory. */
962
963     {   /* Delete bad tracks: */
964         int i;
965         for(i=m_TrackNum-1; i>=0; --i)
966         {   /* Delete bad tracks: */
967             if(m_TrackSeq[i].pBlobs[0]) continue;
968             if(m_TrackNum>0)
969                 m_TrackSeq[i] = m_TrackSeq[--m_TrackNum];
970         }   /* Delete bad tracks: */
971     }
972
973 #ifdef USE_OBJECT_DETECTOR
974     if( m_split_detector && pNewBlobList->GetBlobNum() > 0 )
975     {
976         int num_new_blobs = pNewBlobList->GetBlobNum();
977         int i = 0;
978
979         if( m_roi_seq ) cvClearSeq( m_roi_seq );
980         m_debug_blob_seq.Clear();
981         for( i = 0; i < num_new_blobs; ++i )
982         {
983             CvBlob* b = pNewBlobList->GetBlob(i);
984             CvMat roi_stub;
985             CvMat* roi_mat = 0;
986             CvMat* scaled_roi_mat = 0;
987
988             CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 0 );
989             m_debug_blob_seq.AddBlob(&d_b);
990
991             float scale = m_param_roi_scale * m_min_window_size.height / CV_BLOB_WY(b);
992             
993             float b_width =   MAX(CV_BLOB_WX(b), m_min_window_size.width / scale)
994                             + (m_param_roi_scale - 1.0F) * (m_min_window_size.width / scale)
995                             + 2.0F * m_max_border / scale;
996             float b_height = CV_BLOB_WY(b) * m_param_roi_scale + 2.0F * m_max_border / scale;
997
998             CvRect roi = cvRectIntersection( cvRect( cvFloor(CV_BLOB_X(b) - 0.5F*b_width),
999                                                      cvFloor(CV_BLOB_Y(b) - 0.5F*b_height),
1000                                                      cvCeil(b_width), cvCeil(b_height) ),
1001                                              cvRect( 0, 0, pImg->width, pImg->height ) );
1002             if( roi.width <= 0 || roi.height <= 0 )
1003                 continue;
1004
1005             if( m_roi_seq ) cvSeqPush( m_roi_seq, &roi );
1006
1007             roi_mat = cvGetSubRect( pImg, &roi_stub, roi );
1008             scaled_roi_mat = cvCreateMat( cvCeil(scale*roi.height), cvCeil(scale*roi.width), CV_8UC3 );
1009             cvResize( roi_mat, scaled_roi_mat );
1010
1011             m_detected_blob_seq.Clear();
1012             m_split_detector->Detect( scaled_roi_mat, &m_detected_blob_seq );
1013             cvReleaseMat( &scaled_roi_mat );
1014
1015             for( int k = 0; k < m_detected_blob_seq.GetBlobNum(); ++k )
1016             {
1017                 CvDetectedBlob* b = (CvDetectedBlob*) m_detected_blob_seq.GetBlob(k);
1018
1019                 /* scale and shift each detected blob back to the original image coordinates */
1020                 CV_BLOB_X(b) = CV_BLOB_X(b) / scale + roi.x;
1021                 CV_BLOB_Y(b) = CV_BLOB_Y(b) / scale + roi.y;
1022                 CV_BLOB_WX(b) /= scale;
1023                 CV_BLOB_WY(b) /= scale;
1024
1025                 CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 1,
1026                         b->response );
1027                 m_debug_blob_seq.AddBlob(&d_b);
1028             }
1029             
1030             if( m_detected_blob_seq.GetBlobNum() > 1 )
1031             {
1032                 /*
1033                  * Split blob.
1034                  * The original blob is replaced by the first detected blob,
1035                  * remaining detected blobs are added to the end of the sequence:
1036                  */
1037                 CvBlob* first_b = m_detected_blob_seq.GetBlob(0);
1038                 CV_BLOB_X(b)  = CV_BLOB_X(first_b);  CV_BLOB_Y(b)  = CV_BLOB_Y(first_b);
1039                 CV_BLOB_WX(b) = CV_BLOB_WX(first_b); CV_BLOB_WY(b) = CV_BLOB_WY(first_b);
1040
1041                 for( int j = 1; j < m_detected_blob_seq.GetBlobNum(); ++j )
1042                 {
1043                     CvBlob* detected_b = m_detected_blob_seq.GetBlob(j);
1044                     pNewBlobList->AddBlob(detected_b);
1045                 }
1046             }
1047         }   /* For each new blob. */
1048
1049         for( i = 0; i < pNewBlobList->GetBlobNum(); ++i )
1050         {
1051             CvBlob* b = pNewBlobList->GetBlob(i);
1052             CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 2 );
1053             m_debug_blob_seq.AddBlob(&d_b);
1054         }
1055     }   // if( m_split_detector )
1056 #endif
1057
1058     return result;
1059
1060 }   /* cvDetectNewBlob */
1061
1062