From dec38e594956475c63a7e52f96a8ec9344a8663c Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Sat, 30 Jun 2012 20:47:09 +0000 Subject: [PATCH] Background subtractor GMG: removed flexitype, fixed build errors. --- .../include/opencv2/video/background_segm.hpp | 86 +---- modules/video/src/bgfg_gmg.cpp | 76 ++-- .../test/test_backgroundsubtractor_gbh.cpp | 331 +++++++++--------- 3 files changed, 196 insertions(+), 297 deletions(-) diff --git a/modules/video/include/opencv2/video/background_segm.hpp b/modules/video/include/opencv2/video/background_segm.hpp index 9f5dfe5b37..a71cf3e538 100644 --- a/modules/video/include/opencv2/video/background_segm.hpp +++ b/modules/video/include/opencv2/video/background_segm.hpp @@ -199,75 +199,7 @@ protected: */ class CV_EXPORTS BackgroundSubtractorGMG: public cv::BackgroundSubtractor { -private: - /** - * A general flexible datatype. - * - * 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. - */ - union flexitype{ - char c; - uchar uc; - int i; - unsigned int ui; - long int li; - float f; - double d; - - flexitype(){d = 0.0;} //!< Default constructor, set all bits of the union to 0. - flexitype(char cval){c = cval;} //!< Char type constructor - - bool operator ==(flexitype& rhs) - { - return d == rhs.d; - } - - //! Char type assignment operator - flexitype& operator =(char cval){ - if (this->c == cval){return *this;} - c = cval; return *this; - } - flexitype(unsigned char ucval){uc = ucval;} //!< unsigned char type constructor - - //! unsigned char type assignment operator - flexitype& operator =(unsigned char ucval){ - if (this->uc == ucval){return *this;} - uc = ucval; return *this; - } - flexitype(int ival){i = ival;} //!< int type constructor - //! int type assignment operator - flexitype& operator =(int ival){ - if (this->i == ival){return *this;} - i = ival; return *this; - } - flexitype(unsigned int uival){ui = uival;} //!< unsigned int type constructor - - //! unsigned int type assignment operator - flexitype& operator =(unsigned int uival){ - if (this->ui == uival){return *this;} - ui = uival; return *this; - } - flexitype(float fval){f = fval;} //!< float type constructor - //! float type assignment operator - flexitype& operator =(float fval){ - if (this->f == fval){return *this;} - f = fval; return *this; - } - flexitype(long int lival){li = lival;} //!< long int type constructor - //! long int type assignment operator - flexitype& operator =(long int lival){ - if (this->li == lival){return *this;} - li = lival; return *this; - } - - flexitype(double dval){d=dval;} //!< double type constructor - //! double type assignment operator - flexitype& operator =(double dval){ - if (this->d == dval){return *this;} - d = dval; return *this; - } - }; +protected: /** * Used internally to represent a single feature in a histogram. * Feature is a color and an associated likelihood (weight in the histogram). @@ -387,7 +319,7 @@ public: * @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, flexitype min, flexitype max); + void initializeType(InputArray image, double min, double max); /** * Selectively update the background model. Only update background model for pixels identified * as background. @@ -417,24 +349,20 @@ protected: double decisionThreshold; //!< value above which pixel is determined to be FG. int smoothingRadius; //!< smoothing radius, in pixels, for cleaning up FG image. - flexitype maxVal, minVal; + double maxVal, minVal; /* * General Parameters */ - size_t imWidth; //!< width of image. - size_t imHeight; //!< height of image. - size_t numPixels; + int imWidth; //!< width of image. + int imHeight; //!< height of image. + size_t numPixels; - 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; //!< After general parameters are set, data structures must be initialized. - size_t elemSize; //!< store image mat element sizes - size_t elemSize1; - /* * Data Structures */ diff --git a/modules/video/src/bgfg_gmg.cpp b/modules/video/src/bgfg_gmg.cpp index 145ecc64a3..163445a45b 100644 --- a/modules/video/src/bgfg_gmg.cpp +++ b/modules/video/src/bgfg_gmg.cpp @@ -67,7 +67,7 @@ BackgroundSubtractorGMG::BackgroundSubtractorGMG() smoothingRadius = 7; } -void BackgroundSubtractorGMG::initializeType(InputArray _image,flexitype min, flexitype max) +void BackgroundSubtractorGMG::initializeType(InputArray _image, double min, double max) { minVal = min; maxVal = max; @@ -114,7 +114,6 @@ void BackgroundSubtractorGMG::initializeType(InputArray _image,flexitype min, fl * Detect and accommodate the image depth */ Mat image = _image.getMat(); - imageDepth = image.depth(); // 32f, 8u, etc. numChannels = image.channels(); /* @@ -127,16 +126,15 @@ void BackgroundSubtractorGMG::initializeType(InputArray _image,flexitype min, fl /* * Data Structure Initialization */ - Size imsize = image.size(); - imWidth = imsize.width; - imHeight = imsize.height; - numPixels = imWidth*imHeight; + imWidth = image.cols; + imHeight = image.rows; + numPixels = image.total(); pixels.resize(numPixels); frameNum = 0; // used to iterate through matrix of type unknown at compile time - elemSize = image.elemSize(); - elemSize1 = image.elemSize1(); + //elemSize = image.elemSize(); + //elemSize1 = image.elemSize1(); vector::iterator pixel; vector::iterator pixel_end = pixels.end(); @@ -145,8 +143,8 @@ void BackgroundSubtractorGMG::initializeType(InputArray _image,flexitype min, fl pixel->setMaxFeatures(maxFeatures); } - 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. + 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. isDataInitialized = true; } @@ -171,7 +169,7 @@ void BackgroundSubtractorGMG::operator()(InputArray _image, OutputArray _fgmask, Mat image = _image.getMat(); - _fgmask.create(Size(imHeight,imWidth),CV_8U); + _fgmask.create(imHeight,imWidth,CV_8U); fgMaskImage = _fgmask.getMat(); // 8-bit unsigned mask. 255 for FG, 0 for BG /* @@ -183,54 +181,32 @@ void BackgroundSubtractorGMG::operator()(InputArray _image, OutputArray _fgmask, vector::iterator pixel; vector::iterator pixel_end = pixels.end(); size_t i; -//#pragma omp parallel + //#pragma omp parallel for (i = 0, pixel=pixels.begin(); pixel != pixel_end; ++i,++pixel) { HistogramFeatureGMG newFeature; newFeature.color.clear(); + int irow = int(i / imWidth); + int icol = i % imWidth; for (size_t c = 0; c < numChannels; ++c) { /* * Perform quantization. in each channel. (color-min)*(levels)/(max-min). * Shifts min to 0 and scales, finally casting to an int. */ - size_t quantizedColor; - // pixel at data+elemSize*i. Individual channel c at data+elemSize*i+elemSize1*c - if (imageDepth == CV_8U) - { - uchar *color = (uchar*)(image.data+elemSize*i+elemSize1*c); - quantizedColor = (size_t)((double)(*color-minVal.uc)*quantizationLevels/(maxVal.uc-minVal.uc)); - } - else if (imageDepth == CV_8S) - { - char *color = (char*)(image.data+elemSize*i+elemSize1*c); - quantizedColor = (size_t)((double)(*color-minVal.c)*quantizationLevels/(maxVal.c-minVal.c)); - } - else if (imageDepth == CV_16U) - { - unsigned int *color = (unsigned int*)(image.data+elemSize*i+elemSize1*c); - quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui)); - } - else if (imageDepth == CV_16S) - { - int *color = (int*)(image.data+elemSize*i+elemSize1*c); - quantizedColor = (size_t)((double)(*color-minVal.i)*quantizationLevels/(maxVal.i-minVal.i)); - } - else if (imageDepth == CV_32F) - { - float *color = (float*)image.data+elemSize*i+elemSize1*c; - quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui)); - } - else if (imageDepth == CV_32S) - { - long int *color = (long int*)(image.data+elemSize*i+elemSize1*c); - quantizedColor = (size_t)((double)(*color-minVal.li)*quantizationLevels/(maxVal.li-minVal.li)); - } - else if (imageDepth == CV_64F) + double color; + switch(image.depth()) { - double *color = (double*)image.data+elemSize*i+elemSize1*c; - quantizedColor = (size_t)((double)(*color-minVal.d)*quantizationLevels/(maxVal.d-minVal.d)); + case CV_8U: color = image.ptr(irow)[icol * numChannels + c]; break; + case CV_8S: color = image.ptr(irow)[icol * numChannels + c]; break; + case CV_16U: color = image.ptr(irow)[icol * numChannels + c]; break; + case CV_16S: color = image.ptr(irow)[icol * numChannels + c]; break; + case CV_32S: color = image.ptr(irow)[icol * numChannels + c]; break; + case CV_32F: color = image.ptr(irow)[icol * numChannels + c]; break; + case CV_64F: color = image.ptr(irow)[icol * numChannels + c]; break; + default: color = 0; break; } + size_t quantizedColor = (size_t)((color-minVal)*quantizationLevels/(maxVal-minVal)); newFeature.color.push_back(quantizedColor); } // now that the feature is ready for use, put it in the histogram @@ -251,7 +227,7 @@ void BackgroundSubtractorGMG::operator()(InputArray _image, OutputArray _fgmask, */ int row,col; col = i%imWidth; - row = (i-col)/imWidth; + row = int(i-col)/imWidth; posteriorImage.at(row,col) = (1.0f-posterior); } pixel->setLastObservedFeature(newFeature); @@ -284,10 +260,10 @@ void BackgroundSubtractorGMG::updateBackgroundModel(InputArray _mask) Mat maskImg = _mask.getMat(); //#pragma omp parallel - for (size_t i = 0; i < imHeight; ++i) + for (int i = 0; i < imHeight; ++i) { //#pragma omp parallel - for (size_t j = 0; j < imWidth; ++j) + for (int j = 0; j < imWidth; ++j) { if (frameNum <= numInitializationFrames + 1) { diff --git a/modules/video/test/test_backgroundsubtractor_gbh.cpp b/modules/video/test/test_backgroundsubtractor_gbh.cpp index 4faf122bca..8486e541cb 100644 --- a/modules/video/test/test_backgroundsubtractor_gbh.cpp +++ b/modules/video/test/test_backgroundsubtractor_gbh.cpp @@ -12,9 +12,9 @@ using namespace cv; class CV_BackgroundSubtractorTest : public cvtest::BaseTest { public: - CV_BackgroundSubtractorTest(); + CV_BackgroundSubtractorTest(); protected: - void run(int); + void run(int); }; CV_BackgroundSubtractorTest::CV_BackgroundSubtractorTest() @@ -29,172 +29,167 @@ CV_BackgroundSubtractorTest::CV_BackgroundSubtractorTest() */ void CV_BackgroundSubtractorTest::run(int) { - int code = cvtest::TS::OK; - RNG& rng = ts->get_rng(); - int type = ((unsigned int)rng)%7; //!< pick a random type, 0 - 6, defined in types_c.h - int channels = 1 + ((unsigned int)rng)%4; //!< random number of channels from 1 to 4. - int channelsAndType = CV_MAKETYPE(type,channels); - int width = 2 + ((unsigned int)rng)%98; //!< Mat will be 2 to 100 in width and height - int height = 2 + ((unsigned int)rng)%98; - - Ptr fgbg = - Algorithm::create("BackgroundSubtractor.GMG"); - Mat fgmask; - - if (fgbg == NULL) - CV_Error(CV_StsError,"Failed to create Algorithm\n"); - - /** - * Set a few parameters - */ - fgbg->set("smoothingRadius",7); - fgbg->set("decisionThreshold",0.7); - fgbg->set("initializationFrames",120); - - /** - * Generate bounds for the values in the matrix for each type - */ - uchar maxuc = 0, minuc = 0; - char maxc = 0, minc = 0; - uint maxui = 0, minui = 0; - int maxi=0, mini = 0; - long int maxli = 0, minli = 0; - float maxf = 0, minf = 0; - double maxd = 0, mind = 0; - - /** - * Max value for simulated images picked randomly in upper half of type range - * Min value for simulated images picked randomly in lower half of type range - */ - if (type == CV_8U) - { - unsigned char half = UCHAR_MAX/2; - maxuc = (unsigned char)rng.uniform(half+32,UCHAR_MAX); - minuc = (unsigned char)rng.uniform(0,half-32); - } - else if (type == CV_8S) - { - char half = CHAR_MAX/2 + CHAR_MIN/2; - maxc = (char)rng.uniform(half+32,CHAR_MAX); - minc = (char)rng.uniform(CHAR_MIN,half-32); - } - else if (type == CV_16U) - { - uint half = UINT_MAX/2; - maxui = (unsigned int)rng.uniform((int)half+32,UINT_MAX); - minui = (unsigned int)rng.uniform(0,(int)half-32); - } - else if (type == CV_16S) - { - int half = INT_MAX/2 + INT_MIN/2; - maxi = rng.uniform(half+32,INT_MAX); - mini = rng.uniform(INT_MIN,half-32); - } - else if (type == CV_32S) - { - long int half = LONG_MAX/2 + LONG_MIN/2; - maxli = rng.uniform((int)half+32,(int)LONG_MAX); - minli = rng.uniform((int)LONG_MIN,(int)half-32); - } - else if (type == CV_32F) - { - float half = FLT_MAX/2.0 + FLT_MIN/2.0; - maxf = rng.uniform(half+(float)32.0*FLT_EPSILON,FLT_MAX); - minf = rng.uniform(FLT_MIN,half-(float)32.0*FLT_EPSILON); - } - else if (type == CV_64F) - { - double half = DBL_MAX/2.0 + DBL_MIN/2.0; - maxd = rng.uniform(half+(double)32.0*DBL_EPSILON,DBL_MAX); - mind = rng.uniform(DBL_MIN,half-(double)32.0*DBL_EPSILON); - } - - Mat simImage = Mat::zeros(height,width,channelsAndType); - const uint numLearningFrames = 120; - for (uint i = 0; i < numLearningFrames; ++i) - { - /** - * Genrate simulated "image" for any type. Values always confined to upper half of range. - */ - if (type == CV_8U) - { - rng.fill(simImage,RNG::UNIFORM,(unsigned char)(minuc/2+maxuc/2),maxuc); - if (i == 0) - fgbg->initializeType(simImage,minuc,maxuc); - } - else if (type == CV_8S) - { - rng.fill(simImage,RNG::UNIFORM,(char)(minc/2+maxc/2),maxc); - if (i==0) - fgbg->initializeType(simImage,minc,maxc); - } - else if (type == CV_16U) - { - rng.fill(simImage,RNG::UNIFORM,(unsigned int)(minui/2+maxui/2),maxui); - if (i==0) - fgbg->initializeType(simImage,minui,maxui); - } - else if (type == CV_16S) - { - rng.fill(simImage,RNG::UNIFORM,(int)(mini/2+maxi/2),maxi); - if (i==0) - fgbg->initializeType(simImage,mini,maxi); - } - else if (type == CV_32F) - { - rng.fill(simImage,RNG::UNIFORM,(float)(minf/2.0+maxf/2.0),maxf); - if (i==0) - fgbg->initializeType(simImage,minf,maxf); - } - else if (type == CV_32S) - { - rng.fill(simImage,RNG::UNIFORM,(long int)(minli/2+maxli/2),maxli); - if (i==0) - fgbg->initializeType(simImage,minli,maxli); - } - else if (type == CV_64F) - { - rng.fill(simImage,RNG::UNIFORM,(double)(mind/2.0+maxd/2.0),maxd); - if (i==0) - fgbg->initializeType(simImage,mind,maxd); - } - - /** - * Feed simulated images into background subtractor - */ - (*fgbg)(simImage,fgmask); - Mat fullbg = Mat::zeros(Size(simImage.cols,simImage.rows),CV_8U); - fgbg->updateBackgroundModel(fullbg); - - //! fgmask should be entirely background during training - code = cvtest::cmpEps2( ts, fgmask, fullbg, 0, false, "The training foreground mask" ); - if (code < 0) - ts->set_failed_test_info( code ); - } - //! generate last image, distinct from training images - if (type == CV_8U) - rng.fill(simImage,RNG::UNIFORM,minuc,minuc); - else if (type == CV_8S) - rng.fill(simImage,RNG::UNIFORM,minc,minc); - else if (type == CV_16U) - rng.fill(simImage,RNG::UNIFORM,minui,minui); - else if (type == CV_16S) - rng.fill(simImage,RNG::UNIFORM,mini,mini); - else if (type == CV_32F) - rng.fill(simImage,RNG::UNIFORM,minf,minf); - else if (type == CV_32S) - rng.fill(simImage,RNG::UNIFORM,minli,minli); - else if (type == CV_64F) - rng.fill(simImage,RNG::UNIFORM,mind,mind); - - (*fgbg)(simImage,fgmask); - //! now fgmask should be entirely foreground - Mat fullfg = 255*Mat::ones(Size(simImage.cols,simImage.rows),CV_8U); - code = cvtest::cmpEps2( ts, fgmask, fullfg, 255, false, "The final foreground mask" ); - if (code < 0) - { - ts->set_failed_test_info( code ); - } + int code = cvtest::TS::OK; + RNG& rng = ts->get_rng(); + int type = ((unsigned int)rng)%7; //!< pick a random type, 0 - 6, defined in types_c.h + int channels = 1 + ((unsigned int)rng)%4; //!< random number of channels from 1 to 4. + int channelsAndType = CV_MAKETYPE(type,channels); + int width = 2 + ((unsigned int)rng)%98; //!< Mat will be 2 to 100 in width and height + int height = 2 + ((unsigned int)rng)%98; + + Ptr fgbg = + Algorithm::create("BackgroundSubtractor.GMG"); + Mat fgmask; + + if (fgbg == NULL) + CV_Error(CV_StsError,"Failed to create Algorithm\n"); + + /** + * Set a few parameters + */ + fgbg->set("smoothingRadius",7); + fgbg->set("decisionThreshold",0.7); + fgbg->set("initializationFrames",120); + + /** + * Generate bounds for the values in the matrix for each type + */ + uchar maxuc = 0, minuc = 0; + char maxc = 0, minc = 0; + unsigned int maxui = 0, minui = 0; + int maxi=0, mini = 0; + long int maxli = 0, minli = 0; + float maxf = 0, minf = 0; + double maxd = 0, mind = 0; + + /** + * Max value for simulated images picked randomly in upper half of type range + * Min value for simulated images picked randomly in lower half of type range + */ + if (type == CV_8U) + { + uchar half = UCHAR_MAX/2; + maxuc = (unsigned char)rng.uniform(half+32, UCHAR_MAX); + minuc = (unsigned char)rng.uniform(0, half-32); + } + else if (type == CV_8S) + { + maxc = (char)rng.uniform(32, CHAR_MAX); + minc = (char)rng.uniform(CHAR_MIN, -32); + } + else if (type == CV_16U) + { + ushort half = USHRT_MAX/2; + maxui = (unsigned int)rng.uniform(half+32, USHRT_MAX); + minui = (unsigned int)rng.uniform(0, half-32); + } + else if (type == CV_16S) + { + maxi = rng.uniform(32, SHRT_MAX); + mini = rng.uniform(SHRT_MIN, -32); + } + else if (type == CV_32S) + { + maxli = rng.uniform(32, INT_MAX); + minli = rng.uniform(INT_MIN, -32); + } + else if (type == CV_32F) + { + maxf = rng.uniform(32.0f, FLT_MAX); + minf = rng.uniform(-FLT_MAX, -32.0f); + } + else if (type == CV_64F) + { + maxd = rng.uniform(32.0, DBL_MAX); + mind = rng.uniform(-DBL_MAX, -32.0); + } + + Mat simImage = Mat::zeros(height, width, channelsAndType); + const unsigned int numLearningFrames = 120; + for (unsigned int i = 0; i < numLearningFrames; ++i) + { + /** + * Genrate simulated "image" for any type. Values always confined to upper half of range. + */ + if (type == CV_8U) + { + rng.fill(simImage,RNG::UNIFORM,(unsigned char)(minuc/2+maxuc/2),maxuc); + if (i == 0) + fgbg->initializeType(simImage,minuc,maxuc); + } + else if (type == CV_8S) + { + rng.fill(simImage,RNG::UNIFORM,(char)(minc/2+maxc/2),maxc); + if (i==0) + fgbg->initializeType(simImage,minc,maxc); + } + else if (type == CV_16U) + { + rng.fill(simImage,RNG::UNIFORM,(unsigned int)(minui/2+maxui/2),maxui); + if (i==0) + fgbg->initializeType(simImage,minui,maxui); + } + else if (type == CV_16S) + { + rng.fill(simImage,RNG::UNIFORM,(int)(mini/2+maxi/2),maxi); + if (i==0) + fgbg->initializeType(simImage,mini,maxi); + } + else if (type == CV_32F) + { + rng.fill(simImage,RNG::UNIFORM,(float)(minf/2.0+maxf/2.0),maxf); + if (i==0) + fgbg->initializeType(simImage,minf,maxf); + } + else if (type == CV_32S) + { + rng.fill(simImage,RNG::UNIFORM,(long int)(minli/2+maxli/2),maxli); + if (i==0) + fgbg->initializeType(simImage,minli,maxli); + } + else if (type == CV_64F) + { + rng.fill(simImage,RNG::UNIFORM,(double)(mind/2.0+maxd/2.0),maxd); + if (i==0) + fgbg->initializeType(simImage,mind,maxd); + } + + /** + * Feed simulated images into background subtractor + */ + (*fgbg)(simImage,fgmask); + Mat fullbg = Mat::zeros(simImage.rows, simImage.cols, CV_8U); + fgbg->updateBackgroundModel(fullbg); + + //! fgmask should be entirely background during training + code = cvtest::cmpEps2( ts, fgmask, fullbg, 0, false, "The training foreground mask" ); + if (code < 0) + ts->set_failed_test_info( code ); + } + //! generate last image, distinct from training images + if (type == CV_8U) + rng.fill(simImage,RNG::UNIFORM,minuc,minuc); + else if (type == CV_8S) + rng.fill(simImage,RNG::UNIFORM,minc,minc); + else if (type == CV_16U) + rng.fill(simImage,RNG::UNIFORM,minui,minui); + else if (type == CV_16S) + rng.fill(simImage,RNG::UNIFORM,mini,mini); + else if (type == CV_32F) + rng.fill(simImage,RNG::UNIFORM,minf,minf); + else if (type == CV_32S) + rng.fill(simImage,RNG::UNIFORM,minli,minli); + else if (type == CV_64F) + rng.fill(simImage,RNG::UNIFORM,mind,mind); + + (*fgbg)(simImage,fgmask); + //! now fgmask should be entirely foreground + Mat fullfg = 255*Mat::ones(simImage.rows, simImage.cols, CV_8U); + code = cvtest::cmpEps2( ts, fgmask, fullfg, 255, false, "The final foreground mask" ); + if (code < 0) + { + ts->set_failed_test_info( code ); + } }