contrib/brl/bbas/imesh/algo/imesh_pca.cxx
Go to the documentation of this file.
00001 // This is brl/bbas/imesh/algo/imesh_pca.cxx
00002 #include "imesh_pca.h"
00003 //:
00004 // \file
00005 
00006 #include <imesh/imesh_fileio.h>
00007 #include <vnl/vnl_vector.h>
00008 #include <vnl/algo/vnl_svd.h>
00009 #include <vcl_fstream.h>
00010 #include <vcl_sstream.h>
00011 #include <vcl_cassert.h>
00012 
00013 imesh_pca_mesh::imesh_pca_mesh(const vcl_vector<imesh_mesh>& meshes)
00014   : imesh_mesh(meshes[0]), mean_verts_(this->vertices().clone())
00015 {
00016   vnl_matrix<double> M = compute_mean(meshes);
00017   vnl_svd<double> A(M,-1e-8);
00018 
00019   // remove zero singular values
00020 
00021   std_devs_ = vnl_vector<double>(A.W().diagonal().data_block(), A.rank());
00022   pc_.set_size(A.rank(),M.rows());
00023   A.U().transpose().extract(pc_);
00024 
00025   params_.set_size(std_devs_.size());
00026   params_.fill(0.0);
00027 }
00028 
00029 
00030 //: compute and set the mean return the deviations matrix
00031 vnl_matrix<double>
00032 imesh_pca_mesh::compute_mean(const vcl_vector<imesh_mesh>& meshes)
00033 {
00034   const unsigned num_training = meshes.size();
00035   vnl_matrix<double> M(this->num_verts()*3,num_training);
00036   vnl_vector<double> mean(this->num_verts()*3,0.0);
00037 
00038   for (unsigned int i=0; i<num_training; ++i)
00039   {
00040     assert(meshes[i].num_verts() == this->num_verts());
00041     const imesh_vertex_array<3>& verts = meshes[i].vertices<3>();
00042     for (unsigned v=0; v<verts.size(); ++v) {
00043       M(3*v,i) = verts[v][0];
00044       M(3*v+1,i) = verts[v][1];
00045       M(3*v+2,i) = verts[v][2];
00046       mean[3*v] += verts[v][0];
00047       mean[3*v+1] += verts[v][1];
00048       mean[3*v+2] += verts[v][2];
00049     }
00050   }
00051 
00052   mean /= num_training;
00053 
00054   for (unsigned int i=0; i<num_training; ++i)
00055   {
00056     M.set_column(i,M.get_column(i) - mean);
00057     imesh_vertex_array<3>& verts = this->vertices<3>();
00058     imesh_vertex_array<3>& mverts = this->mean_vertices<3>();
00059     for (unsigned v=0; v<verts.size(); ++v) {
00060       verts[v][0] = mverts[v][0] = mean[3*v];
00061       verts[v][1] = mverts[v][1] = mean[3*v+1];
00062       verts[v][2] = mverts[v][2] = mean[3*v+2];
00063     }
00064   }
00065 
00066   return M;
00067 }
00068 
00069 
00070 //: Constructor from a mesh, mean, standard deviations, and PC matrix
00071 imesh_pca_mesh::imesh_pca_mesh(const imesh_mesh& mesh,
00072                                const vnl_vector<double>& mean,
00073                                const vnl_vector<double>& std_devs,
00074                                const vnl_matrix<double>& pc)
00075   : imesh_mesh(mesh)
00076 {
00077   init(mean,std_devs,pc);
00078 }
00079 
00080 
00081 //: Copy Constructor
00082 imesh_pca_mesh::imesh_pca_mesh(const imesh_pca_mesh& other)
00083   : imesh_mesh(other),
00084     std_devs_(other.std_devs_),
00085     pc_(other.pc_),
00086     mean_verts_((other.mean_verts_.get()) ? other.mean_verts_->clone() : 0),
00087     params_(other.params_)
00088 {
00089 }
00090 
00091 
00092 //: Assignment operator
00093 imesh_pca_mesh& imesh_pca_mesh::operator=(const imesh_pca_mesh& other)
00094 {
00095   if (this != &other) {
00096     this->imesh_mesh::operator=(other);
00097     std_devs_ = other.std_devs_;
00098     pc_ = other.pc_;
00099     mean_verts_ = vcl_auto_ptr<imesh_vertex_array_base>((other.mean_verts_.get()) ?
00100                                                         other.mean_verts_->clone() : 0);
00101     params_ = other.params_;
00102   }
00103   return *this;
00104 }
00105 
00106 
00107 //: Construct from a mesh with no variation
00108 imesh_pca_mesh::imesh_pca_mesh(const imesh_mesh& mesh)
00109   : imesh_mesh(mesh), mean_verts_(this->vertices().clone())
00110 {
00111 }
00112 
00113 
00114 //: Initialize the PCA data (assuming mesh data is already set)
00115 //  Use this to add PCA data after a mesh has be loaded from a file
00116 void imesh_pca_mesh::init(const vnl_vector<double>& mean,
00117                           const vnl_vector<double>& std_devs,
00118                           const vnl_matrix<double>& pc)
00119 {
00120   std_devs_ = std_devs;
00121   pc_ = pc;
00122   mean_verts_.reset(new imesh_vertex_array<3>(this->num_verts()));
00123 
00124   assert(this->num_verts()*3 == mean.size());
00125   assert(this->num_verts()*3 == pc.columns());
00126   assert(std_devs.size() == pc.rows());
00127 
00128   imesh_vertex_array<3>& mverts = this->mean_vertices<3>();
00129   for (unsigned int i=0; i<mverts.size(); ++i)
00130   {
00131     mverts[i] = imesh_vertex<3>(mean[3*i],mean[3*i+1],mean[3*i+2]);
00132   }
00133   params_ = this->project(this->vertices());
00134 }
00135 
00136 
00137 //: Set the pca parameters
00138 void imesh_pca_mesh::set_params(const vnl_vector<double>& p)
00139 {
00140   assert(params_.size() >= p.size());
00141   vnl_vector<double> sp(p.size(),0.0);
00142   unsigned int i=0;
00143   for (; i<p.size(); ++i){
00144     params_[i] = p[i];
00145     sp[i] = std_devs_[i]*p[i];
00146   }
00147   // fill the rest with zeros
00148   for (; i<params_.size(); ++i)
00149     params_[i] = 0.0;
00150 
00151   imesh_vertex_array<3>& verts = this->vertices<3>();
00152   const imesh_vertex_array<3>& mverts = this->mean_vertices<3>();
00153   for (unsigned i=0; i<verts.size(); ++i) {
00154     imesh_vertex<3>& v = verts[i];
00155     const imesh_vertex<3>& mv = mverts[i];
00156     v = mv;
00157     for (unsigned j=0; j<sp.size(); ++j) {
00158       v[0] += pc_(j,3*i)  *sp[j];
00159       v[1] += pc_(j,3*i+1)*sp[j];
00160       v[2] += pc_(j,3*i+2)*sp[j];
00161     }
00162   }
00163 }
00164 
00165 //: Set an individual pca parameter
00166 // This is done by incremental difference, errors may accumulate
00167 // over many calls.
00168 void imesh_pca_mesh::set_param(unsigned int idx, double param)
00169 {
00170   double diff = std_devs_[idx]*(param - params_[idx]);
00171   params_[idx] = param;
00172 
00173   imesh_vertex_array<3>& verts = this->vertices<3>();
00174   for (unsigned i=0; i<verts.size(); ++i) {
00175     imesh_vertex<3>& v = verts[i];
00176     v[0] += pc_(idx,3*i)  *diff;
00177     v[1] += pc_(idx,3*i+1)*diff;
00178     v[2] += pc_(idx,3*i+2)*diff;
00179   }
00180 }
00181 
00182 
00183 //: Reset all the PCA parameters to zero
00184 //  Returning to the mean mesh
00185 void imesh_pca_mesh::set_mean()
00186 {
00187   params_.fill(0.0);
00188 
00189   imesh_vertex_array<3>& verts = this->vertices<3>();
00190   const imesh_vertex_array<3>& mverts = this->mean_vertices<3>();
00191   for (unsigned v=0; v<verts.size(); ++v) {
00192     verts[v] = mverts[v];
00193   }
00194 }
00195 
00196 
00197 //: Project mesh vertices into the PCA parameter space
00198 vnl_vector<double>
00199 imesh_pca_mesh::project(const imesh_vertex_array_base& vertices) const
00200 {
00201   assert(dynamic_cast<const imesh_vertex_array<3>*>(&vertices));
00202   const imesh_vertex_array<3>& verts =
00203       static_cast<const imesh_vertex_array<3>&>(vertices);
00204 
00205   const imesh_vertex_array<3>& mverts = this->mean_vertices<3>();
00206 
00207   const unsigned int num_verts = this->num_verts();
00208   vnl_vector<double> vals(3*num_verts);
00209   for (unsigned int i=0; i<num_verts; ++i)
00210   {
00211     const imesh_vertex<3>& mv = mverts[i];
00212     const imesh_vertex<3>& v = verts[i];
00213     vals[3*i]   = v[0] - mv[0];
00214     vals[3*i+1] = v[1] - mv[1];
00215     vals[3*i+2] = v[2] - mv[2];
00216   }
00217 
00218   vnl_vector<double> p = pc_*vals;
00219   for (unsigned int i=0; i<p.size(); ++i)
00220     p[i] /= std_devs_[i];
00221   return p;
00222 }
00223 
00224 
00225 //=============================================================================
00226 // External functions
00227 
00228 
00229 //: Compute the image Jacobians at each vertex for PCA parameters in the result
00230 //  Matrix n, row i is the image space derivative
00231 //  at vertex n with respect to the ith pca parameter
00232 vcl_vector<vnl_matrix<double> >
00233 imesh_pca_image_jacobians(const vpgl_proj_camera<double>& camera,
00234                           const imesh_pca_mesh& mesh)
00235 {
00236   // convert vertices to vgl points
00237   const imesh_vertex_array<3>& verts = mesh.vertices<3>();
00238   const unsigned int num_verts = mesh.num_verts();
00239   vcl_vector<vgl_point_3d<double> > pts(num_verts);
00240   for (unsigned int i=0; i<num_verts; ++i)
00241     pts[i] = verts[i];
00242 
00243   // compute the Jacobians at each point
00244   vcl_vector<vnl_matrix_fixed<double,2,3> > J = image_jacobians(camera,pts);
00245 
00246   // map the image Jacobians into PCA Jacobians
00247   const vnl_matrix<double>& pc = mesh.principal_comps();
00248   const vnl_vector<double>& std = mesh.std_devs();
00249 
00250   vcl_vector<vnl_matrix<double> > img_jac(num_verts);
00251   for (unsigned int i=0; i<num_verts; ++i)
00252   {
00253     vnl_matrix<double> dir_3d(pc.rows(),3);
00254     pc.extract(dir_3d,0,3*i);
00255     for (unsigned int j=0; j<std.size(); ++j) {
00256       dir_3d(j,0) /= std[j];
00257       dir_3d(j,1) /= std[j];
00258       dir_3d(j,2) /= std[j];
00259     }
00260 
00261     img_jac[i] = J[i]*dir_3d.transpose();
00262   }
00263 
00264   return img_jac;
00265 }
00266 
00267 
00268 //: Read a PCA mesh from a mean mesh and a pca file
00269 imesh_pca_mesh imesh_read_pca(const vcl_string& mean_file,
00270                               const vcl_string& pca_file)
00271 {
00272   imesh_mesh mean;
00273   vnl_vector<double> m,s;
00274   vnl_matrix<double> P;
00275   imesh_read(mean_file,mean);
00276   imesh_read_pca(pca_file,m,s,P);
00277   return imesh_pca_mesh(mean,m,s,P);
00278 }
00279 
00280 
00281 //: Read a PCA file
00282 bool imesh_read_pca(const vcl_string& pca_file,
00283                     vnl_vector<double>& mean,
00284                     vnl_vector<double>& std_devs,
00285                     vnl_matrix<double>& pc)
00286 {
00287   vcl_ifstream ifs(pca_file.c_str());
00288   if (!ifs.is_open())
00289     return false;
00290 
00291   vcl_vector<double> data;
00292   if (ifs.peek() == '#') {
00293     vcl_string s;
00294     ifs >> s;
00295     if (s == "#mag") {
00296       vcl_string line;
00297       vcl_getline(ifs,line);
00298       vcl_stringstream ss(line);
00299       double val;
00300       while (ss >> val) {
00301         data.push_back(val);
00302       }
00303       std_devs.set_size(data.size());
00304       for (unsigned i=0; i<std_devs.size(); ++i)
00305         std_devs[i] = data[i];
00306     }
00307   }
00308   vcl_string line;
00309   data.clear();
00310   vcl_vector<vcl_vector<double> > data_M;
00311   while (vcl_getline(ifs,line).good()) {
00312     vcl_stringstream ss(line);
00313     double val;
00314     ss >> val;
00315     data.push_back(val);
00316     vcl_vector<double> row;
00317     while (ss >> val) {
00318       row.push_back(val);
00319     }
00320     data_M.push_back(row);
00321   }
00322   mean.set_size(data.size());
00323   pc.set_size(data_M[0].size(),mean.size());
00324   for (unsigned i=0; i<mean.size(); ++i) {
00325     mean[i] = data[i];
00326     for (unsigned j=0; j<pc.rows(); ++j) {
00327       pc(j,i) = data_M[i][j];
00328     }
00329   }
00330 
00331   return true;
00332 }
00333 
00334 
00335 //: Write the mean mesh and PCA file
00336 void imesh_write_pca(const vcl_string& mesh_file,
00337                      const vcl_string& pca_file,
00338                      const imesh_pca_mesh& pmesh)
00339 {
00340   const vnl_vector<double>& std_dev = pmesh.std_devs();
00341   const vnl_matrix<double>& pc = pmesh.principal_comps();
00342   const imesh_vertex_array<3>& mverts = pmesh.mean_vertices<3>();
00343   const unsigned int num_data = pc.columns();
00344   vnl_vector<double> mean(num_data);
00345   for (unsigned int i=0; i<num_data; ++i)
00346     mean[i] = mverts[i/3][i%3];
00347 
00348   vcl_auto_ptr<imesh_vertex_array_base> verts(mverts.clone());
00349   vcl_auto_ptr<imesh_face_array_base> faces(pmesh.faces().clone());
00350   imesh_mesh mean_mesh(verts,faces);
00351 
00352   imesh_write_pca(pca_file,mean,std_dev,pc);
00353   imesh_write_obj(mesh_file,mean_mesh);
00354 }
00355 
00356 
00357 //: Write a PCA file
00358 bool imesh_write_pca(const vcl_string& filename,
00359                      const vnl_vector<double>& mean,
00360                      const vnl_vector<double>& svals,
00361                      const vnl_matrix<double>& vars)
00362 {
00363   const unsigned int num_comps = svals.size();
00364   const unsigned int num_data = mean.size();
00365   vcl_ofstream ofs(filename.c_str());
00366   ofs << "#mag";
00367   for (unsigned int i=0; i<num_comps; ++i)
00368     ofs << ' ' << svals[i];
00369   ofs << '\n';
00370   for (unsigned int j=0; j<num_data; ++j) {
00371     ofs << mean[j];
00372     for (unsigned int i=0; i<num_comps; ++i)
00373       ofs << ' ' << vars(i,j);
00374     ofs << '\n';
00375   }
00376 
00377   ofs.close();
00378   return true;
00379 }