contrib/mul/mbl/mbl_thin_plate_spline_3d.cxx
Go to the documentation of this file.
00001 // This is mul/mbl/mbl_thin_plate_spline_3d.cxx
00002 #include "mbl_thin_plate_spline_3d.h"
00003 //:
00004 // \file
00005 // \brief Construct thin plate spline to map 3D to 3D
00006 // \author Tim Cootes
00007 
00008 #include <vcl_cmath.h>
00009 #include <vcl_cstdlib.h> // for vcl_abort()
00010 #include <vsl/vsl_indent.h>
00011 #include <vsl/vsl_vector_io.h>
00012 #include <vnl/vnl_math.h>
00013 #include <vnl/algo/vnl_svd.h>
00014 #include <vnl/io/vnl_io_vector.h>
00015 #include <vnl/io/vnl_io_matrix.h>
00016 #include <vgl/vgl_vector_3d.h>
00017 #include <vgl/io/vgl_io_point_3d.h>
00018 #include <mbl/mbl_matxvec.h>
00019 
00020 //=======================================================================
00021 
00022 //=======================================================================
00023 // Dflt ctor
00024 // Default constructor gives identity mapping
00025 //=======================================================================
00026 
00027 mbl_thin_plate_spline_3d::mbl_thin_plate_spline_3d()
00028   : Wx_(0),Wy_(0),Wz_(0),
00029     Ax0_(0),AxX_(1),AxY_(0),AxZ_(0),
00030     Ay0_(0),AyX_(0),AyY_(1),AyZ_(0),
00031     Az0_(0),AzX_(0),AzY_(0),AzZ_(1),
00032     energy_x_(0),energy_y_(0),energy_z_(0)
00033 {
00034 }
00035 
00036 //=======================================================================
00037 // Destructor
00038 //=======================================================================
00039 
00040 mbl_thin_plate_spline_3d::~mbl_thin_plate_spline_3d()
00041 {
00042 }
00043 
00044 // First some useful maths functions
00045 
00046 inline double r2lnr(const vgl_vector_3d<double>&  pt)
00047 {
00048   double r2 = pt.x() * pt.x() + pt.y() * pt.y() + pt.z() * pt.z();
00049   if (r2>1e-8)   return 0.5 * r2 * vcl_log(r2);
00050   else     return 0;
00051 }
00052 
00053 inline double r2lnr(double x, double y, double z)
00054 {
00055   double r2 = x * x + y * y + z*z;
00056   if (r2>1e-8) return 0.5 * r2 * vcl_log(r2);
00057   else      return 0;
00058 }
00059 
00060 
00061 // Sets L to be a symmetric square matrix of size n + 4 (n = pts.nelems)
00062 // with L(i,j) = Uij = r2lnr(pts(i)-pts(j)) for i,j <= n
00063 static void build_K_part(vnl_matrix<double>& L,
00064                          const vcl_vector<vgl_point_3d<double> >& pts)
00065 {
00066   unsigned int n = pts.size();
00067   if ( (L.rows()!=n+4) | (L.columns()!=n+4) ) L.set_size(n+4,n+4);
00068 
00069   const vgl_point_3d<double> * pts_data = &pts[0];
00070   double** K_data = L.data_array();
00071 
00072     // Zero the diagonal
00073   for (unsigned int i=0;i<n;i++)
00074     K_data[i][i] = 0;
00075     // Now fill upper & lower triangles
00076   for (unsigned int i=1;i<n;i++)
00077     for (unsigned int j=0;j<i;j++)
00078     {
00079       K_data[i][j] = K_data[j][i] = r2lnr(pts_data[i]-pts_data[j]);
00080     }
00081 }
00082 
00083 // L is a (n+4) x (n+43) matrix;
00084 // L = ( K  Q )
00085 //     ( Q' 0 )
00086 // Where K is n x n, K(i,j) = Uij = r2lnr(pts(i)-pts(j)) for i,j <= n
00087 // and Q is ( 1 x0 y0 z0)
00088 //          ( 1 x1 y1 z1)
00089 //             . .  .
00090 static void build_L(vnl_matrix<double>& L, const vcl_vector<vgl_point_3d<double> >& pts)
00091 {
00092   int i,j;
00093 
00094   build_K_part(L,pts);
00095 
00096   int n = pts.size();
00097 
00098   const vgl_point_3d<double> * pts_data = &pts[0];
00099   double** L_data = L.data_array();
00100 
00101   // Build Q part
00102 
00103   for (i=0;i<n;i++)
00104   {
00105     L_data[i][n] = 1;
00106     L_data[i][n+1] = pts_data[i].x();
00107     L_data[i][n+2] = pts_data[i].y();
00108     L_data[i][n+3] = pts_data[i].z();
00109 
00110     L_data[n][i] = 1;
00111     L_data[n+1][i] = pts_data[i].x();
00112     L_data[n+2][i] = pts_data[i].y();
00113     L_data[n+3][i] = pts_data[i].z();
00114   }
00115 
00116   // put 0's in bottom right corner.
00117   for (i=n;i<n+4;i++)
00118     for (j=n;j<n+4;j++)
00119       L_data[i][j] = 0;
00120 }
00121 
00122 //: Build from small number of points
00123 void mbl_thin_plate_spline_3d::build_pure_affine(
00124         const vcl_vector<vgl_point_3d<double> >& source_pts,
00125         const vcl_vector<vgl_point_3d<double> >& dest_pts)
00126 {
00127   int n=source_pts.size();
00128   L_inv_.set_size(0,0);
00129   if (n==0)
00130   {
00131     // Set identity transformation
00132     Ax0_ = 0;
00133     Ay0_ = 0;
00134     Az0_ = 0;
00135     AxX_ = 1; AxY_ = 0; AxZ_ = 0;
00136     AyX_ = 0; AyY_ = 1; AyZ_ = 0;
00137     AzX_ = 0; AzY_ = 0; AzZ_ = 1;
00138 
00139     Wx_.set_size(0);
00140     Wy_.set_size(0);
00141     Wz_.set_size(0);
00142 
00143     src_pts_.resize(0);
00144 
00145     return;
00146   }
00147 
00148   if (n==1)
00149   {
00150     // Just apply a translation :
00151     Ax0_ = dest_pts[0].x() - source_pts[0].x();
00152     Ay0_ = dest_pts[0].y() - source_pts[0].y();
00153     Az0_ = dest_pts[0].z() - source_pts[0].z();
00154     Wx_.set_size(0);
00155     Wy_.set_size(0);
00156     Wz_.set_size(0);
00157     AxX_ = 1; AxY_ = 0; AxZ_ = 0;
00158     AyX_ = 0; AyY_ = 1; AyZ_ = 0;
00159     AzX_ = 0; AzY_ = 0; AzZ_ = 1;
00160     src_pts_.resize(0);
00161 
00162     return;
00163   }
00164   if (n>=2)
00165   {
00166     vcl_cerr<<"mbl_thin_plate_spline_3d::build_pure_affine() Incomplete. sorry.\n";
00167     vcl_abort();
00168   }
00169 }
00170 
00171 //: Set parameters from vectors
00172 void mbl_thin_plate_spline_3d::set_params(const vnl_vector<double>& W1,
00173                                           const vnl_vector<double>& W2,
00174                                           const vnl_vector<double>& W3)
00175 {
00176   int n = W1.size()-4;
00177 
00178   if (int(Wx_.size()) < n) Wx_.set_size(n);
00179   if (int(Wy_.size()) < n) Wy_.set_size(n);
00180   if (int(Wz_.size()) < n) Wz_.set_size(n);
00181 
00182   double *Wx_data=Wx_.data_block();
00183   double *Wy_data=Wy_.data_block();
00184   double *Wz_data=Wz_.data_block();
00185   const double *W1_data=W1.data_block();
00186   const double *W2_data=W2.data_block();
00187   const double *W3_data=W3.data_block();
00188 
00189   for (int i=0;i<n;i++)
00190   {
00191     Wx_data[i] = W1_data[i];
00192     Wy_data[i] = W2_data[i];
00193     Wz_data[i] = W3_data[i];
00194   }
00195 
00196   Ax0_ = W1_data[n];
00197   AxX_ = W1_data[n+1];
00198   AxY_ = W1_data[n+2];
00199   AxZ_ = W1_data[n+3];
00200 
00201   Ay0_ = W2_data[n];
00202   AyX_ = W2_data[n+1];
00203   AyY_ = W2_data[n+2];
00204   AyZ_ = W2_data[n+3];
00205 
00206   Az0_ = W3_data[n];
00207   AzX_ = W3_data[n+1];
00208   AzY_ = W3_data[n+2];
00209   AzZ_ = W3_data[n+3];
00210 }
00211 
00212 void mbl_thin_plate_spline_3d::compute_energy(vnl_vector<double>& W1,
00213                                               vnl_vector<double>& W2,
00214                                               vnl_vector<double>& W3,
00215                                               const vnl_matrix<double>& L)
00216 {
00217   int n = W1.size()-3;
00218   double *W1_data=W1.data_block();
00219   double *W2_data=W2.data_block();
00220   double *W3_data=W3.data_block();
00221 
00222   // Set last elements to zero
00223   for (int i = n;i<=n+3;++i)
00224   {
00225     W1_data[i]=0.0;
00226     W2_data[i]=0.0;
00227     W3_data[i]=0.0;
00228   }
00229 
00230   // Compute bending energy = W_t.K.W/(8pi)
00231   vnl_vector<double> LW;
00232   mbl_matxvec_prod_mv(L,W1,LW);
00233   energy_x_ = dot_product(W1,LW) / (8*vnl_math::pi);
00234 
00235   mbl_matxvec_prod_mv(L,W2,LW);
00236   energy_y_ = dot_product(W2,LW) / (8*vnl_math::pi);
00237 
00238   mbl_matxvec_prod_mv(L,W3,LW);
00239   energy_z_ = dot_product(W3,LW) / (8*vnl_math::pi);
00240 }
00241 
00242 void mbl_thin_plate_spline_3d::set_up_rhs(vnl_vector<double>& Bx,
00243                                           vnl_vector<double>& By,
00244                                           vnl_vector<double>& Bz,
00245                                           const vcl_vector<vgl_point_3d<double> >& dest_pts)
00246 {
00247   int n =dest_pts.size();
00248 
00249   Bx.set_size(n+4);
00250   By.set_size(n+4);
00251   Bz.set_size(n+4);
00252   double* Bx_data=Bx.data_block();
00253   double* By_data=By.data_block();
00254   double* Bz_data=Bz.data_block();
00255   const vgl_point_3d<double>  *d_pts_data=&dest_pts[0];
00256 
00257   for (int i=0;i<n;i++)
00258   {
00259     Bx_data[i] = d_pts_data[i].x();
00260     By_data[i] = d_pts_data[i].y();
00261     Bz_data[i] = d_pts_data[i].z();
00262   }
00263   for (int i=n;i<n+4;i++)
00264   {
00265     Bx_data[i] = 0;
00266     By_data[i] = 0;
00267     Bz_data[i] = 0;
00268   }
00269 }
00270 
00271 void mbl_thin_plate_spline_3d::build(const vcl_vector<vgl_point_3d<double> >& source_pts,
00272                                      const vcl_vector<vgl_point_3d<double> >& dest_pts,
00273                                      bool compute_the_energy)
00274 {
00275   // See Booksteins paper in IPMI 1993 for details of calculation
00276 
00277   unsigned int n=source_pts.size();
00278   if (dest_pts.size() != n)
00279   {
00280     vcl_cerr<<"mbl_thin_plate_spline_3d::build - incompatible number of points.\n";
00281     vcl_abort();
00282   }
00283 
00284   L_inv_.set_size(0,0);
00285 
00286   if (n<2)  // build_pure_affine only copes with trivial cases at the moment
00287   {
00288     build_pure_affine(source_pts,dest_pts);
00289     return;
00290   }
00291 
00292   src_pts_ = source_pts;
00293 
00294   vnl_matrix<double> L;
00295   vnl_vector<double> Bx(n+4), W1(n+4);  // Used to compute X parameters
00296   vnl_vector<double> By(n+4), W2(n+4);  // Used to compute Y parameters
00297   vnl_vector<double> Bz(n+4), W3(n+4);  // Used to compute YZ parameters
00298 
00299   build_L(L,source_pts);
00300 
00301   set_up_rhs(Bx,By,Bz,dest_pts);
00302 
00303   // Solve LW = B for W1,W2,W3 :
00304   // Note that both Cholesky and QR decompositions fail, apparently because of the
00305   // zeroes on the diagonal.  Use SVD to be safe.
00306   {
00307     vnl_svd<double> svd(L);
00308     svd.solve(Bx.data_block(),W1.data_block());
00309     svd.solve(By.data_block(),W2.data_block());
00310     svd.solve(Bz.data_block(),W3.data_block());
00311   }
00312 
00313   set_params(W1,W2,W3);
00314   if (compute_the_energy)
00315     compute_energy(W1,W2,W3,L);
00316 }
00317 
00318 //: Define source point positions
00319 //  Performs pre-computations so that build(dest_points) can be
00320 //  called multiple times efficiently
00321 void mbl_thin_plate_spline_3d::set_source_pts(const vcl_vector<vgl_point_3d<double> >& source_pts)
00322 {
00323   unsigned int n=source_pts.size();
00324   src_pts_ = source_pts;
00325 
00326   if (n<2) // build_pure_affine only copes with trivial cases at the moment
00327   {
00328     L_inv_.set_size(0,0);
00329     return;
00330   }
00331 
00332   vnl_matrix<double> L;
00333   build_L(L,source_pts);
00334 
00335   // Compute inverse of L
00336   // Note that both Cholesky and QR decompositions fail, apparently because of the
00337   // zeroes on the diagonal.  Use SVD to be safe.
00338   {
00339     vnl_svd<double> svd(L);
00340     L_inv_ = svd.inverse();
00341   }
00342 }
00343 
00344 //: Sets up internal transformation to map source_pts onto dest_pts
00345 void mbl_thin_plate_spline_3d::build(const vcl_vector<vgl_point_3d<double> >& dest_pts)
00346 {
00347   unsigned int n=src_pts_.size();
00348   if (dest_pts.size() != n)
00349   {
00350     vcl_cerr<<"mbl_thin_plate_spline_3d::build - incompatible number of points.\n";
00351     vcl_abort();
00352   }
00353 
00354   if (n<2)  // Only copes with trivial cases at the moment
00355   {
00356     build_pure_affine(src_pts_,dest_pts);
00357     return;
00358   }
00359 
00360   vnl_vector<double> Bx(n+4), W1(n+4);  // Used to compute X parameters
00361   vnl_vector<double> By(n+4), W2(n+4);  // Used to compute Y parameters
00362   vnl_vector<double> Bz(n+4), W3(n+4);  // Used to compute Z parameters
00363 
00364   set_up_rhs(Bx,By,Bz,dest_pts);
00365 
00366   mbl_matxvec_prod_mv(L_inv_,Bx,W1);
00367   mbl_matxvec_prod_mv(L_inv_,By,W2);
00368   mbl_matxvec_prod_mv(L_inv_,Bz,W3);
00369 
00370   set_params(W1,W2,W3);
00371   energy_x_ = -1;
00372   energy_y_ = -1;
00373   energy_z_ = -1;
00374 }
00375 
00376 
00377 vgl_point_3d<double>  mbl_thin_plate_spline_3d::operator()(double x, double y, double z) const
00378 {
00379   unsigned int n = src_pts_.size();
00380 
00381   double x_sum = Ax0_ + AxX_ * x + AxY_ * y + AxZ_ * z;
00382   double y_sum = Ay0_ + AyX_ * x + AyY_ * y + AyZ_ * z;
00383   double z_sum = Az0_ + AzX_ * x + AzY_ * y + AzZ_ * z;
00384 
00385   if (n<=4)  // Pure affine
00386     return vgl_point_3d<double>(x_sum,y_sum,z_sum);
00387 
00388   const vgl_point_3d<double> * pts_data = &src_pts_[0];
00389   const double* Wx_data = Wx_.data_block();
00390   const double* Wy_data = Wy_.data_block();
00391   const double* Wz_data = Wz_.data_block();
00392 
00393   for (unsigned int i=0;i<n;i++)
00394   {
00395     double Ui = r2lnr(x - pts_data[i].x(), y - pts_data[i].y(), z - pts_data[i].z() );
00396     x_sum += (Ui * Wx_data[i]);
00397     y_sum += (Ui * Wy_data[i]);
00398     z_sum += (Ui * Wz_data[i]);
00399   }
00400 
00401   return vgl_point_3d<double>(x_sum,y_sum,z_sum);
00402 }
00403 
00404 //=======================================================================
00405 // Method: version_no
00406 //=======================================================================
00407 
00408 short mbl_thin_plate_spline_3d::version_no() const
00409 {
00410   return 1;
00411 }
00412 
00413 
00414 //=======================================================================
00415 // Method: print
00416 //=======================================================================
00417 
00418   // required if data is present in this class
00419 void mbl_thin_plate_spline_3d::print_summary(vcl_ostream& os) const
00420 {
00421   os<<"\nfx: "<<Ax0_<<" + "<<AxX_<<"*x + "<<AxY_
00422     <<"*y + "<<AxZ_<<"*z   Nonlinear terms:";
00423   for (unsigned int i=0;i<Wx_.size();++i)
00424     os<<' '<<Wx_[i];
00425   os<<"\nfy: "<<Ay0_<<" + "<<AyX_<<"*x + "<<AyY_
00426     <<"*y + "<<AyZ_<<"*z   Nonlinear terms:";
00427   for (unsigned int i=0;i<Wy_.size();++i)
00428     os<<' '<<Wy_[i];
00429   os<<"\nfy: "<<Az0_<<" + "<<AzX_<<"*x + "<<AzY_
00430     <<"*y + "<<AzZ_<<"*z   Nonlinear terms:";
00431   for (unsigned int i=0;i<Wz_.size();++i)
00432     os<<' '<<Wz_[i];
00433   os<<'\n';
00434 }
00435 
00436 //=======================================================================
00437 // Method: save
00438 //=======================================================================
00439 
00440   // required if data is present in this class
00441 void mbl_thin_plate_spline_3d::b_write(vsl_b_ostream& bfs) const
00442 {
00443   vsl_b_write(bfs,version_no());
00444   vsl_b_write(bfs,Wx_); vsl_b_write(bfs,Wy_);vsl_b_write(bfs,Wz_);
00445   vsl_b_write(bfs,Ax0_); vsl_b_write(bfs,AxX_);
00446   vsl_b_write(bfs,AxY_); vsl_b_write(bfs,AxZ_);
00447   vsl_b_write(bfs,Ay0_); vsl_b_write(bfs,AyX_);
00448   vsl_b_write(bfs,AyY_); vsl_b_write(bfs,AyZ_);
00449   vsl_b_write(bfs,Az0_); vsl_b_write(bfs,AzX_);
00450   vsl_b_write(bfs,AzY_); vsl_b_write(bfs,AzZ_);
00451   vsl_b_write(bfs,energy_x_);
00452   vsl_b_write(bfs,energy_y_);
00453   vsl_b_write(bfs,energy_z_);
00454   vsl_b_write(bfs,src_pts_);
00455   vsl_b_write(bfs,L_inv_);
00456 }
00457 
00458 //=======================================================================
00459 // Method: load
00460 //=======================================================================
00461 
00462   // required if data is present in this class
00463 void mbl_thin_plate_spline_3d::b_read(vsl_b_istream& bfs)
00464 {
00465   if (!bfs) return;
00466 
00467   short version;
00468   vsl_b_read(bfs,version);
00469   switch (version)
00470   {
00471     case (1):
00472       vsl_b_read(bfs,Wx_); vsl_b_read(bfs,Wy_);vsl_b_read(bfs,Wz_);
00473       vsl_b_read(bfs,Ax0_); vsl_b_read(bfs,AxX_);
00474       vsl_b_read(bfs,AxY_); vsl_b_read(bfs,AxZ_);
00475       vsl_b_read(bfs,Ay0_); vsl_b_read(bfs,AyX_);
00476       vsl_b_read(bfs,AyY_); vsl_b_read(bfs,AyZ_);
00477       vsl_b_read(bfs,Az0_); vsl_b_read(bfs,AzX_);
00478       vsl_b_read(bfs,AzY_); vsl_b_read(bfs,AzZ_);
00479       vsl_b_read(bfs,energy_x_);
00480       vsl_b_read(bfs,energy_y_);
00481       vsl_b_read(bfs,energy_z_);
00482       vsl_b_read(bfs,src_pts_);
00483       vsl_b_read(bfs,L_inv_);
00484       break;
00485     default:
00486       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, mbl_thin_plate_spline_3d &)\n"
00487                << "           Unknown version number "<< version << '\n';
00488       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00489       return;
00490   }
00491 }
00492 
00493 //: Comparison operator
00494 bool mbl_thin_plate_spline_3d::operator==(const mbl_thin_plate_spline_3d& tps) const
00495 {
00496   if (&tps==this) return true;
00497   if (vcl_fabs(Ax0_-tps.Ax0_)>1e-8) return false;
00498   if (vcl_fabs(AxX_-tps.AxX_)>1e-8) return false;
00499   if (vcl_fabs(AxY_-tps.AxY_)>1e-8) return false;
00500   if (vcl_fabs(AxZ_-tps.AxZ_)>1e-8) return false;
00501   if (vcl_fabs(Ay0_-tps.Ay0_)>1e-8) return false;
00502   if (vcl_fabs(AyX_-tps.AyX_)>1e-8) return false;
00503   if (vcl_fabs(AyY_-tps.AyY_)>1e-8) return false;
00504   if (vcl_fabs(AyZ_-tps.AyZ_)>1e-8) return false;
00505   if (vcl_fabs(Az0_-tps.Az0_)>1e-8) return false;
00506   if (vcl_fabs(AzX_-tps.AzX_)>1e-8) return false;
00507   if (vcl_fabs(AzY_-tps.AzY_)>1e-8) return false;
00508   if (vcl_fabs(AzZ_-tps.AzZ_)>1e-8) return false;
00509   if (vnl_vector_ssd(Wx_,tps.Wx_)>1e-6) return false;
00510   if (vnl_vector_ssd(Wy_,tps.Wy_)>1e-6) return false;
00511   if (vnl_vector_ssd(Wz_,tps.Wz_)>1e-6) return false;
00512   return true;
00513 }
00514 
00515 //=======================================================================
00516 // Associated function: operator<<
00517 //=======================================================================
00518 
00519 void vsl_b_write(vsl_b_ostream& bfs, const mbl_thin_plate_spline_3d& b)
00520 {
00521     b.b_write(bfs);
00522 }
00523 
00524 //=======================================================================
00525 // Associated function: operator>>
00526 //=======================================================================
00527 
00528 void vsl_b_read(vsl_b_istream& bfs, mbl_thin_plate_spline_3d& b)
00529 {
00530     b.b_read(bfs);
00531 }
00532 
00533 //=======================================================================
00534 // Associated function: operator<<
00535 //=======================================================================
00536 
00537 vcl_ostream& operator<<(vcl_ostream& os,const mbl_thin_plate_spline_3d& b)
00538 {
00539   os << "mbl_thin_plate_spline_3d: ";
00540   vsl_indent_inc(os);
00541   b.print_summary(os);
00542   vsl_indent_dec(os);
00543   return os;
00544 }