00001
00002
00003
00004
00005
00006 #include "msm_similarity_aligner.h"
00007 #include <vnl/vnl_vector.h>
00008 #include <vsl/vsl_binary_loader.h>
00009 #include <vnl/algo/vnl_svd.h>
00010 #include <vnl/algo/vnl_cholesky.h>
00011 #include <vcl_cstddef.h>
00012 #include <vcl_cassert.h>
00013
00014
00015
00016
00017 vnl_vector<double> msm_similarity_aligner::inverse(const vnl_vector<double>& t) const
00018 {
00019 vnl_vector<double> q(4);
00020 double a=t[0]+1.0, b=t[1];
00021 double s2 = a*a + b*b;
00022 double a1=a/s2, b1=-b/s2;
00023 q[0] = a1-1.0;
00024 q[1] = b1;
00025 q[2] = -a1 * t[2] + b1 * t[3];
00026 q[3] = -b1 * t[2] - a1 * t[3];
00027 return q;
00028 }
00029
00030
00031 void msm_similarity_aligner::apply_transform(const msm_points& points,
00032 const vnl_vector<double>& trans,
00033 msm_points& new_points) const
00034 {
00035 new_points.vector().set_size(points.vector().size());
00036 double a=1.0+trans[0], b=trans[1], tx=trans[2], ty=trans[3];
00037
00038 const double* v1=points.vector().data_block();
00039 const double* end_v=points.vector().end();
00040 double* v2=new_points.vector().data_block();
00041 for (;v1!=end_v;v1+=2,v2+=2)
00042 {
00043 double x=v1[0],y=v1[1];
00044 v2[0]=a*x-b*y+tx;
00045 v2[1]=b*x+a*y+ty;
00046 }
00047 }
00048
00049
00050 double msm_similarity_aligner::scale(const vnl_vector<double>& trans) const
00051 {
00052 assert(trans.size()==4);
00053 double a=1.0+trans[0],b=trans[1];
00054 return vcl_sqrt(a*a+b*b);
00055 }
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070 void msm_similarity_aligner::calc_transform_from_ref(const msm_points& ref_pts,
00071 const msm_points& pts2,
00072 vnl_vector<double>& trans) const
00073 {
00074 vgl_point_2d<double> cog2 = pts2.cog();
00075 const double* p1 = ref_pts.vector().begin();
00076 const double* p2 = pts2.vector().begin();
00077 const double* p1_end = ref_pts.vector().end();
00078
00079 double xy_sum=0.0;
00080 double dot_sum=0.0;
00081
00082 for (;p1!=p1_end;p1+=2,p2+=2)
00083 {
00084 double dpx = p1[0];
00085 double dpy = p1[1];
00086 double dtx = p2[0]-cog2.x();
00087 double dty = p2[1]-cog2.y();
00088
00089 dot_sum += dpx*dtx + dpy*dty;
00090 xy_sum += dpx*dty - dpy*dtx;
00091 }
00092
00093 trans.set_size(4);
00094 trans[0] = dot_sum - 1.0;
00095 trans[1] = xy_sum;
00096 trans[2] = cog2.x();
00097 trans[3] = cog2.y();
00098 }
00099
00100
00101
00102 void msm_similarity_aligner::calc_transform(const msm_points& pts1,
00103 const msm_points& pts2,
00104 vnl_vector<double>& trans) const
00105 {
00106 vgl_point_2d<double> cog1 = pts1.cog();
00107 vgl_point_2d<double> cog2 = pts2.cog();
00108 const double* p1 = pts1.vector().begin();
00109 const double* p2 = pts2.vector().begin();
00110 const double* p1_end = pts1.vector().end();
00111
00112 double x2_sum=0.0;
00113 double xy_sum=0.0;
00114 double dot_sum=0.0;
00115
00116 for (;p1!=p1_end;p1+=2,p2+=2)
00117 {
00118 double dpx = p1[0]-cog1.x();
00119 double dpy = p1[1]-cog1.y();
00120 double dtx = p2[0]-cog2.x();
00121 double dty = p2[1]-cog2.y();
00122
00123 x2_sum += dpx*dpx + dpy*dpy;
00124 dot_sum += dpx*dtx + dpy*dty;
00125 xy_sum += dpx*dty - dpy*dtx;
00126 }
00127
00128 trans.set_size(4);
00129 double a = dot_sum/x2_sum;
00130 double b = xy_sum/x2_sum;
00131 trans[0] = a - 1.0;
00132 trans[1] = b;
00133 trans[2] = cog2.x() - (a*cog1.x() - b*cog1.y());
00134 trans[3] = cog2.y() - (b*cog1.x() + a*cog1.y());
00135 }
00136
00137
00138 inline vgl_point_2d<double> msm_wtd_cog(const msm_points& pts,
00139 const vnl_vector<double>& wts)
00140 {
00141 const double* v=pts.vector().data_block();
00142 const double* w=wts.data_block();
00143 const unsigned int n=pts.size();
00144 const double* end_v=v+2*n;
00145 double cx=0.0,cy=0.0,w_sum=0.0;
00146 for (;v!=end_v;v+=2,++w)
00147 { cx+=w[0]*v[0]; cy+=w[0]*v[1]; w_sum+=w[0]; }
00148
00149 if (w_sum>0) { cx/=w_sum; cy/=w_sum; }
00150 return vgl_point_2d<double>(cx,cy);
00151 }
00152
00153
00154
00155
00156 void msm_similarity_aligner::calc_transform_wt(const msm_points& pts1,
00157 const msm_points& pts2,
00158 const vnl_vector<double>& wts,
00159 vnl_vector<double>& trans) const
00160 {
00161 vgl_point_2d<double> cog1 = msm_wtd_cog(pts1,wts);
00162 vgl_point_2d<double> cog2 = msm_wtd_cog(pts2,wts);
00163 const double* p1 = pts1.vector().begin();
00164 const double* p2 = pts2.vector().begin();
00165 const double* w=wts.data_block();
00166 assert(wts.size()==pts1.size());
00167 const double* p1_end = pts1.vector().end();
00168
00169 double x2_sum=0.0;
00170 double xy_sum=0.0;
00171 double dot_sum=0.0;
00172
00173 for (;p1!=p1_end;p1+=2,p2+=2,++w)
00174 {
00175 double dpx = p1[0]-cog1.x();
00176 double dpy = p1[1]-cog1.y();
00177 double dtx = p2[0]-cog2.x();
00178 double dty = p2[1]-cog2.y();
00179
00180 x2_sum += w[0]*(dpx*dpx + dpy*dpy);
00181 dot_sum += w[0]*(dpx*dtx + dpy*dty);
00182 xy_sum += w[0]*(dpx*dty - dpy*dtx);
00183 }
00184
00185 trans.set_size(4);
00186 double a = dot_sum/x2_sum;
00187 double b = xy_sum/x2_sum;
00188 trans[0] = a - 1.0;
00189 trans[1] = b;
00190 trans[2] = cog2.x() - (a*cog1.x() - b*cog1.y());
00191 trans[3] = cog2.y() - (b*cog1.x() + a*cog1.y());
00192 }
00193
00194 #if 0 // function commented out
00195
00196
00197
00198 *** Incorrect implementation - assumption about CoG incorrect ***
00199 void msm_similarity_aligner::calc_transform_wt_mat(
00200 const msm_points& pts1,
00201 const msm_points& pts2,
00202 const vcl_vector<msm_wt_mat_2d>& wt_mat,
00203 vnl_vector<double>& trans) const
00204 {
00205 assert(pts2.size()==pts1.size());
00206 assert(wt_mat.size()==pts1.size());
00207
00208 msm_wt_mat_2d w_sum(0,0,0);
00209 double x1=0.0,y1=0.0;
00210 double x2=0.0,y2=0.0;
00211 const double* p1 = pts1.vector().begin();
00212 const double* p2 = pts2.vector().begin();
00213 const double* p1_end = pts1.vector().end();
00214 vcl_vector<msm_wt_mat_2d>::const_iterator w=wt_mat.begin();
00215 for (;p1!=p1_end;p1+=2,p2+=2,++w)
00216 {
00217 double wa=w->m11(), wb=w->m12(), wc=w->m22();
00218 w_sum += (*w);
00219 x1 += wa*p1[0]+wb*p1[1];
00220 y1 += wb*p1[0]+wc*p1[1];
00221 x2 += wa*p2[0]+wb*p2[1];
00222 y2 += wb*p2[0]+wc*p2[1];
00223 }
00224 msm_wt_mat_2d w_inv = w_sum.inverse();
00225 double v11=w_inv.m11(), v12=w_inv.m12(), v22=w_inv.m22();
00226 vgl_point_2d<double> cog1(v11*x1+v12*y1,v12*x1+v22*y1);
00227 vgl_point_2d<double> cog2(v11*x2+v12*y2,v12*x2+v22*y2);
00228 vcl_cout<<"CoG1: "<<cog1<<'\n'
00229 <<"CoG2: "<<cog2<<vcl_endl;
00230
00231
00232
00233 msm_wt_mat_2d S(0,0,0);
00234 double txx_sum = 0;
00235 double txy_sum = 0;
00236
00237 p1 = pts1.vector().begin();
00238 p2 = pts2.vector().begin();
00239 w=wt_mat.begin();
00240 for (;p1!=p1_end;p1+=2,p2+=2,++w)
00241 {
00242 double dpx = p1[0]-cog1.x();
00243 double dpy = p1[1]-cog1.y();
00244 double dtx = p2[0]-cog2.x();
00245 double dty = p2[1]-cog2.y();
00246
00247 double wa=w->m11(), wb=w->m12(), wc=w->m22();
00248
00249 double wx = wa*dpx + wb*dpy;
00250 double wy = wb*dpx + wc*dpy;
00251 double vx = wb*dpx - wa*dpy;
00252 double vy = wc*dpx - wb*dpy;
00253
00254 S += msm_wt_mat_2d(dpx*wx + dpy*wy,
00255 dpx*wy - dpy*wx,
00256 dpx*vy - dpy*vx);
00257
00258 txx_sum += dtx*wx + dty*wy;
00259 txy_sum += dtx*vx + dty*vy;
00260 }
00261
00262
00263
00264
00265
00266 trans.set_size(4);
00267 msm_wt_mat_2d S_inv = S.inverse();
00268 double a = S_inv.m11()*txx_sum + S_inv.m12()*txy_sum;
00269 double b = S_inv.m21()*txx_sum + S_inv.m22()*txy_sum;
00270
00271 trans[0] = a - 1.0;
00272 trans[1] = b;
00273 trans[2] = cog2.x() - (a*cog1.x() - b*cog1.y());
00274 trans[3] = cog2.y() - (b*cog1.x() + a*cog1.y());
00275 }
00276 #endif // 0
00277
00278
00279
00280
00281 void msm_similarity_aligner::calc_transform_wt_mat(
00282 const msm_points& pts1,
00283 const msm_points& pts2,
00284 const vcl_vector<msm_wt_mat_2d>& wt_mat,
00285 vnl_vector<double>& trans) const
00286 {
00287 assert(pts2.size()==pts1.size());
00288 assert(wt_mat.size()==pts1.size());
00289
00290
00291
00292 vgl_point_2d<double> cog1 = pts1.cog();
00293 vgl_point_2d<double> cog2 = pts2.cog();
00294
00295
00296
00297 const double* p1 = pts1.vector().begin();
00298 const double* p2 = pts2.vector().begin();
00299 const double* p1_end = pts1.vector().end();
00300 vcl_vector<msm_wt_mat_2d>::const_iterator w=wt_mat.begin();
00301
00302 msm_wt_mat_2d w_sum(0,0,0);
00303 msm_wt_mat_2d S(0,0,0);
00304 double tx_sum = 0, ty_sum=0;
00305 double txx_sum = 0, txy_sum = 0;
00306 double s31=0,s32=0,s41=0,s42=0;
00307
00308 p1 = pts1.vector().begin();
00309 p2 = pts2.vector().begin();
00310 w=wt_mat.begin();
00311 for (;p1!=p1_end;p1+=2,p2+=2,++w)
00312 {
00313 double dpx = p1[0]-cog1.x();
00314 double dpy = p1[1]-cog1.y();
00315 double dtx = p2[0]-cog2.x();
00316 double dty = p2[1]-cog2.y();
00317
00318 double wa=w->m11(), wb=w->m12(), wc=w->m22();
00319
00320 w_sum += (*w);
00321
00322 double wx = wa*dpx + wb*dpy;
00323 double wy = wb*dpx + wc*dpy;
00324 double vx = wb*dpx - wa*dpy;
00325 double vy = wc*dpx - wb*dpy;
00326 s31+=wx; s32+=vx;
00327 s41+=wy; s42+=vy;
00328
00329 S += msm_wt_mat_2d(dpx*wx + dpy*wy,
00330 dpx*wy - dpy*wx,
00331 dpx*vy - dpy*vx);
00332
00333
00334 txx_sum += dtx*wx + dty*wy;
00335 txy_sum += dtx*vx + dty*vy;
00336 tx_sum += wa*dtx+wb*dty;
00337 ty_sum += wb*dtx+wc*dty;
00338 }
00339
00340 vnl_matrix<double> M(4,4);
00341 M(0,0)=S.m11(); M(0,1)=S.m12(); M(0,2)=s31; M(0,3)=s41;
00342 M(1,0)=S.m21(); M(1,1)=S.m22(); M(1,2)=s32; M(1,3)=s42;
00343 M(2,0)= s31; M(2,1)= s32; M(2,2)=w_sum.m11(); M(2,3)=w_sum.m12();
00344 M(3,0)= s41; M(3,1)= s42; M(3,2)=w_sum.m21(); M(3,3)=w_sum.m22();
00345
00346 vnl_vector<double> rhs(4);
00347 rhs[0]=txx_sum; rhs[1]=txy_sum; rhs[2]=tx_sum; rhs[3]=ty_sum;
00348
00349
00350 vnl_cholesky chol(M,vnl_cholesky::estimate_condition);
00351 if (chol.rcond()>1e-6)
00352 {
00353 chol.solve(rhs,&trans);
00354 }
00355 else
00356 {
00357 vnl_svd<double> svd(M);
00358 trans = svd.solve(rhs);
00359 }
00360
00361 double a = trans[0], b=trans[1];
00362
00363 trans[0] = a - 1.0;
00364
00365 trans[2] += cog2.x() - (a*cog1.x() - b*cog1.y());
00366 trans[3] += cog2.y() - (b*cog1.x() + a*cog1.y());
00367 }
00368
00369
00370
00371 void msm_similarity_aligner::transform_wt_mat(
00372 const vcl_vector<msm_wt_mat_2d>& wt_mat,
00373 const vnl_vector<double>& trans,
00374 vcl_vector<msm_wt_mat_2d>& new_wt_mat) const
00375 {
00376 double a = 1+trans[0], b=trans[1];
00377 new_wt_mat.resize(wt_mat.size());
00378 for (unsigned i=0;i<wt_mat.size();++i)
00379 new_wt_mat[i]=wt_mat[i].transform_by(a,b);
00380 }
00381
00382
00383 vnl_vector<double> msm_similarity_aligner::compose(
00384 const vnl_vector<double>& pose1,
00385 const vnl_vector<double>& pose2) const
00386 {
00387 double a1=pose1[0]+1.0, b1=pose1[1];
00388 double a2=pose2[0]+1.0, b2=pose2[1];
00389
00390 vnl_vector<double> p(4);
00391 double a3 = a1*a2-b1*b2;
00392 double b3 = b1*a2+a1*b2;
00393 p[0]= a3-1.0;
00394 p[1]= b3;
00395 p[2]= a1*pose2[2]-b1*pose2[3]+pose1[2];
00396 p[3]= b1*pose2[2]+a1*pose2[3]+pose1[3];
00397 return p;
00398 }
00399
00400
00401
00402
00403 void msm_similarity_aligner::normalise_shape(msm_points& points) const
00404 {
00405 vgl_point_2d<double> cog = points.cog();
00406 points.translate_by(-cog.x(),-cog.y());
00407 double L = points.vector().magnitude();
00408 if (L>1e-6) points.scale_by(1.0/L);
00409 }
00410
00411
00412
00413
00414
00415
00416
00417
00418 void msm_similarity_aligner::align_set(const vcl_vector<msm_points>& points,
00419 msm_points& ref_mean_shape,
00420 vcl_vector<vnl_vector<double> >& pose_to_ref,
00421 vnl_vector<double>& average_pose) const
00422 {
00423 vcl_size_t n_shapes = points.size();
00424 assert(n_shapes>0);
00425 pose_to_ref.resize(n_shapes);
00426
00427
00428 ref_mean_shape = points[0];
00429 normalise_shape(ref_mean_shape);
00430
00431
00432 msm_points base_shape = ref_mean_shape;
00433
00434 vnl_vector<double> pose_from_ref;
00435 msm_points new_mean=ref_mean_shape;
00436 average_pose.set_size(4);
00437
00438 double change=1.0;
00439
00440 unsigned n_its=0;
00441
00442 while (change>1e-6 && n_its<10)
00443 {
00444 ref_mean_shape = new_mean;
00445 normalise_shape(ref_mean_shape);
00446
00447 average_pose.fill(0);
00448
00449 for (unsigned i=0;i<n_shapes;++i)
00450 {
00451 calc_transform_from_ref(ref_mean_shape,points[i],pose_from_ref);
00452 pose_to_ref[i]=inverse(pose_from_ref);
00453 average_pose+=pose_from_ref;
00454 }
00455
00456 mean_of_transformed(points,pose_to_ref,new_mean);
00457
00458
00459
00460
00461 change = (new_mean.vector()-ref_mean_shape.vector()).rms();
00462 n_its++;
00463 }
00464
00465
00466
00467 average_pose/=n_shapes;
00468 }
00469
00470
00471
00472 vcl_string msm_similarity_aligner::is_a() const
00473 {
00474 return vcl_string("msm_similarity_aligner");
00475 }
00476
00477
00478 msm_aligner* msm_similarity_aligner::clone() const
00479 {
00480 return new msm_similarity_aligner(*this);
00481 }
00482
00483