/*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. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. // Copyright (C) 2013, OpenCV Foundation, 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 the copyright holders 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 #include "precomp.hpp" #include "opencl_kernels_video.hpp" namespace cv { /*! The class implements the following algorithm: "Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction" Z.Zivkovic, F. van der Heijden Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006 http://www.zoranz.net/Publications/zivkovicPRL2006.pdf */ // default parameters of gaussian background detection algorithm static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2 static const int defaultNsamples = 7; // number of samples saved in memory static const float defaultDist2Threshold = 20.0f*20.0f;//threshold on distance from the sample // additional parameters static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation class BackgroundSubtractorKNNImpl : public BackgroundSubtractorKNN { public: //! the default constructor BackgroundSubtractorKNNImpl() { frameSize = Size(0,0); frameType = 0; nframes = 0; history = defaultHistory2; //set parameters // N - the number of samples stored in memory per model nN = defaultNsamples; //kNN - k nearest neighbour - number on NN for detecting background - default K=[0.1*nN] nkNN=MAX(1,cvRound(0.1*nN*3+0.40)); //Tb - Threshold Tb*kernelwidth fTb = defaultDist2Threshold; // Shadow detection bShadowDetection = 1;//turn on nShadowDetection = defaultnShadowDetection2; fTau = defaultfTau;// Tau - shadow threshold name_ = "BackgroundSubtractor.KNN"; nLongCounter = 0; nMidCounter = 0; nShortCounter = 0; #ifdef HAVE_OPENCL opencl_ON = true; #endif } //! the full constructor that takes the length of the history, // the number of gaussian mixtures, the background ratio parameter and the noise strength BackgroundSubtractorKNNImpl(int _history, float _dist2Threshold, bool _bShadowDetection=true) { frameSize = Size(0,0); frameType = 0; nframes = 0; history = _history > 0 ? _history : defaultHistory2; //set parameters // N - the number of samples stored in memory per model nN = defaultNsamples; //kNN - k nearest neighbour - number on NN for detcting background - default K=[0.1*nN] nkNN=MAX(1,cvRound(0.1*nN*3+0.40)); //Tb - Threshold Tb*kernelwidth fTb = _dist2Threshold>0? _dist2Threshold : defaultDist2Threshold; bShadowDetection = _bShadowDetection; nShadowDetection = defaultnShadowDetection2; fTau = defaultfTau; name_ = "BackgroundSubtractor.KNN"; nLongCounter = 0; nMidCounter = 0; nShortCounter = 0; #ifdef HAVE_OPENCL opencl_ON = true; #endif } //! the destructor ~BackgroundSubtractorKNNImpl() {} //! the update operator void apply(InputArray image, OutputArray fgmask, double learningRate=-1); //! computes a background image which are the mean of all background gaussians virtual void getBackgroundImage(OutputArray backgroundImage) const; //! re-initialization method void initialize(Size _frameSize, int _frameType) { frameSize = _frameSize; frameType = _frameType; nframes = 0; int nchannels = CV_MAT_CN(frameType); CV_Assert( nchannels <= CV_CN_MAX ); // Reserve memory for the model int size=frameSize.height*frameSize.width; //Reset counters nShortCounter = 0; nMidCounter = 0; nLongCounter = 0; #ifdef HAVE_OPENCL if (ocl::isOpenCLActivated() && opencl_ON) { create_ocl_apply_kernel(); kernel_getBg.create("getBackgroundImage2_kernel", ocl::video::bgfg_knn_oclsrc, format( "-D CN=%d -D NSAMPLES=%d", nchannels, nN)); if (kernel_apply.empty() || kernel_getBg.empty()) opencl_ON = false; } else opencl_ON = false; if (opencl_ON) { u_flag.create(frameSize.height * nN * 3, frameSize.width, CV_8UC1); u_flag.setTo(Scalar::all(0)); if (nchannels==3) nchannels=4; u_sample.create(frameSize.height * nN * 3, frameSize.width, CV_32FC(nchannels)); u_sample.setTo(Scalar::all(0)); u_aModelIndexShort.create(frameSize.height, frameSize.width, CV_8UC1); u_aModelIndexShort.setTo(Scalar::all(0)); u_aModelIndexMid.create(frameSize.height, frameSize.width, CV_8UC1); u_aModelIndexMid.setTo(Scalar::all(0)); u_aModelIndexLong.create(frameSize.height, frameSize.width, CV_8UC1); u_aModelIndexLong.setTo(Scalar::all(0)); u_nNextShortUpdate.create(frameSize.height, frameSize.width, CV_8UC1); u_nNextShortUpdate.setTo(Scalar::all(0)); u_nNextMidUpdate.create(frameSize.height, frameSize.width, CV_8UC1); u_nNextMidUpdate.setTo(Scalar::all(0)); u_nNextLongUpdate.create(frameSize.height, frameSize.width, CV_8UC1); u_nNextLongUpdate.setTo(Scalar::all(0)); } else #endif { // for each sample of 3 speed pixel models each pixel bg model we store ... // values + flag (nchannels+1 values) bgmodel.create( 1,(nN * 3) * (nchannels+1)* size,CV_8U); bgmodel = Scalar::all(0); //index through the three circular lists aModelIndexShort.create(1,size,CV_8U); aModelIndexMid.create(1,size,CV_8U); aModelIndexLong.create(1,size,CV_8U); //when to update next nNextShortUpdate.create(1,size,CV_8U); nNextMidUpdate.create(1,size,CV_8U); nNextLongUpdate.create(1,size,CV_8U); aModelIndexShort = Scalar::all(0);//random? //((m_nN)*rand())/(RAND_MAX+1);//0...m_nN-1 aModelIndexMid = Scalar::all(0); aModelIndexLong = Scalar::all(0); nNextShortUpdate = Scalar::all(0); nNextMidUpdate = Scalar::all(0); nNextLongUpdate = Scalar::all(0); } } virtual int getHistory() const { return history; } virtual void setHistory(int _nframes) { history = _nframes; } virtual int getNSamples() const { return nN; } virtual void setNSamples(int _nN) { nN = _nN; }//needs reinitialization! virtual int getkNNSamples() const { return nkNN; } virtual void setkNNSamples(int _nkNN) { nkNN = _nkNN; } virtual double getDist2Threshold() const { return fTb; } virtual void setDist2Threshold(double _dist2Threshold) { fTb = (float)_dist2Threshold; } virtual bool getDetectShadows() const { return bShadowDetection; } virtual void setDetectShadows(bool detectshadows) { if ((bShadowDetection && detectshadows) || (!bShadowDetection && !detectshadows)) return; bShadowDetection = detectshadows; #ifdef HAVE_OPENCL if (!kernel_apply.empty()) { create_ocl_apply_kernel(); CV_Assert( !kernel_apply.empty() ); } #endif } virtual int getShadowValue() const { return nShadowDetection; } virtual void setShadowValue(int value) { nShadowDetection = (uchar)value; } virtual double getShadowThreshold() const { return fTau; } virtual void setShadowThreshold(double value) { fTau = (float)value; } virtual void write(FileStorage& fs) const { writeFormat(fs); fs << "name" << name_ << "history" << history << "nsamples" << nN << "nKNN" << nkNN << "dist2Threshold" << fTb << "detectShadows" << (int)bShadowDetection << "shadowValue" << (int)nShadowDetection << "shadowThreshold" << fTau; } virtual void read(const FileNode& fn) { CV_Assert( (String)fn["name"] == name_ ); history = (int)fn["history"]; nN = (int)fn["nsamples"]; nkNN = (int)fn["nKNN"]; fTb = (float)fn["dist2Threshold"]; bShadowDetection = (int)fn["detectShadows"] != 0; nShadowDetection = saturate_cast((int)fn["shadowValue"]); fTau = (float)fn["shadowThreshold"]; } protected: Size frameSize; int frameType; int nframes; ///////////////////////// //very important parameters - things you will change //////////////////////// int history; //alpha=1/history - speed of update - if the time interval you want to average over is T //set alpha=1/history. It is also usefull at start to make T slowly increase //from 1 until the desired T float fTb; //Tb - threshold on the squared distance from the sample used to decide if it is well described //by the background model or not. A typical value could be 2 sigma //and that is Tb=2*2*10*10 =400; where we take typical pixel level sigma=10 ///////////////////////// //less important parameters - things you might change but be carefull //////////////////////// int nN;//totlal number of samples int nkNN;//number on NN for detcting background - default K=[0.1*nN] //shadow detection parameters bool bShadowDetection;//default 1 - do shadow detection unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value float fTau; // Tau - shadow threshold. The shadow is detected if the pixel is darker //version of the background. Tau is a threshold on how much darker the shadow can be. //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow //See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003. //model data int nLongCounter;//circular counter int nMidCounter; int nShortCounter; Mat bgmodel; // model data pixel values Mat aModelIndexShort;// index into the models Mat aModelIndexMid; Mat aModelIndexLong; Mat nNextShortUpdate;//random update points per model Mat nNextMidUpdate; Mat nNextLongUpdate; #ifdef HAVE_OPENCL mutable bool opencl_ON; UMat u_flag; UMat u_sample; UMat u_aModelIndexShort; UMat u_aModelIndexMid; UMat u_aModelIndexLong; UMat u_nNextShortUpdate; UMat u_nNextMidUpdate; UMat u_nNextLongUpdate; mutable ocl::Kernel kernel_apply; mutable ocl::Kernel kernel_getBg; #endif String name_; #ifdef HAVE_OPENCL bool ocl_getBackgroundImage(OutputArray backgroundImage) const; bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1); void create_ocl_apply_kernel(); #endif }; CV_INLINE void _cvUpdatePixelBackgroundNP(int x_idx, const uchar* data, int nchannels, int m_nN, uchar* m_aModel, uchar* m_nNextLongUpdate, uchar* m_nNextMidUpdate, uchar* m_nNextShortUpdate, uchar* m_aModelIndexLong, uchar* m_aModelIndexMid, uchar* m_aModelIndexShort, int m_nLongCounter, int m_nMidCounter, int m_nShortCounter, uchar include ) { // hold the offset int ndata=1+nchannels; 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[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[x_idx] = (m_aModelIndexLong[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexLong[x_idx] + 1); }; // Mid update? 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[x_idx] = (m_aModelIndexMid[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexMid[x_idx] + 1); }; // Short update? if (m_nNextShortUpdate[x_idx] == m_nShortCounter) { // add this pixel to the list of values (for each color) memcpy(&m_aModel[offsetShort],data,nchannels*sizeof(unsigned char)); //set the include flag m_aModel[offsetShort+nchannels]=include; // increase the index m_aModelIndexShort[x_idx] = (m_aModelIndexShort[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexShort[x_idx] + 1); }; } CV_INLINE int _cvCheckPixelBackgroundNP(const uchar* data, int nchannels, int m_nN, uchar* m_aModel, float m_fTb, int m_nkNN, float tau, bool m_bShadowDetection, uchar& include) { int Pbf = 0; // the total probability that this pixel is background int Pb = 0; //background model probability float dData[CV_CN_MAX]; //uchar& include=data[nchannels]; include=0;//do we include this pixel into background model? int ndata=nchannels+1; // now increase the probability for each pixel for (int n = 0; n < m_nN*3; n++) { uchar* mean_m = &m_aModel[n*ndata]; //calculate difference and distance float dist2; if( nchannels == 3 ) { dData[0] = (float)mean_m[0] - data[0]; dData[1] = (float)mean_m[1] - data[1]; dData[2] = (float)mean_m[2] - data[2]; dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2]; } else { dist2 = 0.f; for( int c = 0; c < nchannels; c++ ) { dData[c] = (float)mean_m[c] - data[c]; dist2 += dData[c]*dData[c]; } } if (dist2= m_nkNN)//Tb { include=1;//include return 1;//background ->exit }; } }; }; //include? if (Pbf>=m_nkNN)//m_nTbf) { include=1; } int Ps = 0; // the total probability that this pixel is background shadow // Detected as moving object, perform shadow detection if (m_bShadowDetection) { for (int n = 0; n < m_nN*3; n++) { //long subPosPixel = posPixel + n*ndata; uchar* mean_m = &m_aModel[n*ndata]; if(mean_m[nchannels])//check only background { float numerator = 0.0f; float denominator = 0.0f; for( int c = 0; c < nchannels; c++ ) { numerator += (float)data[c] * mean_m[c]; denominator += (float)mean_m[c] * mean_m[c]; } // no division by zero allowed if( denominator == 0 ) return 0; // if tau < a < 1 then also check the color distortion if( numerator <= denominator && numerator >= tau*denominator ) { float a = numerator / denominator; float dist2a = 0.0f; for( int c = 0; c < nchannels; c++ ) { float dD= a*mean_m[c] - data[c]; dist2a += dD*dD; } if (dist2a= m_nkNN)//shadow return 2; }; }; }; }; } return 0; } class KNNInvoker : public ParallelLoopBody { 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; } void operator()(const Range& range) const { 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(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++ ) { //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; } } } 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; }; #ifdef HAVE_OPENCL bool BackgroundSubtractorKNNImpl::ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate) { bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType; if( needToInitialize ) initialize(_image.size(), _image.type()); ++nframes; learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history ); CV_Assert(learningRate >= 0); _fgmask.create(_image.size(), CV_8U); UMat fgmask = _fgmask.getUMat(); UMat frame = _image.getUMat(); //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; int idxArg = 0; idxArg = kernel_apply.set(idxArg, ocl::KernelArg::ReadOnly(frame)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextLongUpdate)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextMidUpdate)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextShortUpdate)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexLong)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexMid)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexShort)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_flag)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_sample)); idxArg = kernel_apply.set(idxArg, ocl::KernelArg::WriteOnlyNoSize(fgmask)); idxArg = kernel_apply.set(idxArg, nLongCounter); idxArg = kernel_apply.set(idxArg, nMidCounter); idxArg = kernel_apply.set(idxArg, nShortCounter); idxArg = kernel_apply.set(idxArg, fTb); idxArg = kernel_apply.set(idxArg, nkNN); idxArg = kernel_apply.set(idxArg, fTau); if (bShadowDetection) kernel_apply.set(idxArg, nShadowDetection); size_t globalsize[2] = {(size_t)frame.cols, (size_t)frame.rows}; if(!kernel_apply.run(2, globalsize, NULL, true)) return false; nShortCounter++;//0,1,...,nShortUpdate-1 nMidCounter++; nLongCounter++; if (nShortCounter >= nShortUpdate) { nShortCounter = 0; randu(u_nNextShortUpdate, Scalar::all(0), Scalar::all(nShortUpdate)); } if (nMidCounter >= nMidUpdate) { nMidCounter = 0; randu(u_nNextMidUpdate, Scalar::all(0), Scalar::all(nMidUpdate)); } if (nLongCounter >= nLongUpdate) { nLongCounter = 0; randu(u_nNextLongUpdate, Scalar::all(0), Scalar::all(nLongUpdate)); } return true; } bool BackgroundSubtractorKNNImpl::ocl_getBackgroundImage(OutputArray _backgroundImage) const { _backgroundImage.create(frameSize, frameType); UMat dst = _backgroundImage.getUMat(); int idxArg = 0; idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_flag)); idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_sample)); idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::WriteOnly(dst)); size_t globalsize[2] = {(size_t)dst.cols, (size_t)dst.rows}; return kernel_getBg.run(2, globalsize, NULL, false); } void BackgroundSubtractorKNNImpl::create_ocl_apply_kernel() { int nchannels = CV_MAT_CN(frameType); String opts = format("-D CN=%d -D NSAMPLES=%d%s", nchannels, nN, bShadowDetection ? " -D SHADOW_DETECT" : ""); kernel_apply.create("knn_kernel", ocl::video::bgfg_knn_oclsrc, opts); } #endif void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask, double learningRate) { CV_INSTRUMENT_REGION() #ifdef HAVE_OPENCL if (opencl_ON) { #ifndef __APPLE__ CV_OCL_RUN(_fgmask.isUMat() && OCL_PERFORMANCE_CHECK(!ocl::Device::getDefault().isIntel() || _image.channels() == 1), ocl_apply(_image, _fgmask, learningRate)) #else CV_OCL_RUN(_fgmask.isUMat() && OCL_PERFORMANCE_CHECK(!ocl::Device::getDefault().isIntel()), ocl_apply(_image, _fgmask, learningRate)) #endif opencl_ON = false; nframes = 0; } #endif bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType; if( needToInitialize ) initialize(_image.size(), _image.type()); Mat image = _image.getMat(); _fgmask.create( image.size(), CV_8U ); Mat fgmask = _fgmask.getMat(); ++nframes; learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history ); CV_Assert(learningRate >= 0); //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 { CV_INSTRUMENT_REGION() #ifdef HAVE_OPENCL if (opencl_ON) { CV_OCL_RUN(opencl_ON, ocl_getBackgroundImage(backgroundImage)) opencl_ON = false; } #endif int nchannels = CV_MAT_CN(frameType); //CV_Assert( nchannels == 3 ); Mat meanBackground(frameSize, CV_8UC3, Scalar::all(0)); int ndata=nchannels+1; int modelstep=(ndata * nN * 3); const uchar* pbgmodel=bgmodel.ptr(0); for(int row=0; row(row, col) = Vec3b(mean_m); break; } } pbgmodel=pbgmodel+modelstep; } } switch(CV_MAT_CN(frameType)) { case 1: { std::vector channels; split(meanBackground, channels); channels[0].copyTo(backgroundImage); break; } case 3: { meanBackground.copyTo(backgroundImage); break; } default: CV_Error(Error::StsUnsupportedFormat, ""); } } Ptr createBackgroundSubtractorKNN(int _history, double _threshold2, bool _bShadowDetection) { return makePtr(_history, (float)_threshold2, _bShadowDetection); } } /* End of file. */