bgfggmg.diff
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 |