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