contrib/mul/msm/msm_shape_instance.cxx
Go to the documentation of this file.
00001 #include "msm_shape_instance.h"
00002 //:
00003 // \file
00004 // \brief Representation of an instance of a shape model.
00005 // \author Tim Cootes
00006 
00007 #include <msm/msm_shape_model.h>
00008 #include <vcl_iostream.h>
00009 #include <vsl/vsl_indent.h>
00010 #include <vsl/vsl_binary_io.h>
00011 #include <vnl/io/vnl_io_vector.h>
00012 #include <vnl/algo/vnl_cholesky.h>
00013 #include <vnl/algo/vnl_svd.h>
00014 
00015 #include <vcl_cstdlib.h>  // for vcl_atoi() & vcl_abort()
00016 #include <vcl_cassert.h>
00017 
00018 //=======================================================================
00019 // Dflt ctor
00020 //=======================================================================
00021 
00022 msm_shape_instance::msm_shape_instance()
00023   : model_(0)
00024 {
00025 }
00026 
00027 //: Set up model (retains pointer to model)
00028 msm_shape_instance::msm_shape_instance(const msm_shape_model& model)
00029 {
00030   set_shape_model(model);
00031 }
00032 
00033 //=======================================================================
00034 // Destructor
00035 //=======================================================================
00036 
00037 msm_shape_instance::~msm_shape_instance()
00038 {
00039 }
00040 
00041 //: Set up model (retains pointer to model)
00042 void msm_shape_instance::set_shape_model(const msm_shape_model& model)
00043 {
00044   model_=&model;
00045   ref_shape_.set_shape_model(model);
00046   pose_ = model.default_pose();
00047 
00048   points_valid_=false;
00049 }
00050 
00051 //: Define current pose
00052 void msm_shape_instance::set_pose(const vnl_vector<double>& pose)
00053 {
00054   assert(pose.size()==pose_.size());
00055   pose_=pose;
00056   points_valid_=false;
00057 }
00058 
00059 //: Define parameters
00060 void msm_shape_instance::set_params(const vnl_vector<double>& b)
00061 {
00062   assert(b.size()<=model().n_modes());
00063   ref_shape_.set_params(b);
00064   points_valid_=false;
00065 }
00066 
00067 //: Set all shape parameters to zero
00068 void msm_shape_instance::set_to_mean()
00069 {
00070   ref_shape_.set_to_mean();
00071   points_valid_=false;
00072 }
00073 
00074 
00075 //: Current shape (uses lazy evaluation)
00076 const msm_points& msm_shape_instance::points()
00077 {
00078   if (points_valid_) return points_;
00079 
00080   // Apply pose transform
00081   model().aligner().apply_transform(model_points(),pose_,points_);
00082 
00083   points_valid_=true;
00084   return points_;
00085 }
00086 
00087 //: Returns approximate scale of points
00088 //  Actually returns scale of mean after applying current pose
00089 double msm_shape_instance::approx_points_scale() const
00090 {
00091   double s = model().aligner().scale(pose_);
00092   return model().ref_mean_points_scale() * s;
00093 }
00094 
00095 
00096 //: Finds parameters and pose to best match to points
00097 //  All points equally weighted.
00098 //  If res_var>0, and use_prior(), then effect of
00099 //  Gaussian prior is to scale parameters by
00100 //  mode_var/(mode_var+rv), where rv is the res_var scaled
00101 //  to the reference frame.
00102 void msm_shape_instance::fit_to_points(const msm_points& pts,
00103                                        double res_var)
00104 {
00105   // Catch case when fitting to self
00106   if (&pts == &points_) return;
00107 
00108 #if 0 // commented out
00109   // Estimate pose from mean model points to target points
00110   // Assuming that the modes are orthogonal to the mean,
00111   // and to the rotation vector for the mean,
00112   // this transformation maps into the tangent space
00113   model().aligner().calc_transform_from_ref(
00114                     ref_shape_.model().mean_points(),pts,pose_);
00115 
00116   vnl_vector<double> pose_inv = model().aligner().inverse(pose_);
00117   model().aligner().apply_transform(pts,pose_inv,tmp_points_);
00118 
00119   double s = model().aligner().scale(pose_);
00120   double rv=res_var/(s*s);  // Scale variance into ref frame.
00121 
00122   ref_shape_.fit_to_points(tmp_points_,rv);
00123   points_valid_=false;
00124 #endif // 0
00125   vnl_vector<double> pose0 = pose_;
00126 
00127   // Estimate pose from current model points to target points
00128   model().aligner().calc_transform(model_points(),pts,pose_);
00129 
00130   double dp=1.0;
00131   int n_its=0;
00132   while (dp>1e-6 && n_its<10)
00133   {
00134     // Transform pts into model frame with these parameters
00135     vnl_vector<double> pose_inv = model().aligner().inverse(pose_);
00136     model().aligner().apply_transform(pts,pose_inv,tmp_points_);
00137 
00138     double s = model().aligner().scale(pose_);
00139     double rv=res_var/(s*s);  // Scale variance into ref frame.
00140 
00141     ref_shape_.fit_to_points(tmp_points_,rv);
00142     points_valid_=false;
00143 
00144     // Check that the pose has converged - if not repeat the loop.
00145     pose0 = pose_;
00146     model().aligner().calc_transform(model_points(),pts,pose_);
00147     dp = (pose_-pose0).magnitude();
00148 
00149     n_its++;
00150   }
00151 }
00152 
00153 //: Finds parameters and pose to best match to points
00154 //  Errors on point i are weighted by wts[i]
00155 void msm_shape_instance::fit_to_points_wt(const msm_points& pts,
00156                                           const vnl_vector<double>& wts)
00157 {
00158   // Catch case when fitting to self
00159   if (&pts == &points_) return;
00160 
00161   vnl_vector<double> pose0 = pose_;
00162   vnl_vector<double> ref_wts;
00163 
00164   // Estimate pose from current model points to target points
00165   model().aligner().calc_transform_wt(model_points(),pts,wts,pose_);
00166 
00167   double dp=1.0;
00168   int n_its=0;
00169   while (dp>1e-6 && n_its<10)
00170   {
00171     // Transform pts into model frame with these parameters
00172     vnl_vector<double> pose_inv = model().aligner().inverse(pose_);
00173     model().aligner().apply_transform(pts,pose_inv,tmp_points_);
00174 
00175     if (ref_shape_.use_prior())
00176     {
00177       // Need to scale the weights into the reference frame
00178       double s = model().aligner().scale(pose_);
00179       ref_wts=wts/(s*s);
00180       ref_shape_.fit_to_points_wt(tmp_points_,ref_wts);
00181     }
00182     else
00183     {
00184       // Absolute value of weights not important, only relative values
00185       // So no need to scale them.
00186       ref_shape_.fit_to_points_wt(tmp_points_,wts);
00187     }
00188 
00189     points_valid_=false;
00190 
00191     // Check that the pose has converged - if not repeat the loop.
00192     pose0 = pose_;
00193     model().aligner().calc_transform_wt(model_points(),pts,wts,pose_);
00194     dp = (pose_-pose0).magnitude();
00195 
00196     n_its++;
00197   }
00198 }
00199 
00200 //: Finds parameters and pose to best match to points
00201 //  Errors on point i are weighted by wt_mat[i] in target frame
00202 void msm_shape_instance::fit_to_points_wt_mat(const msm_points& pts,
00203                                               const vcl_vector<msm_wt_mat_2d>& wt_mat)
00204 {
00205   // Catch case when fitting to self
00206   if (&pts == &points_) return;
00207 
00208   unsigned n=model().size();
00209   assert(wt_mat.size()==n);
00210 
00211   vnl_vector<double> pose0 = pose_;
00212 
00213   // Estimate pose from current model points to target points
00214   model().aligner().calc_transform_wt_mat(model_points(),pts,
00215                                           wt_mat,pose_);
00216 
00217   double dp=1.0;
00218   int n_its=0;
00219   while (dp>1e-6 && n_its<10)
00220   {
00221     // Transform pts into model frame with these parameters
00222     vnl_vector<double> pose_inv = model().aligner().inverse(pose_);
00223     model().aligner().apply_transform(pts,pose_inv,tmp_points_);
00224 
00225     // Transform the weight matrices to the model frame
00226     // If A is 2x2 scale/rot component, then in model frame
00227     // error is (A*dx)'*W*(A*dx), thus:
00228     // wt_mat2[i] = A'*wt_mat[i]*A
00229     vcl_vector<msm_wt_mat_2d> wt_mat2(n);
00230     model().aligner().transform_wt_mat(wt_mat,pose_inv,wt_mat2);
00231 
00232     ref_shape_.fit_to_points_wt_mat(tmp_points_,wt_mat2);
00233     points_valid_=false;
00234 
00235     // Check that the pose has converged - if not repeat the loop.
00236     pose0 = pose_;
00237     model().aligner().calc_transform_wt_mat(model_points(),pts,
00238                                             wt_mat,pose_);
00239     dp = (pose_-pose0).magnitude();
00240     n_its++;
00241   }
00242 }
00243 
00244 
00245 //=======================================================================
00246 // Method: version_no
00247 //=======================================================================
00248 
00249 short msm_shape_instance::version_no() const
00250 {
00251   return 1;
00252 }
00253 
00254 //=======================================================================
00255 // Method: is_a
00256 //=======================================================================
00257 
00258 vcl_string msm_shape_instance::is_a() const
00259 {
00260   return vcl_string("msm_shape_instance");
00261 }
00262 
00263 //=======================================================================
00264 // Method: print
00265 //=======================================================================
00266 
00267   // required if data is present in this class
00268 void msm_shape_instance::print_summary(vcl_ostream& /*os*/) const
00269 {
00270 }
00271 
00272 //=======================================================================
00273 // Method: save
00274 //=======================================================================
00275 
00276   // required if data is present in this class
00277 void msm_shape_instance::b_write(vsl_b_ostream& bfs) const
00278 {
00279   vsl_b_write(bfs,version_no());
00280   vsl_b_write(bfs,ref_shape_);
00281   vsl_b_write(bfs,pose_);
00282 }
00283 
00284 //=======================================================================
00285 // Method: load
00286 //=======================================================================
00287 
00288   // required if data is present in this class
00289 void msm_shape_instance::b_read(vsl_b_istream& bfs)
00290 {
00291   short version;
00292   vsl_b_read(bfs,version);
00293   switch (version)
00294   {
00295     case (1):
00296       vsl_b_read(bfs,ref_shape_);
00297       vsl_b_read(bfs,pose_);
00298       break;
00299     default:
00300       vcl_cerr << "msm_shape_instance::b_read() :\n"
00301                << "Unexpected version number " << version << vcl_endl;
00302       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00303       return;
00304   }
00305 
00306   points_valid_=false;
00307 }
00308 
00309 
00310 //=======================================================================
00311 // Associated function: operator<<
00312 //=======================================================================
00313 
00314 void vsl_b_write(vsl_b_ostream& bfs, const msm_shape_instance& b)
00315 {
00316   b.b_write(bfs);
00317 }
00318 
00319 //=======================================================================
00320 // Associated function: operator>>
00321 //=======================================================================
00322 
00323 void vsl_b_read(vsl_b_istream& bfs, msm_shape_instance& b)
00324 {
00325   b.b_read(bfs);
00326 }
00327 
00328 //=======================================================================
00329 // Associated function: operator<<
00330 //=======================================================================
00331 
00332 vcl_ostream& operator<<(vcl_ostream& os,const msm_shape_instance& b)
00333 {
00334   os << b.is_a() << ": ";
00335   vsl_indent_inc(os);
00336   b.print_summary(os);
00337   vsl_indent_dec(os);
00338   return os;
00339 }
00340 
00341 //: Stream output operator for class reference
00342 void vsl_print_summary(vcl_ostream& os,const msm_shape_instance& b)
00343 {
00344  os << b;
00345 }