contrib/mul/vimt3d/vimt3d_transform_3d.cxx
Go to the documentation of this file.
00001 // This is mul/vimt3d/vimt3d_transform_3d.cxx
00002 #include "vimt3d_transform_3d.h"
00003 //:
00004 // \file
00005 // \brief A class to define and apply a 3D transformation up to affine.
00006 // \author Graham Vincent, Tim Cootes
00007 
00008 #include <vcl_cassert.h>
00009 #include <vcl_cstdlib.h>
00010 #include <vcl_sstream.h>
00011 #include <vsl/vsl_indent.h>
00012 #include <vnl/vnl_matrix_fixed.h>
00013 #include <vnl/vnl_vector.h>
00014 #include <vnl/algo/vnl_determinant.h>
00015 #include <vnl/vnl_math.h>
00016 #include <vul/vul_string.h>
00017 #include <vul/vul_sprintf.h>
00018 #include <mbl/mbl_read_props.h>
00019 #include <mbl/mbl_exception.h>
00020 #include <mbl/mbl_parse_sequence.h>
00021 
00022 
00023 //=======================================================================
00024 
00025 vnl_matrix<double> vimt3d_transform_3d::matrix() const
00026 {
00027   vnl_matrix<double> M(4,4);
00028   matrix(M);
00029   return M;
00030 }
00031 
00032 //=======================================================================
00033 
00034 void vimt3d_transform_3d::matrix(vnl_matrix<double>& M) const
00035 {
00036 #if 0 //grv
00037   if ((M.rows()!=4) || (M.columns()!=4)) M.resize(4,4);
00038 #endif
00039   M.set_size(4,4);
00040   double**m_data = M.data_array();
00041   m_data[0][0]=xx_; m_data[0][1]=xy_; m_data[0][2]=xz_; m_data[0][3]=xt_;
00042   m_data[1][0]=yx_; m_data[1][1]=yy_; m_data[1][2]=yz_; m_data[1][3]=yt_;
00043   m_data[2][0]=zx_; m_data[2][1]=zy_; m_data[2][2]=zz_; m_data[2][3]=zt_;
00044   m_data[3][0]=tx_; m_data[3][1]=ty_; m_data[3][2]=tz_; m_data[3][3]=tt_;
00045 }
00046 
00047 //=======================================================================
00048 // See also vnl_rotation_matrix(), vgl_rotation_3d, and vnl_quaternion
00049 void vimt3d_transform_3d::angles(double& phi_x, double& phi_y, double& phi_z) const
00050 {
00051   //nb in affine case will probablay have to store s_x, s_y, s_z etc somewhere else!
00052   //also won't work properly in rigid body case either!
00053   double det=+xx_*yy_*zz_-xx_*zy_*yz_-yx_*xy_*zz_+yx_*zy_*xz_+zx_*xy_*yz_-zx_*yy_*xz_;
00054 
00055   double xlen = vcl_sqrt(xx_*xx_ + yx_*yx_ + zx_*zx_)* vnl_math_sgn(det);
00056   double ylen = vcl_sqrt(xy_*xy_ + yy_*yy_ + zy_*zy_)* vnl_math_sgn(det);
00057   double zlen = vcl_sqrt(xz_*xz_ + yz_*yz_ + zz_*zz_)* vnl_math_sgn(det);
00058 
00059   double xx3 = xx_ / xlen;
00060   double xy3 = xy_ / ylen;
00061   double xz3 = xz_ / zlen;
00062   double yz3 = yz_ / zlen;
00063   double zz3 = zz_ / zlen;
00064 
00065   phi_x = vcl_atan2(-yz3,zz3);
00066   phi_y = vcl_atan2(-xz3*vcl_cos(phi_x),zz3);
00067   phi_z=vcl_atan2(-xy3,xx3);
00068 
00069   // nb the equation for phi_z doesn't work in affine case
00070   // because sy and sx aren't necessarily the same
00071 
00072   // calc scaling factor
00073   // ie assuming similarity transform here
00074   // assume s is always positive
00075   // to recover original angle
00076   double s;
00077   if (vcl_sin(phi_y) < 1e-20)
00078     s = 1.0;
00079   else
00080     s = vcl_fabs( xz3/ (-1*vcl_sin(phi_y) ) );
00081 
00082 #ifdef DEBUG
00083   vcl_cout<<"s= "<<s<<vcl_endl;
00084 #endif
00085 
00086   // the angles may be wrong by +-vnl_math::pi - we can
00087   // only tell by checking against the signs
00088   // of the original entries in the rotation matrix
00089   if (vcl_fabs(vcl_sin(phi_y)*s + xz3) > 1e-6)
00090   {
00091     if (phi_y > 0)
00092       phi_y -= vnl_math::pi;
00093     else
00094       phi_y += vnl_math::pi;
00095     //phi_y *= -1;
00096   }
00097 
00098   const double cos_y = vcl_cos(phi_y);
00099 
00100   if (vcl_fabs(vcl_sin(phi_x)*cos_y*s + yz3) > 1e-6 ||
00101       vcl_fabs(vcl_cos(phi_x)*cos_y*s - zz3) > 1e-6)
00102   {
00103     if (phi_x > 0)
00104       phi_x -= vnl_math::pi;
00105     else
00106       phi_x += vnl_math::pi;
00107   }
00108 
00109   if (vcl_fabs(vcl_cos(phi_z)*cos_y*s - xx3) > 1e-6 ||
00110       vcl_fabs(vcl_sin(phi_z)*cos_y*s + xy3) > 1e-6)
00111   {
00112     if (phi_z > 0)
00113       phi_z -= vnl_math::pi;
00114     else
00115       phi_z += vnl_math::pi;
00116   }
00117 
00118   // now compress the angles towards zero as much as possible
00119   // (we can add +-vnl_math::pi to each angle and negate phi_y without changing
00120   // the rotation matrix)
00121   int count = 0;
00122   if (vcl_fabs(phi_x) > vnl_math::pi/2) ++count;
00123   if (vcl_fabs(phi_y) > vnl_math::pi/2) ++count;
00124   if (vcl_fabs(phi_z) > vnl_math::pi/2) ++count;
00125 
00126   if (count > 1)
00127   {
00128     if (phi_x > 0)
00129       phi_x -= vnl_math::pi;
00130     else
00131       phi_x += vnl_math::pi;
00132 
00133     phi_y=-phi_y;
00134     if (phi_y > 0)
00135       phi_y -= vnl_math::pi;
00136     else
00137       phi_y += vnl_math::pi;
00138 
00139     if (phi_z > 0)
00140       phi_z -= vnl_math::pi;
00141     else
00142       phi_z += vnl_math::pi;
00143   }
00144 }
00145 
00146 //=======================================================================
00147 
00148 void vimt3d_transform_3d::params(vnl_vector<double>& v) const
00149 {
00150   switch (form_)
00151   {
00152    case (Identity):
00153     v.set_size(0);
00154     break;
00155    case (Translation):
00156     if (v.size()!=3) v.set_size(3);
00157     v[0]=xt_; v[1]=yt_; v[2]=zt_;
00158     break;
00159    case (ZoomOnly):
00160     if (v.size()!=6) v.set_size(6);
00161     v[0]=xx_; v[1]=yy_; v[2]=zz_;
00162     v[3]=xt_; v[4]=yt_; v[5]=zt_;
00163     break;
00164    case (RigidBody):
00165     if (v.size()!=6) v.set_size(6);
00166     angles(v[0],v[1],v[2]);
00167     v[3]=xt_; v[4]=yt_; v[5]=zt_;
00168     break;
00169    case (Similarity): // not sure this is right - kds
00170                       // I think it's fixed now -dac
00171     if (v.size()!=7) v.set_size(7);
00172     angles(v[1],v[2],v[3]);
00173     // compute scaling factor
00174     v[0]= xx_/ ( vcl_cos( v[2] ) *vcl_cos( v[3] ) );
00175     v[4]=xt_; v[5]=yt_; v[6]=zt_;
00176     break;
00177    case (Affine):     // not sure this is right - kds
00178                       // I'm sure it's not correct -dac
00179     {
00180       v.set_size(9);
00181       // computation of angles doesn't work unless
00182       // sx, sy, sz are all the same!
00183 
00184       angles(v[3],v[4],v[5]);
00185       // try to compute scaling factors
00186       double det=+xx_*yy_*zz_-xx_*zy_*yz_-yx_*xy_*zz_+yx_*zy_*xz_+zx_*xy_*yz_-zx_*yy_*xz_;
00187       v[0]=vcl_sqrt(xx_*xx_ + yx_*yx_ + zx_*zx_)* vnl_math_sgn(det);
00188       v[1]=vcl_sqrt(xy_*xy_ + yy_*yy_ + zy_*zy_)* vnl_math_sgn(det);
00189       v[2]=vcl_sqrt(xz_*xz_ + yz_*yz_ + zz_*zz_)* vnl_math_sgn(det);
00190       v[6]=xt_; v[7]=yt_; v[8]=zt_;
00191       break;
00192     }
00193    default:
00194     mbl_exception_error(mbl_exception_abort(
00195       vul_sprintf("vimt3d_transform_3d::params() Unexpected form: %d", form_) ));
00196   }
00197 }
00198 
00199 
00200 //=======================================================================
00201 void vimt3d_transform_3d::simplify(double tol /*=1e-10*/)
00202 {
00203   double rx, ry, rz;
00204   double sx, sy, sz;
00205   double det;
00206   switch (form_)
00207   {
00208    case Affine:
00209     { // Not really true affine, because shear is forbidden.
00210       angles(rx, ry, rz);
00211       double matrix_form[]= {xx_, yx_, zx_, xy_, yy_, zy_, xz_, yz_, zz_};
00212       vnl_matrix_fixed<double, 3, 3> X(matrix_form);
00213       vnl_matrix_fixed<double, 3, 3> S2 = X.transpose() * X;
00214       // if X=R*S then X'X = S'*R'*R*S
00215       // if R is a rotation matrix then R'*R=I and so X'X = S'*S = [s_x^2 0 0; 0 s_y^2 0; 0 0 s_z^2]
00216       if (S2(0,1)*S2(0,1) + S2(0,2)*S2(0,2) + S2(1,0)*S2(1,0) + 
00217           S2(1,2)*S2(1,2) + S2(2,0)*S2(2,0) + S2(2,1)*S2(2,1) >= tol*tol*6)
00218         return;
00219 
00220       // mirroring if det is negative;
00221       double mirror=vnl_math_sgn(vnl_determinant(X[0], X[1], X[2]));
00222 
00223       sx = vcl_sqrt(vcl_abs(S2(0,0))) * mirror;
00224       sy = vcl_sqrt(vcl_abs(S2(1,1))) * mirror;
00225       sz = vcl_sqrt(vcl_abs(S2(2,2))) * mirror;
00226       if (vnl_math_sqr(sx-sy) +  vnl_math_sqr(sx-sz) < tol*tol)
00227         this->set_similarity(sx, rx, ry, rz,
00228                              xt_, yt_, zt_ );
00229       else if (rx*rx+ry*ry+rz*rz < tol*tol)
00230         this->set_zoom_only(sx, sy, sz,
00231                             xt_, yt_, zt_);
00232       else
00233         return;
00234       simplify();
00235       return;
00236     }
00237    case Similarity:
00238     angles(rx, ry, rz);
00239     det=+xx_*yy_*zz_-xx_*zy_*yz_-yx_*xy_*zz_+yx_*zy_*xz_+zx_*xy_*yz_-zx_*yy_*xz_;
00240     sx=vcl_sqrt(xx_*xx_ + yx_*yx_ + zx_*zx_)* vnl_math_sgn(det);
00241     if (rx*rx+ry*ry+rz*rz < tol*tol)
00242       this->set_zoom_only(sx, xt_, yt_, zt_);
00243     else if (vnl_math_sqr(sx-1.0) < tol*tol)
00244       this->set_rigid_body(rx, ry, rz, xt_, yt_, zt_);
00245     else
00246       return;
00247     simplify();
00248     return;
00249 
00250    case RigidBody:
00251     angles(rx, ry, rz);
00252     if (rx*rx+ry*ry+rz*rz >= tol*tol)
00253       return;
00254     this->set_translation(xt_, yt_, zt_);
00255     simplify();
00256     return;
00257    case ZoomOnly:
00258     if (vnl_math_sqr(xx_-1.0) + vnl_math_sqr(yy_-1.0) + vnl_math_sqr(zz_-1.0) >= tol*tol)
00259       return;
00260     set_translation(xt_, yt_, zt_);
00261    case Translation:
00262     if (xt_*xt_+yt_*yt_+zt_*zt_<tol*tol)
00263       set_identity();
00264     return;
00265    case Identity:
00266     return;
00267    default:
00268     mbl_exception_error(mbl_exception_abort(
00269       vul_sprintf("vimt3d_transform_3d::simplify() Unexpected form: %d", form_) ));
00270   }
00271 }
00272 
00273 //=======================================================================
00274 
00275 void vimt3d_transform_3d::setCheck(int n1,int n2,const char* str) const
00276 {
00277   if (n1==n2) return;
00278   vcl_ostringstream ss;
00279   ss << "vimt3d_transform_3d::set() " << n1 << " parameters required for "
00280      << str << ". Passed " << n2;
00281   mbl_exception_error(mbl_exception_abort(ss.str()));
00282 }
00283 
00284 //=======================================================================
00285 
00286 void vimt3d_transform_3d::set(const vnl_vector<double>& v, Form form)
00287 {
00288   int n=v.size();
00289 
00290   switch (form)
00291   {
00292    case (Identity):
00293     set_identity();
00294     break;
00295    case (Translation):
00296     setCheck(3,n,"Translation");
00297     set_translation(v[0],v[1],v[2]);
00298     break;
00299    case (ZoomOnly):
00300     setCheck(6,n,"ZoomOnly");
00301     set_zoom_only( v[0],v[1],v[2],
00302                    v[3],v[4],v[5]);
00303     break;
00304    case (RigidBody):
00305     setCheck(6,n,"RigidBody");
00306     set_rigid_body( v[0],v[1],v[2],
00307                     v[3],v[4],v[5]);
00308     break;
00309    case (Similarity):
00310     setCheck(7,n,"Similarity");
00311     set_similarity( v[0],v[1],v[2],
00312                     v[3],v[4],v[5], v[6]);
00313     form_ = Similarity;
00314     inv_uptodate_=false;
00315     break;
00316    case (Affine): // not sure this is right - kds
00317                   // it works unless you call params()
00318                   // later which gives the wrong answer
00319                   // in affine case -dac
00320     setCheck(9,n,"Affine");
00321     set_affine( v[0],v[1],v[2],
00322                 v[3],v[4],v[5],
00323                 v[6],v[7],v[8]);
00324     form_ = Affine;
00325     inv_uptodate_=false;
00326     break;
00327    default:
00328     mbl_exception_error(mbl_exception_abort(
00329       vul_sprintf("vimt3d_transform_3d::set() Unexpected form: %d", form_) ));
00330   }
00331 }
00332 
00333 //=======================================================================
00334 // See also vnl_rotation_matrix(), vgl_rotation_3d, and vnl_quaternion
00335 void vimt3d_transform_3d::setRotMat( double r_x, double r_y, double r_z )
00336 {
00337   double sinx=vcl_sin(r_x);
00338   double siny=vcl_sin(r_y);
00339   double sinz=vcl_sin(r_z);
00340   double cosx=vcl_cos(r_x);
00341   double cosy=vcl_cos(r_y);
00342   double cosz=vcl_cos(r_z);
00343 
00344   // set R_mat = Rx*Ry*Rz
00345   xx_ =  cosy*cosz;
00346   xy_ = -cosy*sinz;
00347   xz_ = -siny;
00348   yx_ =  cosx*sinz - sinx*siny*cosz;
00349   yy_ =  cosx*cosz + sinx*siny*sinz;
00350   yz_ = -sinx*cosy;
00351   zx_ =  sinx*sinz + cosx*siny*cosz;
00352   zy_ =  sinx*cosz - cosx*siny*sinz;
00353   zz_ =  cosx*cosy;
00354 }
00355 
00356 //=======================================================================
00357 
00358 void vimt3d_transform_3d::set_origin( const vgl_point_3d<double> & p )
00359 {
00360   xt_=p.x()*tt_;
00361   yt_=p.y()*tt_;
00362   zt_=p.z()*tt_;
00363 
00364   if (form_ == Identity) form_=Translation;
00365 
00366   inv_uptodate_=false;
00367 }
00368 
00369 //=======================================================================
00370 
00371 void vimt3d_transform_3d::set_identity()
00372 {
00373   if (form_==Identity) return;
00374   form_=Identity;
00375 
00376   xx_=yy_=zz_=tt_=1;
00377   xy_=xz_=xt_=0;
00378   yx_=yz_=yt_=0;
00379   zx_=zy_=zt_=0;
00380   tx_=ty_=tz_=0;
00381   inv_uptodate_=false;
00382 }
00383 
00384 //=======================================================================
00385 
00386 void vimt3d_transform_3d::set_translation(double t_x, double t_y, double t_z)
00387 {
00388   if (t_x==0 && t_y==0 && t_z==0)
00389     set_identity();
00390   else
00391   {
00392     form_=Translation;
00393 
00394     // Set translation (first 3 elements of final column)
00395     xt_=t_x;
00396     yt_=t_y;
00397     zt_=t_z;
00398 
00399     // Set all other elements to defaults
00400     xx_=yy_=zz_=tt_=1;
00401     xy_=xz_=yx_=yz_=zx_=zy_=0;
00402     tx_=ty_=tz_=0;
00403   }
00404 
00405   inv_uptodate_=false;
00406 }
00407 
00408 //=======================================================================
00409 
00410 void vimt3d_transform_3d::set_zoom_only(double s_x, double s_y, double s_z,
00411                                         double t_x, double t_y, double t_z)
00412 {
00413   form_=ZoomOnly;
00414 
00415   // Set scaling (first 3 diagonal elements)
00416   xx_=s_x;
00417   yy_=s_y;
00418   zz_=s_z;
00419 
00420   // Set translation (first 3 elements of final column)
00421   xt_=t_x;
00422   yt_=t_y;
00423   zt_=t_z;
00424 
00425   // Set all other elements to defaults
00426   tx_=ty_=tz_=0;
00427   xy_=xz_=yx_=yz_=zx_=zy_=0;
00428   tt_=1;
00429 
00430   inv_uptodate_=false;
00431 }
00432 
00433 //=======================================================================
00434 
00435 void vimt3d_transform_3d::set_rigid_body(double r_x, double r_y, double r_z,
00436                                          double t_x, double t_y, double t_z)
00437 {
00438   if (r_x==0.0 && r_y==0.0 && r_z==0.0)
00439   {
00440     set_translation(t_x,t_y,t_z);
00441   }
00442   else
00443   {
00444     form_=RigidBody;
00445 
00446     // Set rotation matrix
00447     setRotMat(r_x,r_y,r_z);
00448 
00449     // Set translation (first 3 elements of final column)
00450     xt_=t_x;
00451     yt_=t_y;
00452     zt_=t_z;
00453 
00454     // Set all other elements to defaults
00455     tx_=0; ty_=0; tz_=0; tt_=1;
00456   }
00457 
00458   inv_uptodate_=false;
00459 }
00460 
00461 //=======================================================================
00462 
00463 void vimt3d_transform_3d::set_similarity(double s,
00464                                          double r_x, double r_y, double r_z,
00465                                          double t_x, double t_y, double t_z)
00466 {
00467   if (s==1.0)
00468   {
00469     set_rigid_body(r_x,r_y,r_z,t_x,t_y,t_z);
00470   }
00471   else
00472   {
00473     form_=Similarity;
00474 
00475     // Set rotation matrix
00476     setRotMat(r_x,r_y,r_z);
00477 
00478 #ifdef DEBUG // debug test
00479     double r_x1, r_y1, r_z1;
00480     angles( r_x1, r_y1, r_z1 );
00481     vcl_cout << "r_x = " << r_x  << vcl_endl
00482              << "r_x1= " << r_x1 << vcl_endl
00483              << "r_y = " << r_y  << vcl_endl
00484              << "r_y1= " << r_y1 << vcl_endl
00485              << "r_z = " << r_z  << vcl_endl
00486              << "r_z1= " << r_z1 << vcl_endl;
00487 #endif
00488 
00489     // Account for scaling (this actually means that scaling was done BEFORE rotation)
00490     xx_*=s;  xy_*=s;  xz_*=s;
00491     yx_*=s;  yy_*=s;  yz_*=s;
00492     zx_*=s;  zy_*=s;  zz_*=s;
00493 
00494     // Set translation (first 3 elements of final column)
00495     xt_=t_x;
00496     yt_=t_y;
00497     zt_=t_z;
00498 
00499     // Set all other elements to defaults
00500     tx_=0; ty_=0; tz_=0; tt_=1;
00501   }
00502   inv_uptodate_=false;
00503 }
00504 
00505 //=======================================================================
00506 
00507 void vimt3d_transform_3d::set_affine(double s_x, double s_y, double s_z,
00508                                      double r_x, double r_y, double r_z,
00509                                      double t_x, double t_y, double t_z)
00510 {
00511   form_=Affine;
00512 
00513   // Set rotation matrix
00514   setRotMat(r_x,r_y,r_z);
00515 
00516   // Account for scaling (this actually means that scaling was done BEFORE rotation)
00517   xx_*=s_x;  xy_*=s_y;  xz_*=s_z;
00518   yx_*=s_x;  yy_*=s_y;  yz_*=s_z;
00519   zx_*=s_x;  zy_*=s_y;  zz_*=s_z;
00520 
00521   // Set translation (first 3 elements of final column)
00522   xt_=t_x;
00523   yt_=t_y;
00524   zt_=t_z;
00525 
00526   // Set all other elements to defaults
00527   tx_=0; ty_=0; tz_=0; tt_=1;
00528 
00529   inv_uptodate_=false;
00530 }
00531 //=======================================================================
00532 
00533 void vimt3d_transform_3d::set_affine(double s_x, double s_y, double s_z,
00534                                      vgl_vector_3d<double> c_x,
00535                                      vgl_vector_3d<double> c_y,
00536                                      vgl_vector_3d<double> c_z,
00537                                      double t_x, double t_y, double t_z)
00538 {
00539   form_=Affine;
00540 
00541   // Set rotation matrix
00542   xx_ = c_x.x(); xy_ = c_y.x(); xz_ = c_z.x();
00543   yx_ = c_x.y(); yy_ = c_y.y(); yz_ = c_z.y();
00544   zx_ = c_x.z(); zy_ = c_y.z(); zz_ = c_z.z();
00545 
00546 
00547   // Account for scaling (this actually means that scaling was done BEFORE rotation)
00548   xx_*=s_x;  xy_*=s_y;  xz_*=s_z;
00549   yx_*=s_x;  yy_*=s_y;  yz_*=s_z;
00550   zx_*=s_x;  zy_*=s_y;  zz_*=s_z;
00551 
00552   // Set translation (first 3 elements of final column)
00553   xt_=t_x;
00554   yt_=t_y;
00555   zt_=t_z;
00556 
00557   // Set all other elements to defaults
00558   tx_=0; ty_=0; tz_=0; tt_=1;
00559 
00560   inv_uptodate_=false;
00561 }
00562 
00563 //=======================================================================
00564 
00565 void vimt3d_transform_3d::set_affine(const vgl_point_3d<double>& p,
00566                                      const vgl_vector_3d<double>& u,
00567                                      const vgl_vector_3d<double>& v,
00568                                      const vgl_vector_3d<double>& w)
00569 {
00570   form_=Affine;
00571 
00572 #ifndef NDEBUG
00573   const double tol=1e-6;
00574 
00575   // Get normalized vectors
00576   vgl_vector_3d<double> uh = normalized(u);
00577   vgl_vector_3d<double> vh = normalized(v);
00578   vgl_vector_3d<double> wh = normalized(w);
00579 
00580   // Test for orthogonality of input vectors
00581   assert(vcl_fabs(dot_product(uh,vh))<tol);
00582   assert(vcl_fabs(dot_product(vh,wh))<tol);
00583   assert(vcl_fabs(dot_product(wh,uh))<tol);
00584 
00585   // Test for right-handedness of input vectors
00586   assert(vcl_fabs((cross_product(uh,vh)-wh).length())<tol);
00587 #endif
00588 
00589   // Set rotation and scaling (this actually means that scaling was done BEFORE rotation)
00590   xx_=u.x();  xy_=v.x();  xz_=w.x();
00591   yx_=u.y();  yy_=v.y();  yz_=w.y();
00592   zx_=u.z();  zy_=v.z();  zz_=w.z();
00593 
00594   // Set translation (first 3 elements of final column)
00595   xt_=p.x();
00596   yt_=p.y();
00597   zt_=p.z();
00598 
00599   // Set final row with default values (for all transforms up to affine)
00600   tx_=0; ty_=0; tz_=0; tt_=1;
00601 
00602   inv_uptodate_=false;
00603 }
00604 
00605 //=======================================================================
00606 
00607 vimt3d_transform_3d vimt3d_transform_3d::inverse() const
00608 {
00609   if (!inv_uptodate_) calcInverse();
00610 
00611   vimt3d_transform_3d inv;
00612 
00613   inv.xx_ = xx2_; inv.xy_ = xy2_; inv.xz_ = xz2_; inv.xt_ = xt2_;
00614   inv.yx_ = yx2_; inv.yy_ = yy2_; inv.yz_ = yz2_; inv.yt_ = yt2_;
00615   inv.zx_ = zx2_; inv.zy_ = zy2_; inv.zz_ = zz2_; inv.zt_ = zt2_;
00616   inv.tx_ = tx2_; inv.ty_ = ty2_; inv.tz_ = tz2_; inv.tt_ = tt2_;
00617 
00618   inv.xx2_ = xx_; inv.xy2_ = xy_; inv.xz2_ = xz_; inv.xt2_ = xt_;
00619   inv.yx2_ = yx_; inv.yy2_ = yy_; inv.yz2_ = yz_; inv.yt2_ = yt_;
00620   inv.zx2_ = zx_; inv.zy2_ = zy_; inv.zz2_ = zz_; inv.zt2_ = zt_;
00621   inv.tx2_ = tx_; inv.ty2_ = ty_; inv.tz2_ = tz_; inv.tt2_ = tt_;
00622 
00623   inv.form_ = form_;
00624   inv.inv_uptodate_ = true;
00625 
00626   return inv;
00627 }
00628 
00629 //=======================================================================
00630 
00631 void vimt3d_transform_3d::calcInverse() const
00632 {
00633   xx2_=yy2_=zz2_=tt2_=1;
00634   xy2_ = xz2_= xt2_ = yx2_ = yz2_= yt2_ = zx2_ = zy2_ = zt2_ = tx2_ = ty2_ = tz2_ = 0;
00635 
00636   switch (form_)
00637   {
00638    case Identity :
00639     break;
00640    case Translation :
00641     xt2_=-xt_;
00642     yt2_=-yt_;
00643     zt2_=-zt_;
00644     break;
00645    case ZoomOnly :
00646     xx2_=1.0/xx_;
00647     yy2_=1.0/yy_;
00648     zz2_=1.0/zz_;
00649     xt2_=-xt_/xx_;
00650     yt2_=-yt_/yy_;
00651     zt2_=-zt_/zz_;
00652     break;
00653    case RigidBody :
00654     // transpose x,y,z part
00655     xx2_=xx_;
00656     xy2_=yx_;
00657     xz2_=zx_;
00658     yx2_=xy_;
00659     yy2_=yy_;
00660     yz2_=zy_;
00661     zx2_=xz_;
00662     zy2_=yz_;
00663     zz2_=zz_;
00664     xt2_=-(xx2_*xt_ + xy2_*yt_ + xz2_*zt_);
00665     yt2_=-(yx2_*xt_ + yy2_*yt_ + yz2_*zt_);
00666     zt2_=-(zx2_*xt_ + zy2_*yt_ + zz2_*zt_);
00667     break;
00668    case Similarity :
00669    case Affine : {
00670       // affine inverse (plugged in from symbolic matlab)
00671       double det=-xx_*yy_*zz_+xx_*zy_*yz_+yx_*xy_*zz_-yx_*zy_*xz_-zx_*xy_*yz_+zx_*yy_*xz_;
00672       if (det==0)
00673       {
00674         vcl_cerr<<"vimt3d_transform_3d() : No inverse exists for this affine transform (det==0)\n";
00675         vcl_abort();
00676       }
00677 
00678       xx2_=(-yy_*zz_+zy_*yz_)/det;
00679       xy2_=( xy_*zz_-zy_*xz_)/det;
00680       xz2_=(-xy_*yz_+yy_*xz_)/det;
00681       xt2_=(xy_*yz_*zt_-xy_*yt_*zz_-yy_*xz_*zt_+yy_*xt_*zz_+zy_*xz_*yt_-zy_*xt_*yz_)/det;
00682 
00683       yx2_=( yx_*zz_-zx_*yz_)/det;
00684       yy2_=(-xx_*zz_+zx_*xz_)/det;
00685       yz2_=( xx_*yz_-yx_*xz_)/det;
00686       yt2_=(-xx_*yz_*zt_+xx_*yt_*zz_+yx_*xz_*zt_-yx_*xt_*zz_-zx_*xz_*yt_+zx_*xt_*yz_)/det;
00687 
00688       zx2_=(-yx_*zy_+zx_*yy_)/det;
00689       zy2_=( xx_*zy_-zx_*xy_)/det;
00690       zz2_=(-xx_*yy_+yx_*xy_)/det;
00691       zt2_=( xx_*yy_*zt_-xx_*yt_*zy_-yx_*xy_*zt_+yx_*xt_*zy_+zx_*xy_*yt_-zx_*xt_*yy_)/det;
00692 
00693       break; }
00694    default:
00695     mbl_exception_error(mbl_exception_abort(
00696       vul_sprintf("vimt3d_transform_3d::calcInverse() Unexpected form: %d", form_) ));
00697   }
00698 
00699   inv_uptodate_=true;
00700 }
00701 
00702 //=======================================================================
00703 
00704 bool vimt3d_transform_3d::operator==( const vimt3d_transform_3d& t) const
00705 {
00706   return
00707     xx_ == t.xx_ &&
00708     yy_ == t.yy_ &&
00709     zz_ == t.zz_ &&
00710     tt_ == t.tt_ &&
00711     xy_ == t.xy_ &&
00712     xz_ == t.xz_ &&
00713     xt_ == t.xt_ &&
00714     yx_ == t.yx_ &&
00715     yz_ == t.yz_ &&
00716     yt_ == t.yt_ &&
00717     zx_ == t.zx_ &&
00718     zy_ == t.zy_ &&
00719     zt_ == t.zt_ &&
00720     tx_ == t.tx_ &&
00721     ty_ == t.ty_ &&
00722     tz_ == t.tz_ ;
00723 }
00724 
00725 //=======================================================================
00726 //: Calculates the product LR
00727 // \param L  Transform
00728 // \param R  Transform
00729 // \return Transform LR = R followed by L
00730 vimt3d_transform_3d operator*(const vimt3d_transform_3d& L, const vimt3d_transform_3d& R)
00731 {
00732   vimt3d_transform_3d T;
00733 
00734   if (L.form() == vimt3d_transform_3d::Identity)
00735     return R;
00736   else
00737   if (R.form() == vimt3d_transform_3d::Identity)
00738     return L;
00739   else
00740   {
00741   /// full multiplication - inefficient but works for
00742   // arbitrary 4*4 transformation matrix
00743     T.xx_ = L.xx_*R.xx_ + L.xy_*R.yx_+ L.xz_*R.zx_ + L.xt_*R.tx_;
00744     T.xy_ = L.xx_*R.xy_ + L.xy_*R.yy_+ L.xz_*R.zy_ + L.xt_*R.ty_;
00745     T.xz_ = L.xx_*R.xz_ + L.xy_*R.yz_+ L.xz_*R.zz_ + L.xt_*R.tz_;
00746     T.xt_ = L.xx_*R.xt_ + L.xy_*R.yt_+ L.xz_*R.zt_ + L.xt_*R.tt_;
00747 
00748     T.yx_ = L.yx_*R.xx_ + L.yy_*R.yx_+ L.yz_*R.zx_ + L.yt_*R.tx_;
00749     T.yy_ = L.yx_*R.xy_ + L.yy_*R.yy_+ L.yz_*R.zy_ + L.yt_*R.ty_;
00750     T.yz_ = L.yx_*R.xz_ + L.yy_*R.yz_+ L.yz_*R.zz_ + L.yt_*R.tz_;
00751     T.yt_ = L.yx_*R.xt_ + L.yy_*R.yt_+ L.yz_*R.zt_ + L.yt_*R.tt_;
00752 
00753     T.zx_ = L.zx_*R.xx_ + L.zy_*R.yx_+ L.zz_*R.zx_ + L.zt_*R.tx_;
00754     T.zy_ = L.zx_*R.xy_ + L.zy_*R.yy_+ L.zz_*R.zy_ + L.zt_*R.ty_;
00755     T.zz_ = L.zx_*R.xz_ + L.zy_*R.yz_+ L.zz_*R.zz_ + L.zt_*R.tz_;
00756     T.zt_ = L.zx_*R.xt_ + L.zy_*R.yt_+ L.zz_*R.zt_ + L.zt_*R.tt_;
00757 
00758     T.tx_ = L.tx_*R.xx_ + L.ty_*R.yx_+ L.tz_*R.zx_ + L.tt_*R.tx_;
00759     T.ty_ = L.tx_*R.xy_ + L.ty_*R.yy_+ L.tz_*R.zy_ + L.tt_*R.ty_;
00760     T.tz_ = L.tx_*R.xz_ + L.ty_*R.yz_+ L.tz_*R.zz_ + L.tt_*R.tz_;
00761     T.tt_ = L.tx_*R.xt_ + L.ty_*R.yt_+ L.tz_*R.zt_ + L.tt_*R.tt_;
00762 
00763     // now set the type using the type of L and R
00764     // not sure this right - kds
00765     if (R.form() == L.form())
00766       T.form_ = R.form();
00767     else
00768     {
00769       if (R.form() == vimt3d_transform_3d::Affine ||
00770           L.form() == vimt3d_transform_3d::Affine)
00771           T.form_ = vimt3d_transform_3d::Affine;
00772       else
00773       if (R.form() == vimt3d_transform_3d::Similarity ||
00774           L.form() == vimt3d_transform_3d::Similarity)
00775           T.form_ = vimt3d_transform_3d::Similarity;
00776       else
00777       if (R.form() == vimt3d_transform_3d::RigidBody ||
00778           L.form() == vimt3d_transform_3d::RigidBody)
00779       {
00780         if (R.form() == vimt3d_transform_3d::ZoomOnly)
00781         {
00782           if (R.xx_ == R.yy_ &&
00783               R.xx_ == R.zz_)
00784             T.form_ = vimt3d_transform_3d::Similarity;
00785           else
00786             T.form_ = vimt3d_transform_3d::Affine;
00787         }
00788         else
00789         if (L.form() == vimt3d_transform_3d::ZoomOnly)
00790         {
00791           if (L.xx_ == L.yy_ &&
00792               L.xx_ == L.zz_)
00793             T.form_ = vimt3d_transform_3d::Similarity;
00794           else
00795             T.form_ = vimt3d_transform_3d::Affine;
00796         }
00797         else
00798           T.form_ = vimt3d_transform_3d::RigidBody;
00799       }
00800       else
00801       if (R.form() == vimt3d_transform_3d::ZoomOnly ||
00802           L.form() == vimt3d_transform_3d::ZoomOnly)
00803           T.form_ = vimt3d_transform_3d::ZoomOnly;
00804       else
00805         T.form_ = vimt3d_transform_3d::Translation;
00806     }
00807   }
00808 
00809   T.inv_uptodate_ = false;
00810 
00811   return T;
00812 }
00813 
00814 //=======================================================================
00815 void vimt3d_transform_3d::print_summary(vcl_ostream& o) const
00816 {
00817   o << vsl_indent()<< "Form: ";
00818   vsl_indent_inc(o);
00819   switch (form_)
00820   {
00821    case Identity:
00822     o << "Identity\n";
00823     break;
00824 
00825    case Translation: {
00826     vnl_vector<double> p(3);
00827     params(p);
00828     o << "Translation (" << p(0) << ',' << p(1) << ',' << p(2) << ")\n";
00829     break; }
00830 
00831    case ZoomOnly: {
00832     vnl_vector<double> p(6);
00833     params(p);
00834     o << "ZoomOnly\n"
00835       << vsl_indent()<< "scale factor = (" << p(0) << ',' << p(1) << ',' << p(2) << ")\n"
00836       << vsl_indent() << "translation = (" << p(3) << ',' << p(4) << ',' << p(5) << ")\n";
00837     break; }
00838 
00839    case RigidBody: {
00840     vnl_vector<double> p(6);
00841     params(p);
00842     o << "RigidBody\n"
00843       << vsl_indent()<< "angles = " << p(0) << ',' << p(1) << ',' << p(2) << '\n'
00844       << vsl_indent()<< "translation = (" << p(3) << ',' << p(4) << ',' << p(5) << ")\n";
00845     break; }
00846 
00847    case Similarity: {
00848     vnl_vector<double> p(7);
00849     params(p);
00850     // not sure this is right - kds
00851     // returns euler angles of rotation
00852     // which might not be same as rotations specified by set command
00853     // cos euler angles not unique
00854     o << "Similarity\n"
00855       << vsl_indent()<< "scale factor = " << p(0) << '\n'
00856       << vsl_indent()<< "angles = " << p(1) << ',' << p(2) << ',' << p(3) << '\n'
00857       << vsl_indent()<< "translation = (" << p(4) << ',' << p(5) << ',' << p(6) << ")\n";
00858     break; }
00859    case Affine: {
00860     vnl_vector<double> p(9);
00861     params(p);
00862     // not sure this is right - kds
00863     // params(p) call is broken for affine
00864     // only works if sx=sy=sz in set command
00865     o << "Affine\n"
00866       << vsl_indent()<< "scale factors = " << p(0) << ',' << p(1) << ',' << p(2) << '\n'
00867       << vsl_indent()<< "angles = " << p(3) << ',' << p(4) << ',' << p(5) << '\n'
00868       << vsl_indent()<< "translation = (" << p(6) << ',' << p(7) << ',' << p(8) << ")\n";
00869     break; }
00870    default:
00871     mbl_exception_error(mbl_exception_abort(
00872       vul_sprintf("vimt3d_transform_3d::print_summary() Unexpected form: %d", form_) ));
00873   }
00874   vsl_indent_dec(o);
00875 }
00876 
00877 //=======================================================================
00878 // Print class to os
00879 void vimt3d_transform_3d::print_all(vcl_ostream& os) const
00880 {
00881   os << vsl_indent() << "Form: ";
00882   switch (form_)
00883   {
00884    case Identity:
00885     os << "Identity\n";
00886     break;
00887 
00888    case Translation:
00889     os << "Translation\n";
00890     break;
00891 
00892    case ZoomOnly:
00893     os << "ZoomOnly\n";
00894     break;
00895 
00896    case RigidBody:
00897     os << "RigidBody\n";
00898     break;
00899 
00900    case Similarity:
00901     os << "Similarity\n";
00902     break;
00903 
00904    case Affine:
00905     os << "Affine\n";
00906     break;
00907    default:
00908     mbl_exception_error(mbl_exception_abort(
00909       vul_sprintf("vimt3d_transform_3d::print_all() Unexpected form: %d", form_) ));
00910   }
00911 
00912   os << vsl_indent() << "Matrix:\n";
00913   vsl_indent_inc(os);
00914   os << vsl_indent() << xx_ << ' ' << xy_ << ' ' << xz_ << ' ' << xt_ << '\n'
00915      << vsl_indent() << yx_ << ' ' << yy_ << ' ' << yz_ << ' ' << yt_ << '\n'
00916      << vsl_indent() << zx_ << ' ' << zy_ << ' ' << zz_ << ' ' << zt_ << '\n'
00917      << vsl_indent() << tx_ << ' ' << ty_ << ' ' << tz_ << ' ' << tt_ << '\n';
00918   vsl_indent_dec(os);
00919 }
00920 
00921 
00922 //=======================================================================
00923 void vimt3d_transform_3d::config(vcl_istream& is)
00924 {
00925   mbl_read_props_type props = mbl_read_props_ws(is);
00926   vcl_string form = props.get_required_property("form");
00927   vul_string_downcase(form);
00928 
00929   bool done=false;
00930 
00931   if (form == "identity")
00932   {
00933     done=true;
00934     form_ = Identity;
00935   }
00936   else if (form == "translation")
00937     form_ = Translation;
00938   else if (form == "zoomonly")
00939     form_ = ZoomOnly;
00940   else if (form == "rigidbody")
00941     form_ = RigidBody;
00942   else if (form == "similarity")
00943     form_ = Similarity;
00944   else if (form == "affine")
00945     form_ = Affine;
00946   else
00947     throw mbl_exception_parse_error("Unknown transformation: \"" + form + "\"");
00948 
00949   vcl_string vector = props.get_optional_property("vector");
00950   if (!vector.empty())
00951   {
00952     vcl_istringstream ss(vector);
00953 
00954     vcl_vector<double> vec1;
00955     mbl_parse_sequence(ss, vcl_back_inserter(vec1), double());
00956     if (vec1.empty())
00957       throw mbl_exception_parse_error("Could not find elements for transformation vector: \""+vector+"\"");
00958     vnl_vector<double> vec2(&vec1.front(), vec1.size());
00959     try
00960     { // translate exception abort into parse error.
00961       this->set(vec2, form_);
00962     }
00963     catch (mbl_exception_abort & e)
00964     {
00965       throw mbl_exception_parse_error(e.what());
00966     }
00967     done = true;
00968   }
00969   if (!done && form_==Translation)
00970   {
00971     this->set_translation(
00972       vul_string_atof(props.get_required_property("t_x")),
00973       vul_string_atof(props.get_required_property("t_y")),
00974       vul_string_atof(props.get_required_property("t_z")) );
00975     done = true;
00976   }
00977   if (!done && form_==ZoomOnly)
00978   {
00979     vcl_string s_str = props.get_optional_property("s");
00980     if (!s_str.empty())
00981       this->set_zoom_only(
00982         vul_string_atof(s_str),
00983         vul_string_atof(props.get_optional_property("t_x")),
00984         vul_string_atof(props.get_optional_property("t_y")),
00985         vul_string_atof(props.get_optional_property("t_z")) );
00986     else
00987       this->set_zoom_only(
00988         vul_string_atof(props.get_optional_property("s_x")),
00989         vul_string_atof(props.get_optional_property("s_y")),
00990         vul_string_atof(props.get_optional_property("s_z")),
00991         vul_string_atof(props.get_optional_property("t_x")),
00992         vul_string_atof(props.get_optional_property("t_y")),
00993         vul_string_atof(props.get_optional_property("t_z")) );
00994     done = true;
00995   }
00996   if (!done && form_==RigidBody)
00997   {
00998     set_rigid_body(
00999       vul_string_atof(props.get_optional_property("r_x")),
01000       vul_string_atof(props.get_optional_property("r_y")),
01001       vul_string_atof(props.get_optional_property("r_z")),
01002       vul_string_atof(props.get_optional_property("t_x")),
01003       vul_string_atof(props.get_optional_property("t_y")),
01004       vul_string_atof(props.get_optional_property("t_z")) );
01005     done = true;
01006   }
01007 
01008   if (!done) throw mbl_exception_parse_error("Not enough transformation values specified");
01009 
01010   mbl_read_props_look_for_unused_props(
01011     "vimt3d_transform_3d::config", props, mbl_read_props_type());
01012   return;
01013 }
01014 
01015 
01016 //=======================================================================
01017 
01018 void vimt3d_transform_3d::b_write(vsl_b_ostream& bfs) const
01019 {
01020   const short version_no = 2;
01021   vsl_b_write(bfs,version_no);
01022   vsl_b_write(bfs,int(form_));
01023   vsl_b_write(bfs,xx_); vsl_b_write(bfs,xy_); vsl_b_write(bfs,xz_); vsl_b_write(bfs,xt_);
01024   vsl_b_write(bfs,yx_); vsl_b_write(bfs,yy_); vsl_b_write(bfs,yz_); vsl_b_write(bfs,yt_);
01025   vsl_b_write(bfs,zx_); vsl_b_write(bfs,zy_); vsl_b_write(bfs,zz_); vsl_b_write(bfs,zt_);
01026   vsl_b_write(bfs,tx_); vsl_b_write(bfs,ty_); vsl_b_write(bfs,tz_); vsl_b_write(bfs,tt_);
01027 }
01028 
01029 //=======================================================================
01030 
01031 void vimt3d_transform_3d::b_read(vsl_b_istream& bfs)
01032 {
01033   short version;
01034   vsl_b_read(bfs,version);
01035   int f;
01036   switch (version)
01037   {
01038    case 1:
01039     vsl_b_read(bfs,f);
01040     if (f==0) // Old Form enum had "Undefined" as the first value. It is never needed, and so was removed.
01041       set_identity();
01042     else
01043     {
01044       form_=Form(f-1);
01045       vsl_b_read(bfs,xx_); vsl_b_read(bfs,xy_); vsl_b_read(bfs,xz_); vsl_b_read(bfs,xt_);
01046       vsl_b_read(bfs,yx_); vsl_b_read(bfs,yy_); vsl_b_read(bfs,yz_); vsl_b_read(bfs,yt_);
01047       vsl_b_read(bfs,zx_); vsl_b_read(bfs,zy_); vsl_b_read(bfs,zz_); vsl_b_read(bfs,zt_);
01048       vsl_b_read(bfs,tx_); vsl_b_read(bfs,ty_); vsl_b_read(bfs,tz_); vsl_b_read(bfs,tt_);
01049     }
01050     break;
01051    case 2:
01052     vsl_b_read(bfs,f); form_=Form(f);
01053     vsl_b_read(bfs,xx_); vsl_b_read(bfs,xy_); vsl_b_read(bfs,xz_); vsl_b_read(bfs,xt_);
01054     vsl_b_read(bfs,yx_); vsl_b_read(bfs,yy_); vsl_b_read(bfs,yz_); vsl_b_read(bfs,yt_);
01055     vsl_b_read(bfs,zx_); vsl_b_read(bfs,zy_); vsl_b_read(bfs,zz_); vsl_b_read(bfs,zt_);
01056     vsl_b_read(bfs,tx_); vsl_b_read(bfs,ty_); vsl_b_read(bfs,tz_); vsl_b_read(bfs,tt_);
01057     break;
01058    default:
01059     vcl_cerr<<"vimt3d_transform_3d::load : Illegal version number "<<version<<'\n';
01060     vcl_abort();
01061   }
01062 
01063   inv_uptodate_ = 0;
01064 }
01065 
01066 //=======================================================================
01067 
01068 void vsl_b_write(vsl_b_ostream& bfs, const vimt3d_transform_3d& b)
01069 {
01070   b.b_write(bfs);
01071 }
01072 
01073 //=======================================================================
01074 
01075 void vsl_b_read(vsl_b_istream& bfs, vimt3d_transform_3d& b)
01076 {
01077   b.b_read(bfs);
01078 }
01079 
01080 //=======================================================================
01081 
01082 vcl_ostream& operator<<(vcl_ostream& os,const vimt3d_transform_3d& b)
01083 {
01084   vsl_indent_inc(os);
01085   b.print_summary(os);
01086   vsl_indent_dec(os);
01087   return os;
01088 }
01089 
01090 //========================================================================
01091 // Test whether a 3D transform is zoom-only or lesser, i.e. there may
01092 // be translation and (anisotropic) scaling but no rotation.
01093 // This tests only for a commonly-occurring special case; there may
01094 // be other zoom-only transforms that are not detected.
01095 //========================================================================
01096 bool vimt3d_transform_is_zoom_only(const vimt3d_transform_3d& transf,
01097                                    const double zero_tol/*=1e-9*/)
01098 {
01099   // Check whether the top-left 3x3 submatrix part of the transform is
01100   // diagonal with strictly-positive elements. Such cases have zero rotation
01101   // and positive (possibly anisotropic) scaling.
01102   vnl_matrix<double> M = transf.matrix().extract(3,3,0,0);
01103 
01104   // Are any diagonal elements zero or negative?
01105   for (unsigned i=0; i<3; ++i)
01106     if (M(i,i)<=zero_tol) return false;
01107 
01108   // Are any off-diagonal elements non-zero?
01109   for (unsigned j=0; j<3; ++j)
01110   {
01111     for (unsigned i=0; i<3; ++i)
01112     {
01113       if (i!=j && vcl_fabs(M(i,j))>=zero_tol) return false;
01114     }
01115   }
01116 
01117   return true;
01118 }
01119