00001 /* 00002 Estimation and computation of a trivariate quadratic polynomial. 00003 00004 Copyright (C) 2010 - 2014 Christoph Haenisch 00005 00006 Chair of Medical Engineering (mediTEC) 00007 RWTH Aachen University 00008 Pauwelsstr. 20 00009 52074 Aachen 00010 Germany 00011 00012 See license.txt for more information. 00013 00014 Version 0.2.0 (2013-03-28) 00015 */ 00016 00023 #ifndef TRIVARIATE_QUADRATIC_POLYNOMIAL_HPP_0512325448 00024 #define TRIVARIATE_QUADRATIC_POLYNOMIAL_HPP_0512325448 00025 00026 00027 #include <cassert> 00028 #include <cmath> 00029 #include <vector> 00030 00031 #include <Eigen/Core> 00032 #include <Eigen/Dense> 00033 00034 #include "Coordinate.hpp" 00035 #include "ErrorObj.hpp" 00036 #include "Polynomial.hpp" 00037 #include "Range.hpp" 00038 00039 00040 namespace TRTK 00041 { 00042 00043 00209 template <class T> 00210 class TrivariateQuadraticPolynomial : public Polynomial<T> 00211 { 00212 private: 00213 00214 typedef Polynomial<T> super; 00215 00216 protected: 00217 00218 typedef typename super::VectorXT VectorXT; 00219 typedef typename super::Vector3T Vector3T; 00220 typedef typename super::coordinate_type coordinate_type; 00221 00222 public: 00223 00224 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 00225 00226 typedef T value_type; 00227 typedef typename super::MatrixXT MatrixXT; 00228 typedef typename super::Point Point; 00229 00230 using super::DIVISION_BY_ZERO; 00231 using super::INVALID_ARGUMENT; 00232 using super::NOT_ENOUGH_POINTS; 00233 using super::UNEQUAL_NUMBER_OF_POINTS; 00234 using super::UNKNOWN_ERROR; 00235 using super::WRONG_COORDINATE_SIZE; 00236 00237 TrivariateQuadraticPolynomial(); 00238 00239 TrivariateQuadraticPolynomial(const MatrixXT &); 00240 00241 TrivariateQuadraticPolynomial(const TrivariateQuadraticPolynomial &); 00242 00243 template <class U> 00244 TrivariateQuadraticPolynomial(const TrivariateQuadraticPolynomial<U> &); 00245 00246 virtual ~TrivariateQuadraticPolynomial(); 00247 00248 MatrixXT & getCoefficients(); 00249 const MatrixXT & getCoefficients() const; 00250 00251 T estimate(Range<Point> source_points, 00252 Range<Point> target_points, 00253 Range<T> weights = Range<T>()); 00254 00255 T estimate(Iterator<Point> source_points_first, 00256 Iterator<Point> source_points_last, 00257 Iterator<Point> target_points_first, 00258 Iterator<Point> target_points_last, 00259 Iterator<T> weights_first = Iterator<T>(), 00260 Iterator<T> weights_last = Iterator<T>()); 00261 00262 Point operator*(const Point &) const; 00263 Point transform(const Point &) const; 00264 00265 Polynomial<T> & reset(); 00266 00267 private: 00268 00269 MatrixXT m_matrix; 00270 }; 00271 00272 00282 template <class T> 00283 TrivariateQuadraticPolynomial<T>::TrivariateQuadraticPolynomial() : 00284 m_matrix(MatrixXT(3, 10)) 00285 { 00286 m_matrix << 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 00287 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 00288 0, 0, 0, 1, 0, 0, 0, 0, 0, 0; 00289 } 00290 00291 00303 template <class T> 00304 TrivariateQuadraticPolynomial<T>::TrivariateQuadraticPolynomial(const MatrixXT & matrix) 00305 { 00306 assert(matrix.rows() == 3 && matrix.cols() == 10); 00307 m_matrix = matrix; 00308 } 00309 00310 00318 template <class T> 00319 TrivariateQuadraticPolynomial<T>::TrivariateQuadraticPolynomial(const TrivariateQuadraticPolynomial & other) 00320 { 00321 m_matrix = other.m_matrix; 00322 } 00323 00324 00333 template <class T> 00334 template <class U> 00335 TrivariateQuadraticPolynomial<T>::TrivariateQuadraticPolynomial(const TrivariateQuadraticPolynomial<U> & other) 00336 { 00337 m_matrix = other.getCoefficients().template cast<T>(); 00338 } 00339 00340 00346 template <class T> 00347 TrivariateQuadraticPolynomial<T>::~TrivariateQuadraticPolynomial() 00348 { 00349 } 00350 00351 00359 template <class T> 00360 typename TrivariateQuadraticPolynomial<T>::MatrixXT & TrivariateQuadraticPolynomial<T>::getCoefficients() 00361 { 00362 return m_matrix; 00363 } 00364 00365 00373 template <class T> 00374 const typename TrivariateQuadraticPolynomial<T>::MatrixXT & TrivariateQuadraticPolynomial<T>::getCoefficients() const 00375 { 00376 return m_matrix; 00377 } 00378 00379 00400 template <class T> 00401 T TrivariateQuadraticPolynomial<T>::estimate(Range<Point> source_points, Range<Point> target_points, Range<T> weights) 00402 { 00403 return estimate(source_points.begin(), 00404 source_points.end(), 00405 target_points.begin(), 00406 target_points.end(), 00407 weights.begin(), 00408 weights.end()); 00409 } 00410 00411 00432 template <class T> 00433 T TrivariateQuadraticPolynomial<T>::estimate(Iterator<Point> source_points_first, 00434 Iterator<Point> source_points_last, 00435 Iterator<Point> target_points_first, 00436 Iterator<Point> target_points_last, 00437 Iterator<T> weights_first, 00438 Iterator<T> weights_last) 00439 { 00440 const int number_points = distance(source_points_first, source_points_last); 00441 00442 if (distance(target_points_first, target_points_last) != number_points) 00443 { 00444 ErrorObj error; 00445 error.setClassName("TrivariateQuadraticPolynomial<T>"); 00446 error.setFunctionName("estimate"); 00447 error.setErrorMessage("Point sets must have the same cardinality."); 00448 error.setErrorCode(UNEQUAL_NUMBER_OF_POINTS); 00449 throw error; 00450 } 00451 00452 if (weights_first && weights_last) 00453 { 00454 if (distance(weights_first, weights_last) != number_points) 00455 { 00456 ErrorObj error; 00457 error.setClassName("TrivariateQuadraticPolynomial<T>"); 00458 error.setFunctionName("estimate"); 00459 error.setErrorMessage("Point sets must have the same cardinality."); 00460 error.setErrorCode(UNEQUAL_NUMBER_OF_POINTS); 00461 throw error; 00462 } 00463 } 00464 00465 if (number_points < 10) 00466 { 00467 ErrorObj error; 00468 error.setClassName("TrivariateQuadraticPolynomial<T>"); 00469 error.setFunctionName("estimate"); 00470 error.setErrorMessage("Not enough points to estimate the transformation."); 00471 error.setErrorCode(NOT_ENOUGH_POINTS); 00472 throw error; 00473 } 00474 00475 /* 00476 00477 Explanation of the algorithm: 00478 00479 The trivariate quadratic function can be described as follows: 00480 00481 | x' | | m_1,1 m_1,2 ... m_1,10 | 00482 | y | = | m_2,1 m_2,2 ... m_2,10 | * | 1 x y ... y^2 yz z^2 |^T 00483 | z' | | m_3,1 m_3,2 ... m_3,10 | 00484 00485 The equation can be rewritten as: 00486 00487 | x' | | 1 ... z^2 0 ... 0 0 ... 0 | | m_1,1 | 00488 | y' | = | 0 ... 0 1 ... z^2 0 ... 0 | * | ... | 00489 | z' | | 0 ... 0 0 ... 0 1 ... z^2 | | m_3,10 | 00490 00491 Or in short: b = A * x (here, x is meant to be (m_1,1, ..., m_3,10)^T) 00492 00493 By using more and more points (source_1, target_1), ..., (source_n, target_n) 00494 00495 | A(1) | | b(1) | 00496 A := | ... | , b := | ... | 00497 | A(n) | | b(n) | 00498 00499 this leads to an over-determined system. However, this can be easily solved 00500 using the pseudo inverse: 00501 00502 <==> A * x = b 00503 <==> A^T * A * x = A^T * b 00504 <==> x = inverse(A^T * A) * A^T * b = pseudo_inverse * b 00505 00506 Thus, we have found the sought coefficients. 00507 00508 Update: We now use weighted least squares so that x = inverse(A^T * W * A) * A^T * W * b 00509 where W is a diagonal matrix 00510 00511 */ 00512 00513 // Fill the matrix A with the above described entries (different order as above). 00514 00515 MatrixXT A(3 * number_points, 30); 00516 00517 A.setZero(); 00518 00519 int i = 0; 00520 for (Iterator<Point> it = source_points_first; it != source_points_last; ++it, ++i) 00521 { 00522 T x = (*it).x(); 00523 T y = (*it).y(); 00524 T z = (*it).z(); 00525 00526 A(0 * number_points + i, 0) = 1; 00527 A(0 * number_points + i, 1) = x; 00528 A(0 * number_points + i, 2) = y; 00529 A(0 * number_points + i, 3) = z; 00530 A(0 * number_points + i, 4) = x * x; 00531 A(0 * number_points + i, 5) = x * y; 00532 A(0 * number_points + i, 6) = x * z; 00533 A(0 * number_points + i, 7) = y * y; 00534 A(0 * number_points + i, 8) = y * z; 00535 A(0 * number_points + i, 9) = z * z; 00536 00537 A(1 * number_points + i, 10) = 1; 00538 A(1 * number_points + i, 11) = x; 00539 A(1 * number_points + i, 12) = y; 00540 A(1 * number_points + i, 13) = z; 00541 A(1 * number_points + i, 14) = x * x; 00542 A(1 * number_points + i, 15) = x * y; 00543 A(1 * number_points + i, 16) = x * z; 00544 A(1 * number_points + i, 17) = y * y; 00545 A(1 * number_points + i, 18) = y * z; 00546 A(1 * number_points + i, 19) = z * z; 00547 00548 A(2 * number_points + i, 20) = 1; 00549 A(2 * number_points + i, 21) = x; 00550 A(2 * number_points + i, 22) = y; 00551 A(2 * number_points + i, 23) = z; 00552 A(2 * number_points + i, 24) = x * x; 00553 A(2 * number_points + i, 25) = x * y; 00554 A(2 * number_points + i, 26) = x * z; 00555 A(2 * number_points + i, 27) = y * y; 00556 A(2 * number_points + i, 28) = y * z; 00557 A(2 * number_points + i, 29) = z * z; 00558 } 00559 00560 // Fill the vector b. 00561 00562 VectorXT b(3 * number_points); 00563 00564 i = 0; 00565 for (Iterator<Point> it = target_points_first; it != target_points_last; ++it, ++i) 00566 { 00567 b(0 * number_points + i) = (*it).x(); 00568 b(1 * number_points + i) = (*it).y(); 00569 b(2 * number_points + i) = (*it).z(); 00570 } 00571 00572 // Compute the vector x whose components are the sought coefficients. 00573 00574 VectorXT x; 00575 VectorXT W = VectorXT::Ones(3 * number_points); // diagonal weighting matrix (W.asDiagonal()) 00576 00577 if (weights_first && weights_last) 00578 { 00579 // Weighted Least Squares 00580 00581 int i = 0; 00582 for (Iterator<T> it = weights_first; it != weights_last; ++it, ++i) 00583 { 00584 W(0 * number_points + i) = *it; 00585 W(1 * number_points + i) = *it; 00586 W(2 * number_points + i) = *it; 00587 } 00588 00589 x = (A.transpose() * W.asDiagonal() * A).inverse() * (A.transpose() * (W.asDiagonal() * b)); 00590 } 00591 else 00592 { 00593 // Ordinary Least Squares 00594 00595 MatrixXT pseudo_inverse = (A.transpose() * A).inverse() * A.transpose(); 00596 x = pseudo_inverse * b; 00597 } 00598 00599 // Reinterprete the solution vector as a row-major matrix 00600 00601 // m_matrix = Eigen::Map<MatrixXT, Eigen::Unaligned, Eigen::Stride<1, 10> >(x.data(), 3, 10); 00602 m_matrix = Eigen::Map<MatrixXT>(x.data(), 10, 3).transpose(); 00603 00604 // Compute the RMSE. 00605 00606 using std::sqrt; 00607 00608 if (weights_first && weights_last) 00609 { 00610 return (W.cwiseSqrt().asDiagonal() * (A * x - b)).norm() / sqrt(T(number_points)); 00611 } 00612 else 00613 { 00614 return (A * x - b).norm() / sqrt(T(number_points)); 00615 } 00616 } 00617 00618 00630 template <class T> 00631 typename TrivariateQuadraticPolynomial<T>::Point TrivariateQuadraticPolynomial<T>::operator* (const Point & point) const 00632 { 00633 assert(point.size() == 3); 00634 00635 return transform(point); 00636 } 00637 00638 00649 template <class T> 00650 Polynomial<T> & TrivariateQuadraticPolynomial<T>::reset() 00651 { 00652 m_matrix << 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 00653 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 00654 0, 0, 0, 1, 0, 0, 0, 0, 0, 0; 00655 00656 return *this; 00657 } 00658 00659 00671 template <class T> 00672 typename TrivariateQuadraticPolynomial<T>::Point TrivariateQuadraticPolynomial<T>::transform(const Point & point) const 00673 { 00674 assert(point.size() == 3); 00675 00676 Eigen::Matrix<T, 10, 1> vec; 00677 00678 const T & x = point.x(); 00679 const T & y = point.y(); 00680 const T & z = point.z(); 00681 00682 vec(0) = 1; 00683 vec(1) = x; 00684 vec(2) = y; 00685 vec(3) = z; 00686 vec(4) = x * x; 00687 vec(5) = x * y; 00688 vec(6) = x * z; 00689 vec(7) = y * y; 00690 vec(8) = y * z; 00691 vec(9) = z * z; 00692 00693 return Point(m_matrix * vec); 00694 } 00695 00696 00697 } // namespace TRTK 00698 00699 00700 #endif // TRIVARIATE_QUADRATIC_POLYNOMIAL_HPP_0512325448
Documentation generated by Doxygen