opencv/modules/legacy/src/enteringblobdetection.cpp

1065 lines
36 KiB
C++

/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
/*
This file implements the virtual interface defined as "CvBlobDetector".
This implementation based on a simple algorithm:
A new blob is detected when several successive frames contains connected components
which have uniform motion not at an unreasonably high speed.
Separation from border and already tracked blobs are also considered.
For an entrypoint into the literature see:
Appearance Models for Occlusion Handling
Andrew Senior &t al, 8p 2001
http://www.research.ibm.com/peoplevision/PETS2001.pdf
*/
//#define USE_OBJECT_DETECTOR
#include "precomp.hpp"
static int CompareContour(const void* a, const void* b, void* )
{
float dx,dy;
float h,w,ht,wt;
CvPoint2D32f pa,pb;
CvRect ra,rb;
CvSeq* pCA = *(CvSeq**)a;
CvSeq* pCB = *(CvSeq**)b;
ra = ((CvContour*)pCA)->rect;
rb = ((CvContour*)pCB)->rect;
pa.x = ra.x + ra.width*0.5f;
pa.y = ra.y + ra.height*0.5f;
pb.x = rb.x + rb.width*0.5f;
pb.y = rb.y + rb.height*0.5f;
w = (ra.width+rb.width)*0.5f;
h = (ra.height+rb.height)*0.5f;
dx = (float)(fabs(pa.x - pb.x)-w);
dy = (float)(fabs(pa.y - pb.y)-h);
//wt = MAX(ra.width,rb.width)*0.1f;
wt = 0;
ht = MAX(ra.height,rb.height)*0.3f;
return (dx < wt && dy < ht);
}
static void cvFindBlobsByCCClasters(IplImage* pFG, CvBlobSeq* pBlobs, CvMemStorage* storage)
{ /* Create contours: */
IplImage* pIB = NULL;
CvSeq* cnt = NULL;
CvSeq* cnt_list = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvSeq*), storage );
CvSeq* clasters = NULL;
int claster_cur, claster_num;
pIB = cvCloneImage(pFG);
cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
cvFindContours(pIB,storage, &cnt, sizeof(CvContour), CV_RETR_EXTERNAL);
cvReleaseImage(&pIB);
/* Create cnt_list. */
/* Process each contour: */
for(; cnt; cnt=cnt->h_next)
{
cvSeqPush( cnt_list, &cnt);
}
claster_num = cvSeqPartition( cnt_list, storage, &clasters, CompareContour, NULL );
for(claster_cur=0; claster_cur<claster_num; ++claster_cur)
{
int cnt_cur;
CvBlob NewBlob;
double M00,X,Y,XX,YY; /* image moments */
CvMoments m;
CvRect rect_res = cvRect(-1,-1,-1,-1);
CvMat mat;
for(cnt_cur=0; cnt_cur<clasters->total; ++cnt_cur)
{
CvRect rect;
CvSeq* cont;
int k = *(int*)cvGetSeqElem( clasters, cnt_cur );
if(k!=claster_cur) continue;
cont = *(CvSeq**)cvGetSeqElem( cnt_list, cnt_cur );
rect = ((CvContour*)cont)->rect;
if(rect_res.height<0)
{
rect_res = rect;
}
else
{ /* Unite rects: */
int x0,x1,y0,y1;
x0 = MIN(rect_res.x,rect.x);
y0 = MIN(rect_res.y,rect.y);
x1 = MAX(rect_res.x+rect_res.width,rect.x+rect.width);
y1 = MAX(rect_res.y+rect_res.height,rect.y+rect.height);
rect_res.x = x0;
rect_res.y = y0;
rect_res.width = x1-x0;
rect_res.height = y1-y0;
}
}
if(rect_res.height < 1 || rect_res.width < 1)
{
X = 0;
Y = 0;
XX = 0;
YY = 0;
}
else
{
cvMoments( cvGetSubRect(pFG,&mat,rect_res), &m, 0 );
M00 = cvGetSpatialMoment( &m, 0, 0 );
if(M00 <= 0 ) continue;
X = cvGetSpatialMoment( &m, 1, 0 )/M00;
Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
}
NewBlob = cvBlob(rect_res.x+(float)X,rect_res.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
pBlobs->AddBlob(&NewBlob);
} /* Next cluster. */
#if 0
{ // Debug info:
IplImage* pI = cvCreateImage(cvSize(pFG->width,pFG->height),IPL_DEPTH_8U,3);
cvZero(pI);
for(claster_cur=0; claster_cur<claster_num; ++claster_cur)
{
int cnt_cur;
CvScalar color = CV_RGB(rand()%256,rand()%256,rand()%256);
for(cnt_cur=0; cnt_cur<clasters->total; ++cnt_cur)
{
CvSeq* cnt;
int k = *(int*)cvGetSeqElem( clasters, cnt_cur );
if(k!=claster_cur) continue;
cnt = *(CvSeq**)cvGetSeqElem( cnt_list, cnt_cur );
cvDrawContours( pI, cnt, color, color, 0, 1, 8);
}
CvBlob* pB = pBlobs->GetBlob(claster_cur);
int x = cvRound(CV_BLOB_RX(pB)), y = cvRound(CV_BLOB_RY(pB));
cvEllipse( pI,
cvPointFrom32f(CV_BLOB_CENTER(pB)),
cvSize(MAX(1,x), MAX(1,y)),
0, 0, 360,
color, 1 );
}
cvNamedWindow( "Clusters", 0);
cvShowImage( "Clusters",pI );
cvReleaseImage(&pI);
} /* Debug info. */
#endif
} /* cvFindBlobsByCCClasters */
/* Simple blob detector. */
/* Number of successive frame to analyse: */
#define EBD_FRAME_NUM 5
class CvBlobDetectorSimple:public CvBlobDetector
{
public:
CvBlobDetectorSimple();
~CvBlobDetectorSimple();
int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
void Release(){delete this;};
protected:
IplImage* m_pMaskBlobNew;
IplImage* m_pMaskBlobExist;
/* Lists of connected components detected on previous frames: */
CvBlobSeq* m_pBlobLists[EBD_FRAME_NUM];
};
/* Blob detector creator (sole interface function for this file) */
CvBlobDetector* cvCreateBlobDetectorSimple(){return new CvBlobDetectorSimple;};
/* Constructor of BlobDetector: */
CvBlobDetectorSimple::CvBlobDetectorSimple()
{
int i = 0;
m_pMaskBlobNew = NULL;
m_pMaskBlobExist = NULL;
for(i=0;i<EBD_FRAME_NUM;++i)m_pBlobLists[i] = NULL;
SetModuleName("Simple");
}
/* Destructor of BlobDetector: */
CvBlobDetectorSimple::~CvBlobDetectorSimple()
{
int i;
if(m_pMaskBlobExist) cvReleaseImage(&m_pMaskBlobExist);
if(m_pMaskBlobNew) cvReleaseImage(&m_pMaskBlobNew);
for(i=0; i<EBD_FRAME_NUM; ++i) delete m_pBlobLists[i];
} /* cvReleaseBlobDetector */
/* cvDetectNewBlobs
* return 1 and fill blob pNewBlob by blob parameters
* if new blob is detected:
*/
int CvBlobDetectorSimple::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
{
int result = 0;
CvSize S = cvSize(pFGMask->width,pFGMask->height);
if(m_pMaskBlobNew == NULL ) m_pMaskBlobNew = cvCreateImage(S,IPL_DEPTH_8U,1);
if(m_pMaskBlobExist == NULL ) m_pMaskBlobExist = cvCreateImage(S,IPL_DEPTH_8U,1);
/* Shift blob list: */
{
int i;
if(m_pBlobLists[0]) delete m_pBlobLists[0];
for(i=1;i<EBD_FRAME_NUM;++i)m_pBlobLists[i-1]=m_pBlobLists[i];
m_pBlobLists[EBD_FRAME_NUM-1] = new CvBlobSeq;
} /* Shift blob list. */
/* Create exist blob mask: */
cvCopy(pFGMask, m_pMaskBlobNew);
/* Create contours and add new blobs to blob list: */
{ /* Create blobs: */
CvBlobSeq Blobs;
CvMemStorage* storage = cvCreateMemStorage();
#if 1
{ /* Glue contours: */
cvFindBlobsByCCClasters(m_pMaskBlobNew, &Blobs, storage );
} /* Glue contours. */
#else
{ /**/
IplImage* pIB = cvCloneImage(m_pMaskBlobNew);
CvSeq* cnts = NULL;
CvSeq* cnt = NULL;
cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
/* Process each contour: */
for(cnt = cnts; cnt; cnt=cnt->h_next)
{
CvBlob NewBlob;
/* Image moments: */
double M00,X,Y,XX,YY;
CvMoments m;
CvRect r = ((CvContour*)cnt)->rect;
CvMat mat;
if(r.height < S.height*0.02 || r.width < S.width*0.02) continue;
cvMoments( cvGetSubRect(m_pMaskBlobNew,&mat,r), &m, 0 );
M00 = cvGetSpatialMoment( &m, 0, 0 );
if(M00 <= 0 ) continue;
X = cvGetSpatialMoment( &m, 1, 0 )/M00;
Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
Blobs.AddBlob(&NewBlob);
} /* Next contour. */
cvReleaseImage(&pIB);
} /* One contour - one blob. */
#endif
{ /* Delete small and intersected blobs: */
int i;
for(i=Blobs.GetBlobNum(); i>0; i--)
{
CvBlob* pB = Blobs.GetBlob(i-1);
if(pB->h < S.height*0.02 || pB->w < S.width*0.02)
{
Blobs.DelBlob(i-1);
continue;
}
if(pOldBlobList)
{
int j;
for(j=pOldBlobList->GetBlobNum(); j>0; j--)
{
CvBlob* pBOld = pOldBlobList->GetBlob(j-1);
if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) &&
(fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB))))
{ /* Intersection is present, so delete blob from list: */
Blobs.DelBlob(i-1);
break;
}
} /* Check next old blob. */
} /* if pOldBlobList */
} /* Check next blob. */
} /* Delete small and intersected blobs. */
{ /* Bubble-sort blobs by size: */
int N = Blobs.GetBlobNum();
int i,j;
for(i=1; i<N; ++i)
{
for(j=i; j>0; --j)
{
CvBlob temp;
float AreaP, AreaN;
CvBlob* pP = Blobs.GetBlob(j-1);
CvBlob* pN = Blobs.GetBlob(j);
AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP);
AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN);
if(AreaN < AreaP)break;
temp = pN[0];
pN[0] = pP[0];
pP[0] = temp;
}
}
/* Copy only first 10 blobs: */
for(i=0; i<MIN(N,10); ++i)
{
m_pBlobLists[EBD_FRAME_NUM-1]->AddBlob(Blobs.GetBlob(i));
}
} /* Sort blobs by size. */
cvReleaseMemStorage(&storage);
} /* Create blobs. */
/* Analyze blob list to find best blob trajectory: */
{
int Count = 0;
int pBLIndex[EBD_FRAME_NUM];
int pBL_BEST[EBD_FRAME_NUM];
int i;
int finish = 0;
double BestError = -1;
int Good = 1;
for(i=0; i<EBD_FRAME_NUM; ++i)
{
pBLIndex[i] = 0;
pBL_BEST[i] = 0;
}
/* Check configuration exist: */
for(i=0; Good && (i<EBD_FRAME_NUM); ++i)
if(m_pBlobLists[i] == NULL || m_pBlobLists[i]->GetBlobNum() == 0)
Good = 0;
if(Good)
do{ /* For each configuration: */
CvBlob* pBL[EBD_FRAME_NUM];
int good = 1;
double Error = 0;
CvBlob* pBNew = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBLIndex[EBD_FRAME_NUM-1]);
for(i=0; i<EBD_FRAME_NUM; ++i) pBL[i] = m_pBlobLists[i]->GetBlob(pBLIndex[i]);
Count++;
/* Check intersection last blob with existed: */
if(good && pOldBlobList)
{ /* Check intersection last blob with existed: */
int k;
for(k=pOldBlobList->GetBlobNum(); k>0; --k)
{
CvBlob* pBOld = pOldBlobList->GetBlob(k-1);
if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) &&
(fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew))))
good = 0;
}
} /* Check intersection last blob with existed. */
/* Check distance to image border: */
if(good)
{ /* Check distance to image border: */
CvBlob* pB = pBNew;
float dx = MIN(pB->x,S.width-pB->x)/CV_BLOB_RX(pB);
float dy = MIN(pB->y,S.height-pB->y)/CV_BLOB_RY(pB);
if(dx < 1.1 || dy < 1.1) good = 0;
} /* Check distance to image border. */
/* Check uniform motion: */
if(good)
{
int N = EBD_FRAME_NUM;
float sum[2] = {0,0};
float jsum[2] = {0,0};
float a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/
int j;
for(j=0; j<N; ++j)
{
float x = pBL[j]->x;
float y = pBL[j]->y;
sum[0] += x;
jsum[0] += j*x;
sum[1] += y;
jsum[1] += j*y;
}
a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1));
b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1));
a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1));
b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1));
for(j=0; j<N; ++j)
{
Error +=
pow(a[0]*j+b[0]-pBL[j]->x,2)+
pow(a[1]*j+b[1]-pBL[j]->y,2);
}
Error = sqrt(Error/N);
if( Error > S.width*0.01 ||
fabs(a[0])>S.width*0.1 ||
fabs(a[1])>S.height*0.1)
good = 0;
} /* Check configuration. */
/* New best trajectory: */
if(good && (BestError == -1 || BestError > Error))
{
for(i=0; i<EBD_FRAME_NUM; ++i)
{
pBL_BEST[i] = pBLIndex[i];
}
BestError = Error;
} /* New best trajectory. */
/* Set next configuration: */
for(i=0; i<EBD_FRAME_NUM; ++i)
{
pBLIndex[i]++;
if(pBLIndex[i] != m_pBlobLists[i]->GetBlobNum()) break;
pBLIndex[i]=0;
} /* Next time shift. */
if(i==EBD_FRAME_NUM)finish=1;
} while(!finish); /* Check next time configuration of connected components. */
#if 0
{/**/
printf("BlobDetector configurations = %d [",Count);
int i;
for(i=0; i<EBD_FRAME_NUM; ++i)
{
printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
}
printf("]\n");
}
#endif
if(BestError != -1)
{ /* Write new blob to output and delete from blob list: */
CvBlob* pNewBlob = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBL_BEST[EBD_FRAME_NUM-1]);
pNewBlobList->AddBlob(pNewBlob);
for(i=0; i<EBD_FRAME_NUM; ++i)
{ /* Remove blob from each list: */
m_pBlobLists[i]->DelBlob(pBL_BEST[i]);
} /* Remove blob from each list. */
result = 1;
} /* Write new blob to output and delete from blob list. */
} /* Analyze blob list to find best blob trajectory. */
return result;
} /* cvDetectNewBlob */
/* Simple blob detector2. */
/* Number of successive frames to analyse: */
#define SEQ_SIZE_MAX 30
#define SEQ_NUM 1000
typedef struct
{
int size;
CvBlob* pBlobs[SEQ_SIZE_MAX];
} DefSeq;
class CvBlobDetectorCC:public CvBlobDetector
{
public:
CvBlobDetectorCC();
~CvBlobDetectorCC();
int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList);
void Release(){delete this;};
virtual void ParamUpdate()
{
if(SEQ_SIZE<1)SEQ_SIZE=1;
if(SEQ_SIZE>SEQ_SIZE_MAX)SEQ_SIZE=SEQ_SIZE_MAX;
#ifdef USE_OBJECT_DETECTOR
if( m_param_split_detector_file_name )
{
m_split_detector = new CvObjectDetector();
if( !m_split_detector->Load( m_param_split_detector_file_name ) )
{
delete m_split_detector;
m_split_detector = 0;
}
else
{
m_min_window_size = m_split_detector->GetMinWindowSize();
m_max_border = m_split_detector->GetMaxBorderSize();
}
}
#endif
}
private:
/* Lists of connected components detected on previous frames: */
CvBlobSeq* m_pBlobLists[SEQ_SIZE_MAX];
DefSeq m_TrackSeq[SEQ_NUM];
int m_TrackNum;
float m_HMin;
float m_WMin;
float m_MinDistToBorder;
int m_Clastering;
int SEQ_SIZE;
/* If not 0 then the detector is loaded from the specified file
* and it is applied for splitting blobs which actually correspond
* to groups of objects:
*/
char* m_param_split_detector_file_name;
float m_param_roi_scale;
int m_param_only_roi;
CvObjectDetector* m_split_detector;
CvSize m_min_window_size;
int m_max_border;
CvBlobSeq m_detected_blob_seq;
CvSeq* m_roi_seq;
CvBlobSeq m_debug_blob_seq;
};
/* Blob detector creator (sole interface function for this file): */
CvBlobDetector* cvCreateBlobDetectorCC(){return new CvBlobDetectorCC;}
/* Constructor for BlobDetector: */
CvBlobDetectorCC::CvBlobDetectorCC() :
m_split_detector(0),
m_detected_blob_seq(sizeof(CvDetectedBlob)),
m_roi_seq(0),
m_debug_blob_seq(sizeof(CvDetectedBlob))
{
/*CvDrawShape shapes[] =
{
{ CvDrawShape::RECT, {{255,255,255}} },
{ CvDrawShape::RECT, {{0,0,255}} },
{ CvDrawShape::ELLIPSE, {{0,255,0}} }
};
int num_shapes = sizeof(shapes) / sizeof(shapes[0]);*/
int i = 0;
SEQ_SIZE = 10;
AddParam("Latency",&SEQ_SIZE);
for(i=0;i<SEQ_SIZE_MAX;++i)m_pBlobLists[i] = NULL;
for(i=0;i<SEQ_NUM;++i)m_TrackSeq[i].size = 0;
m_TrackNum = 0;
m_HMin = 0.02f;
m_WMin = 0.01f;
AddParam("HMin",&m_HMin);
AddParam("WMin",&m_WMin);
m_MinDistToBorder = 1.1f;
AddParam("MinDistToBorder",&m_MinDistToBorder);
CommentParam("MinDistToBorder","Minimal allowed distance from blob center to image border in blob sizes");
m_Clastering=1;
AddParam("Clastering",&m_Clastering);
CommentParam("Clastering","Minimal allowed distance from blob center to image border in blob sizes");
m_param_split_detector_file_name = 0;
#ifdef USE_OBJECT_DETECTOR
AddParam("Detector", &m_param_split_detector_file_name);
CommentParam("Detector", "Detector file name");
#endif
m_param_roi_scale = 1.5F;
AddParam("ROIScale", &m_param_roi_scale);
CommentParam("ROIScale", "Determines the size of search window around a blob");
m_param_only_roi = 1;
AddParam("OnlyROI", &m_param_only_roi);
CommentParam("OnlyROI", "Shows the whole debug image (0) or only ROIs where the detector was applied (1)");
m_min_window_size = cvSize(0,0);
m_max_border = 0;
m_roi_seq = cvCreateSeq( 0, sizeof(*m_roi_seq), sizeof(CvRect), cvCreateMemStorage() );
SetModuleName("CC");
}
/* Destructor for BlobDetector: */
CvBlobDetectorCC::~CvBlobDetectorCC()
{
int i;
for(i=0; i<SEQ_SIZE_MAX; ++i)
{
if(m_pBlobLists[i])
delete m_pBlobLists[i];
}
if( m_roi_seq )
{
cvReleaseMemStorage( &m_roi_seq->storage );
m_roi_seq = 0;
}
//cvDestroyWindow( "EnteringBlobDetectionDebug" );
} /* cvReleaseBlobDetector */
/* cvDetectNewBlobs
* Return 1 and fill blob pNewBlob with
* blob parameters if new blob is detected:
*/
int CvBlobDetectorCC::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
{
int result = 0;
CvSize S = cvSize(pFGMask->width,pFGMask->height);
/* Shift blob list: */
{
int i;
if(m_pBlobLists[SEQ_SIZE-1]) delete m_pBlobLists[SEQ_SIZE-1];
for(i=SEQ_SIZE-1; i>0; --i) m_pBlobLists[i] = m_pBlobLists[i-1];
m_pBlobLists[0] = new CvBlobSeq;
} /* Shift blob list. */
/* Create contours and add new blobs to blob list: */
{ /* Create blobs: */
CvBlobSeq Blobs;
CvMemStorage* storage = cvCreateMemStorage();
if(m_Clastering)
{ /* Glue contours: */
cvFindBlobsByCCClasters(pFGMask, &Blobs, storage );
} /* Glue contours. */
else
{ /**/
IplImage* pIB = cvCloneImage(pFGMask);
CvSeq* cnts = NULL;
CvSeq* cnt = NULL;
cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY);
cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL);
/* Process each contour: */
for(cnt = cnts; cnt; cnt=cnt->h_next)
{
CvBlob NewBlob;
/* Image moments: */
double M00,X,Y,XX,YY;
CvMoments m;
CvRect r = ((CvContour*)cnt)->rect;
CvMat mat;
if(r.height < S.height*m_HMin || r.width < S.width*m_WMin) continue;
cvMoments( cvGetSubRect(pFGMask,&mat,r), &m, 0 );
M00 = cvGetSpatialMoment( &m, 0, 0 );
if(M00 <= 0 ) continue;
X = cvGetSpatialMoment( &m, 1, 0 )/M00;
Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;
NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY)));
Blobs.AddBlob(&NewBlob);
} /* Next contour. */
cvReleaseImage(&pIB);
} /* One contour - one blob. */
{ /* Delete small and intersected blobs: */
int i;
for(i=Blobs.GetBlobNum(); i>0; i--)
{
CvBlob* pB = Blobs.GetBlob(i-1);
if(pB->h < S.height*m_HMin || pB->w < S.width*m_WMin)
{
Blobs.DelBlob(i-1);
continue;
}
if(pOldBlobList)
{
int j;
for(j=pOldBlobList->GetBlobNum(); j>0; j--)
{
CvBlob* pBOld = pOldBlobList->GetBlob(j-1);
if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) &&
(fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB))))
{ /* Intersection detected, delete blob from list: */
Blobs.DelBlob(i-1);
break;
}
} /* Check next old blob. */
} /* if pOldBlobList. */
} /* Check next blob. */
} /* Delete small and intersected blobs. */
{ /* Bubble-sort blobs by size: */
int N = Blobs.GetBlobNum();
int i,j;
for(i=1; i<N; ++i)
{
for(j=i; j>0; --j)
{
CvBlob temp;
float AreaP, AreaN;
CvBlob* pP = Blobs.GetBlob(j-1);
CvBlob* pN = Blobs.GetBlob(j);
AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP);
AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN);
if(AreaN < AreaP)break;
temp = pN[0];
pN[0] = pP[0];
pP[0] = temp;
}
}
/* Copy only first 10 blobs: */
for(i=0; i<MIN(N,10); ++i)
{
m_pBlobLists[0]->AddBlob(Blobs.GetBlob(i));
}
} /* Sort blobs by size. */
cvReleaseMemStorage(&storage);
} /* Create blobs. */
{ /* Shift each track: */
int j;
for(j=0; j<m_TrackNum; ++j)
{
int i;
DefSeq* pTrack = m_TrackSeq+j;
for(i=SEQ_SIZE-1; i>0; --i)
pTrack->pBlobs[i] = pTrack->pBlobs[i-1];
pTrack->pBlobs[0] = NULL;
if(pTrack->size == SEQ_SIZE)pTrack->size--;
}
} /* Shift each track. */
/* Analyze blob list to find best blob trajectory: */
{
double BestError = -1;
int BestTrack = -1;;
CvBlobSeq* pNewBlobs = m_pBlobLists[0];
int i;
int NewTrackNum = 0;
for(i=pNewBlobs->GetBlobNum(); i>0; --i)
{
CvBlob* pBNew = pNewBlobs->GetBlob(i-1);
int j;
int AsignedTrack = 0;
for(j=0; j<m_TrackNum; ++j)
{
double dx,dy;
DefSeq* pTrack = m_TrackSeq+j;
CvBlob* pLastBlob = pTrack->size>0?pTrack->pBlobs[1]:NULL;
if(pLastBlob == NULL) continue;
dx = fabs(CV_BLOB_X(pLastBlob)-CV_BLOB_X(pBNew));
dy = fabs(CV_BLOB_Y(pLastBlob)-CV_BLOB_Y(pBNew));
if(dx > 2*CV_BLOB_WX(pLastBlob) || dy > 2*CV_BLOB_WY(pLastBlob)) continue;
AsignedTrack++;
if(pTrack->pBlobs[0]==NULL)
{ /* Fill existed track: */
pTrack->pBlobs[0] = pBNew;
pTrack->size++;
}
else if((m_TrackNum+NewTrackNum)<SEQ_NUM)
{ /* Duplicate existed track: */
m_TrackSeq[m_TrackNum+NewTrackNum] = pTrack[0];
m_TrackSeq[m_TrackNum+NewTrackNum].pBlobs[0] = pBNew;
NewTrackNum++;
}
} /* Next track. */
if(AsignedTrack==0 && (m_TrackNum+NewTrackNum)<SEQ_NUM )
{ /* Initialize new track: */
m_TrackSeq[m_TrackNum+NewTrackNum].size = 1;
m_TrackSeq[m_TrackNum+NewTrackNum].pBlobs[0] = pBNew;
NewTrackNum++;
}
} /* Next new blob. */
m_TrackNum += NewTrackNum;
/* Check each track: */
for(i=0; i<m_TrackNum; ++i)
{
int Good = 1;
DefSeq* pTrack = m_TrackSeq+i;
CvBlob* pBNew = pTrack->pBlobs[0];
if(pTrack->size != SEQ_SIZE) continue;
if(pBNew == NULL ) continue;
/* Check intersection last blob with existed: */
if(Good && pOldBlobList)
{
int k;
for(k=pOldBlobList->GetBlobNum(); k>0; --k)
{
CvBlob* pBOld = pOldBlobList->GetBlob(k-1);
if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) &&
(fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew))))
Good = 0;
}
} /* Check intersection last blob with existed. */
/* Check distance to image border: */
if(Good)
{ /* Check distance to image border: */
float dx = MIN(pBNew->x,S.width-pBNew->x)/CV_BLOB_RX(pBNew);
float dy = MIN(pBNew->y,S.height-pBNew->y)/CV_BLOB_RY(pBNew);
if(dx < m_MinDistToBorder || dy < m_MinDistToBorder) Good = 0;
} /* Check distance to image border. */
/* Check uniform motion: */
if(Good)
{ /* Check uniform motion: */
double Error = 0;
int N = pTrack->size;
CvBlob** pBL = pTrack->pBlobs;
float sum[2] = {0,0};
float jsum[2] = {0,0};
float a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/
int j;
for(j=0; j<N; ++j)
{
float x = pBL[j]->x;
float y = pBL[j]->y;
sum[0] += x;
jsum[0] += j*x;
sum[1] += y;
jsum[1] += j*y;
}
a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1));
b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1));
a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1));
b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1));
for(j=0; j<N; ++j)
{
Error +=
pow(a[0]*j+b[0]-pBL[j]->x,2)+
pow(a[1]*j+b[1]-pBL[j]->y,2);
}
Error = sqrt(Error/N);
if( Error > S.width*0.01 ||
fabs(a[0])>S.width*0.1 ||
fabs(a[1])>S.height*0.1)
Good = 0;
/* New best trajectory: */
if(Good && (BestError == -1 || BestError > Error))
{ /* New best trajectory: */
BestTrack = i;
BestError = Error;
} /* New best trajectory. */
} /* Check uniform motion. */
} /* Next track. */
#if 0
{ /**/
printf("BlobDetector configurations = %d [",m_TrackNum);
int i;
for(i=0; i<SEQ_SIZE; ++i)
{
printf("%d,",m_pBlobLists[i]?m_pBlobLists[i]->GetBlobNum():0);
}
printf("]\n");
}
#endif
if(BestTrack >= 0)
{ /* Put new blob to output and delete from blob list: */
assert(m_TrackSeq[BestTrack].size == SEQ_SIZE);
assert(m_TrackSeq[BestTrack].pBlobs[0]);
pNewBlobList->AddBlob(m_TrackSeq[BestTrack].pBlobs[0]);
m_TrackSeq[BestTrack].pBlobs[0] = NULL;
m_TrackSeq[BestTrack].size--;
result = 1;
} /* Put new blob to output and mark in blob list to delete. */
} /* Analyze blod list to find best blob trajectory. */
{ /* Delete bad tracks: */
int i;
for(i=m_TrackNum-1; i>=0; --i)
{ /* Delete bad tracks: */
if(m_TrackSeq[i].pBlobs[0]) continue;
if(m_TrackNum>0)
m_TrackSeq[i] = m_TrackSeq[--m_TrackNum];
} /* Delete bad tracks: */
}
#ifdef USE_OBJECT_DETECTOR
if( m_split_detector && pNewBlobList->GetBlobNum() > 0 )
{
int num_new_blobs = pNewBlobList->GetBlobNum();
int i = 0;
if( m_roi_seq ) cvClearSeq( m_roi_seq );
m_debug_blob_seq.Clear();
for( i = 0; i < num_new_blobs; ++i )
{
CvBlob* b = pNewBlobList->GetBlob(i);
CvMat roi_stub;
CvMat* roi_mat = 0;
CvMat* scaled_roi_mat = 0;
CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 0 );
m_debug_blob_seq.AddBlob(&d_b);
float scale = m_param_roi_scale * m_min_window_size.height / CV_BLOB_WY(b);
float b_width = MAX(CV_BLOB_WX(b), m_min_window_size.width / scale)
+ (m_param_roi_scale - 1.0F) * (m_min_window_size.width / scale)
+ 2.0F * m_max_border / scale;
float b_height = CV_BLOB_WY(b) * m_param_roi_scale + 2.0F * m_max_border / scale;
CvRect roi = cvRectIntersection( cvRect( cvFloor(CV_BLOB_X(b) - 0.5F*b_width),
cvFloor(CV_BLOB_Y(b) - 0.5F*b_height),
cvCeil(b_width), cvCeil(b_height) ),
cvRect( 0, 0, pImg->width, pImg->height ) );
if( roi.width <= 0 || roi.height <= 0 )
continue;
if( m_roi_seq ) cvSeqPush( m_roi_seq, &roi );
roi_mat = cvGetSubRect( pImg, &roi_stub, roi );
scaled_roi_mat = cvCreateMat( cvCeil(scale*roi.height), cvCeil(scale*roi.width), CV_8UC3 );
cvResize( roi_mat, scaled_roi_mat );
m_detected_blob_seq.Clear();
m_split_detector->Detect( scaled_roi_mat, &m_detected_blob_seq );
cvReleaseMat( &scaled_roi_mat );
for( int k = 0; k < m_detected_blob_seq.GetBlobNum(); ++k )
{
CvDetectedBlob* b = (CvDetectedBlob*) m_detected_blob_seq.GetBlob(k);
/* scale and shift each detected blob back to the original image coordinates */
CV_BLOB_X(b) = CV_BLOB_X(b) / scale + roi.x;
CV_BLOB_Y(b) = CV_BLOB_Y(b) / scale + roi.y;
CV_BLOB_WX(b) /= scale;
CV_BLOB_WY(b) /= scale;
CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 1,
b->response );
m_debug_blob_seq.AddBlob(&d_b);
}
if( m_detected_blob_seq.GetBlobNum() > 1 )
{
/*
* Split blob.
* The original blob is replaced by the first detected blob,
* remaining detected blobs are added to the end of the sequence:
*/
CvBlob* first_b = m_detected_blob_seq.GetBlob(0);
CV_BLOB_X(b) = CV_BLOB_X(first_b); CV_BLOB_Y(b) = CV_BLOB_Y(first_b);
CV_BLOB_WX(b) = CV_BLOB_WX(first_b); CV_BLOB_WY(b) = CV_BLOB_WY(first_b);
for( int j = 1; j < m_detected_blob_seq.GetBlobNum(); ++j )
{
CvBlob* detected_b = m_detected_blob_seq.GetBlob(j);
pNewBlobList->AddBlob(detected_b);
}
}
} /* For each new blob. */
for( i = 0; i < pNewBlobList->GetBlobNum(); ++i )
{
CvBlob* b = pNewBlobList->GetBlob(i);
CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 2 );
m_debug_blob_seq.AddBlob(&d_b);
}
} // if( m_split_detector )
#endif
return result;
} /* cvDetectNewBlob */