bgfggmg.diff

src, include, sample, test - Andrew Godbehere, 2012-06-19 12:06 am

Download (37.2 kB)

 
samples/cpp/BackgroundSubtractorGMG_demo.cpp (working copy)
1
/*
2
 * FGBGTest.cpp
3
 *
4
 *  Created on: May 7, 2012
5
 *      Author: Andrew B. Godbehere
6
 */
7

  
8
#include <opencv2/opencv.hpp>
9
#include <iostream>
10
#include <sstream>
11

  
12
using namespace cv;
13

  
14
void help()
15
{
16
	std::cout <<
17
	"\nA program demonstrating the use and capabilities of a particular BackgroundSubtraction\n"
18
	"algorithm described in A. Godbehere, A. Matsukawa, K. Goldberg, \n"
19
	"\"Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive\n"
20
	"Audio Art Installation\", American Control Conference, 2012, used in an interactive\n"
21
	"installation at the Contemporary Jewish Museum in San Francisco, CA from March 31 through\n"
22
	"July 31, 2011.\n"
23
	"Call:\n"
24
	"./BackgroundSubtractorGMG_sample\n"
25
	"Using OpenCV version " << CV_VERSION << "\n"<<std::endl;
26
}
27

  
28
int main(char *argc, char** argv)
29
{
30
	help();
31
	setUseOptimized(true);
32
	setNumThreads(8);
33

  
34
	Ptr<BackgroundSubtractorGMG> fgbg = Algorithm::create<BackgroundSubtractorGMG>("BackgroundSubtractor.GMG");
35
	if (fgbg == NULL)
36
	{
37
		CV_Error(CV_StsError,"Failed to create Algorithm\n");
38
	}
39
	fgbg->set("smoothingRadius",7);
40
	fgbg->set("decisionThreshold",0.7);
41

  
42
	VideoCapture cap;
43
	cap.open("fgbg_gmg.avi");
44
	if (!cap.isOpened())
45
	{
46
		CV_Error(CV_StsError, "Fatal error, cannot read video. Try moving video file to sample directory.\n");
47
		return -1;
48
	}
49

  
50
	Mat img, downimg, fgmask, upfgmask, posterior, upposterior;
51

  
52
	bool first = true;
53
	namedWindow("posterior");
54
	namedWindow("fgmask");
55
	namedWindow("FG Segmentation");
56
	int i = 0;
57
	while (true)
58
	{
59
		std::stringstream txt;
60
		txt << "frame: ";
61
		txt << i++;
62

  
63
		cap >> img;
64
		putText(img,txt.str(),Point(20,40),FONT_HERSHEY_SIMPLEX,0.8,Scalar(1.0,0.0,0.0));
65

  
66
		resize(img,downimg,Size(160,120),0,0,INTER_NEAREST);   // Size(cols, rows) or Size(width,height)
67
		if (first)
68
		{
69
			fgbg->initializeType(downimg,0,255);
70
			first = false;
71
		}
72
		if (img.empty())
73
		{
74
			return 0;
75
		}
76
		(*fgbg)(downimg,fgmask);
77
		fgbg->updateBackgroundModel(Mat::zeros(120,160,CV_8U));
78
		fgbg->getPosteriorImage(posterior);
79
		resize(fgmask,upfgmask,Size(640,480),0,0,INTER_NEAREST);
80
		Mat coloredFG = Mat::zeros(480,640,CV_8UC3);
81
		coloredFG.setTo(Scalar(100,100,0),upfgmask);
82

  
83
		resize(posterior,upposterior,Size(640,480),0,0,INTER_NEAREST);
84
		imshow("posterior",upposterior);
85
		imshow("fgmask",upfgmask);
86
		imshow("FG Segmentation",img+coloredFG);
87
		if (waitKey(2) == 'q')
88
			break;
89
	}
90

  
91
}
92

  
modules/video/include/opencv2/video/background_segm.hpp (working copy)
44 44
#define __OPENCV_BACKGROUND_SEGM_HPP__
45 45

  
46 46
#include "opencv2/core/core.hpp"
47

  
47
#include <list>
48 48
namespace cv
49 49
{
50 50

  
......
189 189
    //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
190 190
    //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
191 191
};	    
192
    
192

  
193
/**
194
 * Background Subtractor module. Takes a series of images and returns a sequence of mask (8UC1)
195
 * images of the same size, where 255 indicates Foreground and 0 represents Background.
196
 * This class implements an algorithm described in "Visual Tracking of Human Visitors under
197
 * Variable-Lighting Conditions for a Responsive Audio Art Installation," A. Godbehere,
198
 * A. Matsukawa, K. Goldberg, American Control Conference, Montreal, June 2012.
199
 */
200
class CV_EXPORTS BackgroundSubtractorGMG: public cv::BackgroundSubtractor
201
{
202
private:
203
	/**
204
	 * 	A general flexible datatype.
205
	 *
206
	 * 	Used internally to enable background subtraction algorithm to be robust to any input Mat type.
207
	 * 	Datatype can be char, unsigned char, int, unsigned int, long int, float, or double.
208
	 */
209
	union flexitype{
210
		char c;
211
		uchar uc;
212
		int i;
213
		unsigned int ui;
214
		long int li;
215
		float f;
216
		double d;
217

  
218
		flexitype(){d = 0.0;}  //!< Default constructor, set all bits of the union to 0.
219
		flexitype(char cval){c = cval;} //!< Char type constructor
220

  
221
		bool operator ==(flexitype& rhs)
222
		{
223
			return d == rhs.d;
224
		}
225

  
226
		//! Char type assignment operator
227
		flexitype& operator =(char cval){
228
			if (this->c == cval){return *this;}
229
			c = cval; return *this;
230
		}
231
		flexitype(unsigned char ucval){uc = ucval;} //!< unsigned char type constructor
232

  
233
		//! unsigned char type assignment operator
234
		flexitype& operator =(unsigned char ucval){
235
			if (this->uc == ucval){return *this;}
236
			uc = ucval; return *this;
237
		}
238
		flexitype(int ival){i = ival;} //!< int type constructor
239
		//! int type assignment operator
240
		flexitype& operator =(int ival){
241
			if (this->i == ival){return *this;}
242
			i = ival; return *this;
243
		}
244
		flexitype(unsigned int uival){ui = uival;} //!< unsigned int type constructor
245

  
246
		//! unsigned int type assignment operator
247
		flexitype& operator =(unsigned int uival){
248
			if (this->ui == uival){return *this;}
249
			ui = uival; return *this;
250
		}
251
		flexitype(float fval){f = fval;} //!< float type constructor
252
		//! float type assignment operator
253
		flexitype& operator =(float fval){
254
			if (this->f == fval){return *this;}
255
			f = fval; return *this;
256
		}
257
		flexitype(long int lival){li = lival;} //!< long int type constructor
258
		//! long int type assignment operator
259
		flexitype& operator =(long int lival){
260
			if (this->li == lival){return *this;}
261
			li = lival; return *this;
262
		}
263

  
264
		flexitype(double dval){d=dval;} //!< double type constructor
265
		//! double type assignment operator
266
		flexitype& operator =(double dval){
267
			if (this->d == dval){return *this;}
268
		d = dval; return *this;
269
		}
270
	};
271
	/**
272
	 *	Used internally to represent a single feature in a histogram.
273
	 *	Feature is a color and an associated likelihood (weight in the histogram).
274
	 */
275
	struct HistogramFeatureGMG
276
	{
277
		/**
278
		 * Default constructor.
279
		 * Initializes likelihood of feature to 0, color remains uninitialized.
280
		 */
281
		HistogramFeatureGMG(){likelihood = 0.0;}
282

  
283
		/**
284
		 * Copy constructor.
285
		 * Required to use HistogramFeatureGMG in a std::vector
286
		 * @see operator =()
287
		 */
288
		HistogramFeatureGMG(const HistogramFeatureGMG& orig){
289
			color = orig.color; likelihood = orig.likelihood;
290
		}
291

  
292
		/**
293
		 * Assignment operator.
294
		 * Required to use HistogramFeatureGMG in a std::vector
295
		 */
296
		HistogramFeatureGMG& operator =(const HistogramFeatureGMG& orig){
297
			color = orig.color; likelihood = orig.likelihood; return *this;
298
		}
299

  
300
		/**
301
		 * Tests equality of histogram features.
302
		 * Equality is tested only by matching the color (feature), not the likelihood.
303
		 * This operator is used to look up an observed feature in a histogram.
304
		 */
305
		bool operator ==(HistogramFeatureGMG &rhs);
306

  
307
		//! Regardless of the image datatype, it is quantized and mapped to an integer and represented as a vector.
308
		vector<size_t>			color;
309

  
310
		//! Represents the weight of feature in the histogram.
311
		float 					likelihood;
312
		friend class PixelModelGMG;
313
	};
314

  
315
	/**
316
	 * 	Representation of the statistical model of a single pixel for use in the background subtraction
317
	 * 	algorithm.
318
	 */
319
	class PixelModelGMG
320
	{
321
	public:
322
		PixelModelGMG();
323
		virtual ~PixelModelGMG();
324

  
325
		/**
326
		 * 	Incorporate the last observed feature into the statistical model.
327
		 *
328
		 * 	@param learningRate	The adaptation parameter for the histogram. -1.0 to use default. Value
329
		 * 						should be between 0.0 and 1.0, the higher the value, the faster the
330
		 * 						adaptation. 1.0 is limiting case where fast adaptation means no memory.
331
		 */
332
		void 	insertFeature(double learningRate = -1.0);
333

  
334
		/**
335
		 * 	Set the feature last observed, to save before incorporating it into the statistical
336
		 * 	model with insertFeature().
337
		 *
338
		 * 	@param feature		The feature (color) just observed.
339
		 */
340
		void	setLastObservedFeature(BackgroundSubtractorGMG::HistogramFeatureGMG feature);
341
		/**
342
		 *	Set the upper limit for the number of features to store in the histogram. Use to adjust
343
		 *	memory requirements.
344
		 *
345
		 *	@param max			size_t representing the max number of features.
346
		 */
347
		void	setMaxFeatures(size_t max) {
348
			maxFeatures = max; histogram.resize(max); histogram.clear();
349
		}
350
		/**
351
		 * 	Normalize the histogram, so sum of weights of all features = 1.0
352
		 */
353
		void	normalizeHistogram();
354
		/**
355
		 * 	Return the weight of a feature in the histogram. If the feature is not represented in the
356
		 * 	histogram, the weight returned is 0.0.
357
		 */
358
		double 	getLikelihood(HistogramFeatureGMG f);
359
		PixelModelGMG& operator *=(const float &rhs);
360
		//friend class BackgroundSubtractorGMG;
361
		//friend class HistogramFeatureGMG;
362
	protected:
363
		size_t numFeatures;  //!< number of features in histogram
364
		size_t maxFeatures; //!< max allowable features in histogram
365
		std::list<HistogramFeatureGMG> histogram; //!< represents the histogram as a list of features
366
		HistogramFeatureGMG			   lastObservedFeature;
367
		//!< store last observed feature in case we need to add it to histogram
368
	};
369

  
370
public:
371
	BackgroundSubtractorGMG();
372
	virtual ~BackgroundSubtractorGMG();
373
	virtual AlgorithmInfo* info() const;
374

  
375
	/**
376
	 * Performs single-frame background subtraction and builds up a statistical background image
377
	 * model.
378
	 * @param image Input image
379
	 * @param fgmask Output mask image representing foreground and background pixels
380
	 */
381
	virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=-1.0);
382

  
383
	/**
384
	 * Validate parameters and set up data structures for appropriate image type. Must call before
385
	 * running on data.
386
	 * @param image One sample image from dataset
387
	 * @param min	minimum value taken on by pixels in image sequence. Usually 0
388
	 * @param max	maximum value taken on by pixels in image sequence. e.g. 1.0 or 255
389
	 */
390
	void	initializeType(InputArray image, flexitype min, flexitype max);
391
	/**
392
	 * Selectively update the background model. Only update background model for pixels identified
393
	 * as background.
394
	 * @param mask 	Mask image same size as images in sequence. Must be 8UC1 matrix, 255 for foreground
395
	 * and 0 for background.
396
	 */
397
	void	updateBackgroundModel(InputArray mask);
398
	/**
399
	 * Retrieve the greyscale image representing the probability that each pixel is foreground given
400
	 * the current estimated background model. Values are 0.0 (black) to 1.0 (white).
401
	 * @param img The 32FC1 image representing per-pixel probabilities that the pixel is foreground.
402
	 */
403
	void	getPosteriorImage(OutputArray img);
404

  
405
protected:
406
	//! Total number of distinct colors to maintain in histogram.
407
	int		maxFeatures;
408
	//! Set between 0.0 and 1.0, determines how quickly features are "forgotten" from histograms.
409
	double	learningRate;
410
	//! Number of frames of video to use to initialize histograms.
411
	int		numInitializationFrames;
412
	//! Number of discrete levels in each channel to be used in histograms.
413
	int		quantizationLevels;
414
	//! Prior probability that any given pixel is a background pixel. A sensitivity parameter.
415
	double	backgroundPrior;
416

  
417
	double	decisionThreshold; //!< value above which pixel is determined to be FG.
418
	int		smoothingRadius;  //!< smoothing radius, in pixels, for cleaning up FG image.
419

  
420
	flexitype maxVal, minVal;
421

  
422
	/*
423
	 * General Parameters
424
	 */
425
	size_t		imWidth;		//!< width of image.
426
	size_t		imHeight;		//!< height of image.
427
	size_t		numPixels;
428

  
429
	int					imageDepth;  //!< Depth of image, e.g. CV_8U
430
	unsigned int		numChannels; //!< Number of channels in image.
431

  
432
	bool	isDataInitialized;
433
	//!< After general parameters are set, data structures must be initialized.
434

  
435
	size_t	elemSize;  //!< store image mat element sizes
436
	size_t	elemSize1;
437

  
438
	/*
439
	 * Data Structures
440
	 */
441
	vector<PixelModelGMG>		pixels; //!< Probabilistic background models for each pixel in image.
442
	int							frameNum; //!< Frame number counter, used to count frames in training mode.
443
	Mat							posteriorImage;  //!< Posterior probability image.
444
	Mat							fgMaskImage;   //!< Foreground mask image.
445
};
446

  
447
bool initModule_BackgroundSubtractorGMG(void);
448

  
193 449
}
194 450

  
195 451
#endif
modules/video/src/bgfg_gmg.cpp (working copy)
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
//  By downloading, copying, installing or using the software you agree to this license.
6
//  If you do not agree to this license, do not download, install,
7
//  copy or use the software.
8
//
9
//
10
//                        	License Agreement
11
//
12
// Copyright (C) 2000, Intel Corporation, all rights reserved.
13
// Third party copyrights are property of their respective owners.
14
//
15
// Redistribution and use in source and binary forms, with or without modification,
16
// are permitted provided that the following conditions are met:
17
//
18
//   * Redistribution's of source code must retain the above copyright notice,
19
//     this list of conditions and the following disclaimer.
20
//
21
//   * Redistribution's in binary form must reproduce the above copyright notice,
22
//     this list of conditions and the following disclaimer in the documentation
23
//     and/or other materials provided with the distribution.
24
//
25
//   * The name of Intel Corporation may not be used to endorse or promote products
26
//     derived from this software without specific prior written permission.
27
//
28
// This software is provided by the copyright holders and contributors "as is" and
29
// any express or implied warranties, including, but not limited to, the implied
30
// warranties of merchantability and fitness for a particular purpose are disclaimed.
31
// In no event shall the Intel Corporation or contributors be liable for any direct,
32
// indirect, incidental, special, exemplary, or consequential damages
33
// (including, but not limited to, procurement of substitute goods or services;
34
// loss of use, data, or profits; or business interruption) however caused
35
// and on any theory of liability, whether in contract, strict liability,
36
// or tort (including negligence or otherwise) arising in any way out of
37
// the use of this software, even if advised of the possibility of such damage.
38
//
39
//M*/
40

  
41
/*
42
 * This class implements an algorithm described in "Visual Tracking of Human Visitors under
43
 * Variable-Lighting Conditions for a Responsive Audio Art Installation," A. Godbehere,
44
 * A. Matsukawa, K. Goldberg, American Control Conference, Montreal, June 2012.
45
 *
46
 * Prepared and integrated by Andrew B. Godbehere.
47
 */
48

  
49
#include "precomp.hpp"
50

  
51
using namespace std;
52

  
53
namespace cv
54
{
55

  
56
static Algorithm* createBackgroundSubtractorGMG()
57
{
58
    return new BackgroundSubtractorGMG;
59
}
60
static AlgorithmInfo sparseBayes_info("BackgroundSubtractor.GMG",
61
										createBackgroundSubtractorGMG);
62

  
63
BackgroundSubtractorGMG::BackgroundSubtractorGMG()
64
{
65
	/*
66
	 * Default Parameter Values. Override with algorithm "set" method.
67
	 */
68
	maxFeatures = 64;
69
	learningRate = 0.025;
70
	numInitializationFrames = 120;
71
	quantizationLevels = 16;
72
	backgroundPrior = 0.8;
73
	decisionThreshold = 0.8;
74
	smoothingRadius = 7;
75
}
76

  
77
bool initModule_BackgroundSubtractorGMG(void)
78
{
79
    Ptr<Algorithm> sb = createBackgroundSubtractorGMG();
80
    return sb->info() != 0;
81
}
82

  
83
AlgorithmInfo* BackgroundSubtractorGMG::info() const
84
{
85
    static volatile bool initialized = false;
86
    if( !initialized )
87
    {
88
        BackgroundSubtractorGMG obj;
89
        sparseBayes_info.addParam(obj, "maxFeatures", obj.maxFeatures,false,0,0,"Maximum number of features to store in histogram. Harsh enforcement of sparsity constraint.");
90
        sparseBayes_info.addParam(obj, "learningRate", obj.learningRate,false,0,0,"Adaptation rate of histogram. Close to 1, slow adaptation. Close to 0, fast adaptation, features forgotten quickly.");
91
        sparseBayes_info.addParam(obj, "initializationFrames", obj.numInitializationFrames,false,0,0,"Number of frames to use to initialize histograms of pixels.");
92
        sparseBayes_info.addParam(obj, "quantizationLevels", obj.quantizationLevels,false,0,0,"Number of discrete colors to be used in histograms. Up-front quantization.");
93
        sparseBayes_info.addParam(obj, "backgroundPrior", obj.backgroundPrior,false,0,0,"Prior probability that each individual pixel is a background pixel.");
94
        sparseBayes_info.addParam(obj, "smoothingRadius", obj.smoothingRadius,false,0,0,"Radius of smoothing kernel to filter noise from FG mask image.");
95
        sparseBayes_info.addParam(obj, "decisionThreshold", obj.decisionThreshold,false,0,0,"Threshold for FG decision rule. Pixel is FG if posterior probability exceeds threshold.");
96
        initialized = true;
97
    }
98
    return &sparseBayes_info;
99
}
100

  
101
void BackgroundSubtractorGMG::initializeType(InputArray _image,flexitype min, flexitype max)
102
{
103
	minVal = min;
104
	maxVal = max;
105

  
106
	if (minVal == maxVal)
107
	{
108
		CV_Error_(CV_StsBadArg,("minVal and maxVal cannot be the same."));
109
	}
110

  
111
	/*
112
	 * Parameter validation
113
	 */
114
	if (maxFeatures <= 0)
115
	{
116
		CV_Error_(CV_StsBadArg,
117
				("maxFeatures parameter must be 1 or greater. Instead, it is %d.",maxFeatures));
118
	}
119
	if (learningRate < 0.0 || learningRate > 1.0)
120
	{
121
		CV_Error_(CV_StsBadArg,
122
				("learningRate parameter must be in the range [0.0,1.0]. Instead, it is %f.",
123
				learningRate));
124
	}
125
	if (numInitializationFrames < 1)
126
	{
127
		CV_Error_(CV_StsBadArg,
128
				("numInitializationFrames must be at least 1. Instead, it is %d.",
129
						numInitializationFrames));
130
	}
131
	if (quantizationLevels < 1)
132
	{
133
		CV_Error_(CV_StsBadArg,
134
				("quantizationLevels must be at least 1 (preferably more). Instead it is %d.",
135
						quantizationLevels));
136
	}
137
	if (backgroundPrior < 0.0 || backgroundPrior > 1.0)
138
	{
139
		CV_Error_(CV_StsBadArg,
140
				("backgroundPrior must be a probability, between 0.0 and 1.0. Instead it is %f.",
141
						backgroundPrior));
142
	}
143

  
144
	/*
145
	 * Detect and accommodate the image depth
146
	 */
147
	Mat image = _image.getMat();
148
	imageDepth = image.depth();  // 32f, 8u, etc.
149
	numChannels = image.channels();
150

  
151
	/*
152
	 * Color quantization [0 | | | | max] --> [0 | | max]
153
	 *  (0) Use double as intermediary to convert all types to int.
154
	 *  (i) Shift min to 0,
155
	 * 	(ii) max/(num intervals) = factor.  x/factor * factor = quantized result, after integer operation.
156
	 */
157

  
158
	/*
159
	 * Data Structure Initialization
160
	 */
161
	Size imsize = image.size();
162
	imWidth = imsize.width;
163
	imHeight = imsize.height;
164
	numPixels = imWidth*imHeight;
165
	pixels.resize(numPixels);
166
	frameNum = 0;
167

  
168
	// used to iterate through matrix of type unknown at compile time
169
	elemSize = image.elemSize();
170
	elemSize1 = image.elemSize1();
171

  
172
	vector<PixelModelGMG>::iterator pixel;
173
	vector<PixelModelGMG>::iterator pixel_end = pixels.end();
174
	for (pixel = pixels.begin(); pixel != pixel_end; ++pixel)
175
	{
176
		pixel->setMaxFeatures(maxFeatures);
177
	}
178

  
179
	fgMaskImage = Mat::zeros(imHeight,imWidth,CV_8UC1);  // 8-bit unsigned mask. 255 for FG, 0 for BG
180
	posteriorImage = Mat::zeros(imHeight,imWidth,CV_32FC1);  // float for storing probabilities. Can be viewed directly with imshow.
181
	isDataInitialized = true;
182
}
183

  
184
void BackgroundSubtractorGMG::operator()(InputArray _image, OutputArray _fgmask, double newLearningRate)
185
{
186
	if (!isDataInitialized)
187
	{
188
		CV_Error(CV_StsError,"BackgroundSubstractorGMG has not been initialized. Call initialize() first.\n");
189
	}
190

  
191
	/*
192
	 * Update learning rate parameter, if desired
193
	 */
194
	if (newLearningRate != -1.0)
195
	{
196
		if (newLearningRate < 0.0 || newLearningRate > 1.0)
197
		{
198
			CV_Error(CV_StsOutOfRange,"Learning rate for Operator () must be between 0.0 and 1.0.\n");
199
		}
200
		this->learningRate = newLearningRate;
201
	}
202

  
203
	Mat image = _image.getMat();
204

  
205
	_fgmask.create(Size(imHeight,imWidth),CV_8U);
206
	fgMaskImage = _fgmask.getMat();  // 8-bit unsigned mask. 255 for FG, 0 for BG
207

  
208
	/*
209
	 * Iterate over pixels in image
210
	 */
211
	// grab data at each pixel (1,2,3 channels, int, float, etc.)
212
	// 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.
213
	// multiple channels do have sequential storage, use mat::elemSize() and mat::elemSize1()
214
	vector<PixelModelGMG>::iterator pixel;
215
	vector<PixelModelGMG>::iterator pixel_end = pixels.end();
216
	size_t i;
217
//#pragma omp parallel
218
	for (i = 0, pixel=pixels.begin(); pixel != pixel_end; ++i,++pixel)
219
	{
220
		HistogramFeatureGMG newFeature;
221
		newFeature.color.clear();
222
		for (size_t c = 0; c < numChannels; ++c)
223
		{
224
			/*
225
			 * Perform quantization. in each channel. (color-min)*(levels)/(max-min).
226
			 * Shifts min to 0 and scales, finally casting to an int.
227
			 */
228
			size_t quantizedColor;
229
			// pixel at data+elemSize*i. Individual channel c at data+elemSize*i+elemSize1*c
230
			if (imageDepth == CV_8U)
231
			{
232
				uchar *color = (uchar*)(image.data+elemSize*i+elemSize1*c);
233
				quantizedColor = (size_t)((double)(*color-minVal.uc)*quantizationLevels/(maxVal.uc-minVal.uc));
234
			}
235
			else if (imageDepth == CV_8S)
236
			{
237
				char *color = (char*)(image.data+elemSize*i+elemSize1*c);
238
				quantizedColor = (size_t)((double)(*color-minVal.c)*quantizationLevels/(maxVal.c-minVal.c));
239
			}
240
			else if (imageDepth == CV_16U)
241
			{
242
				unsigned int *color = (unsigned int*)(image.data+elemSize*i+elemSize1*c);
243
				quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui));
244
			}
245
			else if (imageDepth == CV_16S)
246
			{
247
				int *color = (int*)(image.data+elemSize*i+elemSize1*c);
248
				quantizedColor = (size_t)((double)(*color-minVal.i)*quantizationLevels/(maxVal.i-minVal.i));
249
			}
250
			else if (imageDepth == CV_32F)
251
			{
252
				float *color = (float*)image.data+elemSize*i+elemSize1*c;
253
				quantizedColor = (size_t)((double)(*color-minVal.ui)*quantizationLevels/(maxVal.ui-minVal.ui));
254
			}
255
			else if (imageDepth == CV_32S)
256
			{
257
				long int *color = (long int*)(image.data+elemSize*i+elemSize1*c);
258
				quantizedColor = (size_t)((double)(*color-minVal.li)*quantizationLevels/(maxVal.li-minVal.li));
259
			}
260
			else if (imageDepth == CV_64F)
261
			{
262
				double *color = (double*)image.data+elemSize*i+elemSize1*c;
263
				quantizedColor = (size_t)((double)(*color-minVal.d)*quantizationLevels/(maxVal.d-minVal.d));
264
			}
265
			newFeature.color.push_back(quantizedColor);
266
		}
267
		// now that the feature is ready for use, put it in the histogram
268

  
269
		if (frameNum > numInitializationFrames)  // typical operation
270
		{
271
			newFeature.likelihood = learningRate;
272
			/*
273
			 * (1) Query histogram to find posterior probability of feature under model.
274
			 */
275
			float likelihood = (float)pixel->getLikelihood(newFeature);
276

  
277
			// see Godbehere, Matsukawa, Goldberg (2012) for reasoning behind this implementation of Bayes rule
278
			float posterior = (likelihood*backgroundPrior)/(likelihood*backgroundPrior+(1-likelihood)*(1-backgroundPrior));
279

  
280
			/*
281
			 * (2) feed posterior probability into the posterior image
282
			 */
283
			int row,col;
284
			col = i%imWidth;
285
			row = (i-col)/imWidth;
286
			posteriorImage.at<float>(row,col) = (1.0-posterior);
287
		}
288
		pixel->setLastObservedFeature(newFeature);
289
	}
290
	/*
291
	 * (3) Perform filtering and threshold operations to yield final mask image.
292
	 *
293
	 * 2 options. First is morphological open/close as before. Second is "median filtering" which Jon Barron says is good to remove noise
294
	 */
295
	Mat thresholdedPosterior;
296
	threshold(posteriorImage,thresholdedPosterior,decisionThreshold,1.0,THRESH_BINARY);
297
	thresholdedPosterior.convertTo(fgMaskImage,CV_8U,255);  // convert image to integer space for further filtering and mask creation
298
	medianBlur(fgMaskImage,fgMaskImage,smoothingRadius);
299

  
300
	fgMaskImage.copyTo(_fgmask);
301

  
302
	++frameNum;  // keep track of how many frames we have processed
303
}
304

  
305
void BackgroundSubtractorGMG::getPosteriorImage(OutputArray _img)
306
{
307
	_img.create(Size(imWidth,imHeight),CV_32F);
308
	Mat img = _img.getMat();
309
	posteriorImage.copyTo(img);
310
}
311

  
312
void BackgroundSubtractorGMG::updateBackgroundModel(InputArray _mask)
313
{
314
	CV_Assert(_mask.size() == Size(imWidth,imHeight));  // mask should be same size as image
315

  
316
	Mat maskImg = _mask.getMat();
317
//#pragma omp parallel
318
	for (size_t i = 0; i < imHeight; ++i)
319
	{
320
//#pragma omp parallel
321
		for (size_t j = 0; j < imWidth; ++j)
322
		{
323
			if (frameNum <= numInitializationFrames + 1)
324
			{
325
				// insert previously observed feature into the histogram. -1.0 parameter indicates training.
326
				pixels[i*imWidth+j].insertFeature(-1.0);
327
				if (frameNum >= numInitializationFrames+1)  // training is done, normalize
328
				{
329
					pixels[i*imWidth+j].normalizeHistogram();
330
				}
331
			}
332
			// if mask is 0, pixel is identified as a background pixel, so update histogram.
333
			else if (maskImg.at<uchar>(i,j) == 0)
334
			{
335
				pixels[i*imWidth+j].insertFeature(learningRate); // updates the histogram for the next iteration.
336
			}
337
		}
338
	}
339
}
340

  
341
BackgroundSubtractorGMG::~BackgroundSubtractorGMG()
342
{
343

  
344
}
345

  
346
BackgroundSubtractorGMG::PixelModelGMG::PixelModelGMG()
347
{
348
	numFeatures = 0;
349
	maxFeatures = 0;
350
}
351

  
352
BackgroundSubtractorGMG::PixelModelGMG::~PixelModelGMG()
353
{
354

  
355
}
356

  
357
void BackgroundSubtractorGMG::PixelModelGMG::setLastObservedFeature(HistogramFeatureGMG f)
358
{
359
	this->lastObservedFeature = f;
360
}
361

  
362
double BackgroundSubtractorGMG::PixelModelGMG::getLikelihood(BackgroundSubtractorGMG::HistogramFeatureGMG f)
363
{
364
	std::list<HistogramFeatureGMG>::iterator feature = histogram.begin();
365
	std::list<HistogramFeatureGMG>::iterator feature_end = histogram.end();
366

  
367
	for (feature = histogram.begin(); feature != feature_end; ++feature)
368
	{
369
		// comparing only feature color, not likelihood. See equality operator for HistogramFeatureGMG
370
		if (f == *feature)
371
		{
372
			return feature->likelihood;
373
		}
374
	}
375

  
376
	return 0.0; // not in histogram, so return 0.
377
}
378

  
379
void BackgroundSubtractorGMG::PixelModelGMG::insertFeature(double learningRate)
380
{
381

  
382
	std::list<HistogramFeatureGMG>::iterator feature;
383
	std::list<HistogramFeatureGMG>::iterator swap_end;
384
	std::list<HistogramFeatureGMG>::iterator last_feature = histogram.end();
385
	/*
386
	 * If feature is in histogram already, add the weights, and move feature to front.
387
	 * If there are too many features, remove the end feature and push new feature to beginning
388
	 */
389
	if (learningRate == -1.0) // then, this is a training-mode update.
390
	{
391
		/*
392
		 * (1) Check if feature already represented in histogram
393
		 */
394
		lastObservedFeature.likelihood = 1.0;
395

  
396
		for (feature = histogram.begin(); feature != last_feature; ++feature)
397
		{
398
			if (lastObservedFeature == *feature)  // feature in histogram
399
			{
400
				feature->likelihood += lastObservedFeature.likelihood;
401
				// now, move feature to beginning of list and break the loop
402
				HistogramFeatureGMG tomove = *feature;
403
				histogram.erase(feature);
404
				histogram.push_front(tomove);
405
				return;
406
			}
407
		}
408
		if (numFeatures == maxFeatures)
409
		{
410
			histogram.pop_back(); // discard oldest feature
411
			histogram.push_front(lastObservedFeature);
412
		}
413
		else
414
		{
415
			histogram.push_front(lastObservedFeature);
416
			++numFeatures;
417
		}
418
	}
419
	else
420
	{
421
		/*
422
		 * (1) Scale entire histogram by scaling factor
423
		 * (2) Scale input feature.
424
		 * (3) Check if feature already represented. If so, simply add.
425
		 * (4) If feature is not represented, remove old feature, distribute weight evenly among existing features, add in new feature.
426
		 */
427
		*this *= (1.0-learningRate);
428
		lastObservedFeature.likelihood = learningRate;
429

  
430
		for (feature = histogram.begin(); feature != last_feature; ++feature)
431
		{
432
			if (lastObservedFeature == *feature)  // feature in histogram
433
			{
434
				lastObservedFeature.likelihood += feature->likelihood;
435
				histogram.erase(feature);
436
				histogram.push_front(lastObservedFeature);
437
				return;  // done with the update.
438
			}
439
		}
440
		if (numFeatures == maxFeatures)
441
		{
442
			histogram.pop_back(); // discard oldest feature
443
			histogram.push_front(lastObservedFeature);
444
			normalizeHistogram();
445
		}
446
		else
447
		{
448
			histogram.push_front(lastObservedFeature);
449
			++numFeatures;
450
		}
451
	}
452
}
453

  
454
BackgroundSubtractorGMG::PixelModelGMG& BackgroundSubtractorGMG::PixelModelGMG::operator *=(const float &rhs)
455
{
456
	/*
457
	 * Used to scale histogram by a constant factor
458
	 */
459
	list<HistogramFeatureGMG>::iterator feature;
460
	list<HistogramFeatureGMG>::iterator last_feature = histogram.end();
461
	for (feature = histogram.begin(); feature != last_feature; ++feature)
462
	{
463
		feature->likelihood *= rhs;
464
	}
465
	return *this;
466
}
467

  
468
void BackgroundSubtractorGMG::PixelModelGMG::normalizeHistogram()
469
{
470
	/*
471
	 * First, calculate the total weight in the histogram
472
	 */
473
	list<HistogramFeatureGMG>::iterator feature;
474
	list<HistogramFeatureGMG>::iterator last_feature = histogram.end();
475
	double total = 0.0;
476
	for (feature = histogram.begin(); feature != last_feature; ++feature)
477
	{
478
		total += feature->likelihood;
479
	}
480

  
481
	/*
482
	 * Then, if weight is not 0, divide every feature by the total likelihood to re-normalize.
483
	 */
484
	for (feature = histogram.begin(); feature != last_feature; ++feature)
485
	{
486
		if (total != 0.0)
487
			feature->likelihood /= total;
488
	}
489
}
490

  
491
bool BackgroundSubtractorGMG::HistogramFeatureGMG::operator ==(HistogramFeatureGMG &rhs)
492
{
493
	CV_Assert(color.size() == rhs.color.size());
494

  
495
	std::vector<size_t>::iterator color_a;
496
	std::vector<size_t>::iterator color_b;
497
	std::vector<size_t>::iterator color_a_end = this->color.end();
498
	std::vector<size_t>::iterator color_b_end = rhs.color.end();
499
	for (color_a = color.begin(),color_b =rhs.color.begin();color_a!=color_a_end;++color_a,++color_b)
500
	{
501
		if (*color_a != *color_b)
502
		{
503
			return false;
504
		}
505
	}
506
	return true;
507
}
508

  
509

  
510
}
511

  
modules/video/src/precomp.hpp (working copy)
52 52
#include "opencv2/imgproc/imgproc_c.h"
53 53
#include "opencv2/core/internal.hpp"
54 54

  
55
#include <list>
56
#include <stdint.h>
57

  
55 58
#ifdef HAVE_TEGRA_OPTIMIZATION
56 59
#include "opencv2/video/video_tegra.hpp"
57 60
#endif
modules/video/test/test_backgroundsubtractorGBH.cpp (working copy)
1
/*
2
 * BackgroundSubtractorGBH_test.cpp
3
 *
4
 *  Created on: Jun 14, 2012
5
 *      Author: andrewgodbehere
6
 */
7

  
8
#include "test_precomp.hpp"
9

  
10
using namespace cv;
11

  
12
class CV_BackgroundSubtractorTest : public cvtest::BaseTest
13
{
14
public:
15
	CV_BackgroundSubtractorTest();
16
protected:
17
	void run(int);
18
};
19

  
20
CV_BackgroundSubtractorTest::CV_BackgroundSubtractorTest()
21
{
22
}
23

  
24
/**
25
 * This test checks the following:
26
 * (i) BackgroundSubtractorGMG can operate with matrices of various types and sizes
27
 * (ii) Training mode returns empty fgmask
28
 * (iii) End of training mode, and anomalous frame yields every pixel detected as FG
29
 */
30
void CV_BackgroundSubtractorTest::run(int)
31
{
32
	int code = cvtest::TS::OK;
33
	RNG& rng = ts->get_rng();
34
	int type = ((unsigned int)rng)%7;  //!< pick a random type, 0 - 6, defined in types_c.h
35
	int channels = 1 + ((unsigned int)rng)%4;  //!< random number of channels from 1 to 4.
36
	int channelsAndType = CV_MAKETYPE(type,channels);
37
	int width = 2 + ((unsigned int)rng)%98; //!< Mat will be 2 to 100 in width and height
38
	int height = 2 + ((unsigned int)rng)%98;
39

  
40
	Ptr<BackgroundSubtractorGMG> fgbg =
41
			Algorithm::create<BackgroundSubtractorGMG>("BackgroundSubtractor.GMG");
42
	Mat fgmask;
43

  
44
	if (fgbg == NULL)
45
		CV_Error(CV_StsError,"Failed to create Algorithm\n");
46

  
47
	/**
48
	 * Set a few parameters
49
	 */
50
	fgbg->set("smoothingRadius",7);
51
	fgbg->set("decisionThreshold",0.7);
52
	fgbg->set("initializationFrames",120);
53

  
54
	/**
55
	 * Generate bounds for the values in the matrix for each type
56
	 */
57
	uchar maxuc,minuc = 0;
58
	char maxc,minc = 0;
59
	uint maxui,minui = 0;
60
	int maxi,mini = 0;
61
	long int maxli,minli = 0;
62
	float maxf,minf = 0.0;
63
	double maxd,mind = 0.0;
64

  
65
	/**
66
	 * Max value for simulated images picked randomly in upper half of type range
67
	 * Min value for simulated images picked randomly in lower half of type range
68
	 */
69
	if (type == CV_8U)
70
	{
71
		unsigned char half = UCHAR_MAX/2;
72
		maxuc = (unsigned char)rng.uniform(half+32,UCHAR_MAX);
73
		minuc = (unsigned char)rng.uniform(0,half-32);
74
	}
75
	else if (type == CV_8S)
76
	{
77
		char half = CHAR_MAX/2 + CHAR_MIN/2;
78
		maxc = (char)rng.uniform(half+32,CHAR_MAX);
79
		minc = (char)rng.uniform(CHAR_MIN,half-32);
80
	}
81
	else if (type == CV_16U)
82
	{
83
		uint half = UINT_MAX/2;
84
		maxui = (unsigned int)rng.uniform((int)half+32,UINT_MAX);
85
		minui = (unsigned int)rng.uniform(0,(int)half-32);
86
	}
87
	else if (type == CV_16S)
88
	{
89
		int half = INT_MAX/2 + INT_MIN/2;
90
		maxi = rng.uniform(half+32,INT_MAX);
91
		mini = rng.uniform(INT_MIN,half-32);
92
	}
93
	else if (type == CV_32S)
94
	{
95
		long int half = LONG_MAX/2 + LONG_MIN/2;
96
		maxli = rng.uniform((int)half+32,(int)LONG_MAX);
97
		minli = rng.uniform((int)LONG_MIN,(int)half-32);
98
	}
99
	else if (type == CV_32F)
100
	{
101
		float half = FLT_MAX/2.0 + FLT_MIN/2.0;
102
		maxf = rng.uniform(half+(float)32.0*FLT_EPSILON,FLT_MAX);
103
		minf = rng.uniform(FLT_MIN,half-(float)32.0*FLT_EPSILON);
104
	}
105
	else if (type == CV_64F)
106
	{
107
		double half = DBL_MAX/2.0 + DBL_MIN/2.0;
108
		maxd = rng.uniform(half+(double)32.0*DBL_EPSILON,DBL_MAX);
109
		mind = rng.uniform(DBL_MIN,half-(double)32.0*DBL_EPSILON);
110
	}
111

  
112
	Mat simImage = Mat::zeros(height,width,channelsAndType);
113
	const uint numLearningFrames = 120;
114
	for (uint i = 0; i < numLearningFrames; ++i)
115
	{
116
		/**
117
		 * Genrate simulated "image" for any type. Values always confined to upper half of range.
118
		 */
119
		if (type == CV_8U)
120
		{
121
			rng.fill(simImage,RNG::UNIFORM,(unsigned char)(minuc/2+maxuc/2),maxuc);
122
			if (i == 0)
123
				fgbg->initializeType(simImage,minuc,maxuc);
124
		}
125
		else if (type == CV_8S)
126
		{
127
			rng.fill(simImage,RNG::UNIFORM,(char)(minc/2+maxc/2),maxc);
128
			if (i==0)
129
				fgbg->initializeType(simImage,minc,maxc);
130
		}
131
		else if (type == CV_16U)
132
		{
133
			rng.fill(simImage,RNG::UNIFORM,(unsigned int)(minui/2+maxui/2),maxui);
134
			if (i==0)
135
				fgbg->initializeType(simImage,minui,maxui);
136
		}
137
		else if (type == CV_16S)
138
		{
139
			rng.fill(simImage,RNG::UNIFORM,(int)(mini/2+maxi/2),maxi);
140
			if (i==0)
141
				fgbg->initializeType(simImage,mini,maxi);
142
		}
143
		else if (type == CV_32F)
144
		{
145
			rng.fill(simImage,RNG::UNIFORM,(float)(minf/2.0+maxf/2.0),maxf);
146
			if (i==0)
147
				fgbg->initializeType(simImage,minf,maxf);
148
		}
149
		else if (type == CV_32S)
150
		{
151
			rng.fill(simImage,RNG::UNIFORM,(long int)(minli/2+maxli/2),maxli);
152
			if (i==0)
153
				fgbg->initializeType(simImage,minli,maxli);
154
		}
155
		else if (type == CV_64F)
156
		{
157
			rng.fill(simImage,RNG::UNIFORM,(double)(mind/2.0+maxd/2.0),maxd);
158
			if (i==0)
159
				fgbg->initializeType(simImage,mind,maxd);
160
		}
161

  
162
		/**
163
		 * Feed simulated images into background subtractor
164
		 */
165
		(*fgbg)(simImage,fgmask);
166
		Mat fullbg = Mat::zeros(Size(simImage.cols,simImage.rows),CV_8U);
167
		fgbg->updateBackgroundModel(fullbg);
168

  
169
		//! fgmask should be entirely background during training
170
		code = cvtest::cmpEps2( ts, fgmask, fullbg, 0, false, "The training foreground mask" );
171
		if (code < 0)
172
			ts->set_failed_test_info( code );
173
	}
174
	//! generate last image, distinct from training images
175
	if (type == CV_8U)
176
		rng.fill(simImage,RNG::UNIFORM,minuc,minuc);
177
	else if (type == CV_8S)
178
		rng.fill(simImage,RNG::UNIFORM,minc,minc);
179
	else if (type == CV_16U)
180
		rng.fill(simImage,RNG::UNIFORM,minui,minui);
181
	else if (type == CV_16S)
182
		rng.fill(simImage,RNG::UNIFORM,mini,mini);
183
	else if (type == CV_32F)
184
		rng.fill(simImage,RNG::UNIFORM,minf,minf);
185
	else if (type == CV_32S)
186
		rng.fill(simImage,RNG::UNIFORM,minli,minli);
187
	else if (type == CV_64F)
188
		rng.fill(simImage,RNG::UNIFORM,mind,mind);
189

  
190
	(*fgbg)(simImage,fgmask);
191
	//! now fgmask should be entirely foreground
192
	Mat fullfg = 255*Mat::ones(Size(simImage.cols,simImage.rows),CV_8U);
193
	code = cvtest::cmpEps2( ts, fgmask, fullfg, 255, false, "The final foreground mask" );
194
	if (code < 0)
195
	{
196
		ts->set_failed_test_info( code );
197
	}
198

  
199
}
200

  
201
TEST(VIDEO_BGSUBGMG, accuracy) { CV_BackgroundSubtractorTest test; test.safe_run(); }
modules/video/test/test_precomp.hpp (working copy)
9 9
#include "opencv2/imgproc/imgproc.hpp"
10 10
#include "opencv2/imgproc/imgproc_c.h"
11 11
#include "opencv2/video/tracking.hpp"
12
#include "opencv2/video/background_segm.hpp"
12 13
#include "opencv2/highgui/highgui.hpp"
13 14
#include <iostream>
14 15