From a2adab72839931c94c5d27db178bc5e557ec249d Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 6 May 2013 16:52:06 +0400 Subject: [PATCH] refactored MOG algorithm converted it to abstract interface --- .../gpubgsegm/include/opencv2/gpubgsegm.hpp | 73 +++---- modules/gpubgsegm/perf/perf_bgsegm.cpp | 7 +- modules/gpubgsegm/src/mog.cpp | 185 +++++++++++------- modules/gpubgsegm/test/test_bgsegm.cpp | 4 +- samples/gpu/bgfg_segm.cpp | 8 +- samples/gpu/performance/tests.cpp | 6 +- 6 files changed, 157 insertions(+), 126 deletions(-) diff --git a/modules/gpubgsegm/include/opencv2/gpubgsegm.hpp b/modules/gpubgsegm/include/opencv2/gpubgsegm.hpp index 3fe62ec94b..b60d207f7a 100644 --- a/modules/gpubgsegm/include/opencv2/gpubgsegm.hpp +++ b/modules/gpubgsegm/include/opencv2/gpubgsegm.hpp @@ -47,13 +47,37 @@ # error gpubgsegm.hpp header must be compiled as C++ #endif -#include - #include "opencv2/core/gpu.hpp" +#include "opencv2/video/background_segm.hpp" + +#include #include "opencv2/gpufilters.hpp" namespace cv { namespace gpu { +//////////////////////////////////////////////////// +// MOG + +class CV_EXPORTS BackgroundSubtractorMOG : public cv::BackgroundSubtractorMOG +{ +public: + using cv::BackgroundSubtractorMOG::apply; + using cv::BackgroundSubtractorMOG::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 + createBackgroundSubtractorMOG(int history = 200, int nmixtures = 5, + double backgroundRatio = 0.7, double noiseSigma = 0); + + + + + + // Foreground Object Detection from Videos Containing Complex Background. // Liyuan Li, Weimin Huang, Irene Y.H. Gu, and Qi Tian. // ACM MM2003 9p @@ -116,51 +140,6 @@ private: std::auto_ptr impl_; }; -/*! - Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm - - The class implements the following algorithm: - "An improved adaptive background mixture model for real-time tracking with shadow detection" - P. KadewTraKuPong and R. Bowden, - Proc. 2nd European Workshp on Advanced Video-Based Surveillance Systems, 2001." - http://personal.ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/avbs01.pdf -*/ -class CV_EXPORTS MOG_GPU -{ -public: - //! the default constructor - MOG_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 = 0.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(); - - int history; - float varThreshold; - float backgroundRatio; - float noiseSigma; - -private: - int nmixtures_; - - Size frameSize_; - int frameType_; - int nframes_; - - GpuMat weight_; - GpuMat sortKey_; - GpuMat mean_; - GpuMat var_; -}; - /*! The class implements the following algorithm: "Improved adaptive Gausian mixture model for background subtraction" diff --git a/modules/gpubgsegm/perf/perf_bgsegm.cpp b/modules/gpubgsegm/perf/perf_bgsegm.cpp index 15842d59b5..a05428887f 100644 --- a/modules/gpubgsegm/perf/perf_bgsegm.cpp +++ b/modules/gpubgsegm/perf/perf_bgsegm.cpp @@ -176,11 +176,12 @@ PERF_TEST_P(Video_Cn_LearningRate, MOG, if (PERF_RUN_GPU()) { + cv::Ptr d_mog = cv::gpu::createBackgroundSubtractorMOG(); + cv::gpu::GpuMat d_frame(frame); - cv::gpu::MOG_GPU d_mog; cv::gpu::GpuMat foreground; - d_mog(d_frame, foreground, learningRate); + d_mog->apply(d_frame, foreground, learningRate); for (int i = 0; i < 10; ++i) { @@ -200,7 +201,7 @@ PERF_TEST_P(Video_Cn_LearningRate, MOG, d_frame.upload(frame); startTimer(); next(); - d_mog(d_frame, foreground, learningRate); + d_mog->apply(d_frame, foreground, learningRate); stopTimer(); } diff --git a/modules/gpubgsegm/src/mog.cpp b/modules/gpubgsegm/src/mog.cpp index 1acb844df3..65adb9425c 100644 --- a/modules/gpubgsegm/src/mog.cpp +++ b/modules/gpubgsegm/src/mog.cpp @@ -42,13 +42,12 @@ #include "precomp.hpp" +using namespace cv; +using namespace cv::gpu; + #if !defined HAVE_CUDA || defined(CUDA_DISABLER) -cv::gpu::MOG_GPU::MOG_GPU(int) { throw_no_cuda(); } -void cv::gpu::MOG_GPU::initialize(cv::Size, int) { throw_no_cuda(); } -void cv::gpu::MOG_GPU::operator()(const cv::gpu::GpuMat&, cv::gpu::GpuMat&, float, Stream&) { throw_no_cuda(); } -void cv::gpu::MOG_GPU::getBackgroundImage(GpuMat&, Stream&) const { throw_no_cuda(); } -void cv::gpu::MOG_GPU::release() {} +Ptr cv::gpu::createBackgroundSubtractorMOG(int, int, double, double) { throw_no_cuda(); return Ptr(); } #else @@ -63,7 +62,7 @@ namespace cv { namespace gpu { namespace cudev } }}} -namespace mog +namespace { const int defaultNMixtures = 5; const int defaultHistory = 200; @@ -71,88 +70,140 @@ namespace mog const float defaultVarThreshold = 2.5f * 2.5f; const float defaultNoiseSigma = 30.0f * 0.5f; const float defaultInitialWeight = 0.05f; -} -cv::gpu::MOG_GPU::MOG_GPU(int nmixtures) : - frameSize_(0, 0), frameType_(0), nframes_(0) -{ - nmixtures_ = std::min(nmixtures > 0 ? nmixtures : mog::defaultNMixtures, 8); - history = mog::defaultHistory; - varThreshold = mog::defaultVarThreshold; - backgroundRatio = mog::defaultBackgroundRatio; - noiseSigma = mog::defaultNoiseSigma; -} + class MOGImpl : public gpu::BackgroundSubtractorMOG + { + public: + MOGImpl(int history, int nmixtures, double backgroundRatio, double noiseSigma); -void cv::gpu::MOG_GPU::initialize(cv::Size frameSize, int frameType) -{ - CV_Assert(frameType == CV_8UC1 || frameType == CV_8UC3 || frameType == CV_8UC4); + void apply(InputArray image, OutputArray fgmask, double learningRate=-1); + void apply(InputArray image, OutputArray fgmask, double learningRate, Stream& stream); - frameSize_ = frameSize; - frameType_ = frameType; + void getBackgroundImage(OutputArray backgroundImage) const; + void getBackgroundImage(OutputArray backgroundImage, Stream& stream) const; - int ch = CV_MAT_CN(frameType); - int work_ch = ch; + int getHistory() const { return history_; } + void setHistory(int nframes) { history_ = nframes; } - // for each gaussian mixture of each pixel bg model we store - // the mixture sort key (w/sum_of_variances), the mixture weight (w), - // the mean (nchannels values) and - // the diagonal covariance matrix (another nchannels values) + int getNMixtures() const { return nmixtures_; } + void setNMixtures(int nmix) { nmixtures_ = nmix; } - weight_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1); - sortKey_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1); - mean_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC(work_ch)); - var_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC(work_ch)); + double getBackgroundRatio() const { return backgroundRatio_; } + void setBackgroundRatio(double backgroundRatio) { backgroundRatio_ = (float) backgroundRatio; } - weight_.setTo(cv::Scalar::all(0)); - sortKey_.setTo(cv::Scalar::all(0)); - mean_.setTo(cv::Scalar::all(0)); - var_.setTo(cv::Scalar::all(0)); + double getNoiseSigma() const { return noiseSigma_; } + void setNoiseSigma(double noiseSigma) { noiseSigma_ = (float) noiseSigma; } - nframes_ = 0; -} + private: + //! re-initiaization method + void initialize(Size frameSize, int frameType); -void cv::gpu::MOG_GPU::operator()(const cv::gpu::GpuMat& frame, cv::gpu::GpuMat& fgmask, float learningRate, Stream& stream) -{ - using namespace cv::gpu::cudev::mog; + int history_; + int nmixtures_; + float backgroundRatio_; + float noiseSigma_; - CV_Assert(frame.depth() == CV_8U); + float varThreshold_; - int ch = frame.channels(); - int work_ch = ch; + Size frameSize_; + int frameType_; + int nframes_; - if (nframes_ == 0 || learningRate >= 1.0 || frame.size() != frameSize_ || work_ch != mean_.channels()) - initialize(frame.size(), frame.type()); + GpuMat weight_; + GpuMat sortKey_; + GpuMat mean_; + GpuMat var_; + }; - fgmask.create(frameSize_, CV_8UC1); + MOGImpl::MOGImpl(int history, int nmixtures, double backgroundRatio, double noiseSigma) : + frameSize_(0, 0), frameType_(0), nframes_(0) + { + history_ = history > 0 ? history : defaultHistory; + nmixtures_ = std::min(nmixtures > 0 ? nmixtures : defaultNMixtures, 8); + backgroundRatio_ = backgroundRatio > 0 ? (float) backgroundRatio : defaultBackgroundRatio; + noiseSigma_ = noiseSigma > 0 ? (float) noiseSigma : defaultNoiseSigma; - ++nframes_; - learningRate = learningRate >= 0.0f && nframes_ > 1 ? learningRate : 1.0f / std::min(nframes_, history); - CV_Assert(learningRate >= 0.0f); + varThreshold_ = defaultVarThreshold; + } - mog_gpu(frame, ch, fgmask, weight_, sortKey_, mean_, var_, nmixtures_, - varThreshold, learningRate, backgroundRatio, noiseSigma, - StreamAccessor::getStream(stream)); -} + void MOGImpl::apply(InputArray image, OutputArray fgmask, double learningRate) + { + apply(image, fgmask, learningRate, Stream::Null()); + } -void cv::gpu::MOG_GPU::getBackgroundImage(GpuMat& backgroundImage, Stream& stream) const -{ - using namespace cv::gpu::cudev::mog; + void MOGImpl::apply(InputArray _frame, OutputArray _fgmask, double learningRate, Stream& stream) + { + using namespace cv::gpu::cudev::mog; + + GpuMat frame = _frame.getGpuMat(); + + CV_Assert( frame.depth() == CV_8U ); + + 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()); - backgroundImage.create(frameSize_, frameType_); + _fgmask.create(frameSize_, CV_8UC1); + GpuMat fgmask = _fgmask.getGpuMat(); - getBackgroundImage_gpu(backgroundImage.channels(), weight_, mean_, backgroundImage, nmixtures_, backgroundRatio, StreamAccessor::getStream(stream)); + ++nframes_; + learningRate = learningRate >= 0 && nframes_ > 1 ? learningRate : 1.0 / std::min(nframes_, history_); + CV_Assert( learningRate >= 0 ); + + mog_gpu(frame, ch, fgmask, weight_, sortKey_, mean_, var_, nmixtures_, + varThreshold_, (float) learningRate, backgroundRatio_, noiseSigma_, + StreamAccessor::getStream(stream)); + } + + void MOGImpl::getBackgroundImage(OutputArray backgroundImage) const + { + getBackgroundImage(backgroundImage, Stream::Null()); + } + + void MOGImpl::getBackgroundImage(OutputArray _backgroundImage, Stream& stream) const + { + using namespace cv::gpu::cudev::mog; + + _backgroundImage.create(frameSize_, frameType_); + GpuMat backgroundImage = _backgroundImage.getGpuMat(); + + getBackgroundImage_gpu(backgroundImage.channels(), weight_, mean_, backgroundImage, nmixtures_, backgroundRatio_, StreamAccessor::getStream(stream)); + } + + void MOGImpl::initialize(Size frameSize, int frameType) + { + CV_Assert( frameType == CV_8UC1 || frameType == CV_8UC3 || frameType == CV_8UC4 ); + + frameSize_ = frameSize; + frameType_ = frameType; + + int ch = CV_MAT_CN(frameType); + int work_ch = ch; + + // for each gaussian mixture of each pixel bg model we store + // the mixture sort key (w/sum_of_variances), the mixture weight (w), + // the mean (nchannels values) and + // the diagonal covariance matrix (another nchannels values) + + weight_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1); + sortKey_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC1); + mean_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC(work_ch)); + var_.create(frameSize.height * nmixtures_, frameSize_.width, CV_32FC(work_ch)); + + weight_.setTo(cv::Scalar::all(0)); + sortKey_.setTo(cv::Scalar::all(0)); + mean_.setTo(cv::Scalar::all(0)); + var_.setTo(cv::Scalar::all(0)); + + nframes_ = 0; + } } -void cv::gpu::MOG_GPU::release() +Ptr cv::gpu::createBackgroundSubtractorMOG(int history, int nmixtures, double backgroundRatio, double noiseSigma) { - frameSize_ = Size(0, 0); - frameType_ = 0; - nframes_ = 0; - - weight_.release(); - sortKey_.release(); - mean_.release(); - var_.release(); + return new MOGImpl(history, nmixtures, backgroundRatio, noiseSigma); } #endif diff --git a/modules/gpubgsegm/test/test_bgsegm.cpp b/modules/gpubgsegm/test/test_bgsegm.cpp index a5d187b043..588db8badf 100644 --- a/modules/gpubgsegm/test/test_bgsegm.cpp +++ b/modules/gpubgsegm/test/test_bgsegm.cpp @@ -193,7 +193,7 @@ GPU_TEST_P(MOG, Update) cap >> frame; ASSERT_FALSE(frame.empty()); - cv::gpu::MOG_GPU mog; + cv::Ptr mog = cv::gpu::createBackgroundSubtractorMOG(); cv::gpu::GpuMat foreground = createMat(frame.size(), CV_8UC1, useRoi); cv::Ptr mog_gold = cv::createBackgroundSubtractorMOG(); @@ -211,7 +211,7 @@ GPU_TEST_P(MOG, Update) cv::swap(temp, frame); } - mog(loadMat(frame, useRoi), foreground, (float)learningRate); + mog->apply(loadMat(frame, useRoi), foreground, learningRate); mog_gold->apply(frame, foreground_gold, learningRate); diff --git a/samples/gpu/bgfg_segm.cpp b/samples/gpu/bgfg_segm.cpp index 4c2d8dc18a..3fe26a99da 100644 --- a/samples/gpu/bgfg_segm.cpp +++ b/samples/gpu/bgfg_segm.cpp @@ -76,7 +76,7 @@ int main(int argc, const char** argv) GpuMat d_frame(frame); FGDStatModel fgd_stat; - MOG_GPU mog; + cv::Ptr mog = cv::gpu::createBackgroundSubtractorMOG(); MOG2_GPU mog2; GMG_GPU gmg; gmg.numInitializationFrames = 40; @@ -96,7 +96,7 @@ int main(int argc, const char** argv) break; case MOG: - mog(d_frame, d_fgmask, 0.01f); + mog->apply(d_frame, d_fgmask, 0.01); break; case MOG2: @@ -135,8 +135,8 @@ int main(int argc, const char** argv) break; case MOG: - mog(d_frame, d_fgmask, 0.01f); - mog.getBackgroundImage(d_bgimg); + mog->apply(d_frame, d_fgmask, 0.01); + mog->getBackgroundImage(d_bgimg); break; case MOG2: diff --git a/samples/gpu/performance/tests.cpp b/samples/gpu/performance/tests.cpp index 3c26d16b04..3681ff95c2 100644 --- a/samples/gpu/performance/tests.cpp +++ b/samples/gpu/performance/tests.cpp @@ -1346,10 +1346,10 @@ TEST(MOG) cap >> frame; cv::gpu::GpuMat d_frame(frame); - cv::gpu::MOG_GPU d_mog; + cv::Ptr d_mog = cv::gpu::createBackgroundSubtractorMOG(); cv::gpu::GpuMat d_foreground; - d_mog(d_frame, d_foreground, 0.01f); + d_mog->apply(d_frame, d_foreground, 0.01); while (!TestSystem::instance().stop()) { @@ -1358,7 +1358,7 @@ TEST(MOG) TestSystem::instance().gpuOn(); - d_mog(d_frame, d_foreground, 0.01f); + d_mog->apply(d_frame, d_foreground, 0.01); TestSystem::instance().gpuOff(); } -- GitLab