--- /dev/null
+/*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
+// For Open Source Computer Vision Library
+//
+// 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*/
+
+#include "_cvaux.h"
+
+typedef struct DefTrackPoint
+{
+ float x,y,r,vx,vy,v;
+} DefTrackPoint;
+
+class DefTrackRec
+{
+private:
+ int ID;
+public:
+ DefTrackRec(int id = 0,int BlobSize = sizeof(DefTrackPoint))
+ {
+ ID = id;
+ m_pMem = cvCreateMemStorage();
+ m_pSeq = cvCreateSeq(0,sizeof(CvSeq),BlobSize,m_pMem);
+ }
+ ~DefTrackRec()
+ {
+ cvReleaseMemStorage(&m_pMem);
+ };
+ inline DefTrackPoint* GetPoint(int PointIndex)
+ {
+ return (DefTrackPoint*)cvGetSeqElem(m_pSeq,PointIndex);
+ };
+ inline void DelPoint(int PointIndex)
+ {
+ cvSeqRemove(m_pSeq,PointIndex);
+ };
+ inline void Clear()
+ {
+ cvClearSeq(m_pSeq);
+ };
+ inline void AddPoint(float x, float y, float r)
+ {
+ DefTrackPoint p = {x,y,r,0};
+ int Num = GetPointNum();
+
+ if(Num > 0)
+ {
+ DefTrackPoint* pPrev = GetPoint(Num-1);
+ float Alpha = 0.8f;
+ float dx = x-pPrev->x;
+ float dy = y-pPrev->y;
+ p.vx = Alpha*dx+(1-Alpha)*pPrev->vx;
+ p.vy = Alpha*dy+(1-Alpha)*pPrev->vy;
+ p.v = Alpha*dx+(1-Alpha)*pPrev->v;
+ }
+ AddPoint(&p);
+ }
+
+ inline void AddPoint(DefTrackPoint* pB)
+ { /* Add point and recalculate last velocities: */
+ int wnd=3;
+ int Num;
+ int i;
+ cvSeqPush(m_pSeq,pB);
+
+ Num = GetPointNum();
+
+ for(i=MAX(0,Num-wnd-1); i<Num; ++i)
+ { /* Next updating point: */
+ DefTrackPoint* p = GetPoint(i);
+ int j0 = i - wnd;
+ int j1 = i + wnd;
+
+ if(j0<0) j0 = 0;
+ if(j1>=Num)j1=Num-1;
+
+ if(j1>j0)
+ {
+ float dt = (float)(j1-j0);
+ DefTrackPoint* p0 = GetPoint(j0);
+ DefTrackPoint* p1 = GetPoint(j1);
+ p->vx = (p1->x - p0->x) / dt;
+ p->vy = (p1->y - p0->y) / dt;
+ p->v = (float)sqrt(p->vx*p->vx+p->vy*p->vy);
+ }
+ } /* Next updating point. */
+
+#if 0
+ if(0)
+ { /* Debug: */
+ int i;
+ printf("Blob %d: ",ID);
+
+ for(i=0; i<GetPointNum(); ++i)
+ {
+ DefTrackPoint* p = GetPoint(i);
+ printf(",(%.2f,%.2f,%f.2)",p->vx,p->vy,p->v);
+ }
+ printf("\n");
+ }
+#endif
+ };
+ inline int GetPointNum()
+ {
+ return m_pSeq->total;
+ };
+private:
+ CvMemStorage* m_pMem;
+ CvSeq* m_pSeq;
+};
+
+/* Fill array pIdxPairs by pair of index of correspondent blobs. */
+/* Return number of pairs. */
+/* pIdxPairs must have size not less that 2*(pSeqNum+pSeqTNum) */
+/* pTmp is pointer to memory which size is pSeqNum*pSeqTNum*16 */
+typedef struct DefMatch
+{
+ int Idx; /* Previous best blob index. */
+ int IdxT; /* Previous best template blob index. */
+ double D; /* Blob to blob distance sum. */
+} DefMatch;
+
+static int cvTrackMatch(DefTrackRec* pSeq, int MaxLen, DefTrackRec* pSeqT, int* pIdxPairs, void* pTmp)
+{
+ int NumPair = 0;
+ DefMatch* pMT = (DefMatch*)pTmp;
+ int Num = pSeq->GetPointNum();
+ int NumT = pSeqT->GetPointNum();
+ int i,it;
+ int i0=0; /* Last point in the track sequence. */
+
+ if(MaxLen > 0 && Num > MaxLen)
+ { /* Set new point seq len and new last point in this seq: */
+ Num = MaxLen;
+ i0 = pSeq->GetPointNum() - Num;
+ }
+
+ for(i=0; i<Num; ++i)
+ { /* For each point row: */
+ for(it=0; it<NumT; ++it)
+ { /* For each point template column: */
+ DefTrackPoint* pB = pSeq->GetPoint(i+i0);
+ DefTrackPoint* pBT = pSeqT->GetPoint(it);
+ DefMatch* pMT_cur = pMT + i*NumT + it;
+ double dx = pB->x-pBT->x;
+ double dy = pB->y-pBT->y;
+ double D = dx*dx+dy*dy;
+ int DI[3][2] = {{-1,-1},{-1,0},{0,-1}};
+ int iDI;
+
+ pMT_cur->D = D;
+ pMT_cur->Idx = -1;
+ pMT_cur->IdxT = 0;
+
+ if(i==0) continue;
+
+ for(iDI=0; iDI<3; ++iDI)
+ {
+ int i_prev = i+DI[iDI][0];
+ int it_prev = it+DI[iDI][1];
+
+ if(i_prev >= 0 && it_prev>=0)
+ {
+ double D_cur = D+pMT[NumT*i_prev+it_prev].D;
+
+ if(pMT_cur->D > D_cur || (pMT_cur->Idx<0) )
+ { /* Set new best local way: */
+ pMT_cur->D = D_cur;
+ pMT_cur->Idx = i_prev;
+ pMT_cur->IdxT = it_prev;
+ }
+ }
+ } /* Check next direction. */
+ } /* Fill next colum from table. */
+ } /* Fill next row. */
+
+ { /* Back tracking. */
+ /* Find best end in template: */
+ int it_best = 0;
+ DefMatch* pMT_best = pMT + (Num-1)*NumT;
+ i = Num-1; /* set current i to last position */
+
+ for(it=1; it<NumT; ++it)
+ {
+ DefMatch* pMT_new = pMT + it + i*NumT;
+
+ if(pMT_best->D > pMT_new->D)
+ {
+ pMT_best->D = pMT_new->D;
+ it_best = it;
+ }
+ } /* Find best end template point. */
+
+ /* Back tracking whole sequence: */
+ for(it = it_best;i>=0 && it>=0;)
+ {
+ DefMatch* pMT_new = pMT + it + i*NumT;
+ pIdxPairs[2*NumPair] = i+i0;
+ pIdxPairs[2*NumPair+1] = it;
+ NumPair++;
+
+ it = pMT_new->IdxT;
+ i = pMT_new->Idx;
+ }
+ } /* End back tracing. */
+
+ return NumPair;
+} /* cvTrackMatch. */
+
+typedef struct DefTrackForDist
+{
+ CvBlob blob;
+ DefTrackRec* pTrack;
+ int LastFrame;
+ float state;
+ /* for debug */
+ int close;
+} DefTrackForDist;
+
+class CvBlobTrackAnalysisTrackDist : public CvBlobTrackAnalysis
+{
+ /*---------------- Internal functions: --------------------*/
+private:
+ const char* m_pDebugAVIName; /* For debugging. */
+ //CvVideoWriter* m_pDebugAVI; /* For debugging. */
+ IplImage* m_pDebugImg; /* For debugging. */
+
+ char m_DataFileName[1024];
+ CvBlobSeq m_Tracks;
+ CvBlobSeq m_TrackDataBase;
+ int m_Frame;
+ void* m_pTempData;
+ int m_TempDataSize;
+ int m_TraceLen;
+ float m_AbnormalThreshold;
+ float m_PosThreshold;
+ float m_VelThreshold;
+ inline void* ReallocTempData(int Size)
+ {
+ if(Size <= m_TempDataSize && m_pTempData) return m_pTempData;
+ cvFree(&m_pTempData);
+ m_TempDataSize = 0;
+ m_pTempData = cvAlloc(Size);
+ if(m_pTempData) m_TempDataSize = Size;
+ return m_pTempData;
+ } /* ReallocTempData. */
+
+public:
+ CvBlobTrackAnalysisTrackDist():m_Tracks(sizeof(DefTrackForDist)),m_TrackDataBase(sizeof(DefTrackForDist))
+ {
+ m_pDebugImg = 0;
+ //m_pDebugAVI = 0;
+ m_Frame = 0;
+ m_pTempData = NULL;
+ m_TempDataSize = 0;
+
+ m_pDebugAVIName = NULL;
+ AddParam("DebugAVI",&m_pDebugAVIName);
+ CommentParam("DebugAVI","Name of AVI file to save images from debug window");
+
+ m_TraceLen = 50;
+ AddParam("TraceLen",&m_TraceLen);
+ CommentParam("TraceLen","Length (in frames) of trajectory part that is used for comparison");
+
+ m_AbnormalThreshold = 0.02f;
+ AddParam("AbnormalThreshold",&m_AbnormalThreshold);
+ CommentParam("AbnormalThreshold","If trajectory is equal with less then <AbnormalThreshold*DataBaseTrackNum> tracks then trajectory is abnormal");
+
+ m_PosThreshold = 1.25;
+ AddParam("PosThreshold",&m_PosThreshold);
+ CommentParam("PosThreshold","Minimal allowd distance in blob width that is allowed");
+
+ m_VelThreshold = 0.5;
+ AddParam("VelThreshold",&m_VelThreshold);
+ CommentParam("VelThreshold","Minimal allowed relative difference between blob speed");
+
+ SetModuleName("TrackDist");
+
+ } /* Constructor. */
+
+ ~CvBlobTrackAnalysisTrackDist()
+ {
+ int i;
+ for(i=m_Tracks.GetBlobNum(); i>0; --i)
+ {
+ DefTrackForDist* pF = (DefTrackForDist*)m_Tracks.GetBlob(i-1);
+ delete pF->pTrack;
+ }
+ if(m_pDebugImg) cvReleaseImage(&m_pDebugImg);
+ //if(m_pDebugAVI) cvReleaseVideoWriter(&m_pDebugAVI);
+ } /* Destructor. */
+
+ /*----------------- Interface: --------------------*/
+ virtual void AddBlob(CvBlob* pBlob)
+ {
+ DefTrackForDist* pF = (DefTrackForDist*)m_Tracks.GetBlobByID(CV_BLOB_ID(pBlob));
+
+ if(pF == NULL)
+ { /* Create new TRack record: */
+ DefTrackForDist F;
+ F.state = 0;
+ F.blob = pBlob[0];
+ F.LastFrame = m_Frame;
+ F.pTrack = new DefTrackRec(CV_BLOB_ID(pBlob));
+ m_Tracks.AddBlob((CvBlob*)&F);
+ pF = (DefTrackForDist*)m_Tracks.GetBlobByID(CV_BLOB_ID(pBlob));
+ }
+
+ assert(pF);
+ assert(pF->pTrack);
+ pF->pTrack->AddPoint(pBlob->x,pBlob->y,pBlob->w*0.5f);
+ pF->blob = pBlob[0];
+ pF->LastFrame = m_Frame;
+ };
+
+ virtual void Process(IplImage* pImg, IplImage* /*pFG*/)
+ {
+ int i;
+ double MinTv = pImg->width/1440.0; /* minimal threshold for speed difference */
+ double MinTv2 = MinTv*MinTv;
+
+ for(i=m_Tracks.GetBlobNum(); i>0; --i)
+ {
+ DefTrackForDist* pF = (DefTrackForDist*)m_Tracks.GetBlob(i-1);
+ pF->state = 0;
+
+ if(pF->LastFrame == m_Frame || pF->LastFrame+1 == m_Frame)
+ { /* Process one blob trajectory: */
+ int NumEq = 0;
+ int it;
+
+ for(it=m_TrackDataBase.GetBlobNum(); it>0; --it)
+ { /* Check template: */
+ DefTrackForDist* pFT = (DefTrackForDist*)m_TrackDataBase.GetBlob(it-1);
+ int Num = pF->pTrack->GetPointNum();
+ int NumT = pFT->pTrack->GetPointNum();
+ int* pPairIdx = (int*)ReallocTempData(sizeof(int)*2*(Num+NumT)+sizeof(DefMatch)*Num*NumT);
+ void* pTmpData = pPairIdx+2*(Num+NumT);
+ int PairNum = 0;
+ int k;
+ int Equal = 1;
+ int UseVel = 0;
+ int UsePos = 0;
+
+ if(i==it) continue;
+
+ /* Match track: */
+ PairNum = cvTrackMatch( pF->pTrack, m_TraceLen, pFT->pTrack, pPairIdx, pTmpData );
+ Equal = MAX(1,cvRound(PairNum*0.1));
+
+ UseVel = 3*pF->pTrack->GetPointNum() > m_TraceLen;
+ UsePos = 10*pF->pTrack->GetPointNum() > m_TraceLen;
+
+ { /* Check continues: */
+ float D;
+ int DI = pPairIdx[0*2+0]-pPairIdx[(PairNum-1)*2+0];
+ int DIt = pPairIdx[0*2+1]-pPairIdx[(PairNum-1)*2+1];
+ if(UseVel && DI != 0)
+ {
+ D = (float)(DI-DIt)/(float)DI;
+ if(fabs(D)>m_VelThreshold)Equal=0;
+ if(fabs(D)>m_VelThreshold*0.5)Equal/=2;
+ }
+ } /* Check continues. */
+
+ for(k=0; Equal>0 && k<PairNum; ++k)
+ { /* Compare with threshold: */
+ int j = pPairIdx[k*2+0];
+ int jt = pPairIdx[k*2+1];
+ DefTrackPoint* pB = pF->pTrack->GetPoint(j);
+ DefTrackPoint* pBT = pFT->pTrack->GetPoint(jt);
+ double dx = pB->x-pBT->x;
+ double dy = pB->y-pBT->y;
+ double dvx = pB->vx - pBT->vx;
+ double dvy = pB->vy - pBT->vy;
+ //double dv = pB->v - pBT->v;
+ double D = dx*dx+dy*dy;
+ double Td = pBT->r*m_PosThreshold;
+ double dv2 = dvx*dvx+dvy*dvy;
+ double Tv2 = (pBT->vx*pBT->vx+pBT->vy*pBT->vy)*m_VelThreshold*m_VelThreshold;
+ double Tvm = pBT->v*m_VelThreshold;
+
+
+ if(Tv2 < MinTv2) Tv2 = MinTv2;
+ if(Tvm < MinTv) Tvm = MinTv;
+
+ /* Check trajectory position: */
+ if(UsePos && D > Td*Td)
+ {
+ Equal--;
+ }
+ else
+ /* Check trajectory velocity. */
+ /* Don't consider trajectory tail because its unstable for velocity computation. */
+ if(UseVel && j>5 && jt>5 && dv2 > Tv2 )
+ {
+ Equal--;
+ }
+ } /* Compare with threshold. */
+
+ if(Equal>0)
+ {
+ NumEq++;
+ pFT->close++;
+ }
+ } /* Next template. */
+
+ { /* Calculate state: */
+ float T = m_TrackDataBase.GetBlobNum() * m_AbnormalThreshold; /* calc threshold */
+
+ if(T>0)
+ {
+ pF->state = (T - NumEq)/(T*0.2f) + 0.5f;
+ }
+ if(pF->state<0)pF->state=0;
+ if(pF->state>1)pF->state=1;
+
+ /*if(0)if(pF->state>0)
+ {// if abnormal blob
+ printf("Abnormal blob(%d) %d < %f, state=%f\n",CV_BLOB_ID(pF),NumEq,T, pF->state);
+ }*/
+ } /* Calculate state. */
+ } /* Process one blob trajectory. */
+ else
+ { /* Move track to tracks data base: */
+ m_TrackDataBase.AddBlob((CvBlob*)pF);
+ m_Tracks.DelBlob(i-1);
+ }
+ } /* Next blob. */
+
+
+ if(m_Wnd)
+ { /* Debug output: */
+ int i;
+
+ if(m_pDebugImg==NULL)
+ m_pDebugImg = cvCloneImage(pImg);
+ else
+ cvCopyImage(pImg, m_pDebugImg);
+
+ for(i=m_TrackDataBase.GetBlobNum(); i>0; --i)
+ { /* Draw all elements in track data base: */
+ int j;
+ DefTrackForDist* pF = (DefTrackForDist*)m_TrackDataBase.GetBlob(i-1);
+ CvScalar color = CV_RGB(0,0,0);
+ if(!pF->close) continue;
+ if(pF->close)
+ {
+ color = CV_RGB(0,0,255);
+ }
+ else
+ {
+ color = CV_RGB(0,0,128);
+ }
+
+ for(j=pF->pTrack->GetPointNum(); j>0; j--)
+ {
+ DefTrackPoint* pB = pF->pTrack->GetPoint(j-1);
+ int r = 0;//MAX(cvRound(pB->r),1);
+ cvCircle(m_pDebugImg, cvPoint(cvRound(pB->x),cvRound(pB->y)), r, color);
+ }
+ pF->close = 0;
+ } /* Draw all elements in track data base. */
+
+ for(i=m_Tracks.GetBlobNum(); i>0; --i)
+ { /* Draw all elements for all trajectories: */
+ DefTrackForDist* pF = (DefTrackForDist*)m_Tracks.GetBlob(i-1);
+ int j;
+ int c = cvRound(pF->state*255);
+ CvScalar color = CV_RGB(c,255-c,0);
+ CvPoint p = cvPointFrom32f(CV_BLOB_CENTER(pF));
+ int x = cvRound(CV_BLOB_RX(pF)), y = cvRound(CV_BLOB_RY(pF));
+ CvSize s = cvSize(MAX(1,x), MAX(1,y));
+
+ cvEllipse( m_pDebugImg,
+ p,
+ s,
+ 0, 0, 360,
+ CV_RGB(c,255-c,0), cvRound(1+(0*c)/255) );
+
+ for(j=pF->pTrack->GetPointNum(); j>0; j--)
+ {
+ DefTrackPoint* pB = pF->pTrack->GetPoint(j-1);
+ if(pF->pTrack->GetPointNum()-j > m_TraceLen) break;
+ cvCircle(m_pDebugImg, cvPoint(cvRound(pB->x),cvRound(pB->y)), 0, color);
+ }
+ pF->close = 0;
+
+ } /* Draw all elements for all trajectories. */
+
+ //cvNamedWindow("Tracks",0);
+ //cvShowImage("Tracks", m_pDebugImg);
+ } /* Debug output. */
+
+#if 0
+ if(m_pDebugImg && m_pDebugAVIName)
+ {
+ if(m_pDebugAVI==NULL)
+ { /* Create avi file for writing: */
+ m_pDebugAVI = cvCreateVideoWriter(
+ m_pDebugAVIName,
+ CV_FOURCC('x','v','i','d'),
+ 25,
+ cvSize(m_pDebugImg->width,m_pDebugImg->height));
+
+ if(m_pDebugAVI == NULL)
+ {
+ printf("WARNING!!! Can not create AVI file %s for writing\n",m_pDebugAVIName);
+ }
+ } /* Create avi file for writing. */
+
+ if(m_pDebugAVI)cvWriteFrame( m_pDebugAVI, m_pDebugImg );
+ } /* Write debug window to AVI file. */
+#endif
+ m_Frame++;
+ };
+ float GetState(int BlobID)
+ {
+ DefTrackForDist* pF = (DefTrackForDist*)m_Tracks.GetBlobByID(BlobID);
+ return pF?pF->state:0.0f;
+ };
+
+ /* Return 0 if trajectory is normal;
+ return >0 if trajectory abnormal. */
+ virtual const char* GetStateDesc(int BlobID)
+ {
+ if(GetState(BlobID)>0.5) return "abnormal";
+ return NULL;
+ }
+
+ virtual void SetFileName(char* DataBaseName)
+ {
+ m_DataFileName[0] = 0;
+ if(DataBaseName)
+ {
+ strncpy(m_DataFileName,DataBaseName,1000);
+ strcat(m_DataFileName, ".yml");
+ }
+ };
+
+ virtual void Release(){ delete this; };
+};
+
+
+
+CvBlobTrackAnalysis* cvCreateModuleBlobTrackAnalysisTrackDist()
+{return (CvBlobTrackAnalysis*) new CvBlobTrackAnalysisTrackDist;}
+