00001 /* 00002 Copyright (C) 2010 - 2014 Christoph Haenisch, Fabian Killus 00003 00004 Chair of Medical Engineering (mediTEC) 00005 RWTH Aachen University 00006 Pauwelsstr. 20 00007 52074 Aachen 00008 Germany 00009 00010 Version 0.5.0 (2012-03-19) 00011 */ 00012 00018 #ifndef FIT_PLANE_HPP_5948294843 00019 #define FIT_PLANE_HPP_5948294843 00020 00021 00022 #include <cmath> 00023 #include <vector> 00024 00025 #include <Eigen/Core> 00026 #include <Eigen/Dense> 00027 #include <Eigen/StdVector> 00028 00029 #include "ErrorObj.hpp" 00030 #include "Coordinate.hpp" 00031 #include "Fit3D.hpp" 00032 00033 namespace TRTK 00034 { 00035 00036 00117 template <class T> 00118 class FitPlane : public Fit3D<T> 00119 { 00120 private: 00121 typedef Fit3D<T> super; 00122 00123 public: 00124 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 00125 00126 typedef T value_type; 00127 typedef typename super::Vector3T Vector3T; 00128 typedef typename super::Vector4T Vector4T; 00129 typedef typename super::VectorXT VectorXT; 00130 typedef typename super::Matrix3T Matrix3T; 00131 typedef typename super::MatrixXT MatrixXT; 00132 00133 FitPlane(); 00134 FitPlane(const std::vector<Coordinate<T> > & points); 00135 FitPlane(const std::vector<Vector3T> & points); 00136 FitPlane(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & points); 00137 00138 virtual ~FitPlane(); 00139 00140 unsigned getNumberPointsRequired() const; 00141 void compute(); 00142 T getRMS() const; 00143 00144 const Coordinate<T> & getNormal() const; 00145 const Coordinate<T> & getPointInPlane() const; 00146 T getDistanceFromOrigin() const; 00147 T getDistanceTo(const Coordinate<T> & point) const; 00148 00149 using super::DATA_POINTS_TOO_SIMILAR; 00150 using super::NOT_ENOUGH_POINTS; 00151 using super::UNKNOWN_ERROR; 00152 using super::WRONG_POINT_SIZE; 00153 00154 private: 00155 Coordinate<T> m_normal; 00156 Coordinate<T> m_point_in_plane; 00157 T m_rms; 00158 }; 00159 00160 00166 template <class T> 00167 FitPlane<T>::FitPlane() : 00168 m_normal(0, 0, 0), 00169 m_point_in_plane(0, 0, 0), 00170 m_rms(0) 00171 { 00172 } 00173 00174 00183 template <class T> 00184 FitPlane<T>::FitPlane(const std::vector<Coordinate<T> > & points) : 00185 m_normal(0, 0, 0), 00186 m_point_in_plane(0, 0, 0), 00187 m_rms(0) 00188 { 00189 super::setPoints(points); 00190 } 00191 00192 00200 template <class T> 00201 FitPlane<T>::FitPlane(const std::vector<Vector3T> & points) : 00202 m_normal(0, 0, 0), 00203 m_point_in_plane(0, 0, 0), 00204 m_rms(0) 00205 { 00206 super::setPoints(points); 00207 } 00208 00209 00219 template <class T> 00220 FitPlane<T>::FitPlane(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & points) : 00221 m_normal(0, 0, 0), 00222 m_point_in_plane(0, 0, 0), 00223 m_rms(0) 00224 { 00225 super::setPoints(points); 00226 } 00227 00228 00234 template <class T> 00235 FitPlane<T>::~FitPlane() 00236 { 00237 } 00238 00239 00253 template <class T> 00254 void FitPlane<T>::compute() 00255 { 00256 /* 00257 00258 Explanation of the algorithm: 00259 00260 The columns of matrix X contain the provided points on the surface of the plane. 00261 First we subtract the mean of the point set from each point in the set such that 00262 the new set of points is centered around the point of origin. 00263 00264 x = X - mean(X) 00265 00266 With n being the normal vector of the plane, we should ideally get x_i^T * n = 0 00267 for all columns x_i of the matrix x. Therefore we define an error measure as 00268 00269 E = sum_i ( x_i^T * n )^2 00270 00271 This can be rearranged to 00272 00273 E = sum_i ( x_i^T * n )^2 00274 = sum_i ( (x_i^T * n)^T * (x_i^T * n) ) because x_i^T * n is a scalar 00275 = sum_i ( n^T * x_i * x_i^T * n ) 00276 = n^T * sum_i ( x_i * x_i^T ) * n 00277 = n^T * A * n with the matrix A = x * x^T 00278 00279 Minimizing the above term yields the sought/unknown normal n. To avoid the trivial 00280 solution n = 0, we use the Lagrange formalism, i.e. Lagrange multipliers. Our 00281 auxiliary condition is n * n^T = 1. 00282 00283 L(n, lambda) := n^T * A * n - lambda * ( n^T * n - 1) and Grad( L(n, lambda) ) != 0 00284 00285 => 2 A n - A lambda I n = 0 with I being the indentity matrix 00286 => A n = lambda n 00287 00288 Thus, the minimization problem reduces to an eigenvalue problem. Since 00289 00290 E = n^T * A * n = n^T * lambda * n = lambda 00291 00292 the sought normal is the eigenvector corresponding to the smallest eigenvalue. 00293 00294 */ 00295 00296 using namespace std; 00297 using namespace Eigen; 00298 00299 using std::sqrt; 00300 00301 const int number_of_points = super::m_points.cols(); 00302 00303 if (number_of_points < 3) 00304 { 00305 ErrorObj error; 00306 error.setClassName("FitPlane<T>"); 00307 error.setFunctionName("compute"); 00308 error.setErrorMessage("Not enough points to fit the plane."); 00309 error.setErrorCode(NOT_ENOUGH_POINTS); 00310 throw error; 00311 } 00312 00313 MatrixXT X = super::m_points; 00314 00315 // Subtract the population mean and which is a point in the plane. 00316 00317 VectorXT mean = X.rowwise().mean(); 00318 X.colwise() -= mean; 00319 00320 m_point_in_plane = Coordinate<T>(mean); 00321 00322 // Construct the matrix A = sum ( x_i^T * x_i ). 00323 00324 Matrix3T A = X * X.transpose(); 00325 00326 if (A.isZero()) 00327 { 00328 ErrorObj error; 00329 error.setClassName("FitPlane<T>"); 00330 error.setFunctionName("compute"); 00331 error.setErrorMessage("Data points are too similar."); 00332 error.setErrorCode(DATA_POINTS_TOO_SIMILAR); 00333 throw error; 00334 } 00335 00336 // Compute the eigenvalues and the eigenvectors of A. 00337 00338 // Note: - Since A is symmetric, there are only real eigenvalues. 00339 // ==> Use SelfAdjointEigenSolver. 00340 // - SelfAdjointEigenSolver normalizes the eigenvectors. 00341 00342 SelfAdjointEigenSolver<Matrix3T> eigensolver(A); 00343 00344 VectorXT eigen_values = eigensolver.eigenvalues(); 00345 MatrixXT eigen_vectors = eigensolver.eigenvectors(); 00346 00347 // Find the absolute minimum eigenvalue and its corresponding eigenvector. 00348 // Do also compute the RMS from the smallest eigenvalue. 00349 00350 int index = 0; 00351 T minimum_eigenvalue = abs(eigen_values(index)); 00352 00353 for (int i = 1; i < eigen_values.rows(); ++i) 00354 { 00355 if (abs(eigen_values[i]) < minimum_eigenvalue) 00356 { 00357 index = i; 00358 minimum_eigenvalue = abs(eigen_values[index]); 00359 } 00360 } 00361 00362 m_normal = Coordinate<T>(eigen_vectors.col(index)); 00363 00364 T sum_of_squared_errors = minimum_eigenvalue; 00365 m_rms = sqrt(sum_of_squared_errors / number_of_points); 00366 } 00367 00368 00374 template <class T> 00375 T FitPlane<T>::getDistanceFromOrigin() const 00376 { 00377 using std::abs; 00378 00379 // The shortest distance can be found by projecting a vector pointing 00380 // to an arbitrary point in the plane (we just use the population mean 00381 // of all points) onto the normal vector of the plane. 00382 00383 return abs(m_point_in_plane.dot(m_normal)); 00384 } 00385 00386 00392 template <class T> 00393 T FitPlane<T>::getDistanceTo(const Coordinate<T> & point) const 00394 { 00395 using std::abs; 00396 00397 /* 00398 00399 The shortest distance can be found by projecting a difference vector 00400 formed from the given point and an arbitrary point in the plane (we 00401 just use the population mean of all points) onto the normal vector of 00402 the plane. 00403 00404 If we take the normal n and two orthonormal vectors alpha and beta 00405 which altogether form a new basis, then we can express the above 00406 difference vector d by a linear combination as follows: 00407 00408 d = a * alpha + b * beta + distance_to_plane * n 00409 00410 Now, a simple inner product with n yields the sought distance 00411 00412 d * n = 0 + 0 + distance_to_plane * 1 00413 00414 */ 00415 00416 return abs((point - m_point_in_plane).dot(m_normal)); 00417 } 00418 00419 00425 template <class T> 00426 const Coordinate<T> & FitPlane<T>::getNormal() const 00427 { 00428 return m_normal; 00429 } 00430 00431 00432 template <class T> 00433 inline unsigned FitPlane<T>::getNumberPointsRequired() const 00434 { 00435 return 3; 00436 } 00437 00438 00444 template <class T> 00445 const Coordinate<T> & FitPlane<T>::getPointInPlane() const 00446 { 00447 return m_point_in_plane; 00448 } 00449 00450 00456 template <class T> 00457 T FitPlane<T>::getRMS() const 00458 { 00459 return m_rms; 00460 } 00461 00462 00463 } // namespace TRTK 00464 00465 00466 #endif // FITSPHERE_HPP_6184653510
Documentation generated by Doxygen