Update to 2.0.0 tree from current Fremantle build
[opencv] / src / cvaux / vs / blobtrackingmsfg.cpp
diff --git a/src/cvaux/vs/blobtrackingmsfg.cpp b/src/cvaux/vs/blobtrackingmsfg.cpp
new file mode 100644 (file)
index 0000000..e275535
--- /dev/null
@@ -0,0 +1,1180 @@
+/*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*/
+
+#include "_cvaux.h"
+
+// Uncomment to trade flexibility for speed
+//#define CONST_HIST_SIZE
+
+// Uncomment to get some performance stats in stderr
+//#define REPORT_TICKS
+
+#ifdef CONST_HIST_SIZE
+#define m_BinBit 5
+#define m_ByteShift 3
+#endif
+
+typedef float DefHistType;
+#define DefHistTypeMat CV_32F
+#define HIST_INDEX(_pData) (((_pData)[0]>>m_ByteShift) + (((_pData)[1]>>(m_ByteShift))<<m_BinBit)+((pImgData[2]>>m_ByteShift)<<(m_BinBit*2)))
+
+class DefHist
+{
+public:
+    CvMat*          m_pHist;
+    DefHistType     m_HistVolume;
+    DefHist(int BinNum=0)
+    {
+        m_pHist = NULL;
+        m_HistVolume = 0;
+        Resize(BinNum);
+    }
+
+    ~DefHist()
+    {
+        if(m_pHist)cvReleaseMat(&m_pHist);
+    }
+
+    void Resize(int BinNum)
+    {
+        if(m_pHist)cvReleaseMat(&m_pHist);
+        if(BinNum>0)
+        {
+            m_pHist = cvCreateMat(1, BinNum, DefHistTypeMat);
+            cvZero(m_pHist);
+        }
+        m_HistVolume = 0;
+    }
+
+    void Update(DefHist* pH, float W)
+    {   /* Update histogram: */
+        double  Vol, WM, WC;
+        Vol = 0.5*(m_HistVolume + pH->m_HistVolume);
+        WM = Vol*(1-W)/m_HistVolume;
+        WC = Vol*(W)/pH->m_HistVolume;
+        cvAddWeighted(m_pHist, WM, pH->m_pHist,WC,0,m_pHist);
+        m_HistVolume = (float)cvSum(m_pHist).val[0];
+    }   /* Update histogram: */
+};
+
+class CvBlobTrackerOneMSFG:public CvBlobTrackerOne
+{
+protected:
+    int             m_BinNumTotal; /* protected only for parralel MSPF */
+    CvSize          m_ObjSize;
+
+    void ReAllocKernel(int  w, int h)
+    {
+        int     x,y;
+        float   x0 = 0.5f*(w-1);
+        float   y0 = 0.5f*(h-1);
+        assert(w>0);
+        assert(h>0);
+        m_ObjSize = cvSize(w,h);
+
+        if(m_KernelHist) cvReleaseMat(&m_KernelHist);
+        if(m_KernelMeanShift) cvReleaseMat(&m_KernelMeanShift);
+        m_KernelHist = cvCreateMat(h, w, DefHistTypeMat);
+        m_KernelMeanShift = cvCreateMat(h, w, DefHistTypeMat);
+
+        for(y=0; y<h; ++y) for(x=0; x<w; ++x)
+        {
+            double r2 = ((x-x0)*(x-x0)/(x0*x0)+(y-y0)*(y-y0)/(y0*y0));
+//            double r2 = ((x-x0)*(x-x0)+(y-y0)*(y-y0))/((y0*y0)+(x0*x0));
+            CV_MAT_ELEM(m_KernelHist[0],DefHistType, y, x) = (DefHistType)GetKernelHist(r2);
+            CV_MAT_ELEM(m_KernelMeanShift[0],DefHistType, y, x) = (DefHistType)GetKernelMeanShift(r2);
+        }
+    }
+
+private:
+    /* Parameters: */
+    int             m_IterNum;
+    float           m_FGWeight;
+    float           m_Alpha;
+    CvMat*          m_KernelHist;
+    CvMat*          m_KernelMeanShift;
+#ifndef CONST_HIST_SIZE
+    int             m_BinBit;
+    int             m_ByteShift;
+#endif
+    int             m_BinNum;
+    int             m_Dim;
+    /*
+    CvMat*          m_HistModel;
+    float           m_HistModelVolume;
+    CvMat*          m_HistCandidate;
+    float           m_HistCandidateVolume;
+    CvMat*          m_HistTemp;
+    */
+    DefHist         m_HistModel;
+    DefHist         m_HistCandidate;
+    DefHist         m_HistTemp;
+
+    CvBlob          m_Blob;
+    int             m_Collision;
+
+    void ReAllocHist(int Dim, int BinBit)
+    {
+#ifndef CONST_HIST_SIZE
+        m_BinBit = BinBit;
+        m_ByteShift = 8-BinBit;
+#endif
+        m_Dim = Dim;
+        m_BinNum = (1<<BinBit);
+        m_BinNumTotal = cvRound(pow((double)m_BinNum,(double)m_Dim));
+        /*
+        if(m_HistModel) cvReleaseMat(&m_HistModel);
+        if(m_HistCandidate) cvReleaseMat(&m_HistCandidate);
+        if(m_HistTemp) cvReleaseMat(&m_HistTemp);
+        m_HistCandidate = cvCreateMat(1, m_BinNumTotal, DefHistTypeMat);
+        m_HistModel = cvCreateMat(1, m_BinNumTotal, DefHistTypeMat);
+        m_HistTemp = cvCreateMat(1, m_BinNumTotal, DefHistTypeMat);
+        cvZero(m_HistCandidate);
+        cvZero(m_HistModel);
+        m_HistModelVolume = 0.0f;
+        m_HistCandidateVolume = 0.0f;
+        */
+        m_HistCandidate.Resize(m_BinNumTotal);
+        m_HistModel.Resize(m_BinNumTotal);
+        m_HistTemp.Resize(m_BinNumTotal);
+    }
+
+    double GetKernelHist(double r2)
+    {
+        return (r2 < 1) ? 1 -  r2 : 0;
+    }
+
+    double GetKernelMeanShift(double r2)
+    {
+        return (r2<1) ? 1 : 0;
+    }
+
+//    void CollectHist(IplImage* pImg, IplImage* pMask, CvPoint Center, CvMat* pHist, DefHistType* pHistVolume)
+//    void CollectHist(IplImage* pImg, IplImage* pMask, CvPoint Center, DefHist* pHist)
+
+    void CollectHist(IplImage* pImg, IplImage* pMask, CvBlob* pBlob, DefHist* pHist)
+    {
+        int UsePrecalculatedKernel = 0;
+        int BW = cvRound(pBlob->w);
+        int BH = cvRound(pBlob->h);
+        DefHistType Volume = 0;
+        int         x0 = cvRound(pBlob->x - BW*0.5);
+        int         y0 = cvRound(pBlob->y - BH*0.5);
+        int         x,y;
+
+        UsePrecalculatedKernel = (BW == m_ObjSize.width && BH == m_ObjSize.height ) ;
+
+        //cvZero(pHist);
+        cvSet(pHist->m_pHist,cvScalar(1.0/m_BinNumTotal)); /* no zero bins, all bins have very small value*/
+        Volume = 1;
+
+        assert(BW < pImg->width);
+        assert(BH < pImg->height);
+        if((x0+BW)>=pImg->width) BW=pImg->width-x0-1;
+        if((y0+BH)>=pImg->height) BH=pImg->height-y0-1;
+        if(x0<0){ x0=0;}
+        if(y0<0){ y0=0;}
+
+        if(m_Dim == 3)
+        {
+            for(y=0; y<BH; ++y)
+            {
+                unsigned char* pImgData = &CV_IMAGE_ELEM(pImg,unsigned char,y+y0,x0*3);
+                unsigned char* pMaskData = pMask?(&CV_IMAGE_ELEM(pMask,unsigned char,y+y0,x0)):NULL;
+                DefHistType* pKernelData = NULL;
+
+                if(UsePrecalculatedKernel)
+                {
+                    pKernelData = ((DefHistType*)CV_MAT_ELEM_PTR_FAST(m_KernelHist[0],y,0,sizeof(DefHistType)));
+                }
+
+                for(x=0; x<BW; ++x, pImgData+=3)
+                {
+                    DefHistType K;
+                    int index = HIST_INDEX(pImgData);
+                    assert(index >= 0 && index < pHist->m_pHist->cols);
+
+                    if(UsePrecalculatedKernel)
+                    {
+                        K = pKernelData[x];
+                    }
+                    else
+                    {
+                        float dx = (x+x0-pBlob->x)/(pBlob->w*0.5f);
+                        float dy = (y+y0-pBlob->y)/(pBlob->h*0.5f);
+                        double r2 = dx*dx+dy*dy;
+                        K = (float)GetKernelHist(r2);
+                    }
+
+                    if(pMaskData)
+                    {
+                        K *= pMaskData[x]*0.003921568627450980392156862745098f;
+                    }
+                    Volume += K;
+                    ((DefHistType*)(pHist->m_pHist->data.ptr))[index] += K;
+
+                }   /* Next column. */
+            }   /*  Next row. */
+        }   /* if m_Dim == 3. */
+
+        pHist->m_HistVolume = Volume;
+
+    };  /* CollectHist */
+
+    double calcBhattacharyya(DefHist* pHM = NULL, DefHist* pHC = NULL, DefHist* pHT = NULL)
+    {
+        if(pHM==NULL) pHM = &m_HistModel;
+        if(pHC==NULL) pHC = &m_HistCandidate;
+        if(pHT==NULL) pHT = &m_HistTemp;
+        if(pHC->m_HistVolume*pHM->m_HistVolume > 0)
+        {
+#if 0
+            // Use CV functions:
+            cvMul(pHM->m_pHist,pHC->m_pHist,pHT->m_pHist);
+            cvPow(pHT->m_pHist,pHT->m_pHist,0.5);
+            return cvSum(pHT->m_pHist).val[0] / sqrt(pHC->m_HistVolume*pHM->m_HistVolume);
+#else
+            // Do computations manually and let autovectorizer do the job:
+            DefHistType *hm, *hc, *ht;
+            double sum;
+            int     size;
+            hm=(DefHistType *)(pHM->m_pHist->data.ptr);
+            hc=(DefHistType *)(pHC->m_pHist->data.ptr);
+            ht=(DefHistType *)(pHT->m_pHist->data.ptr);
+            size = pHM->m_pHist->width*pHM->m_pHist->height;
+            sum = 0.;
+            for(int i = 0; i < size; i++ )
+            {
+                sum += sqrt(hm[i]*hc[i]);
+            }
+            return sum / sqrt(pHC->m_HistVolume*pHM->m_HistVolume);
+#endif
+        }
+        return 0;
+    }   /* calcBhattacharyyaCoefficient. */
+
+protected:
+    // double GetBhattacharyya(IplImage* pImg, IplImage* pImgFG, float x, float y, DefHist* pHist=NULL)
+    double GetBhattacharyya(IplImage* pImg, IplImage* pImgFG, CvBlob* pBlob, DefHist* pHist=NULL, int /*thread_number*/ = 0)
+    {
+        if(pHist==NULL)pHist = &m_HistTemp;
+        CollectHist(pImg, pImgFG, pBlob, pHist);
+        return calcBhattacharyya(&m_HistModel, pHist, pHist);
+    }
+
+    void UpdateModelHist(IplImage* pImg, IplImage* pImgFG, CvBlob* pBlob)
+    {
+        if(m_Alpha>0 && !m_Collision)
+        {   /* Update histogram: */
+            CollectHist(pImg, pImgFG, pBlob, &m_HistCandidate);
+            m_HistModel.Update(&m_HistCandidate, m_Alpha);
+        }   /* Update histogram. */
+
+    }   /* UpdateModelHist */
+
+public:
+    CvBlobTrackerOneMSFG()
+    {
+
+        /* Add several parameters for external use: */
+        m_FGWeight = 2;
+        AddParam("FGWeight", &m_FGWeight);
+        CommentParam("FGWeight","Weight of FG mask using (0 - mask will not be used for tracking)");
+
+        m_Alpha = 0.01f;
+        AddParam("Alpha", &m_Alpha);
+        CommentParam("Alpha","Coefficient for model histogram updating (0 - hist is not upated)");
+
+        m_IterNum = 10;
+        AddParam("IterNum", &m_IterNum);
+        CommentParam("IterNum","Maximal number of iteration in meanshift operation");
+
+        /* Initialize internal data: */
+        m_Collision = 0;
+//        m_BinBit=0;
+        m_Dim = 0;
+        /*
+        m_HistModel = NULL;
+        m_HistCandidate = NULL;
+        m_HistTemp = NULL;
+        */
+        m_KernelHist = NULL;
+        m_KernelMeanShift = NULL;
+        ReAllocHist(3,5);   /* 3D hist, each dim has 2^5 bins*/
+
+        SetModuleName("MSFG");
+    }
+
+    ~CvBlobTrackerOneMSFG()
+    {
+        /*
+        if(m_HistModel) cvReleaseMat(&m_HistModel);
+        if(m_HistCandidate) cvReleaseMat(&m_HistCandidate);
+        if(m_HistTemp) cvReleaseMat(&m_HistTemp);
+        */
+        if(m_KernelHist) cvReleaseMat(&m_KernelHist);
+        if(m_KernelMeanShift) cvReleaseMat(&m_KernelMeanShift);
+    }
+
+    /* Interface: */
+    virtual void Init(CvBlob* pBlobInit, IplImage* pImg, IplImage* pImgFG = NULL)
+    {
+        int w = cvRound(CV_BLOB_WX(pBlobInit));
+        int h = cvRound(CV_BLOB_WY(pBlobInit));
+        if(w<CV_BLOB_MINW)w=CV_BLOB_MINW;
+        if(h<CV_BLOB_MINH)h=CV_BLOB_MINH;
+        if(pImg)
+        {
+            if(w>pImg->width)w=pImg->width;
+            if(h>pImg->height)h=pImg->height;
+        }
+        ReAllocKernel(w,h);
+        if(pImg)
+            CollectHist(pImg, pImgFG, pBlobInit, &m_HistModel);
+        m_Blob = pBlobInit[0];
+    };
+
+    virtual CvBlob* Process(CvBlob* pBlobPrev, IplImage* pImg, IplImage* pImgFG = NULL)
+    {
+        int     iter;
+
+        if(pBlobPrev)
+        {
+            m_Blob = pBlobPrev[0];
+        }
+
+        {   /* Check blob size and realloc kernels if it is necessary: */
+            int w = cvRound(m_Blob.w);
+            int h = cvRound(m_Blob.h);
+            if( w != m_ObjSize.width || h!=m_ObjSize.height)
+            {
+                ReAllocKernel(w,h);
+                /* after this ( w != m_ObjSize.width || h!=m_ObjSize.height) shoiuld be false */
+            }
+        }   /* Check blob size and realloc kernels if it is necessary: */
+
+
+        for(iter=0; iter<m_IterNum; ++iter)
+        {
+            float   newx=0,newy=0,sum=0;
+            //int     x,y;
+            double  B0;
+
+            //CvPoint Center = cvPoint(cvRound(m_Blob.x),cvRound(m_Blob.y));
+            CollectHist(pImg, NULL, &m_Blob, &m_HistCandidate);
+            B0 = calcBhattacharyya();
+
+            if(m_Wnd)if(CV_BLOB_ID(pBlobPrev)==0 && iter == 0)
+            {   /* Debug: */
+                IplImage*   pW = cvCloneImage(pImgFG);
+                IplImage*   pWFG = cvCloneImage(pImgFG);
+                int         x,y;
+
+                cvZero(pW);
+                cvZero(pWFG);
+
+                assert(m_ObjSize.width < pImg->width);
+                assert(m_ObjSize.height < pImg->height);
+
+                /* Calculate shift vector: */
+                for(y=0; y<pImg->height; ++y)
+                {
+                    unsigned char* pImgData = &CV_IMAGE_ELEM(pImg,unsigned char,y,0);
+                    unsigned char* pMaskData = pImgFG?(&CV_IMAGE_ELEM(pImgFG,unsigned char,y,0)):NULL;
+
+                    for(x=0; x<pImg->width; ++x, pImgData+=3)
+                    {
+                        int         xk = cvRound(x-(m_Blob.x-m_Blob.w*0.5));
+                        int         yk = cvRound(y-(m_Blob.y-m_Blob.h*0.5));
+                        double      HM = 0;
+                        double      HC = 0;
+                        double      K;
+                        int index = HIST_INDEX(pImgData);
+                        assert(index >= 0 && index < m_BinNumTotal);
+
+                        if(fabs(x-m_Blob.x)>m_Blob.w*0.6) continue;
+                        if(fabs(y-m_Blob.y)>m_Blob.h*0.6) continue;
+
+                        if(xk < 0 || xk >= m_KernelMeanShift->cols) continue;
+                        if(yk < 0 || yk >= m_KernelMeanShift->rows) continue;
+
+                        if(m_HistModel.m_HistVolume>0)
+                            HM = ((DefHistType*)m_HistModel.m_pHist->data.ptr)[index]/m_HistModel.m_HistVolume;
+
+                        if(m_HistCandidate.m_HistVolume>0)
+                            HC = ((DefHistType*)m_HistCandidate.m_pHist->data.ptr)[index]/m_HistCandidate.m_HistVolume;
+
+                        K = *(DefHistType*)CV_MAT_ELEM_PTR_FAST(m_KernelMeanShift[0],yk,xk,sizeof(DefHistType));
+
+                        if(HC>0)
+                        {
+                            double  V = sqrt(HM / HC);
+                            int     Vi = cvRound(V * 64);
+                            if(Vi < 0) Vi = 0;
+                            if(Vi > 255) Vi = 255;
+                            CV_IMAGE_ELEM(pW,uchar,y,x) = (uchar)Vi;
+
+                            V += m_FGWeight*(pMaskData?(pMaskData[x]/255.0f):0);
+                            V*=K;
+                            Vi = cvRound(V * 64);
+                            if(Vi < 0) Vi = 0;
+                            if(Vi > 255) Vi = 255;
+                            CV_IMAGE_ELEM(pWFG,uchar,y,x) = (uchar)Vi;
+                        }
+
+                    }   /* Next column. */
+                }   /*  Next row. */
+
+                //cvNamedWindow("MSFG_W",0);
+                //cvShowImage("MSFG_W",pW);
+                //cvNamedWindow("MSFG_WFG",0);
+                //cvShowImage("MSFG_WFG",pWFG);
+                //cvNamedWindow("MSFG_FG",0);
+                //cvShowImage("MSFG_FG",pImgFG);
+
+                //cvSaveImage("MSFG_W.bmp",pW);
+                //cvSaveImage("MSFG_WFG.bmp",pWFG);
+                //cvSaveImage("MSFG_FG.bmp",pImgFG);
+
+            }   /* Debug. */
+
+
+            /* Calculate new position by meanshift: */
+
+            /* Calculate new position: */
+            if(m_Dim == 3)
+            {
+                int         x0 = cvRound(m_Blob.x - m_ObjSize.width*0.5);
+                int         y0 = cvRound(m_Blob.y - m_ObjSize.height*0.5);
+                int         x,y;
+
+                assert(m_ObjSize.width < pImg->width);
+                assert(m_ObjSize.height < pImg->height);
+
+                /* Crop blob bounds: */
+                if((x0+m_ObjSize.width)>=pImg->width) x0=pImg->width-m_ObjSize.width-1;
+                if((y0+m_ObjSize.height)>=pImg->height) y0=pImg->height-m_ObjSize.height-1;
+                if(x0<0){ x0=0;}
+                if(y0<0){ y0=0;}
+
+                /* Calculate shift vector: */
+                for(y=0; y<m_ObjSize.height; ++y)
+                {
+                    unsigned char* pImgData = &CV_IMAGE_ELEM(pImg,unsigned char,y+y0,x0*3);
+                    unsigned char* pMaskData = pImgFG?(&CV_IMAGE_ELEM(pImgFG,unsigned char,y+y0,x0)):NULL;
+                    DefHistType* pKernelData = (DefHistType*)CV_MAT_ELEM_PTR_FAST(m_KernelMeanShift[0],y,0,sizeof(DefHistType));
+
+                    for(x=0; x<m_ObjSize.width; ++x, pImgData+=3)
+                    {
+                        DefHistType K = pKernelData[x];
+                        double      HM = 0;
+                        double      HC = 0;
+                        int index = HIST_INDEX(pImgData);
+                        assert(index >= 0 && index < m_BinNumTotal);
+
+                        if(m_HistModel.m_HistVolume>0)
+                            HM = ((DefHistType*)m_HistModel.m_pHist->data.ptr)[index]/m_HistModel.m_HistVolume;
+
+                        if(m_HistCandidate.m_HistVolume>0)
+                            HC = ((DefHistType*)m_HistCandidate.m_pHist->data.ptr)[index]/m_HistCandidate.m_HistVolume;
+
+                        if(HC>0)
+                        {
+                            double V = sqrt(HM / HC);
+                            if(!m_Collision && m_FGWeight>0 && pMaskData)
+                            {
+                                V += m_FGWeight*(pMaskData[x]/255.0f);
+                            }
+                            K *= (float)MIN(V,100000.);
+                        }
+
+                        sum += K;
+                        newx += K*x;
+                        newy += K*y;
+                    }   /* Next column. */
+                }   /*  Next row. */
+
+                if(sum > 0)
+                {
+                    newx /= sum;
+                    newy /= sum;
+                }
+                newx += x0;
+                newy += y0;
+
+            }   /* if m_Dim == 3. */
+
+            /* Calculate new position by meanshift: */
+
+            for(;;)
+            {   /* Iterate using bahattcharrya coefficient: */
+                double  B1;
+                CvBlob  B = m_Blob;
+//                CvPoint NewCenter = cvPoint(cvRound(newx),cvRound(newy));
+                B.x = newx;
+                B.y = newy;
+                CollectHist(pImg, NULL, &B, &m_HistCandidate);
+                B1 = calcBhattacharyya();
+                if(B1 > B0) break;
+                newx = 0.5f*(newx+m_Blob.x);
+                newy = 0.5f*(newy+m_Blob.y);
+                if(fabs(newx-m_Blob.x)<0.1 && fabs(newy-m_Blob.y)<0.1) break;
+            }   /* Iterate using bahattcharrya coefficient. */
+
+            if(fabs(newx-m_Blob.x)<0.5 && fabs(newy-m_Blob.y)<0.5) break;
+            m_Blob.x = newx;
+            m_Blob.y = newy;
+        }   /* Next iteration. */
+
+        while(!m_Collision && m_FGWeight>0)
+        {   /* Update size if no collision. */
+            float       Alpha = 0.04f;
+            CvBlob      NewBlob;
+            double      M00,X,Y,XX,YY;
+            CvMoments   m;
+            CvRect      r;
+            CvMat       mat;
+
+            r.width = cvRound(m_Blob.w*1.5+0.5);
+            r.height = cvRound(m_Blob.h*1.5+0.5);
+            r.x = cvRound(m_Blob.x - 0.5*r.width);
+            r.y = cvRound(m_Blob.y - 0.5*r.height);
+            if(r.x < 0) break;
+            if(r.y < 0) break;
+            if(r.x+r.width >= pImgFG->width) break;
+            if(r.y+r.height >= pImgFG->height) break;
+            if(r.height < 5 || r.width < 5) break;
+
+            cvMoments( cvGetSubRect(pImgFG,&mat,r), &m, 0 );
+            M00 = cvGetSpatialMoment( &m, 0, 0 );
+            if(M00 <= 0 ) break;
+            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)));
+
+            NewBlob.w = Alpha*NewBlob.w+m_Blob.w*(1-Alpha);
+            NewBlob.h = Alpha*NewBlob.h+m_Blob.h*(1-Alpha);
+
+            m_Blob.w = MAX(NewBlob.w,5);
+            m_Blob.h = MAX(NewBlob.h,5);
+            break;
+
+        }   /* Update size if no collision. */
+
+        return &m_Blob;
+
+    };  /* CvBlobTrackerOneMSFG::Process */
+
+    virtual double GetConfidence(CvBlob* pBlob, IplImage* pImg, IplImage* /*pImgFG*/ = NULL, IplImage* pImgUnusedReg = NULL)
+    {
+        double  S = 0.2;
+        double  B = GetBhattacharyya(pImg, pImgUnusedReg, pBlob, &m_HistTemp);
+        return exp((B-1)/(2*S));
+
+    };  /*CvBlobTrackerOneMSFG::*/
+
+    virtual void Update(CvBlob* pBlob, IplImage* pImg, IplImage* pImgFG = NULL)
+    {   /* Update histogram: */
+        UpdateModelHist(pImg, pImgFG, pBlob?pBlob:&m_Blob);
+    }   /*CvBlobTrackerOneMSFG::*/
+
+    virtual void Release(){delete this;};
+    virtual void SetCollision(int CollisionFlag)
+    {
+        m_Collision = CollisionFlag;
+    }
+    virtual void SaveState(CvFileStorage* fs)
+    {
+        cvWriteStruct(fs, "Blob", &m_Blob, "ffffi");
+        cvWriteInt(fs,"Collision", m_Collision);
+        cvWriteInt(fs,"HistVolume", cvRound(m_HistModel.m_HistVolume));
+        cvWrite(fs,"Hist", m_HistModel.m_pHist);
+    };
+    virtual void LoadState(CvFileStorage* fs, CvFileNode* node)
+    {
+        CvMat* pM;
+        cvReadStructByName(fs, node, "Blob",&m_Blob, "ffffi");
+        m_Collision = cvReadIntByName(fs,node,"Collision",m_Collision);
+        pM = (CvMat*)cvRead(fs,cvGetFileNodeByName(fs,node,"Hist"));
+        if(pM)
+        {
+            m_HistModel.m_pHist = pM;
+            m_HistModel.m_HistVolume = (float)cvSum(pM).val[0];
+        }
+    };
+
+};  /*CvBlobTrackerOneMSFG*/
+
+#if 0
+void CvBlobTrackerOneMSFG::CollectHist(IplImage* pImg, IplImage* pMask, CvBlob* pBlob, DefHist* pHist)
+{
+    int UsePrecalculatedKernel = 0;
+    int BW = cvRound(pBlob->w);
+    int BH = cvRound(pBlob->h);
+    DefHistType Volume = 0;
+    int         x0 = cvRound(pBlob->x - BW*0.5);
+    int         y0 = cvRound(pBlob->y - BH*0.5);
+    int         x,y;
+
+    UsePrecalculatedKernel = (BW == m_ObjSize.width && BH == m_ObjSize.height ) ;
+
+    //cvZero(pHist);
+    cvSet(pHist->m_pHist,cvScalar(1.0/m_BinNumTotal)); /* no zero bins, all bins have very small value*/
+    Volume = 1;
+
+    assert(BW < pImg->width);
+    assert(BH < pImg->height);
+    if((x0+BW)>=pImg->width) BW=pImg->width-x0-1;
+    if((y0+BH)>=pImg->height) BH=pImg->height-y0-1;
+    if(x0<0){ x0=0;}
+    if(y0<0){ y0=0;}
+
+    if(m_Dim == 3)
+    {
+        for(y=0; y<BH; ++y)
+        {
+            unsigned char* pImgData = &CV_IMAGE_ELEM(pImg,unsigned char,y+y0,x0*3);
+            unsigned char* pMaskData = pMask?(&CV_IMAGE_ELEM(pMask,unsigned char,y+y0,x0)):NULL;
+            DefHistType* pKernelData = NULL;
+
+            if(UsePrecalculatedKernel)
+            {
+                pKernelData = ((DefHistType*)CV_MAT_ELEM_PTR_FAST(m_KernelHist[0],y,0,sizeof(DefHistType)));
+            }
+
+            for(x=0; x<BW; ++x, pImgData+=3)
+            {
+                DefHistType K;
+                int index = HIST_INDEX(pImgData);
+                assert(index >= 0 && index < pHist->m_pHist->cols);
+
+                if(UsePrecalculatedKernel)
+                {
+                    K = pKernelData[x];
+                }
+                else
+                {
+                    float dx = (x+x0-pBlob->x)/(pBlob->w*0.5);
+                    float dy = (y+y0-pBlob->y)/(pBlob->h*0.5);
+                    double r2 = dx*dx+dy*dy;
+                    K = GetKernelHist(r2);
+                }
+
+                if(pMaskData)
+                {
+                    K *= pMaskData[x]*0.003921568627450980392156862745098;
+                }
+                Volume += K;
+                ((DefHistType*)(pHist->m_pHist->data.ptr))[index] += K;
+
+            }   /* Next column. */
+        }   /*  Next row. */
+    }   /*  if m_Dim == 3. */
+
+    pHist->m_HistVolume = Volume;
+
+};  /* CollectHist */
+#endif
+
+CvBlobTrackerOne* cvCreateBlobTrackerOneMSFG()
+{
+    return (CvBlobTrackerOne*) new CvBlobTrackerOneMSFG;
+}
+
+CvBlobTracker* cvCreateBlobTrackerMSFG()
+{
+    return cvCreateBlobTrackerList(cvCreateBlobTrackerOneMSFG);
+}
+
+/* Create specific tracker without FG
+ * usin - just simple mean-shift tracker: */
+class CvBlobTrackerOneMS:public CvBlobTrackerOneMSFG
+{
+public:
+    CvBlobTrackerOneMS()
+    {
+        SetParam("FGWeight",0);
+        DelParam("FGWeight");
+        SetModuleName("MS");
+    };
+};
+
+CvBlobTrackerOne* cvCreateBlobTrackerOneMS()
+{
+    return (CvBlobTrackerOne*) new CvBlobTrackerOneMS;
+}
+
+CvBlobTracker* cvCreateBlobTrackerMS()
+{
+    return cvCreateBlobTrackerList(cvCreateBlobTrackerOneMS);
+}
+
+typedef struct DefParticle
+{
+    CvBlob  blob;
+    float   Vx,Vy;
+    double  W;
+} DefParticle;
+
+class CvBlobTrackerOneMSPF:public CvBlobTrackerOneMS
+{
+private:
+    /* parameters */
+    int             m_ParticleNum;
+    float           m_UseVel;
+    float           m_SizeVar;
+    float           m_PosVar;
+
+    CvSize          m_ImgSize;
+    CvBlob          m_Blob;
+    DefParticle*    m_pParticlesPredicted;
+    DefParticle*    m_pParticlesResampled;
+    CvRNG           m_RNG;
+#ifdef _OPENMP
+    int             m_ThreadNum;
+    DefHist*        m_HistForParalel;
+#endif
+
+public:
+    virtual void SaveState(CvFileStorage* fs)
+    {
+        CvBlobTrackerOneMS::SaveState(fs);
+        cvWriteInt(fs,"ParticleNum",m_ParticleNum);
+        cvWriteStruct(fs,"ParticlesPredicted",m_pParticlesPredicted,"ffffiffd",m_ParticleNum);
+        cvWriteStruct(fs,"ParticlesResampled",m_pParticlesResampled,"ffffiffd",m_ParticleNum);
+    };
+
+    virtual void LoadState(CvFileStorage* fs, CvFileNode* node)
+    {
+        //CvMat* pM;
+        CvBlobTrackerOneMS::LoadState(fs,node);
+        m_ParticleNum = cvReadIntByName(fs,node,"ParticleNum",m_ParticleNum);
+        if(m_ParticleNum>0)
+        {
+            Realloc();
+            printf("sizeof(DefParticle) is %d\n", (int)sizeof(DefParticle));
+            cvReadStructByName(fs,node,"ParticlesPredicted",m_pParticlesPredicted,"ffffiffd");
+            cvReadStructByName(fs,node,"ParticlesResampled",m_pParticlesResampled,"ffffiffd");
+        }
+    };
+    CvBlobTrackerOneMSPF()
+    {
+        m_pParticlesPredicted = NULL;
+        m_pParticlesResampled = NULL;
+        m_ParticleNum = 200;
+
+        AddParam("ParticleNum",&m_ParticleNum);
+        CommentParam("ParticleNum","Number of particles");
+        Realloc();
+
+        m_UseVel = 0;
+        AddParam("UseVel",&m_UseVel);
+        CommentParam("UseVel","Percent of particles which use velocity feature");
+
+        m_SizeVar = 0.05f;
+        AddParam("SizeVar",&m_SizeVar);
+        CommentParam("SizeVar","Size variation (in object size)");
+
+        m_PosVar = 0.2f;
+        AddParam("PosVar",&m_PosVar);
+        CommentParam("PosVar","Position variation (in object size)");
+
+        m_RNG = cvRNG(0);
+
+        SetModuleName("MSPF");
+
+#ifdef _OPENMP
+        {
+            m_ThreadNum = omp_get_num_procs();
+            m_HistForParalel = new DefHist[m_ThreadNum];
+        }
+#endif
+    }
+
+    ~CvBlobTrackerOneMSPF()
+    {
+        if(m_pParticlesResampled)cvFree(&m_pParticlesResampled);
+        if(m_pParticlesPredicted)cvFree(&m_pParticlesPredicted);
+#ifdef _OPENMP
+        if(m_HistForParalel) delete[] m_HistForParalel;
+#endif
+    }
+
+private:
+    void Realloc()
+    {
+        if(m_pParticlesResampled)cvFree(&m_pParticlesResampled);
+        if(m_pParticlesPredicted)cvFree(&m_pParticlesPredicted);
+        m_pParticlesPredicted = (DefParticle*)cvAlloc(sizeof(DefParticle)*m_ParticleNum);
+        m_pParticlesResampled = (DefParticle*)cvAlloc(sizeof(DefParticle)*m_ParticleNum);
+    };  /* Realloc*/
+
+    void DrawDebug(IplImage* pImg, IplImage* /*pImgFG*/)
+    {
+        int k;
+        for(k=0; k<2; ++k)
+        {
+            DefParticle*    pBP = k?m_pParticlesResampled:m_pParticlesPredicted;
+            //const char*   name = k?"MSPF resampled particle":"MSPF Predicted particle";
+            IplImage*       pI = cvCloneImage(pImg);
+            int             h,hN = m_ParticleNum;
+            CvBlob          C = cvBlob(0,0,0,0);
+            double          WS = 0;
+            for(h=0; h<hN; ++h)
+            {
+                CvBlob  B = pBP[h].blob;
+                int     CW = cvRound(255*pBP[h].W);
+                CvBlob* pB = &B;
+                int x = cvRound(CV_BLOB_RX(pB)), y = cvRound(CV_BLOB_RY(pB));
+                CvSize  s = cvSize(MAX(1,x), MAX(1,y));
+                double  W = pBP[h].W;
+                C.x += pB->x;
+                C.y += pB->y;
+                C.w += pB->w;
+                C.h += pB->h;
+                WS+=W;
+
+                s = cvSize(1,1);
+                cvEllipse( pI,
+                    cvPointFrom32f(CV_BLOB_CENTER(pB)),
+                    s,
+                    0, 0, 360,
+                    CV_RGB(CW,0,0), 1 );
+
+            }   /* Next hypothesis. */
+
+            C.x /= hN;
+            C.y /= hN;
+            C.w /= hN;
+            C.h /= hN;
+
+            cvEllipse( pI,
+                cvPointFrom32f(CV_BLOB_CENTER(&C)),
+                cvSize(cvRound(C.w*0.5),cvRound(C.h*0.5)),
+                0, 0, 360,
+                CV_RGB(0,0,255), 1 );
+
+            cvEllipse( pI,
+                cvPointFrom32f(CV_BLOB_CENTER(&m_Blob)),
+                cvSize(cvRound(m_Blob.w*0.5),cvRound(m_Blob.h*0.5)),
+                0, 0, 360,
+                CV_RGB(0,255,0), 1 );
+
+            //cvNamedWindow(name,0);
+            //cvShowImage(name,pI);
+            cvReleaseImage(&pI);
+        } /*  */
+
+        //printf("Blob %d, point (%.1f,%.1f) size (%.1f,%.1f)\n",m_Blob.ID,m_Blob.x,m_Blob.y,m_Blob.w,m_Blob.h);
+    } /* ::DrawDebug */
+
+private:
+    void Prediction()
+    {
+        int p;
+        for(p=0; p<m_ParticleNum; ++p)
+        {   /* "Prediction" of particle: */
+            //double  t;
+            float   r[5];
+            CvMat   rm = cvMat(1,5,CV_32F,r);
+            cvRandArr(&m_RNG,&rm,CV_RAND_NORMAL,cvScalar(0),cvScalar(1));
+
+            m_pParticlesPredicted[p] = m_pParticlesResampled[p];
+
+            if(cvRandReal(&m_RNG)<0.5)
+            {   /* Half of particles will predict based on external blob: */
+                m_pParticlesPredicted[p].blob = m_Blob;
+            }
+
+            if(cvRandReal(&m_RNG)<m_UseVel)
+            {   /* Predict moving particle by usual way by using speed: */
+                m_pParticlesPredicted[p].blob.x += m_pParticlesPredicted[p].Vx;
+                m_pParticlesPredicted[p].blob.y += m_pParticlesPredicted[p].Vy;
+            }
+            else
+            {   /* Stop several particles: */
+                m_pParticlesPredicted[p].Vx = 0;
+                m_pParticlesPredicted[p].Vy = 0;
+            }
+
+            {   /* Update position: */
+                float S = (m_Blob.w + m_Blob.h)*0.5f;
+                m_pParticlesPredicted[p].blob.x += m_PosVar*S*r[0];
+                m_pParticlesPredicted[p].blob.y += m_PosVar*S*r[1];
+
+                /* Update velocity: */
+                m_pParticlesPredicted[p].Vx += (float)(m_PosVar*S*0.1*r[3]);
+                m_pParticlesPredicted[p].Vy += (float)(m_PosVar*S*0.1*r[4]);
+            }
+
+            /* Update size: */
+            m_pParticlesPredicted[p].blob.w *= (1+m_SizeVar*r[2]);
+            m_pParticlesPredicted[p].blob.h *= (1+m_SizeVar*r[2]);
+
+            /* Truncate size of particle: */
+            if(m_pParticlesPredicted[p].blob.w > m_ImgSize.width*0.5f)
+            {
+                m_pParticlesPredicted[p].blob.w = m_ImgSize.width*0.5f;
+            }
+
+            if(m_pParticlesPredicted[p].blob.h > m_ImgSize.height*0.5f)
+            {
+                m_pParticlesPredicted[p].blob.h = m_ImgSize.height*0.5f;
+            }
+
+            if(m_pParticlesPredicted[p].blob.w < 1 )
+            {
+                m_pParticlesPredicted[p].blob.w = 1;
+            }
+
+            if(m_pParticlesPredicted[p].blob.h < 1)
+            {
+                m_pParticlesPredicted[p].blob.h = 1;
+            }
+        }   /* "Prediction" of particle. */
+    }   /* Prediction */
+
+    void UpdateWeightsMS(IplImage* pImg, IplImage* /*pImgFG*/)
+    {
+        int p;
+#ifdef _OPENMP
+        if( m_HistForParalel[0].m_pHist==NULL || m_HistForParalel[0].m_pHist->cols != m_BinNumTotal)
+        {
+            int t;
+            for(t=0; t<m_ThreadNum; ++t)
+                m_HistForParalel[t].Resize(m_BinNumTotal);
+        }
+#endif
+
+#ifdef _OPENMP
+#pragma omp parallel for num_threads(m_ThreadNum) schedule(runtime)
+#endif
+        for(p=0;p<m_ParticleNum;++p)
+        {   /* Calculate weights for particles: */
+            double  S = 0.2;
+            double  B = 0;
+#ifdef _OPENMP
+            assert(omp_get_thread_num()<m_ThreadNum);
+#endif
+
+            B = GetBhattacharyya(
+                pImg, NULL,
+                &(m_pParticlesPredicted[p].blob)
+#ifdef _OPENMP
+                ,&(m_HistForParalel[omp_get_thread_num()])
+#endif
+                );
+            m_pParticlesPredicted[p].W *= exp((B-1)/(2*S));
+
+        }   /* Calculate weights for particles. */
+    }
+
+    void UpdateWeightsCC(IplImage* /*pImg*/, IplImage* /*pImgFG*/)
+    {
+        int p;
+#ifdef _OPENMP
+#pragma omp parallel for
+#endif
+        for(p=0; p<m_ParticleNum; ++p)
+        {   /* Calculate weights for particles: */
+            double W = 1;
+            m_pParticlesPredicted[p].W *= W;
+        }   /* Calculate weights for particles. */
+    }
+
+    void Resample()
+    {   /* Resample particle: */
+        int         p;
+        double      Sum = 0;
+
+        for(p=0; p<m_ParticleNum; ++p)
+        {
+            Sum += m_pParticlesPredicted[p].W;
+        }
+
+        for(p=0; p<m_ParticleNum; ++p)
+        {
+            double  T = Sum * cvRandReal(&m_RNG);   /* Set current random threshold for cululative weight. */
+            int     p2;
+            double  Sum2 = 0;
+
+            for(p2=0; p2<m_ParticleNum; ++p2)
+            {
+                Sum2 += m_pParticlesPredicted[p2].W;
+                if(Sum2 >= T)break;
+            }
+
+            if(p2>=m_ParticleNum)p2=m_ParticleNum-1;
+            m_pParticlesResampled[p] = m_pParticlesPredicted[p2];
+            m_pParticlesResampled[p].W = 1;
+
+        }   /* Find next particle. */
+    }   /*  Resample particle. */
+
+
+public:
+    virtual void Init(CvBlob* pBlobInit, IplImage* pImg, IplImage* pImgFG = NULL)
+    {
+        int i;
+        CvBlobTrackerOneMSFG::Init(pBlobInit, pImg, pImgFG);
+        DefParticle PP;
+        PP.W = 1;
+        PP.Vx = 0;
+        PP.Vy = 0;
+        PP.blob = pBlobInit[0];
+        for(i=0;i<m_ParticleNum;++i)
+        {
+            m_pParticlesPredicted[i] = PP;
+            m_pParticlesResampled[i] = PP;
+        }
+        m_Blob = pBlobInit[0];
+
+    }   /* CvBlobTrackerOneMSPF::Init*/
+
+    virtual CvBlob* Process(CvBlob* pBlobPrev, IplImage* pImg, IplImage* pImgFG = NULL)
+    {
+        int p;
+
+        m_ImgSize.width = pImg->width;
+        m_ImgSize.height = pImg->height;
+
+
+        m_Blob = pBlobPrev[0];
+
+        {   /* Check blob size and realloc kernels if it is necessary: */
+            int w = cvRound(m_Blob.w);
+            int h = cvRound(m_Blob.h);
+            if( w != m_ObjSize.width || h!=m_ObjSize.height)
+            {
+                ReAllocKernel(w,h);
+                /* After this ( w != m_ObjSize.width || h!=m_ObjSize.height) should be false. */
+            }
+        }   /* Check blob size and realloc kernels if it is necessary. */
+
+        Prediction();
+
+#ifdef REPORT_TICKS
+        int64 ticks = cvGetTickCount();
+#endif
+
+        UpdateWeightsMS(pImg, pImgFG);
+
+#ifdef REPORT_TICKS
+        ticks = cvGetTickCount() - ticks;
+        fprintf(stderr, "PF UpdateWeights, %d ticks\n",  (int)ticks);
+        ticks = cvGetTickCount();
+#endif
+
+        Resample();
+
+#ifdef REPORT_TICKS
+        ticks = cvGetTickCount() - ticks;
+        fprintf(stderr, "PF Resampling, %d ticks\n",  (int)ticks);
+#endif
+
+        {   /* Find average result: */
+            float   x = 0;
+            float   y = 0;
+            float   w = 0;
+            float   h = 0;
+            float   Sum = 0;
+
+            DefParticle* pP = m_pParticlesResampled;
+
+            for(p=0; p<m_ParticleNum; ++p)
+            {
+                float W = (float)pP[p].W;
+                x += W*pP[p].blob.x;
+                y += W*pP[p].blob.y;
+                w += W*pP[p].blob.w;
+                h += W*pP[p].blob.h;
+                Sum += W;
+            }
+
+            if(Sum>0)
+            {
+                m_Blob.x = x / Sum;
+                m_Blob.y = y / Sum;
+                m_Blob.w = w / Sum;
+                m_Blob.h = h / Sum;
+            }
+        }   /* Find average result. */
+
+        if(m_Wnd)
+        {
+            DrawDebug(pImg, pImgFG);
+        }
+
+        return &m_Blob;
+
+    }   /* CvBlobTrackerOneMSPF::Process */
+
+    virtual void SkipProcess(CvBlob* pBlob, IplImage* /*pImg*/, IplImage* /*pImgFG*/ = NULL)
+    {
+        int p;
+        for(p=0; p<m_ParticleNum; ++p)
+        {
+            m_pParticlesResampled[p].blob = pBlob[0];
+            m_pParticlesResampled[p].Vx = 0;
+            m_pParticlesResampled[p].Vy = 0;
+            m_pParticlesResampled[p].W = 1;
+        }
+    }
+
+    virtual void Release(){delete this;};
+    virtual void ParamUpdate()
+    {
+        Realloc();
+    }
+
+};  /* CvBlobTrackerOneMSPF */
+
+CvBlobTrackerOne* cvCreateBlobTrackerOneMSPF()
+{
+    return (CvBlobTrackerOne*) new CvBlobTrackerOneMSPF;
+}
+
+CvBlobTracker* cvCreateBlobTrackerMSPF()
+{
+    return cvCreateBlobTrackerList(cvCreateBlobTrackerOneMSPF);
+}
+