1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
12 // Copyright (C) 2000, Intel Corporation, all rights reserved.
13 // Third party copyrights are property of their respective owners.
15 // Redistribution and use in source and binary forms, with or without modification,
16 // are permitted provided that the following conditions are met:
18 // * Redistribution's of source code must retain the above copyright notice,
19 // this list of conditions and the following disclaimer.
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.
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.
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.
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.
48 For an entrypoint into the literature see:
50 Appearance Models for Occlusion Handling
51 Andrew Senior &t al, 8p 2001
52 http://www.research.ibm.com/peoplevision/PETS2001.pdf
56 //#define USE_OBJECT_DETECTOR
60 static int CompareContour(const void* a, const void* b, void* )
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;
77 dx = (float)(fabs(pa.x - pb.x)-w);
78 dy = (float)(fabs(pa.y - pb.y)-h);
80 //wt = MAX(ra.width,rb.width)*0.1f;
82 ht = MAX(ra.height,rb.height)*0.3f;
83 return (dx < wt && dy < ht);
86 void cvFindBlobsByCCClasters(IplImage* pFG, CvBlobSeq* pBlobs, CvMemStorage* storage)
87 { /* Create contours: */
90 CvSeq* cnt_list = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvSeq*), storage );
91 CvSeq* clasters = NULL;
92 int claster_cur, claster_num;
94 pIB = cvCloneImage(pFG);
95 cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
96 cvFindContours(pIB,storage, &cnt, sizeof(CvContour), CV_RETR_EXTERNAL);
99 /* Create cnt_list. */
100 /* Process each contour: */
101 for(; cnt; cnt=cnt->h_next)
103 cvSeqPush( cnt_list, &cnt);
106 claster_num = cvSeqPartition( cnt_list, storage, &clasters, CompareContour, NULL );
108 for(claster_cur=0; claster_cur<claster_num; ++claster_cur)
112 double M00,X,Y,XX,YY; /* image moments */
114 CvRect rect_res = cvRect(-1,-1,-1,-1);
117 for(cnt_cur=0; cnt_cur<clasters->total; ++cnt_cur)
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;
126 if(rect_res.height<0)
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);
139 rect_res.width = x1-x0;
140 rect_res.height = y1-y0;
144 if(rect_res.height < 1 || rect_res.width < 1)
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;
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);
164 } /* Next cluster. */
168 IplImage* pI = cvCreateImage(cvSize(pFG->width,pFG->height),IPL_DEPTH_8U,3);
170 for(claster_cur=0; claster_cur<claster_num; ++claster_cur)
173 CvScalar color = CV_RGB(rand()%256,rand()%256,rand()%256);
175 for(cnt_cur=0; cnt_cur<clasters->total; ++cnt_cur)
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);
184 CvBlob* pB = pBlobs->GetBlob(claster_cur);
185 int x = cvRound(CV_BLOB_RX(pB)), y = cvRound(CV_BLOB_RY(pB));
187 cvPointFrom32f(CV_BLOB_CENTER(pB)),
188 cvSize(MAX(1,x), MAX(1,y)),
193 cvNamedWindow( "Clusters", 0);
194 cvShowImage( "Clusters",pI );
201 } /* cvFindBlobsByCCClasters */
203 /* Simple blob detector. */
204 /* Number of successive frame to analyse: */
205 #define EBD_FRAME_NUM 5
206 class CvBlobDetectorSimple:public CvBlobDetector
209 CvBlobDetectorSimple();
210 ~CvBlobDetectorSimple();
211 int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
212 void Release(){delete this;};
215 IplImage* m_pMaskBlobNew;
216 IplImage* m_pMaskBlobExist;
217 /* Lists of connected components detected on previous frames: */
218 CvBlobSeq* m_pBlobLists[EBD_FRAME_NUM];
221 /* Blob detector creator (sole interface function for this file) */
222 CvBlobDetector* cvCreateBlobDetectorSimple(){return new CvBlobDetectorSimple;};
224 /* Constructor of BlobDetector: */
225 CvBlobDetectorSimple::CvBlobDetectorSimple()
228 m_pMaskBlobNew = NULL;
229 m_pMaskBlobExist = NULL;
230 for(i=0;i<EBD_FRAME_NUM;++i)m_pBlobLists[i] = NULL;
232 SetModuleName("Simple");
235 /* Destructor of BlobDetector: */
236 CvBlobDetectorSimple::~CvBlobDetectorSimple()
239 if(m_pMaskBlobExist) cvReleaseImage(&m_pMaskBlobExist);
240 if(m_pMaskBlobNew) cvReleaseImage(&m_pMaskBlobNew);
241 for(i=0; i<EBD_FRAME_NUM; ++i) delete m_pBlobLists[i];
242 } /* cvReleaseBlobDetector */
245 * return 1 and fill blob pNewBlob by blob parameters
246 * if new blob is detected:
248 int CvBlobDetectorSimple::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
251 CvSize S = cvSize(pFGMask->width,pFGMask->height);
252 if(m_pMaskBlobNew == NULL ) m_pMaskBlobNew = cvCreateImage(S,IPL_DEPTH_8U,1);
253 if(m_pMaskBlobExist == NULL ) m_pMaskBlobExist = cvCreateImage(S,IPL_DEPTH_8U,1);
255 /* Shift blob list: */
258 if(m_pBlobLists[0]) delete m_pBlobLists[0];
259 for(i=1;i<EBD_FRAME_NUM;++i)m_pBlobLists[i-1]=m_pBlobLists[i];
260 m_pBlobLists[EBD_FRAME_NUM-1] = new CvBlobSeq;
261 } /* Shift blob list. */
263 /* Create exist blob mask: */
264 cvCopy(pFGMask, m_pMaskBlobNew);
266 /* Create contours and add new blobs to blob list: */
267 { /* Create blobs: */
269 CvMemStorage* storage = cvCreateMemStorage();
272 { /* Glue contours: */
273 cvFindBlobsByCCClasters(m_pMaskBlobNew, &Blobs, storage );
274 } /* Glue contours. */
277 IplImage* pIB = cvCloneImage(m_pMaskBlobNew);
280 cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
281 cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
283 /* Process each contour: */
284 for(cnt = cnts; cnt; cnt=cnt->h_next)
289 double M00,X,Y,XX,YY;
291 CvRect r = ((CvContour*)cnt)->rect;
294 if(r.height < S.height*0.02 || r.width < S.width*0.02) continue;
296 cvMoments( cvGetSubRect(m_pMaskBlobNew,&mat,r), &m, 0 );
297 M00 = cvGetSpatialMoment( &m, 0, 0 );
299 if(M00 <= 0 ) continue;
301 X = cvGetSpatialMoment( &m, 1, 0 )/M00;
302 Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
304 XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
305 YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
307 NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
309 Blobs.AddBlob(&NewBlob);
311 } /* Next contour. */
313 cvReleaseImage(&pIB);
315 } /* One contour - one blob. */
318 { /* Delete small and intersected blobs: */
320 for(i=Blobs.GetBlobNum(); i>0; i--)
322 CvBlob* pB = Blobs.GetBlob(i-1);
324 if(pB->h < S.height*0.02 || pB->w < S.width*0.02)
332 for(j=pOldBlobList->GetBlobNum(); j>0; j--)
334 CvBlob* pBOld = pOldBlobList->GetBlob(j-1);
335 if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) &&
336 (fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB))))
337 { /* Intersection is present, so delete blob from list: */
341 } /* Check next old blob. */
342 } /* if pOldBlobList */
343 } /* Check next blob. */
344 } /* Delete small and intersected blobs. */
346 { /* Bubble-sort blobs by size: */
347 int N = Blobs.GetBlobNum();
355 CvBlob* pP = Blobs.GetBlob(j-1);
356 CvBlob* pN = Blobs.GetBlob(j);
357 AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP);
358 AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN);
359 if(AreaN < AreaP)break;
366 /* Copy only first 10 blobs: */
367 for(i=0; i<MIN(N,10); ++i)
369 m_pBlobLists[EBD_FRAME_NUM-1]->AddBlob(Blobs.GetBlob(i));
372 } /* Sort blobs by size. */
374 cvReleaseMemStorage(&storage);
376 } /* Create blobs. */
378 /* Analyze blob list to find best blob trajectory: */
381 int pBLIndex[EBD_FRAME_NUM];
382 int pBL_BEST[EBD_FRAME_NUM];
385 double BestError = -1;
388 for(i=0; i<EBD_FRAME_NUM; ++i)
394 /* Check configuration exist: */
395 for(i=0; Good && (i<EBD_FRAME_NUM); ++i)
396 if(m_pBlobLists[i] == NULL || m_pBlobLists[i]->GetBlobNum() == 0)
400 do{ /* For each configuration: */
401 CvBlob* pBL[EBD_FRAME_NUM];
404 CvBlob* pBNew = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBLIndex[EBD_FRAME_NUM-1]);
406 for(i=0; i<EBD_FRAME_NUM; ++i) pBL[i] = m_pBlobLists[i]->GetBlob(pBLIndex[i]);
410 /* Check intersection last blob with existed: */
411 if(Good && pOldBlobList)
412 { /* Check intersection last blob with existed: */
414 for(k=pOldBlobList->GetBlobNum(); k>0; --k)
416 CvBlob* pBOld = pOldBlobList->GetBlob(k-1);
417 if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) &&
418 (fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew))))
421 } /* Check intersection last blob with existed. */
423 /* Check distance to image border: */
425 { /* Check distance to image border: */
427 float dx = MIN(pB->x,S.width-pB->x)/CV_BLOB_RX(pB);
428 float dy = MIN(pB->y,S.height-pB->y)/CV_BLOB_RY(pB);
430 if(dx < 1.1 || dy < 1.1) Good = 0;
431 } /* Check distance to image border. */
433 /* Check uniform motion: */
436 int N = EBD_FRAME_NUM;
437 float sum[2] = {0,0};
438 float jsum[2] = {0,0};
439 float a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/
452 a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1));
453 b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1));
454 a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1));
455 b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1));
460 pow(a[0]*j+b[0]-pBL[j]->x,2)+
461 pow(a[1]*j+b[1]-pBL[j]->y,2);
464 Error = sqrt(Error/N);
466 if( Error > S.width*0.01 ||
467 fabs(a[0])>S.width*0.1 ||
468 fabs(a[1])>S.height*0.1)
471 } /* Check configuration. */
474 /* New best trajectory: */
475 if(Good && (BestError == -1 || BestError > Error))
477 for(i=0; i<EBD_FRAME_NUM; ++i)
479 pBL_BEST[i] = pBLIndex[i];
482 } /* New best trajectory. */
484 /* Set next configuration: */
485 for(i=0; i<EBD_FRAME_NUM; ++i)
488 if(pBLIndex[i] != m_pBlobLists[i]->GetBlobNum()) break;
490 } /* Next time shift. */
492 if(i==EBD_FRAME_NUM)finish=1;
494 } while(!finish); /* Check next time configuration of connected components. */
498 printf("BlobDetector configurations = %d [",Count);
500 for(i=0; i<EBD_FRAME_NUM; ++i)
502 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
510 { /* Write new blob to output and delete from blob list: */
511 CvBlob* pNewBlob = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBL_BEST[EBD_FRAME_NUM-1]);
512 pNewBlobList->AddBlob(pNewBlob);
514 for(i=0; i<EBD_FRAME_NUM; ++i)
515 { /* Remove blob from each list: */
516 m_pBlobLists[i]->DelBlob(pBL_BEST[i]);
517 } /* Remove blob from each list. */
521 } /* Write new blob to output and delete from blob list. */
522 } /* Analyze blob list to find best blob trajectory. */
526 } /* cvDetectNewBlob */
531 /* Simple blob detector2. */
532 /* Number of successive frames to analyse: */
533 #define SEQ_SIZE_MAX 30
538 CvBlob* pBlobs[SEQ_SIZE_MAX];
541 class CvBlobDetectorCC:public CvBlobDetector
546 int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
547 void Release(){delete this;};
549 virtual void ParamUpdate()
551 if(SEQ_SIZE<1)SEQ_SIZE=1;
552 if(SEQ_SIZE>SEQ_SIZE_MAX)SEQ_SIZE=SEQ_SIZE_MAX;
554 #ifdef USE_OBJECT_DETECTOR
555 if( m_param_split_detector_file_name )
557 m_split_detector = new CvObjectDetector();
558 if( !m_split_detector->Load( m_param_split_detector_file_name ) )
560 delete m_split_detector;
561 m_split_detector = 0;
565 m_min_window_size = m_split_detector->GetMinWindowSize();
566 m_max_border = m_split_detector->GetMaxBorderSize();
573 /* Lists of connected components detected on previous frames: */
574 CvBlobSeq* m_pBlobLists[SEQ_SIZE_MAX];
575 DefSeq m_TrackSeq[SEQ_NUM];
579 float m_MinDistToBorder;
583 /* If not 0 then the detector is loaded from the specified file
584 * and it is applied for splitting blobs which actually correspond
585 * to groups of objects:
587 char* m_param_split_detector_file_name;
588 float m_param_roi_scale;
589 int m_param_only_roi;
591 CvObjectDetector* m_split_detector;
592 CvSize m_min_window_size;
595 CvBlobSeq m_detected_blob_seq;
598 CvBlobSeq m_debug_blob_seq;
601 /* Blob detector creator (sole interface function for this file): */
602 CvBlobDetector* cvCreateBlobDetectorCC(){return new CvBlobDetectorCC;}
604 /* Constructor for BlobDetector: */
605 CvBlobDetectorCC::CvBlobDetectorCC() :
607 m_detected_blob_seq(sizeof(CvDetectedBlob)),
609 m_debug_blob_seq(sizeof(CvDetectedBlob))
611 /*CvDrawShape shapes[] =
613 { CvDrawShape::RECT, {{255,255,255}} },
614 { CvDrawShape::RECT, {{0,0,255}} },
615 { CvDrawShape::ELLIPSE, {{0,255,0}} }
617 int num_shapes = sizeof(shapes) / sizeof(shapes[0]);*/
621 AddParam("Latency",&SEQ_SIZE);
622 for(i=0;i<SEQ_SIZE_MAX;++i)m_pBlobLists[i] = NULL;
623 for(i=0;i<SEQ_NUM;++i)m_TrackSeq[i].size = 0;
628 AddParam("HMin",&m_HMin);
629 AddParam("WMin",&m_WMin);
630 m_MinDistToBorder = 1.1f;
631 AddParam("MinDistToBorder",&m_MinDistToBorder);
632 CommentParam("MinDistToBorder","Minimal allowed distance from blob center to image border in blob sizes");
635 AddParam("Clastering",&m_Clastering);
636 CommentParam("Clastering","Minimal allowed distance from blob center to image border in blob sizes");
638 m_param_split_detector_file_name = 0;
639 #ifdef USE_OBJECT_DETECTOR
640 AddParam("Detector", &m_param_split_detector_file_name);
641 CommentParam("Detector", "Detector file name");
644 m_param_roi_scale = 1.5F;
645 AddParam("ROIScale", &m_param_roi_scale);
646 CommentParam("ROIScale", "Determines the size of search window around a blob");
648 m_param_only_roi = 1;
649 AddParam("OnlyROI", &m_param_only_roi);
650 CommentParam("OnlyROI", "Shows the whole debug image (0) or only ROIs where the detector was applied (1)");
652 m_min_window_size = cvSize(0,0);
654 m_roi_seq = cvCreateSeq( 0, sizeof(*m_roi_seq), sizeof(CvRect), cvCreateMemStorage() );
659 /* Destructor for BlobDetector: */
660 CvBlobDetectorCC::~CvBlobDetectorCC()
663 for(i=0; i<SEQ_SIZE_MAX; ++i)
666 delete m_pBlobLists[i];
671 cvReleaseMemStorage( &m_roi_seq->storage );
674 //cvDestroyWindow( "EnteringBlobDetectionDebug" );
675 } /* cvReleaseBlobDetector */
679 * Return 1 and fill blob pNewBlob with
680 * blob parameters if new blob is detected:
682 int CvBlobDetectorCC::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
685 CvSize S = cvSize(pFGMask->width,pFGMask->height);
687 /* Shift blob list: */
690 if(m_pBlobLists[SEQ_SIZE-1]) delete m_pBlobLists[SEQ_SIZE-1];
692 for(i=SEQ_SIZE-1; i>0; --i) m_pBlobLists[i] = m_pBlobLists[i-1];
694 m_pBlobLists[0] = new CvBlobSeq;
696 } /* Shift blob list. */
698 /* Create contours and add new blobs to blob list: */
699 { /* Create blobs: */
701 CvMemStorage* storage = cvCreateMemStorage();
704 { /* Glue contours: */
705 cvFindBlobsByCCClasters(pFGMask, &Blobs, storage );
706 } /* Glue contours. */
709 IplImage* pIB = cvCloneImage(pFGMask);
712 cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
713 cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
715 /* Process each contour: */
716 for(cnt = cnts; cnt; cnt=cnt->h_next)
720 double M00,X,Y,XX,YY;
722 CvRect r = ((CvContour*)cnt)->rect;
724 if(r.height < S.height*m_HMin || r.width < S.width*m_WMin) continue;
725 cvMoments( cvGetSubRect(pFGMask,&mat,r), &m, 0 );
726 M00 = cvGetSpatialMoment( &m, 0, 0 );
727 if(M00 <= 0 ) continue;
728 X = cvGetSpatialMoment( &m, 1, 0 )/M00;
729 Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
730 XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
731 YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
732 NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
733 Blobs.AddBlob(&NewBlob);
735 } /* Next contour. */
737 cvReleaseImage(&pIB);
739 } /* One contour - one blob. */
741 { /* Delete small and intersected blobs: */
743 for(i=Blobs.GetBlobNum(); i>0; i--)
745 CvBlob* pB = Blobs.GetBlob(i-1);
747 if(pB->h < S.height*m_HMin || pB->w < S.width*m_WMin)
756 for(j=pOldBlobList->GetBlobNum(); j>0; j--)
758 CvBlob* pBOld = pOldBlobList->GetBlob(j-1);
759 if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) &&
760 (fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB))))
761 { /* Intersection detected, delete blob from list: */
765 } /* Check next old blob. */
766 } /* if pOldBlobList. */
767 } /* Check next blob. */
768 } /* Delete small and intersected blobs. */
770 { /* Bubble-sort blobs by size: */
771 int N = Blobs.GetBlobNum();
779 CvBlob* pP = Blobs.GetBlob(j-1);
780 CvBlob* pN = Blobs.GetBlob(j);
781 AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP);
782 AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN);
783 if(AreaN < AreaP)break;
790 /* Copy only first 10 blobs: */
791 for(i=0; i<MIN(N,10); ++i)
793 m_pBlobLists[0]->AddBlob(Blobs.GetBlob(i));
796 } /* Sort blobs by size. */
798 cvReleaseMemStorage(&storage);
800 } /* Create blobs. */
802 { /* Shift each track: */
804 for(j=0; j<m_TrackNum; ++j)
807 DefSeq* pTrack = m_TrackSeq+j;
809 for(i=SEQ_SIZE-1; i>0; --i)
810 pTrack->pBlobs[i] = pTrack->pBlobs[i-1];
812 pTrack->pBlobs[0] = NULL;
813 if(pTrack->size == SEQ_SIZE)pTrack->size--;
815 } /* Shift each track. */
817 /* Analyze blob list to find best blob trajectory: */
819 double BestError = -1;
821 CvBlobSeq* pNewBlobs = m_pBlobLists[0];
824 for(i=pNewBlobs->GetBlobNum(); i>0; --i)
826 CvBlob* pBNew = pNewBlobs->GetBlob(i-1);
828 int AsignedTrack = 0;
829 for(j=0; j<m_TrackNum; ++j)
832 DefSeq* pTrack = m_TrackSeq+j;
833 CvBlob* pLastBlob = pTrack->size>0?pTrack->pBlobs[1]:NULL;
834 if(pLastBlob == NULL) continue;
835 dx = fabs(CV_BLOB_X(pLastBlob)-CV_BLOB_X(pBNew));
836 dy = fabs(CV_BLOB_Y(pLastBlob)-CV_BLOB_Y(pBNew));
837 if(dx > 2*CV_BLOB_WX(pLastBlob) || dy > 2*CV_BLOB_WY(pLastBlob)) continue;
840 if(pTrack->pBlobs[0]==NULL)
841 { /* Fill existed track: */
842 pTrack->pBlobs[0] = pBNew;
845 else if((m_TrackNum+NewTrackNum)<SEQ_NUM)
846 { /* Duplicate existed track: */
847 m_TrackSeq[m_TrackNum+NewTrackNum] = pTrack[0];
848 m_TrackSeq[m_TrackNum+NewTrackNum].pBlobs[0] = pBNew;
853 if(AsignedTrack==0 && (m_TrackNum+NewTrackNum)<SEQ_NUM )
854 { /* Initialize new track: */
855 m_TrackSeq[m_TrackNum+NewTrackNum].size = 1;
856 m_TrackSeq[m_TrackNum+NewTrackNum].pBlobs[0] = pBNew;
859 } /* Next new blob. */
861 m_TrackNum += NewTrackNum;
863 /* Check each track: */
864 for(i=0; i<m_TrackNum; ++i)
867 DefSeq* pTrack = m_TrackSeq+i;
868 CvBlob* pBNew = pTrack->pBlobs[0];
869 if(pTrack->size != SEQ_SIZE) continue;
870 if(pBNew == NULL ) continue;
872 /* Check intersection last blob with existed: */
873 if(Good && pOldBlobList)
876 for(k=pOldBlobList->GetBlobNum(); k>0; --k)
878 CvBlob* pBOld = pOldBlobList->GetBlob(k-1);
879 if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) &&
880 (fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew))))
883 } /* Check intersection last blob with existed. */
885 /* Check distance to image border: */
887 { /* Check distance to image border: */
888 float dx = MIN(pBNew->x,S.width-pBNew->x)/CV_BLOB_RX(pBNew);
889 float dy = MIN(pBNew->y,S.height-pBNew->y)/CV_BLOB_RY(pBNew);
890 if(dx < m_MinDistToBorder || dy < m_MinDistToBorder) Good = 0;
891 } /* Check distance to image border. */
893 /* Check uniform motion: */
895 { /* Check uniform motion: */
897 int N = pTrack->size;
898 CvBlob** pBL = pTrack->pBlobs;
899 float sum[2] = {0,0};
900 float jsum[2] = {0,0};
901 float a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/
914 a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1));
915 b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1));
916 a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1));
917 b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1));
922 pow(a[0]*j+b[0]-pBL[j]->x,2)+
923 pow(a[1]*j+b[1]-pBL[j]->y,2);
926 Error = sqrt(Error/N);
928 if( Error > S.width*0.01 ||
929 fabs(a[0])>S.width*0.1 ||
930 fabs(a[1])>S.height*0.1)
933 /* New best trajectory: */
934 if(Good && (BestError == -1 || BestError > Error))
935 { /* New best trajectory: */
938 } /* New best trajectory. */
939 } /* Check uniform motion. */
944 printf("BlobDetector configurations = %d [",m_TrackNum);
946 for(i=0; i<SEQ_SIZE; ++i)
948 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
955 { /* Put new blob to output and delete from blob list: */
956 assert(m_TrackSeq[BestTrack].size == SEQ_SIZE);
957 assert(m_TrackSeq[BestTrack].pBlobs[0]);
958 pNewBlobList->AddBlob(m_TrackSeq[BestTrack].pBlobs[0]);
959 m_TrackSeq[BestTrack].pBlobs[0] = NULL;
960 m_TrackSeq[BestTrack].size--;
962 } /* Put new blob to output and mark in blob list to delete. */
963 } /* Analyze blod list to find best blob trajectory. */
965 { /* Delete bad tracks: */
967 for(i=m_TrackNum-1; i>=0; --i)
968 { /* Delete bad tracks: */
969 if(m_TrackSeq[i].pBlobs[0]) continue;
971 m_TrackSeq[i] = m_TrackSeq[--m_TrackNum];
972 } /* Delete bad tracks: */
975 #ifdef USE_OBJECT_DETECTOR
976 if( m_split_detector && pNewBlobList->GetBlobNum() > 0 )
978 int num_new_blobs = pNewBlobList->GetBlobNum();
981 if( m_roi_seq ) cvClearSeq( m_roi_seq );
982 m_debug_blob_seq.Clear();
983 for( i = 0; i < num_new_blobs; ++i )
985 CvBlob* b = pNewBlobList->GetBlob(i);
988 CvMat* scaled_roi_mat = 0;
990 CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 0 );
991 m_debug_blob_seq.AddBlob(&d_b);
993 float scale = m_param_roi_scale * m_min_window_size.height / CV_BLOB_WY(b);
995 float b_width = MAX(CV_BLOB_WX(b), m_min_window_size.width / scale)
996 + (m_param_roi_scale - 1.0F) * (m_min_window_size.width / scale)
997 + 2.0F * m_max_border / scale;
998 float b_height = CV_BLOB_WY(b) * m_param_roi_scale + 2.0F * m_max_border / scale;
1000 CvRect roi = cvRectIntersection( cvRect( cvFloor(CV_BLOB_X(b) - 0.5F*b_width),
1001 cvFloor(CV_BLOB_Y(b) - 0.5F*b_height),
1002 cvCeil(b_width), cvCeil(b_height) ),
1003 cvRect( 0, 0, pImg->width, pImg->height ) );
1004 if( roi.width <= 0 || roi.height <= 0 )
1007 if( m_roi_seq ) cvSeqPush( m_roi_seq, &roi );
1009 roi_mat = cvGetSubRect( pImg, &roi_stub, roi );
1010 scaled_roi_mat = cvCreateMat( cvCeil(scale*roi.height), cvCeil(scale*roi.width), CV_8UC3 );
1011 cvResize( roi_mat, scaled_roi_mat );
1013 m_detected_blob_seq.Clear();
1014 m_split_detector->Detect( scaled_roi_mat, &m_detected_blob_seq );
1015 cvReleaseMat( &scaled_roi_mat );
1017 for( int k = 0; k < m_detected_blob_seq.GetBlobNum(); ++k )
1019 CvDetectedBlob* b = (CvDetectedBlob*) m_detected_blob_seq.GetBlob(k);
1021 /* scale and shift each detected blob back to the original image coordinates */
1022 CV_BLOB_X(b) = CV_BLOB_X(b) / scale + roi.x;
1023 CV_BLOB_Y(b) = CV_BLOB_Y(b) / scale + roi.y;
1024 CV_BLOB_WX(b) /= scale;
1025 CV_BLOB_WY(b) /= scale;
1027 CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 1,
1029 m_debug_blob_seq.AddBlob(&d_b);
1032 if( m_detected_blob_seq.GetBlobNum() > 1 )
1036 * The original blob is replaced by the first detected blob,
1037 * remaining detected blobs are added to the end of the sequence:
1039 CvBlob* first_b = m_detected_blob_seq.GetBlob(0);
1040 CV_BLOB_X(b) = CV_BLOB_X(first_b); CV_BLOB_Y(b) = CV_BLOB_Y(first_b);
1041 CV_BLOB_WX(b) = CV_BLOB_WX(first_b); CV_BLOB_WY(b) = CV_BLOB_WY(first_b);
1043 for( int j = 1; j < m_detected_blob_seq.GetBlobNum(); ++j )
1045 CvBlob* detected_b = m_detected_blob_seq.GetBlob(j);
1046 pNewBlobList->AddBlob(detected_b);
1049 } /* For each new blob. */
1051 for( i = 0; i < pNewBlobList->GetBlobNum(); ++i )
1053 CvBlob* b = pNewBlobList->GetBlob(i);
1054 CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 2 );
1055 m_debug_blob_seq.AddBlob(&d_b);
1057 } // if( m_split_detector )
1062 } /* cvDetectNewBlob */