00001 /* 00002 Estimates the parameters of a circle in 3D space from a set of points lying on 00003 the circle. 00004 00005 Copyright (C) 2010 - 2014 Christoph Haenisch, Fabian Killus 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_3D_HPP_6647389212 00022 #define FIT_CIRCLE_3D_HPP_6647389212 00023 00024 00025 #include <cmath> 00026 #include <vector> 00027 00028 #include <Eigen/Core> 00029 #include <Eigen/Dense> 00030 #include <Eigen/StdVector> 00031 #include <Eigen/SVD> 00032 00033 #include "ErrorObj.hpp" 00034 #include "Coordinate.hpp" 00035 #include "Fit3D.hpp" 00036 #include "FitPlane.hpp" 00037 #include "FitCircle.hpp" 00038 00039 00040 namespace TRTK 00041 { 00042 00043 00120 template <class T> 00121 class FitCircle3D : public Fit3D<T> 00122 { 00123 private: 00124 typedef Fit3D<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::Vector4T Vector4T; 00133 typedef typename super::Matrix3T Matrix3T; 00134 00135 FitCircle3D(); 00136 FitCircle3D(const std::vector<Coordinate<T> > & points); 00137 FitCircle3D(const std::vector<Vector3T> & points); 00138 FitCircle3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & points); 00139 00140 virtual ~FitCircle3D(); 00141 00142 void compute(); 00143 00144 const Coordinate<T> & getCenterPoint() const; 00145 T getDistanceTo(const Coordinate<T> & point) const; 00146 const Coordinate<T> & getNormal() const; 00147 unsigned getNumberPointsRequired() 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 T m_radius; 00157 Coordinate<T> m_center_point; 00158 Coordinate<T> m_normal; 00159 }; 00160 00161 00167 template <class T> 00168 FitCircle3D<T>::FitCircle3D() : 00169 m_radius(0), 00170 m_center_point(0, 0, 0), 00171 m_normal(0, 0, 0) 00172 { 00173 } 00174 00175 00184 template <class T> 00185 FitCircle3D<T>::FitCircle3D(const std::vector<Coordinate<T> > & points) : 00186 m_radius(0), 00187 m_center_point(0, 0, 0), 00188 m_normal(0, 0, 0) 00189 { 00190 super::setPoints(points); 00191 } 00192 00193 00201 template <class T> 00202 FitCircle3D<T>::FitCircle3D(const std::vector<Vector3T> & points) : 00203 m_radius(0), 00204 m_center_point(0, 0, 0), 00205 m_normal(0, 0, 0) 00206 { 00207 super::setPoints(points); 00208 } 00209 00210 00220 template <class T> 00221 FitCircle3D<T>::FitCircle3D(const std::vector<Vector4T, Eigen::aligned_allocator<Vector4T> > & points) : 00222 m_radius(0), 00223 m_center_point(0, 0, 0), 00224 m_normal(0, 0, 0) 00225 { 00226 super::setPoints(points); 00227 } 00228 00229 00235 template <class T> 00236 FitCircle3D<T>::~FitCircle3D() 00237 { 00238 } 00239 00240 00254 template <class T> 00255 void FitCircle3D<T>::compute() 00256 { 00257 /* 00258 00259 Explanation of the algorithm: 00260 00261 1) First, the plane in which the circle lies is estimated from the given set of 00262 points. This yields the normal vector of the plane. 00263 00264 2) Then the above plane is rotated such that its normal corresponds to the z-axis. 00265 00266 3) As a result, all points lie in an x-y-plane where z = const. (Now, the last 00267 component can be just dropped.) 00268 00269 4) The circle within the x-y-plane is estimated and its center point is transformed 00270 (inverse rotation) to the original position. For this purpose we need the 00271 z-coordinate which is the interception of the plane with the z-axis. 00272 00273 */ 00274 00275 using namespace std; 00276 00277 const int number_of_points = super::m_points.cols(); 00278 00279 if (number_of_points < 3) 00280 { 00281 ErrorObj error; 00282 error.setClassName("FitCircle3D<T>"); 00283 error.setFunctionName("compute"); 00284 error.setErrorMessage("Not enough points to fit the circle."); 00285 error.setErrorCode(NOT_ENOUGH_POINTS); 00286 throw error; 00287 } 00288 00289 // Estimate the plane the circle lies in. 00290 00291 vector<Coordinate<T> > points(number_of_points); 00292 00293 for (int i = 0; i < number_of_points; ++i) 00294 { 00295 Vector3T point = super::m_points.col(i); 00296 points[i] = Coordinate<T>(point); 00297 } 00298 00299 FitPlane<T> fitPlane(points); 00300 fitPlane.compute(); 00301 00302 m_normal = fitPlane.getNormal(); 00303 00304 // Rotate the original point set such that the normal of the 00305 // plane corresponds to the z-axis. Then estimate the 2D 00306 // circle from the projection of the points to the x-y-plane 00307 // (just drop the last component). 00308 00309 Matrix3T R; // rotation matrix 00310 00311 Coordinate<T> row3 = m_normal; 00312 Coordinate<T> row2 = row3.orthonormal(); 00313 Coordinate<T> row1 = row2.cross(row3); 00314 00315 R << row1.x(), row1.y(), row1.z(), 00316 row2.x(), row2.y(), row2.z(), 00317 row3.x(), row3.y(), row3.z(); 00318 00319 for (int i = 0; i < number_of_points; ++i) 00320 { 00321 Vector3T point = super::m_points.col(i); 00322 Vector2T projected_point = (R * point).head(2); 00323 points[i] = Coordinate<T>(projected_point); // overwrite above point set 00324 } 00325 00326 // interception of the rotated plane with the z-axis 00327 00328 T interception_z_axis = (R * fitPlane.getPointInPlane().toArray().matrix()).z(); 00329 00330 // Estimate the parameters of the 2D circle. 00331 00332 FitCircle<T> fitCircle(points); 00333 fitCircle.compute(); 00334 00335 m_radius = fitCircle.getRadius(); 00336 00337 // Invers-transform the center point of the 2D circle to obtain 00338 // the center point of the circle in the original 3D point set. 00339 00340 Coordinate<T> center_point = (fitCircle.getCenterPoint(), interception_z_axis); // yields a 3D coordinate 00341 00342 m_center_point = Coordinate<T>(R.inverse() * center_point.toArray().matrix()); 00343 00344 } 00345 00346 00352 template <class T> 00353 const Coordinate<T> & FitCircle3D<T>::getCenterPoint() const 00354 { 00355 return m_center_point; 00356 } 00357 00358 00366 template <class T> 00367 T FitCircle3D<T>::getDistanceTo(const Coordinate<T> & point) const 00368 { 00369 assert(point.size() == 3); 00370 00371 using std::sqrt; 00372 00373 // Vector pointing from the center point to the current point. 00374 00375 Coordinate<T> difference_vector = point - m_center_point; 00376 00377 // Shortest distance from the current point to the plane the 00378 // circle lies in (value can be negative, but it is squared 00379 // anyway). 00380 00381 T distance_to_plane = difference_vector.dot(m_normal); 00382 00383 // Distance from the (onto the plane) projected point to the circle. 00384 // (The original point, the projected point, and the center point 00385 // form a right angle triangle which allows using the Pythagorean 00386 // theorem). 00387 00388 T op_distance_to_circle = sqrt(difference_vector.norm() * difference_vector.norm() - 00389 distance_to_plane * distance_to_plane) - m_radius; 00390 00391 // Now, the Pythagorean theorem can be used once again to compute 00392 // the sought distance. 00393 00394 T distance = sqrt(distance_to_plane * distance_to_plane + 00395 op_distance_to_circle * op_distance_to_circle); 00396 00397 return distance; 00398 } 00399 00400 00406 template <class T> 00407 const Coordinate<T> & FitCircle3D<T>::getNormal() const 00408 { 00409 return m_normal; 00410 } 00411 00412 00413 template <class T> 00414 inline unsigned FitCircle3D<T>::getNumberPointsRequired() const 00415 { 00416 return 3; 00417 } 00418 00419 00425 template <class T> 00426 T FitCircle3D<T>::getRadius() const 00427 { 00428 return m_radius; 00429 } 00430 00431 00437 template <class T> 00438 T FitCircle3D<T>::getRMS() const 00439 { 00440 // The error between a point from the given point set and the 00441 // estimated model is defined to be the shortest distance 00442 // between that point and the circle. 00443 00444 using std::sqrt; 00445 00446 const int number_of_points = super::m_points.cols(); 00447 00448 T sum_of_squared_errors = 0.0; 00449 00450 for (int i = 0; i < number_of_points; ++i) 00451 { 00452 // Vector pointing from the center point to the current point. 00453 00454 Coordinate<T> difference_vector = Coordinate<T>(super::m_points.col(i)) - m_center_point; 00455 00456 // Shortest distance from the current point to the plane the 00457 // circle lies in (value can be negative, but it is squared 00458 // anyway). 00459 00460 T distance_to_plane = difference_vector.dot(m_normal); 00461 00462 // Distance from the (onto the plane) projected point to the circle. 00463 // (The original point, the projected point, and the center point 00464 // form a right angle triangle which allows using the Pythagorean 00465 // theorem). 00466 00467 T op_distance_to_circle = sqrt(difference_vector.norm() * difference_vector.norm() - 00468 distance_to_plane * distance_to_plane) - m_radius; 00469 00470 // Now, the Pythagorean theorem can be used once again to compute 00471 // the sought distance. 00472 00473 // T distance = sqrt(distance_to_plane * distance_to_plane + 00474 // op_distance_to_circle * op_distance_to_circle); 00475 00476 sum_of_squared_errors += distance_to_plane * distance_to_plane + 00477 op_distance_to_circle * op_distance_to_circle; 00478 } 00479 00480 return sqrt(sum_of_squared_errors / number_of_points); 00481 } 00482 00483 00484 } // namespace TRTK 00485 00486 00487 #endif // FIT_CIRCLE_3D_HPP_6647389212
Documentation generated by Doxygen