Commit 62edeeed authored by Vladislav Vinogradov's avatar Vladislav Vinogradov

refactored FGD algorithm

parent 69779309
...@@ -50,9 +50,6 @@ ...@@ -50,9 +50,6 @@
#include "opencv2/core/gpu.hpp" #include "opencv2/core/gpu.hpp"
#include "opencv2/video/background_segm.hpp" #include "opencv2/video/background_segm.hpp"
#include <memory>
#include "opencv2/gpufilters.hpp"
namespace cv { namespace gpu { namespace cv { namespace gpu {
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
...@@ -105,22 +102,22 @@ public: ...@@ -105,22 +102,22 @@ public:
CV_EXPORTS Ptr<gpu::BackgroundSubtractorGMG> CV_EXPORTS Ptr<gpu::BackgroundSubtractorGMG>
createBackgroundSubtractorGMG(int initializationFrames = 120, double decisionThreshold = 0.8); createBackgroundSubtractorGMG(int initializationFrames = 120, double decisionThreshold = 0.8);
////////////////////////////////////////////////////
// FGD
/**
* Foreground Object Detection from Videos Containing Complex Background.
* Liyuan Li, Weimin Huang, Irene Y.H. Gu, and Qi Tian.
* ACM MM2003 9p
*/
// Foreground Object Detection from Videos Containing Complex Background. class CV_EXPORTS BackgroundSubtractorFGD : public cv::BackgroundSubtractor
// Liyuan Li, Weimin Huang, Irene Y.H. Gu, and Qi Tian.
// ACM MM2003 9p
class CV_EXPORTS FGDStatModel
{ {
public: public:
struct CV_EXPORTS Params virtual void getForegroundRegions(OutputArrayOfArrays foreground_regions) = 0;
{ };
struct CV_EXPORTS FGDParams
{
int Lc; // Quantized levels per 'color' component. Power of two, typically 32, 64 or 128. int Lc; // Quantized levels per 'color' component. Power of two, typically 32, 64 or 128.
int N1c; // Number of color vectors used to model normal background color variation at a given pixel. int N1c; // Number of color vectors used to model normal background color variation at a given pixel.
int N2c; // Number of color vectors retained at given pixel. Must be > N1c, typically ~ 5/3 of N1c. int N2c; // Number of color vectors retained at given pixel. Must be > N1c, typically ~ 5/3 of N1c.
...@@ -144,37 +141,12 @@ public: ...@@ -144,37 +141,12 @@ public:
float minArea; // Discard foreground blobs whose bounding box is smaller than this threshold. float minArea; // Discard foreground blobs whose bounding box is smaller than this threshold.
// default Params // default Params
Params(); FGDParams();
};
// out_cn - channels count in output result (can be 3 or 4)
// 4-channels require more memory, but a bit faster
explicit FGDStatModel(int out_cn = 3);
explicit FGDStatModel(const cv::gpu::GpuMat& firstFrame, const Params& params = Params(), int out_cn = 3);
~FGDStatModel();
void create(const cv::gpu::GpuMat& firstFrame, const Params& params = Params());
void release();
int update(const cv::gpu::GpuMat& curFrame);
//8UC3 or 8UC4 reference background image
cv::gpu::GpuMat background;
//8UC1 foreground image
cv::gpu::GpuMat foreground;
std::vector< std::vector<cv::Point> > foreground_regions;
private:
FGDStatModel(const FGDStatModel&);
FGDStatModel& operator=(const FGDStatModel&);
class Impl;
std::auto_ptr<Impl> impl_;
}; };
CV_EXPORTS Ptr<gpu::BackgroundSubtractorFGD>
createBackgroundSubtractorFGD(const FGDParams& params = FGDParams());
}} // namespace cv { namespace gpu { }} // namespace cv { namespace gpu {
#endif /* __OPENCV_GPUBGSEGM_HPP__ */ #endif /* __OPENCV_GPUBGSEGM_HPP__ */
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "perf_precomp.hpp" #include "perf_precomp.hpp"
#include "opencv2/legacy.hpp" #include "opencv2/legacy.hpp"
#include "opencv2/gpuimgproc.hpp"
using namespace std; using namespace std;
using namespace testing; using namespace testing;
...@@ -90,10 +91,10 @@ PERF_TEST_P(Video, FGDStatModel, ...@@ -90,10 +91,10 @@ PERF_TEST_P(Video, FGDStatModel,
if (PERF_RUN_GPU()) if (PERF_RUN_GPU())
{ {
cv::gpu::GpuMat d_frame(frame); cv::gpu::GpuMat d_frame(frame), foreground, background3, background;
cv::gpu::FGDStatModel d_model(4); cv::Ptr<cv::gpu::BackgroundSubtractorFGD> d_fgd = cv::gpu::createBackgroundSubtractorFGD();
d_model.create(d_frame); d_fgd->apply(d_frame, foreground);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
{ {
...@@ -103,12 +104,12 @@ PERF_TEST_P(Video, FGDStatModel, ...@@ -103,12 +104,12 @@ PERF_TEST_P(Video, FGDStatModel,
d_frame.upload(frame); d_frame.upload(frame);
startTimer(); next(); startTimer(); next();
d_model.update(d_frame); d_fgd->apply(d_frame, foreground);
stopTimer(); stopTimer();
} }
const cv::gpu::GpuMat background = d_model.background; d_fgd->getBackgroundImage(background3);
const cv::gpu::GpuMat foreground = d_model.foreground; cv::gpu::cvtColor(background3, background, cv::COLOR_BGR2BGRA);
GPU_SANITY_CHECK(background, 1e-2, ERROR_RELATIVE); GPU_SANITY_CHECK(background, 1e-2, ERROR_RELATIVE);
GPU_SANITY_CHECK(foreground, 1e-2, ERROR_RELATIVE); GPU_SANITY_CHECK(foreground, 1e-2, ERROR_RELATIVE);
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
using namespace cv::gpu; using namespace cv::gpu;
using namespace cv::gpu::cudev; using namespace cv::gpu::cudev;
namespace bgfg namespace fgd
{ {
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// calcDiffHistogram // calcDiffHistogram
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#include "opencv2/core/gpu_types.hpp" #include "opencv2/core/gpu_types.hpp"
namespace bgfg namespace fgd
{ {
struct BGPixelStat struct BGPixelStat
{ {
......
...@@ -42,329 +42,150 @@ ...@@ -42,329 +42,150 @@
#include "precomp.hpp" #include "precomp.hpp"
#if !defined HAVE_CUDA || defined(CUDA_DISABLER) using namespace cv;
using namespace cv::gpu;
class cv::gpu::FGDStatModel::Impl #if !defined HAVE_CUDA || defined(CUDA_DISABLER)
{
};
cv::gpu::FGDStatModel::Params::Params() { throw_no_cuda(); } cv::gpu::FGDParams::FGDParams() { throw_no_cuda(); }
cv::gpu::FGDStatModel::FGDStatModel(int) { throw_no_cuda(); } Ptr<gpu::BackgroundSubtractorFGD> cv::gpu::createBackgroundSubtractorFGD(const FGDParams&) { throw_no_cuda(); return Ptr<gpu::BackgroundSubtractorFGD>(); }
cv::gpu::FGDStatModel::FGDStatModel(const cv::gpu::GpuMat&, const Params&, int) { throw_no_cuda(); }
cv::gpu::FGDStatModel::~FGDStatModel() {}
void cv::gpu::FGDStatModel::create(const cv::gpu::GpuMat&, const Params&) { throw_no_cuda(); }
void cv::gpu::FGDStatModel::release() {}
int cv::gpu::FGDStatModel::update(const cv::gpu::GpuMat&) { throw_no_cuda(); return 0; }
#else #else
#include "cuda/fgd.hpp" #include "cuda/fgd.hpp"
#include "opencv2/imgproc/imgproc_c.h" #include "opencv2/imgproc/imgproc_c.h"
/////////////////////////////////////////////////////////////////////////
// FGDParams
namespace namespace
{ {
class BGPixelStat // Default parameters of foreground detection algorithm:
{ const int BGFG_FGD_LC = 128;
public: const int BGFG_FGD_N1C = 15;
void create(cv::Size size, const cv::gpu::FGDStatModel::Params& params, int out_cn); const int BGFG_FGD_N2C = 25;
void release();
void setTrained();
operator bgfg::BGPixelStat();
private:
cv::gpu::GpuMat Pbc_;
cv::gpu::GpuMat Pbcc_;
cv::gpu::GpuMat is_trained_st_model_;
cv::gpu::GpuMat is_trained_dyn_model_;
cv::gpu::GpuMat ctable_Pv_;
cv::gpu::GpuMat ctable_Pvb_;
cv::gpu::GpuMat ctable_v_;
cv::gpu::GpuMat cctable_Pv_;
cv::gpu::GpuMat cctable_Pvb_;
cv::gpu::GpuMat cctable_v1_;
cv::gpu::GpuMat cctable_v2_;
};
void BGPixelStat::create(cv::Size size, const cv::gpu::FGDStatModel::Params& params, int out_cn)
{
cv::gpu::ensureSizeIsEnough(size, CV_32FC1, Pbc_);
Pbc_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(size, CV_32FC1, Pbcc_);
Pbcc_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(size, CV_8UC1, is_trained_st_model_);
is_trained_st_model_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(size, CV_8UC1, is_trained_dyn_model_);
is_trained_dyn_model_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(params.N2c * size.height, size.width, CV_32FC1, ctable_Pv_);
ctable_Pv_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(params.N2c * size.height, size.width, CV_32FC1, ctable_Pvb_);
ctable_Pvb_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(params.N2c * size.height, size.width, CV_8UC(out_cn), ctable_v_);
ctable_v_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_32FC1, cctable_Pv_);
cctable_Pv_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_32FC1, cctable_Pvb_);
cctable_Pvb_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_8UC(out_cn), cctable_v1_);
cctable_v1_.setTo(cv::Scalar::all(0));
cv::gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_8UC(out_cn), cctable_v2_);
cctable_v2_.setTo(cv::Scalar::all(0));
}
void BGPixelStat::release()
{
Pbc_.release();
Pbcc_.release();
is_trained_st_model_.release();
is_trained_dyn_model_.release();
ctable_Pv_.release();
ctable_Pvb_.release();
ctable_v_.release();
cctable_Pv_.release();
cctable_Pvb_.release();
cctable_v1_.release();
cctable_v2_.release();
}
void BGPixelStat::setTrained()
{
is_trained_st_model_.setTo(cv::Scalar::all(1));
is_trained_dyn_model_.setTo(cv::Scalar::all(1));
}
BGPixelStat::operator bgfg::BGPixelStat()
{
bgfg::BGPixelStat stat;
stat.rows_ = Pbc_.rows;
stat.Pbc_data_ = Pbc_.data;
stat.Pbc_step_ = Pbc_.step;
stat.Pbcc_data_ = Pbcc_.data;
stat.Pbcc_step_ = Pbcc_.step;
stat.is_trained_st_model_data_ = is_trained_st_model_.data;
stat.is_trained_st_model_step_ = is_trained_st_model_.step;
stat.is_trained_dyn_model_data_ = is_trained_dyn_model_.data;
stat.is_trained_dyn_model_step_ = is_trained_dyn_model_.step;
stat.ctable_Pv_data_ = ctable_Pv_.data;
stat.ctable_Pv_step_ = ctable_Pv_.step;
stat.ctable_Pvb_data_ = ctable_Pvb_.data; const int BGFG_FGD_LCC = 64;
stat.ctable_Pvb_step_ = ctable_Pvb_.step; const int BGFG_FGD_N1CC = 25;
const int BGFG_FGD_N2CC = 40;
stat.ctable_v_data_ = ctable_v_.data; // Background reference image update parameter:
stat.ctable_v_step_ = ctable_v_.step; const float BGFG_FGD_ALPHA_1 = 0.1f;
stat.cctable_Pv_data_ = cctable_Pv_.data; // stat model update parameter
stat.cctable_Pv_step_ = cctable_Pv_.step; // 0.002f ~ 1K frame(~45sec), 0.005 ~ 18sec (if 25fps and absolutely static BG)
const float BGFG_FGD_ALPHA_2 = 0.005f;
stat.cctable_Pvb_data_ = cctable_Pvb_.data; // start value for alpha parameter (to fast initiate statistic model)
stat.cctable_Pvb_step_ = cctable_Pvb_.step; const float BGFG_FGD_ALPHA_3 = 0.1f;
stat.cctable_v1_data_ = cctable_v1_.data; const float BGFG_FGD_DELTA = 2.0f;
stat.cctable_v1_step_ = cctable_v1_.step;
stat.cctable_v2_data_ = cctable_v2_.data; const float BGFG_FGD_T = 0.9f;
stat.cctable_v2_step_ = cctable_v2_.step;
return stat; const float BGFG_FGD_MINAREA= 15.0f;
}
} }
class cv::gpu::FGDStatModel::Impl cv::gpu::FGDParams::FGDParams()
{ {
public: Lc = BGFG_FGD_LC;
Impl(cv::gpu::GpuMat& background, cv::gpu::GpuMat& foreground, std::vector< std::vector<cv::Point> >& foreground_regions, int out_cn); N1c = BGFG_FGD_N1C;
~Impl(); N2c = BGFG_FGD_N2C;
void create(const cv::gpu::GpuMat& firstFrame, const cv::gpu::FGDStatModel::Params& params);
void release();
int update(const cv::gpu::GpuMat& curFrame);
private:
Impl(const Impl&);
Impl& operator=(const Impl&);
int out_cn_;
cv::gpu::FGDStatModel::Params params_;
cv::gpu::GpuMat& background_;
cv::gpu::GpuMat& foreground_;
std::vector< std::vector<cv::Point> >& foreground_regions_;
cv::Mat h_foreground_;
cv::gpu::GpuMat prevFrame_;
cv::gpu::GpuMat Ftd_;
cv::gpu::GpuMat Fbd_;
BGPixelStat stat_;
cv::gpu::GpuMat hist_;
cv::gpu::GpuMat histBuf_;
cv::gpu::GpuMat countBuf_;
cv::gpu::GpuMat buf_; Lcc = BGFG_FGD_LCC;
cv::gpu::GpuMat filterBrd_; N1cc = BGFG_FGD_N1CC;
N2cc = BGFG_FGD_N2CC;
cv::Ptr<cv::gpu::Filter> dilateFilter_; delta = BGFG_FGD_DELTA;
cv::Ptr<cv::gpu::Filter> erodeFilter_;
CvMemStorage* storage_; alpha1 = BGFG_FGD_ALPHA_1;
}; alpha2 = BGFG_FGD_ALPHA_2;
alpha3 = BGFG_FGD_ALPHA_3;
cv::gpu::FGDStatModel::Impl::Impl(cv::gpu::GpuMat& background, cv::gpu::GpuMat& foreground, std::vector< std::vector<cv::Point> >& foreground_regions, int out_cn) : T = BGFG_FGD_T;
out_cn_(out_cn), background_(background), foreground_(foreground), foreground_regions_(foreground_regions) minArea = BGFG_FGD_MINAREA;
{
CV_Assert( out_cn_ == 3 || out_cn_ == 4 );
storage_ = cvCreateMemStorage(); is_obj_without_holes = true;
CV_Assert( storage_ != 0 ); perform_morphing = 1;
} }
cv::gpu::FGDStatModel::Impl::~Impl() /////////////////////////////////////////////////////////////////////////
{ // copyChannels
cvReleaseMemStorage(&storage_);
}
namespace namespace
{ {
void copyChannels(const cv::gpu::GpuMat& src, cv::gpu::GpuMat& dst, int dst_cn = -1) void copyChannels(const GpuMat& src, GpuMat& dst, int dst_cn = -1)
{ {
const int src_cn = src.channels(); const int src_cn = src.channels();
if (dst_cn < 0) if (dst_cn < 0)
dst_cn = src_cn; dst_cn = src_cn;
cv::gpu::ensureSizeIsEnough(src.size(), CV_MAKE_TYPE(src.depth(), dst_cn), dst); gpu::ensureSizeIsEnough(src.size(), CV_MAKE_TYPE(src.depth(), dst_cn), dst);
if (src_cn == dst_cn) if (src_cn == dst_cn)
{
src.copyTo(dst); src.copyTo(dst);
}
else else
{ {
static const int cvt_codes[4][4] = static const int cvt_codes[4][4] =
{ {
{-1, -1, cv::COLOR_GRAY2BGR, cv::COLOR_GRAY2BGRA}, {-1, -1, COLOR_GRAY2BGR, COLOR_GRAY2BGRA},
{-1, -1, -1, -1}, {-1, -1, -1, -1},
{cv::COLOR_BGR2GRAY, -1, -1, cv::COLOR_BGR2BGRA}, {COLOR_BGR2GRAY, -1, -1, COLOR_BGR2BGRA},
{cv::COLOR_BGRA2GRAY, -1, cv::COLOR_BGRA2BGR, -1} {COLOR_BGRA2GRAY, -1, COLOR_BGRA2BGR, -1}
}; };
const int cvt_code = cvt_codes[src_cn - 1][dst_cn - 1]; const int cvt_code = cvt_codes[src_cn - 1][dst_cn - 1];
CV_DbgAssert( cvt_code >= 0 ); CV_DbgAssert( cvt_code >= 0 );
cv::gpu::cvtColor(src, dst, cvt_code, dst_cn); gpu::cvtColor(src, dst, cvt_code, dst_cn);
} }
} }
} }
void cv::gpu::FGDStatModel::Impl::create(const cv::gpu::GpuMat& firstFrame, const cv::gpu::FGDStatModel::Params& params)
{
CV_Assert(firstFrame.type() == CV_8UC3 || firstFrame.type() == CV_8UC4);
params_ = params;
cv::gpu::ensureSizeIsEnough(firstFrame.size(), CV_8UC1, foreground_);
copyChannels(firstFrame, background_, out_cn_);
copyChannels(firstFrame, prevFrame_);
cv::gpu::ensureSizeIsEnough(firstFrame.size(), CV_8UC1, Ftd_);
cv::gpu::ensureSizeIsEnough(firstFrame.size(), CV_8UC1, Fbd_);
stat_.create(firstFrame.size(), params_, out_cn_);
bgfg::setBGPixelStat(stat_);
if (params_.perform_morphing > 0)
{
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1 + params_.perform_morphing * 2, 1 + params_.perform_morphing * 2));
cv::Point anchor(params_.perform_morphing, params_.perform_morphing);
dilateFilter_ = cv::gpu::createMorphologyFilter(cv::MORPH_DILATE, CV_8UC1, kernel, anchor);
erodeFilter_ = cv::gpu::createMorphologyFilter(cv::MORPH_ERODE, CV_8UC1, kernel, anchor);
}
}
void cv::gpu::FGDStatModel::Impl::release()
{
background_.release();
foreground_.release();
prevFrame_.release();
Ftd_.release();
Fbd_.release();
stat_.release();
hist_.release();
histBuf_.release();
countBuf_.release();
buf_.release();
filterBrd_.release();
}
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
// changeDetection // changeDetection
namespace namespace
{ {
void calcDiffHistogram(const cv::gpu::GpuMat& prevFrame, const cv::gpu::GpuMat& curFrame, cv::gpu::GpuMat& hist, cv::gpu::GpuMat& histBuf) void calcDiffHistogram(const GpuMat& prevFrame, const GpuMat& curFrame, GpuMat& hist, GpuMat& histBuf)
{ {
typedef void (*func_t)(cv::gpu::PtrStepSzb prevFrame, cv::gpu::PtrStepSzb curFrame, unsigned int* hist0, unsigned int* hist1, unsigned int* hist2, unsigned int* partialBuf0, unsigned int* partialBuf1, unsigned int* partialBuf2, bool cc20, cudaStream_t stream); typedef void (*func_t)(PtrStepSzb prevFrame, PtrStepSzb curFrame,
unsigned int* hist0, unsigned int* hist1, unsigned int* hist2,
unsigned int* partialBuf0, unsigned int* partialBuf1, unsigned int* partialBuf2,
bool cc20, cudaStream_t stream);
static const func_t funcs[4][4] = static const func_t funcs[4][4] =
{ {
{0,0,0,0}, {0,0,0,0},
{0,0,0,0}, {0,0,0,0},
{0,0,bgfg::calcDiffHistogram_gpu<uchar3, uchar3>,bgfg::calcDiffHistogram_gpu<uchar3, uchar4>}, {0,0,fgd::calcDiffHistogram_gpu<uchar3, uchar3>,fgd::calcDiffHistogram_gpu<uchar3, uchar4>},
{0,0,bgfg::calcDiffHistogram_gpu<uchar4, uchar3>,bgfg::calcDiffHistogram_gpu<uchar4, uchar4>} {0,0,fgd::calcDiffHistogram_gpu<uchar4, uchar3>,fgd::calcDiffHistogram_gpu<uchar4, uchar4>}
}; };
hist.create(3, 256, CV_32SC1); hist.create(3, 256, CV_32SC1);
histBuf.create(3, bgfg::PARTIAL_HISTOGRAM_COUNT * bgfg::HISTOGRAM_BIN_COUNT, CV_32SC1); histBuf.create(3, fgd::PARTIAL_HISTOGRAM_COUNT * fgd::HISTOGRAM_BIN_COUNT, CV_32SC1);
funcs[prevFrame.channels() - 1][curFrame.channels() - 1]( funcs[prevFrame.channels() - 1][curFrame.channels() - 1](
prevFrame, curFrame, prevFrame, curFrame,
hist.ptr<unsigned int>(0), hist.ptr<unsigned int>(1), hist.ptr<unsigned int>(2), hist.ptr<unsigned int>(0), hist.ptr<unsigned int>(1), hist.ptr<unsigned int>(2),
histBuf.ptr<unsigned int>(0), histBuf.ptr<unsigned int>(1), histBuf.ptr<unsigned int>(2), histBuf.ptr<unsigned int>(0), histBuf.ptr<unsigned int>(1), histBuf.ptr<unsigned int>(2),
cv::gpu::deviceSupports(cv::gpu::FEATURE_SET_COMPUTE_20), 0); deviceSupports(FEATURE_SET_COMPUTE_20), 0);
} }
void calcRelativeVariance(unsigned int hist[3 * 256], double relativeVariance[3][bgfg::HISTOGRAM_BIN_COUNT]) void calcRelativeVariance(unsigned int hist[3 * 256], double relativeVariance[3][fgd::HISTOGRAM_BIN_COUNT])
{ {
std::memset(relativeVariance, 0, 3 * bgfg::HISTOGRAM_BIN_COUNT * sizeof(double)); std::memset(relativeVariance, 0, 3 * fgd::HISTOGRAM_BIN_COUNT * sizeof(double));
for (int thres = bgfg::HISTOGRAM_BIN_COUNT - 2; thres >= 0; --thres) for (int thres = fgd::HISTOGRAM_BIN_COUNT - 2; thres >= 0; --thres)
{ {
cv::Vec3d sum(0.0, 0.0, 0.0); Vec3d sum(0.0, 0.0, 0.0);
cv::Vec3d sqsum(0.0, 0.0, 0.0); Vec3d sqsum(0.0, 0.0, 0.0);
cv::Vec3i count(0, 0, 0); Vec3i count(0, 0, 0);
for (int j = thres; j < bgfg::HISTOGRAM_BIN_COUNT; ++j) for (int j = thres; j < fgd::HISTOGRAM_BIN_COUNT; ++j)
{ {
sum[0] += static_cast<double>(j) * hist[j]; sum[0] += static_cast<double>(j) * hist[j];
sqsum[0] += static_cast<double>(j * j) * hist[j]; sqsum[0] += static_cast<double>(j * j) * hist[j];
...@@ -383,7 +204,7 @@ namespace ...@@ -383,7 +204,7 @@ namespace
count[1] = std::max(count[1], 1); count[1] = std::max(count[1], 1);
count[2] = std::max(count[2], 1); count[2] = std::max(count[2], 1);
cv::Vec3d my( Vec3d my(
sum[0] / count[0], sum[0] / count[0],
sum[1] / count[1], sum[1] / count[1],
sum[2] / count[2] sum[2] / count[2]
...@@ -395,37 +216,39 @@ namespace ...@@ -395,37 +216,39 @@ namespace
} }
} }
void calcDiffThreshMask(const cv::gpu::GpuMat& prevFrame, const cv::gpu::GpuMat& curFrame, cv::Vec3d bestThres, cv::gpu::GpuMat& changeMask) void calcDiffThreshMask(const GpuMat& prevFrame, const GpuMat& curFrame, Vec3d bestThres, GpuMat& changeMask)
{ {
typedef void (*func_t)(cv::gpu::PtrStepSzb prevFrame, cv::gpu::PtrStepSzb curFrame, uchar3 bestThres, cv::gpu::PtrStepSzb changeMask, cudaStream_t stream); typedef void (*func_t)(PtrStepSzb prevFrame, PtrStepSzb curFrame, uchar3 bestThres, PtrStepSzb changeMask, cudaStream_t stream);
static const func_t funcs[4][4] = static const func_t funcs[4][4] =
{ {
{0,0,0,0}, {0,0,0,0},
{0,0,0,0}, {0,0,0,0},
{0,0,bgfg::calcDiffThreshMask_gpu<uchar3, uchar3>,bgfg::calcDiffThreshMask_gpu<uchar3, uchar4>}, {0,0,fgd::calcDiffThreshMask_gpu<uchar3, uchar3>,fgd::calcDiffThreshMask_gpu<uchar3, uchar4>},
{0,0,bgfg::calcDiffThreshMask_gpu<uchar4, uchar3>,bgfg::calcDiffThreshMask_gpu<uchar4, uchar4>} {0,0,fgd::calcDiffThreshMask_gpu<uchar4, uchar3>,fgd::calcDiffThreshMask_gpu<uchar4, uchar4>}
}; };
changeMask.setTo(cv::Scalar::all(0)); changeMask.setTo(Scalar::all(0));
funcs[prevFrame.channels() - 1][curFrame.channels() - 1](prevFrame, curFrame, make_uchar3((uchar)bestThres[0], (uchar)bestThres[1], (uchar)bestThres[2]), changeMask, 0); funcs[prevFrame.channels() - 1][curFrame.channels() - 1](prevFrame, curFrame,
make_uchar3((uchar)bestThres[0], (uchar)bestThres[1], (uchar)bestThres[2]),
changeMask, 0);
} }
// performs change detection for Foreground detection algorithm // performs change detection for Foreground detection algorithm
void changeDetection(const cv::gpu::GpuMat& prevFrame, const cv::gpu::GpuMat& curFrame, cv::gpu::GpuMat& changeMask, cv::gpu::GpuMat& hist, cv::gpu::GpuMat& histBuf) void changeDetection(const GpuMat& prevFrame, const GpuMat& curFrame, GpuMat& changeMask, GpuMat& hist, GpuMat& histBuf)
{ {
calcDiffHistogram(prevFrame, curFrame, hist, histBuf); calcDiffHistogram(prevFrame, curFrame, hist, histBuf);
unsigned int histData[3 * 256]; unsigned int histData[3 * 256];
cv::Mat h_hist(3, 256, CV_32SC1, histData); Mat h_hist(3, 256, CV_32SC1, histData);
hist.download(h_hist); hist.download(h_hist);
double relativeVariance[3][bgfg::HISTOGRAM_BIN_COUNT]; double relativeVariance[3][fgd::HISTOGRAM_BIN_COUNT];
calcRelativeVariance(histData, relativeVariance); calcRelativeVariance(histData, relativeVariance);
// Find maximum: // Find maximum:
cv::Vec3d bestThres(10.0, 10.0, 10.0); Vec3d bestThres(10.0, 10.0, 10.0);
for (int i = 0; i < bgfg::HISTOGRAM_BIN_COUNT; ++i) for (int i = 0; i < fgd::HISTOGRAM_BIN_COUNT; ++i)
{ {
bestThres[0] = std::max(bestThres[0], relativeVariance[0][i]); bestThres[0] = std::max(bestThres[0], relativeVariance[0][i]);
bestThres[1] = std::max(bestThres[1], relativeVariance[1][i]); bestThres[1] = std::max(bestThres[1], relativeVariance[1][i]);
...@@ -441,12 +264,12 @@ namespace ...@@ -441,12 +264,12 @@ namespace
namespace namespace
{ {
int bgfgClassification(const cv::gpu::GpuMat& prevFrame, const cv::gpu::GpuMat& curFrame, int bgfgClassification(const GpuMat& prevFrame, const GpuMat& curFrame,
const cv::gpu::GpuMat& Ftd, const cv::gpu::GpuMat& Fbd, const GpuMat& Ftd, const GpuMat& Fbd,
cv::gpu::GpuMat& foreground, cv::gpu::GpuMat& countBuf, GpuMat& foreground, GpuMat& countBuf,
const cv::gpu::FGDStatModel::Params& params, int out_cn) const FGDParams& params, int out_cn)
{ {
typedef void (*func_t)(cv::gpu::PtrStepSzb prevFrame, cv::gpu::PtrStepSzb curFrame, cv::gpu::PtrStepSzb Ftd, cv::gpu::PtrStepSzb Fbd, cv::gpu::PtrStepSzb foreground, typedef void (*func_t)(PtrStepSzb prevFrame, PtrStepSzb curFrame, PtrStepSzb Ftd, PtrStepSzb Fbd, PtrStepSzb foreground,
int deltaC, int deltaCC, float alpha2, int N1c, int N1cc, cudaStream_t stream); int deltaC, int deltaCC, float alpha2, int N1c, int N1cc, cudaStream_t stream);
static const func_t funcs[4][4][4] = static const func_t funcs[4][4][4] =
{ {
...@@ -458,24 +281,26 @@ namespace ...@@ -458,24 +281,26 @@ namespace
}, },
{ {
{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0},
{0,0,bgfg::bgfgClassification_gpu<uchar3, uchar3, uchar3>,bgfg::bgfgClassification_gpu<uchar3, uchar3, uchar4>}, {0,0,fgd::bgfgClassification_gpu<uchar3, uchar3, uchar3>,fgd::bgfgClassification_gpu<uchar3, uchar3, uchar4>},
{0,0,bgfg::bgfgClassification_gpu<uchar3, uchar4, uchar3>,bgfg::bgfgClassification_gpu<uchar3, uchar4, uchar4>} {0,0,fgd::bgfgClassification_gpu<uchar3, uchar4, uchar3>,fgd::bgfgClassification_gpu<uchar3, uchar4, uchar4>}
}, },
{ {
{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0},
{0,0,bgfg::bgfgClassification_gpu<uchar4, uchar3, uchar3>,bgfg::bgfgClassification_gpu<uchar4, uchar3, uchar4>}, {0,0,fgd::bgfgClassification_gpu<uchar4, uchar3, uchar3>,fgd::bgfgClassification_gpu<uchar4, uchar3, uchar4>},
{0,0,bgfg::bgfgClassification_gpu<uchar4, uchar4, uchar3>,bgfg::bgfgClassification_gpu<uchar4, uchar4, uchar4>} {0,0,fgd::bgfgClassification_gpu<uchar4, uchar4, uchar3>,fgd::bgfgClassification_gpu<uchar4, uchar4, uchar4>}
} }
}; };
const int deltaC = cvRound(params.delta * 256 / params.Lc); const int deltaC = cvRound(params.delta * 256 / params.Lc);
const int deltaCC = cvRound(params.delta * 256 / params.Lcc); const int deltaCC = cvRound(params.delta * 256 / params.Lcc);
funcs[prevFrame.channels() - 1][curFrame.channels() - 1][out_cn - 1](prevFrame, curFrame, Ftd, Fbd, foreground, deltaC, deltaCC, params.alpha2, params.N1c, params.N1cc, 0); funcs[prevFrame.channels() - 1][curFrame.channels() - 1][out_cn - 1](prevFrame, curFrame, Ftd, Fbd, foreground,
deltaC, deltaCC, params.alpha2,
params.N1c, params.N1cc, 0);
int count = cv::gpu::countNonZero(foreground, countBuf); int count = gpu::countNonZero(foreground, countBuf);
cv::gpu::multiply(foreground, cv::Scalar::all(255), foreground); gpu::multiply(foreground, Scalar::all(255), foreground);
return count; return count;
} }
...@@ -486,20 +311,20 @@ namespace ...@@ -486,20 +311,20 @@ namespace
namespace namespace
{ {
void morphology(const cv::gpu::GpuMat& src, cv::gpu::GpuMat& dst, cv::gpu::GpuMat& filterBrd, int brd, cv::Ptr<cv::gpu::Filter>& filter, cv::Scalar brdVal) void morphology(const GpuMat& src, GpuMat& dst, GpuMat& filterBrd, int brd, Ptr<gpu::Filter>& filter, Scalar brdVal)
{ {
cv::gpu::copyMakeBorder(src, filterBrd, brd, brd, brd, brd, cv::BORDER_CONSTANT, brdVal); gpu::copyMakeBorder(src, filterBrd, brd, brd, brd, brd, BORDER_CONSTANT, brdVal);
filter->apply(filterBrd(cv::Rect(brd, brd, src.cols, src.rows)), dst); filter->apply(filterBrd(Rect(brd, brd, src.cols, src.rows)), dst);
} }
void smoothForeground(cv::gpu::GpuMat& foreground, cv::gpu::GpuMat& filterBrd, cv::gpu::GpuMat& buf, void smoothForeground(GpuMat& foreground, GpuMat& filterBrd, GpuMat& buf,
cv::Ptr<cv::gpu::Filter>& erodeFilter, cv::Ptr<cv::gpu::Filter>& dilateFilter, Ptr<gpu::Filter>& erodeFilter, Ptr<gpu::Filter>& dilateFilter,
const cv::gpu::FGDStatModel::Params& params) const FGDParams& params)
{ {
const int brd = params.perform_morphing; const int brd = params.perform_morphing;
const cv::Scalar erodeBrdVal = cv::Scalar::all(UCHAR_MAX); const Scalar erodeBrdVal = Scalar::all(UCHAR_MAX);
const cv::Scalar dilateBrdVal = cv::Scalar::all(0); const Scalar dilateBrdVal = Scalar::all(0);
// MORPH_OPEN // MORPH_OPEN
morphology(foreground, buf, filterBrd, brd, erodeFilter, erodeBrdVal); morphology(foreground, buf, filterBrd, brd, erodeFilter, erodeBrdVal);
...@@ -516,28 +341,28 @@ namespace ...@@ -516,28 +341,28 @@ namespace
namespace namespace
{ {
void seqToContours(CvSeq* _ccontours, CvMemStorage* storage, cv::OutputArrayOfArrays _contours) void seqToContours(CvSeq* _ccontours, CvMemStorage* storage, OutputArrayOfArrays _contours)
{ {
cv::Seq<CvSeq*> all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage)); Seq<CvSeq*> all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage));
size_t total = all_contours.size(); size_t total = all_contours.size();
_contours.create((int) total, 1, 0, -1, true); _contours.create((int) total, 1, 0, -1, true);
cv::SeqIterator<CvSeq*> it = all_contours.begin(); SeqIterator<CvSeq*> it = all_contours.begin();
for (size_t i = 0; i < total; ++i, ++it) for (size_t i = 0; i < total; ++i, ++it)
{ {
CvSeq* c = *it; CvSeq* c = *it;
((CvContour*)c)->color = (int)i; ((CvContour*)c)->color = (int)i;
_contours.create((int)c->total, 1, CV_32SC2, (int)i, true); _contours.create((int)c->total, 1, CV_32SC2, (int)i, true);
cv::Mat ci = _contours.getMat((int)i); Mat ci = _contours.getMat((int)i);
CV_Assert( ci.isContinuous() ); CV_Assert( ci.isContinuous() );
cvCvtSeqToArray(c, ci.data); cvCvtSeqToArray(c, ci.data);
} }
} }
int findForegroundRegions(cv::gpu::GpuMat& d_foreground, cv::Mat& h_foreground, std::vector< std::vector<cv::Point> >& foreground_regions, int findForegroundRegions(GpuMat& d_foreground, Mat& h_foreground, std::vector< std::vector<Point> >& foreground_regions,
CvMemStorage* storage, const cv::gpu::FGDStatModel::Params& params) CvMemStorage* storage, const FGDParams& params)
{ {
int region_count = 0; int region_count = 0;
...@@ -581,7 +406,7 @@ namespace ...@@ -581,7 +406,7 @@ namespace
seqToContours(first_seq, storage, foreground_regions); seqToContours(first_seq, storage, foreground_regions);
h_foreground.setTo(0); h_foreground.setTo(0);
cv::drawContours(h_foreground, foreground_regions, -1, cv::Scalar::all(255), -1); drawContours(h_foreground, foreground_regions, -1, Scalar::all(255), -1);
d_foreground.upload(h_foreground); d_foreground.upload(h_foreground);
...@@ -594,12 +419,12 @@ namespace ...@@ -594,12 +419,12 @@ namespace
namespace namespace
{ {
void updateBackgroundModel(const cv::gpu::GpuMat& prevFrame, const cv::gpu::GpuMat& curFrame, const cv::gpu::GpuMat& Ftd, const cv::gpu::GpuMat& Fbd, void updateBackgroundModel(const GpuMat& prevFrame, const GpuMat& curFrame, const GpuMat& Ftd, const GpuMat& Fbd,
const cv::gpu::GpuMat& foreground, cv::gpu::GpuMat& background, const GpuMat& foreground, GpuMat& background,
const cv::gpu::FGDStatModel::Params& params) const FGDParams& params)
{ {
typedef void (*func_t)(cv::gpu::PtrStepSzb prevFrame, cv::gpu::PtrStepSzb curFrame, cv::gpu::PtrStepSzb Ftd, cv::gpu::PtrStepSzb Fbd, typedef void (*func_t)(PtrStepSzb prevFrame, PtrStepSzb curFrame, PtrStepSzb Ftd, PtrStepSzb Fbd,
cv::gpu::PtrStepSzb foreground, cv::gpu::PtrStepSzb background, PtrStepSzb foreground, PtrStepSzb background,
int deltaC, int deltaCC, float alpha1, float alpha2, float alpha3, int N1c, int N1cc, int N2c, int N2cc, float T, cudaStream_t stream); int deltaC, int deltaCC, float alpha1, float alpha2, float alpha3, int N1c, int N1cc, int N2c, int N2cc, float T, cudaStream_t stream);
static const func_t funcs[4][4][4] = static const func_t funcs[4][4][4] =
{ {
...@@ -611,13 +436,13 @@ namespace ...@@ -611,13 +436,13 @@ namespace
}, },
{ {
{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0},
{0,0,bgfg::updateBackgroundModel_gpu<uchar3, uchar3, uchar3>,bgfg::updateBackgroundModel_gpu<uchar3, uchar3, uchar4>}, {0,0,fgd::updateBackgroundModel_gpu<uchar3, uchar3, uchar3>,fgd::updateBackgroundModel_gpu<uchar3, uchar3, uchar4>},
{0,0,bgfg::updateBackgroundModel_gpu<uchar3, uchar4, uchar3>,bgfg::updateBackgroundModel_gpu<uchar3, uchar4, uchar4>} {0,0,fgd::updateBackgroundModel_gpu<uchar3, uchar4, uchar3>,fgd::updateBackgroundModel_gpu<uchar3, uchar4, uchar4>}
}, },
{ {
{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0},
{0,0,bgfg::updateBackgroundModel_gpu<uchar4, uchar3, uchar3>,bgfg::updateBackgroundModel_gpu<uchar4, uchar3, uchar4>}, {0,0,fgd::updateBackgroundModel_gpu<uchar4, uchar3, uchar3>,fgd::updateBackgroundModel_gpu<uchar4, uchar3, uchar4>},
{0,0,bgfg::updateBackgroundModel_gpu<uchar4, uchar4, uchar3>,bgfg::updateBackgroundModel_gpu<uchar4, uchar4, uchar4>} {0,0,fgd::updateBackgroundModel_gpu<uchar4, uchar4, uchar3>,fgd::updateBackgroundModel_gpu<uchar4, uchar4, uchar4>}
} }
}; };
...@@ -626,34 +451,205 @@ namespace ...@@ -626,34 +451,205 @@ namespace
funcs[prevFrame.channels() - 1][curFrame.channels() - 1][background.channels() - 1]( funcs[prevFrame.channels() - 1][curFrame.channels() - 1][background.channels() - 1](
prevFrame, curFrame, Ftd, Fbd, foreground, background, prevFrame, curFrame, Ftd, Fbd, foreground, background,
deltaC, deltaCC, params.alpha1, params.alpha2, params.alpha3, params.N1c, params.N1cc, params.N2c, params.N2cc, params.T, deltaC, deltaCC, params.alpha1, params.alpha2, params.alpha3,
params.N1c, params.N1cc, params.N2c, params.N2cc, params.T,
0); 0);
} }
} }
/////////////////////////////////////////////////////////////////////////
// Impl::update
int cv::gpu::FGDStatModel::Impl::update(const cv::gpu::GpuMat& curFrame) namespace
{ {
CV_Assert(curFrame.type() == CV_8UC3 || curFrame.type() == CV_8UC4); class BGPixelStat
CV_Assert(curFrame.size() == prevFrame_.size()); {
public:
void create(Size size, const FGDParams& params);
void setTrained();
operator fgd::BGPixelStat();
private:
GpuMat Pbc_;
GpuMat Pbcc_;
GpuMat is_trained_st_model_;
GpuMat is_trained_dyn_model_;
GpuMat ctable_Pv_;
GpuMat ctable_Pvb_;
GpuMat ctable_v_;
GpuMat cctable_Pv_;
GpuMat cctable_Pvb_;
GpuMat cctable_v1_;
GpuMat cctable_v2_;
};
void BGPixelStat::create(Size size, const FGDParams& params)
{
gpu::ensureSizeIsEnough(size, CV_32FC1, Pbc_);
Pbc_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(size, CV_32FC1, Pbcc_);
Pbcc_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(size, CV_8UC1, is_trained_st_model_);
is_trained_st_model_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(size, CV_8UC1, is_trained_dyn_model_);
is_trained_dyn_model_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(params.N2c * size.height, size.width, CV_32FC1, ctable_Pv_);
ctable_Pv_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(params.N2c * size.height, size.width, CV_32FC1, ctable_Pvb_);
ctable_Pvb_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(params.N2c * size.height, size.width, CV_8UC4, ctable_v_);
ctable_v_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_32FC1, cctable_Pv_);
cctable_Pv_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_32FC1, cctable_Pvb_);
cctable_Pvb_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_8UC4, cctable_v1_);
cctable_v1_.setTo(Scalar::all(0));
gpu::ensureSizeIsEnough(params.N2cc * size.height, size.width, CV_8UC4, cctable_v2_);
cctable_v2_.setTo(Scalar::all(0));
}
void BGPixelStat::setTrained()
{
is_trained_st_model_.setTo(Scalar::all(1));
is_trained_dyn_model_.setTo(Scalar::all(1));
}
BGPixelStat::operator fgd::BGPixelStat()
{
fgd::BGPixelStat stat;
stat.rows_ = Pbc_.rows;
stat.Pbc_data_ = Pbc_.data;
stat.Pbc_step_ = Pbc_.step;
stat.Pbcc_data_ = Pbcc_.data;
stat.Pbcc_step_ = Pbcc_.step;
stat.is_trained_st_model_data_ = is_trained_st_model_.data;
stat.is_trained_st_model_step_ = is_trained_st_model_.step;
stat.is_trained_dyn_model_data_ = is_trained_dyn_model_.data;
stat.is_trained_dyn_model_step_ = is_trained_dyn_model_.step;
stat.ctable_Pv_data_ = ctable_Pv_.data;
stat.ctable_Pv_step_ = ctable_Pv_.step;
stat.ctable_Pvb_data_ = ctable_Pvb_.data;
stat.ctable_Pvb_step_ = ctable_Pvb_.step;
stat.ctable_v_data_ = ctable_v_.data;
stat.ctable_v_step_ = ctable_v_.step;
stat.cctable_Pv_data_ = cctable_Pv_.data;
stat.cctable_Pv_step_ = cctable_Pv_.step;
stat.cctable_Pvb_data_ = cctable_Pvb_.data;
stat.cctable_Pvb_step_ = cctable_Pvb_.step;
stat.cctable_v1_data_ = cctable_v1_.data;
stat.cctable_v1_step_ = cctable_v1_.step;
stat.cctable_v2_data_ = cctable_v2_.data;
stat.cctable_v2_step_ = cctable_v2_.step;
return stat;
}
class FGDImpl : public gpu::BackgroundSubtractorFGD
{
public:
explicit FGDImpl(const FGDParams& params);
~FGDImpl();
void apply(InputArray image, OutputArray fgmask, double learningRate=-1);
void getBackgroundImage(OutputArray backgroundImage) const;
void getForegroundRegions(OutputArrayOfArrays foreground_regions);
private:
void initialize(const GpuMat& firstFrame);
FGDParams params_;
Size frameSize_;
GpuMat background_;
GpuMat foreground_;
std::vector< std::vector<Point> > foreground_regions_;
Mat h_foreground_;
GpuMat prevFrame_;
GpuMat Ftd_;
GpuMat Fbd_;
BGPixelStat stat_;
GpuMat hist_;
GpuMat histBuf_;
GpuMat countBuf_;
GpuMat buf_;
GpuMat filterBrd_;
Ptr<gpu::Filter> dilateFilter_;
Ptr<gpu::Filter> erodeFilter_;
CvMemStorage* storage_;
};
FGDImpl::FGDImpl(const FGDParams& params) : params_(params), frameSize_(0, 0)
{
storage_ = cvCreateMemStorage();
CV_Assert( storage_ != 0 );
}
FGDImpl::~FGDImpl()
{
cvReleaseMemStorage(&storage_);
}
void FGDImpl::apply(InputArray _frame, OutputArray fgmask, double)
{
GpuMat curFrame = _frame.getGpuMat();
if (curFrame.size() != frameSize_)
{
initialize(curFrame);
return;
}
CV_Assert( curFrame.type() == CV_8UC3 || curFrame.type() == CV_8UC4 );
CV_Assert( curFrame.size() == prevFrame_.size() );
cvClearMemStorage(storage_); cvClearMemStorage(storage_);
foreground_regions_.clear(); foreground_regions_.clear();
foreground_.setTo(cv::Scalar::all(0)); foreground_.setTo(Scalar::all(0));
changeDetection(prevFrame_, curFrame, Ftd_, hist_, histBuf_); changeDetection(prevFrame_, curFrame, Ftd_, hist_, histBuf_);
changeDetection(background_, curFrame, Fbd_, hist_, histBuf_); changeDetection(background_, curFrame, Fbd_, hist_, histBuf_);
int FG_pixels_count = bgfgClassification(prevFrame_, curFrame, Ftd_, Fbd_, foreground_, countBuf_, params_, out_cn_); int FG_pixels_count = bgfgClassification(prevFrame_, curFrame, Ftd_, Fbd_, foreground_, countBuf_, params_, 4);
if (params_.perform_morphing > 0) if (params_.perform_morphing > 0)
smoothForeground(foreground_, filterBrd_, buf_, erodeFilter_, dilateFilter_, params_); smoothForeground(foreground_, filterBrd_, buf_, erodeFilter_, dilateFilter_, params_);
int region_count = 0;
if (params_.minArea > 0 || params_.is_obj_without_holes) if (params_.minArea > 0 || params_.is_obj_without_holes)
region_count = findForegroundRegions(foreground_, h_foreground_, foreground_regions_, storage_, params_); findForegroundRegions(foreground_, h_foreground_, foreground_regions_, storage_, params_);
// Check ALL BG update condition: // Check ALL BG update condition:
const double BGFG_FGD_BG_UPDATE_TRESH = 0.5; const double BGFG_FGD_BG_UPDATE_TRESH = 0.5;
...@@ -662,90 +658,64 @@ int cv::gpu::FGDStatModel::Impl::update(const cv::gpu::GpuMat& curFrame) ...@@ -662,90 +658,64 @@ int cv::gpu::FGDStatModel::Impl::update(const cv::gpu::GpuMat& curFrame)
updateBackgroundModel(prevFrame_, curFrame, Ftd_, Fbd_, foreground_, background_, params_); updateBackgroundModel(prevFrame_, curFrame, Ftd_, Fbd_, foreground_, background_, params_);
copyChannels(curFrame, prevFrame_); copyChannels(curFrame, prevFrame_, 4);
return region_count;
}
namespace foreground_.copyTo(fgmask);
{ }
// Default parameters of foreground detection algorithm:
const int BGFG_FGD_LC = 128;
const int BGFG_FGD_N1C = 15;
const int BGFG_FGD_N2C = 25;
const int BGFG_FGD_LCC = 64;
const int BGFG_FGD_N1CC = 25;
const int BGFG_FGD_N2CC = 40;
// Background reference image update parameter:
const float BGFG_FGD_ALPHA_1 = 0.1f;
// stat model update parameter
// 0.002f ~ 1K frame(~45sec), 0.005 ~ 18sec (if 25fps and absolutely static BG)
const float BGFG_FGD_ALPHA_2 = 0.005f;
// start value for alpha parameter (to fast initiate statistic model)
const float BGFG_FGD_ALPHA_3 = 0.1f;
const float BGFG_FGD_DELTA = 2.0f; void FGDImpl::getBackgroundImage(OutputArray backgroundImage) const
{
gpu::cvtColor(background_, backgroundImage, COLOR_BGRA2BGR);
}
const float BGFG_FGD_T = 0.9f; void FGDImpl::getForegroundRegions(OutputArrayOfArrays dst)
{
size_t total = foreground_regions_.size();
const float BGFG_FGD_MINAREA= 15.0f; dst.create((int) total, 1, 0, -1, true);
}
cv::gpu::FGDStatModel::Params::Params() for (size_t i = 0; i < total; ++i)
{ {
Lc = BGFG_FGD_LC; std::vector<Point>& c = foreground_regions_[i];
N1c = BGFG_FGD_N1C;
N2c = BGFG_FGD_N2C;
Lcc = BGFG_FGD_LCC; dst.create((int) c.size(), 1, CV_32SC2, (int) i, true);
N1cc = BGFG_FGD_N1CC; Mat ci = dst.getMat((int) i);
N2cc = BGFG_FGD_N2CC;
delta = BGFG_FGD_DELTA; Mat(ci.size(), ci.type(), &c[0]).copyTo(ci);
}
}
alpha1 = BGFG_FGD_ALPHA_1; void FGDImpl::initialize(const GpuMat& firstFrame)
alpha2 = BGFG_FGD_ALPHA_2; {
alpha3 = BGFG_FGD_ALPHA_3; CV_Assert( firstFrame.type() == CV_8UC3 || firstFrame.type() == CV_8UC4 );
T = BGFG_FGD_T; frameSize_ = firstFrame.size();
minArea = BGFG_FGD_MINAREA;
is_obj_without_holes = true; gpu::ensureSizeIsEnough(firstFrame.size(), CV_8UC1, foreground_);
perform_morphing = 1;
}
cv::gpu::FGDStatModel::FGDStatModel(int out_cn) copyChannels(firstFrame, background_, 4);
{ copyChannels(firstFrame, prevFrame_, 4);
impl_.reset(new Impl(background, foreground, foreground_regions, out_cn));
}
cv::gpu::FGDStatModel::FGDStatModel(const cv::gpu::GpuMat& firstFrame, const Params& params, int out_cn) gpu::ensureSizeIsEnough(firstFrame.size(), CV_8UC1, Ftd_);
{ gpu::ensureSizeIsEnough(firstFrame.size(), CV_8UC1, Fbd_);
impl_.reset(new Impl(background, foreground, foreground_regions, out_cn));
create(firstFrame, params);
}
cv::gpu::FGDStatModel::~FGDStatModel() stat_.create(firstFrame.size(), params_);
{ fgd::setBGPixelStat(stat_);
}
void cv::gpu::FGDStatModel::create(const cv::gpu::GpuMat& firstFrame, const Params& params) if (params_.perform_morphing > 0)
{ {
impl_->create(firstFrame, params); Mat kernel = getStructuringElement(MORPH_RECT, Size(1 + params_.perform_morphing * 2, 1 + params_.perform_morphing * 2));
} Point anchor(params_.perform_morphing, params_.perform_morphing);
void cv::gpu::FGDStatModel::release() dilateFilter_ = gpu::createMorphologyFilter(MORPH_DILATE, CV_8UC1, kernel, anchor);
{ erodeFilter_ = gpu::createMorphologyFilter(MORPH_ERODE, CV_8UC1, kernel, anchor);
impl_->release(); }
}
} }
int cv::gpu::FGDStatModel::update(const cv::gpu::GpuMat& curFrame) Ptr<gpu::BackgroundSubtractorFGD> cv::gpu::createBackgroundSubtractorFGD(const FGDParams& params)
{ {
return impl_->update(curFrame); return new FGDImpl(params);
} }
#endif // HAVE_CUDA #endif // HAVE_CUDA
...@@ -72,11 +72,10 @@ namespace cv ...@@ -72,11 +72,10 @@ namespace cv
} }
} }
PARAM_TEST_CASE(FGDStatModel, cv::gpu::DeviceInfo, std::string, Channels) PARAM_TEST_CASE(FGDStatModel, cv::gpu::DeviceInfo, std::string)
{ {
cv::gpu::DeviceInfo devInfo; cv::gpu::DeviceInfo devInfo;
std::string inputFile; std::string inputFile;
int out_cn;
virtual void SetUp() virtual void SetUp()
{ {
...@@ -84,8 +83,6 @@ PARAM_TEST_CASE(FGDStatModel, cv::gpu::DeviceInfo, std::string, Channels) ...@@ -84,8 +83,6 @@ PARAM_TEST_CASE(FGDStatModel, cv::gpu::DeviceInfo, std::string, Channels)
cv::gpu::setDevice(devInfo.deviceID()); cv::gpu::setDevice(devInfo.deviceID());
inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "video/" + GET_PARAM(1); inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "video/" + GET_PARAM(1);
out_cn = GET_PARAM(2);
} }
}; };
...@@ -102,15 +99,10 @@ GPU_TEST_P(FGDStatModel, Update) ...@@ -102,15 +99,10 @@ GPU_TEST_P(FGDStatModel, Update)
cv::Ptr<CvBGStatModel> model(cvCreateFGDStatModel(&ipl_frame)); cv::Ptr<CvBGStatModel> model(cvCreateFGDStatModel(&ipl_frame));
cv::gpu::GpuMat d_frame(frame); cv::gpu::GpuMat d_frame(frame);
cv::gpu::FGDStatModel d_model(out_cn); cv::Ptr<cv::gpu::BackgroundSubtractorFGD> d_fgd = cv::gpu::createBackgroundSubtractorFGD();
d_model.create(d_frame); cv::gpu::GpuMat d_foreground, d_background;
std::vector< std::vector<cv::Point> > foreground_regions;
cv::Mat h_background; d_fgd->apply(d_frame, d_foreground);
cv::Mat h_foreground;
cv::Mat h_background3;
cv::Mat backgroundDiff;
cv::Mat foregroundDiff;
for (int i = 0; i < 5; ++i) for (int i = 0; i < 5; ++i)
{ {
...@@ -121,32 +113,23 @@ GPU_TEST_P(FGDStatModel, Update) ...@@ -121,32 +113,23 @@ GPU_TEST_P(FGDStatModel, Update)
int gold_count = cvUpdateBGStatModel(&ipl_frame, model); int gold_count = cvUpdateBGStatModel(&ipl_frame, model);
d_frame.upload(frame); d_frame.upload(frame);
d_fgd->apply(d_frame, d_foreground);
int count = d_model.update(d_frame); d_fgd->getBackgroundImage(d_background);
d_fgd->getForegroundRegions(foreground_regions);
ASSERT_EQ(gold_count, count); int count = (int) foreground_regions.size();
cv::Mat gold_background = cv::cvarrToMat(model->background); cv::Mat gold_background = cv::cvarrToMat(model->background);
cv::Mat gold_foreground = cv::cvarrToMat(model->foreground); cv::Mat gold_foreground = cv::cvarrToMat(model->foreground);
if (out_cn == 3) ASSERT_MAT_NEAR(gold_background, d_background, 1.0);
d_model.background.download(h_background3); ASSERT_MAT_NEAR(gold_foreground, d_foreground, 0.0);
else ASSERT_EQ(gold_count, count);
{
d_model.background.download(h_background);
cv::cvtColor(h_background, h_background3, cv::COLOR_BGRA2BGR);
}
d_model.foreground.download(h_foreground);
ASSERT_MAT_NEAR(gold_background, h_background3, 1.0);
ASSERT_MAT_NEAR(gold_foreground, h_foreground, 0.0);
} }
} }
INSTANTIATE_TEST_CASE_P(GPU_BgSegm, FGDStatModel, testing::Combine( INSTANTIATE_TEST_CASE_P(GPU_BgSegm, FGDStatModel, testing::Combine(
ALL_DEVICES, ALL_DEVICES,
testing::Values(std::string("768x576.avi")), testing::Values(std::string("768x576.avi"))));
testing::Values(Channels(3), Channels(4))));
#endif #endif
......
...@@ -78,7 +78,7 @@ int main(int argc, const char** argv) ...@@ -78,7 +78,7 @@ int main(int argc, const char** argv)
Ptr<BackgroundSubtractor> mog = gpu::createBackgroundSubtractorMOG(); Ptr<BackgroundSubtractor> mog = gpu::createBackgroundSubtractorMOG();
Ptr<BackgroundSubtractor> mog2 = gpu::createBackgroundSubtractorMOG2(); Ptr<BackgroundSubtractor> mog2 = gpu::createBackgroundSubtractorMOG2();
Ptr<BackgroundSubtractor> gmg = gpu::createBackgroundSubtractorGMG(40); Ptr<BackgroundSubtractor> gmg = gpu::createBackgroundSubtractorGMG(40);
FGDStatModel fgd_stat; Ptr<BackgroundSubtractor> fgd = gpu::createBackgroundSubtractorFGD();
GpuMat d_fgmask; GpuMat d_fgmask;
GpuMat d_fgimg; GpuMat d_fgimg;
...@@ -103,7 +103,7 @@ int main(int argc, const char** argv) ...@@ -103,7 +103,7 @@ int main(int argc, const char** argv)
break; break;
case FGD_STAT: case FGD_STAT:
fgd_stat.create(d_frame); fgd->apply(d_frame, d_fgmask);
break; break;
} }
...@@ -142,9 +142,8 @@ int main(int argc, const char** argv) ...@@ -142,9 +142,8 @@ int main(int argc, const char** argv)
break; break;
case FGD_STAT: case FGD_STAT:
fgd_stat.update(d_frame); fgd->apply(d_frame, d_fgmask);
d_fgmask = fgd_stat.foreground; fgd->getBackgroundImage(d_bgimg);
d_bgimg = fgd_stat.background;
break; break;
} }
......
...@@ -1271,14 +1271,14 @@ TEST(FGDStatModel) ...@@ -1271,14 +1271,14 @@ TEST(FGDStatModel)
{ {
const std::string inputFile = abspath("768x576.avi"); const std::string inputFile = abspath("768x576.avi");
cv::VideoCapture cap(inputFile); VideoCapture cap(inputFile);
if (!cap.isOpened()) throw runtime_error("can't open 768x576.avi"); if (!cap.isOpened()) throw runtime_error("can't open 768x576.avi");
cv::Mat frame; Mat frame;
cap >> frame; cap >> frame;
IplImage ipl_frame = frame; IplImage ipl_frame = frame;
cv::Ptr<CvBGStatModel> model(cvCreateFGDStatModel(&ipl_frame)); Ptr<CvBGStatModel> model(cvCreateFGDStatModel(&ipl_frame));
while (!TestSystem::instance().stop()) while (!TestSystem::instance().stop())
{ {
...@@ -1297,8 +1297,10 @@ TEST(FGDStatModel) ...@@ -1297,8 +1297,10 @@ TEST(FGDStatModel)
cap >> frame; cap >> frame;
cv::gpu::GpuMat d_frame(frame); gpu::GpuMat d_frame(frame), d_fgmask;
cv::gpu::FGDStatModel d_model(d_frame); Ptr<BackgroundSubtractor> d_fgd = gpu::createBackgroundSubtractorFGD();
d_fgd->apply(d_frame, d_fgmask);
while (!TestSystem::instance().stop()) while (!TestSystem::instance().stop())
{ {
...@@ -1307,7 +1309,7 @@ TEST(FGDStatModel) ...@@ -1307,7 +1309,7 @@ TEST(FGDStatModel)
TestSystem::instance().gpuOn(); TestSystem::instance().gpuOn();
d_model.update(d_frame); d_fgd->apply(d_frame, d_fgmask);
TestSystem::instance().gpuOff(); TestSystem::instance().gpuOff();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment