1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
12 // Copyright (C) 2000, Intel Corporation, all rights reserved.
13 // Third party copyrights are property of their respective owners.
15 // Redistribution and use in source and binary forms, with or without modification,
16 // are permitted provided that the following conditions are met:
18 // * Redistribution's of source code must retain the above copyright notice,
19 // this list of conditions and the following disclaimer.
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.
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.
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.
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.
46 * Prepared and integrated by Andrew B. Godbehere.
49 #include "precomp.hpp"
51 cv::BackgroundSubtractorGMG::BackgroundSubtractorGMG()
54 * Default Parameter Values. Override with algorithm "set" method.
58 numInitializationFrames = 120;
59 quantizationLevels = 16;
60 backgroundPrior = 0.8;
61 decisionThreshold = 0.8;
65 cv::BackgroundSubtractorGMG::~BackgroundSubtractorGMG()
69 void cv::BackgroundSubtractorGMG::initialize(cv::Size frameSize, double min, double max)
72 CV_Assert(maxFeatures > 0);
73 CV_Assert(learningRate >= 0.0 && learningRate <= 1.0);
74 CV_Assert(numInitializationFrames >= 1);
75 CV_Assert(quantizationLevels >= 1 && quantizationLevels <= 255);
76 CV_Assert(backgroundPrior >= 0.0 && backgroundPrior <= 1.0);
81 frameSize_ = frameSize;
84 nfeatures_.create(frameSize_);
85 colors_.create(frameSize_.area(), maxFeatures);
86 weights_.create(frameSize_.area(), maxFeatures);
88 nfeatures_.setTo(cv::Scalar::all(0));
93 float findFeature(int color, const int* colors, const float* weights, int nfeatures)
95 for (int i = 0; i < nfeatures; ++i)
97 if (color == colors[i])
101 // not in histogram, so return 0.
105 void normalizeHistogram(float* weights, int nfeatures)
108 for (int i = 0; i < nfeatures; ++i)
113 for (int i = 0; i < nfeatures; ++i)
118 bool insertFeature(int color, float weight, int* colors, float* weights, int& nfeatures, int maxFeatures)
121 for (int i = 0; i < nfeatures; ++i)
123 if (color == colors[i])
125 // feature in histogram
126 weight += weights[i];
134 // move feature to beginning of list
136 ::memmove(colors + 1, colors, idx * sizeof(int));
137 ::memmove(weights + 1, weights, idx * sizeof(float));
142 else if (nfeatures == maxFeatures)
144 // discard oldest feature
146 ::memmove(colors + 1, colors, (nfeatures - 1) * sizeof(int));
147 ::memmove(weights + 1, weights, (nfeatures - 1) * sizeof(float));
154 colors[nfeatures] = color;
155 weights[nfeatures] = weight;
168 template <int cn> struct Quantization_
170 template <typename T>
171 static inline int apply(T val, double minVal, double maxVal, int quantizationLevels)
174 res |= static_cast<int>((val[0] - minVal) * quantizationLevels / (maxVal - minVal));
175 res |= static_cast<int>((val[1] - minVal) * quantizationLevels / (maxVal - minVal)) << 8;
176 res |= static_cast<int>((val[2] - minVal) * quantizationLevels / (maxVal - minVal)) << 16;
180 template <> struct Quantization_<1>
182 template <typename T>
183 static inline int apply(T val, double minVal, double maxVal, int quantizationLevels)
185 return static_cast<int>((val - minVal) * quantizationLevels / (maxVal - minVal));
188 template <typename T> struct Quantization
190 static int apply(const void* src_, int x, double minVal, double maxVal, int quantizationLevels)
192 const T* src = static_cast<const T*>(src_);
193 return Quantization_<cv::DataType<T>::channels>::apply(src[x], minVal, maxVal, quantizationLevels);
197 class GMG_LoopBody : public cv::ParallelLoopBody
200 GMG_LoopBody(const cv::Mat& frame, const cv::Mat& fgmask, const cv::Mat_<int>& nfeatures, const cv::Mat_<int>& colors, const cv::Mat_<float>& weights,
201 int maxFeatures, double learningRate, int numInitializationFrames, int quantizationLevels, double backgroundPrior, double decisionThreshold,
202 double maxVal, double minVal, int frameNum) :
203 frame_(frame), fgmask_(fgmask), nfeatures_(nfeatures), colors_(colors), weights_(weights),
204 maxFeatures_(maxFeatures), learningRate_(learningRate), numInitializationFrames_(numInitializationFrames),
205 quantizationLevels_(quantizationLevels), backgroundPrior_(backgroundPrior), decisionThreshold_(decisionThreshold),
206 maxVal_(maxVal), minVal_(minVal), frameNum_(frameNum)
210 void operator() (const cv::Range& range) const;
213 const cv::Mat frame_;
215 mutable cv::Mat_<uchar> fgmask_;
217 mutable cv::Mat_<int> nfeatures_;
218 mutable cv::Mat_<int> colors_;
219 mutable cv::Mat_<float> weights_;
222 double learningRate_;
223 int numInitializationFrames_;
224 int quantizationLevels_;
225 double backgroundPrior_;
226 double decisionThreshold_;
233 void GMG_LoopBody::operator() (const cv::Range& range) const
235 typedef int (*func_t)(const void* src_, int x, double minVal, double maxVal, int quantizationLevels);
236 static const func_t funcs[6][4] =
238 {Quantization<uchar>::apply, 0, Quantization<cv::Vec3b>::apply, Quantization<cv::Vec4b>::apply},
240 {Quantization<ushort>::apply, 0, Quantization<cv::Vec3w>::apply, Quantization<cv::Vec4w>::apply},
243 {Quantization<float>::apply, 0, Quantization<cv::Vec3f>::apply, Quantization<cv::Vec4f>::apply},
246 const func_t func = funcs[frame_.depth()][frame_.channels() - 1];
247 CV_Assert(func != 0);
249 for (int y = range.start, featureIdx = y * frame_.cols; y < range.end; ++y)
251 const uchar* frame_row = frame_.ptr(y);
252 int* nfeatures_row = nfeatures_[y];
253 uchar* fgmask_row = fgmask_[y];
255 for (int x = 0; x < frame_.cols; ++x, ++featureIdx)
257 int nfeatures = nfeatures_row[x];
258 int* colors = colors_[featureIdx];
259 float* weights = weights_[featureIdx];
261 int newFeatureColor = func(frame_row, x, minVal_, maxVal_, quantizationLevels_);
263 bool isForeground = false;
265 if (frameNum_ >= numInitializationFrames_)
269 const double weight = findFeature(newFeatureColor, colors, weights, nfeatures);
271 // see Godbehere, Matsukawa, Goldberg (2012) for reasoning behind this implementation of Bayes rule
272 const double posterior = (weight * backgroundPrior_) / (weight * backgroundPrior_ + (1.0 - weight) * (1.0 - backgroundPrior_));
274 isForeground = ((1.0 - posterior) > decisionThreshold_);
278 for (int i = 0; i < nfeatures; ++i)
279 weights[i] *= 1.0f - learningRate_;
281 bool inserted = insertFeature(newFeatureColor, learningRate_, colors, weights, nfeatures, maxFeatures_);
285 normalizeHistogram(weights, nfeatures);
286 nfeatures_row[x] = nfeatures;
291 // training-mode update
293 insertFeature(newFeatureColor, 1.0f, colors, weights, nfeatures, maxFeatures_);
295 if (frameNum_ == numInitializationFrames_ - 1)
296 normalizeHistogram(weights, nfeatures);
299 fgmask_row[x] = (uchar)(-isForeground);
305 void cv::BackgroundSubtractorGMG::operator ()(InputArray _frame, OutputArray _fgmask, double newLearningRate)
307 cv::Mat frame = _frame.getMat();
309 CV_Assert(frame.depth() == CV_8U || frame.depth() == CV_16U || frame.depth() == CV_32F);
310 CV_Assert(frame.channels() == 1 || frame.channels() == 3 || frame.channels() == 4);
312 if (newLearningRate != -1.0)
314 CV_Assert(newLearningRate >= 0.0 && newLearningRate <= 1.0);
315 learningRate = newLearningRate;
318 if (frame.size() != frameSize_)
319 initialize(frame.size(), 0.0, frame.depth() == CV_8U ? 255.0 : frame.depth() == CV_16U ? std::numeric_limits<ushort>::max() : 1.0);
321 _fgmask.create(frameSize_, CV_8UC1);
322 cv::Mat fgmask = _fgmask.getMat();
324 GMG_LoopBody body(frame, fgmask, nfeatures_, colors_, weights_,
325 maxFeatures, learningRate, numInitializationFrames, quantizationLevels, backgroundPrior, decisionThreshold,
326 maxVal_, minVal_, frameNum_);
327 cv::parallel_for_(cv::Range(0, frame.rows), body);
329 cv::medianBlur(fgmask, buf_, smoothingRadius);
330 cv::swap(fgmask, buf_);
332 // keep track of how many frames we have processed