createBackgroundSubtractorMOG(int history = 200, int nmixtures = 5,
double backgroundRatio = 0.7, double noiseSigma = 0);
+////////////////////////////////////////////////////
+// MOG2
+
+class CV_EXPORTS BackgroundSubtractorMOG2 : public cv::BackgroundSubtractorMOG2
+{
+public:
+ using cv::BackgroundSubtractorMOG2::apply;
+ using cv::BackgroundSubtractorMOG2::getBackgroundImage;
+
+ virtual void apply(InputArray image, OutputArray fgmask, double learningRate, Stream& stream) = 0;
+
+ virtual void getBackgroundImage(OutputArray backgroundImage, Stream& stream) const = 0;
+};
+
+CV_EXPORTS Ptr<gpu::BackgroundSubtractorMOG2>
+ createBackgroundSubtractorMOG2(int history = 500, double varThreshold = 16,
+ bool detectShadows = true);
+
+
+
+
std::auto_ptr<Impl> impl_;
};
-/*!
- The class implements the following algorithm:
- "Improved adaptive Gausian mixture model for background subtraction"
- Z.Zivkovic
- International Conference Pattern Recognition, UK, August, 2004.
- http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf
-*/
-class CV_EXPORTS MOG2_GPU
-{
-public:
- //! the default constructor
- MOG2_GPU(int nmixtures = -1);
-
- //! re-initiaization method
- void initialize(Size frameSize, int frameType);
-
- //! the update operator
- void operator()(const GpuMat& frame, GpuMat& fgmask, float learningRate = -1.0f, Stream& stream = Stream::Null());
-
- //! computes a background image which are the mean of all background gaussians
- void getBackgroundImage(GpuMat& backgroundImage, Stream& stream = Stream::Null()) const;
-
- //! releases all inner buffers
- void release();
-
- // parameters
- // you should call initialize after parameters changes
-
- int history;
-
- //! here it is the maximum allowed number of mixture components.
- //! Actual number is determined dynamically per pixel
- float varThreshold;
- // threshold on the squared Mahalanobis distance to decide if it is well described
- // by the background model or not. Related to Cthr from the paper.
- // This does not influence the update of the background. A typical value could be 4 sigma
- // and that is varThreshold=4*4=16; Corresponds to Tb in the paper.
-
- /////////////////////////
- // less important parameters - things you might change but be carefull
- ////////////////////////
-
- float backgroundRatio;
- // corresponds to fTB=1-cf from the paper
- // TB - threshold when the component becomes significant enough to be included into
- // the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
- // For alpha=0.001 it means that the mode should exist for approximately 105 frames before
- // it is considered foreground
- // float noiseSigma;
- float varThresholdGen;
-
- //correspondts to Tg - threshold on the squared Mahalan. dist. to decide
- //when a sample is close to the existing components. If it is not close
- //to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
- //Smaller Tg leads to more generated components and higher Tg might make
- //lead to small number of components but they can grow too large
- float fVarInit;
- float fVarMin;
- float fVarMax;
-
- //initial variance for the newly generated components.
- //It will will influence the speed of adaptation. A good guess should be made.
- //A simple way is to estimate the typical standard deviation from the images.
- //I used here 10 as a reasonable value
- // min and max can be used to further control the variance
- float fCT; //CT - complexity reduction prior
- //this is related to the number of samples needed to accept that a component
- //actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
- //the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
-
- //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,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
-
-private:
- int nmixtures_;
-
- Size frameSize_;
- int frameType_;
- int nframes_;
-
- GpuMat weight_;
- GpuMat variance_;
- GpuMat mean_;
-
- GpuMat bgmodelUsedModes_; //keep track of number of modes per pixel
-};
-
/**
* Background Subtractor module. Takes a series of images and returns a sequence of mask (8UC1)
* images of the same size, where 255 indicates Foreground and 0 represents Background.
#include "precomp.hpp"
+using namespace cv;
+using namespace cv::gpu;
+
#if !defined HAVE_CUDA || defined(CUDA_DISABLER)
-cv::gpu::MOG2_GPU::MOG2_GPU(int) { throw_no_cuda(); }
-void cv::gpu::MOG2_GPU::initialize(cv::Size, int) { throw_no_cuda(); }
-void cv::gpu::MOG2_GPU::operator()(const GpuMat&, GpuMat&, float, Stream&) { throw_no_cuda(); }
-void cv::gpu::MOG2_GPU::getBackgroundImage(GpuMat&, Stream&) const { throw_no_cuda(); }
-void cv::gpu::MOG2_GPU::release() {}
+Ptr<gpu::BackgroundSubtractorMOG2> cv::gpu::createBackgroundSubtractorMOG2(int, double, bool) { throw_no_cuda(); return Ptr<gpu::BackgroundSubtractorMOG2>(); }
#else
}
}}}
-namespace mog2
+namespace
{
// default parameters of gaussian background detection algorithm
const int defaultHistory = 500; // Learning rate; alpha = 1/defaultHistory2
const float defaultVarMin = 4.0f;
// additional parameters
- const float defaultfCT = 0.05f; // complexity reduction prior constant 0 - no reduction of number of components
- const unsigned char defaultnShadowDetection = 127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection
- const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation
-}
+ const float defaultCT = 0.05f; // complexity reduction prior constant 0 - no reduction of number of components
+ const unsigned char defaultShadowValue = 127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection
+ const float defaultShadowThreshold = 0.5f; // Tau - shadow threshold, see the paper for explanation
-cv::gpu::MOG2_GPU::MOG2_GPU(int nmixtures) :
- frameSize_(0, 0), frameType_(0), nframes_(0)
-{
- nmixtures_ = nmixtures > 0 ? nmixtures : mog2::defaultNMixtures;
+ class MOG2Impl : public gpu::BackgroundSubtractorMOG2
+ {
+ public:
+ MOG2Impl(int history, double varThreshold, bool detectShadows);
- history = mog2::defaultHistory;
- varThreshold = mog2::defaultVarThreshold;
- bShadowDetection = true;
+ void apply(InputArray image, OutputArray fgmask, double learningRate=-1);
+ void apply(InputArray image, OutputArray fgmask, double learningRate, Stream& stream);
- backgroundRatio = mog2::defaultBackgroundRatio;
- fVarInit = mog2::defaultVarInit;
- fVarMax = mog2::defaultVarMax;
- fVarMin = mog2::defaultVarMin;
+ void getBackgroundImage(OutputArray backgroundImage) const;
+ void getBackgroundImage(OutputArray backgroundImage, Stream& stream) const;
- varThresholdGen = mog2::defaultVarThresholdGen;
- fCT = mog2::defaultfCT;
- nShadowDetection = mog2::defaultnShadowDetection;
- fTau = mog2::defaultfTau;
-}
+ int getHistory() const { return history_; }
+ void setHistory(int history) { history_ = history; }
-void cv::gpu::MOG2_GPU::initialize(cv::Size frameSize, int frameType)
-{
- using namespace cv::gpu::cudev::mog2;
+ int getNMixtures() const { return nmixtures_; }
+ void setNMixtures(int nmixtures) { nmixtures_ = nmixtures; }
- CV_Assert(frameType == CV_8UC1 || frameType == CV_8UC3 || frameType == CV_8UC4);
+ double getBackgroundRatio() const { return backgroundRatio_; }
+ void setBackgroundRatio(double ratio) { backgroundRatio_ = (float) ratio; }
- frameSize_ = frameSize;
- frameType_ = frameType;
- nframes_ = 0;
+ double getVarThreshold() const { return varThreshold_; }
+ void setVarThreshold(double varThreshold) { varThreshold_ = (float) varThreshold; }
- int ch = CV_MAT_CN(frameType);
- int work_ch = ch;
+ double getVarThresholdGen() const { return varThresholdGen_; }
+ void setVarThresholdGen(double varThresholdGen) { varThresholdGen_ = (float) varThresholdGen; }
- // for each gaussian mixture of each pixel bg model we store ...
- // the mixture weight (w),
- // the mean (nchannels values) and
- // the covariance
- weight_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1);
- variance_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1);
- mean_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC(work_ch));
+ double getVarInit() const { return varInit_; }
+ void setVarInit(double varInit) { varInit_ = (float) varInit; }
- //make the array for keeping track of the used modes per pixel - all zeros at start
- bgmodelUsedModes_.create(frameSize_, CV_8UC1);
- bgmodelUsedModes_.setTo(cv::Scalar::all(0));
+ double getVarMin() const { return varMin_; }
+ void setVarMin(double varMin) { varMin_ = (float) varMin; }
- loadConstants(nmixtures_, varThreshold, backgroundRatio, varThresholdGen, fVarInit, fVarMin, fVarMax, fTau, nShadowDetection);
-}
+ double getVarMax() const { return varMax_; }
+ void setVarMax(double varMax) { varMax_ = (float) varMax; }
-void cv::gpu::MOG2_GPU::operator()(const GpuMat& frame, GpuMat& fgmask, float learningRate, Stream& stream)
-{
- using namespace cv::gpu::cudev::mog2;
+ double getComplexityReductionThreshold() const { return ct_; }
+ void setComplexityReductionThreshold(double ct) { ct_ = (float) ct; }
- int ch = frame.channels();
- int work_ch = ch;
+ bool getDetectShadows() const { return detectShadows_; }
+ void setDetectShadows(bool detectShadows) { detectShadows_ = detectShadows; }
- if (nframes_ == 0 || learningRate >= 1.0f || frame.size() != frameSize_ || work_ch != mean_.channels())
- initialize(frame.size(), frame.type());
+ int getShadowValue() const { return shadowValue_; }
+ void setShadowValue(int value) { shadowValue_ = (uchar) value; }
- fgmask.create(frameSize_, CV_8UC1);
- fgmask.setTo(cv::Scalar::all(0));
+ double getShadowThreshold() const { return shadowThreshold_; }
+ void setShadowThreshold(double threshold) { shadowThreshold_ = (float) threshold; }
- ++nframes_;
- learningRate = learningRate >= 0.0f && nframes_ > 1 ? learningRate : 1.0f / std::min(2 * nframes_, history);
- CV_Assert(learningRate >= 0.0f);
+ private:
+ void initialize(Size frameSize, int frameType);
- mog2_gpu(frame, frame.channels(), fgmask, bgmodelUsedModes_, weight_, variance_, mean_, learningRate, -learningRate * fCT, bShadowDetection, StreamAccessor::getStream(stream));
-}
+ int history_;
+ int nmixtures_;
+ float backgroundRatio_;
+ float varThreshold_;
+ float varThresholdGen_;
+ float varInit_;
+ float varMin_;
+ float varMax_;
+ float ct_;
+ bool detectShadows_;
+ uchar shadowValue_;
+ float shadowThreshold_;
-void cv::gpu::MOG2_GPU::getBackgroundImage(GpuMat& backgroundImage, Stream& stream) const
-{
- using namespace cv::gpu::cudev::mog2;
+ Size frameSize_;
+ int frameType_;
+ int nframes_;
- backgroundImage.create(frameSize_, frameType_);
+ GpuMat weight_;
+ GpuMat variance_;
+ GpuMat mean_;
- getBackgroundImage2_gpu(backgroundImage.channels(), bgmodelUsedModes_, weight_, mean_, backgroundImage, StreamAccessor::getStream(stream));
-}
+ //keep track of number of modes per pixel
+ GpuMat bgmodelUsedModes_;
+ };
-void cv::gpu::MOG2_GPU::release()
-{
- frameSize_ = Size(0, 0);
- frameType_ = 0;
- nframes_ = 0;
+ MOG2Impl::MOG2Impl(int history, double varThreshold, bool detectShadows) :
+ frameSize_(0, 0), frameType_(0), nframes_(0)
+ {
+ history_ = history > 0 ? history : defaultHistory;
+ varThreshold_ = varThreshold > 0 ? (float) varThreshold : defaultVarThreshold;
+ detectShadows_ = detectShadows;
+
+ nmixtures_ = defaultNMixtures;
+ backgroundRatio_ = defaultBackgroundRatio;
+ varInit_ = defaultVarInit;
+ varMax_ = defaultVarMax;
+ varMin_ = defaultVarMin;
+ varThresholdGen_ = defaultVarThresholdGen;
+ ct_ = defaultCT;
+ shadowValue_ = defaultShadowValue;
+ shadowThreshold_ = defaultShadowThreshold;
+ }
+
+ void MOG2Impl::apply(InputArray image, OutputArray fgmask, double learningRate)
+ {
+ apply(image, fgmask, learningRate, Stream::Null());
+ }
+
+ void MOG2Impl::apply(InputArray _frame, OutputArray _fgmask, double learningRate, Stream& stream)
+ {
+ using namespace cv::gpu::cudev::mog2;
+
+ GpuMat frame = _frame.getGpuMat();
+
+ int ch = frame.channels();
+ int work_ch = ch;
+
+ if (nframes_ == 0 || learningRate >= 1.0 || frame.size() != frameSize_ || work_ch != mean_.channels())
+ initialize(frame.size(), frame.type());
+
+ _fgmask.create(frameSize_, CV_8UC1);
+ GpuMat fgmask = _fgmask.getGpuMat();
- weight_.release();
- variance_.release();
- mean_.release();
+ fgmask.setTo(Scalar::all(0), stream);
- bgmodelUsedModes_.release();
+ ++nframes_;
+ learningRate = learningRate >= 0 && nframes_ > 1 ? learningRate : 1.0 / std::min(2 * nframes_, history_);
+ CV_Assert( learningRate >= 0 );
+
+ mog2_gpu(frame, frame.channels(), fgmask, bgmodelUsedModes_, weight_, variance_, mean_,
+ (float) learningRate, static_cast<float>(-learningRate * ct_), detectShadows_, StreamAccessor::getStream(stream));
+ }
+
+ void MOG2Impl::getBackgroundImage(OutputArray backgroundImage) const
+ {
+ getBackgroundImage(backgroundImage, Stream::Null());
+ }
+
+ void MOG2Impl::getBackgroundImage(OutputArray _backgroundImage, Stream& stream) const
+ {
+ using namespace cv::gpu::cudev::mog2;
+
+ _backgroundImage.create(frameSize_, frameType_);
+ GpuMat backgroundImage = _backgroundImage.getGpuMat();
+
+ getBackgroundImage2_gpu(backgroundImage.channels(), bgmodelUsedModes_, weight_, mean_, backgroundImage, StreamAccessor::getStream(stream));
+ }
+
+ void MOG2Impl::initialize(cv::Size frameSize, int frameType)
+ {
+ using namespace cv::gpu::cudev::mog2;
+
+ CV_Assert( frameType == CV_8UC1 || frameType == CV_8UC3 || frameType == CV_8UC4 );
+
+ frameSize_ = frameSize;
+ frameType_ = frameType;
+ nframes_ = 0;
+
+ int ch = CV_MAT_CN(frameType);
+ int work_ch = ch;
+
+ // for each gaussian mixture of each pixel bg model we store ...
+ // the mixture weight (w),
+ // the mean (nchannels values) and
+ // the covariance
+ weight_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1);
+ variance_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1);
+ mean_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC(work_ch));
+
+ //make the array for keeping track of the used modes per pixel - all zeros at start
+ bgmodelUsedModes_.create(frameSize_, CV_8UC1);
+ bgmodelUsedModes_.setTo(Scalar::all(0));
+
+ loadConstants(nmixtures_, varThreshold_, backgroundRatio_, varThresholdGen_, varInit_, varMin_, varMax_, shadowThreshold_, shadowValue_);
+ }
+}
+
+Ptr<gpu::BackgroundSubtractorMOG2> cv::gpu::createBackgroundSubtractorMOG2(int history, double varThreshold, bool detectShadows)
+{
+ return new MOG2Impl(history, varThreshold, detectShadows);
}
#endif