createBackgroundSubtractorMOG2(int history = 500, double varThreshold = 16,
bool detectShadows = true);
+////////////////////////////////////////////////////
+// GMG
+
+class CV_EXPORTS BackgroundSubtractorGMG : public cv::BackgroundSubtractorGMG
+{
+public:
+ using cv::BackgroundSubtractorGMG::apply;
+
+ virtual void apply(InputArray image, OutputArray fgmask, double learningRate, Stream& stream) = 0;
+};
+
+CV_EXPORTS Ptr<gpu::BackgroundSubtractorGMG>
+ createBackgroundSubtractorGMG(int initializationFrames = 120, double decisionThreshold = 0.8);
+
std::auto_ptr<Impl> impl_;
};
-/**
- * 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.
- * This class implements an algorithm described in "Visual Tracking of Human Visitors under
- * Variable-Lighting Conditions for a Responsive Audio Art Installation," A. Godbehere,
- * A. Matsukawa, K. Goldberg, American Control Conference, Montreal, June 2012.
- */
-class CV_EXPORTS GMG_GPU
-{
-public:
- GMG_GPU();
-
- /**
- * Validate parameters and set up data structures for appropriate frame size.
- * @param frameSize Input frame size
- * @param min Minimum value taken on by pixels in image sequence. Usually 0
- * @param max Maximum value taken on by pixels in image sequence. e.g. 1.0 or 255
- */
- void initialize(Size frameSize, float min = 0.0f, float max = 255.0f);
-
- /**
- * Performs single-frame background subtraction and builds up a statistical background image
- * model.
- * @param frame Input frame
- * @param fgmask Output mask image representing foreground and background pixels
- * @param stream Stream for the asynchronous version
- */
- void operator ()(const GpuMat& frame, GpuMat& fgmask, float learningRate = -1.0f, Stream& stream = Stream::Null());
-
- //! Releases all inner buffers
- void release();
-
- //! Total number of distinct colors to maintain in histogram.
- int maxFeatures;
-
- //! Set between 0.0 and 1.0, determines how quickly features are "forgotten" from histograms.
- float learningRate;
-
- //! Number of frames of video to use to initialize histograms.
- int numInitializationFrames;
-
- //! Number of discrete levels in each channel to be used in histograms.
- int quantizationLevels;
-
- //! Prior probability that any given pixel is a background pixel. A sensitivity parameter.
- float backgroundPrior;
-
- //! Value above which pixel is determined to be FG.
- float decisionThreshold;
-
- //! Smoothing radius, in pixels, for cleaning up FG image.
- int smoothingRadius;
-
- //! Perform background model update.
- bool updateBackgroundModel;
-
-private:
- float maxVal_, minVal_;
-
- Size frameSize_;
-
- int frameNum_;
-
- GpuMat nfeatures_;
- GpuMat colors_;
- GpuMat weights_;
-
- Ptr<gpu::Filter> boxFilter_;
- GpuMat buf_;
-};
-
}} // namespace cv { namespace gpu {
#endif /* __OPENCV_GPUBGSEGM_HPP__ */
#include "precomp.hpp"
-#if !defined HAVE_CUDA || defined(CUDA_DISABLER)
+using namespace cv;
+using namespace cv::gpu;
-cv::gpu::GMG_GPU::GMG_GPU() { throw_no_cuda(); }
-void cv::gpu::GMG_GPU::initialize(cv::Size, float, float) { throw_no_cuda(); }
-void cv::gpu::GMG_GPU::operator ()(const cv::gpu::GpuMat&, cv::gpu::GpuMat&, float, cv::gpu::Stream&) { throw_no_cuda(); }
-void cv::gpu::GMG_GPU::release() {}
+#if !defined HAVE_CUDA || defined(CUDA_DISABLER) || !defined(HAVE_OPENCV_GPUFILTERS)
+
+Ptr<gpu::BackgroundSubtractorGMG> cv::gpu::createBackgroundSubtractorGMG(int, double) { throw_no_cuda(); return Ptr<gpu::BackgroundSubtractorGMG>(); }
#else
namespace cv { namespace gpu { namespace cudev {
- namespace bgfg_gmg
+ namespace gmg
{
void loadConstants(int width, int height, float minVal, float maxVal, int quantizationLevels, float backgroundPrior,
float decisionThreshold, int maxFeatures, int numInitializationFrames);
}
}}}
-cv::gpu::GMG_GPU::GMG_GPU()
+namespace
{
- maxFeatures = 64;
- learningRate = 0.025f;
- numInitializationFrames = 120;
- quantizationLevels = 16;
- backgroundPrior = 0.8f;
- decisionThreshold = 0.8f;
- smoothingRadius = 7;
- updateBackgroundModel = true;
-}
+ class GMGImpl : public gpu::BackgroundSubtractorGMG
+ {
+ public:
+ GMGImpl(int initializationFrames, double decisionThreshold);
-void cv::gpu::GMG_GPU::initialize(cv::Size frameSize, float min, float max)
-{
- using namespace cv::gpu::cudev::bgfg_gmg;
+ void apply(InputArray image, OutputArray fgmask, double learningRate=-1);
+ void apply(InputArray image, OutputArray fgmask, double learningRate, Stream& stream);
- CV_Assert(min < max);
- CV_Assert(maxFeatures > 0);
- CV_Assert(learningRate >= 0.0f && learningRate <= 1.0f);
- CV_Assert(numInitializationFrames >= 1);
- CV_Assert(quantizationLevels >= 1 && quantizationLevels <= 255);
- CV_Assert(backgroundPrior >= 0.0f && backgroundPrior <= 1.0f);
+ void getBackgroundImage(OutputArray backgroundImage) const;
- minVal_ = min;
- maxVal_ = max;
+ int getMaxFeatures() const { return maxFeatures_; }
+ void setMaxFeatures(int maxFeatures) { maxFeatures_ = maxFeatures; }
- frameSize_ = frameSize;
+ double getDefaultLearningRate() const { return learningRate_; }
+ void setDefaultLearningRate(double lr) { learningRate_ = (float) lr; }
- frameNum_ = 0;
+ int getNumFrames() const { return numInitializationFrames_; }
+ void setNumFrames(int nframes) { numInitializationFrames_ = nframes; }
- nfeatures_.create(frameSize_, CV_32SC1);
- colors_.create(maxFeatures * frameSize_.height, frameSize_.width, CV_32SC1);
- weights_.create(maxFeatures * frameSize_.height, frameSize_.width, CV_32FC1);
+ int getQuantizationLevels() const { return quantizationLevels_; }
+ void setQuantizationLevels(int nlevels) { quantizationLevels_ = nlevels; }
- nfeatures_.setTo(cv::Scalar::all(0));
+ double getBackgroundPrior() const { return backgroundPrior_; }
+ void setBackgroundPrior(double bgprior) { backgroundPrior_ = (float) bgprior; }
- if (smoothingRadius > 0)
- boxFilter_ = cv::gpu::createBoxFilter(CV_8UC1, -1, cv::Size(smoothingRadius, smoothingRadius));
+ int getSmoothingRadius() const { return smoothingRadius_; }
+ void setSmoothingRadius(int radius) { smoothingRadius_ = radius; }
- loadConstants(frameSize_.width, frameSize_.height, minVal_, maxVal_, quantizationLevels, backgroundPrior, decisionThreshold, maxFeatures, numInitializationFrames);
-}
+ double getDecisionThreshold() const { return decisionThreshold_; }
+ void setDecisionThreshold(double thresh) { decisionThreshold_ = (float) thresh; }
-void cv::gpu::GMG_GPU::operator ()(const cv::gpu::GpuMat& frame, cv::gpu::GpuMat& fgmask, float newLearningRate, cv::gpu::Stream& stream)
-{
- using namespace cv::gpu::cudev::bgfg_gmg;
+ bool getUpdateBackgroundModel() const { return updateBackgroundModel_; }
+ void setUpdateBackgroundModel(bool update) { updateBackgroundModel_ = update; }
- typedef void (*func_t)(PtrStepSzb frame, PtrStepb fgmask, PtrStepSzi colors, PtrStepf weights, PtrStepi nfeatures,
- int frameNum, float learningRate, bool updateBackgroundModel, cudaStream_t stream);
- static const func_t funcs[6][4] =
- {
- {update_gpu<uchar>, 0, update_gpu<uchar3>, update_gpu<uchar4>},
- {0,0,0,0},
- {update_gpu<ushort>, 0, update_gpu<ushort3>, update_gpu<ushort4>},
- {0,0,0,0},
- {0,0,0,0},
- {update_gpu<float>, 0, update_gpu<float3>, update_gpu<float4>}
+ double getMinVal() const { return minVal_; }
+ void setMinVal(double val) { minVal_ = (float) val; }
+
+ double getMaxVal() const { return maxVal_; }
+ void setMaxVal(double val) { maxVal_ = (float) val; }
+
+ private:
+ void initialize(Size frameSize, float min, float max);
+
+ //! Total number of distinct colors to maintain in histogram.
+ int maxFeatures_;
+
+ //! Set between 0.0 and 1.0, determines how quickly features are "forgotten" from histograms.
+ float learningRate_;
+
+ //! Number of frames of video to use to initialize histograms.
+ int numInitializationFrames_;
+
+ //! Number of discrete levels in each channel to be used in histograms.
+ int quantizationLevels_;
+
+ //! Prior probability that any given pixel is a background pixel. A sensitivity parameter.
+ float backgroundPrior_;
+
+ //! Smoothing radius, in pixels, for cleaning up FG image.
+ int smoothingRadius_;
+
+ //! Value above which pixel is determined to be FG.
+ float decisionThreshold_;
+
+ //! Perform background model update.
+ bool updateBackgroundModel_;
+
+ float minVal_, maxVal_;
+
+ Size frameSize_;
+ int frameNum_;
+
+ GpuMat nfeatures_;
+ GpuMat colors_;
+ GpuMat weights_;
+
+ Ptr<gpu::Filter> boxFilter_;
+ GpuMat buf_;
};
- CV_Assert(frame.depth() == CV_8U || frame.depth() == CV_16U || frame.depth() == CV_32F);
- CV_Assert(frame.channels() == 1 || frame.channels() == 3 || frame.channels() == 4);
+ GMGImpl::GMGImpl(int initializationFrames, double decisionThreshold)
+ {
+ maxFeatures_ = 64;
+ learningRate_ = 0.025f;
+ numInitializationFrames_ = initializationFrames;
+ quantizationLevels_ = 16;
+ backgroundPrior_ = 0.8f;
+ decisionThreshold_ = (float) decisionThreshold;
+ smoothingRadius_ = 7;
+ updateBackgroundModel_ = true;
+ minVal_ = maxVal_ = 0;
+ }
- if (newLearningRate != -1.0f)
+ void GMGImpl::apply(InputArray image, OutputArray fgmask, double learningRate)
{
- CV_Assert(newLearningRate >= 0.0f && newLearningRate <= 1.0f);
- learningRate = newLearningRate;
+ apply(image, fgmask, learningRate, Stream::Null());
}
- if (frame.size() != frameSize_)
- initialize(frame.size(), 0.0f, frame.depth() == CV_8U ? 255.0f : frame.depth() == CV_16U ? std::numeric_limits<ushort>::max() : 1.0f);
+ void GMGImpl::apply(InputArray _frame, OutputArray _fgmask, double newLearningRate, Stream& stream)
+ {
+ using namespace cv::gpu::cudev::gmg;
+
+ typedef void (*func_t)(PtrStepSzb frame, PtrStepb fgmask, PtrStepSzi colors, PtrStepf weights, PtrStepi nfeatures,
+ int frameNum, float learningRate, bool updateBackgroundModel, cudaStream_t stream);
+ static const func_t funcs[6][4] =
+ {
+ {update_gpu<uchar>, 0, update_gpu<uchar3>, update_gpu<uchar4>},
+ {0,0,0,0},
+ {update_gpu<ushort>, 0, update_gpu<ushort3>, update_gpu<ushort4>},
+ {0,0,0,0},
+ {0,0,0,0},
+ {update_gpu<float>, 0, update_gpu<float3>, update_gpu<float4>}
+ };
+
+ GpuMat frame = _frame.getGpuMat();
+
+ CV_Assert( frame.depth() == CV_8U || frame.depth() == CV_16U || frame.depth() == CV_32F );
+ CV_Assert( frame.channels() == 1 || frame.channels() == 3 || frame.channels() == 4 );
+
+ if (newLearningRate != -1.0)
+ {
+ CV_Assert( newLearningRate >= 0.0 && newLearningRate <= 1.0 );
+ learningRate_ = (float) newLearningRate;
+ }
+
+ if (frame.size() != frameSize_)
+ {
+ double minVal = minVal_;
+ double maxVal = maxVal_;
- fgmask.create(frameSize_, CV_8UC1);
- fgmask.setTo(cv::Scalar::all(0), stream);
+ if (minVal_ == 0 && maxVal_ == 0)
+ {
+ minVal = 0;
+ maxVal = frame.depth() == CV_8U ? 255.0 : frame.depth() == CV_16U ? std::numeric_limits<ushort>::max() : 1.0;
+ }
- funcs[frame.depth()][frame.channels() - 1](frame, fgmask, colors_, weights_, nfeatures_, frameNum_, learningRate, updateBackgroundModel, cv::gpu::StreamAccessor::getStream(stream));
+ initialize(frame.size(), (float) minVal, (float) maxVal);
+ }
+
+ _fgmask.create(frameSize_, CV_8UC1);
+ GpuMat fgmask = _fgmask.getGpuMat();
+
+ fgmask.setTo(Scalar::all(0), stream);
+
+ funcs[frame.depth()][frame.channels() - 1](frame, fgmask, colors_, weights_, nfeatures_, frameNum_,
+ learningRate_, updateBackgroundModel_, StreamAccessor::getStream(stream));
+
+ // medianBlur
+ if (smoothingRadius_ > 0)
+ {
+ boxFilter_->apply(fgmask, buf_, stream);
+ const int minCount = (smoothingRadius_ * smoothingRadius_ + 1) / 2;
+ const double thresh = 255.0 * minCount / (smoothingRadius_ * smoothingRadius_);
+ gpu::threshold(buf_, fgmask, thresh, 255.0, THRESH_BINARY, stream);
+ }
+
+ // keep track of how many frames we have processed
+ ++frameNum_;
+ }
- // medianBlur
- if (smoothingRadius > 0)
+ void GMGImpl::getBackgroundImage(OutputArray backgroundImage) const
{
- boxFilter_->apply(fgmask, buf_, stream);
- int minCount = (smoothingRadius * smoothingRadius + 1) / 2;
- double thresh = 255.0 * minCount / (smoothingRadius * smoothingRadius);
- cv::gpu::threshold(buf_, fgmask, thresh, 255.0, cv::THRESH_BINARY, stream);
+ (void) backgroundImage;
+ CV_Error(Error::StsNotImplemented, "Not implemented");
}
- // keep track of how many frames we have processed
- ++frameNum_;
+ void GMGImpl::initialize(Size frameSize, float min, float max)
+ {
+ using namespace cv::gpu::cudev::gmg;
+
+ CV_Assert( maxFeatures_ > 0 );
+ CV_Assert( learningRate_ >= 0.0f && learningRate_ <= 1.0f);
+ CV_Assert( numInitializationFrames_ >= 1);
+ CV_Assert( quantizationLevels_ >= 1 && quantizationLevels_ <= 255);
+ CV_Assert( backgroundPrior_ >= 0.0f && backgroundPrior_ <= 1.0f);
+
+ minVal_ = min;
+ maxVal_ = max;
+ CV_Assert( minVal_ < maxVal_ );
+
+ frameSize_ = frameSize;
+
+ frameNum_ = 0;
+
+ nfeatures_.create(frameSize_, CV_32SC1);
+ colors_.create(maxFeatures_ * frameSize_.height, frameSize_.width, CV_32SC1);
+ weights_.create(maxFeatures_ * frameSize_.height, frameSize_.width, CV_32FC1);
+
+ nfeatures_.setTo(Scalar::all(0));
+
+ if (smoothingRadius_ > 0)
+ boxFilter_ = gpu::createBoxFilter(CV_8UC1, -1, Size(smoothingRadius_, smoothingRadius_));
+
+ loadConstants(frameSize_.width, frameSize_.height, minVal_, maxVal_,
+ quantizationLevels_, backgroundPrior_, decisionThreshold_, maxFeatures_, numInitializationFrames_);
+ }
}
-void cv::gpu::GMG_GPU::release()
+Ptr<gpu::BackgroundSubtractorGMG> cv::gpu::createBackgroundSubtractorGMG(int initializationFrames, double decisionThreshold)
{
- frameSize_ = Size();
-
- nfeatures_.release();
- colors_.release();
- weights_.release();
- boxFilter_.release();
- buf_.release();
+ return new GMGImpl(initializationFrames, decisionThreshold);
}
#endif