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.4.0 (2019-07-19) 00011 */ 00012 00019 #ifndef ESTIMATE_RIGID_TRANSFORMATION_3D_HPP_1230814474 00020 #define ESTIMATE_RIGID_TRANSFORMATION_3D_HPP_1230814474 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 EstimateRigidTransformation3D : 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 EstimateRigidTransformation3D(); 00234 00235 EstimateRigidTransformation3D(const std::vector<Coordinate<T> > & source_points, 00236 const std::vector<Coordinate<T> > & target_points); 00237 00238 EstimateRigidTransformation3D(const std::vector<Vector3T> & source_points, 00239 const std::vector<Vector3T> & target_points); 00240 00241 EstimateRigidTransformation3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & source_points, 00242 const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & target_points); 00243 00244 virtual ~EstimateRigidTransformation3D(); 00245 00246 virtual void compute() override; 00247 void allowReflection(bool value = true); 00248 00249 Matrix3T getRotationMatrix() const; 00250 Vector3T getTranslationVector() const; 00251 00252 private: 00253 bool allow_reflection = false; 00254 }; 00255 00256 00262 template <class T> 00263 EstimateRigidTransformation3D<T>::EstimateRigidTransformation3D() 00264 { 00265 } 00266 00267 00289 template <class T> 00290 EstimateRigidTransformation3D<T>::EstimateRigidTransformation3D(const std::vector<Coordinate<T> > & source_points, 00291 const std::vector<Coordinate<T> > & target_points) 00292 { 00293 super::setSourcePoints(source_points); 00294 super::setTargetPoints(target_points); 00295 } 00296 00297 00311 template <class T> 00312 EstimateRigidTransformation3D<T>::EstimateRigidTransformation3D(const std::vector<Vector3T> & source_points, 00313 const std::vector<Vector3T> & target_points) 00314 { 00315 super::setSourcePoints(source_points); 00316 super::setTargetPoints(target_points); 00317 } 00318 00319 00333 template <class T> 00334 EstimateRigidTransformation3D<T>::EstimateRigidTransformation3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & source_points, 00335 const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & target_points) 00336 { 00337 super::setSourcePoints(source_points); 00338 super::setTargetPoints(target_points); 00339 } 00340 00341 00347 template <class T> 00348 EstimateRigidTransformation3D<T>::~EstimateRigidTransformation3D() 00349 { 00350 } 00351 00352 00358 template <class T> 00359 void EstimateRigidTransformation3D<T>::allowReflection(bool value) 00360 { 00361 allow_reflection = value; 00362 } 00363 00364 00386 template <class T> 00387 void EstimateRigidTransformation3D<T>::compute() 00388 { 00389 if (super::m_source_points.size() != super::m_target_points.size()) 00390 { 00391 ErrorObj error; 00392 error.setClassName("EstimateRigidTransformation3D<T>"); 00393 error.setFunctionName("compute"); 00394 error.setErrorMessage("Point sets must have the same cardinality."); 00395 error.setErrorCode(UNEQUAL_NUMBER_OF_POINTS); 00396 throw error; 00397 } 00398 00399 if (super::m_source_points.cols() < 3 || super::m_target_points.cols() < 3) 00400 { 00401 ErrorObj error; 00402 error.setClassName("EstimateRigidTransformation3D<T>"); 00403 error.setFunctionName("compute"); 00404 error.setErrorMessage("Not enough points to compute transformation matrix."); 00405 error.setErrorCode(NOT_ENOUGH_POINTS); 00406 throw error; 00407 } 00408 00409 /* 00410 00411 Explanation of the algorithm (see also "Image Guided Therapy" lecture): 00412 00413 We try to estimate the rotation around the centroids of both data sets. Thus we first 00414 subtract the mean of both point sets such that the sets are centered around the point 00415 of origin. Then we compute the correlation matrix between both point sets: 00416 00417 R_XY = (X - mean(X)) * (Y - mean(Y))^T. 00418 00419 By the way, R_XY is nothing else but the cross-covariance matrix of X and Y, cov(X,Y). 00420 Now, R_XY can be (equivalently) decomposed in two ways, first by means of a singular 00421 value decomposition (SVD) 00422 00423 R_XY = U * Sigma * V^T, 00424 00425 where U and V are orthogonal matrices and Sigma is a diagonal matrix, and second 00426 by using an Eigen decomposition 00427 00428 R_XY = (X - mean(X)) * (Y - mean(Y))^T 00429 = x * y^T 00430 = x * [R * x]^T (use y = R * x) 00431 = x * x^T * R^T 00432 = S * D * S^T * R^T (Eigen decomposition of x * x^T) 00433 00434 where x and y are the zero mean vectors of X and Y, respectively, D is a diagonal 00435 matrix, S is an orthogonal matrix and R is the sought rotation matrix. Comparison of 00436 00437 R_XY = U * Sigma * V^T, 00438 = S * D * S^T * R^T 00439 00440 yields 00441 00442 V^T = S^T * R^T = U^T * R^T. 00443 00444 It follows that 00445 00446 R = V * U^T. 00447 00448 Now we can construct the homogeneous transformation matrix as follows: 00449 00450 | R | T | | I | mean(Y) | | R | 0 | | I | -mean(X) | 00451 | --+-- | = | --+-------- | * | --+-- | * | --+--------- | 00452 | 0 | 1 | | 0 | 1 | | 0 | 1 | | 0 | 1 | 00453 00454 | R | mean(Y) - R * mean(X) | 00455 = | --+---------------------- | 00456 | 0 | 1 | 00457 00458 Note: The above equation basically says: move the centroid of the source data to 00459 the point of origin, rotate the data and move it to the position of the 00460 centroid of the target data. 00461 00462 Note: I is the identity matrix and T is an arbitrary translation vector. 00463 00464 Note: Actually the rotation matrix on the left hand side should have had a name 00465 different from R, but since the entries are the same, we kept the name. 00466 */ 00467 00468 MatrixXT X = super::m_source_points; 00469 MatrixXT Y = super::m_target_points; 00470 00471 // subtract mean 00472 00473 Vector3T mean_X = X.rowwise().mean(); 00474 Vector3T mean_Y = Y.rowwise().mean(); 00475 00476 X.colwise() -= mean_X; 00477 Y.colwise() -= mean_Y; 00478 00479 // compute SVD of cross-covariance matrix 00480 00481 MatrixXT R_XY = X * Y.adjoint(); 00482 00483 Eigen::JacobiSVD<MatrixXT> svd(R_XY, Eigen::ComputeThinU | Eigen::ComputeThinV); 00484 00485 // compute estimate of the rotation matrix 00486 00487 Matrix3T R = svd.matrixV() * svd.matrixU().adjoint(); 00488 00489 // assure a right-handed coordinate system 00490 00491 if (R.determinant() < 0 && !allow_reflection) 00492 { 00493 R = svd.matrixV() * Vector3T(1, 1, -1).asDiagonal() * svd.matrixU().transpose(); 00494 } 00495 00496 // construct homogeneous transformation matrix 00497 00498 super::m_transformation_matrix.block(0, 0, 3, 3) = R; 00499 super::m_transformation_matrix.block(0, 3, 3, 1) = mean_Y - R * mean_X; 00500 super::m_transformation_matrix.block(3, 0, 1, 3) = RowVector3T::Zero(); 00501 // super::m_transformation_matrix(3, 3) = 1.0; // already one from initialisation 00502 } 00503 00504 00512 template <class T> 00513 typename EstimateRigidTransformation3D<T>::Matrix3T EstimateRigidTransformation3D<T>::getRotationMatrix() const 00514 { 00515 return super::m_transformation_matrix.block(0, 0, 3, 3); 00516 } 00517 00518 00526 template <class T> 00527 typename EstimateRigidTransformation3D<T>::Vector3T EstimateRigidTransformation3D<T>::getTranslationVector() const 00528 { 00529 return super::m_transformation_matrix.block(0, 3, 3, 1); 00530 } 00531 00532 00533 } // namespace TRTK 00534 00535 00536 #endif // ESTIMATE_RIGID_TRANSFORMATION_3D_HPP_1230814474
Documentation generated by Doxygen