v2.0.0
Loading...
Searching...
No Matches
sphere.cpp
Go to the documentation of this file.
1//=============================================================================================================
26
27//=============================================================================================================
28// INCLUDES
29//=============================================================================================================
30
31#include "sphere.h"
32#include "simplex_algorithm.h"
33
34#include <QDebug>
35
36//=============================================================================================================
37// EIGEN INCLUDES
38//=============================================================================================================
39
40#include <Eigen/Dense>
41
42#include <iostream>
43
44//=============================================================================================================
45// USED NAMESPACES
46//=============================================================================================================
47
48using namespace UTILSLIB;
49using namespace Eigen;
50
51//=============================================================================================================
52// DEFINE MEMBER METHODS
53//=============================================================================================================
54
55Sphere::Sphere(const Vector3f& center, float radius)
56: m_center(center)
57, m_r(radius)
58{
59}
60
61//=============================================================================================================
62
63Sphere Sphere::fit_sphere(const MatrixX3f& points)
64{
65 const VectorXf& x = points.col(0);
66 const VectorXf& y = points.col(1);
67 const VectorXf& z = points.col(2);
68
69 VectorXf point_means = points.colwise().mean();
70
71 VectorXf x_mean_free = x.array() - point_means(0);
72 VectorXf y_mean_free = y.array() - point_means(1);
73 VectorXf z_mean_free = z.array() - point_means(2);
74
75 Matrix3f A;
76 A << (x.cwiseProduct(x_mean_free)).mean(), 2*(x.cwiseProduct(y_mean_free)).mean(), 2*(x.cwiseProduct(z_mean_free)).mean(),
77 0, (y.cwiseProduct(y_mean_free)).mean(), 2*(y.cwiseProduct(z_mean_free)).mean(),
78 0, 0, (z.cwiseProduct(z_mean_free)).mean();
79
80 Matrix3f A_T = A.transpose();
81 A += A_T;
82
83 Vector3f b;
84 VectorXf sq_sum = x.array().pow(2)+y.array().pow(2)+z.array().pow(2);
85 b << (sq_sum.cwiseProduct(x_mean_free)).mean(),
86 (sq_sum.cwiseProduct(y_mean_free)).mean(),
87 (sq_sum.cwiseProduct(z_mean_free)).mean();
88
89 Vector3f center = A.ldlt().solve(b);
90
91 MatrixX3f tmp(points.rows(),3);
92 tmp.col(0) = x.array() - center(0);
93 tmp.col(1) = y.array() - center(1);
94 tmp.col(2) = z.array() - center(2);
95
96 float r = sqrt(tmp.array().pow(2).rowwise().sum().mean());
97
98 return Sphere(center, r);
99}
100
101//=============================================================================================================
102
103Sphere Sphere::fit_sphere_simplex(const MatrixX3f& points, double simplex_size)
104{
105 VectorXf center;
106 float R;
107 if(fit_sphere_to_points( points, simplex_size, center, R)) {
108 return Sphere(center, R);
109 }
110
111 return Sphere(Vector3f(), 0.0f);
112}
113
114//=============================================================================================================
115
116bool Sphere::fit_sphere_to_points(float **rr, int np, float simplex_size, float *r0, float *R)
117{
118 MatrixXf rr_eigen(np,3);
119 VectorXf r0_eigen(3);
120
121 MatrixX3f dig_rr_eigen;
122 for (int k = 0; k < np; k++) {
123 rr_eigen(k, 0) = rr[k][0];
124 rr_eigen(k, 1) = rr[k][1];
125 rr_eigen(k, 2) = rr[k][2];
126 }
127
128 if(rr_eigen.rows() < 0) {
129 std::cout << "Sphere::fit_sphere_to_points - No points were passed." << std::endl;
130 return false;
131 }
132
133 r0_eigen(0) = r0[0];
134 r0_eigen(1) = r0[1];
135 r0_eigen(2) = r0[2];
136
137 bool state = UTILSLIB::Sphere::fit_sphere_to_points(rr_eigen,simplex_size,r0_eigen,*R);
138
139 r0[0] = r0_eigen(0);
140 r0[1] = r0_eigen(1);
141 r0[2] = r0_eigen(2);
142
143 return state;
144}
145
146//=============================================================================================================
147
148bool Sphere::fit_sphere_to_points(const MatrixXf &rr, float simplex_size, VectorXf &r0, float &R)
149{
150// int np = rr.rows();
151
152 /*
153 * Find the optimal sphere origin
154 */
155 FitUser user;
156 float ftol = 1e-5f;
157 int max_eval = 500;
158 int report_interval = -1;
159 int neval;
160 MatrixXf init_simplex;
161 VectorXf init_vals(4);
162
163 VectorXf cm(3);
164 float R0 = 0.1f;
165
166 user.rr = rr;
167
168 calculate_cm_ave_dist(rr, cm, R0);// [done]
169
170 init_simplex = make_initial_simplex( cm, simplex_size );
171
172 user.report = false;
173
174 for (int k = 0; k < 4; k++) {
175 init_vals[k] = fit_eval( static_cast<VectorXf>(init_simplex.row(k)), &user );
176 }
177
178 user.report = false;
179
180 // Capture user data in type-safe lambda — no void* needed
181 auto cost = [&user](const VectorXf& x) -> float { return fit_eval(x, &user); };
182
183 //Start the minimization
184 if(!SimplexAlgorithm::simplex_minimize<float>( init_simplex, /* The initial simplex */
185 init_vals, /* Function values at the vertices */
186 ftol, /* Relative convergence tolerance */
187 0.0f, /* No spatial convergence tolerance */
188 cost, /* The cost function (captures user data) */
189 max_eval, /* Maximum number of function evaluations */
190 neval, /* Number of function evaluations */
191 report_interval,/* How often to report (-1 = no_reporting) */
192 report_func)) /* The function to be called when reporting */
193 {
194 return false;
195 }
196
197 r0 = init_simplex.row(0);
198 R = opt_rad(r0, &user);
199
200 return true;
201}
202
203//=============================================================================================================
204
205bool Sphere::report_func(int loop, const VectorXf &fitpar, double fval_lo, double /*fval_hi*/, double /*par_diff*/)
206{
207 /*
208 * Report periodically
209 */
210 const VectorXf& r0 = fitpar;
211
212 std::cout << "loop: " << loop << "; r0: " << 1000*r0[0] << ", r1: " << 1000*r0[1] << ", r2: " << 1000*r0[2] << "; fval: " << fval_lo << std::endl;
213
214 return true;
215}
216
217//=============================================================================================================
218
219void Sphere::calculate_cm_ave_dist(const MatrixXf &rr, VectorXf &cm, float &avep)
220{
221 cm = rr.colwise().mean();
222 MatrixXf diff = rr.rowwise() - cm.transpose();
223 avep = diff.rowwise().norm().mean();
224}
225
226//=============================================================================================================
227
228MatrixXf Sphere::make_initial_simplex(const VectorXf &pars, float size)
229{
230 /*
231 * Make the initial tetrahedron
232 */
233 int npar = pars.size();
234
235 MatrixXf simplex = MatrixXf::Zero(npar+1,npar);
236
237 simplex.rowwise() += pars.transpose();
238
239 for (int k = 1; k < npar+1; k++) {
240 simplex(k,k-1) += size;
241 }
242
243 return simplex;
244}
245
246//=============================================================================================================
247
248float Sphere::fit_eval(const VectorXf &fitpar, const void *user_data)
249{
250 /*
251 * Calculate the cost function value
252 * Optimize for the radius inside here
253 */
254 const FitUser* user = static_cast<const FitUser*>(user_data);
255 const VectorXf& r0 = fitpar;
256
257 float F;
258
259 MatrixXf diff = user->rr.rowwise() - r0.transpose();
260 VectorXf one = diff.rowwise().norm();
261
262 float sum = one.sum();
263 float sum2 = one.dot(one);
264
265 F = sum2 - sum*sum/user->rr.rows();
266
267 if(user->report)
268 std::cout << "r0: " << 1000*r0[0] << ", r1: " << 1000*r0[1] << ", r2: " << 1000*r0[2] << "; R: " << 1000*sum/user->rr.rows() << "; fval: "<<F<<std::endl;
269
270 return F;
271}
272
273//=============================================================================================================
274
275float Sphere::opt_rad(const VectorXf &r0,const FitUser* user)
276{
277 MatrixXf diff = user->rr.rowwise() - r0.transpose();
278 return diff.rowwise().norm().mean();
279}
Eigen::Matrix3f R
Best-fit sphere from a 3-D point cloud with closed-form and Nelder–Mead solvers.
Header-only Nelder–Mead simplex minimiser with pluggable cost and report callables.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
static bool simplex_minimize(Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &p, Eigen::Matrix< T, Eigen::Dynamic, 1 > &y, T ftol, T stol, CostFunc &&func, int max_eval, int &neval, int report, ReportFunc &&report_func)
Cost-function workspace for Sphere::fit_sphere_simplex: holds the nx3 point cloud and a verbose-repor...
Definition sphere.h:57
Eigen::MatrixXf rr
Definition sphere.h:58
static bool fit_sphere_to_points(const Eigen::MatrixXf &rr, float simplex_size, Eigen::VectorXf &r0, float &R)
static Sphere fit_sphere_simplex(const Eigen::MatrixX3f &points, double simplex_size=2e-2)
Definition sphere.cpp:103
Eigen::Vector3f & center()
Definition sphere.h:111
float & radius()
Definition sphere.h:119
static Sphere fit_sphere(const Eigen::MatrixX3f &points)
Definition sphere.cpp:63
Sphere(const Eigen::Vector3f &center, float radius)
Definition sphere.cpp:55