contrib/mul/msm/msm_translation_aligner.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Tim Cootes
00004 // \brief Calculate and apply 2D translations
00005 
00006 #include "msm_translation_aligner.h"
00007 #include <vnl/vnl_vector.h>
00008 #include <vsl/vsl_binary_loader.h>
00009 #include <vcl_cassert.h>
00010 #include <vcl_cstddef.h> // for std::size_t
00011 
00012 //=======================================================================
00013 
00014   //: Compute parameters for inverse transformation
00015 vnl_vector<double> msm_translation_aligner::inverse(const vnl_vector<double>& t) const
00016 {
00017   return -t;
00018 }
00019 
00020   //: Apply the transformation to the given points
00021 void msm_translation_aligner::apply_transform(const msm_points& points,
00022                                               const vnl_vector<double>& trans,
00023                                               msm_points& new_points) const
00024 {
00025   new_points=points;
00026   new_points.translate_by(trans[0],trans[1]);
00027 }
00028 
00029 
00030 //: Return scaling applied by the transform with given parameters.
00031 double msm_translation_aligner::scale(const vnl_vector<double>& trans) const
00032 {
00033   return 1.0;
00034 }
00035 
00036   //: Estimate parameter which best map ref_points to points2
00037   //  Minimises ||points2-T(ref_points)||^2.
00038   //  Takes advantage of assumed properties of ref_points (eg CoG=origin,
00039   //  unit size etc) to perform efficiently.
00040   //
00041   //  When used with a shape model of form ref_points+Pb, where the modes P
00042   //  have certain orthogonality properties with respect to the ref shape,
00043   //  this can give the optimal transformation into a tangent plane, independent
00044   //  of the current parameters.  In this case a one-shot method can be used
00045   //  to compute the optimal shape and pose parameters, rather than an iterative
00046   //  method which is required where the orthogonality properties do not hold,
00047   //  or where weights are considered.
00048 void msm_translation_aligner::calc_transform_from_ref(const msm_points& ref_pts,
00049                                                       const msm_points& pts2,
00050                                                       vnl_vector<double>& trans) const
00051 {
00052   vgl_point_2d<double> cog2 = pts2.cog();
00053   trans.set_size(2);
00054   trans[0]=cog2.x();
00055   trans[1]=cog2.y();
00056 }
00057 
00058   //: Estimate parameter which best map points1 to points2
00059   //  Minimises ||points2-T(points1)||^2
00060 void msm_translation_aligner::calc_transform(const msm_points& pts1,
00061                                              const msm_points& pts2,
00062                                              vnl_vector<double>& trans) const
00063 {
00064   vgl_point_2d<double> cog1 = pts1.cog();
00065   vgl_point_2d<double> cog2 = pts2.cog();
00066 
00067   trans.set_size(2);
00068   trans[0]=cog2.x()-cog1.x();
00069   trans[1]=cog2.y()-cog1.y();
00070 }
00071 
00072 // Compute weighted CoG
00073 inline vgl_point_2d<double> msm_wtd_cog(const msm_points& pts,
00074                                         const vnl_vector<double>& wts)
00075 {
00076   const double* v=pts.vector().data_block();
00077   const double* w=wts.data_block();
00078   unsigned n=pts.size();
00079   const double* end_v=v+2*n;
00080   double cx=0.0,cy=0.0,w_sum=0.0;
00081   for (;v!=end_v;v+=2,++w)
00082   { cx+=w[0]*v[0]; cy+=w[0]*v[1]; w_sum+=w[0]; }
00083 
00084   if (w_sum>0) { cx/=w_sum; cy/=w_sum; }
00085   return vgl_point_2d<double>(cx,cy);
00086 }
00087 
00088   //: Estimate parameters which map points1 to points2 allowing for weights
00089   //  Minimises sum of weighted squares error in frame of pts2,
00090   //  ie sum w_i * ||p2_i - T(p1_i)||
00091 void msm_translation_aligner::calc_transform_wt(const msm_points& pts1,
00092                                                 const msm_points& pts2,
00093                                                 const vnl_vector<double>& wts,
00094                                                 vnl_vector<double>& trans) const
00095 {
00096   vgl_point_2d<double> cog1 = msm_wtd_cog(pts1,wts);
00097   vgl_point_2d<double> cog2 = msm_wtd_cog(pts2,wts);
00098 
00099   trans.set_size(2);
00100   trans[0]=cog2.x()-cog1.x();
00101   trans[1]=cog2.y()-cog1.y();
00102 }
00103 
00104   //: Estimate parameters which map points allowing for anisotropic wts
00105 //  Errors on point i are weighted by wt_mat[i] in pts2 frame.
00106 //  ie error is sum (p2_i-T(p1_i)'*wt_mat[i]*(p2_i-T(p1_i)
00107 void msm_translation_aligner::calc_transform_wt_mat(const msm_points& pts1,
00108                                                     const msm_points& pts2,
00109                                                     const vcl_vector<msm_wt_mat_2d>& wt_mat,
00110                                                     vnl_vector<double>& trans) const
00111 {
00112   assert(pts2.size()==pts1.size());
00113   assert(wt_mat.size()==pts1.size());
00114   // Compute weighted CoGs
00115   msm_wt_mat_2d w_sum(0,0,0);
00116   double x1=0.0,y1=0.0;
00117   double x2=0.0,y2=0.0;
00118   const double* p1 = pts1.vector().begin();
00119   const double* p2 = pts2.vector().begin();
00120   const double* p1_end = pts1.vector().end();
00121   vcl_vector<msm_wt_mat_2d>::const_iterator w=wt_mat.begin();
00122   for (;p1!=p1_end;p1+=2,p2+=2,++w)
00123   {
00124     double wa=w->m11(), wb=w->m12(), wc=w->m22();
00125     w_sum += (*w);
00126     x1 += wa*p1[0]+wb*p1[1];
00127     y1 += wb*p1[0]+wc*p1[1];
00128     x2 += wa*p2[0]+wb*p2[1];
00129     y2 += wb*p2[0]+wc*p2[1];
00130   }
00131   msm_wt_mat_2d w_inv = w_sum.inverse();
00132   double v11=w_inv.m11(), v12=w_inv.m12(), v22=w_inv.m22();
00133   vgl_point_2d<double> cog1(v11*x1+v12*y1,v12*x1+v22*y1);
00134   vgl_point_2d<double> cog2(v11*x2+v12*y2,v12*x2+v22*y2);
00135 
00136   trans.set_size(2);
00137   trans[0]=cog2.x()-cog1.x();
00138   trans[1]=cog2.y()-cog1.y();
00139 }
00140 
00141   //: Apply transform to weight matrices (ie ignore translation component)
00142 void msm_translation_aligner::transform_wt_mat(const vcl_vector<msm_wt_mat_2d>& wt_mat,
00143                                                const vnl_vector<double>& trans,
00144                                                vcl_vector<msm_wt_mat_2d>& new_wt_mat) const
00145 {
00146   new_wt_mat = wt_mat;
00147 }
00148 
00149 //: Returns params of pose such that pose(x) = pose1(pose2(x))
00150 vnl_vector<double> msm_translation_aligner::compose(
00151                          const vnl_vector<double>& pose1,
00152                          const vnl_vector<double>& pose2) const
00153 {
00154   return pose1+pose2;
00155 }
00156 
00157 //: Apply transform to generate points in some reference frame
00158 //  For instance, depending on transform, may translate so the
00159 //  centre of gravity is at the origin and scale to a unit size.
00160 void msm_translation_aligner::normalise_shape(msm_points& points) const
00161 {
00162   vgl_point_2d<double> cog = points.cog();
00163   points.translate_by(-cog.x(),-cog.y());
00164 }
00165 
00166 
00167 //: Find poses which align a set of points
00168 //  On exit ref_mean_shape is the mean shape in the reference
00169 //  frame, pose_to_ref[i] maps points[i] into the reference
00170 //  frame (ie pose is the mapping from the reference frame to
00171 //  the target frames).
00172 // \param average_pose Average mapping from ref to target frame
00173 void msm_translation_aligner::align_set(const vcl_vector<msm_points>& points,
00174                                         msm_points& ref_mean_shape,
00175                                         vcl_vector<vnl_vector<double> >& pose_to_ref,
00176                                         vnl_vector<double>& average_pose) const
00177 {
00178   vcl_size_t n_shapes = points.size();
00179   assert(n_shapes>0);
00180   pose_to_ref.resize(n_shapes);
00181 
00182   // Use first shape as initial reference
00183   ref_mean_shape = points[0];
00184   normalise_shape(ref_mean_shape);
00185 
00186   vnl_vector<double> pose_from_ref;
00187 
00188   average_pose.set_size(2);
00189   average_pose.fill(0);
00190 
00191   for (unsigned i=0;i<n_shapes;++i)
00192   {
00193     calc_transform_from_ref(ref_mean_shape,points[i],pose_from_ref);
00194     pose_to_ref[i]=inverse(pose_from_ref);
00195     average_pose+=pose_from_ref;
00196   }
00197 
00198   mean_of_transformed(points,pose_to_ref,ref_mean_shape);
00199   average_pose/=n_shapes;
00200 }
00201 
00202 //=======================================================================
00203 
00204 vcl_string msm_translation_aligner::is_a() const
00205 {
00206   return vcl_string("msm_translation_aligner");
00207 }
00208 
00209 //: Create a copy on the heap and return base class pointer
00210 msm_aligner* msm_translation_aligner::clone() const
00211 {
00212   return new msm_translation_aligner(*this);
00213 }
00214 
00215