00001 #include "m23d_ortho_flexible_builder.h"
00002
00003
00004
00005
00006
00007 #include <m23d/m23d_rotation_from_ortho_projection.h>
00008 #include <m23d/m23d_scaled_ortho_projection.h>
00009 #include <m23d/m23d_set_q_constraint.h>
00010 #include <m23d/m23d_correction_matrix_error.h>
00011 #include <m23d/m23d_select_basis_views.h>
00012 #include <m23d/m23d_pure_ortho_projection.h>
00013
00014 #include <vnl/algo/vnl_svd.h>
00015 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00016 #include <vnl/algo/vnl_levenberg_marquardt.h>
00017 #include <vgl/vgl_vector_3d.h>
00018
00019 #include <vcl_iostream.h>
00020 #include <vcl_algorithm.h>
00021 #include <vcl_cstdlib.h>
00022 #include <vcl_cassert.h>
00023
00024
00025
00026
00027
00028
00029
00030 void m23d_ortho_flexible_builder::reconstruct_with_first_as_basis(const vnl_matrix<double>& P2D,
00031 unsigned n_modes)
00032 {
00033 partial_reconstruct(P2D,n_modes);
00034 refine();
00035 }
00036
00037
00038 inline void m23d_swap_rows(vnl_matrix<double>& M,
00039 unsigned i, unsigned j, unsigned n)
00040 {
00041 vnl_matrix<double> Mi = M.extract(n,M.cols(),i,0);
00042 M.update(M.extract(n,M.cols(),j,0),i,0);
00043 M.update(Mi,j,0);
00044 }
00045
00046
00047
00048
00049 void m23d_ortho_flexible_builder::reconstruct(
00050 const vcl_vector< vcl_vector< vgl_point_2d<double> > >& pt_vec_list,
00051 const unsigned& n_modes )
00052 {
00053
00054 int nf= pt_vec_list.size();
00055 int n0= pt_vec_list[0].size();
00056 vnl_matrix<double> D(2*nf,n0);
00057 for (int i=0; i<nf; ++i)
00058 {
00059 if ( (unsigned)n0!= pt_vec_list[i].size() )
00060 {
00061 vcl_cerr<< "ERROR m23d_ortho_rigid_builder::reconstruct()\n"
00062 << "problem with different numbers of pts\n"
00063 << "pt_vec_list[0].size()= " << pt_vec_list[0].size() << '\n'
00064 << "pt_vec_list[" << i << "].size()= " << pt_vec_list[i].size()
00065 << vcl_endl;
00066 vcl_abort();
00067 }
00068
00069 for (int p=0; p<n0; ++p)
00070 {
00071 D(2*i,p)= pt_vec_list[i][p].x();
00072 D(2*i+1,p)= pt_vec_list[i][p].y();
00073 }
00074 }
00075
00076 reconstruct(D, n_modes);
00077 }
00078
00079
00080
00081
00082
00083
00084
00085
00086 void m23d_ortho_flexible_builder::reconstruct(const vnl_matrix<double>& P2D, unsigned n_modes)
00087 {
00088 assert(P2D.rows()%2==0);
00089
00090 set_view_data(P2D);
00091
00092 vcl_vector<unsigned> basis = m23d_select_basis_views(P2Dc_,n_modes,1000);
00093
00094
00095 for (unsigned i=0;i<=n_modes;++i)
00096 m23d_swap_rows(P2Dc_,2*i,2*basis[i],2);
00097
00098 initial_decomposition(n_modes);
00099
00100 vnl_matrix<double> G;
00101 compute_correction(P_,G);
00102
00103
00104 P_=P_*G;
00105 vnl_svd<double> G_svd(G);
00106 P3D_=G_svd.inverse() * P3D_;
00107
00108 disambiguate_z();
00109 correct_coord_frame(P_,P3D_);
00110 refine();
00111
00112
00113 for (int i=n_modes;i>=0;--i)
00114 {
00115 m23d_swap_rows(P_,2*i,2*basis[i],2);
00116 m23d_swap_rows(P2Dc_,2*i,2*basis[i],2);
00117 m23d_swap_rows(pure_P_,2*i,2*basis[i],2);
00118 m23d_swap_rows(coeffs_,i,basis[i],1);
00119 }
00120 }
00121
00122
00123 void m23d_ortho_flexible_builder::set_view_data(const vnl_matrix<double>& P2D)
00124 {
00125 assert(P2D.rows()%2==0);
00126 unsigned ns = P2D.rows()/2;
00127
00128
00129 P2Dc_=P2D;
00130 cog_.resize(ns);
00131 for (unsigned i=0;i<ns;++i)
00132 {
00133 vnl_vector<double> row_x=P2D.get_row(2*i);
00134 vnl_vector<double> row_y=P2D.get_row(2*i+1);
00135 double cog_x = row_x.mean();
00136 double cog_y = row_y.mean();
00137 row_x-=cog_x;
00138 row_y-=cog_y;
00139 P2Dc_.set_row(2*i,row_x);
00140 P2Dc_.set_row(2*i+1,row_y);
00141 cog_[i]=vgl_point_2d<double>(cog_x,cog_y);
00142 }
00143 }
00144
00145
00146
00147 void m23d_ortho_flexible_builder::initial_decomposition(unsigned n_modes)
00148 {
00149 unsigned ns = P2Dc_.rows()/2;
00150 unsigned np = P2Dc_.cols();
00151
00152
00153
00154 vnl_svd<double> svd(P2Dc_);
00155 unsigned t=3*(1+n_modes);
00156 P_.set_size(2*ns,t);
00157 P3D_.set_size(t,np);
00158 for (unsigned i=0;i<t;++i)
00159 {
00160 P_.set_column(i,svd.W(i)*svd.U().get_column(i));
00161 P3D_.set_row(i,svd.V().get_column(i));
00162 }
00163
00164 vcl_cout<<"Initial reconstruction error: "<<(P_*P3D_-P2Dc_).rms()<<vcl_endl;
00165 }
00166
00167
00168
00169 void m23d_ortho_flexible_builder::disambiguate_z()
00170 {
00171 unsigned ns = P2Dc_.rows()/2;
00172 unsigned np = P2Dc_.cols();
00173
00174 for (unsigned i=0;i<np;++i)
00175 {
00176 if (P3D_(2,i)<0) break;
00177 if (P3D_(2,i)>0)
00178 {
00179
00180 for (unsigned j=0;j<np;++j) P3D_(2,j)*=-1;
00181 for (unsigned j=0;j<2*ns;++j) P_(j,2)*=-1;
00182 break;
00183 }
00184 }
00185 }
00186
00187
00188
00189
00190
00191
00192
00193 void m23d_ortho_flexible_builder::partial_reconstruct(const vnl_matrix<double>& P2D,
00194 unsigned n_modes)
00195 {
00196 assert(P2D.rows()%2==0);
00197
00198 set_view_data(P2D);
00199 initial_decomposition(n_modes);
00200
00201 vnl_matrix<double> G;
00202 compute_correction(P_,G);
00203
00204
00205 P_=P_*G;
00206 vnl_svd<double> G_svd(G);
00207 P3D_=G_svd.inverse() * P3D_;
00208
00209 disambiguate_z();
00210 correct_coord_frame(P_,P3D_);
00211 }
00212
00213
00214 static vnl_matrix<double> sym_matrix_from_vec(const vnl_vector<double>& v, unsigned n)
00215 {
00216 vnl_matrix<double> S(n,n);
00217 unsigned k=0;
00218 for (unsigned i=0;i<n;++i)
00219 for (unsigned j=0;j<=i;++j,++k)
00220 S(i,j)=S(j,i)=v[k];
00221 return S;
00222 }
00223
00224
00225 static vnl_matrix<double> am_solve_for_G0(const vnl_matrix<double>& A,
00226 const vnl_vector<double>& rhs)
00227 {
00228 unsigned n=3;
00229 vnl_svd<double> svd(A);
00230 vnl_vector<double> q0 = svd.solve(rhs);
00231 vnl_matrix<double> Q0=sym_matrix_from_vec(q0,n);
00232
00233
00234
00235
00236 vnl_symmetric_eigensystem<double> eig(Q0);
00237 vnl_matrix<double> Gk(n,3);
00238 for (unsigned i=0;i<3;++i)
00239 {
00240
00241
00242
00243
00244
00245 double s= vcl_sqrt( vcl_fabs(eig.get_eigenvalue(n-1-i)) );
00246 Gk.set_column(i, s*eig.get_eigenvector(n-1-i) );
00247 }
00248
00249 return Gk;
00250 }
00251
00252 static vnl_matrix<double> am_solve_for_Gk(const vnl_matrix<double>& A,
00253 const vnl_vector<double>& rhs,
00254 unsigned m, unsigned k)
00255 {
00256 m23d_correction_matrix_error err_fn(A,rhs,m,k);
00257
00258 vnl_levenberg_marquardt LM(err_fn);
00259
00260
00261 vnl_vector<double> g(9*(m+1),0.0);
00262 for (unsigned i=0;i<3;++i) g[9*k+4*i]=1.0;
00263
00264 if (!LM.minimize_using_gradient(g))
00265 {
00266 vcl_cout<<"LM failed!!" << vcl_endl;
00267 vcl_abort();
00268 }
00269
00270 vcl_cout<<"am_solve_for_Gk (k="<<k<<") RMS="<<err_fn.rms(g)<<vcl_endl;
00271
00272
00273 return vnl_matrix<double>(g.data_block(),3*(m+1),3);
00274 }
00275
00276 #if 0 // refinement of am_solve_for_Gk()
00277
00278 static vnl_vector<double> vec_from_sym_matrix(const vnl_matrix<double>& S)
00279 {
00280 unsigned n = S.rows();
00281 vnl_vector<double> v((n*(n+1))/2);
00282 unsigned k=0;
00283 for (unsigned i=0;i<n;++i)
00284 for (unsigned j=0;j<=i;++j,++k)
00285 v[k]=S(i,j);
00286 return v;
00287 }
00288
00289 static vnl_matrix<double> am_solve_for_Gk(const vnl_matrix<double>& A,
00290 const vnl_vector<double>& rhs,
00291 unsigned m, unsigned k)
00292 {
00293
00294 vnl_diag_matrix<double> W(nq);
00295 double w0=0.01*svd.W(0);
00296 for (unsigned i=0;i<nq;++i)
00297 {
00298 if (svd.W(i)>w0) W[i]=0.0;
00299 else W[i]=1.0;
00300 }
00301 vnl_matrix<double> K=svd.V()*W*svd.V().transpose();
00302 for (unsigned k=0;k<3;++k)
00303 {
00304 vnl_matrix<double> Q1=Gk*Gk.transpose();
00305 vnl_vector<double> q1=vec_from_sym_matrix(Q1);
00306 vcl_cout<<"Error for q1 = "<<(A*q1-rhs).rms()<<vcl_endl;
00307 vnl_vector<double> q2=q0+K*(q1-q0);
00308
00309 Q0=sym_matrix_from_vec(q2,n);
00310 vnl_symmetric_eigensystem<double> eig2(Q0);
00311 vcl_cout<<"Eigenvalues: "<<eig2.D.diagonal()<<vcl_endl;
00312 for (unsigned i=0;i<3;++i)
00313 {
00314 Gk.set_column(i,vcl_sqrt(eig2.get_eigenvalue(n-1-i))
00315 *eig2.get_eigenvector(n-1-i));
00316 }
00317 }
00318 vcl_cout<<"Gk*Gk'="<<Gk*Gk.transpose()<<vcl_endl;
00319
00320 return Gk;
00321 }
00322 #endif // 0
00323
00324 static void compute_Gk(const vnl_matrix<double> & M, unsigned k,
00325 vnl_matrix<double>& Gk)
00326 {
00327 assert(M.cols()>=3);
00328 unsigned m = M.cols()/3 -1;
00329
00330 vnl_matrix<double> A;
00331 vnl_vector<double> rhs;
00332 m23d_set_q_constraints(M,k,A,rhs);
00333
00334 if (m==0)
00335 Gk=am_solve_for_G0(A,rhs);
00336 else
00337 {
00338 Gk=am_solve_for_Gk(A,rhs,m,k);
00339 }
00340
00341 if (k==0)
00342 {
00343
00344
00345 vnl_matrix<double> M0=M.extract(2,3*(1+m))*Gk;
00346
00347
00348 vnl_matrix<double> R=m23d_rotation_from_ortho_projection(M0);
00349
00350
00351 Gk=Gk*R.transpose();
00352 }
00353 }
00354
00355
00356 void m23d_ortho_flexible_builder::compute_correction(
00357 const vnl_matrix<double>& M1,
00358 vnl_matrix<double>& G)
00359 {
00360 unsigned t = M1.cols();
00361 unsigned n_modes=t/3-1;
00362 G.set_size(t,t);
00363 for (unsigned k=0;k<=n_modes;++k)
00364 {
00365 vnl_matrix<double> Gk;
00366 compute_Gk(M1,k,Gk);
00367 G.update(Gk,0,k*3);
00368 }
00369 }
00370
00371 #if 0 // unused static function
00372 static double min_row_scale(const vnl_matrix<double>& M)
00373 {
00374 double r1=0,r2=0;
00375 for (unsigned j=0;j<3;++j)
00376 {
00377 r1+=M(0,j)*M(0,j);
00378 r2+=M(1,j)*M(1,j);
00379 }
00380 return vcl_sqrt(vcl_min(r1,r2));
00381 }
00382 #endif // 0
00383
00384
00385
00386
00387 void m23d_ortho_flexible_builder::correct_coord_frame(vnl_matrix<double>& M,
00388 vnl_matrix<double>& B)
00389 {
00390 unsigned m = M.cols()/3-1;
00391 unsigned ns = M.rows()/2;
00392 unsigned np = B.cols();
00393
00394 vnl_matrix<double> U0 = m23d_rotation_from_ortho_projection(M.extract(2,3));
00395 M.update(M.extract(2*ns,3) * U0.transpose());
00396 B.update(U0*B.extract(3,np));
00397
00398 for (unsigned j=1;j<=m;++j)
00399 {
00400
00401
00402
00403
00404
00405
00406 vcl_vector<vnl_matrix<double> > U;
00407 for (unsigned i=0;i<ns;++i)
00408 {
00409 vnl_matrix<double> Mi0=M.extract(2,3,2*i,0);
00410 vnl_matrix<double> Mij=M.extract(2,3,2*i,3*j);
00411 #if 0 // **** TEST
00412 if (Mij(0,0)/Mi0(0,0)<0) Mij*=-1;
00413 #endif
00414 vnl_matrix<double> Ui0 = m23d_rotation_from_ortho_projection(Mi0);
00415 vnl_matrix<double> Uij = m23d_rotation_from_ortho_projection(Mij);
00416 #if 0 // DEBUG
00417 vcl_cout<<Mij*Uij.transpose()<<vcl_endl;
00418 #endif
00419 U.push_back(Uij.transpose()*Ui0);
00420 #if 0 // DEBUG
00421 vcl_cout<<"j="<<j<<" i="<<i<<") C1:"<<vcl_endl
00422 <<Uij.transpose()*Ui0<<vcl_endl;
00423 #endif
00424
00425 for (unsigned k=0;k<3;++k) Uij(2,k)*=-1;
00426 U.push_back(Uij.transpose()*Ui0);
00427 #if 0 // DEBUG
00428 vcl_cout<<"j="<<j<<" i="<<i<<") C2:"<<vcl_endl
00429 <<Uij.transpose()*Ui0<<vcl_endl
00430 <<"Rel wts: "<<Mij(0,0)/Mi0(0,0)<<vcl_endl;
00431 #endif
00432 }
00433
00434
00435
00436
00437
00438 vnl_matrix<double> best_meanU;
00439 double best_rms = 1e99;
00440
00441 for (unsigned i=0;i<vcl_min(2*ns,40u);++i)
00442 {
00443 vnl_matrix<double> baseR = U[i];
00444 vnl_matrix<double> sumU(3,3,0.0), bestU;
00445 double sum_rms=0.0;
00446
00447 for (unsigned k=0;k<ns;++k)
00448 {
00449 double d_min=(U[2*k]-baseR).rms();
00450 bestU = U[2*k];
00451 double d1=(U[2*k]+baseR).rms();
00452 if (d1<d_min) { d_min=d1; bestU=-U[2*k]; }
00453 double d2=(U[2*k+1]-baseR).rms();
00454 if (d2<d_min) { d_min=d2; bestU=U[2*k+1]; }
00455 double d3=(U[2*k+1]+baseR).rms();
00456 if (d3<d_min) { d_min=d3; bestU=-U[2*k+1]; }
00457
00458 sumU+=bestU;
00459 sum_rms+=d_min;
00460 }
00461 sumU/=ns;
00462 sum_rms/=ns;
00463 if (i==0 || sum_rms<best_rms)
00464 {
00465 best_meanU=sumU;
00466 best_rms=sum_rms;
00467 }
00468 }
00469
00470 vnl_svd<double> Usvd(best_meanU);
00471 vnl_matrix<double> Uj = Usvd.U()*Usvd.V().transpose();
00472 M.update(M.extract(2*ns,3,0,3*j) * Uj,0,3*j);
00473 B.update(Uj.transpose()*B.extract(3,np,3*j,0),3*j,0);
00474 }
00475 }
00476
00477
00478
00479 void m23d_ortho_flexible_builder::make_pure_projections()
00480 {
00481 unsigned ns = P_.rows()/2;
00482 unsigned m = P_.cols()/3-1;
00483
00484 pure_P_.set_size(2*ns,3);
00485 coeffs_.set_size(ns,m+1);
00486
00487
00488 vnl_matrix<double> PI(2,3),Pzero(2,3,0.0);
00489 PI(0,0)=1; PI(0,1)=0; PI(0,2)=0;
00490 PI(1,0)=0; PI(1,1)=1; PI(1,2)=0;
00491
00492
00493 P_.update(PI,0,0);
00494 pure_P_.update(PI,0,0);
00495 coeffs_(0,0)=1.0;
00496 for (unsigned i=1;i<=m;++i)
00497 {
00498 P_.update(Pzero,0,3*i);
00499 coeffs_(0,i)=0.0;
00500 }
00501
00502
00503 for (unsigned i=1;i<=m;++i)
00504 for (unsigned j=0;j<=m;++j)
00505 {
00506 if (i==j)
00507 {
00508 vnl_matrix<double> P0=m23d_pure_ortho_projection(P_.extract(2,3,2*i,3*j));
00509 P_.update(P0,2*i,3*j);
00510 pure_P_.update(P0,2*i,0);
00511 coeffs_(i,j)=1.0;
00512 }
00513 else
00514 {
00515 P_.update(Pzero,2*i,3*j);
00516 coeffs_(i,j)=0.0;
00517 }
00518 }
00519
00520
00521
00522 for (unsigned i=m+1;i<ns;++i)
00523 {
00524
00525 vnl_matrix<double> Mi(m+1,6);
00526 for (unsigned j=0;j<=m;++j)
00527 {
00528 Mi(j,0)=P_(2*i,3*j); Mi(j,1)=P_(2*i,3*j+1); Mi(j,2)=P_(2*i,3*j+2);
00529 Mi(j,3)=P_(2*i+1,3*j); Mi(j,4)=P_(2*i+1,3*j+1); Mi(j,5)=P_(2*i+1,3*j+2);
00530 }
00531
00532
00533 vnl_svd<double> Mi_svd(Mi);
00534 vnl_vector<double> p=Mi_svd.V().get_column(0)*vcl_sqrt(2.0);
00535
00536 vnl_matrix<double> Pi0 = vnl_matrix<double>(p.data_block(),2,3);
00537 vnl_matrix<double> Pi = m23d_pure_ortho_projection(Pi0);
00538 vnl_vector<double> pi(Pi.data_block(),6);
00539
00540
00541
00542
00543
00544 vnl_vector<double> a = Mi*pi*0.5;
00545
00546 for (unsigned j=0;j<=m;++j)
00547 P_.update(Pi*a[j],2*i,3*j);
00548
00549 pure_P_.update(Pi,2*i,0);
00550 coeffs_.set_row(i,a);
00551 }
00552 }
00553
00554
00555 void m23d_ortho_flexible_builder::refine()
00556 {
00557 vcl_cout<<"RMS Reconstruction error before refine = "
00558 <<(P2Dc_-P_*P3D_).rms()<<vcl_endl;
00559
00560 make_pure_projections();
00561
00562 vnl_svd<double> svd(P_);
00563 P3D_ = svd.pinverse() * P2Dc_;
00564
00565
00566 vcl_cout<<"RMS Reconstruction error after refine = "
00567 <<(P2Dc_-P_*P3D_).rms()<<vcl_endl;
00568
00569 compute_mean(mean_shape_,mean_coeffs_);
00570 }
00571
00572
00573 void m23d_ortho_flexible_builder::compute_mean(vnl_matrix<double>& mean_shape,
00574 vnl_vector<double>& mean_coeffs)
00575 {
00576
00577 unsigned ns=coeffs_.rows();
00578 unsigned nm=coeffs_.cols();
00579 unsigned np=P3D_.cols();
00580 mean_coeffs.set_size(nm);
00581 mean_coeffs.fill(0.0);
00582 for (unsigned i=0;i<ns;++i) mean_coeffs+=coeffs_.get_row(i);
00583 mean_coeffs/=ns;
00584 mean_shape.set_size(3,np);
00585 mean_shape.fill(0.0);
00586 for (unsigned j=0;j<nm;++j)
00587 mean_shape += P3D_.extract(3,np,3*j,0) * mean_coeffs[j];
00588 }
00589
00590
00591 vnl_matrix<double> m23d_ortho_flexible_builder::shape(unsigned i) const
00592 {
00593 unsigned np=P3D_.cols();
00594 vnl_matrix<double> S(3,np,0.0);
00595 for (unsigned j=0;j<coeffs_.cols();++j)
00596 S += P3D_.extract(3,np,3*j,0) * coeffs_(i,j);
00597 return S;
00598 }
00599
00600
00601
00602 void m23d_ortho_flexible_builder::get_shape_3d_pts( vcl_vector< vgl_point_3d<double> >& pts ) const
00603 {
00604 #if 0
00605 if (P_.rows() < 2 || P_.cols() != 3 )
00606 {
00607 vcl_cerr<<"ERROR m23d_ortho_flexible_builder::get_shape_3d_pts()\n"
00608 <<"problem with size of P_\n"
00609 <<"P_.rows()= "<<P_.rows()<<'\n'
00610 <<"P_.cols()= "<<P_.cols()<<vcl_endl;
00611 vcl_abort();
00612 }
00613
00614
00615
00616
00617 vnl_matrix<double> P0= P_.extract(2,3);
00618 #endif // 0
00619
00620
00621 vnl_matrix<double> P0=shape(0);
00622
00623
00624
00625 vnl_matrix<double> R0=m23d_rotation_from_ortho_projection(P0);
00626
00627
00628 vnl_matrix<double> rot_pts_mat=R0* P3D_;
00629 mat_to_3d_pts( pts, rot_pts_mat );
00630
00631
00632 int np= pts.size();
00633 vgl_vector_3d<double> tran_vec( cog_[0].x(), cog_[0].y(), 0 );
00634 for (int p=0; p<np; ++p)
00635 {
00636 pts[p]= pts[p] + tran_vec;
00637 }
00638 }
00639
00640
00641
00642 void m23d_ortho_flexible_builder::mat_to_3d_pts(vcl_vector< vgl_point_3d<double> >& pt_vec,
00643 const vnl_matrix<double>& M) const
00644 {
00645 #if 0
00646
00647 if ( M.rows() != 3 )
00648 {
00649 vcl_cerr<<"ERROR m23d_ortho_flexible_builder::mat_to_3d_pts()\n"
00650 <<"problem with size of matrix\n"
00651 <<"M.rows()= "<<M.rows()<<'\n'
00652 <<"M.cols()= "<<M.cols()<<vcl_endl;
00653 vcl_abort();
00654 }
00655 #endif // 0
00656
00657 int np= M.cols();
00658 pt_vec.resize(np);
00659 for (int i=0; i<np; ++i)
00660 {
00661 pt_vec[i].set( M(0,i), M(1,i), M(2,i) );
00662 }
00663 }
00664