00001 /* 00002 Estimates the parameters of a circle from a set of 2D points lying on 00003 the circle. 00004 00005 Copyright (C) 2010 - 2014 Fabian Killus, Christoph Haenisch 00006 00007 Chair of Medical Engineering (mediTEC) 00008 RWTH Aachen University 00009 Pauwelsstr. 20 00010 52074 Aachen 00011 Germany 00012 00013 Version 0.2.0 (2012-03-20) 00014 */ 00015 00021 #ifndef FIT_CIRCLE_HPP_1950385693 00022 #define FIT_CIRCLE_HPP_1950385693 00023 00024 00025 #include <cmath> 00026 #include <Eigen/Core> 00027 #include <Eigen/Dense> 00028 #include <Eigen/StdVector> 00029 #include <Eigen/SVD> 00030 #include <vector> 00031 00032 #include "ErrorObj.hpp" 00033 #include "Coordinate.hpp" 00034 #include "Fit2D.hpp" 00035 00036 00037 namespace TRTK 00038 { 00039 00040 00120 template <class T> 00121 class FitCircle : public Fit2D<T> 00122 { 00123 private: 00124 typedef Fit2D<T> super; 00125 00126 public: 00127 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 00128 00129 typedef T value_type; 00130 typedef typename super::Vector2T Vector2T; 00131 typedef typename super::Vector3T Vector3T; 00132 typedef typename super::VectorXT VectorXT; 00133 typedef typename super::MatrixXT MatrixXT; 00134 00135 FitCircle(); 00136 FitCircle(const std::vector<Coordinate<T> > & points); 00137 FitCircle(const std::vector<Vector2T, Eigen::aligned_allocator<Vector2T> > & points); 00138 FitCircle(const std::vector<Vector3T> & points); 00139 00140 virtual ~FitCircle(); 00141 00142 void compute(); 00143 00144 unsigned getNumberPointsRequired() const; 00145 00146 const Coordinate<T> & getCenterPoint() const; 00147 T getDistanceTo(const Coordinate<T> & point) const; 00148 T getRadius() const; 00149 T getRMS() const; 00150 00151 using super::NOT_ENOUGH_POINTS; 00152 using super::UNKNOWN_ERROR; 00153 using super::WRONG_POINT_SIZE; 00154 00155 private: 00156 Coordinate<T> m_center_point; 00157 T m_radius; 00158 }; 00159 00160 00166 template <class T> 00167 FitCircle<T>::FitCircle() : 00168 m_center_point(0, 0), 00169 m_radius(0) 00170 { 00171 } 00172 00173 00182 template <class T> 00183 FitCircle<T>::FitCircle(const std::vector<Coordinate<T> > & points) : 00184 m_center_point(0, 0), 00185 m_radius(0) 00186 { 00187 super::setPoints(points); 00188 } 00189 00190 00198 template <class T> 00199 FitCircle<T>::FitCircle(const std::vector<Vector2T, Eigen::aligned_allocator<Vector2T> > & points) : 00200 m_center_point(0, 0), 00201 m_radius(0) 00202 { 00203 super::setPoints(points); 00204 } 00205 00206 00216 template <class T> 00217 FitCircle<T>::FitCircle(const std::vector<Vector3T> & points) : 00218 m_center_point(0, 0), 00219 m_radius(0) 00220 { 00221 super::setPoints(points); 00222 } 00223 00224 00230 template <class T> 00231 FitCircle<T>::~FitCircle() 00232 { 00233 } 00234 00235 00249 template <class T> 00250 void FitCircle<T>::compute() 00251 { 00252 /* 00253 00254 Explanation of the algorithm: 00255 00256 A point (x, y) on the circle with center point (x0, y0) and radius R 00257 fullfills the following equation: 00258 00259 (x - x0)^2 + (y - y0)^2 = R^2 00260 00261 This can be rewritten as 00262 00263 2*x*x0 + 2*y*y0 - x0^2 - y0^2 + R^2 = x^2 + y^2 00264 00265 or written as dot product 00266 00267 [2x 2y 1] * [x0 y0 (R^2 - x0^2 - y0^2)]^T = x^2 + y^2 00268 00269 Now we have an equation of the form 00270 00271 A(i) * p = b(i). 00272 00273 where 00274 00275 A(i) = [2x 2y 1] 00276 p = [x0 y0 (R^2 - x0^2 - y0^2)]^T 00277 b(i) = x^2 + y^2 00278 00279 The index denotes, that this equation holds for a point (x(i), y(i)). Note 00280 that the vector p is constant for differing points on the same circle. As you can 00281 see, the above equation is nothing else but a linear system. That is, in the 00282 above case A(i) is a matrix with one row only and p is a vector. Since the above 00283 equation must hold for all points of a set of n points lying on the given circle, 00284 we can construct a bigger matrix A and a vector b that still fullfill the above 00285 linear equation A * p = b. 00286 00287 A := [A(1) ... A(n)]^T 00288 b := [b(1) ... b(n)]^T 00289 00290 Now, A is a n times 3 matrix (which in general is non-square) and b a n times 1 00291 vector. As mentioned before, p is a constant vector wich contains the sought 00292 parameters. To solve the system, we just construct the pseudo inverse as follows: 00293 00294 A * p = b 00295 00296 ==> A^T * A * p = A^T * b (now A^T * A is square!) 00297 00298 ==> p = inverse(A^T * A) * A^T * b = pseudo_inverse(A) * b 00299 00300 */ 00301 00302 if (super::m_points.cols() < 3) 00303 { 00304 ErrorObj error; 00305 error.setClassName("FitCircle<T>"); 00306 error.setFunctionName("compute"); 00307 error.setErrorMessage("Not enough points to fit the circle."); 00308 error.setErrorCode(NOT_ENOUGH_POINTS); 00309 throw error; 00310 } 00311 00312 // Construct matrix A. 00313 00314 const int number_points = super::m_points.cols(); 00315 00316 const VectorXT & x = super::m_points.row(0); 00317 const VectorXT & y = super::m_points.row(1); 00318 00319 MatrixXT A(number_points, 3); 00320 00321 A.block(0, 0, number_points, 1) = 2 * x; 00322 A.block(0, 1, number_points, 1) = 2 * y; 00323 A.block(0, 2, number_points, 1).setConstant(1); 00324 00325 // Construct vector b. 00326 00327 VectorXT b(number_points); 00328 00329 b = x.array().square() + y.array().square(); 00330 00331 // Compute pseudo inverse. 00332 00333 MatrixXT pseudo_inverse = (A.transpose() * A).inverse() * A.transpose(); 00334 00335 // Compute vector p whose components are/contain the sought transformation entries. 00336 00337 VectorXT p = pseudo_inverse * b; // FIXME: inverse(A^T * A) * (A^T * b) is more efficient to compute 00338 00339 m_center_point[0] = p(0); // x0 00340 m_center_point[1] = p(1); // y0 00341 00342 using std::sqrt; 00343 m_radius = sqrt(p(2) + p(0) * p(0) + p(1) * p(1)); // p(2) = R^2 - x0^2 - y0^2 00344 } 00345 00346 00352 template <class T> 00353 const Coordinate<T> & FitCircle<T>::getCenterPoint() const 00354 { 00355 return m_center_point; 00356 } 00357 00358 00366 template <class T> 00367 T FitCircle<T>::getDistanceTo(const Coordinate<T> & point) const 00368 { 00369 assert(point.size() == 2); 00370 00371 using std::abs; 00372 return abs((point - m_center_point).norm() - m_radius); 00373 } 00374 00375 00376 template <class T> 00377 inline unsigned FitCircle<T>::getNumberPointsRequired() const 00378 { 00379 return 3; 00380 } 00381 00382 00388 template <class T> 00389 T FitCircle<T>::getRMS() const 00390 { 00391 using std::sqrt; 00392 00393 const int number_points = super::m_points.cols(); 00394 00395 T sum_of_squared_errors = 0.0; 00396 00397 for (int i = 0; i < number_points; ++i) 00398 { 00399 // Compute the distance from the current point to the circle and add it up. 00400 00401 Coordinate<T> point(super::m_points.col(i)); 00402 T distance = m_radius - (point - m_center_point).norm(); // Omit abs() due to subsequent squaring. 00403 00404 sum_of_squared_errors += distance * distance; 00405 } 00406 00407 return sqrt(sum_of_squared_errors / number_points); 00408 } 00409 00410 00416 template <class T> 00417 T FitCircle<T>::getRadius() const 00418 { 00419 return m_radius; 00420 } 00421 00422 00423 } // namespace TRTK 00424 00425 00426 #endif // FIT_CIRCLE_HPP_1950385693
Documentation generated by Doxygen