contrib/mul/vpdfl/vpdfl_gaussian_builder.cxx
Go to the documentation of this file.
00001 // This is mul/vpdfl/vpdfl_gaussian_builder.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \brief Multi-variate gaussian PDF with arbitrary axes.
00008 // \author Tim Cootes
00009 // \date 16-Oct-98
00010 //
00011 // Modifications
00012 // \verbatim
00013 //    IMS   Converted to VXL 18 April 2000
00014 // \endverbatim
00015 
00016 #include "vpdfl_gaussian_builder.h"
00017 
00018 #include <vcl_cstdlib.h>
00019 #include <vcl_string.h>
00020 #include <vcl_cassert.h>
00021 #include <mbl/mbl_data_wrapper.h>
00022 #include <vpdfl/vpdfl_gaussian.h>
00023 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00024 
00025 #include <mbl/mbl_parse_block.h>
00026 #include <mbl/mbl_read_props.h>
00027 #include <vul/vul_string.h>
00028 #include <mbl/mbl_exception.h>
00029 
00030 // Weights smaller than this are assumed to be zero
00031 const double min_wt = 1e-8;
00032 
00033 //=======================================================================
00034 // Dflt ctor
00035 //=======================================================================
00036 
00037 vpdfl_gaussian_builder::vpdfl_gaussian_builder()
00038   : min_var_(1.0e-6)
00039 {
00040 }
00041 
00042 //=======================================================================
00043 // Destructor
00044 //=======================================================================
00045 
00046 vpdfl_gaussian_builder::~vpdfl_gaussian_builder()
00047 {
00048 }
00049 
00050 //=======================================================================
00051 
00052 vpdfl_gaussian& vpdfl_gaussian_builder::gaussian(vpdfl_pdf_base& model) const
00053 {
00054     // need a vpdfl_gaussian
00055   assert(model.is_class("vpdfl_gaussian"));
00056   return static_cast<vpdfl_gaussian&>(model);
00057 }
00058 //=======================================================================
00059 
00060 vpdfl_pdf_base* vpdfl_gaussian_builder::new_model() const
00061 {
00062   return new vpdfl_gaussian;
00063 }
00064 
00065 //=======================================================================
00066 //: Define lower threshold on variance for built models
00067 //=======================================================================
00068 void vpdfl_gaussian_builder::set_min_var(double min_var)
00069 {
00070   min_var_ = min_var;
00071 }
00072 
00073 //=======================================================================
00074 //: Get lower threshold on variance for built models
00075 //=======================================================================
00076 double vpdfl_gaussian_builder::min_var() const
00077 {
00078   return min_var_;
00079 }
00080 
00081 //=======================================================================
00082 
00083 void vpdfl_gaussian_builder::build(vpdfl_pdf_base& model,
00084                                    const vnl_vector<double>& mean) const
00085 {
00086   vpdfl_gaussian& g = gaussian(model);
00087   int n = mean.size();
00088 
00089   vnl_vector<double> var(n);
00090   for (int i=0;i<n;i++) var(i)=min_var_;
00091 
00092   // Generate an identity matrix for eigenvectors
00093   vnl_matrix<double> P(n,n);
00094   for (int i=0;i<n;++i)
00095     for (int j=0;j<n;++j) P(i,j) = 0.0;
00096 
00097   for (int i=0;i<n;++i) P(i,i) = 1.0;
00098 
00099   g.set(mean,var,P,var);
00100 }
00101 //=======================================================================
00102 
00103 void vpdfl_gaussian_builder::updateCovar(vnl_matrix<double>& S,
00104                                          const vnl_vector<double>& vec,
00105                                          double w) const
00106 {
00107   unsigned int n = vec.size();
00108   const double *v = vec.data_block();
00109   if (S.rows()!=n)
00110   {
00111     S.set_size(n,n);
00112     double **S_data = S.data_array();
00113     for (unsigned int i=0; i<n; ++i)
00114       for (unsigned int j=0; j<n; ++j)
00115         S_data[j][i] = w*v[i]*v[j];
00116   }
00117   else
00118   {
00119     double **S_data = S.data_array();
00120     double * S_row;
00121     for (unsigned int i=0; i<n; ++i)
00122     {
00123       S_row = S_data[i];
00124       double vw = w*v[i];
00125       for (unsigned int j=0; j<n; ++j)
00126         S_row[j] += vw*v[j];
00127     }
00128   }
00129 }
00130 //=======================================================================
00131 
00132 //: Build model from mean and covariance
00133 void vpdfl_gaussian_builder::buildFromCovar(vpdfl_gaussian& g,
00134                                             const vnl_vector<double>& mean,
00135                                             const vnl_matrix<double>& S) const
00136 {
00137   unsigned int n = mean.size();
00138   vnl_matrix<double> evecs(S.rows(), S.rows());
00139   vnl_vector<double> evals(S.rows());
00140 
00141 
00142   vnl_symmetric_eigensystem_compute(S, evecs, evals);
00143   // eigenvalues are lowest first here
00144   evals.flip();
00145   evecs.fliplr();
00146   // eigenvalues are highest first now
00147 
00148   // Apply threshold to variance
00149   double *ev = evals.data_block();
00150   for (unsigned int i=0;i<n;++i)
00151     if (ev[i]<min_var_) ev[i]=min_var_;
00152 
00153   g.set(mean,evecs,evals);
00154 }
00155 
00156 //=======================================================================
00157 
00158 void vpdfl_gaussian_builder::build(vpdfl_pdf_base& model,
00159                                    mbl_data_wrapper<vnl_vector<double> >& data) const
00160 {
00161   vpdfl_gaussian& g = gaussian(model);
00162 
00163   assert(data.size() >= 2L); // Not enough examples available
00164 
00165   vnl_vector<double> mean;
00166   vnl_matrix<double> S;
00167 
00168   meanCovar(mean,S,data);
00169   buildFromCovar(g,mean,S);
00170 }
00171 //=======================================================================
00172 
00173 //: Computes mean and covariance of given data
00174 void vpdfl_gaussian_builder::meanCovar(vnl_vector<double>& mean, vnl_matrix<double>& S,
00175                                        mbl_data_wrapper<vnl_vector<double> >& data) const
00176 {
00177   unsigned long n_samples = data.size();
00178 
00179   assert(n_samples!=0L);
00180 
00181   data.reset();
00182   int n_dims = data.current().size();
00183   vnl_vector<double> sum(n_dims);
00184   sum.fill(0);
00185 
00186   S.set_size(0,0);
00187 
00188   data.reset();
00189   for (unsigned long i=0;i<n_samples;i++)
00190   {
00191     sum += data.current();
00192     updateCovar(S,data.current(),1.0);
00193 
00194     data.next();
00195   }
00196 
00197   mean = sum / (double) n_samples;
00198   updateCovar(S, mean, - (double)n_samples);
00199   S/=(n_samples-1);
00200 }
00201 
00202 //=======================================================================
00203 
00204 void vpdfl_gaussian_builder::weighted_build(vpdfl_pdf_base& model,
00205                                             mbl_data_wrapper<vnl_vector<double> >& data,
00206                                             const vcl_vector<double>& wts) const
00207 {
00208   vpdfl_gaussian& g = gaussian(model);
00209 
00210   unsigned long n_samples = data.size();
00211 
00212   assert(n_samples>=2L); // Need enough samples
00213 
00214   data.reset();
00215   int n_dims = data.current().size();
00216   vnl_vector<double> sum(n_dims);
00217   sum.fill(0);
00218   vnl_matrix<double> S;
00219   double w_sum = 0.0;
00220   unsigned actual_samples = 0;
00221 
00222   data.reset();
00223   for (unsigned long i=0;i<n_samples;i++)
00224   {
00225     double w = wts[i];
00226     if (w >= min_wt) actual_samples ++;
00227     w_sum += w;
00228     sum += w*data.current();
00229     updateCovar(S,data.current(),w);
00230 
00231     data.next();
00232   }
00233 
00234   if (w_sum/n_samples<min_wt)  // ie near zero
00235   {
00236     vcl_cerr<<"vpdfl_gaussian_builder::weighted_build() :\n"
00237             <<"Weights too close to zero. Sum = "<<w_sum<<vcl_endl;
00238     vcl_abort();
00239   }
00240 
00241   if (actual_samples==0)
00242   {
00243     vcl_cerr<<"vpdfl_gaussian_builder::weighted_build() :\nAll weights zero.\n";
00244     vcl_abort();
00245   }
00246 
00247   if (actual_samples==1)
00248   {
00249 #if 0
00250     vcl_cerr<<"vpdfl_gaussian_builder::weighted_build() :\n"
00251             <<" Warning: Only one sample has non-zero weight.\n";
00252 #endif
00253     // Build minimal model about the mean (the one non-zero sample)
00254     sum/=w_sum;
00255     build(g,sum);
00256     return;
00257   }
00258 
00259   S*=actual_samples/((actual_samples - 1) *w_sum);
00260   sum/=w_sum;
00261   updateCovar(S, sum, -1.0);  // Remove mean.mean'
00262   // now sum = weighted mean
00263   // and S = weighted covariance corrected for unbiased rather than ML result.
00264 
00265   buildFromCovar(g,sum,S);
00266 }
00267 //=======================================================================
00268 // Method: is_a
00269 //=======================================================================
00270 
00271 vcl_string vpdfl_gaussian_builder::is_a() const
00272 {
00273   static vcl_string class_name_ = "vpdfl_gaussian_builder";
00274   return class_name_;
00275 }
00276 
00277 //=======================================================================
00278 // Method: is_class
00279 //=======================================================================
00280 
00281 bool vpdfl_gaussian_builder::is_class(vcl_string const& s) const
00282 {
00283   return vpdfl_builder_base::is_class(s) || s==vpdfl_gaussian_builder::is_a();
00284 }
00285 
00286 //=======================================================================
00287 // Method: version_no
00288 //=======================================================================
00289 
00290 short vpdfl_gaussian_builder::version_no() const
00291 {
00292   return 1;
00293 }
00294 
00295 //=======================================================================
00296 // Method: clone
00297 //=======================================================================
00298 
00299 vpdfl_builder_base* vpdfl_gaussian_builder::clone() const
00300 {
00301   return new vpdfl_gaussian_builder(*this);
00302 }
00303 
00304 //=======================================================================
00305 // Method: print
00306 //=======================================================================
00307 
00308 void vpdfl_gaussian_builder::print_summary(vcl_ostream& os) const
00309 {
00310   os << "Min. var. : "<< min_var_;
00311 }
00312 
00313 //=======================================================================
00314 // Method: save
00315 //=======================================================================
00316 
00317 void vpdfl_gaussian_builder::b_write(vsl_b_ostream& bfs) const
00318 {
00319   vsl_b_write(bfs,is_a());
00320   vsl_b_write(bfs,version_no());
00321   vsl_b_write(bfs,min_var_);
00322 }
00323 
00324 //=======================================================================
00325 // Method: load
00326 //=======================================================================
00327 
00328 void vpdfl_gaussian_builder::b_read(vsl_b_istream& bfs)
00329 {
00330   if (!bfs) return;
00331 
00332   vcl_string name;
00333   vsl_b_read(bfs,name);
00334   if (name != is_a())
00335   {
00336     vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vpdfl_gaussian_builder &)\n"
00337              << "           Attempted to load object of type "
00338              << name <<" into object of type " << is_a() << vcl_endl;
00339     bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00340     return;
00341   }
00342 
00343   short version;
00344   vsl_b_read(bfs,version);
00345   switch (version)
00346   {
00347     case (1):
00348       vsl_b_read(bfs,min_var_);
00349       break;
00350     default:
00351       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vpdfl_gaussian_builder &)\n"
00352                << "           Unknown version number "<< version << vcl_endl;
00353       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00354       return;
00355   }
00356 }
00357 
00358 //: Read initialisation settings from a stream.
00359 // Parameters:
00360 // \verbatim
00361 // {
00362 //   min_var: 1.0e-6
00363 // }
00364 // \endverbatim
00365 // \throw mbl_exception_parse_error if the parse fails.
00366 void vpdfl_gaussian_builder::config_from_stream(vcl_istream & is)
00367 {
00368   vcl_string s = mbl_parse_block(is);
00369 
00370   vcl_istringstream ss(s);
00371   mbl_read_props_type props = mbl_read_props_ws(ss);
00372 
00373   double mv=1.0e-6;
00374 
00375   if (props.find("min_var")!=props.end())
00376   {
00377     mv=vul_string_atof(props["min_var"]);
00378     props.erase("min_var");
00379   }
00380   set_min_var(mv);
00381   try
00382   {
00383     mbl_read_props_look_for_unused_props(
00384         "vpdfl_gaussian_builder::config_from_stream", props);
00385   }
00386   catch(mbl_exception_unused_props &e)
00387   {
00388     throw mbl_exception_parse_error(e.what());
00389   }
00390 }
00391 
00392 
00393 //==================< end of vpdfl_gaussian_builder.cxx >====================