点云的基本几何计算
1.计算法向量
function normal = estimateNormal(data, tree, query, radius, min_neighbors) % ESTIMATENORMAL Given a point cloud and query point, estimate the surface % normal by performing an eigendecomposition of the covariance matrix created % from the nearest neighbors of the query point for a fixed radius. % % Example: % data = randn(256,3); % tree = KDTreeSearcher(data); % query = [data(1,1) data(1,2) data(1,3)]; % radius = 1.0; % min_neighbors = 8; % normal = estimateNormal(data, tree, query, radius, min_neighbors) % % Copyright (c) 2014, William J. Beksi <beksi@cs.umn.edu> % % Find neighbors within the fixed radius idx = rangesearch(tree, query, radius); idxs = idx{1}; neighbors = [data(idxs(:),1) data(idxs(:),2) data(idxs(:),3)]; if size(neighbors) < min_neighbors normal = {1}; return; end % Compute the covariance matrix C C = cov(neighbors); % Compute the eigenvector of C [v, lambda] = eig(C); % Find the eigenvector corresponding to the minimum eigenvalue in C [~, i] = min(diag(lambda)); % Normalize normal = v(:,i) ./ norm(v(:,i)); end
2.计算曲率
曲线的曲率(curvature)就是针对曲线上某个点的切线方向角对弧长的转动率,通过微分来定义,表明曲线偏离直线的程度。数学上表明曲线在某一点的弯曲程度的数值。曲率越大,表示曲线的弯曲程度越大。曲率的倒数就是曲率半径。
平均曲率、高斯曲率、主曲率计算
贺美芳, et al. (2005.8). "散乱点云数据的曲率估算及应用."
1 ScalarType Neighbourhood::computeCurvature(unsigned neighbourIndex, CC_CURVATURE_TYPE cType) 2 { 3 switch (cType) 4 { 5 case GAUSSIAN_CURV: 6 case MEAN_CURV: 7 { 8 //we get 2D1/2 quadric parameters 9 const PointCoordinateType* H = getQuadric(); 10 if (!H) 11 return NAN_VALUE; 12 13 //compute centroid 14 const CCVector3* G = getGravityCenter(); 15 16 //we compute curvature at the input neighbour position + we recenter it by the way 17 CCVector3 Q = *m_associatedCloud->getPoint(neighbourIndex) - *G; 18 19 const unsigned char X = m_quadricEquationDirections.x; 20 const unsigned char Y = m_quadricEquationDirections.y; 21 22 //z = a+b.x+c.y+d.x^2+e.x.y+f.y^2 23 //const PointCoordinateType& a = H[0]; 24 const PointCoordinateType& b = H[1]; 25 const PointCoordinateType& c = H[2]; 26 const PointCoordinateType& d = H[3]; 27 const PointCoordinateType& e = H[4]; 28 const PointCoordinateType& f = H[5]; 29 30 //See "CURVATURE OF CURVES AND SURFACES – A PARABOLIC APPROACH" by ZVI HAR’EL 31 const PointCoordinateType fx = b + (d*2) * Q.u[X] + (e ) * Q.u[Y]; // b+2d*X+eY 32 const PointCoordinateType fy = c + (e ) * Q.u[X] + (f*2) * Q.u[Y]; // c+2f*Y+eX 33 const PointCoordinateType fxx = d*2; // 2d 34 const PointCoordinateType fyy = f*2; // 2f 35 const PointCoordinateType& fxy = e; // e 36 37 const PointCoordinateType fx2 = fx*fx; 38 const PointCoordinateType fy2 = fy*fy; 39 const PointCoordinateType q = (1 + fx2 + fy2); 40 41 switch (cType) 42 { 43 case GAUSSIAN_CURV: 44 { 45 //to sign the curvature, we need a normal! 46 PointCoordinateType K = fabs( fxx*fyy - fxy*fxy ) / (q*q); 47 return static_cast<ScalarType>(K); 48 } 49 50 case MEAN_CURV: 51 { 52 //to sign the curvature, we need a normal! 53 PointCoordinateType H = fabs( ((1+fx2)*fyy - 2*fx*fy*fxy + (1+fy2)*fxx) ) / (2*sqrt(q)*q); 54 return static_cast<ScalarType>(H); 55 } 56 57 default: 58 assert(false); 59 } 60 } 61 break; 62 63 case NORMAL_CHANGE_RATE: 64 { 65 assert(m_associatedCloud); 66 unsigned pointCount = (m_associatedCloud ? m_associatedCloud->size() : 0); 67 68 //we need at least 4 points 69 if (pointCount < 4) 70 { 71 //not enough points! 72 return pointCount == 3 ? 0 : NAN_VALUE; 73 } 74 75 //we determine plane normal by computing the smallest eigen value of M = 1/n * S[(p-µ)*(p-µ)'] 76 CCLib::SquareMatrixd eigVectors; 77 std::vector<double> eigValues; 78 if (!Jacobi<double>::ComputeEigenValuesAndVectors(computeCovarianceMatrix(), eigVectors, eigValues)) 79 { 80 //failure 81 return NAN_VALUE; 82 } 83 84 //compute curvature as the rate of change of the surface 85 double e0 = eigValues[0]; 86 double e1 = eigValues[1]; 87 double e2 = eigValues[2]; 88 double sum = fabs(e0+e1+e2); 89 if (sum < ZERO_TOLERANCE) 90 { 91 return NAN_VALUE; 92 } 93 94 double eMin = std::min(std::min(e0,e1),e2); 95 return static_cast<ScalarType>(fabs(eMin) / sum); 96 } 97 break; 98 99 default: 100 assert(false); 101 } 102 103 return NAN_VALUE; 104 }
3.计算点云密度
CloudCompare中的实现方法 http://www.cnblogs.com/yhlx125/p/5874936.html
4.计算点云表面粗糙度
1 //"PER-CELL" METHOD: ROUGHNESS ESTIMATION (LEAST SQUARES PLANE FIT) 2 //ADDITIONNAL PARAMETERS (1): 3 // [0] -> (PointCoordinateType*) kernelRadius : neighbourhood radius 4 bool GeometricalAnalysisTools::computePointsRoughnessInACellAtLevel(const DgmOctree::octreeCell& cell, 5 void** additionalParameters, 6 NormalizedProgress* nProgress/*=0*/) 7 { 8 //parameter(s) 9 PointCoordinateType radius = *static_cast<PointCoordinateType*>(additionalParameters[0]); 10 11 //structure for nearest neighbors search 12 DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; 13 nNSS.level = cell.level; 14 nNSS.prepare(radius,cell.parentOctree->getCellSize(nNSS.level)); 15 cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); 16 cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); 17 18 unsigned n = cell.points->size(); //number of points in the current cell 19 20 //for each point in the cell 21 for (unsigned i=0; i<n; ++i) 22 { 23 ScalarType d = NAN_VALUE; 24 cell.points->getPoint(i,nNSS.queryPoint); 25 26 //look for neighbors inside a sphere 27 //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (= neighborCount)! 28 unsigned neighborCount = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,false); 29 if (neighborCount > 3) 30 { 31 //find the query point in the nearest neighbors set and place it at the end 32 const unsigned globalIndex = cell.points->getPointGlobalIndex(i); 33 unsigned localIndex = 0; 34 while (localIndex < neighborCount && nNSS.pointsInNeighbourhood[localIndex].pointIndex != globalIndex) 35 ++localIndex; 36 //the query point should be in the nearest neighbors set! 37 assert(localIndex < neighborCount); 38 if (localIndex+1 < neighborCount) //no need to swap with another point if it's already at the end! 39 { 40 std::swap(nNSS.pointsInNeighbourhood[localIndex],nNSS.pointsInNeighbourhood[neighborCount-1]); 41 } 42 43 DgmOctreeReferenceCloud neighboursCloud(&nNSS.pointsInNeighbourhood,neighborCount-1); //we don't take the query point into account! 44 Neighbourhood Z(&neighboursCloud); 45 46 const PointCoordinateType* lsPlane = Z.getLSPlane(); 47 if (lsPlane) 48 d = fabs(DistanceComputationTools::computePoint2PlaneDistance(&nNSS.queryPoint,lsPlane)); 49 50 //swap the points back to their original position (DGM: not necessary) 51 //if (localIndex+1 < neighborCount) 52 //{ 53 // std::swap(nNSS.pointsInNeighbourhood[localIndex],nNSS.pointsInNeighbourhood[neighborCount-1]); 54 //} 55 } 56 57 cell.points->setPointScalarValue(i,d); 58 59 if (nProgress && !nProgress->oneStep()) 60 { 61 return false; 62 } 63 } 64 65 return true; 66 }