00001 /* 00002 Copyright (C) 2010 - 2014 Christoph Haenisch 00003 00004 Chair of Medical Engineering (mediTEC) 00005 RWTH Aachen University 00006 Pauwelsstr. 20 00007 52074 Aachen 00008 Germany 00009 00010 Version 0.1.3 (2012-10-12) 00011 */ 00012 00019 #ifndef ESTIMATE_SIMILARITY_TRANSFORMATION_3D_HPP_7829730810 00020 #define ESTIMATE_SIMILARITY_TRANSFORMATION_3D_HPP_7829730810 00021 00022 00023 #include <cmath> 00024 #include <vector> 00025 00026 #include <Eigen/Core> 00027 #include <Eigen/Dense> 00028 #include <Eigen/StdVector> 00029 #include <Eigen/SVD> 00030 00031 #include "Coordinate.hpp" 00032 #include "ErrorObj.hpp" 00033 #include "EstimateTransformation3D.hpp" 00034 00035 00036 namespace TRTK 00037 { 00038 00039 00204 template <class T> 00205 class EstimateSimilarityTransformation3D : public EstimateTransformation3D<T> 00206 { 00207 private: 00208 00209 typedef EstimateTransformation3D<T> super; 00210 00211 public: 00212 00213 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 00214 00215 typedef T value_type; 00216 00217 typedef typename super::VectorXT VectorXT; 00218 typedef typename super::MatrixXT MatrixXT; 00219 typedef typename super::Vector2T Vector2T; 00220 typedef typename super::Vector3T Vector3T; 00221 typedef typename super::Vector4T Vector4T; 00222 typedef typename super::RowVector2T RowVector2T; 00223 typedef typename super::RowVector3T RowVector3T; 00224 typedef typename super::Matrix2T Matrix2T; 00225 typedef typename super::Matrix3T Matrix3T; 00226 typedef typename super::Matrix4T Matrix4T; 00227 00228 using super::NOT_ENOUGH_POINTS; 00229 using super::UNEQUAL_NUMBER_OF_POINTS; 00230 using super::UNKNOWN_ERROR; 00231 using super::WRONG_POINT_SIZE; 00232 00233 EstimateSimilarityTransformation3D(); 00234 00235 EstimateSimilarityTransformation3D(const std::vector<Coordinate<T> > & source_points, 00236 const std::vector<Coordinate<T> > & target_points); 00237 00238 EstimateSimilarityTransformation3D(const std::vector<Vector3T> & source_points, 00239 const std::vector<Vector3T> & target_points); 00240 00241 EstimateSimilarityTransformation3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & source_points, 00242 const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & target_points); 00243 00244 virtual ~EstimateSimilarityTransformation3D(); 00245 00246 virtual void compute(); 00247 00248 Matrix3T getRotationMatrix() const; 00249 Vector3T getTranslationVector() const; 00250 value_type getScalingFactor() const; 00251 00252 private: 00253 00254 value_type scaling_factor; 00255 }; 00256 00257 00263 template <class T> 00264 EstimateSimilarityTransformation3D<T>::EstimateSimilarityTransformation3D() 00265 { 00266 } 00267 00268 00290 template <class T> 00291 EstimateSimilarityTransformation3D<T>::EstimateSimilarityTransformation3D(const std::vector<Coordinate<T> > & source_points, 00292 const std::vector<Coordinate<T> > & target_points) : 00293 scaling_factor(0) 00294 { 00295 super::setSourcePoints(source_points); 00296 super::setTargetPoints(target_points); 00297 } 00298 00299 00313 template <class T> 00314 EstimateSimilarityTransformation3D<T>::EstimateSimilarityTransformation3D(const std::vector<Vector3T> & source_points, 00315 const std::vector<Vector3T> & target_points) : 00316 scaling_factor(0) 00317 { 00318 super::setSourcePoints(source_points); 00319 super::setTargetPoints(target_points); 00320 } 00321 00322 00336 template <class T> 00337 EstimateSimilarityTransformation3D<T>::EstimateSimilarityTransformation3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & source_points, 00338 const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & target_points) : 00339 scaling_factor(0) 00340 { 00341 super::setSourcePoints(source_points); 00342 super::setTargetPoints(target_points); 00343 } 00344 00345 00351 template <class T> 00352 EstimateSimilarityTransformation3D<T>::~EstimateSimilarityTransformation3D() 00353 { 00354 } 00355 00356 00378 template <class T> 00379 void EstimateSimilarityTransformation3D<T>::compute() 00380 { 00381 if (super::m_source_points.size() != super::m_target_points.size()) 00382 { 00383 ErrorObj error; 00384 error.setClassName("EstimateSimilarityTransformation3D<T>"); 00385 error.setFunctionName("compute"); 00386 error.setErrorMessage("Point sets must have the same cardinality."); 00387 error.setErrorCode(UNEQUAL_NUMBER_OF_POINTS); 00388 throw error; 00389 } 00390 00391 if (super::m_source_points.cols() < 2 || super::m_target_points.cols() < 2) 00392 { 00393 ErrorObj error; 00394 error.setClassName("EstimateSimilarityTransformation3D<T>"); 00395 error.setFunctionName("compute"); 00396 error.setErrorMessage("Not enough points to compute transformation matrix."); 00397 error.setErrorCode(NOT_ENOUGH_POINTS); 00398 throw error; 00399 } 00400 00401 /* 00402 00403 Explanation of the algorithm (see also "Image Guided Therapy" lecture): 00404 00405 We try to estimate the rotation around the centroids of both data sets. Thus we first 00406 subtract the mean of both point sets such that the sets are centered around the point 00407 of origin. Then we compute the correlation matrix between both point sets: 00408 00409 R_XY = (X - mean(X)) * (Y - mean(Y))^T. 00410 00411 By the way, R_XY is nothing else but the cross-covariance matrix of X and Y, cov(X,Y). 00412 Now, R_XY can be (equivalently) decomposed in two ways, first by means of a singular 00413 value decomposition (SVD) 00414 00415 R_XY = U * Sigma * V^T, 00416 00417 where U and V are orthogonal matrices and Sigma is a diagonal matrix, and second 00418 by using an Eigen decomposition 00419 00420 R_XY = (X - mean(X)) * (Y - mean(Y))^T 00421 = x * y^T 00422 = x * [R * x]^T (use y = R * x) 00423 = x * x^T * R^T 00424 = S * D * S^T * R^T (Eigen decomposition of x * x^T) 00425 00426 where x and y are the zero mean vectors of X and Y, respectively, D is a diagonal 00427 matrix, S is an orthogonal matrix and R is the sought rotation matrix. Comparison of 00428 00429 R_XY = U * Sigma * V^T, 00430 = S * D * S^T * R^T 00431 00432 yields 00433 00434 V^T = S^T * R^T = U^T * R^T. 00435 00436 It follows that 00437 00438 R = V * U^T 00439 00440 up to a possible scaling factor alpha contained in Sigma. 00441 00442 To find the lost scaling factor, we use x and y again which are zero mean vectors of X and Y. 00443 The distance from origin (norm) of each target vector in x (column in x) is divided through the 00444 distance from the origin (norm) of its corresponding source vector in y (column in y). 00445 The scaling factor should be the same for all target source pairs. We use the mean 00446 value as scaling factor and apply it to the rotation matrix: 00447 00448 R = R * scaling_factor; 00449 00450 Now we can construct the homogeneous transformation matrix as follows: 00451 00452 | R | T | | I | mean(Y) | | R | 0 | | I | -mean(X) | 00453 | --+-- | = | --+-------- | * | --+-- | * | --+--------- | 00454 | 0 | 1 | | 0 | 1 | | 0 | 1 | | 0 | 1 | 00455 00456 | R | mean(Y) - R * mean(X) | 00457 = | --+---------------------- | 00458 | 0 | 1 | 00459 00460 Note: The above equation basically says: move the centroid of the source data to 00461 the point of origin, rotate the data and move it to the position of the 00462 centroid of the target data. 00463 00464 Note: I is the identity matrix and T is an arbitrary translation vector. 00465 00466 Note: Actually the rotation matrix on the left hand side should have had a name 00467 different from R, but since the entries are the same, we kept the name. 00468 00469 Note: The rotation matrix is computed via the SVD to guarantee an orthogonality. 00470 The scaling factor can still be computed via the almost orthogonal matrix 00471 [x * x^T]^(-1) * R_XY. 00472 00473 */ 00474 00475 using std::pow; 00476 00477 MatrixXT X = super::m_source_points; 00478 MatrixXT Y = super::m_target_points; 00479 00480 // subtract mean 00481 00482 Vector3T mean_X = X.rowwise().mean(); 00483 Vector3T mean_Y = Y.rowwise().mean(); 00484 00485 X.colwise() -= mean_X; 00486 Y.colwise() -= mean_Y; 00487 00488 // compute SVD of cross-covariance matrix 00489 00490 MatrixXT R_XY = X * Y.transpose(); 00491 00492 Eigen::JacobiSVD<MatrixXT> svd(R_XY, Eigen::ComputeThinU | Eigen::ComputeThinV); 00493 00494 // compute estimate of the rotation matrix 00495 00496 Matrix3T R = svd.matrixV() * svd.matrixU().transpose(); 00497 00498 // assure a right-handed coordinate system 00499 00500 if (R.determinant() < 0) 00501 { 00502 R = svd.matrixV() * Vector3T(1, 1, -1).asDiagonal() * svd.matrixU().transpose(); 00503 } 00504 00505 // compute the scaling factor 00506 00507 scaling_factor = ( Y.colwise().norm().array() / X.colwise().norm().array() ).mean(); 00508 00509 R *= scaling_factor; 00510 00511 // construct homogeneous transformation matrix 00512 00513 super::m_transformation_matrix.block(0, 0, 3, 3) = R; 00514 super::m_transformation_matrix.block(0, 3, 3, 1) = mean_Y - R * mean_X; 00515 super::m_transformation_matrix.block(3, 0, 1, 3) = RowVector3T::Zero(); 00516 // super::m_transformation_matrix(3, 3) = 1.0; // already one from initialisation 00517 } 00518 00519 00528 template <class T> 00529 typename EstimateSimilarityTransformation3D<T>::Matrix3T EstimateSimilarityTransformation3D<T>::getRotationMatrix() const 00530 { 00531 return super::m_transformation_matrix.block(0, 0, 3, 3); 00532 } 00533 00534 00543 template <class T> 00544 typename EstimateSimilarityTransformation3D<T>::value_type EstimateSimilarityTransformation3D<T>::getScalingFactor() const 00545 { 00546 return scaling_factor; 00547 } 00548 00549 00558 template <class T> 00559 typename EstimateSimilarityTransformation3D<T>::Vector3T EstimateSimilarityTransformation3D<T>::getTranslationVector() const 00560 { 00561 return super::m_transformation_matrix.block(0, 3, 3, 1); 00562 } 00563 00564 00565 } // namespace TRTK 00566 00567 00568 #endif // ESTIMATE_SIMILARITY_TRANSFORMATION_3D_HPP_7829730810
Documentation generated by Doxygen