fitting_circle_3d.cpp 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. #include "algorithms.h"
  2. #include "core/math_helper.hpp"
  3. using namespace Eigen;
  4. fitting_circle_2d::result_type
  5. fitting_circle_2d::operator()(const input_type &input) {
  6. const auto N = input.points.cols();
  7. auto A = MatrixX3f(N, 3);
  8. A.leftCols<2>() = input.points.transpose();
  9. A.col(2).setOnes();
  10. const auto x = A.col(0).array(), y = A.col(1).array();
  11. VectorXf b = x.pow(2) + y.pow(2);
  12. if (input.weights) {
  13. const auto w = input.weights->array();
  14. A.array().colwise() *= w;
  15. b.array().colwise() *= w;
  16. }
  17. const auto solver = ColPivHouseholderQR<MatrixX3f>(A);
  18. const auto c = solver.solve(b);
  19. const auto center = Vector2f(c[0] / 2, c[1] / 2);
  20. auto ret = result_type();
  21. ret.center = to_vec2(center);
  22. ret.radius = sqrtf(c[2] + center.squaredNorm());
  23. auto err = VectorXf(N);
  24. for (auto k = 0; k < N; ++k) {
  25. auto point = input.points.col(k);
  26. err[k] = ret.radius - (center - point).norm();
  27. }
  28. ret.error_rms = sqrtf(err.array().square().sum() / N);
  29. return ret;
  30. }
  31. namespace {
  32. MatrixX3f rodrigues_rot(const MatrixX3f &points,
  33. Vector3f n0, Vector3f n1) {
  34. const auto N = points.rows();
  35. n0.normalize();
  36. n1.normalize();
  37. const auto n2 = n0.cross(n1).normalized();
  38. const auto theta = acos(n0.dot(n1));
  39. const auto cos_theta = cos(theta);
  40. const auto sin_theta = sin(theta);
  41. auto ret = MatrixX3f(N, 3);
  42. for (auto k = 0; k < N; k++) {
  43. auto p = points.row(k).transpose();
  44. ret.row(k) = p * cos_theta
  45. + n2.cross(p) * sin_theta
  46. + n2 * n2.dot(p) * (1 - cos_theta);
  47. }
  48. return ret;
  49. }
  50. Vector3f rodrigues_rot(const Vector3f &vec,
  51. const Vector3f &n0, const Vector3f &n1) {
  52. auto matrix = MatrixX3f(1, 3);
  53. matrix.row(0) = vec;
  54. auto ret = rodrigues_rot(matrix, n0, n1);
  55. return ret.row(0);
  56. }
  57. }
  58. fitting_circle_3d::result_type
  59. fitting_circle_3d::operator()(const input_type &input) {
  60. auto ret = result_type();
  61. MatrixX3f P = input.points.transpose();
  62. const Vector3f mean = P.colwise().mean();
  63. P.array().rowwise() -= mean.array().transpose();
  64. // Calculate the SVD of the centered points
  65. const auto svd = JacobiSVD(P, ComputeFullV);
  66. const auto normal = svd.matrixV().col(2);
  67. ret.axis = to_vec3(normal);
  68. // Fit a circle to the rotated points
  69. auto P_xy = rodrigues_rot(P, normal, Vector3f::UnitZ());
  70. auto input_2d = fitting_circle_2d::input_type();
  71. input_2d.points = P_xy.leftCols<2>().transpose();
  72. input_2d.weights = input.weights;
  73. const auto ret_2d = fitting_circle_2d()(input_2d);
  74. ret.radius = ret_2d.radius;
  75. ret.error_rms = ret_2d.error_rms;
  76. auto center = Vector3f();
  77. center << ret.center.x, ret.center.y, 0;
  78. center = rodrigues_rot(center, Vector3f::UnitZ(), normal);
  79. center += mean;
  80. ret.center = to_vec3(center);
  81. return ret;
  82. }