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.2.4 (2013-08-20) 00011 */ 00012 00019 #ifndef ESTIMATE_PROJECTIVE_TRANSFORMATION_3D_HPP_1231333218 00020 #define ESTIMATE_PROJECTIVE_TRANSFORMATION_3D_HPP_1231333218 00021 00022 00023 #include <cmath> 00024 #include <vector> 00025 00026 #include <Eigen/Core> 00027 #include <Eigen/Dense> 00028 #include <Eigen/StdVector> 00029 00030 #include "Coordinate.hpp" 00031 #include "ErrorObj.hpp" 00032 #include "EstimateTransformation3D.hpp" 00033 #include "Transform3D.hpp" 00034 00035 00036 namespace TRTK 00037 { 00038 00039 00212 template <class T> 00213 class EstimateProjectiveTransformation3D : public EstimateTransformation3D<T> 00214 { 00215 private: 00216 00217 typedef EstimateTransformation3D<T> super; 00218 00219 public: 00220 00221 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 00222 00223 typedef T value_type; 00224 00225 typedef typename super::ArrayXT ArrayXT; 00226 typedef typename super::MatrixXT MatrixXT; 00227 typedef typename super::VectorXT VectorXT; 00228 typedef typename super::Vector2T Vector2T; 00229 typedef typename super::Vector3T Vector3T; 00230 typedef typename super::Vector4T Vector4T; 00231 typedef typename super::RowVector2T RowVector2T; 00232 typedef typename super::RowVector3T RowVector3T; 00233 typedef typename super::Matrix2T Matrix2T; 00234 typedef typename super::Matrix3T Matrix3T; 00235 typedef typename super::Matrix4T Matrix4T; 00236 00237 using super::NOT_ENOUGH_POINTS; 00238 using super::UNEQUAL_NUMBER_OF_POINTS; 00239 using super::UNKNOWN_ERROR; 00240 using super::WRONG_POINT_SIZE; 00241 00242 EstimateProjectiveTransformation3D(); 00243 00244 EstimateProjectiveTransformation3D(const std::vector<Coordinate<T> > & source_points, 00245 const std::vector<Coordinate<T> > & target_points); 00246 00247 EstimateProjectiveTransformation3D(const std::vector<Vector3T> & source_points, 00248 const std::vector<Vector3T> & target_points); 00249 00250 EstimateProjectiveTransformation3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & source_points, 00251 const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & target_points); 00252 00253 virtual ~EstimateProjectiveTransformation3D(); 00254 00255 virtual void compute(); 00256 00257 void setMaxIterations(int value); 00258 void setMaxRMS(value_type value); 00259 00260 private: 00261 00262 int m_max_iterations; 00263 static const int MAX_ITERATIONS = 10; 00264 00265 value_type m_max_RMS; 00266 static const value_type MAX_RMS; 00267 }; 00268 00269 00270 template <class T> 00271 const T EstimateProjectiveTransformation3D<T>::MAX_RMS = 0.1; 00272 00273 00279 template <class T> 00280 EstimateProjectiveTransformation3D<T>::EstimateProjectiveTransformation3D() : 00281 m_max_iterations(MAX_ITERATIONS), 00282 m_max_RMS(MAX_RMS) 00283 { 00284 } 00285 00286 00308 template <class T> 00309 EstimateProjectiveTransformation3D<T>::EstimateProjectiveTransformation3D(const std::vector<Coordinate<T> > & source_points, 00310 const std::vector<Coordinate<T> > & target_points) : 00311 m_max_iterations(MAX_ITERATIONS), 00312 m_max_RMS(MAX_RMS) 00313 { 00314 super::setSourcePoints(source_points); 00315 super::setTargetPoints(target_points); 00316 } 00317 00318 00332 template <class T> 00333 EstimateProjectiveTransformation3D<T>::EstimateProjectiveTransformation3D(const std::vector<Vector3T> & source_points, 00334 const std::vector<Vector3T> & target_points) : 00335 m_max_iterations(MAX_ITERATIONS), 00336 m_max_RMS(MAX_RMS) 00337 { 00338 super::setSourcePoints(source_points); 00339 super::setTargetPoints(target_points); 00340 } 00341 00342 00356 template <class T> 00357 EstimateProjectiveTransformation3D<T>::EstimateProjectiveTransformation3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & source_points, 00358 const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & target_points) : 00359 m_max_iterations(MAX_ITERATIONS), 00360 m_max_RMS(MAX_RMS) 00361 { 00362 super::setSourcePoints(source_points); 00363 super::setTargetPoints(target_points); 00364 } 00365 00366 00372 template <class T> 00373 EstimateProjectiveTransformation3D<T>::~EstimateProjectiveTransformation3D() 00374 { 00375 } 00376 00377 00398 template <class T> 00399 void EstimateProjectiveTransformation3D<T>::compute() 00400 { 00401 if (super::m_source_points.size() != super::m_target_points.size()) 00402 { 00403 ErrorObj error; 00404 error.setClassName("EstimateProjectiveTransformation3D<T>"); 00405 error.setFunctionName("compute"); 00406 error.setErrorMessage("Point sets must have the same cardinality."); 00407 error.setErrorCode(UNEQUAL_NUMBER_OF_POINTS); 00408 throw error; 00409 } 00410 00411 if (super::m_source_points.cols() < 5 || super::m_target_points.cols() < 5) 00412 { 00413 ErrorObj error; 00414 error.setClassName("EstimateProjectiveTransformation3D<T>"); 00415 error.setFunctionName("compute"); 00416 error.setErrorMessage("Not enough points to compute transformation matrix."); 00417 error.setErrorCode(NOT_ENOUGH_POINTS); 00418 throw error; 00419 } 00420 00421 /* 00422 00423 Explanation of the algorithm (in 2D): 00424 00425 By using homogeneous coordinates an projective transformation can be described as follows: 00426 00427 | a_11 a_12 a_13 | | source_x | | target_x' | 00428 | a_21 a_22 a_23 | * | source_y | = | target_y' | 00429 | a_31 a_32 1 | | 1 | | target_z' |. <-- note: target_z' is *not* known 00430 00431 Then the sought target coordinates are: 00432 00433 | target_x | | target_x' / target_z' | 00434 | target_y | = | target_y' / target_z' | 00435 | target_z | | 1 |. 00436 00437 The matrix A can be normalized such that component a_33 is equal to one, i.e. A := A' / a'_33, because of the 00438 subsequent normalisation step. Hence A has only 8 DoF. Since the normalisation step is nonlinear, we use 00439 Newton's method (after Simpson) to solve for the entries a_11, ..., a_32. 00440 00441 The equation can be rewritten as a function f of the sought variables a_11, ..., a_32: 00442 00443 | f_1 | 1 | a_11 * source_x + a_12 * source_y + a_13 | 00444 f = | | = -------------------------------------- * | | 00445 | f_2 | a_31 * source_x + a_32 * source_y + 1 | a_21 * source_x + a_22 * source_y + a_23 |. 00446 00447 Then we try to find the zero-crossing of the residual (and we are done): 00448 00449 residual(a_11, ..., a_32) = residual(A) := f(a_11, ..., a_32) - |target_x, target_y|^T. 00450 00451 This can be done iteratively by computing 00452 00453 A_new = A_old - Jacobian(residual)^(-1) * residual 00454 00455 where 00456 00457 | c_3*source_x c_3*source_y c_3 0 0 0 -c_1*c_4*source_x -c_1*c_4*source_y | 00458 Jacobian = | | 00459 | 0 0 0 c_3*source_x c_3*source_y c_3 -c_2*c_4*source_x -c_2*c_4*source_y | 00460 00461 and 00462 00463 c_1 = a_11 * source_x + a_12 * source_y + a_13 00464 c_2 = a_21 * source_x + a_22 * source_y + a_23 00465 c_3 = 1 / (a_31 * source_x + a_31 * source_y + 1) 00466 c_4 = c_3^2. 00467 00468 Instead of inverting the Jacobian directly we rewrite the above equation: 00469 00470 delta_A := A_new - A_old = - Jacobian(residual)^(-1) * residual 00471 00472 and solve the system of linear equations with respect to delta_A: 00473 00474 delta_A * Jacobian = -residual. 00475 00476 By using the method of least squares (or the Moore-Penrose pseudoinverse) we get 00477 00478 delta_A = - (Jacobian^T * Jacobian)^(-1) * (Jacobian^T * residual). 00479 00480 Then (in each iteration step) the new solution vector becomes: 00481 00482 A_new = A_old + delta_A. 00483 00484 */ 00485 00486 const int number_points = super::m_source_points.cols(); 00487 00488 // initialize solution matrix 00489 00490 Matrix4T A; 00491 A.setIdentity(); 00492 00493 const ArrayXT & source_x = super::m_source_points.block(0, 0, 1, number_points).transpose(); 00494 const ArrayXT & source_y = super::m_source_points.block(1, 0, 1, number_points).transpose(); 00495 const ArrayXT & source_z = super::m_source_points.block(2, 0, 1, number_points).transpose(); 00496 00497 const ArrayXT & target_x = super::m_target_points.block(0, 0, 1, number_points).transpose(); 00498 const ArrayXT & target_y = super::m_target_points.block(1, 0, 1, number_points).transpose(); 00499 const ArrayXT & target_z = super::m_target_points.block(2, 0, 1, number_points).transpose(); 00500 00501 for (int i = 0; i < m_max_iterations; ++i) 00502 { 00503 // construct Jacobian matrix 00504 00505 ArrayXT ones = ArrayXT::Ones(number_points); 00506 00507 ArrayXT c_1(number_points, 1); 00508 ArrayXT c_2(number_points, 1); 00509 ArrayXT c_3(number_points, 1); 00510 ArrayXT c_4(number_points, 1); 00511 ArrayXT c_5(number_points, 1); 00512 00513 c_1 = A(0, 0) * source_x + A(0, 1) * source_y + A(0, 2) * source_z + A(0, 3) * ones; 00514 c_2 = A(1, 0) * source_x + A(1, 1) * source_y + A(1, 2) * source_z + A(1, 3) * ones; 00515 c_3 = A(2, 0) * source_x + A(2, 1) * source_y + A(2, 2) * source_z + A(2, 3) * ones; 00516 c_4 = (A(3, 0) * source_x + A(3, 1) * source_y + A(3, 2) * source_z + ones).inverse(); 00517 c_5 = c_4 * c_4; 00518 00519 MatrixXT Jacobian(3 * number_points, 15); 00520 00521 Jacobian.setZero(); 00522 00523 Jacobian.block(0 * number_points, 0, number_points, 1) = c_4 * source_x; 00524 Jacobian.block(0 * number_points, 1, number_points, 1) = c_4 * source_y; 00525 Jacobian.block(0 * number_points, 2, number_points, 1) = c_4 * source_z; 00526 Jacobian.block(0 * number_points, 3, number_points, 1) = c_4; 00527 Jacobian.block(0 * number_points, 12, number_points, 1) = -c_1 * c_5 * source_x; 00528 Jacobian.block(0 * number_points, 13, number_points, 1) = -c_1 * c_5 * source_y; 00529 Jacobian.block(0 * number_points, 14, number_points, 1) = -c_1 * c_5 * source_z; 00530 00531 Jacobian.block(1 * number_points, 4, number_points, 1) = c_4 * source_x; 00532 Jacobian.block(1 * number_points, 5, number_points, 1) = c_4 * source_y; 00533 Jacobian.block(1 * number_points, 6, number_points, 1) = c_4 * source_z; 00534 Jacobian.block(1 * number_points, 7, number_points, 1) = c_4; 00535 Jacobian.block(1 * number_points, 12, number_points, 1) = -c_2 * c_5 * source_x; 00536 Jacobian.block(1 * number_points, 13, number_points, 1) = -c_2 * c_5 * source_y; 00537 Jacobian.block(1 * number_points, 14, number_points, 1) = -c_2 * c_5 * source_z; 00538 00539 Jacobian.block(2 * number_points, 8, number_points, 1) = c_4 * source_x; 00540 Jacobian.block(2 * number_points, 9, number_points, 1) = c_4 * source_y; 00541 Jacobian.block(2 * number_points, 10, number_points, 1) = c_4 * source_z; 00542 Jacobian.block(2 * number_points, 11, number_points, 1) = c_4; 00543 Jacobian.block(2 * number_points, 12, number_points, 1) = -c_3 * c_5 * source_x; 00544 Jacobian.block(2 * number_points, 13, number_points, 1) = -c_3 * c_5 * source_y; 00545 Jacobian.block(2 * number_points, 14, number_points, 1) = -c_3 * c_5 * source_z; 00546 00547 // compute residual vector 00548 00549 VectorXT residual(3 * number_points); 00550 00551 residual.block(0 * number_points, 0, number_points, 1) = c_1 * c_4 - target_x; 00552 residual.block(1 * number_points, 0, number_points, 1) = c_2 * c_4 - target_y; 00553 residual.block(2 * number_points, 0, number_points, 1) = c_3 * c_4 - target_z; 00554 00555 // compute difference vector 00556 00557 VectorXT delta_A = - ((Jacobian.transpose() * Jacobian).inverse() * (Jacobian.transpose() * residual)); 00558 00559 // compute estimate of transformation matrix 00560 00561 A(0, 0) += delta_A( 0); 00562 A(0, 1) += delta_A( 1); 00563 A(0, 2) += delta_A( 2); 00564 A(0, 3) += delta_A( 3); 00565 A(1, 0) += delta_A( 4); 00566 A(1, 1) += delta_A( 5); 00567 A(1, 2) += delta_A( 6); 00568 A(1, 3) += delta_A( 7); 00569 A(2, 0) += delta_A( 8); 00570 A(2, 1) += delta_A( 9); 00571 A(2, 2) += delta_A(10); 00572 A(2, 3) += delta_A(11); 00573 A(3, 0) += delta_A(12); 00574 A(3, 1) += delta_A(13); 00575 A(3, 2) += delta_A(14); 00576 00577 // compute MSE as stop criterion 00578 00579 Transform3D<T> transform(A); 00580 00581 value_type RMS = 0; 00582 00583 for (int n = 0; n < number_points; ++n) 00584 { 00585 Vector3T source = Vector3T(source_x(n), source_y(n), source_z(n)); 00586 Vector3T target = Vector3T(target_x(n), target_y(n), target_z(n)); 00587 00588 RMS += (transform * source - target).squaredNorm(); 00589 } 00590 00591 using std::sqrt; 00592 00593 RMS = sqrt(RMS / number_points); 00594 00595 if (RMS < m_max_RMS) break; 00596 } 00597 00598 super::m_transformation_matrix = A; 00599 } 00600 00601 00614 template <class T> 00615 void EstimateProjectiveTransformation3D<T>::setMaxIterations(int value) 00616 { 00617 m_max_iterations = value; 00618 } 00619 00620 00634 template <class T> 00635 void EstimateProjectiveTransformation3D<T>::setMaxRMS(value_type value) 00636 { 00637 m_max_RMS = value; 00638 } 00639 00640 00641 } // namespace TRTK 00642 00643 00644 #endif // ESTIMATE_PROJECTIVE_TRANSFORMATION_3D_HPP_1231333218
Documentation generated by Doxygen