Fixed windows build problems of BackgroundSubtractorGMG but code still need more work.

This commit is contained in:
Andrey Kamaev 2012-06-28 20:42:26 +00:00
parent 82cb2ab556
commit a25c27ca05
3 changed files with 559 additions and 561 deletions

View File

@ -50,7 +50,7 @@ namespace cv
/*! /*!
The Base Class for Background/Foreground Segmentation The Base Class for Background/Foreground Segmentation
The class is only used to define the common interface for The class is only used to define the common interface for
the whole family of background/foreground segmentation algorithms. the whole family of background/foreground segmentation algorithms.
*/ */
@ -70,13 +70,13 @@ public:
/*! /*!
Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm
The class implements the following algorithm: The class implements the following algorithm:
"An improved adaptive background mixture model for real-time tracking with shadow detection" "An improved adaptive background mixture model for real-time tracking with shadow detection"
P. KadewTraKuPong and R. Bowden, P. KadewTraKuPong and R. Bowden,
Proc. 2nd European Workshp on Advanced Video-Based Surveillance Systems, 2001." Proc. 2nd European Workshp on Advanced Video-Based Surveillance Systems, 2001."
http://personal.ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/avbs01.pdf http://personal.ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/avbs01.pdf
*/ */
class CV_EXPORTS_W BackgroundSubtractorMOG : public BackgroundSubtractor class CV_EXPORTS_W BackgroundSubtractorMOG : public BackgroundSubtractor
{ {
@ -89,13 +89,13 @@ public:
virtual ~BackgroundSubtractorMOG(); virtual ~BackgroundSubtractorMOG();
//! the update operator //! the update operator
virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=0); virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=0);
//! re-initiaization method //! re-initiaization method
virtual void initialize(Size frameSize, int frameType); virtual void initialize(Size frameSize, int frameType);
virtual AlgorithmInfo* info() const; virtual AlgorithmInfo* info() const;
protected: protected:
Size frameSize; Size frameSize;
int frameType; int frameType;
Mat bgmodel; Mat bgmodel;
@ -105,7 +105,7 @@ protected:
double varThreshold; double varThreshold;
double backgroundRatio; double backgroundRatio;
double noiseSigma; double noiseSigma;
}; };
/*! /*!
@ -126,16 +126,16 @@ public:
virtual ~BackgroundSubtractorMOG2(); virtual ~BackgroundSubtractorMOG2();
//! the update operator //! the update operator
virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=-1); virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=-1);
//! computes a background image which are the mean of all background gaussians //! computes a background image which are the mean of all background gaussians
virtual void getBackgroundImage(OutputArray backgroundImage) const; virtual void getBackgroundImage(OutputArray backgroundImage) const;
//! re-initiaization method //! re-initiaization method
virtual void initialize(Size frameSize, int frameType); virtual void initialize(Size frameSize, int frameType);
virtual AlgorithmInfo* info() const; virtual AlgorithmInfo* info() const;
protected: protected:
Size frameSize; Size frameSize;
int frameType; int frameType;
Mat bgmodel; Mat bgmodel;
@ -150,7 +150,7 @@ protected:
// by the background model or not. Related to Cthr from the paper. // 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 // 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. // and that is varThreshold=4*4=16; Corresponds to Tb in the paper.
///////////////////////// /////////////////////////
// less important parameters - things you might change but be carefull // less important parameters - things you might change but be carefull
//////////////////////// ////////////////////////
@ -179,7 +179,7 @@ protected:
//this is related to the number of samples needed to accept that a component //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 //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) //the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
//shadow detection parameters //shadow detection parameters
bool bShadowDetection;//default 1 - do shadow detection bool bShadowDetection;//default 1 - do shadow detection
unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value
@ -188,7 +188,7 @@ protected:
//version of the background. Tau is a threshold on how much darker the shadow can be. //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 //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. //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
}; };
/** /**
* Background Subtractor module. Takes a series of images and returns a sequence of mask (8UC1) * Background Subtractor module. Takes a series of images and returns a sequence of mask (8UC1)
@ -200,252 +200,250 @@ protected:
class CV_EXPORTS BackgroundSubtractorGMG: public cv::BackgroundSubtractor class CV_EXPORTS BackgroundSubtractorGMG: public cv::BackgroundSubtractor
{ {
private: private:
/** /**
* A general flexible datatype. * A general flexible datatype.
* *
* Used internally to enable background subtraction algorithm to be robust to any input Mat type. * Used internally to enable background subtraction algorithm to be robust to any input Mat type.
* Datatype can be char, unsigned char, int, unsigned int, long int, float, or double. * Datatype can be char, unsigned char, int, unsigned int, long int, float, or double.
*/ */
union flexitype{ union flexitype{
char c; char c;
uchar uc; uchar uc;
int i; int i;
unsigned int ui; unsigned int ui;
long int li; long int li;
float f; float f;
double d; double d;
flexitype(){d = 0.0;} //!< Default constructor, set all bits of the union to 0. flexitype(){d = 0.0;} //!< Default constructor, set all bits of the union to 0.
flexitype(char cval){c = cval;} //!< Char type constructor flexitype(char cval){c = cval;} //!< Char type constructor
bool operator ==(flexitype& rhs) bool operator ==(flexitype& rhs)
{ {
return d == rhs.d; return d == rhs.d;
} }
//! Char type assignment operator //! Char type assignment operator
flexitype& operator =(char cval){ flexitype& operator =(char cval){
if (this->c == cval){return *this;} if (this->c == cval){return *this;}
c = cval; return *this; c = cval; return *this;
} }
flexitype(unsigned char ucval){uc = ucval;} //!< unsigned char type constructor flexitype(unsigned char ucval){uc = ucval;} //!< unsigned char type constructor
//! unsigned char type assignment operator //! unsigned char type assignment operator
flexitype& operator =(unsigned char ucval){ flexitype& operator =(unsigned char ucval){
if (this->uc == ucval){return *this;} if (this->uc == ucval){return *this;}
uc = ucval; return *this; uc = ucval; return *this;
} }
flexitype(int ival){i = ival;} //!< int type constructor flexitype(int ival){i = ival;} //!< int type constructor
//! int type assignment operator //! int type assignment operator
flexitype& operator =(int ival){ flexitype& operator =(int ival){
if (this->i == ival){return *this;} if (this->i == ival){return *this;}
i = ival; return *this; i = ival; return *this;
} }
flexitype(unsigned int uival){ui = uival;} //!< unsigned int type constructor flexitype(unsigned int uival){ui = uival;} //!< unsigned int type constructor
//! unsigned int type assignment operator //! unsigned int type assignment operator
flexitype& operator =(unsigned int uival){ flexitype& operator =(unsigned int uival){
if (this->ui == uival){return *this;} if (this->ui == uival){return *this;}
ui = uival; return *this; ui = uival; return *this;
} }
flexitype(float fval){f = fval;} //!< float type constructor flexitype(float fval){f = fval;} //!< float type constructor
//! float type assignment operator //! float type assignment operator
flexitype& operator =(float fval){ flexitype& operator =(float fval){
if (this->f == fval){return *this;} if (this->f == fval){return *this;}
f = fval; return *this; f = fval; return *this;
} }
flexitype(long int lival){li = lival;} //!< long int type constructor flexitype(long int lival){li = lival;} //!< long int type constructor
//! long int type assignment operator //! long int type assignment operator
flexitype& operator =(long int lival){ flexitype& operator =(long int lival){
if (this->li == lival){return *this;} if (this->li == lival){return *this;}
li = lival; return *this; li = lival; return *this;
} }
flexitype(double dval){d=dval;} //!< double type constructor flexitype(double dval){d=dval;} //!< double type constructor
//! double type assignment operator //! double type assignment operator
flexitype& operator =(double dval){ flexitype& operator =(double dval){
if (this->d == dval){return *this;} if (this->d == dval){return *this;}
d = dval; return *this; d = dval; return *this;
} }
}; };
/** /**
* Used internally to represent a single feature in a histogram. * Used internally to represent a single feature in a histogram.
* Feature is a color and an associated likelihood (weight in the histogram). * Feature is a color and an associated likelihood (weight in the histogram).
*/ */
struct HistogramFeatureGMG struct CV_EXPORTS HistogramFeatureGMG
{ {
/** /**
* Default constructor. * Default constructor.
* Initializes likelihood of feature to 0, color remains uninitialized. * Initializes likelihood of feature to 0, color remains uninitialized.
*/ */
HistogramFeatureGMG(){likelihood = 0.0;} HistogramFeatureGMG(){likelihood = 0.0;}
/** /**
* Copy constructor. * Copy constructor.
* Required to use HistogramFeatureGMG in a std::vector * Required to use HistogramFeatureGMG in a std::vector
* @see operator =() * @see operator =()
*/ */
HistogramFeatureGMG(const HistogramFeatureGMG& orig){ HistogramFeatureGMG(const HistogramFeatureGMG& orig){
color = orig.color; likelihood = orig.likelihood; color = orig.color; likelihood = orig.likelihood;
} }
/** /**
* Assignment operator. * Assignment operator.
* Required to use HistogramFeatureGMG in a std::vector * Required to use HistogramFeatureGMG in a std::vector
*/ */
HistogramFeatureGMG& operator =(const HistogramFeatureGMG& orig){ HistogramFeatureGMG& operator =(const HistogramFeatureGMG& orig){
color = orig.color; likelihood = orig.likelihood; return *this; color = orig.color; likelihood = orig.likelihood; return *this;
} }
/** /**
* Tests equality of histogram features. * Tests equality of histogram features.
* Equality is tested only by matching the color (feature), not the likelihood. * 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. * This operator is used to look up an observed feature in a histogram.
*/ */
bool operator ==(HistogramFeatureGMG &rhs); bool operator ==(HistogramFeatureGMG &rhs);
//! Regardless of the image datatype, it is quantized and mapped to an integer and represented as a vector. //! Regardless of the image datatype, it is quantized and mapped to an integer and represented as a vector.
vector<size_t> color; vector<size_t> color;
//! Represents the weight of feature in the histogram. //! Represents the weight of feature in the histogram.
float likelihood; float likelihood;
friend class PixelModelGMG; friend class PixelModelGMG;
}; };
/** /**
* Representation of the statistical model of a single pixel for use in the background subtraction * Representation of the statistical model of a single pixel for use in the background subtraction
* algorithm. * algorithm.
*/ */
class PixelModelGMG class CV_EXPORTS PixelModelGMG
{ {
public: public:
PixelModelGMG(); PixelModelGMG();
virtual ~PixelModelGMG(); ~PixelModelGMG();
/** /**
* Incorporate the last observed feature into the statistical model. * Incorporate the last observed feature into the statistical model.
* *
* @param learningRate The adaptation parameter for the histogram. -1.0 to use default. Value * @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 * 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. * adaptation. 1.0 is limiting case where fast adaptation means no memory.
*/ */
void insertFeature(double learningRate = -1.0); void insertFeature(double learningRate = -1.0);
/** /**
* Set the feature last observed, to save before incorporating it into the statistical * Set the feature last observed, to save before incorporating it into the statistical
* model with insertFeature(). * model with insertFeature().
* *
* @param feature The feature (color) just observed. * @param feature The feature (color) just observed.
*/ */
void setLastObservedFeature(BackgroundSubtractorGMG::HistogramFeatureGMG feature); void setLastObservedFeature(BackgroundSubtractorGMG::HistogramFeatureGMG feature);
/** /**
* Set the upper limit for the number of features to store in the histogram. Use to adjust * Set the upper limit for the number of features to store in the histogram. Use to adjust
* memory requirements. * memory requirements.
* *
* @param max size_t representing the max number of features. * @param max size_t representing the max number of features.
*/ */
void setMaxFeatures(size_t max) { void setMaxFeatures(size_t max) {
maxFeatures = max; histogram.resize(max); histogram.clear(); maxFeatures = max; histogram.resize(max); histogram.clear();
} }
/** /**
* Normalize the histogram, so sum of weights of all features = 1.0 * Normalize the histogram, so sum of weights of all features = 1.0
*/ */
void normalizeHistogram(); void normalizeHistogram();
/** /**
* Return the weight of a feature in the histogram. If the feature is not represented in the * Return the weight of a feature in the histogram. If the feature is not represented in the
* histogram, the weight returned is 0.0. * histogram, the weight returned is 0.0.
*/ */
double getLikelihood(HistogramFeatureGMG f); double getLikelihood(HistogramFeatureGMG f);
PixelModelGMG& operator *=(const float &rhs); PixelModelGMG& operator *=(const float &rhs);
//friend class BackgroundSubtractorGMG; //friend class BackgroundSubtractorGMG;
//friend class HistogramFeatureGMG; //friend class HistogramFeatureGMG;
protected: private:
size_t numFeatures; //!< number of features in histogram size_t numFeatures; //!< number of features in histogram
size_t maxFeatures; //!< max allowable features in histogram size_t maxFeatures; //!< max allowable features in histogram
std::list<HistogramFeatureGMG> histogram; //!< represents the histogram as a list of features std::list<HistogramFeatureGMG> histogram; //!< represents the histogram as a list of features
HistogramFeatureGMG lastObservedFeature; HistogramFeatureGMG lastObservedFeature;
//!< store last observed feature in case we need to add it to histogram //!< 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;
/** /**
* 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.
* @param image Input image * @param image Input image
* @param fgmask Output mask image representing foreground and background pixels * @param fgmask Output mask image representing foreground and background pixels
*/ */
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 * Validate parameters and set up data structures for appropriate image type. Must call before
* running on data. * running on data.
* @param image One sample image from dataset * @param image One sample image from dataset
* @param min minimum value taken on by pixels in image sequence. Usually 0 * @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 * @param max maximum value taken on by pixels in image sequence. e.g. 1.0 or 255
*/ */
void initializeType(InputArray image, flexitype min, flexitype max); void initializeType(InputArray image, flexitype min, flexitype max);
/** /**
* Selectively update the background model. Only update background model for pixels identified * Selectively update the background model. Only update background model for pixels identified
* as background. * as background.
* @param mask Mask image same size as images in sequence. Must be 8UC1 matrix, 255 for foreground * @param mask Mask image same size as images in sequence. Must be 8UC1 matrix, 255 for foreground
* and 0 for background. * and 0 for background.
*/ */
void updateBackgroundModel(InputArray mask); void updateBackgroundModel(InputArray mask);
/** /**
* Retrieve the greyscale image representing the probability that each pixel is foreground given * 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). * 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. * @param img The 32FC1 image representing per-pixel probabilities that the pixel is foreground.
*/ */
void getPosteriorImage(OutputArray img); 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;
//! Set between 0.0 and 1.0, determines how quickly features are "forgotten" from histograms. //! Set between 0.0 and 1.0, determines how quickly features are "forgotten" from histograms.
double learningRate; double learningRate;
//! Number of frames of video to use to initialize histograms. //! Number of frames of video to use to initialize histograms.
int numInitializationFrames; int numInitializationFrames;
//! Number of discrete levels in each channel to be used in histograms. //! Number of discrete levels in each channel to be used in histograms.
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;
double decisionThreshold; //!< value above which pixel is determined to be FG. double decisionThreshold; //!< value above which pixel is determined to be FG.
int smoothingRadius; //!< smoothing radius, in pixels, for cleaning up FG image. int smoothingRadius; //!< smoothing radius, in pixels, for cleaning up FG image.
flexitype maxVal, minVal; flexitype maxVal, minVal;
/* /*
* General Parameters * General Parameters
*/ */
size_t imWidth; //!< width of image. size_t imWidth; //!< width of image.
size_t imHeight; //!< height of image. size_t imHeight; //!< height of image.
size_t numPixels; size_t numPixels;
int imageDepth; //!< Depth of image, e.g. CV_8U int imageDepth; //!< Depth of image, e.g. CV_8U
unsigned int numChannels; //!< Number of channels in image. unsigned int numChannels; //!< Number of channels in image.
bool isDataInitialized; bool isDataInitialized;
//!< After general parameters are set, data structures must be initialized. //!< After general parameters are set, data structures must be initialized.
size_t elemSize; //!< store image mat element sizes size_t elemSize; //!< store image mat element sizes
size_t elemSize1; size_t elemSize1;
/* /*
* Data Structures * Data Structures
*/ */
vector<PixelModelGMG> pixels; //!< Probabilistic background models for each pixel in image. vector<PixelModelGMG> pixels; //!< Probabilistic background models for each pixel in image.
int frameNum; //!< Frame number counter, used to count frames in training mode. int frameNum; //!< Frame number counter, used to count frames in training mode.
Mat posteriorImage; //!< Posterior probability image. Mat posteriorImage; //!< Posterior probability image.
Mat fgMaskImage; //!< Foreground mask image. Mat fgMaskImage; //!< Foreground mask image.
}; };
bool initModule_BackgroundSubtractorGMG(void);
} }
#endif #endif

View File

@ -7,7 +7,7 @@
// copy or use the software. // copy or use the software.
// //
// //
// License Agreement // License Agreement
// //
// Copyright (C) 2000, Intel Corporation, all rights reserved. // Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners. // Third party copyrights are property of their respective owners.
@ -66,245 +66,245 @@ BackgroundSubtractorGMG::BackgroundSubtractorGMG()
decisionThreshold = 0.8; decisionThreshold = 0.8;
smoothingRadius = 7; smoothingRadius = 7;
} }
void BackgroundSubtractorGMG::initializeType(InputArray _image,flexitype min, flexitype max) void BackgroundSubtractorGMG::initializeType(InputArray _image,flexitype min, flexitype max)
{ {
minVal = min; minVal = min;
maxVal = max; maxVal = max;
if (minVal == maxVal) if (minVal == maxVal)
{ {
CV_Error_(CV_StsBadArg,("minVal and maxVal cannot be the same.")); CV_Error_(CV_StsBadArg,("minVal and maxVal cannot be the same."));
} }
/* /*
* Parameter validation * Parameter validation
*/ */
if (maxFeatures <= 0) if (maxFeatures <= 0)
{ {
CV_Error_(CV_StsBadArg, CV_Error_(CV_StsBadArg,
("maxFeatures parameter must be 1 or greater. Instead, it is %d.",maxFeatures)); ("maxFeatures parameter must be 1 or greater. Instead, it is %d.",maxFeatures));
} }
if (learningRate < 0.0 || learningRate > 1.0) if (learningRate < 0.0 || learningRate > 1.0)
{ {
CV_Error_(CV_StsBadArg, CV_Error_(CV_StsBadArg,
("learningRate parameter must be in the range [0.0,1.0]. Instead, it is %f.", ("learningRate parameter must be in the range [0.0,1.0]. Instead, it is %f.",
learningRate)); learningRate));
} }
if (numInitializationFrames < 1) if (numInitializationFrames < 1)
{ {
CV_Error_(CV_StsBadArg, CV_Error_(CV_StsBadArg,
("numInitializationFrames must be at least 1. Instead, it is %d.", ("numInitializationFrames must be at least 1. Instead, it is %d.",
numInitializationFrames)); numInitializationFrames));
} }
if (quantizationLevels < 1) if (quantizationLevels < 1)
{ {
CV_Error_(CV_StsBadArg, CV_Error_(CV_StsBadArg,
("quantizationLevels must be at least 1 (preferably more). Instead it is %d.", ("quantizationLevels must be at least 1 (preferably more). Instead it is %d.",
quantizationLevels)); quantizationLevels));
} }
if (backgroundPrior < 0.0 || backgroundPrior > 1.0) if (backgroundPrior < 0.0 || backgroundPrior > 1.0)
{ {
CV_Error_(CV_StsBadArg, CV_Error_(CV_StsBadArg,
("backgroundPrior must be a probability, between 0.0 and 1.0. Instead it is %f.", ("backgroundPrior must be a probability, between 0.0 and 1.0. Instead it is %f.",
backgroundPrior)); backgroundPrior));
} }
/* /*
* Detect and accommodate the image depth * Detect and accommodate the image depth
*/ */
Mat image = _image.getMat(); Mat image = _image.getMat();
imageDepth = image.depth(); // 32f, 8u, etc. imageDepth = image.depth(); // 32f, 8u, etc.
numChannels = image.channels(); numChannels = image.channels();
/* /*
* Color quantization [0 | | | | max] --> [0 | | max] * Color quantization [0 | | | | max] --> [0 | | max]
* (0) Use double as intermediary to convert all types to int. * (0) Use double as intermediary to convert all types to int.
* (i) Shift min to 0, * (i) Shift min to 0,
* (ii) max/(num intervals) = factor. x/factor * factor = quantized result, after integer operation. * (ii) max/(num intervals) = factor. x/factor * factor = quantized result, after integer operation.
*/ */
/* /*
* Data Structure Initialization * Data Structure Initialization
*/ */
Size imsize = image.size(); Size imsize = image.size();
imWidth = imsize.width; imWidth = imsize.width;
imHeight = imsize.height; imHeight = imsize.height;
numPixels = imWidth*imHeight; numPixels = imWidth*imHeight;
pixels.resize(numPixels); pixels.resize(numPixels);
frameNum = 0; frameNum = 0;
// used to iterate through matrix of type unknown at compile time // used to iterate through matrix of type unknown at compile time
elemSize = image.elemSize(); elemSize = image.elemSize();
elemSize1 = image.elemSize1(); elemSize1 = image.elemSize1();
vector<PixelModelGMG>::iterator pixel; vector<PixelModelGMG>::iterator pixel;
vector<PixelModelGMG>::iterator pixel_end = pixels.end(); vector<PixelModelGMG>::iterator pixel_end = pixels.end();
for (pixel = pixels.begin(); pixel != pixel_end; ++pixel) for (pixel = pixels.begin(); pixel != pixel_end; ++pixel)
{ {
pixel->setMaxFeatures(maxFeatures); pixel->setMaxFeatures(maxFeatures);
} }
fgMaskImage = Mat::zeros(imHeight,imWidth,CV_8UC1); // 8-bit unsigned mask. 255 for FG, 0 for BG fgMaskImage = Mat::zeros(imHeight,imWidth,CV_8UC1); // 8-bit unsigned mask. 255 for FG, 0 for BG
posteriorImage = Mat::zeros(imHeight,imWidth,CV_32FC1); // float for storing probabilities. Can be viewed directly with imshow. posteriorImage = Mat::zeros(imHeight,imWidth,CV_32FC1); // float for storing probabilities. Can be viewed directly with imshow.
isDataInitialized = true; isDataInitialized = true;
} }
void BackgroundSubtractorGMG::operator()(InputArray _image, OutputArray _fgmask, double newLearningRate) void BackgroundSubtractorGMG::operator()(InputArray _image, OutputArray _fgmask, double newLearningRate)
{ {
if (!isDataInitialized) if (!isDataInitialized)
{ {
CV_Error(CV_StsError,"BackgroundSubstractorGMG has not been initialized. Call initialize() first.\n"); CV_Error(CV_StsError,"BackgroundSubstractorGMG has not been initialized. Call initialize() first.\n");
} }
/* /*
* Update learning rate parameter, if desired * Update learning rate parameter, if desired
*/ */
if (newLearningRate != -1.0) if (newLearningRate != -1.0)
{ {
if (newLearningRate < 0.0 || newLearningRate > 1.0) if (newLearningRate < 0.0 || newLearningRate > 1.0)
{ {
CV_Error(CV_StsOutOfRange,"Learning rate for Operator () must be between 0.0 and 1.0.\n"); CV_Error(CV_StsOutOfRange,"Learning rate for Operator () must be between 0.0 and 1.0.\n");
} }
this->learningRate = newLearningRate; this->learningRate = newLearningRate;
} }
Mat image = _image.getMat(); Mat image = _image.getMat();
_fgmask.create(Size(imHeight,imWidth),CV_8U); _fgmask.create(Size(imHeight,imWidth),CV_8U);
fgMaskImage = _fgmask.getMat(); // 8-bit unsigned mask. 255 for FG, 0 for BG fgMaskImage = _fgmask.getMat(); // 8-bit unsigned mask. 255 for FG, 0 for BG
/* /*
* Iterate over pixels in image * Iterate over pixels in image
*/ */
// grab data at each pixel (1,2,3 channels, int, float, etc.) // grab data at each pixel (1,2,3 channels, int, float, etc.)
// grab data as an array of bytes. Then, send that array to a function that reads data into vector of appropriate types... and quantizing... before saving as a feature, which is a vector of flexitypes, so code can be portable. // grab data as an array of bytes. Then, send that array to a function that reads data into vector of appropriate types... and quantizing... before saving as a feature, which is a vector of flexitypes, so code can be portable.
// multiple channels do have sequential storage, use mat::elemSize() and mat::elemSize1() // multiple channels do have sequential storage, use mat::elemSize() and mat::elemSize1()
vector<PixelModelGMG>::iterator pixel; vector<PixelModelGMG>::iterator pixel;
vector<PixelModelGMG>::iterator pixel_end = pixels.end(); vector<PixelModelGMG>::iterator pixel_end = pixels.end();
size_t i; size_t i;
//#pragma omp parallel //#pragma omp parallel
for (i = 0, pixel=pixels.begin(); pixel != pixel_end; ++i,++pixel) for (i = 0, pixel=pixels.begin(); pixel != pixel_end; ++i,++pixel)
{ {
HistogramFeatureGMG newFeature; HistogramFeatureGMG newFeature;
newFeature.color.clear(); newFeature.color.clear();
for (size_t c = 0; c < numChannels; ++c) for (size_t c = 0; c < numChannels; ++c)
{ {
/* /*
* Perform quantization. in each channel. (color-min)*(levels)/(max-min). * Perform quantization. in each channel. (color-min)*(levels)/(max-min).
* Shifts min to 0 and scales, finally casting to an int. * Shifts min to 0 and scales, finally casting to an int.
*/ */
size_t quantizedColor; size_t quantizedColor;
// pixel at data+elemSize*i. Individual channel c at data+elemSize*i+elemSize1*c // pixel at data+elemSize*i. Individual channel c at data+elemSize*i+elemSize1*c
if (imageDepth == CV_8U) if (imageDepth == CV_8U)
{ {
uchar *color = (uchar*)(image.data+elemSize*i+elemSize1*c); uchar *color = (uchar*)(image.data+elemSize*i+elemSize1*c);
quantizedColor = (size_t)((double)(*color-minVal.uc)*quantizationLevels/(maxVal.uc-minVal.uc)); quantizedColor = (size_t)((double)(*color-minVal.uc)*quantizationLevels/(maxVal.uc-minVal.uc));
} }
else if (imageDepth == CV_8S) else if (imageDepth == CV_8S)
{ {
char *color = (char*)(image.data+elemSize*i+elemSize1*c); char *color = (char*)(image.data+elemSize*i+elemSize1*c);
quantizedColor = (size_t)((double)(*color-minVal.c)*quantizationLevels/(maxVal.c-minVal.c)); quantizedColor = (size_t)((double)(*color-minVal.c)*quantizationLevels/(maxVal.c-minVal.c));
} }
else if (imageDepth == CV_16U) else if (imageDepth == CV_16U)
{ {
unsigned int *color = (unsigned int*)(image.data+elemSize*i+elemSize1*c); unsigned int *color = (unsigned int*)(image.data+elemSize*i+elemSize1*c);
quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui)); quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui));
} }
else if (imageDepth == CV_16S) else if (imageDepth == CV_16S)
{ {
int *color = (int*)(image.data+elemSize*i+elemSize1*c); int *color = (int*)(image.data+elemSize*i+elemSize1*c);
quantizedColor = (size_t)((double)(*color-minVal.i)*quantizationLevels/(maxVal.i-minVal.i)); quantizedColor = (size_t)((double)(*color-minVal.i)*quantizationLevels/(maxVal.i-minVal.i));
} }
else if (imageDepth == CV_32F) else if (imageDepth == CV_32F)
{ {
float *color = (float*)image.data+elemSize*i+elemSize1*c; float *color = (float*)image.data+elemSize*i+elemSize1*c;
quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui)); quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui));
} }
else if (imageDepth == CV_32S) else if (imageDepth == CV_32S)
{ {
long int *color = (long int*)(image.data+elemSize*i+elemSize1*c); long int *color = (long int*)(image.data+elemSize*i+elemSize1*c);
quantizedColor = (size_t)((double)(*color-minVal.li)*quantizationLevels/(maxVal.li-minVal.li)); quantizedColor = (size_t)((double)(*color-minVal.li)*quantizationLevels/(maxVal.li-minVal.li));
} }
else if (imageDepth == CV_64F) else if (imageDepth == CV_64F)
{ {
double *color = (double*)image.data+elemSize*i+elemSize1*c; double *color = (double*)image.data+elemSize*i+elemSize1*c;
quantizedColor = (size_t)((double)(*color-minVal.d)*quantizationLevels/(maxVal.d-minVal.d)); quantizedColor = (size_t)((double)(*color-minVal.d)*quantizationLevels/(maxVal.d-minVal.d));
} }
newFeature.color.push_back(quantizedColor); newFeature.color.push_back(quantizedColor);
} }
// now that the feature is ready for use, put it in the histogram // now that the feature is ready for use, put it in the histogram
if (frameNum > numInitializationFrames) // typical operation if (frameNum > numInitializationFrames) // typical operation
{ {
newFeature.likelihood = learningRate; newFeature.likelihood = float(learningRate);
/* /*
* (1) Query histogram to find posterior probability of feature under model. * (1) Query histogram to find posterior probability of feature under model.
*/ */
float likelihood = (float)pixel->getLikelihood(newFeature); float likelihood = (float)pixel->getLikelihood(newFeature);
// see Godbehere, Matsukawa, Goldberg (2012) for reasoning behind this implementation of Bayes rule // see Godbehere, Matsukawa, Goldberg (2012) for reasoning behind this implementation of Bayes rule
float posterior = (likelihood*backgroundPrior)/(likelihood*backgroundPrior+(1-likelihood)*(1-backgroundPrior)); float posterior = float((likelihood*backgroundPrior)/(likelihood*backgroundPrior+(1-likelihood)*(1-backgroundPrior)));
/* /*
* (2) feed posterior probability into the posterior image * (2) feed posterior probability into the posterior image
*/ */
int row,col; int row,col;
col = i%imWidth; col = i%imWidth;
row = (i-col)/imWidth; row = (i-col)/imWidth;
posteriorImage.at<float>(row,col) = (1.0-posterior); posteriorImage.at<float>(row,col) = (1.0f-posterior);
} }
pixel->setLastObservedFeature(newFeature); pixel->setLastObservedFeature(newFeature);
} }
/* /*
* (3) Perform filtering and threshold operations to yield final mask image. * (3) Perform filtering and threshold operations to yield final mask image.
* *
* 2 options. First is morphological open/close as before. Second is "median filtering" which Jon Barron says is good to remove noise * 2 options. First is morphological open/close as before. Second is "median filtering" which Jon Barron says is good to remove noise
*/ */
Mat thresholdedPosterior; Mat thresholdedPosterior;
threshold(posteriorImage,thresholdedPosterior,decisionThreshold,1.0,THRESH_BINARY); threshold(posteriorImage,thresholdedPosterior,decisionThreshold,1.0,THRESH_BINARY);
thresholdedPosterior.convertTo(fgMaskImage,CV_8U,255); // convert image to integer space for further filtering and mask creation thresholdedPosterior.convertTo(fgMaskImage,CV_8U,255); // convert image to integer space for further filtering and mask creation
medianBlur(fgMaskImage,fgMaskImage,smoothingRadius); medianBlur(fgMaskImage,fgMaskImage,smoothingRadius);
fgMaskImage.copyTo(_fgmask); fgMaskImage.copyTo(_fgmask);
++frameNum; // keep track of how many frames we have processed ++frameNum; // keep track of how many frames we have processed
} }
void BackgroundSubtractorGMG::getPosteriorImage(OutputArray _img) void BackgroundSubtractorGMG::getPosteriorImage(OutputArray _img)
{ {
_img.create(Size(imWidth,imHeight),CV_32F); _img.create(Size(imWidth,imHeight),CV_32F);
Mat img = _img.getMat(); Mat img = _img.getMat();
posteriorImage.copyTo(img); posteriorImage.copyTo(img);
} }
void BackgroundSubtractorGMG::updateBackgroundModel(InputArray _mask) void BackgroundSubtractorGMG::updateBackgroundModel(InputArray _mask)
{ {
CV_Assert(_mask.size() == Size(imWidth,imHeight)); // mask should be same size as image CV_Assert(_mask.size() == Size(imWidth,imHeight)); // mask should be same size as image
Mat maskImg = _mask.getMat(); Mat maskImg = _mask.getMat();
//#pragma omp parallel //#pragma omp parallel
for (size_t i = 0; i < imHeight; ++i) for (size_t i = 0; i < imHeight; ++i)
{ {
//#pragma omp parallel //#pragma omp parallel
for (size_t j = 0; j < imWidth; ++j) for (size_t j = 0; j < imWidth; ++j)
{ {
if (frameNum <= numInitializationFrames + 1) if (frameNum <= numInitializationFrames + 1)
{ {
// insert previously observed feature into the histogram. -1.0 parameter indicates training. // insert previously observed feature into the histogram. -1.0 parameter indicates training.
pixels[i*imWidth+j].insertFeature(-1.0); pixels[i*imWidth+j].insertFeature(-1.0);
if (frameNum >= numInitializationFrames+1) // training is done, normalize if (frameNum >= numInitializationFrames+1) // training is done, normalize
{ {
pixels[i*imWidth+j].normalizeHistogram(); pixels[i*imWidth+j].normalizeHistogram();
} }
} }
// if mask is 0, pixel is identified as a background pixel, so update histogram. // if mask is 0, pixel is identified as a background pixel, so update histogram.
else if (maskImg.at<uchar>(i,j) == 0) else if (maskImg.at<uchar>(i,j) == 0)
{ {
pixels[i*imWidth+j].insertFeature(learningRate); // updates the histogram for the next iteration. pixels[i*imWidth+j].insertFeature(learningRate); // updates the histogram for the next iteration.
} }
} }
} }
} }
BackgroundSubtractorGMG::~BackgroundSubtractorGMG() BackgroundSubtractorGMG::~BackgroundSubtractorGMG()
@ -314,8 +314,8 @@ BackgroundSubtractorGMG::~BackgroundSubtractorGMG()
BackgroundSubtractorGMG::PixelModelGMG::PixelModelGMG() BackgroundSubtractorGMG::PixelModelGMG::PixelModelGMG()
{ {
numFeatures = 0; numFeatures = 0;
maxFeatures = 0; maxFeatures = 0;
} }
BackgroundSubtractorGMG::PixelModelGMG::~PixelModelGMG() BackgroundSubtractorGMG::PixelModelGMG::~PixelModelGMG()
@ -325,154 +325,154 @@ BackgroundSubtractorGMG::PixelModelGMG::~PixelModelGMG()
void BackgroundSubtractorGMG::PixelModelGMG::setLastObservedFeature(HistogramFeatureGMG f) void BackgroundSubtractorGMG::PixelModelGMG::setLastObservedFeature(HistogramFeatureGMG f)
{ {
this->lastObservedFeature = f; this->lastObservedFeature = f;
} }
double BackgroundSubtractorGMG::PixelModelGMG::getLikelihood(BackgroundSubtractorGMG::HistogramFeatureGMG f) double BackgroundSubtractorGMG::PixelModelGMG::getLikelihood(BackgroundSubtractorGMG::HistogramFeatureGMG f)
{ {
std::list<HistogramFeatureGMG>::iterator feature = histogram.begin(); std::list<HistogramFeatureGMG>::iterator feature = histogram.begin();
std::list<HistogramFeatureGMG>::iterator feature_end = histogram.end(); std::list<HistogramFeatureGMG>::iterator feature_end = histogram.end();
for (feature = histogram.begin(); feature != feature_end; ++feature) for (feature = histogram.begin(); feature != feature_end; ++feature)
{ {
// comparing only feature color, not likelihood. See equality operator for HistogramFeatureGMG // comparing only feature color, not likelihood. See equality operator for HistogramFeatureGMG
if (f == *feature) if (f == *feature)
{ {
return feature->likelihood; return feature->likelihood;
} }
} }
return 0.0; // not in histogram, so return 0. return 0.0; // not in histogram, so return 0.
} }
void BackgroundSubtractorGMG::PixelModelGMG::insertFeature(double learningRate) void BackgroundSubtractorGMG::PixelModelGMG::insertFeature(double learningRate)
{ {
std::list<HistogramFeatureGMG>::iterator feature; std::list<HistogramFeatureGMG>::iterator feature;
std::list<HistogramFeatureGMG>::iterator swap_end; std::list<HistogramFeatureGMG>::iterator swap_end;
std::list<HistogramFeatureGMG>::iterator last_feature = histogram.end(); std::list<HistogramFeatureGMG>::iterator last_feature = histogram.end();
/* /*
* If feature is in histogram already, add the weights, and move feature to front. * If feature is in histogram already, add the weights, and move feature to front.
* If there are too many features, remove the end feature and push new feature to beginning * If there are too many features, remove the end feature and push new feature to beginning
*/ */
if (learningRate == -1.0) // then, this is a training-mode update. if (learningRate == -1.0) // then, this is a training-mode update.
{ {
/* /*
* (1) Check if feature already represented in histogram * (1) Check if feature already represented in histogram
*/ */
lastObservedFeature.likelihood = 1.0; lastObservedFeature.likelihood = 1.0;
for (feature = histogram.begin(); feature != last_feature; ++feature) for (feature = histogram.begin(); feature != last_feature; ++feature)
{ {
if (lastObservedFeature == *feature) // feature in histogram if (lastObservedFeature == *feature) // feature in histogram
{ {
feature->likelihood += lastObservedFeature.likelihood; feature->likelihood += lastObservedFeature.likelihood;
// now, move feature to beginning of list and break the loop // now, move feature to beginning of list and break the loop
HistogramFeatureGMG tomove = *feature; HistogramFeatureGMG tomove = *feature;
histogram.erase(feature); histogram.erase(feature);
histogram.push_front(tomove); histogram.push_front(tomove);
return; return;
} }
} }
if (numFeatures == maxFeatures) if (numFeatures == maxFeatures)
{ {
histogram.pop_back(); // discard oldest feature histogram.pop_back(); // discard oldest feature
histogram.push_front(lastObservedFeature); histogram.push_front(lastObservedFeature);
} }
else else
{ {
histogram.push_front(lastObservedFeature); histogram.push_front(lastObservedFeature);
++numFeatures; ++numFeatures;
} }
} }
else else
{ {
/* /*
* (1) Scale entire histogram by scaling factor * (1) Scale entire histogram by scaling factor
* (2) Scale input feature. * (2) Scale input feature.
* (3) Check if feature already represented. If so, simply add. * (3) Check if feature already represented. If so, simply add.
* (4) If feature is not represented, remove old feature, distribute weight evenly among existing features, add in new feature. * (4) If feature is not represented, remove old feature, distribute weight evenly among existing features, add in new feature.
*/ */
*this *= (1.0-learningRate); *this *= float(1.0-learningRate);
lastObservedFeature.likelihood = learningRate; lastObservedFeature.likelihood = float(learningRate);
for (feature = histogram.begin(); feature != last_feature; ++feature) for (feature = histogram.begin(); feature != last_feature; ++feature)
{ {
if (lastObservedFeature == *feature) // feature in histogram if (lastObservedFeature == *feature) // feature in histogram
{ {
lastObservedFeature.likelihood += feature->likelihood; lastObservedFeature.likelihood += feature->likelihood;
histogram.erase(feature); histogram.erase(feature);
histogram.push_front(lastObservedFeature); histogram.push_front(lastObservedFeature);
return; // done with the update. return; // done with the update.
} }
} }
if (numFeatures == maxFeatures) if (numFeatures == maxFeatures)
{ {
histogram.pop_back(); // discard oldest feature histogram.pop_back(); // discard oldest feature
histogram.push_front(lastObservedFeature); histogram.push_front(lastObservedFeature);
normalizeHistogram(); normalizeHistogram();
} }
else else
{ {
histogram.push_front(lastObservedFeature); histogram.push_front(lastObservedFeature);
++numFeatures; ++numFeatures;
} }
} }
} }
BackgroundSubtractorGMG::PixelModelGMG& BackgroundSubtractorGMG::PixelModelGMG::operator *=(const float &rhs) BackgroundSubtractorGMG::PixelModelGMG& BackgroundSubtractorGMG::PixelModelGMG::operator *=(const float &rhs)
{ {
/* /*
* Used to scale histogram by a constant factor * Used to scale histogram by a constant factor
*/ */
list<HistogramFeatureGMG>::iterator feature; list<HistogramFeatureGMG>::iterator feature;
list<HistogramFeatureGMG>::iterator last_feature = histogram.end(); list<HistogramFeatureGMG>::iterator last_feature = histogram.end();
for (feature = histogram.begin(); feature != last_feature; ++feature) for (feature = histogram.begin(); feature != last_feature; ++feature)
{ {
feature->likelihood *= rhs; feature->likelihood *= rhs;
} }
return *this; return *this;
} }
void BackgroundSubtractorGMG::PixelModelGMG::normalizeHistogram() void BackgroundSubtractorGMG::PixelModelGMG::normalizeHistogram()
{ {
/* /*
* First, calculate the total weight in the histogram * First, calculate the total weight in the histogram
*/ */
list<HistogramFeatureGMG>::iterator feature; list<HistogramFeatureGMG>::iterator feature;
list<HistogramFeatureGMG>::iterator last_feature = histogram.end(); list<HistogramFeatureGMG>::iterator last_feature = histogram.end();
double total = 0.0; double total = 0.0;
for (feature = histogram.begin(); feature != last_feature; ++feature) for (feature = histogram.begin(); feature != last_feature; ++feature)
{ {
total += feature->likelihood; total += feature->likelihood;
} }
/* /*
* Then, if weight is not 0, divide every feature by the total likelihood to re-normalize. * Then, if weight is not 0, divide every feature by the total likelihood to re-normalize.
*/ */
for (feature = histogram.begin(); feature != last_feature; ++feature) for (feature = histogram.begin(); feature != last_feature; ++feature)
{ {
if (total != 0.0) if (total != 0.0)
feature->likelihood /= total; feature->likelihood = float(feature->likelihood / total);
} }
} }
bool BackgroundSubtractorGMG::HistogramFeatureGMG::operator ==(HistogramFeatureGMG &rhs) bool BackgroundSubtractorGMG::HistogramFeatureGMG::operator ==(HistogramFeatureGMG &rhs)
{ {
CV_Assert(color.size() == rhs.color.size()); CV_Assert(color.size() == rhs.color.size());
std::vector<size_t>::iterator color_a; std::vector<size_t>::iterator color_a;
std::vector<size_t>::iterator color_b; std::vector<size_t>::iterator color_b;
std::vector<size_t>::iterator color_a_end = this->color.end(); std::vector<size_t>::iterator color_a_end = this->color.end();
std::vector<size_t>::iterator color_b_end = rhs.color.end(); std::vector<size_t>::iterator color_b_end = rhs.color.end();
for (color_a = color.begin(),color_b =rhs.color.begin();color_a!=color_a_end;++color_a,++color_b) for (color_a = color.begin(),color_b =rhs.color.begin();color_a!=color_a_end;++color_a,++color_b)
{ {
if (*color_a != *color_b) if (*color_a != *color_b)
{ {
return false; return false;
} }
} }
return true; return true;
} }

View File

@ -79,7 +79,7 @@ CV_INIT_ALGORITHM(BackgroundSubtractorGMG, "BackgroundSubtractor.GMG",
"Radius of smoothing kernel to filter noise from FG mask image."); "Radius of smoothing kernel to filter noise from FG mask image.");
obj.info()->addParam(obj, "decisionThreshold", obj.decisionThreshold,false,0,0, obj.info()->addParam(obj, "decisionThreshold", obj.decisionThreshold,false,0,0,
"Threshold for FG decision rule. Pixel is FG if posterior probability exceeds threshold.")); "Threshold for FG decision rule. Pixel is FG if posterior probability exceeds threshold."));
bool initModule_video(void) bool initModule_video(void)
{ {
bool all = true; bool all = true;