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