contrib/mul/vimt3d/vimt3d_transform_3d.h
Go to the documentation of this file.
00001 #ifndef vimt3d_transform_3d_h_
00002 #define 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 <vgl/vgl_point_3d.h>
00009 #include <vgl/vgl_vector_3d.h>
00010 #include <vnl/io/vnl_io_matrix.h>
00011 #include <vsl/vsl_binary_io.h>
00012 #include <vcl_iosfwd.h>
00013 #include <vnl/vnl_quaternion.h>
00014 
00015 //=======================================================================
00016 
00017 //: A class to define and apply a 3D transform.
00018 // The transform which can be up to an affine transformation.
00019 // In order of complexity the transform can be
00020 // - Identity     x->x, y->y, z->z
00021 // - Translation  x->x + tx, y->y + ty, z->z + tz
00022 // - RigidBody    (Rotation, followed by translation)
00023 // - Similarity   (Isotropic scaling, followed by rotation, then translation)
00024 // - ZoomOnly     (Anisotropic scaling, followed by translation)
00025 // - Affine
00026 //
00027 // One useful special case of Affine involves anisotropic scaling, followed
00028 // by rotation, then translation.
00029 //
00030 // The transform types Translation, ZoomOnly, RigidBody and Similarity have
00031 // a defined order in which scaling, rotation and translation components
00032 // are applied, and the components are thus separable.
00033 // Other transformations (e.g. translation followed by rotation) can be
00034 // obtained by composing multiple transforms. The resulting transform will
00035 // in general be termed affine.
00036 //
00037 // The transformation can be represented by a 4x4 matrix of
00038 // homogeneous co-ordinates.
00039 // \verbatim
00040 // ( xx xy xz xt )
00041 // ( yx yy yz yt )
00042 // ( zx zy zz zt )
00043 // ( tx ty tz tt )
00044 // \endverbatim
00045 // For efficiency the elements are stored explicitly, rather than in a
00046 // vnl_matrix<double>, to avoid lots of copying of matrices with all the
00047 // attendant memory allocation.
00048 class vimt3d_transform_3d
00049 {
00050  public:
00051 
00052   //: Defines form of transformation
00053   enum Form {Identity,
00054              Translation,
00055              ZoomOnly,   //!< Anisotropic scaling, followed by translation
00056              RigidBody,  //!< Rotation, followed by translation
00057              Similarity, //!< Isotropic scaling, followed by rotation, then translation
00058              Affine};
00059 
00060   //: Construct as identity transform
00061   vimt3d_transform_3d() :
00062     xx_(1), xy_(0), xz_(0), xt_(0),
00063     yx_(0), yy_(1), yz_(0), yt_(0),
00064     zx_(0), zy_(0), zz_(1), zt_(0),
00065     tx_(0), ty_(0), tz_(0), tt_(1),
00066     form_(Identity), inv_uptodate_(false) {}
00067 
00068   // An explicit destructor is required to avoid an internal compiler
00069   // error in icc 8.0 (internal error: 0_1270)
00070 
00071   //: Destructor
00072   ~vimt3d_transform_3d() {}
00073 
00074   //: True if identity.
00075   bool is_identity() const { return form_==Identity; }
00076 
00077   //: Form of transformation.
00078   Form form() const { return form_; }
00079 
00080   //: Gets 4x4 Matrix representing transformation
00081   vnl_matrix<double> matrix() const;
00082 
00083   //: Gets 4x4 Matrix representing transformation
00084   // \retval M  a 4x4 Matrix representing transformation
00085   void matrix(vnl_matrix<double>& M) const;
00086 
00087   //: Fills v with parameters
00088   void params(vnl_vector<double>& v) const;
00089 
00090   //: Define the transform in terms of a 4x4 homogeneous matrix.
00091   // \param M 4x4 homogeneous matrix defining the transform.
00092   // \note Client must ensure that \a M is a valid representation of an affine (or simpler) transform.
00093   // \note The form will be set to Affine - call simplify() if you need the simplest form.
00094   void set_matrix(const vnl_matrix<double>& M);
00095 
00096   //: Sets transform using v
00097   void set(const vnl_vector<double>& v, Form);
00098 
00099   //: Sets transform to identity.
00100   void set_identity();
00101 
00102   //: Sets the transformation to be a translation.
00103   // \param t_x  Translation in x
00104   // \param t_y  Translation in y
00105   // \param t_z  Translation in z
00106   void set_translation(double t_x, double t_y, double t_z);
00107 
00108   //: Sets the transformation to be anisotropic scaling, followed by translation.
00109   // The transformation is separable affine.
00110   // x' = s_x.x + t_x,  y' = s_y.y + t_y,  z' = s_z.z + t_z
00111   // \param s_x  Scaling in x
00112   // \param s_y  Scaling in y
00113   // \param s_z  Scaling in z
00114   // \param t_x  Translation in x
00115   // \param t_y  Translation in y
00116   // \param t_z  Translation in z
00117   void set_zoom_only( double s_x, double s_y, double s_z,
00118                       double t_x, double t_y, double t_z);
00119 
00120   //: Sets the transformation to be isotropic scaling, followed by translation.
00121   // The transformation is separable affine.
00122   // x' = s.x + t_x,  y' = s.y + t_y,  z' = s.z + t_z
00123   // \param s  Scaling in x, y and z
00124   // \param t_x  Translation in x
00125   // \param t_y  Translation in y
00126   // \param t_z  Translation in z
00127   void set_zoom_only(double s, double t_x, double t_y, double t_z)
00128     { set_zoom_only(s, s, s, t_x, t_y, t_z); }
00129 
00130   //: Sets the transformation to be rotation, followed by translation.
00131   // The transformation is separable affine.
00132   // \param r_x  Angle of rotation in x
00133   // \param r_y  Angle of rotation in y
00134   // \param r_z  Angle of rotation in z
00135   // \param t_x  Translation in x
00136   // \param t_y  Translation in y
00137   // \param t_z  Translation in z
00138   void set_rigid_body(double r_x, double r_y, double r_z,
00139                       double t_x, double t_y, double t_z);
00140   
00141   //: Sets the transformation to be rotation, followed by translation.
00142   // The transformation is separable affine.
00143   // \param unit_q  Unit quaternion defining rotation
00144   void set_rigid_body(const vnl_quaternion<double>& unit_q,
00145                       double t_x, double t_y, double t_z);
00146 
00147   //: Sets the transformation to be isotropic scaling, followed by rotation, then translation.
00148   // The transformation is separable affine.
00149   // \param s  Scaling factor
00150   // \param r_x  Angle of rotation in x
00151   // \param r_y  Angle of rotation in y
00152   // \param r_z  Angle of rotation in z
00153   // \param t_x  Translation in x
00154   // \param t_y  Translation in y
00155   // \param t_z  Translation in z
00156   void set_similarity(double s,
00157                       double r_x, double r_y, double r_z,
00158                       double t_x, double t_y, double t_z);
00159 
00160   
00161   //: Sets the transformation to be similarity: scale, rotation, followed by translation.
00162   // The transformation is separable affine.
00163   // \param unit_q  Unit quaternion defining rotation
00164   void set_similarity(double scale, const vnl_quaternion<double>& unit_q,
00165                       double t_x, double t_y, double t_z);
00166 
00167   //: Sets the transformation to be a special case of Affine: anisotropic scaling, followed by rotation, then translation.
00168   // \param s_x  Scaling factor in x
00169   // \param s_y  Scaling factor in y
00170   // \param s_z  Scaling factor in z
00171   // \param r_x  Angle of rotation in x
00172   // \param r_y  Angle of rotation in y
00173   // \param r_z  Angle of rotation in z
00174   // \param t_x  Translation in x
00175   // \param t_y  Translation in y
00176   // \param t_z  Translation in z
00177   // \note This creates a special case of Affine. Although this special case
00178   // is separable, in general Affine transformations are not separable.
00179   void set_affine(double s_x, double s_y, double s_z,
00180                   double r_x, double r_y, double r_z,
00181                   double t_x, double t_y, double t_z);
00182 
00183   //: Sets the transformation to be a special case of Affine: anisotropic scaling, followed by rotation, then translation.
00184   // \param s_x  Scaling factor in x
00185   // \param s_y  Scaling factor in y
00186   // \param s_z  Scaling factor in z
00187   // \param c_x  First column of rotation matrix
00188   // \param c_y  Second column of rotation matrix
00189   // \param c_z  Third column of rotation matrix
00190   // \param t_x  Translation in x
00191   // \param t_y  Translation in y
00192   // \param t_z  Translation in z
00193   // \note This creates a special case of Affine. Although this special case
00194   // is separable, in general Affine transformations are not
00195   // separable. The rotation matrix is assumed to be valid.
00196   void set_affine(double s_x, double s_y, double s_z,
00197                   vgl_vector_3d<double> c_x,
00198                   vgl_vector_3d<double> c_y,
00199                   vgl_vector_3d<double> c_z,
00200                   double t_x, double t_y, double t_z);
00201 
00202   //: Sets the transformation to be a special case of Affine.
00203   // T(x,y,z) = p +x.u +y.v + z.w
00204   // \param p Origin point
00205   // \param u Vector to which the x-axis is mapped. The length of \a u indicates scaling in x.
00206   // \param v Vector to which the y-axis is mapped. The length of \a v indicates scaling in y.
00207   // \param w Vector to which the z-axis is mapped. The length of \a w indicates scaling in z.
00208   // \note Currently, the implementation assumes that u,v,w are orthogonal
00209   // and form a right-handed system. There are asserts for this condition.
00210   // \note This creates a special case of Affine. Although this special case
00211   // is separable, in general Affine transformations are not separable.
00212   void set_affine(const vgl_point_3d<double>& p,
00213                   const vgl_vector_3d<double>& u,
00214                   const vgl_vector_3d<double>& v,
00215                   const vgl_vector_3d<double>& w);
00216 
00217   //: Returns the coordinates of the origin
00218   vgl_point_3d<double>  origin() const
00219     { return vgl_point_3d<double> (tt_==1?xt_:xt_/tt_,tt_==1?yt_:yt_/tt_,tt_==1?zt_:zt_/tt_); }
00220 
00221   //: Modifies the transformation so that origin == p.
00222   // Modifies the transformation so that
00223   // operator()(vgl_point_3d<double> (0,0)) == p.
00224   // The rest of the transformation is unaffected.
00225   // If the transformation was previously the identity,
00226   // it becomes a translation.
00227   void set_origin( const vgl_point_3d<double> & );
00228 
00229   //: Applies transformation to (x,y,z)
00230   // \param x  x coordinate
00231   // \param y  y co-ord
00232   // \param z  z co-ord
00233   //ret: Point = T(x,y,z)
00234   vgl_point_3d<double>  operator()(double x, double y, double z) const
00235   {
00236     switch (form_)
00237     {
00238      case Identity :
00239       return vgl_point_3d<double> (x,y,z);
00240      case Translation :
00241       return vgl_point_3d<double> (x+xt_,y+yt_,z+zt_);
00242      case ZoomOnly :
00243       return vgl_point_3d<double> (
00244         x*xx_+xt_,
00245         y*yy_+yt_,
00246         z*zz_+zt_);
00247 //   case RigidBody, Similarity, Affine :
00248      default :
00249       return vgl_point_3d<double> (
00250         x*xx_+y*xy_+z*xz_+xt_,
00251         x*yx_+y*yy_+z*yz_+yt_,
00252         x*zx_+y*zy_+z*zz_+zt_);
00253     }
00254   }
00255 
00256   //: Applies transformation to point p
00257   // \param p  Point
00258   // \return Point = T(p)
00259   vgl_point_3d<double>  operator()(vgl_point_3d<double>  p) const
00260     { return operator()(p.x(),p.y(),p.z()); }
00261 
00262   //: Returns the inverse of the current transform
00263   // \return inverse of current transform.
00264   vimt3d_transform_3d inverse() const;
00265 
00266   //: Returns change in transformed point when original point moved by dp
00267   // \param p  point
00268   // \param dp  movement from point
00269   // \return T(p+dp)-T(p)
00270   vgl_vector_3d<double>  delta(vgl_point_3d<double>  /*p*/, vgl_vector_3d<double>  dp) const
00271   {
00272     switch (form_)
00273     {
00274      case Identity :
00275      case Translation:
00276       return dp;
00277      case ZoomOnly :
00278       return vgl_vector_3d<double> (dp.x()*xx_,
00279                                     dp.y()*yy_,
00280                                     dp.z()*zz_);
00281 //   case RigidBody, Similarity, Affine :
00282      default : // Don't worry that the returned value is independent of p --- this is correct.
00283       return vgl_vector_3d<double> (dp.x()*xx_+dp.y()*xy_+dp.z()*xz_,
00284                                     dp.x()*yx_+dp.y()*yy_+dp.z()*yz_,
00285                                     dp.x()*zx_+dp.y()*zy_+dp.z()*zz_);
00286     }
00287   }
00288 
00289   //: Calculates the product LR
00290   // \param L  Transform
00291   // \param R  Transform
00292   // \return Transform LR = R followed by L
00293   friend vimt3d_transform_3d operator*(const vimt3d_transform_3d& L,
00294                                        const vimt3d_transform_3d& R);
00295 
00296   //: Print class to os
00297   // This function prints the extracted params.
00298   // \sa params()
00299   // \sa set()
00300   void print_summary(vcl_ostream& os) const;
00301 
00302   //: Print class to os
00303   // This function prints the actual parameters xx_,xy_,xz_,xt_, yx_,yy_,yz_,yt_, zx_,zy_,zz_,zt_, tx_,ty_,tz_,tt_
00304   void print_all(vcl_ostream& os) const;
00305 
00306   //: Set transformation from stream;
00307   // You can specify the vector as used in the set() operation.
00308   // \verbatim
00309   // form: rigidbody
00310   // vector: { 0.1 0.1 0.1 2 2 2 }
00311   // \endverbatim
00312   // or with explicit parameter names from the set_...() methods.
00313   // \verbatim
00314   // form: rigidbody
00315   // r_x: 0.1
00316   // r_y: 0.1
00317   // r_z: 0.1
00318   // t_x: 2
00319   // t_y: 2
00320   // t_z: 2
00321   // \endverbatim
00322   void config(vcl_istream& is);
00323 
00324   //: Save class to binary file stream
00325   void b_write(vsl_b_ostream& bfs) const;
00326 
00327   //: Load class from binary file stream
00328   void b_read(vsl_b_istream& bfs);
00329 
00330   //: True if t is the same as this.
00331   // \note All underlying parameters xx_, xy_, etc are required to be equal,
00332   // but the declared Form (etc RigidBody) need not be equal.
00333   bool operator==(const vimt3d_transform_3d&) const;
00334 
00335   //: Reduce to the simplest form possible.
00336   void simplify(double tol =1e-10);
00337 
00338  protected:
00339 
00340   double xx_,xy_,xz_,xt_,yx_,yy_,yz_,yt_,zx_, zy_, zz_, zt_, tx_,ty_,tz_,tt_;
00341   Form form_;
00342 
00343   // This is all the inverse data
00344   // Notice the mutable here - take care if using threads!
00345   mutable double xx2_,xy2_,xz2_,xt2_,yx2_,yy2_,yz2_,yt2_,zx2_, zy2_, zz2_, zt2_, tx2_,ty2_,tz2_,tt2_;
00346   mutable bool inv_uptodate_;
00347 
00348 
00349   void calcInverse() const;
00350   void setCheck(int n1,int n2,const char* str) const;
00351   void angles(double& phi_x, double& phi_y, double& phi_z) const;
00352   void setRotMat(double r_x, double r_y, double r_z);
00353 };
00354 
00355 
00356 //: Calculates the product LR
00357 // \param L  Transform
00358 // \param R  Transform
00359 // \return Transform LR = R followed by L
00360 vimt3d_transform_3d operator*(const vimt3d_transform_3d& L,
00361                               const vimt3d_transform_3d& R);
00362 
00363 //: Binary file stream output operator for class reference
00364 void vsl_b_write(vsl_b_ostream& bfs, const vimt3d_transform_3d& b);
00365 
00366 //: Binary file stream input operator for class reference
00367 void vsl_b_read(vsl_b_istream& bfs, vimt3d_transform_3d& b);
00368 
00369 //: Stream output operator for class reference
00370 vcl_ostream& operator<<(vcl_ostream& os,const vimt3d_transform_3d& b);
00371 
00372 //: Stream output operator for class reference
00373 inline void vsl_print_summary(vcl_ostream& afs, const vimt3d_transform_3d& b)
00374 {
00375   b.print_summary(afs);
00376 }
00377 
00378 //: Test whether a 3D transform is zoom-only or lesser.
00379 // i.e. there may be translation and (anisotropic) scaling but no rotation.
00380 // \note This tests only for a commonly-occurring special case; there may
00381 // be other zoom-only transforms that are not detected.
00382 // \param zero_tol Used for testing whether elements are zero or not.
00383 bool vimt3d_transform_is_zoom_only(const vimt3d_transform_3d& transf,
00384                                    const double zero_tol=1e-9);
00385 
00386 //=======================================================================
00387 
00388 #endif // vimt3d_transform_3d_h_