| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <algorithm>
|
| | #include <cstdlib>
|
| | #include <iterator>
|
| | #include <limits>
|
| |
|
| | #include "SphereFit.h"
|
| |
|
| |
|
| | using namespace MeshCoreFit;
|
| |
|
| | SphereFit::SphereFit()
|
| | : _vCenter(0, 0, 0)
|
| | {}
|
| |
|
| |
|
| | void SphereFit::SetApproximations(double radius, const Base::Vector3d& center)
|
| | {
|
| | _bIsFitted = false;
|
| | _fLastResult = std::numeric_limits<float>::max();
|
| | _numIter = 0;
|
| | _dRadius = radius;
|
| | _vCenter = center;
|
| | }
|
| |
|
| |
|
| |
|
| | void SphereFit::SetConvergenceCriteria(double posConvLimit, double vConvLimit, int maxIter)
|
| | {
|
| | if (posConvLimit > 0.0) {
|
| | _posConvLimit = posConvLimit;
|
| | }
|
| | if (vConvLimit > 0.0) {
|
| | _vConvLimit = vConvLimit;
|
| | }
|
| | if (maxIter > 0) {
|
| | _maxIter = maxIter;
|
| | }
|
| | }
|
| |
|
| |
|
| | double SphereFit::GetRadius() const
|
| | {
|
| | if (_bIsFitted) {
|
| | return _dRadius;
|
| | }
|
| |
|
| | return 0.0;
|
| | }
|
| |
|
| | Base::Vector3d SphereFit::GetCenter() const
|
| | {
|
| | if (_bIsFitted) {
|
| | return _vCenter;
|
| | }
|
| |
|
| | return Base::Vector3d();
|
| | }
|
| |
|
| | int SphereFit::GetNumIterations() const
|
| | {
|
| | if (_bIsFitted) {
|
| | return _numIter;
|
| | }
|
| |
|
| | return 0;
|
| | }
|
| |
|
| | float SphereFit::GetDistanceToSphere(const Base::Vector3f& rcPoint) const
|
| | {
|
| | float fResult = std::numeric_limits<float>::max();
|
| | if (_bIsFitted) {
|
| | fResult = Base::Vector3d(
|
| | (double)rcPoint.x - _vCenter.x,
|
| | (double)rcPoint.y - _vCenter.y,
|
| | (double)rcPoint.z - _vCenter.z
|
| | )
|
| | .Length()
|
| | - _dRadius;
|
| | }
|
| | return fResult;
|
| | }
|
| |
|
| | float SphereFit::GetStdDeviation() const
|
| | {
|
| |
|
| |
|
| |
|
| | if (!_bIsFitted) {
|
| | return std::numeric_limits<float>::max();
|
| | }
|
| |
|
| | double sumXi = 0.0, sumXi2 = 0.0, dist = 0.0;
|
| | for (auto it : _vPoints) {
|
| | dist = GetDistanceToSphere(it);
|
| | sumXi += dist;
|
| | sumXi2 += (dist * dist);
|
| | }
|
| |
|
| | double N = static_cast<double>(CountPoints());
|
| | double mean = sumXi / N;
|
| | return sqrt((N / (N - 1.0)) * (sumXi2 / N - mean * mean));
|
| | }
|
| |
|
| | void SphereFit::ProjectToSphere()
|
| | {
|
| | for (auto& cPnt : _vPoints) {
|
| |
|
| |
|
| |
|
| |
|
| | Base::Vector3d diff(
|
| | (double)cPnt.x - _vCenter.x,
|
| | (double)cPnt.y - _vCenter.y,
|
| | (double)cPnt.z - _vCenter.z
|
| | );
|
| | double length = diff.Length();
|
| | if (length == 0.0) {
|
| |
|
| |
|
| | cPnt.z += (float)_dRadius;
|
| | }
|
| | else {
|
| | diff /= length;
|
| | Base::Vector3d proj = _vCenter + diff * _dRadius;
|
| | cPnt.x = (float)proj.x;
|
| | cPnt.y = (float)proj.y;
|
| | cPnt.z = (float)proj.z;
|
| | }
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | void SphereFit::ComputeApproximations()
|
| | {
|
| | _bIsFitted = false;
|
| | _fLastResult = std::numeric_limits<float>::max();
|
| | _numIter = 0;
|
| | _vCenter.Set(0.0, 0.0, 0.0);
|
| | _dRadius = 0.0;
|
| | if (!_vPoints.empty()) {
|
| | std::list<Base::Vector3f>::const_iterator cIt;
|
| | for (cIt = _vPoints.begin(); cIt != _vPoints.end(); ++cIt) {
|
| | _vCenter.x += cIt->x;
|
| | _vCenter.y += cIt->y;
|
| | _vCenter.z += cIt->z;
|
| | }
|
| | _vCenter /= (double)_vPoints.size();
|
| |
|
| | for (cIt = _vPoints.begin(); cIt != _vPoints.end(); ++cIt) {
|
| | Base::Vector3d diff(
|
| | (double)cIt->x - _vCenter.x,
|
| | (double)cIt->y - _vCenter.y,
|
| | (double)cIt->z - _vCenter.z
|
| | );
|
| | _dRadius += diff.Length();
|
| | }
|
| | _dRadius /= (double)_vPoints.size();
|
| | }
|
| | }
|
| |
|
| | float SphereFit::Fit()
|
| | {
|
| | _bIsFitted = false;
|
| | _fLastResult = std::numeric_limits<float>::max();
|
| | _numIter = 0;
|
| |
|
| |
|
| | if (CountPoints() < 4) {
|
| | return std::numeric_limits<float>::max();
|
| | }
|
| |
|
| |
|
| | if (_dRadius == 0.0) {
|
| | ComputeApproximations();
|
| | }
|
| |
|
| |
|
| | std::vector<Base::Vector3d> residuals(CountPoints(), Base::Vector3d(0.0, 0.0, 0.0));
|
| | Matrix4x4 atpa;
|
| | Eigen::VectorXd atpl(4);
|
| |
|
| |
|
| | double sigma0 {};
|
| | bool cont = true;
|
| | while (cont && (_numIter < _maxIter)) {
|
| | ++_numIter;
|
| |
|
| |
|
| | setupNormalEquationMatrices(residuals, atpa, atpl);
|
| |
|
| |
|
| | Eigen::LLT<Matrix4x4> llt(atpa);
|
| | if (llt.info() != Eigen::Success) {
|
| | return std::numeric_limits<float>::max();
|
| | }
|
| | Eigen::VectorXd x = llt.solve(atpl);
|
| |
|
| |
|
| | cont = false;
|
| | if ((fabs(x(0)) > _posConvLimit) || (fabs(x(1)) > _posConvLimit)
|
| | || (fabs(x(2)) > _posConvLimit) || (fabs(x(3)) > _posConvLimit)) {
|
| | cont = true;
|
| | }
|
| |
|
| |
|
| |
|
| | bool vConverged {};
|
| | if (!computeResiduals(x, residuals, sigma0, _vConvLimit, vConverged)) {
|
| | return std::numeric_limits<float>::max();
|
| | }
|
| | if (!vConverged) {
|
| | cont = true;
|
| | }
|
| |
|
| |
|
| | _vCenter.x += x(0);
|
| | _vCenter.y += x(1);
|
| | _vCenter.z += x(2);
|
| | _dRadius += x(3);
|
| | }
|
| |
|
| |
|
| | if (cont) {
|
| | return std::numeric_limits<float>::max();
|
| | }
|
| |
|
| | _bIsFitted = true;
|
| | _fLastResult = sigma0;
|
| |
|
| | return _fLastResult;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | void SphereFit::setupNormalEquationMatrices(
|
| | const std::vector<Base::Vector3d>& residuals,
|
| | Matrix4x4& atpa,
|
| | Eigen::VectorXd& atpl
|
| | ) const
|
| | {
|
| |
|
| | atpa.setZero();
|
| | atpl.setZero();
|
| |
|
| |
|
| |
|
| | double a[4] {}, b[3] {};
|
| | double f0 {}, qw {};
|
| | std::vector<Base::Vector3d>::const_iterator vIt = residuals.begin();
|
| | for (auto cIt = _vPoints.begin(); cIt != _vPoints.end(); ++cIt, ++vIt) {
|
| |
|
| |
|
| | setupObservation(*cIt, *vIt, a, f0, qw, b);
|
| | addObservationU(a, f0, qw, atpa, atpl);
|
| |
|
| | }
|
| | setLowerPart(atpa);
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | void SphereFit::setupObservation(
|
| | const Base::Vector3f& point,
|
| | const Base::Vector3d& residual,
|
| | double a[4],
|
| | double& f0,
|
| | double& qw,
|
| | double b[3]
|
| | ) const
|
| | {
|
| |
|
| |
|
| |
|
| | double xEstimate = (double)point.x + residual.x;
|
| | double yEstimate = (double)point.y + residual.y;
|
| | double zEstimate = (double)point.z + residual.z;
|
| |
|
| |
|
| | double dx = xEstimate - _vCenter.x;
|
| | double dy = yEstimate - _vCenter.y;
|
| | double dz = zEstimate - _vCenter.z;
|
| | b[0] = 2.0 * dx;
|
| | b[1] = 2.0 * dy;
|
| | b[2] = 2.0 * dz;
|
| |
|
| |
|
| | a[0] = -b[0];
|
| | a[1] = -b[1];
|
| | a[2] = -b[2];
|
| | a[3] = -2.0 * _dRadius;
|
| |
|
| |
|
| | f0 = _dRadius * _dRadius - dx * dx - dy * dy - dz * dz + b[0] * residual.x + b[1] * residual.y
|
| | + b[2] * residual.z;
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | qw = 1.0 / (b[0] * b[0] + b[1] * b[1] + b[2] * b[2]);
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | void SphereFit::addObservationU(double a[4], double li, double pi, Matrix4x4& atpa, Eigen::VectorXd& atpl) const
|
| | {
|
| | for (int i = 0; i < 4; ++i) {
|
| | double aipi = a[i] * pi;
|
| | for (int j = i; j < 4; ++j) {
|
| | atpa(i, j) += aipi * a[j];
|
| |
|
| |
|
| | }
|
| | atpl(i) += aipi * li;
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| | void SphereFit::setLowerPart(Matrix4x4& atpa) const
|
| | {
|
| | for (int i = 0; i < 4; ++i) {
|
| | for (int j = i + 1; j < 4; ++j) {
|
| | atpa(j, i) = atpa(i, j);
|
| | }
|
| | }
|
| | }
|
| |
|
| |
|
| | bool SphereFit::computeResiduals(
|
| | const Eigen::VectorXd& x,
|
| | std::vector<Base::Vector3d>& residuals,
|
| | double& sigma0,
|
| | double vConvLimit,
|
| | bool& vConverged
|
| | ) const
|
| | {
|
| | vConverged = true;
|
| | int nPtsUsed = 0;
|
| | sigma0 = 0.0;
|
| | double a[4] {}, b[3] {};
|
| | double f0 {}, qw {};
|
| |
|
| |
|
| |
|
| |
|
| | std::vector<Base::Vector3d>::iterator vIt = residuals.begin();
|
| | for (auto cIt = _vPoints.begin(); cIt != _vPoints.end(); ++cIt, ++vIt) {
|
| |
|
| |
|
| | ++nPtsUsed;
|
| | Base::Vector3d& v = *vIt;
|
| | setupObservation(*cIt, v, a, f0, qw, b);
|
| | double qv = -f0;
|
| | for (int i = 0; i < 4; ++i) {
|
| | qv += a[i] * x(i);
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | double vx = -qw * qv * b[0];
|
| | double vy = -qw * qv * b[1];
|
| | double vz = -qw * qv * b[2];
|
| | double dVx = fabs(vx - v.x);
|
| | double dVy = fabs(vy - v.y);
|
| | double dVz = fabs(vz - v.z);
|
| | v.x = vx;
|
| | v.y = vy;
|
| | v.z = vz;
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | sigma0 += v.x * v.x + v.y * v.y + v.z * v.z;
|
| |
|
| | if ((dVx > vConvLimit) || (dVy > vConvLimit) || (dVz > vConvLimit)) {
|
| | vConverged = false;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | }
|
| |
|
| |
|
| | if (nPtsUsed < 4)
|
| | {
|
| | sigma0 = 0.0;
|
| | return false;
|
| | }
|
| | int df = nPtsUsed - 4;
|
| | if (df == 0) {
|
| | sigma0 = 0.0;
|
| | }
|
| | else {
|
| | sigma0 = sqrt(sigma0 / (double)df);
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | return true;
|
| | }
|
| |
|