2 #ifndef vgl_fit_sphere_3d_hxx_ 3 #define vgl_fit_sphere_3d_hxx_ 10 #include <vnl/algo/vnl_svd.h> 11 #include <vnl/vnl_matrix.h> 13 # include <vcl_msvc_warnings.h> 16 #include <vnl/algo/vnl_levenberg_marquardt.h> 17 #include <vnl/vnl_least_squares_function.h> 21 vnl_least_squares_function(4, static_cast<unsigned>(pts.size()), vnl_least_squares_function::use_gradient),
pts_(pts){}
27 void f(vnl_vector<double>
const& x, vnl_vector<double>& fx)
override{
28 double x0 = x[0], y0 = x[1], z0 = x[2], r = x[3];
29 unsigned n = get_number_of_residuals();
30 for(
unsigned i = 0; i<n; ++i){
32 double xi = (p.
x()-x0), yi = (p.
y()-y0), zi = (p.
z()-z0);
33 double ri = std::sqrt(xi*xi + yi*yi + zi*zi);
43 void gradf(vnl_vector<double>
const& x, vnl_matrix<double>& J)
override{
44 double x0 = x[0], y0 = x[1], z0 = x[2];
45 unsigned n = get_number_of_residuals();
46 for (
unsigned i = 0; i<n; ++i)
49 double xi = (p.
x()-x0), yi = (p.
y()-y0), zi = (p.
z()-z0);
50 double ri = std::sqrt(xi*xi + yi*yi + zi*zi);
51 J[i][0]= -(xi-x0)/ri; J[i][1]= -(yi-y0)/ri; J[i][2]= -(zi-z0)/ri; J[i][3]= -1.0;
56 std::vector<vgl_homg_point_3d<double> >
pts_;
63 for(
typename std::vector<
vgl_point_3d<T> >::iterator pit = points.begin();
64 pit != points.end(); ++pit)
89 const unsigned n = static_cast<unsigned>(points_.size());
92 *errstream <<
"No points to fit sphere\n";
98 *errstream <<
"there is a problem with norm transform\n";
101 vnl_matrix<T> A(n,4), B(n,1);
102 for (
unsigned i=0; i<n; i++) {
104 const T x = hp.
x()/hp.
w();
105 const T y = hp.
y()/hp.
w();
106 const T z = hp.
z()/hp.
w();
107 A[i][0]= -T(2)*x; A[i][1]= -T(2)*y; A[i][2]= -T(2)*z; A[i][3]=T(1);
108 B[i][0] = -(x*x + y*y + z*z);
111 vnl_matrix<T> P = svd.solve(B);
112 T x0 = P[0][0], y0 = P[1][0], z0 = P[2][0];
114 T r2 = x0*x0 + y0*y0 + z0*z0 - rho;
117 *errstream <<
"Negative squared radius - impossible result \n";
122 vnl_matrix_fixed<T,4,4> H = norm.
get_matrix();
124 T tx = H[0][3]; T ty= H[1][3]; T tz= H[2][3];
127 T x0p = (x0-tx)/scale, y0p = (y0-ty)/scale, z0p = (z0-tz)/scale, rp = r/scale;
130 sphere_lin_.set_radius(rp);
134 for (
unsigned i=0; i<n; i++) {
139 return static_cast<T>(dsum/n);
144 T error = this->fit_linear(outstream);
145 T lin_radius = sphere_lin_.radius();
147 if(error == T(-1) || error> T(0.1)*lin_radius){
149 *outstream <<
" Linear fit failed - non-linear fit abandoned\n";
152 unsigned n = static_cast<unsigned>(points_.size());
155 if (!norm.compute_from_points(points_) && outstream) {
156 *outstream <<
"there is a problem with norm transform\n";
159 vnl_matrix_fixed<T,4,4> H = norm.get_matrix();
161 T tx = H[0][3]; T ty= H[1][3]; T tz= H[2][3];
166 T lin_x0 = scale*c.
x()+tx, lin_y0 = scale*c.
y()+ty, lin_z0 = scale*c.
z()+tz;
168 std::vector<vgl_homg_point_3d<double> > pts;
169 for(
unsigned i = 0; i<n; ++i){
172 static_cast<double>(hp.
y()),
173 static_cast<double>(hp.
z()),
174 static_cast<double>(hp.
w()));
178 vnl_levenberg_marquardt lm(srf);
180 vnl_vector<double> x_init(4);
181 x_init[0]=static_cast<double>(lin_x0);x_init[1]=static_cast<double>(lin_y0);
182 x_init[2]=static_cast<double>(lin_z0);x_init[3]=static_cast<double>(lin_radius);
185 if(outstream && verbose)
186 lm.diagnose_outcome(*outstream);
188 vnl_nonlinear_minimizer::ReturnCodes code = lm.get_failure_code();
189 if((code==vnl_nonlinear_minimizer::CONVERGED_FTOL||
190 code==vnl_nonlinear_minimizer::CONVERGED_XTOL||
191 code==vnl_nonlinear_minimizer::CONVERGED_XFTOL||
192 code==vnl_nonlinear_minimizer::CONVERGED_GTOL)){
193 T x0 = static_cast<T>(x_init[0]), y0 = static_cast<T>(x_init[1]), z0 = static_cast<T>(x_init[2]);
194 T nr = static_cast<T>(x_init[3]);
196 T x0p = (x0-tx)/scale, y0p = (y0-ty)/scale, z0p = (z0-tz)/scale, rp = nr/scale;
199 sphere_non_lin_.set_centre(cf);
200 sphere_non_lin_.set_radius(rp);
202 sphere_non_lin_ = sphere_lin_;
206 for (
unsigned i=0; i<n; i++) {
211 return static_cast<T>(dsum/n);
215 std::vector<vgl_point_3d<T> > ret;
216 const unsigned n = static_cast<unsigned>(points_.size());
217 for (
unsigned i=0; i<n; i++){
225 T error = fit_linear(outstream);
226 return (error<error_marg);
231 T error = fit(outstream, verbose);
232 return (error<error_marg);
235 #undef VGL_FIT_SPHERE_3D_INSTANTIATE 236 #define VGL_FIT_SPHERE_3D_INSTANTIATE(T) \ 237 template class vgl_fit_sphere_3d<T > 239 #endif // vgl_fit_sphere_3d_hxx_ std::vector< vgl_homg_point_3d< double > > pts_
T fit_linear(std::ostream *outstream=nullptr)
fit a sphere to the stored points using a linear method.
sphere_residual_function(std::vector< vgl_homg_point_3d< double > > const &pts)
Represents a cartesian 3D point.
Represents a homogeneous 3D point.
std::vector< vgl_point_3d< T > > get_points() const
vgl_fit_sphere_3d()=default
void add_point(vgl_point_3d< T > const &p)
add a point to point set.
bool compute_from_points(std::vector< vgl_homg_point_3d< T > > const &points)
compute the normalizing transform.
void gradf(vnl_vector< double > const &x, vnl_matrix< double > &J) override
Fits a sphere to a set of 3D points.
vnl_matrix_fixed< T, 4, 4 > const & get_matrix() const
Return the 4x4 homography matrix.
T fit(std::ostream *outstream=nullptr, bool verbose=false)
fits a sphere nonlinearly to the stored points using Levenberg Marquardt.
double vgl_distance(vgl_point_2d< T >const &p1, vgl_point_2d< T >const &p2)
return the distance between two points.
void f(vnl_vector< double > const &x, vnl_vector< double > &fx) override
Set of distance functions.
The similarity transform that normalizes a 3-d point set.
void clear()
clear internal data.
vgl_point_3d< Type > centre(vgl_point_3d< Type > const &p1, vgl_point_3d< Type > const &p2)
Return the point at the centre of gravity of two given points.