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 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.
49 //#define USE_OBJECT_DETECTOR
53 static int CompareContour(const void* a, const void* b, void* )
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;
70 dx = (float)(fabs(pa.x - pb.x)-w);
71 dy = (float)(fabs(pa.y - pb.y)-h);
73 //wt = MAX(ra.width,rb.width)*0.1f;
75 ht = MAX(ra.height,rb.height)*0.3f;
76 if(dx < wt && dy < ht) return 1;
79 void cvFindBlobsByCCClasters(IplImage* pFG, CvBlobSeq* pBlobs, CvMemStorage* storage)
80 {/* create contours */
83 CvSeq* cnt_list = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvSeq*), storage );
84 CvSeq* clasters = NULL;
85 int claster_cur, claster_num;
87 pIB = cvCloneImage(pFG);
88 cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
89 cvFindContours(pIB,storage, &cnt, sizeof(CvContour), CV_RETR_EXTERNAL);
93 /* process each contours*/
94 for(;cnt;cnt=cnt->h_next)
96 cvSeqPush( cnt_list, &cnt);
99 claster_num = cvSeqPartition( cnt_list, storage, &clasters, CompareContour, NULL );
101 for(claster_cur=0;claster_cur<claster_num;++claster_cur)
105 double M00,X,Y,XX,YY; /* image moments */
107 CvRect rect_res = cvRect(-1,-1,-1,-1);
110 for(cnt_cur=0;cnt_cur<clasters->total;++cnt_cur)
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;
119 if(rect_res.height<0)
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);
132 rect_res.width = x1-x0;
133 rect_res.height = y1-y0;
137 if(rect_res.height < 1 || rect_res.width < 1)
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;
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);
160 IplImage* pI = cvCreateImage(cvSize(pFG->width,pFG->height),IPL_DEPTH_8U,3);
162 for(claster_cur=0;claster_cur<claster_num;++claster_cur)
165 CvScalar color = CV_RGB(rand()%256,rand()%256,rand()%256);
166 for(cnt_cur=0;cnt_cur<clasters->total;++cnt_cur)
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);
175 CvBlob* pB = pBlobs->GetBlob(claster_cur);
176 int x = cvRound(CV_BLOB_RX(pB)), y = cvRound(CV_BLOB_RY(pB));
178 cvPointFrom32f(CV_BLOB_CENTER(pB)),
179 cvSize(MAX(1,x), MAX(1,y)),
183 cvNamedWindow( "Clusters", 0);
184 cvShowImage( "Clusters",pI );
190 }/* cvFindBlobsByCCClasters */
192 /* simple blob detector */
193 /* numer of successive frame to analyse */
194 #define EBD_FRAME_NUM 5
195 class CvBlobDetectorSimple:public CvBlobDetector
198 CvBlobDetectorSimple();
199 ~CvBlobDetectorSimple();
200 int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
201 void Release(){delete this;};
203 IplImage* m_pMaskBlobNew;
204 IplImage* m_pMaskBlobExist;
205 /* lists of connected components detected on previouse frames */
206 CvBlobSeq* m_pBlobLists[EBD_FRAME_NUM];
209 /* Blob detector creator (sole interface function for this file) */
210 CvBlobDetector* cvCreateBlobDetectorSimple(){return new CvBlobDetectorSimple;};
212 /* Constructor of BlobDetector */
213 CvBlobDetectorSimple::CvBlobDetectorSimple()
216 m_pMaskBlobNew = NULL;
217 m_pMaskBlobExist = NULL;
218 for(i=0;i<EBD_FRAME_NUM;++i)m_pBlobLists[i] = NULL;
221 /* destructor of BlobDetector*/
222 CvBlobDetectorSimple::~CvBlobDetectorSimple()
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 */
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)
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);
238 /* shift blob list */
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 */
246 /* create exist blob mask */
247 cvCopy(pFGMask, m_pMaskBlobNew);
249 /* create contours and add new blobs to blob list */
252 CvMemStorage* storage = cvCreateMemStorage();
256 cvFindBlobsByCCClasters(m_pMaskBlobNew, &Blobs, storage );
260 IplImage* pIB = cvCloneImage(m_pMaskBlobNew);
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)
270 double M00,X,Y,XX,YY;
272 CvRect r = ((CvContour*)cnt)->rect;
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);
285 cvReleaseImage(&pIB);
286 }/* one contour - one blob */
289 {/* Delete small and intersected blobs */
291 for(i=Blobs.GetBlobNum();i>0;i--)
293 CvBlob* pB = Blobs.GetBlob(i-1);
295 if(pB->h < S.height*0.02 || pB->w < S.width*0.02)
303 for(j=pOldBlobList->GetBlobNum();j>0;j--)
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*/
312 }/* checl next old blob */
313 }/*if pOldBlobList */
314 }/* check next blob */
315 }/* Delete small and intersected blobs */
317 {/* bubble sort blobs by size */
318 int N = Blobs.GetBlobNum();
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;
336 /* copy only first 10 blobs */
337 for(i=0;i<MIN(N,10);++i)
339 m_pBlobLists[EBD_FRAME_NUM-1]->AddBlob(Blobs.GetBlob(i));
341 }/* sort blobs by size */
342 cvReleaseMemStorage(&storage);
345 /* analize blob list to find best blob trajectory */
346 {/* analize blob list to find best blob trajectory */
348 int pBLIndex[EBD_FRAME_NUM];
349 int pBL_BEST[EBD_FRAME_NUM];
352 double BestError = -1;
354 for(i=0;i<EBD_FRAME_NUM;++i)
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)
366 do{/* for each configuration */
367 CvBlob* pBL[EBD_FRAME_NUM];
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]);
375 /* check intersection last blob with existed */
376 if(Good && pOldBlobList)
377 { /* check intersection last blob with existed */
379 for(k=pOldBlobList->GetBlobNum();k>0;--k)
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))))
386 }/* check intersection last blob with existed */
388 /* check distance to image border */
390 { /* check distance to image border */
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);
395 if(dx < 1.1 || dy < 1.1) Good = 0;
396 }/* check distance to image border */
398 /* check uniform moveing */
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*/
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));
423 pow(a[0]*j+b[0]-pBL[j]->x,2)+
424 pow(a[1]*j+b[1]-pBL[j]->y,2);
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)
431 }/* check configuration */
434 /* new best trajectory */
435 if(Good && (BestError == -1 || BestError > Error))
436 {/* new best trajectory */
437 for(i=0;i<EBD_FRAME_NUM;++i)
439 pBL_BEST[i] = pBLIndex[i];
442 }/* new best trajectory */
444 /* set next configuration */
445 for(i=0;i<EBD_FRAME_NUM;++i)
448 if(pBLIndex[i] != m_pBlobLists[i]->GetBlobNum()) break;
450 }/* next time shift */
451 if(i==EBD_FRAME_NUM)finish=1;
452 }while(!finish); /* check next time configuration of connected components */
456 printf("BlobDetector configurations = %d [",Count);
458 for(i=0;i<EBD_FRAME_NUM;++i)
460 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
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 */
476 }/* put new blob to output and delete from blob list */
477 }/* analize blod list to find best blob trajectory */
480 }/* cvDetectNewBlob */
485 /* simple blob detector2 */
486 /* numer of successive frame to analyse */
487 #define SEQ_SIZE_MAX 30
492 CvBlob* pBlobs[SEQ_SIZE_MAX];
494 class CvBlobDetectorCC:public CvBlobDetector
499 int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
500 void Release(){delete this;};
501 virtual void ParamUpdate()
503 if(SEQ_SIZE<1)SEQ_SIZE=1;
504 if(SEQ_SIZE>SEQ_SIZE_MAX)SEQ_SIZE=SEQ_SIZE_MAX;
506 #ifdef USE_OBJECT_DETECTOR
507 if( m_param_split_detector_file_name )
509 m_split_detector = new CvObjectDetector();
510 if( !m_split_detector->Load( m_param_split_detector_file_name ) )
512 delete m_split_detector;
513 m_split_detector = 0;
517 m_min_window_size = m_split_detector->GetMinWindowSize();
518 m_max_border = m_split_detector->GetMaxBorderSize();
524 /* lists of connected components detected on previouse frames */
525 CvBlobSeq* m_pBlobLists[SEQ_SIZE_MAX];
526 DefSeq m_TrackSeq[SEQ_NUM];
530 float m_MinDistToBorder;
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;
541 CvObjectDetector* m_split_detector;
542 CvSize m_min_window_size;
545 CvBlobSeq m_detected_blob_seq;
548 CvBlobSeq m_debug_blob_seq;
551 /* Blob detector creator (sole interface function for this file) */
552 CvBlobDetector* cvCreateBlobDetectorCC(){return new CvBlobDetectorCC;}
554 /* Constructor of BlobDetector */
555 CvBlobDetectorCC::CvBlobDetectorCC() :
557 m_detected_blob_seq(sizeof(CvDetectedBlob)),
559 m_debug_blob_seq(sizeof(CvDetectedBlob))
561 /*CvDrawShape shapes[] =
563 { CvDrawShape::RECT, {{255,255,255}} },
564 { CvDrawShape::RECT, {{0,0,255}} },
565 { CvDrawShape::ELLIPSE, {{0,255,0}} }
567 int num_shapes = sizeof(shapes) / sizeof(shapes[0]);*/
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;
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");
585 AddParam("Clastering",&m_Clastering);
586 CommentParam("Clastering","Minimal allowed distance from blob center to image border in blob sizes");
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");
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");
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)");
602 m_min_window_size = cvSize(0,0);
604 m_roi_seq = cvCreateSeq( 0, sizeof(*m_roi_seq), sizeof(CvRect), cvCreateMemStorage() );
606 SetModuleName("BD_CC");
610 /* destructor of BlobDetector*/
611 CvBlobDetectorCC::~CvBlobDetectorCC()
614 for(i=0;i<SEQ_SIZE_MAX;++i)
617 delete m_pBlobLists[i];
622 cvReleaseMemStorage( &m_roi_seq->storage );
625 //cvDestroyWindow( "EnteringBlobDetectionDebug" );
626 }/* cvReleaseBlobDetector */
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)
633 CvSize S = cvSize(pFGMask->width,pFGMask->height);
635 /* shift blob list */
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 */
643 /* create contours and add new blobs to blob list */
646 CvMemStorage* storage = cvCreateMemStorage();
650 cvFindBlobsByCCClasters(pFGMask, &Blobs, storage );
654 IplImage* pIB = cvCloneImage(pFGMask);
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)
664 double M00,X,Y,XX,YY;
666 CvRect r = ((CvContour*)cnt)->rect;
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);
679 cvReleaseImage(&pIB);
680 }/* one contour - one blob */
682 {/* Delete small and intersected blobs */
684 for(i=Blobs.GetBlobNum();i>0;i--)
686 CvBlob* pB = Blobs.GetBlob(i-1);
688 if(pB->h < S.height*m_HMin || pB->w < S.width*m_WMin)
696 for(j=pOldBlobList->GetBlobNum();j>0;j--)
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*/
705 }/* checl next old blob */
706 }/*if pOldBlobList */
707 }/* check next blob */
708 }/* Delete small and intersected blobs */
710 {/* bubble sort blobs by size */
711 int N = Blobs.GetBlobNum();
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;
729 /* copy only first 10 blobs */
730 for(i=0;i<MIN(N,10);++i)
732 m_pBlobLists[0]->AddBlob(Blobs.GetBlob(i));
734 }/* sort blobs by size */
735 cvReleaseMemStorage(&storage);
738 {/* shift each track */
740 for(j=0;j<m_TrackNum;++j)
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--;
748 }/* shift each track */
750 /* analize blob list to find best blob trajectory */
751 {/* analize blob list to find best blob trajectory */
752 double BestError = -1;
754 CvBlobSeq* pNewBlobs = m_pBlobLists[0];
757 for(i=pNewBlobs->GetBlobNum();i>0;--i)
759 CvBlob* pBNew = pNewBlobs->GetBlob(i-1);
761 int AsignedTrack = 0;
762 for(j=0;j<m_TrackNum;++j)
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;
772 if(pTrack->pBlobs[0]==NULL)
773 {/*fill existed track */
774 pTrack->pBlobs[0] = pBNew;
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;
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;
793 m_TrackNum += NewTrackNum;
795 /* check each track */
796 for(i=0;i<m_TrackNum;++i)
799 DefSeq* pTrack = m_TrackSeq+i;
800 CvBlob* pBNew = pTrack->pBlobs[0];
801 if(pTrack->size != SEQ_SIZE) continue;
802 if(pBNew == NULL ) continue;
804 /* check intersection last blob with existed */
805 if(Good && pOldBlobList)
806 { /* check intersection last blob with existed */
808 for(k=pOldBlobList->GetBlobNum();k>0;--k)
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))))
815 }/* check intersection last blob with existed */
817 /* check distance to image border */
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 */
825 /* check uniform moveing */
827 {/* check uniform moveing */
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*/
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));
852 pow(a[0]*j+b[0]-pBL[j]->x,2)+
853 pow(a[1]*j+b[1]-pBL[j]->y,2);
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)
861 /* new best trajectory */
862 if(Good && (BestError == -1 || BestError > Error))
863 {/* new best trajectory */
866 }/* new best trajectory */
867 }/* check uniform moveing */
872 printf("BlobDetector configurations = %d [",m_TrackNum);
874 for(i=0;i<SEQ_SIZE;++i)
876 printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():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--;
891 }/* put new blob to output and mark in blob list to delete*/
892 }/* analize blod list to find best blob trajectory */
894 {/* delete bad tracks */
896 for(i=m_TrackNum-1;i>=0;--i)
897 {/* delete bad tracks */
898 if(m_TrackSeq[i].pBlobs[0]) continue;
900 m_TrackSeq[i] = m_TrackSeq[--m_TrackNum];
901 }/* delete bad tracks */
904 #ifdef USE_OBJECT_DETECTOR
905 if( m_split_detector && pNewBlobList->GetBlobNum() > 0 )
907 int num_new_blobs = pNewBlobList->GetBlobNum();
910 if( m_roi_seq ) cvClearSeq( m_roi_seq );
911 m_debug_blob_seq.Clear();
912 for( i = 0; i < num_new_blobs; ++i )
914 CvBlob* b = pNewBlobList->GetBlob(i);
917 CvMat* scaled_roi_mat = 0;
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);
922 float scale = m_param_roi_scale * m_min_window_size.height / CV_BLOB_WY(b);
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;
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 )
936 if( m_roi_seq ) cvSeqPush( m_roi_seq, &roi );
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 );
942 m_detected_blob_seq.Clear();
943 m_split_detector->Detect( scaled_roi_mat, &m_detected_blob_seq );
944 cvReleaseMat( &scaled_roi_mat );
946 for( int k = 0; k < m_detected_blob_seq.GetBlobNum(); ++k )
948 CvDetectedBlob* b = (CvDetectedBlob*) m_detected_blob_seq.GetBlob(k);
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;
956 CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 1,
958 m_debug_blob_seq.AddBlob(&d_b);
961 if( m_detected_blob_seq.GetBlobNum() > 1 )
965 * The original blob is replaced by the first detected blob,
966 * remaining detected blobs are added to the end of the sequence
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);
972 for( int j = 1; j < m_detected_blob_seq.GetBlobNum(); ++j )
974 CvBlob* detected_b = m_detected_blob_seq.GetBlob(j);
975 pNewBlobList->AddBlob(detected_b);
978 } /* for each new blob */
979 for( i = 0; i < pNewBlobList->GetBlobNum(); ++i )
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);
985 } // if( m_split_detector )
989 }/* cvDetectNewBlob */