Commit 42c1d4f4 authored by Vladislav Vinogradov's avatar Vladislav Vinogradov

new optimized version of BackgroundSubtractorGMG

parent 1995b1a0
...@@ -199,111 +199,20 @@ protected: ...@@ -199,111 +199,20 @@ protected:
*/ */
class CV_EXPORTS BackgroundSubtractorGMG: public cv::BackgroundSubtractor class CV_EXPORTS BackgroundSubtractorGMG: public cv::BackgroundSubtractor
{ {
protected:
/**
* Used internally to represent a single feature in a histogram.
* Feature is a color and an associated likelihood (weight in the histogram).
*/
struct CV_EXPORTS HistogramFeatureGMG
{
/**
* Default constructor.
* Initializes likelihood of feature to 0, color remains uninitialized.
*/
HistogramFeatureGMG(){likelihood = 0.0;}
/**
* Copy constructor.
* Required to use HistogramFeatureGMG in a std::vector
* @see operator =()
*/
HistogramFeatureGMG(const HistogramFeatureGMG& orig){
color = orig.color; likelihood = orig.likelihood;
}
/**
* Assignment operator.
* Required to use HistogramFeatureGMG in a std::vector
*/
HistogramFeatureGMG& operator =(const HistogramFeatureGMG& orig){
color = orig.color; likelihood = orig.likelihood; return *this;
}
/**
* Tests equality of histogram features.
* Equality is tested only by matching the color (feature), not the likelihood.
* This operator is used to look up an observed feature in a histogram.
*/
bool operator ==(HistogramFeatureGMG &rhs);
//! Regardless of the image datatype, it is quantized and mapped to an integer and represented as a vector.
vector<size_t> color;
//! Represents the weight of feature in the histogram.
float likelihood;
friend class PixelModelGMG;
};
/**
* Representation of the statistical model of a single pixel for use in the background subtraction
* algorithm.
*/
class CV_EXPORTS PixelModelGMG
{
public:
PixelModelGMG();
~PixelModelGMG();
/**
* Incorporate the last observed feature into the statistical model.
*
* @param learningRate The adaptation parameter for the histogram. -1.0 to use default. Value
* should be between 0.0 and 1.0, the higher the value, the faster the
* adaptation. 1.0 is limiting case where fast adaptation means no memory.
*/
void insertFeature(double learningRate = -1.0);
/**
* Set the feature last observed, to save before incorporating it into the statistical
* model with insertFeature().
*
* @param feature The feature (color) just observed.
*/
void setLastObservedFeature(BackgroundSubtractorGMG::HistogramFeatureGMG feature);
/**
* Set the upper limit for the number of features to store in the histogram. Use to adjust
* memory requirements.
*
* @param max size_t representing the max number of features.
*/
void setMaxFeatures(size_t max) {
maxFeatures = max; histogram.resize(max); histogram.clear();
}
/**
* Normalize the histogram, so sum of weights of all features = 1.0
*/
void normalizeHistogram();
/**
* Return the weight of a feature in the histogram. If the feature is not represented in the
* histogram, the weight returned is 0.0.
*/
double getLikelihood(HistogramFeatureGMG f);
PixelModelGMG& operator *=(const float &rhs);
//friend class BackgroundSubtractorGMG;
//friend class HistogramFeatureGMG;
private:
size_t numFeatures; //!< number of features in histogram
size_t maxFeatures; //!< max allowable features in histogram
std::list<HistogramFeatureGMG> histogram; //!< represents the histogram as a list of features
HistogramFeatureGMG lastObservedFeature;
//!< store last observed feature in case we need to add it to histogram
};
public: public:
BackgroundSubtractorGMG(); BackgroundSubtractorGMG();
virtual ~BackgroundSubtractorGMG(); virtual ~BackgroundSubtractorGMG();
virtual AlgorithmInfo* info() const; virtual AlgorithmInfo* info() const;
/**
* Validate parameters and set up data structures for appropriate image size.
* Must call before running on data.
* @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(cv::Size frameSize, double min, double max);
/** /**
* Performs single-frame background subtraction and builds up a statistical background image * Performs single-frame background subtraction and builds up a statistical background image
* model. * model.
...@@ -312,28 +221,6 @@ public: ...@@ -312,28 +221,6 @@ public:
*/ */
virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=-1.0); virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=-1.0);
/**
* Validate parameters and set up data structures for appropriate image type. Must call before
* running on data.
* @param image One sample image from dataset
* @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 initializeType(InputArray image, double min, double max);
/**
* Selectively update the background model. Only update background model for pixels identified
* as background.
* @param mask Mask image same size as images in sequence. Must be 8UC1 matrix, 255 for foreground
* and 0 for background.
*/
void updateBackgroundModel(InputArray mask);
/**
* Retrieve the greyscale image representing the probability that each pixel is foreground given
* the current estimated background model. Values are 0.0 (black) to 1.0 (white).
* @param img The 32FC1 image representing per-pixel probabilities that the pixel is foreground.
*/
void getPosteriorImage(OutputArray img);
protected: protected:
//! Total number of distinct colors to maintain in histogram. //! Total number of distinct colors to maintain in histogram.
int maxFeatures; int maxFeatures;
...@@ -345,31 +232,23 @@ protected: ...@@ -345,31 +232,23 @@ protected:
int quantizationLevels; int quantizationLevels;
//! Prior probability that any given pixel is a background pixel. A sensitivity parameter. //! Prior probability that any given pixel is a background pixel. A sensitivity parameter.
double backgroundPrior; double backgroundPrior;
//! value above which pixel is determined to be FG.
double decisionThreshold;
//! smoothing radius, in pixels, for cleaning up FG image.
int smoothingRadius;
double decisionThreshold; //!< value above which pixel is determined to be FG. private:
int smoothingRadius; //!< smoothing radius, in pixels, for cleaning up FG image. double maxVal_;
double minVal_;
double maxVal, minVal; cv::Size frameSize_;
size_t frameNum_;
/* cv::Mat_<int> nfeatures_;
* General Parameters cv::Mat_<int> colors_;
*/ cv::Mat_<float> weights_;
int imWidth; //!< width of image.
int imHeight; //!< height of image.
size_t numPixels;
unsigned int numChannels; //!< Number of channels in image. cv::Mat buf_;
bool isDataInitialized;
//!< After general parameters are set, data structures must be initialized.
/*
* Data Structures
*/
vector<PixelModelGMG> pixels; //!< Probabilistic background models for each pixel in image.
int frameNum; //!< Frame number counter, used to count frames in training mode.
Mat posteriorImage; //!< Posterior probability image.
Mat fgMaskImage; //!< Foreground mask image.
}; };
} }
......
This diff is collapsed.
...@@ -115,43 +115,43 @@ void CV_BackgroundSubtractorTest::run(int) ...@@ -115,43 +115,43 @@ void CV_BackgroundSubtractorTest::run(int)
{ {
rng.fill(simImage,RNG::UNIFORM,(unsigned char)(minuc/2+maxuc/2),maxuc); rng.fill(simImage,RNG::UNIFORM,(unsigned char)(minuc/2+maxuc/2),maxuc);
if (i == 0) if (i == 0)
fgbg->initializeType(simImage,minuc,maxuc); fgbg->initialize(simImage.size(),minuc,maxuc);
} }
else if (type == CV_8S) else if (type == CV_8S)
{ {
rng.fill(simImage,RNG::UNIFORM,(char)(minc/2+maxc/2),maxc); rng.fill(simImage,RNG::UNIFORM,(char)(minc/2+maxc/2),maxc);
if (i==0) if (i==0)
fgbg->initializeType(simImage,minc,maxc); fgbg->initialize(simImage.size(),minc,maxc);
} }
else if (type == CV_16U) else if (type == CV_16U)
{ {
rng.fill(simImage,RNG::UNIFORM,(unsigned int)(minui/2+maxui/2),maxui); rng.fill(simImage,RNG::UNIFORM,(unsigned int)(minui/2+maxui/2),maxui);
if (i==0) if (i==0)
fgbg->initializeType(simImage,minui,maxui); fgbg->initialize(simImage.size(),minui,maxui);
} }
else if (type == CV_16S) else if (type == CV_16S)
{ {
rng.fill(simImage,RNG::UNIFORM,(int)(mini/2+maxi/2),maxi); rng.fill(simImage,RNG::UNIFORM,(int)(mini/2+maxi/2),maxi);
if (i==0) if (i==0)
fgbg->initializeType(simImage,mini,maxi); fgbg->initialize(simImage.size(),mini,maxi);
} }
else if (type == CV_32F) else if (type == CV_32F)
{ {
rng.fill(simImage,RNG::UNIFORM,(float)(minf/2.0+maxf/2.0),maxf); rng.fill(simImage,RNG::UNIFORM,(float)(minf/2.0+maxf/2.0),maxf);
if (i==0) if (i==0)
fgbg->initializeType(simImage,minf,maxf); fgbg->initialize(simImage.size(),minf,maxf);
} }
else if (type == CV_32S) else if (type == CV_32S)
{ {
rng.fill(simImage,RNG::UNIFORM,(long int)(minli/2+maxli/2),maxli); rng.fill(simImage,RNG::UNIFORM,(long int)(minli/2+maxli/2),maxli);
if (i==0) if (i==0)
fgbg->initializeType(simImage,minli,maxli); fgbg->initialize(simImage.size(),minli,maxli);
} }
else if (type == CV_64F) else if (type == CV_64F)
{ {
rng.fill(simImage,RNG::UNIFORM,(double)(mind/2.0+maxd/2.0),maxd); rng.fill(simImage,RNG::UNIFORM,(double)(mind/2.0+maxd/2.0),maxd);
if (i==0) if (i==0)
fgbg->initializeType(simImage,mind,maxd); fgbg->initialize(simImage.size(),mind,maxd);
} }
/** /**
...@@ -159,7 +159,6 @@ void CV_BackgroundSubtractorTest::run(int) ...@@ -159,7 +159,6 @@ void CV_BackgroundSubtractorTest::run(int)
*/ */
(*fgbg)(simImage,fgmask); (*fgbg)(simImage,fgmask);
Mat fullbg = Mat::zeros(simImage.rows, simImage.cols, CV_8U); Mat fullbg = Mat::zeros(simImage.rows, simImage.cols, CV_8U);
fgbg->updateBackgroundModel(fullbg);
//! fgmask should be entirely background during training //! fgmask should be entirely background during training
code = cvtest::cmpEps2( ts, fgmask, fullbg, 0, false, "The training foreground mask" ); code = cvtest::cmpEps2( ts, fgmask, fullbg, 0, false, "The training foreground mask" );
......
...@@ -7,91 +7,76 @@ ...@@ -7,91 +7,76 @@
#include <opencv2/opencv.hpp> #include <opencv2/opencv.hpp>
#include <iostream> #include <iostream>
#include <sstream>
using namespace cv; using namespace cv;
static void help() static void help()
{ {
std::cout << std::cout <<
"\nA program demonstrating the use and capabilities of a particular BackgroundSubtraction\n" "\nA program demonstrating the use and capabilities of a particular BackgroundSubtraction\n"
"algorithm described in A. Godbehere, A. Matsukawa, K. Goldberg, \n" "algorithm described in A. Godbehere, A. Matsukawa, K. Goldberg, \n"
"\"Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive\n" "\"Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive\n"
"Audio Art Installation\", American Control Conference, 2012, used in an interactive\n" "Audio Art Installation\", American Control Conference, 2012, used in an interactive\n"
"installation at the Contemporary Jewish Museum in San Francisco, CA from March 31 through\n" "installation at the Contemporary Jewish Museum in San Francisco, CA from March 31 through\n"
"July 31, 2011.\n" "July 31, 2011.\n"
"Call:\n" "Call:\n"
"./BackgroundSubtractorGMG_sample\n" "./BackgroundSubtractorGMG_sample\n"
"Using OpenCV version " << CV_VERSION << "\n"<<std::endl; "Using OpenCV version " << CV_VERSION << "\n"<<std::endl;
} }
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
help(); help();
setUseOptimized(true);
setNumThreads(8); initModule_video();
setUseOptimized(true);
Ptr<BackgroundSubtractorGMG> fgbg = Algorithm::create<BackgroundSubtractorGMG>("BackgroundSubtractor.GMG"); setNumThreads(8);
if (fgbg == NULL)
{ Ptr<BackgroundSubtractorGMG> fgbg = Algorithm::create<BackgroundSubtractorGMG>("BackgroundSubtractor.GMG");
CV_Error(CV_StsError,"Failed to create Algorithm\n"); if (fgbg.empty())
} {
fgbg->set("smoothingRadius",7); std::cerr << "Failed to create BackgroundSubtractor.GMG Algorithm." << std::endl;
fgbg->set("decisionThreshold",0.7); return -1;
}
VideoCapture cap;
if( argc > 1 ) fgbg->set("initializationFrames", 20);
fgbg->set("decisionThreshold", 0.7);
VideoCapture cap;
if (argc > 1)
cap.open(argv[1]); cap.open(argv[1]);
else else
cap.open(0); cap.open(0);
if (!cap.isOpened()) if (!cap.isOpened())
{ {
std::cout << "error: cannot read video. Try moving video file to sample directory.\n"; std::cerr << "Cannot read video. Try moving video file to sample directory." << std::endl;
return -1; return -1;
} }
Mat img, downimg, downimg2, fgmask, upfgmask, posterior, upposterior; Mat frame, fgmask, segm;
bool first = true; namedWindow("FG Segmentation", WINDOW_NORMAL);
namedWindow("posterior");
namedWindow("fgmask"); for (;;)
namedWindow("FG Segmentation"); {
int i = 0; cap >> frame;
for (;;)
{ if (frame.empty())
std::stringstream txt; break;
txt << "frame: ";
txt << i++; (*fgbg)(frame, fgmask);
cap >> img; frame.copyTo(segm);
putText(img,txt.str(),Point(20,40),FONT_HERSHEY_SIMPLEX,0.8,Scalar(1.0,0.0,0.0)); add(frame, Scalar(100, 100, 0), segm, fgmask);
resize(img,downimg,Size(160,120),0,0,INTER_NEAREST); // Size(cols, rows) or Size(width,height) imshow("FG Segmentation", segm);
if (first)
{
fgbg->initializeType(downimg,0,255);
first = false;
}
if (img.empty())
{
return 0;
}
(*fgbg)(downimg,fgmask);
fgbg->updateBackgroundModel(Mat::zeros(120,160,CV_8U));
fgbg->getPosteriorImage(posterior);
resize(fgmask,upfgmask,Size(640,480),0,0,INTER_NEAREST);
Mat coloredFG = Mat::zeros(480,640,CV_8UC3);
coloredFG.setTo(Scalar(100,100,0),upfgmask);
resize(posterior,upposterior,Size(640,480),0,0,INTER_NEAREST);
imshow("posterior",upposterior);
imshow("fgmask",upfgmask);
resize(img, downimg2, Size(640, 480),0,0,INTER_LINEAR);
imshow("FG Segmentation",downimg2 + coloredFG);
int c = waitKey(30); int c = waitKey(30);
if( c == 'q' || c == 'Q' || (c & 255) == 27 ) if (c == 'q' || c == 'Q' || (c & 255) == 27)
break; break;
} }
return 0;
} }
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