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