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;
233 /* Destructor of BlobDetector: */
234 CvBlobDetectorSimple::~CvBlobDetectorSimple()
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 */
243 * return 1 and fill blob pNewBlob by blob parameters
244 * if new blob is detected:
246 int CvBlobDetectorSimple::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
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);
253 /* Shift blob list: */
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. */
261 /* Create exist blob mask: */
262 cvCopy(pFGMask, m_pMaskBlobNew);
264 /* Create contours and add new blobs to blob list: */
265 { /* Create blobs: */
267 CvMemStorage* storage = cvCreateMemStorage();
270 { /* Glue contours: */
271 cvFindBlobsByCCClasters(m_pMaskBlobNew, &Blobs, storage );
272 } /* Glue contours. */
275 IplImage* pIB = cvCloneImage(m_pMaskBlobNew);
278 cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
279 cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
281 /* Process each contour: */
282 for(cnt = cnts; cnt; cnt=cnt->h_next)
287 double M00,X,Y,XX,YY;
289 CvRect r = ((CvContour*)cnt)->rect;
292 if(r.height < S.height*0.02 || r.width < S.width*0.02) continue;
294 cvMoments( cvGetSubRect(m_pMaskBlobNew,&mat,r), &m, 0 );
295 M00 = cvGetSpatialMoment( &m, 0, 0 );
297 if(M00 <= 0 ) continue;
299 X = cvGetSpatialMoment( &m, 1, 0 )/M00;
300 Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
302 XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
303 YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
305 NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
307 Blobs.AddBlob(&NewBlob);
309 } /* Next contour. */
311 cvReleaseImage(&pIB);
313 } /* One contour - one blob. */
316 { /* Delete small and intersected blobs: */
318 for(i=Blobs.GetBlobNum(); i>0; i--)
320 CvBlob* pB = Blobs.GetBlob(i-1);
322 if(pB->h < S.height*0.02 || pB->w < S.width*0.02)
330 for(j=pOldBlobList->GetBlobNum(); j>0; j--)
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: */
339 } /* Check next old blob. */
340 } /* if pOldBlobList */
341 } /* Check next blob. */
342 } /* Delete small and intersected blobs. */
344 { /* Bubble-sort blobs by size: */
345 int N = Blobs.GetBlobNum();
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;
364 /* Copy only first 10 blobs: */
365 for(i=0; i<MIN(N,10); ++i)
367 m_pBlobLists[EBD_FRAME_NUM-1]->AddBlob(Blobs.GetBlob(i));
370 } /* Sort blobs by size. */
372 cvReleaseMemStorage(&storage);
374 } /* Create blobs. */
376 /* Analyze blob list to find best blob trajectory: */
379 int pBLIndex[EBD_FRAME_NUM];
380 int pBL_BEST[EBD_FRAME_NUM];
383 double BestError = -1;
386 for(i=0; i<EBD_FRAME_NUM; ++i)
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)
398 do{ /* For each configuration: */
399 CvBlob* pBL[EBD_FRAME_NUM];
402 CvBlob* pBNew = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBLIndex[EBD_FRAME_NUM-1]);
404 for(i=0; i<EBD_FRAME_NUM; ++i) pBL[i] = m_pBlobLists[i]->GetBlob(pBLIndex[i]);
408 /* Check intersection last blob with existed: */
409 if(Good && pOldBlobList)
410 { /* Check intersection last blob with existed: */
412 for(k=pOldBlobList->GetBlobNum(); k>0; --k)
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))))
419 } /* Check intersection last blob with existed. */
421 /* Check distance to image border: */
423 { /* Check distance to image border: */
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);
428 if(dx < 1.1 || dy < 1.1) Good = 0;
429 } /* Check distance to image border. */
431 /* Check uniform motion: */
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*/
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));
458 pow(a[0]*j+b[0]-pBL[j]->x,2)+
459 pow(a[1]*j+b[1]-pBL[j]->y,2);
462 Error = sqrt(Error/N);
464 if( Error > S.width*0.01 ||
465 fabs(a[0])>S.width*0.1 ||
466 fabs(a[1])>S.height*0.1)
469 } /* Check configuration. */
472 /* New best trajectory: */
473 if(Good && (BestError == -1 || BestError > Error))
475 for(i=0; i<EBD_FRAME_NUM; ++i)
477 pBL_BEST[i] = pBLIndex[i];
480 } /* New best trajectory. */
482 /* Set next configuration: */
483 for(i=0; i<EBD_FRAME_NUM; ++i)
486 if(pBLIndex[i] != m_pBlobLists[i]->GetBlobNum()) break;
488 } /* Next time shift. */
490 if(i==EBD_FRAME_NUM)finish=1;
492 } while(!finish); /* Check next time configuration of connected components. */
496 printf("BlobDetector configurations = %d [",Count);
498 for(i=0; i<EBD_FRAME_NUM; ++i)
500 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
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);
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. */
519 } /* Write new blob to output and delete from blob list. */
520 } /* Analyze blob list to find best blob trajectory. */
524 } /* cvDetectNewBlob */
529 /* Simple blob detector2. */
530 /* Number of successive frames to analyse: */
531 #define SEQ_SIZE_MAX 30
536 CvBlob* pBlobs[SEQ_SIZE_MAX];
539 class CvBlobDetectorCC:public CvBlobDetector
544 int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
545 void Release(){delete this;};
547 virtual void ParamUpdate()
549 if(SEQ_SIZE<1)SEQ_SIZE=1;
550 if(SEQ_SIZE>SEQ_SIZE_MAX)SEQ_SIZE=SEQ_SIZE_MAX;
552 #ifdef USE_OBJECT_DETECTOR
553 if( m_param_split_detector_file_name )
555 m_split_detector = new CvObjectDetector();
556 if( !m_split_detector->Load( m_param_split_detector_file_name ) )
558 delete m_split_detector;
559 m_split_detector = 0;
563 m_min_window_size = m_split_detector->GetMinWindowSize();
564 m_max_border = m_split_detector->GetMaxBorderSize();
571 /* Lists of connected components detected on previous frames: */
572 CvBlobSeq* m_pBlobLists[SEQ_SIZE_MAX];
573 DefSeq m_TrackSeq[SEQ_NUM];
577 float m_MinDistToBorder;
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:
585 char* m_param_split_detector_file_name;
586 float m_param_roi_scale;
587 int m_param_only_roi;
589 CvObjectDetector* m_split_detector;
590 CvSize m_min_window_size;
593 CvBlobSeq m_detected_blob_seq;
596 CvBlobSeq m_debug_blob_seq;
599 /* Blob detector creator (sole interface function for this file): */
600 CvBlobDetector* cvCreateBlobDetectorCC(){return new CvBlobDetectorCC;}
602 /* Constructor for BlobDetector: */
603 CvBlobDetectorCC::CvBlobDetectorCC() :
605 m_detected_blob_seq(sizeof(CvDetectedBlob)),
607 m_debug_blob_seq(sizeof(CvDetectedBlob))
609 /*CvDrawShape shapes[] =
611 { CvDrawShape::RECT, {{255,255,255}} },
612 { CvDrawShape::RECT, {{0,0,255}} },
613 { CvDrawShape::ELLIPSE, {{0,255,0}} }
615 int num_shapes = sizeof(shapes) / sizeof(shapes[0]);*/
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;
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");
633 AddParam("Clastering",&m_Clastering);
634 CommentParam("Clastering","Minimal allowed distance from blob center to image border in blob sizes");
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");
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");
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)");
650 m_min_window_size = cvSize(0,0);
652 m_roi_seq = cvCreateSeq( 0, sizeof(*m_roi_seq), sizeof(CvRect), cvCreateMemStorage() );
654 SetModuleName("BD_CC");
657 /* Destructor for BlobDetector: */
658 CvBlobDetectorCC::~CvBlobDetectorCC()
661 for(i=0; i<SEQ_SIZE_MAX; ++i)
664 delete m_pBlobLists[i];
669 cvReleaseMemStorage( &m_roi_seq->storage );
672 //cvDestroyWindow( "EnteringBlobDetectionDebug" );
673 } /* cvReleaseBlobDetector */
677 * Return 1 and fill blob pNewBlob with
678 * blob parameters if new blob is detected:
680 int CvBlobDetectorCC::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
683 CvSize S = cvSize(pFGMask->width,pFGMask->height);
685 /* Shift blob list: */
688 if(m_pBlobLists[SEQ_SIZE-1]) delete m_pBlobLists[SEQ_SIZE-1];
690 for(i=SEQ_SIZE-1; i>0; --i) m_pBlobLists[i] = m_pBlobLists[i-1];
692 m_pBlobLists[0] = new CvBlobSeq;
694 } /* Shift blob list. */
696 /* Create contours and add new blobs to blob list: */
697 { /* Create blobs: */
699 CvMemStorage* storage = cvCreateMemStorage();
702 { /* Glue contours: */
703 cvFindBlobsByCCClasters(pFGMask, &Blobs, storage );
704 } /* Glue contours. */
707 IplImage* pIB = cvCloneImage(pFGMask);
710 cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
711 cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
713 /* Process each contour: */
714 for(cnt = cnts; cnt; cnt=cnt->h_next)
718 double M00,X,Y,XX,YY;
720 CvRect r = ((CvContour*)cnt)->rect;
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);
733 } /* Next contour. */
735 cvReleaseImage(&pIB);
737 } /* One contour - one blob. */
739 { /* Delete small and intersected blobs: */
741 for(i=Blobs.GetBlobNum(); i>0; i--)
743 CvBlob* pB = Blobs.GetBlob(i-1);
745 if(pB->h < S.height*m_HMin || pB->w < S.width*m_WMin)
754 for(j=pOldBlobList->GetBlobNum(); j>0; j--)
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: */
763 } /* Check next old blob. */
764 } /* if pOldBlobList. */
765 } /* Check next blob. */
766 } /* Delete small and intersected blobs. */
768 { /* Bubble-sort blobs by size: */
769 int N = Blobs.GetBlobNum();
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;
788 /* Copy only first 10 blobs: */
789 for(i=0; i<MIN(N,10); ++i)
791 m_pBlobLists[0]->AddBlob(Blobs.GetBlob(i));
794 } /* Sort blobs by size. */
796 cvReleaseMemStorage(&storage);
798 } /* Create blobs. */
800 { /* Shift each track: */
802 for(j=0; j<m_TrackNum; ++j)
805 DefSeq* pTrack = m_TrackSeq+j;
807 for(i=SEQ_SIZE-1; i>0; --i)
808 pTrack->pBlobs[i] = pTrack->pBlobs[i-1];
810 pTrack->pBlobs[0] = NULL;
811 if(pTrack->size == SEQ_SIZE)pTrack->size--;
813 } /* Shift each track. */
815 /* Analyze blob list to find best blob trajectory: */
817 double BestError = -1;
819 CvBlobSeq* pNewBlobs = m_pBlobLists[0];
822 for(i=pNewBlobs->GetBlobNum(); i>0; --i)
824 CvBlob* pBNew = pNewBlobs->GetBlob(i-1);
826 int AsignedTrack = 0;
827 for(j=0; j<m_TrackNum; ++j)
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;
838 if(pTrack->pBlobs[0]==NULL)
839 { /* Fill existed track: */
840 pTrack->pBlobs[0] = pBNew;
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;
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;
857 } /* Next new blob. */
859 m_TrackNum += NewTrackNum;
861 /* Check each track: */
862 for(i=0; i<m_TrackNum; ++i)
865 DefSeq* pTrack = m_TrackSeq+i;
866 CvBlob* pBNew = pTrack->pBlobs[0];
867 if(pTrack->size != SEQ_SIZE) continue;
868 if(pBNew == NULL ) continue;
870 /* Check intersection last blob with existed: */
871 if(Good && pOldBlobList)
874 for(k=pOldBlobList->GetBlobNum(); k>0; --k)
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))))
881 } /* Check intersection last blob with existed. */
883 /* Check distance to image border: */
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. */
891 /* Check uniform motion: */
893 { /* Check uniform motion: */
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*/
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));
920 pow(a[0]*j+b[0]-pBL[j]->x,2)+
921 pow(a[1]*j+b[1]-pBL[j]->y,2);
924 Error = sqrt(Error/N);
926 if( Error > S.width*0.01 ||
927 fabs(a[0])>S.width*0.1 ||
928 fabs(a[1])>S.height*0.1)
931 /* New best trajectory: */
932 if(Good && (BestError == -1 || BestError > Error))
933 { /* New best trajectory: */
936 } /* New best trajectory. */
937 } /* Check uniform motion. */
942 printf("BlobDetector configurations = %d [",m_TrackNum);
944 for(i=0; i<SEQ_SIZE; ++i)
946 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():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--;
960 } /* Put new blob to output and mark in blob list to delete. */
961 } /* Analyze blod list to find best blob trajectory. */
963 { /* Delete bad tracks: */
965 for(i=m_TrackNum-1; i>=0; --i)
966 { /* Delete bad tracks: */
967 if(m_TrackSeq[i].pBlobs[0]) continue;
969 m_TrackSeq[i] = m_TrackSeq[--m_TrackNum];
970 } /* Delete bad tracks: */
973 #ifdef USE_OBJECT_DETECTOR
974 if( m_split_detector && pNewBlobList->GetBlobNum() > 0 )
976 int num_new_blobs = pNewBlobList->GetBlobNum();
979 if( m_roi_seq ) cvClearSeq( m_roi_seq );
980 m_debug_blob_seq.Clear();
981 for( i = 0; i < num_new_blobs; ++i )
983 CvBlob* b = pNewBlobList->GetBlob(i);
986 CvMat* scaled_roi_mat = 0;
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);
991 float scale = m_param_roi_scale * m_min_window_size.height / CV_BLOB_WY(b);
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;
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 )
1005 if( m_roi_seq ) cvSeqPush( m_roi_seq, &roi );
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 );
1011 m_detected_blob_seq.Clear();
1012 m_split_detector->Detect( scaled_roi_mat, &m_detected_blob_seq );
1013 cvReleaseMat( &scaled_roi_mat );
1015 for( int k = 0; k < m_detected_blob_seq.GetBlobNum(); ++k )
1017 CvDetectedBlob* b = (CvDetectedBlob*) m_detected_blob_seq.GetBlob(k);
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;
1025 CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 1,
1027 m_debug_blob_seq.AddBlob(&d_b);
1030 if( m_detected_blob_seq.GetBlobNum() > 1 )
1034 * The original blob is replaced by the first detected blob,
1035 * remaining detected blobs are added to the end of the sequence:
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);
1041 for( int j = 1; j < m_detected_blob_seq.GetBlobNum(); ++j )
1043 CvBlob* detected_b = m_detected_blob_seq.GetBlob(j);
1044 pNewBlobList->AddBlob(detected_b);
1047 } /* For each new blob. */
1049 for( i = 0; i < pNewBlobList->GetBlobNum(); ++i )
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);
1055 } // if( m_split_detector )
1060 } /* cvDetectNewBlob */