From 60456083308efa97ef0e0708c367876e7a28076a Mon Sep 17 00:00:00 2001
From: Glue Crow <yacwu@cs.nctu.edu.tw>
Date: Tue, 26 Dec 2017 02:46:22 +0800
Subject: [PATCH] Parallelization of BackgroundSubtractorKNN

---
 modules/video/src/bgfg_KNN.cpp | 345 +++++++++++++++++----------------
 1 file changed, 179 insertions(+), 166 deletions(-)

diff --git a/modules/video/src/bgfg_KNN.cpp b/modules/video/src/bgfg_KNN.cpp
index fa1a6dbaec..0f0ff12958 100755
--- a/modules/video/src/bgfg_KNN.cpp
+++ b/modules/video/src/bgfg_KNN.cpp
@@ -259,10 +259,8 @@ protected:
     String name_;
 };
 
-//{ to do - paralelization ...
-//struct KNNInvoker....
 CV_INLINE void
-        _cvUpdatePixelBackgroundNP(	long pixel,const uchar* data, int nchannels, int m_nN,
+        _cvUpdatePixelBackgroundNP(int x_idx, const uchar* data, int nchannels, int m_nN,
         uchar* m_aModel,
         uchar* m_nNextLongUpdate,
         uchar* m_nNextMidUpdate,
@@ -273,70 +271,53 @@ CV_INLINE void
         int m_nLongCounter,
         int m_nMidCounter,
         int m_nShortCounter,
-        int m_nLongUpdate,
-        int m_nMidUpdate,
-        int m_nShortUpdate,
         uchar include
         )
 {
     // hold the offset
     int ndata=1+nchannels;
-    long offsetLong =  ndata * (pixel * m_nN * 3 + m_aModelIndexLong[pixel] + m_nN * 2);
-    long offsetMid =   ndata * (pixel * m_nN * 3 + m_aModelIndexMid[pixel]  + m_nN * 1);
-    long offsetShort = ndata * (pixel * m_nN * 3 + m_aModelIndexShort[pixel]);
+    long offsetLong =  ndata * (m_aModelIndexLong[x_idx] + m_nN * 2);
+    long offsetMid =   ndata * (m_aModelIndexMid[x_idx]  + m_nN * 1);
+    long offsetShort = ndata * (m_aModelIndexShort[x_idx]);
 
     // Long update?
-    if (m_nNextLongUpdate[pixel] == m_nLongCounter)
+    if (m_nNextLongUpdate[x_idx] == m_nLongCounter)
     {
         // add the oldest pixel from Mid to the list of values (for each color)
         memcpy(&m_aModel[offsetLong],&m_aModel[offsetMid],ndata*sizeof(unsigned char));
         // increase the index
-        m_aModelIndexLong[pixel] = (m_aModelIndexLong[pixel] >= (m_nN-1)) ? 0 : (m_aModelIndexLong[pixel] + 1);
-    };
-    if (m_nLongCounter == (m_nLongUpdate-1))
-    {
-        //m_nNextLongUpdate[pixel] = (uchar)(((m_nLongUpdate)*(rand()-1))/RAND_MAX);//0,...m_nLongUpdate-1;
-        m_nNextLongUpdate[pixel] = (uchar)( rand() % m_nLongUpdate );//0,...m_nLongUpdate-1;
+        m_aModelIndexLong[x_idx] = (m_aModelIndexLong[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexLong[x_idx] + 1);
     };
 
     // Mid update?
-    if (m_nNextMidUpdate[pixel] == m_nMidCounter)
+    if (m_nNextMidUpdate[x_idx] == m_nMidCounter)
     {
         // add this pixel to the list of values (for each color)
         memcpy(&m_aModel[offsetMid],&m_aModel[offsetShort],ndata*sizeof(unsigned char));
         // increase the index
-        m_aModelIndexMid[pixel] = (m_aModelIndexMid[pixel] >= (m_nN-1)) ? 0 : (m_aModelIndexMid[pixel] + 1);
-    };
-    if (m_nMidCounter == (m_nMidUpdate-1))
-    {
-        m_nNextMidUpdate[pixel] = (uchar)( rand() % m_nMidUpdate );
+        m_aModelIndexMid[x_idx] = (m_aModelIndexMid[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexMid[x_idx] + 1);
     };
 
     // Short update?
-    if (m_nNextShortUpdate[pixel] == m_nShortCounter)
+    if (m_nNextShortUpdate[x_idx] == m_nShortCounter)
     {
         // add this pixel to the list of values (for each color)
-        memcpy(&m_aModel[offsetShort],data,ndata*sizeof(unsigned char));
+        memcpy(&m_aModel[offsetShort],data,nchannels*sizeof(unsigned char));
         //set the include flag
         m_aModel[offsetShort+nchannels]=include;
         // increase the index
-        m_aModelIndexShort[pixel] = (m_aModelIndexShort[pixel] >= (m_nN-1)) ? 0 : (m_aModelIndexShort[pixel] + 1);
-    };
-    if (m_nShortCounter == (m_nShortUpdate-1))
-    {
-        m_nNextShortUpdate[pixel] = (uchar)( rand() % m_nShortUpdate );
+        m_aModelIndexShort[x_idx] = (m_aModelIndexShort[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexShort[x_idx] + 1);
     };
 }
 
 CV_INLINE int
-        _cvCheckPixelBackgroundNP(long pixel,
-        const uchar* data, int nchannels,
+        _cvCheckPixelBackgroundNP(const uchar* data, int nchannels,
         int m_nN,
         uchar* m_aModel,
         float m_fTb,
         int m_nkNN,
         float tau,
-        int m_nShadowDetection,
+        bool m_bShadowDetection,
         uchar& include)
 {
     int Pbf = 0; // the total probability that this pixel is background
@@ -347,12 +328,11 @@ CV_INLINE int
     include=0;//do we include this pixel into background model?
 
     int ndata=nchannels+1;
-    long posPixel = pixel * ndata * m_nN * 3;
 //	float k;
     // now increase the probability for each pixel
     for (int n = 0; n < m_nN*3; n++)
     {
-        uchar* mean_m = &m_aModel[posPixel + n*ndata];
+        uchar* mean_m = &m_aModel[n*ndata];
 
         //calculate difference and distance
         float dist2;
@@ -399,12 +379,12 @@ CV_INLINE int
 
     int Ps = 0; // the total probability that this pixel is background shadow
     // Detected as moving object, perform shadow detection
-    if (m_nShadowDetection)
+    if (m_bShadowDetection)
     {
         for (int n = 0; n < m_nN*3; n++)
         {
             //long subPosPixel = posPixel + n*ndata;
-            uchar* mean_m = &m_aModel[posPixel + n*ndata];
+            uchar* mean_m = &m_aModel[n*ndata];
 
             if(mean_m[nchannels])//check only background
             {
@@ -445,123 +425,126 @@ CV_INLINE int
     return 0;
 }
 
-CV_INLINE void
-        icvUpdatePixelBackgroundNP(const Mat& _src, Mat& _dst,
-        Mat& _bgmodel,
-        Mat& _nNextLongUpdate,
-        Mat& _nNextMidUpdate,
-        Mat& _nNextShortUpdate,
-        Mat& _aModelIndexLong,
-        Mat& _aModelIndexMid,
-        Mat& _aModelIndexShort,
-        int& _nLongCounter,
-        int& _nMidCounter,
-        int& _nShortCounter,
-        int _nN,
-        float _fAlphaT,
-        float _fTb,
-        int _nkNN,
-        float _fTau,
-        int _bShadowDetection,
-        uchar nShadowDetection
-        )
+class KNNInvoker : public ParallelLoopBody
 {
-    int nchannels = CV_MAT_CN(_src.type());
-
-    //model
-    uchar* m_aModel=_bgmodel.ptr(0);
-    uchar* m_nNextLongUpdate=_nNextLongUpdate.ptr(0);
-    uchar* m_nNextMidUpdate=_nNextMidUpdate.ptr(0);
-    uchar* m_nNextShortUpdate=_nNextShortUpdate.ptr(0);
-    uchar* m_aModelIndexLong=_aModelIndexLong.ptr(0);
-    uchar* m_aModelIndexMid=_aModelIndexMid.ptr(0);
-    uchar* m_aModelIndexShort=_aModelIndexShort.ptr(0);
-
-    //some constants
-    int m_nN=_nN;
-    float m_fAlphaT=_fAlphaT;
-    float m_fTb=_fTb;//Tb - threshold on the distance
-    float m_fTau=_fTau;
-    int m_nkNN=_nkNN;
-    int m_bShadowDetection=_bShadowDetection;
-
-    //recalculate update rates - in case alpha is changed
-    // calculate update parameters (using alpha)
-    int Kshort,Kmid,Klong;
-    //approximate exponential learning curve
-    Kshort=(int)(log(0.7)/log(1-m_fAlphaT))+1;//Kshort
-    Kmid=(int)(log(0.4)/log(1-m_fAlphaT))-Kshort+1;//Kmid
-    Klong=(int)(log(0.1)/log(1-m_fAlphaT))-Kshort-Kmid+1;//Klong
+public:
+    KNNInvoker(const Mat& _src, Mat& _dst,
+               uchar* _bgmodel,
+               uchar* _nNextLongUpdate,
+               uchar* _nNextMidUpdate,
+               uchar* _nNextShortUpdate,
+               uchar* _aModelIndexLong,
+               uchar* _aModelIndexMid,
+               uchar* _aModelIndexShort,
+               int _nLongCounter,
+               int _nMidCounter,
+               int _nShortCounter,
+               int _nN,
+               float _fTb,
+               int _nkNN,
+               float _fTau,
+               bool _bShadowDetection,
+               uchar _nShadowDetection)
+    {
+        src = &_src;
+        dst = &_dst;
+        m_aModel0 = _bgmodel;
+        m_nNextLongUpdate0 = _nNextLongUpdate;
+        m_nNextMidUpdate0 = _nNextMidUpdate;
+        m_nNextShortUpdate0 = _nNextShortUpdate;
+        m_aModelIndexLong0 = _aModelIndexLong;
+        m_aModelIndexMid0 = _aModelIndexMid;
+        m_aModelIndexShort0 = _aModelIndexShort;
+        m_nLongCounter = _nLongCounter;
+        m_nMidCounter = _nMidCounter;
+        m_nShortCounter = _nShortCounter;
+        m_nN = _nN;
+        m_fTb = _fTb;
+        m_fTau = _fTau;
+        m_nkNN = _nkNN;
+        m_bShadowDetection = _bShadowDetection;
+        m_nShadowDetection = _nShadowDetection;
+    }
 
-    //refresh rates
-    int	m_nShortUpdate = (Kshort/m_nN)+1;
-    int m_nMidUpdate = (Kmid/m_nN)+1;
-    int m_nLongUpdate = (Klong/m_nN)+1;
-
-    //int	m_nShortUpdate = MAX((Kshort/m_nN),m_nN);
-    //int m_nMidUpdate = MAX((Kmid/m_nN),m_nN);
-    //int m_nLongUpdate = MAX((Klong/m_nN),m_nN);
-
-    //update counters for the refresh rate
-    int m_nLongCounter=_nLongCounter;
-    int m_nMidCounter=_nMidCounter;
-    int m_nShortCounter=_nShortCounter;
-
-    _nShortCounter++;//0,1,...,m_nShortUpdate-1
-    _nMidCounter++;
-    _nLongCounter++;
-    if (_nShortCounter >= m_nShortUpdate) _nShortCounter = 0;
-    if (_nMidCounter >= m_nMidUpdate) _nMidCounter = 0;
-    if (_nLongCounter >= m_nLongUpdate) _nLongCounter = 0;
-
-    //go through the image
-    long i = 0;
-    for (long y = 0; y < _src.rows; y++)
+    void operator()(const Range& range) const
     {
-        for (long x = 0; x < _src.cols; x++)
+        int y0 = range.start, y1 = range.end;
+        int ncols = src->cols, nchannels = src->channels();
+        int ndata=nchannels+1;
+
+        for ( int y = y0; y < y1; y++ )
         {
-            const uchar* data = _src.ptr((int)y, (int)x);
-
-            //update model+ background subtract
-            uchar include=0;
-            int result= _cvCheckPixelBackgroundNP(i, data, nchannels,
-                    m_nN, m_aModel, m_fTb,m_nkNN, m_fTau,m_bShadowDetection,include);
-
-            _cvUpdatePixelBackgroundNP(i,data,nchannels,
-                    m_nN, m_aModel,
-                    m_nNextLongUpdate,
-                    m_nNextMidUpdate,
-                    m_nNextShortUpdate,
-                    m_aModelIndexLong,
-                    m_aModelIndexMid,
-                    m_aModelIndexShort,
-                    m_nLongCounter,
-                    m_nMidCounter,
-                    m_nShortCounter,
-                    m_nLongUpdate,
-                    m_nMidUpdate,
-                    m_nShortUpdate,
-                    include
-                    );
-            switch (result)
+            const uchar* data = src->ptr(y);
+            uchar* m_aModel = m_aModel0 + ncols*m_nN*3*ndata*y;
+            uchar* m_nNextLongUpdate = m_nNextLongUpdate0 + ncols*y;
+            uchar* m_nNextMidUpdate = m_nNextMidUpdate0 + ncols*y;
+            uchar* m_nNextShortUpdate = m_nNextShortUpdate0 + ncols*y;
+            uchar* m_aModelIndexLong = m_aModelIndexLong0 + ncols*y;
+            uchar* m_aModelIndexMid = m_aModelIndexMid0 + ncols*y;
+            uchar* m_aModelIndexShort = m_aModelIndexShort0 + ncols*y;
+            uchar* mask = dst->ptr(y);
+
+            for ( int x = 0; x < ncols; x++ )
             {
-                case 0:
-                    //foreground
-                    *_dst.ptr((int)y, (int)x) = 255;
-                    break;
-                case 1:
-                    //background
-                    *_dst.ptr((int)y, (int)x) = 0;
-                    break;
-                case 2:
-                    //shadow
-                    *_dst.ptr((int)y, (int)x) = nShadowDetection;
-                    break;
+
+                //update model+ background subtract
+                uchar include=0;
+                int result= _cvCheckPixelBackgroundNP(data, nchannels,
+                        m_nN, m_aModel, m_fTb,m_nkNN, m_fTau,m_bShadowDetection,include);
+
+                _cvUpdatePixelBackgroundNP(x,data,nchannels,
+                        m_nN, m_aModel,
+                        m_nNextLongUpdate,
+                        m_nNextMidUpdate,
+                        m_nNextShortUpdate,
+                        m_aModelIndexLong,
+                        m_aModelIndexMid,
+                        m_aModelIndexShort,
+                        m_nLongCounter,
+                        m_nMidCounter,
+                        m_nShortCounter,
+                        include
+                        );
+                switch (result)
+                {
+                    case 0:
+                        //foreground
+                        mask[x] = 255;
+                        break;
+                    case 1:
+                        //background
+                        mask[x] = 0;
+                        break;
+                    case 2:
+                        //shadow
+                        mask[x] = m_nShadowDetection;
+                        break;
+                }
+                data += nchannels;
+                m_aModel += m_nN*3*ndata;
             }
-            i++;
         }
     }
-}
+
+    const Mat* src;
+    Mat* dst;
+    uchar* m_aModel0;
+    uchar* m_nNextLongUpdate0;
+    uchar* m_nNextMidUpdate0;
+    uchar* m_nNextShortUpdate0;
+    uchar* m_aModelIndexLong0;
+    uchar* m_aModelIndexMid0;
+    uchar* m_aModelIndexShort0;
+    int m_nLongCounter;
+    int m_nMidCounter;
+    int m_nShortCounter;
+    int m_nN;
+    float m_fTb;
+    float m_fTau;
+    int m_nkNN;
+    bool m_bShadowDetection;
+    uchar m_nShadowDetection;
+};
 
 
 
@@ -582,27 +565,57 @@ void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask,
     learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
     CV_Assert(learningRate >= 0);
 
-    //parallel_for_(Range(0, image.rows),
-    //              KNNInvoker(image, fgmask,
-    icvUpdatePixelBackgroundNP(image, fgmask,
-            bgmodel,
-            nNextLongUpdate,
-            nNextMidUpdate,
-            nNextShortUpdate,
-            aModelIndexLong,
-            aModelIndexMid,
-            aModelIndexShort,
-            nLongCounter,
-            nMidCounter,
-            nShortCounter,
-            nN,
-            (float)learningRate,
-            fTb,
-            nkNN,
-            fTau,
-            bShadowDetection,
-            nShadowDetection
-            );
+    //recalculate update rates - in case alpha is changed
+    // calculate update parameters (using alpha)
+    int Kshort,Kmid,Klong;
+    //approximate exponential learning curve
+    Kshort=(int)(log(0.7)/log(1-learningRate))+1;//Kshort
+    Kmid=(int)(log(0.4)/log(1-learningRate))-Kshort+1;//Kmid
+    Klong=(int)(log(0.1)/log(1-learningRate))-Kshort-Kmid+1;//Klong
+
+    //refresh rates
+    int nShortUpdate = (Kshort/nN)+1;
+    int nMidUpdate = (Kmid/nN)+1;
+    int nLongUpdate = (Klong/nN)+1;
+
+    parallel_for_(Range(0, image.rows),
+                  KNNInvoker(image, fgmask,
+                             bgmodel.ptr(),
+                             nNextLongUpdate.ptr(),
+                             nNextMidUpdate.ptr(),
+                             nNextShortUpdate.ptr(),
+                             aModelIndexLong.ptr(),
+                             aModelIndexMid.ptr(),
+                             aModelIndexShort.ptr(),
+                             nLongCounter,
+                             nMidCounter,
+                             nShortCounter,
+                             nN,
+                             fTb,
+                             nkNN,
+                             fTau,
+                             bShadowDetection,
+                             nShadowDetection),
+                             image.total()/(double)(1 << 16));
+
+    nShortCounter++;//0,1,...,nShortUpdate-1
+    nMidCounter++;
+    nLongCounter++;
+    if (nShortCounter >= nShortUpdate)
+    {
+        nShortCounter = 0;
+        randu(nNextShortUpdate, Scalar::all(0),  Scalar::all(nShortUpdate));
+    }
+    if (nMidCounter >= nMidUpdate)
+    {
+        nMidCounter = 0;
+        randu(nNextMidUpdate, Scalar::all(0),  Scalar::all(nMidUpdate));
+    }
+    if (nLongCounter >= nLongUpdate)
+    {
+        nLongCounter = 0;
+        randu(nNextLongUpdate, Scalar::all(0),  Scalar::all(nLongUpdate));
+    }
 }
 
 void BackgroundSubtractorKNNImpl::getBackgroundImage(OutputArray backgroundImage) const