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