vpdt_update_mog.h
Go to the documentation of this file.
1 // This is core/vpdl/vpdt/vpdt_update_mog.h
2 #ifndef vpdt_update_mog_h_
3 #define vpdt_update_mog_h_
4 //:
5 // \file
6 // \author Matt Leotta (mleotta@lems.brown.edu)
7 // \date March 8, 2009
8 // \brief Iterative updating of a mixture of Gaussians
9 
10 #include <utility>
16 #include <vpdl/vpdt/vpdt_num_obs.h>
17 #ifdef _MSC_VER
18 # include <vcl_msvc_warnings.h>
19 #endif
20 #include <cassert>
21 
22 //: A mixture of Gaussians updater
23 // Base class for common functionality in adaptive updating schemes
24 template <class mog_type>
26 {
27  public:
28  typedef mog_type distribution_type;
29  typedef typename mog_type::field_type field_type;
30 
31  private:
32  typedef typename mog_type::component_type gaussian_type;
33  typedef typename gaussian_type::field_type F;
35 
36  protected:
37  //: Constructor
38  vpdt_mog_updater(const gaussian_type& init_gaussian,
39  unsigned int max_cmp = 5)
40  : init_gaussian_(init_gaussian), max_components_(max_cmp) {}
41 
42  //: insert a sample in the mixture
43  // \param sample A Gaussian is inserted with a mean of \a sample and a
44  // covariance from \a init_gaussian_
45  // \param init_weight The normalized weight the resulting sample
46  // should have after insertion
47  void insert(mog_type& mixture, const F& sample, T init_weight) const
48  {
49  // Ensure that the number of components does not exceed the maximum.
50  // Remove the last component if necessary to make room for the new one.
51  // Components should be sorted such that the last component is least important.
52  while (mixture.num_components() >= max_components_)
53  {
54  mixture.remove_last();
55  }
56 
57  // this correction accounts for the fact that the remaining components
58  // are not normalized to 1-init_weight
59  if (mixture.num_components() > 0)
60  init_weight /= (1-init_weight)*mixture.norm_const();
61  else
62  init_weight = T(1);
63 
64  // the mean is moved to the sample point (initial covariance is fixed)
65  init_gaussian_.mean = sample;
66  mixture.insert(init_gaussian_,init_weight);
67  }
68 
69  //: Return the index of the first Gaussian within the threshold distance
70  // The threshold \a gt2 is on the square distance.
71  // The computed square distance is returned by reference in \a sqr_dist
72  // If there are no matches return the number of components (last index + 1)
73  unsigned int match( const mog_type& mix, const F& sample,
74  const T& gt2, T& sqr_dist ) const
75  {
76  const unsigned int mix_nc = mix.num_components();
77  for (unsigned int i=0; i<mix_nc; ++i) {
78  const gaussian_type& g = mix.distribution(i);
79  sqr_dist = g.sqr_mahal_dist(sample);
80  if (sqr_dist < gt2)
81  return i;
82  }
83  return mix_nc;
84  }
85 
86  //: A model for new Gaussians inserted
88  //: The maximum number of components in the mixture
89  unsigned int max_components_;
90 };
91 
92 
93 //: A mixture of Gaussians adaptive updater based on Stauffer-Grimson.
94 // Using the S-G approximation to prior probabilities
95 // This algorithm is based on: C. Stauffer and W.E.L. Grimson,
96 // "Adaptive background mixture models for real-time tracking", CVPR 1999
97 template <class mog_type>
98 class vpdt_mog_sg_updater : public vpdt_mog_updater<mog_type>
99 {
100  public:
101  typedef typename mog_type::component_type gaussian_type;
102  typedef typename gaussian_type::field_type F;
104 
105  //: Constructor
106  vpdt_mog_sg_updater(const gaussian_type& init_gaussian,
107  unsigned int max_cmp = 5,
108  T g_thresh = T(2.5),
109  T alpha = T(0.1),
110  T init_weight = T(0.1),
111  T min_stdev = T(0.16) )
112  : vpdt_mog_updater<mog_type>(init_gaussian, max_cmp),
113  gt2_(g_thresh*g_thresh), alpha_(alpha), init_weight_(init_weight),
114  min_var_(min_stdev*min_stdev) {}
115 
116  //: The main function
117  void operator() ( mog_type& mix, const F& sample ) const
118  {
119  this->update(mix, sample, alpha_);
120  }
121 
122  protected:
123 
124  //: Update the mixture with \a sample using learning rate \a alpha
125  void update( mog_type& mix, const F& sample, T alpha ) const
126  {
127  T sqr_dist;
128  unsigned int i = vpdt_mog_updater<mog_type>::match(mix,sample,gt2_,sqr_dist);
129  if (i<mix.num_components())
130  {
131  gaussian_type& g = mix.distribution(i);
132  // unlike the original paper, the normal distribution here is unnormalized
133  // if normalized, rho can exceed 1 leading to divergence
134  T rho = alpha * std::exp(-sqr_dist/2);
135  if (min_var_ > T(0))
136  vpdt_update_gaussian(g, rho, sample, min_var_);
137  else
138  vpdt_update_gaussian(g, rho, sample);
139  // this is equivalent to
140  // w_j = (1-alpha)*w_j + alpha*(j==i?1:0) for all j, but without normalization
141  mix.set_weight(i, mix.weight(i) + alpha/((1-alpha)*mix.norm_const()));
142  }
143  else
144  {
146  }
148  }
149 
150  //: Squared Gaussian Mahalanobis distance threshold
152  //: The learning rate
154  //: The initial weight for added Gaussians
156  //: Minimum variance allowed in each Gaussian component
158 };
159 
160 
161 //: A mixture of Gaussians adaptive updater based on D.-S. Lee.
162 // Modification of the S-G method. Each Gaussian has its own learning rate
163 // that adjusts with the number of observations.
164 // This algorithm requires that the Gaussians in the mixture are wrapped
165 // in a vpdt_num_obs class to count the number of observations.
166 // This algorithm is based on: D.-S. Lee.
167 // "Effective gaussian mixture learning for video background subtraction",
168 // PAMI, 27:827--832, May 2005.
169 template <class mog_type>
170 class vpdt_mog_lee_updater : public vpdt_mog_updater<mog_type>
171 {
172  public:
173  typedef typename mog_type::component_type gaussian_type;
174  typedef typename gaussian_type::field_type F;
176 
177  //: Constructor
178  vpdt_mog_lee_updater(const gaussian_type& init_gaussian,
179  unsigned int max_cmp = 5,
180  T g_thresh = T(2.5),
181  T min_stdev = T(0.16),
182  bool use_wta = false )
183  : vpdt_mog_updater<mog_type>(init_gaussian, max_cmp),
184  gt2_(g_thresh*g_thresh), min_var_(min_stdev*min_stdev),
185  use_winner_take_all_(use_wta) {}
186 
187  //: The main function
188  void operator() ( mog_type& mix, const F& sample ) const
189  {
190  T num_obs = total_num_obs(mix) + 1;
191  this->update(mix, sample, T(1)/num_obs);
192  }
193 
194  protected:
195 
196  //: Count the total number of observations in all components
197  T total_num_obs( const mog_type& mix ) const
198  {
199  T num_obs = T(0);
200  const unsigned int mix_nc = mix.num_components();
201  for (unsigned int i=0; i<mix_nc; ++i) {
202  const gaussian_type& g = mix.distribution(i);
203  num_obs += g.num_observations;
204  }
205  return num_obs;
206  }
207 
208  //: find all matches within the \c gt2_ threshold and compute the probability of each
209  std::vector<std::pair<unsigned int,double> >
210  matches(const mog_type& mix, const F& sample) const
211  {
212  const unsigned int mix_nc = mix.num_components();
213  double sum_p = 0.0;
214  std::vector<std::pair<unsigned int,double> > matchez;
215  // find the square distance to all components, count those below gt2_
216  for (unsigned int i=0; i<mix_nc; ++i) {
217  const gaussian_type& g = mix.distribution(i);
218  double sqr_dist = g.sqr_mahal_dist(sample);
219  if (sqr_dist < gt2_)
220  matchez.push_back(std::pair<unsigned int,double>(i,sqr_dist));
221  }
222  // if only one match, it has prob 1
223  if (matchez.size() == 1) {
224  matchez[0].second = 1.0;
225  }
226  // find the probability of each match
227  else if (matchez.size() > 1) {
228  for (unsigned int j=0; j<matchez.size(); ++j) {
229  unsigned int& i = matchez[j].first;
230  double& p = matchez[j].second;
231  const gaussian_type& g = mix.distribution(i);
232  p = mix.weight(i) * g.norm_const() * std::exp(-p/2);
233  sum_p += p;
234  }
235  // normalize
236  for (unsigned int j=0; j<matchez.size(); ++j) {
237  matchez[j].second /= sum_p;
238  }
239  }
240  return matchez;
241  }
242 
243  //: Apply a winner-take-all strategy to the matches.
244  // Keep only the highest probability match and assign it probability 1
245  void winner_take_all(std::vector<std::pair<unsigned int,double> >& m) const
246  {
247  double max_p = m[0].second;
248  unsigned int max_j = 0;
249  for (unsigned int j=1; j<m.size(); ++j) {
250  if (m[j].second > max_p) {
251  max_p = m[j].second;
252  max_j = j;
253  }
254  }
255  m[0].first = m[max_j].first;
256  m[0].second = 1.0;
257  m.resize(1);
258  }
259 
260  //: Update the mixture with \a sample using learning rate \a alpha
261  void update( mog_type& mix, const F& sample, T alpha ) const
262  {
263  const unsigned int mix_nc = mix.num_components();
264  if (mix_nc == 0) {
265  insert(mix,sample,alpha);
266  return;
267  }
268  // find all matching components (sqr dist < gt2_) and their probabilites
269  std::vector<std::pair<unsigned int,double> > m = matches(mix, sample);
270 
271  if (!m.empty())
272  {
273  if (use_winner_take_all_ && m.size() > 1)
275  T w_inc = alpha / ((T(1)-alpha)*mix.norm_const());
276  for (unsigned int j=0; j<m.size(); ++j) {
277  unsigned int i = m[j].first;
278  double p = m[j].second;
279  gaussian_type& g = mix.distribution(i);
280  g.num_observations += T(p);
281  T rho = (T(1)-alpha)/g.num_observations + alpha;
282  rho *= p;
283  if (min_var_ > T(0))
284  vpdt_update_gaussian(g, rho, sample, min_var_);
285  else
286  vpdt_update_gaussian(g, rho, sample);
287  // this is equivalent to
288  // w_j = (1-alpha)*w_j + alpha*(j==i?1:0) for all j, but without normalization
289  mix.set_weight(i, mix.weight(i)+p*w_inc);
290  }
291  }
292  else
293  {
294  insert(mix,sample,alpha);
295  }
297  }
298 
299  //: Squared Gaussian Mahalanobis distance threshold
301  //: Minimum variance allowed in each Gaussian component
303 
304  //: Use a winner-take_all strategy
306 };
307 
308 
309 //: A mixture of Gaussians adaptive updater base on Leotta-Mundy
310 // Combines the greedy matching of the S-G method for speed with the
311 // dynamic learning rate of Lee for faster learning.
312 // Unnormalized weights serve a dual role with observation counts.
313 // This algorithm is based on: M. Leotta and J. Mundy,
314 // "Learning background and shadow appearance with 3-d vehicle models",
315 // BMVC, 2:649--658, September 2006.
316 template <class mog_type>
317 class vpdt_mog_lm_updater : public vpdt_mog_updater<mog_type>
318 {
319  public:
320  typedef typename mog_type::component_type gaussian_type;
321  typedef typename gaussian_type::field_type F;
323 
324  //: Constructor
325  vpdt_mog_lm_updater(const gaussian_type& init_gaussian,
326  unsigned int max_cmp = 5,
327  T g_thresh = T(2.5),
328  T min_stdev = T(0.16),
329  unsigned int ws = 300)
330  : vpdt_mog_updater<mog_type>(init_gaussian, max_cmp),
331  gt2_(g_thresh*g_thresh), min_var_(min_stdev*min_stdev),
332  window_size_(ws) {}
333 
334  //: The main function
335  void operator() ( mog_type& mix, const F& sample, T sample_weight = T(1) ) const
336  {
337  this->update(mix, sample, sample_weight);
338  }
339 
340  protected:
341 
342  //: Count the total mixture weight
343  T total_weight( const mog_type& mix ) const
344  {
345  T tw = T(0);
346  const unsigned int mix_nc = mix.num_components();
347  for (unsigned int i=0; i<mix_nc; ++i) {
348  tw += mix.weight(i);
349  }
350  return tw;
351  }
352 
353  //: Update the mixture with \a sample using learning rate \a alpha
354  void update( mog_type& mix, const F& sample, T samp_weight ) const
355  {
356  assert(samp_weight > 0.0);
357  T tw = total_weight(mix);
358  tw += samp_weight;
359 
360  T alpha = 1/tw;
361  T sqr_dist;
362  unsigned int i = this->match(mix,sample,gt2_,sqr_dist);
363  if (i<mix.num_components())
364  {
365  gaussian_type& g = mix.distribution(i);
366  T w = mix.weight(i);
367  w += samp_weight;
368  T rho = (T(1)-alpha)/w + alpha;
369  if (min_var_ > T(0))
370  vpdt_update_gaussian(g, rho, sample, min_var_);
371  else
372  vpdt_update_gaussian(g, rho, sample);
373  mix.set_weight(i,w);
374  }
375  else
376  {
377  this->insert(mix,sample,alpha);
378  }
379  // scale down weights for a moving window effect
380  if (tw > window_size_) {
381  T scale = window_size_ / tw;
382  const unsigned int mix_nc = mix.num_components();
383  for (unsigned int i=0; i<mix_nc; ++i) {
384  mix.set_weight(i, mix.weight(i)*scale);
385  }
386  }
388  }
389 
390  //: Squared Gaussian Mahalanobis distance threshold
392  //: Minimum variance allowed in each Gaussian component
394  //: The moving window size in number of frames
395  unsigned int window_size_;
396 };
397 
398 
399 #endif // vpdt_update_mog_h_
mog_type::component_type gaussian_type
mog_type::component_type gaussian_type
T gt2_
Squared Gaussian Mahalanobis distance threshold.
vpdt_field_traits< F >::scalar_type T
void winner_take_all(std::vector< std::pair< unsigned int, double > > &m) const
Apply a winner-take-all strategy to the matches.
void operator()(mog_type &mix, const F &sample) const
The main function.
gaussian_type::field_type F
T total_weight(const mog_type &mix) const
Count the total mixture weight.
gaussian_type::field_type F
T min_var_
Minimum variance allowed in each Gaussian component.
Iterative updating of Gaussians.
gaussian_type init_gaussian_
A model for new Gaussians inserted.
#define m
T init_weight_
The initial weight for added Gaussians.
std::vector< std::pair< unsigned int, double > > matches(const mog_type &mix, const F &sample) const
find all matches within the gt2_ threshold and compute the probability of each.
A mixture of Gaussians adaptive updater base on Leotta-Mundy.
T gt2_
Squared Gaussian Mahalanobis distance threshold.
vpdt_mog_updater(const gaussian_type &init_gaussian, unsigned int max_cmp=5)
Constructor.
gaussian_type::field_type F
T alpha_
The learning rate.
vpdt_field_traits< F >::scalar_type T
A mixture of Gaussians updater.
vpdt_field_traits< F >::scalar_type T
void vpdt_update_gaussian(vpdt_gaussian< F, Covar > &gaussian, typename vpdt_field_traits< F >::scalar_type rho, const F &sample)
Update the statistics given a 1D Gaussian distribution and a learning rate.
void insert(mog_type &mixture, const F &sample, T init_weight) const
insert a sample in the mixture.
mog_type::field_type field_type
vpdt_field_traits< F >::scalar_type T
mog_type::component_type gaussian_type
Attach a "number of observations" variable to any distribution.
T min_var_
Minimum variance allowed in each Gaussian component.
A mixture of Gaussians adaptive updater based on Stauffer-Grimson.
The Stauffer-Grimson ordering function of mixture component fitness.
void update(mog_type &mix, const F &sample, T samp_weight) const
Update the mixture with sample using learning rate alpha.
vpdt_mog_sg_updater(const gaussian_type &init_gaussian, unsigned int max_cmp=5, T g_thresh=T(2.5), T alpha=T(0.1), T init_weight=T(0.1), T min_stdev=T(0.16))
Constructor.
vpdt_mog_lee_updater(const gaussian_type &init_gaussian, unsigned int max_cmp=5, T g_thresh=T(2.5), T min_stdev=T(0.16), bool use_wta=false)
Constructor.
void update(mog_type &mix, const F &sample, T alpha) const
Update the mixture with sample using learning rate alpha.
A generic Gaussian distribution.
void operator()(mog_type &mix, const F &sample, T sample_weight=T(1)) const
The main function.
bool use_winner_take_all_
Use a winner-take_all strategy.
mog_type::component_type gaussian_type
unsigned int max_components_
The maximum number of components in the mixture.
specialized template trait classes for properties of a field type
A mixture of Gaussians adaptive updater based on D.
void operator()(mog_type &mix, const F &sample) const
The main function.
T gt2_
Squared Gaussian Mahalanobis distance threshold.
vpdt_mog_lm_updater(const gaussian_type &init_gaussian, unsigned int max_cmp=5, T g_thresh=T(2.5), T min_stdev=T(0.16), unsigned int ws=300)
Constructor.
The field traits class (scalar).
T min_var_
Minimum variance allowed in each Gaussian component.
A mixture of a fixed type of distributions.
Ordering functions for sorting mixtures of Gaussian components.
T total_num_obs(const mog_type &mix) const
Count the total number of observations in all components.
mog_type distribution_type
gaussian_type::field_type F
unsigned int window_size_
The moving window size in number of frames.
void update(mog_type &mix, const F &sample, T alpha) const
Update the mixture with sample using learning rate alpha.
unsigned int match(const mog_type &mix, const F &sample, const T &gt2, T &sqr_dist) const
Return the index of the first Gaussian within the threshold distance.