#pragma once #include #include #include #include namespace cgv{ namespace math{ /** * A plane is defined as a vector (a,b,c,d) => a*x1 + b*x2 +c*x3 +d = 0 */ ///evaluate implicit plane equation at x =(x1,x2,x3) ///return value should be zero on plane template T plane_val(const vec& plane,const vec& x) { assert(plane.size()-1 == x.size()); T val=0; for(unsigned i = 0;i< x.size(); i++) val += plane(i)*x(i); return val+plane(plane.size()-1); } ///evaluate plame equation on multiple positions xs template vec plane_val(const vec& plane,const mat& xs) { assert(plane.size()-1 == xs.nrows()); vec vals(xs.ncols()); for(unsigned i = 0;i< xs.ncols(); i++) vals(i) = plane_val(plane,xs.col(i)); return vals; } //construct implicit plane from 3 points template vec plane_fit(const vec& p1,const vec& p2,const vec& p3) { vec plane(4); double l_nml; vec nml = cross(normalize(p2-p1),normalize(p3-p1)); l_nml = length(nml); assert(l_nml != 0); nml/=l_nml; plane.set(nml(0),nml(1),nml(2),-dot(p1,nml)); return plane; } //fit implicit plane into multiple points (total least squares) template vec tls_plane_fit(const mat& points) { vec plane(4); mat covmat,v; diag_mat d; vec mean; covmat_and_mean(points,covmat, mean); eig_sym(covmat,v,d); // T l_nml; vec nml = normalize(v.col(2)); plane.set(nml(0),nml(1),nml(2),-dot(mean,nml)); return plane; } ///ransac plane fit ///p_out... outlier prob ///d_max... threshold distance ///p_surety... surety to compute number needed samples ///if m_sac flag is true m-estimator cost function is used ///if loransac flag is true template vec ransac_plane_fit(const mat& points,const T p_out=0.8, const T d_max=0.001, const T p_surety = 0.99, bool msac=true) { assert(points.nrows == 3); vec plane(4); vec ind; vec p1,p2,p3,dists(points.ncols()); cgv::math::random rg; unsigned n = points.ncols; unsigned max_iter = num_ransac_iterations(3,p_out,psurety); unsigned num_inlier = (unsigned)::ceil((1-p_out)*points.ncols()); T error = std::numeric_limits::max(); for(unsigned i = 0; i < max_iter; i++) { //random sample do { rg.uniform_nchoosek(n,3,ind); p1 = points.col(ind(0)); p2 = points.col(ind(1)); p3 = points.col(ind(2)); } while(fabs(length(cross(q2-q1,q3-q1)))<0.001); //fit vec pl = plane_fit(p1,p2,p3); //consensus int n_inlier=0; T e = 0; for(unsigned i = 0; i < points.ncols(); i++) { dists(i) = std::abs(plane_val(pl,points.col(i))); if(dists(i) < d_max) { n_inlier++; if(msac) e += dists(i); } else { e += dmax; } } //local reoptimize if(loransac && n_inlier > 3) { mat inliers(3,n_inlier); int j=0; n_inlier=0; for(unsigned i = 0; i < points.ncols(); i++) { if(dists(i) < d_max) { n_inlier++; inliers.set_col(j,points.col(i)); j++; } } pl = plane_fit(inliers) //recompute error for(unsigned i = 0; i < points.ncols(); i++) { dists(i) = std::abs(plane_val(pl,points.col(i))); if(dists(i) < d_max) { if(msac) e += dists(i); } else { e += dmax; } } } //remember best plane if(error > e) { error = e; plane = pl; } if(n_inlier >= num_inlier) { p_out = (T)1-(T)n_inlier/(T)points.ncols(); max_iter = num_ransac_iterations(3,p_out,psurety); } } return plane; } } }