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_