1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
|
//##########################################################################
//# #
//# CCLIB #
//# #
//# This program is free software; you can redistribute it and/or modify #
//# it under the terms of the GNU Library General Public License as #
//# published by the Free Software Foundation; version 2 or later of the #
//# License. #
//# #
//# This program is distributed in the hope that it will be useful, #
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
//# GNU General Public License for more details. #
//# #
//# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
//# #
//##########################################################################
#ifndef CC_NEIGHBOURHOOD_HEADER
#define CC_NEIGHBOURHOOD_HEADER
//Local
#include "CCMiscTools.h"
#include "GenericIndexedCloudPersist.h"
#include "SquareMatrix.h"
namespace CCLib
{
class GenericIndexedMesh;
//! A specific point could structure to handle subsets of points, provided with several geometric processings
/** Typically suited for "nearest neighbours".
It implements the GenericIndexCloud interface by
inheriting the ReferenceCloud class.
**/
class CC_CORE_LIB_API Neighbourhood
{
public:
//! Geometric properties/elements that can be computed from the set of points (see Neighbourhood::getGeometricalElement)
enum GeomElement { FLAG_DEPRECATED = 0,
FLAG_GRAVITY_CENTER = 1,
FLAG_LS_PLANE = 2,
FLAG_QUADRIC = 4 };
//! Curvature type
enum CurvatureType { GAUSSIAN_CURV = 1,
MEAN_CURV,
NORMAL_CHANGE_RATE};
//! Default constructor
/** \param associatedCloud reference cloud
**/
explicit Neighbourhood(GenericIndexedCloudPersist* associatedCloud);
//! Default destructor
virtual ~Neighbourhood() = default;
//! Resets structure (depreactes all associated geometrical fetaures)
virtual void reset();
//! Returns associated cloud
GenericIndexedCloudPersist* associatedCloud() const { return m_associatedCloud; }
//! Applies 2D Delaunay triangulation
/** Cloud selection is first projected on the best least-square plane.
\param duplicateVertices whether to duplicate vertices (a new point cloud is created) or to use the associated one)
\param maxEdgeLength max edge length for output triangles (0 = ignored)
\param errorStr error (if any) [optional]
***/
GenericIndexedMesh* triangulateOnPlane( bool duplicateVertices = false,
PointCoordinateType maxEdgeLength = 0,
char* errorStr = nullptr);
//! Fit a quadric on point set (see getQuadric) then triangulates it inside bounding box
GenericIndexedMesh* triangulateFromQuadric(unsigned stepsX, unsigned stepsY);
enum InputVectorsUsage { UseOXYasBase, UseYAsUpDir, None };
//! Projects points on the best fitting LS plane
/** Projected points are stored in the points2D vector.
\param points2D output set
\param planeEquation custom plane equation (otherwise the default Neighbouhood's one is used)
\param O if set, the local plane base origin will be output here
\param X if set, the local plane base X vector will be output here
\param Y if set, the local plane base Y vector will be output here
\param useOXYasBase whether O, X and Y should be used as input base
\return success
**/
template<class Vec2D> bool projectPointsOn2DPlane( std::vector<Vec2D>& points2D,
const PointCoordinateType* planeEquation = nullptr,
CCVector3* O = nullptr,
CCVector3* X = nullptr,
CCVector3* Y = nullptr,
InputVectorsUsage vectorsUsage = None)
{
//need at least one point ;)
unsigned count = (m_associatedCloud ? m_associatedCloud->size() : 0);
if (!count)
return false;
//if no custom plane equation is provided, get the default best LS one
if (!planeEquation)
{
planeEquation = getLSPlane();
if (!planeEquation)
return false;
}
//reserve memory for output set
try
{
points2D.resize(count);
}
catch (const std::bad_alloc&)
{
//out of memory
return false;
}
//we construct the plane local base
CCVector3 G(0, 0, 0), u(1, 0, 0), v(0, 1, 0);
if ((vectorsUsage == UseOXYasBase) && O && X && Y)
{
G = *O;
u = *X;
v = *Y;
}
else
{
CCVector3 N(planeEquation);
if ((vectorsUsage == UseYAsUpDir) && Y)
{
v = (*Y - Y->dot(N) * N);
v.normalize();
u = v.cross(N);
}
else
{
CCMiscTools::ComputeBaseVectors(N, u, v);
}
//get the barycenter
const CCVector3* _G = getGravityCenter();
assert(_G);
G = *_G;
}
//project the points
for (unsigned i = 0; i < count; ++i)
{
//we recenter current point
const CCVector3 P = *m_associatedCloud->getPoint(i) - G;
//then we project it on plane (with scalar prods)
points2D[i] = Vec2D(P.dot(u), P.dot(v));
}
//output the local base if necessary
if (vectorsUsage != UseOXYasBase)
{
if (O)
*O = G;
if (X)
*X = u;
if (Y)
*Y = v;
}
return true;
}
//! Geometric feature computed from eigen values/vectors
/** Most of them are defined in "Contour detection in unstructured 3D point clouds", Hackel et al, 2016
PCA1 and PCA2 are defined in "3D terrestrial lidar data classification of complex natural scenes using a multi-scale dimensionality criterion: Applications in geomorphology", Brodu and Lague, 2012
**/
enum GeomFeature
{
EigenValuesSum = 1,
Omnivariance,
EigenEntropy,
Anisotropy,
Planarity,
Linearity,
PCA1,
PCA2,
SurfaceVariation,
Sphericity,
Verticality,
EigenValue1,
EigenValue2,
EigenValue3
};
//! Computes the given feature on a set of point
/** \return feature value
**/
double computeFeature(GeomFeature feature);
//! Computes the 1st order moment of a set of point (based on the eigenvalues)
/** \return 1st order moment at a given position P
DGM: The article states that the result should be between 0 and 1,
but this is actually wrong (as (a + b)^2 can be > a^2 + b^2)
**/
ScalarType computeMomentOrder1(const CCVector3& P);
//! Computes the roughness of a set of point (by fitting a 2D plane)
/** \return roughness value at a given position P
\warning The point P shouldn't be in the set of points
**/
ScalarType computeRoughness(const CCVector3& P);
//! Computes the curvature of a set of point (by fitting a 2.5D quadric)
/** \return curvature value at a given position P or NAN_VALUE if the computation failed
\warning The curvature value is always unsigned
**/
ScalarType computeCurvature(const CCVector3& P, CurvatureType cType);
/**** GETTERS ****/
//! Returns gravity center
/** \return 0 if computation failed
**/
const CCVector3* getGravityCenter();
//! Sets gravity center
/** Handle with care!
\param G gravity center
**/
void setGravityCenter(const CCVector3& G);
//! Returns best interpolating plane equation (Least-square)
/** Returns an array of the form [a,b,c,d] such as:
ax + by + cz = d
\return 0 if computation failed
**/
const PointCoordinateType* getLSPlane();
//! Sets the best interpolating plane equation (Least-square)
/** Handle with care!
\param eq plane equation (ax + by + cz = d)
\param X local base X vector
\param Y local base Y vector
\param N normal vector
**/
void setLSPlane( const PointCoordinateType eq[4],
const CCVector3& X,
const CCVector3& Y,
const CCVector3& N);
//! Returns best interpolating plane (Least-square) 'X' base vector
/** This corresponds to the largest eigen value (i.e. the largest cloud dimension)
\return 0 if computation failed
**/
const CCVector3* getLSPlaneX();
//! Returns best interpolating plane (Least-square) 'Y' base vector
/** This corresponds to the second largest eigen value (i.e. the second largest cloud dimension)
\return 0 if computation failed
**/
const CCVector3* getLSPlaneY();
//! Returns best interpolating plane (Least-square) normal vector
/** This corresponds to the smallest eigen value (i.e. the second largest cloud dimension)
\return 0 if computation failed
**/
const CCVector3* getLSPlaneNormal();
//! Returns the best interpolating 2.5D quadric
/** Returns an array of the form [a,b,c,d,e,f] such as:
Z = a + b.X + c.Y + d.X^2 + e.X.Y + f.Y^2
\warning: 'X','Y' and 'Z' are output in dims (optional):
dims = [index(X),index(Y),index(Z)] where: 0=x, 1=y, 2=z
\return 0 if computation failed
**/
const PointCoordinateType* getQuadric(Tuple3ub* dims = nullptr);
//! Computes the best interpolating quadric (Least-square)
/** \param[out] quadricEquation an array of 10 coefficients [a,b,c,d,e,f,g,l,m,n] such as
a.x^2+b.y^2+c.z^2+2e.x.y+2f.y.z+2g.z.x+2l.x+2m.y+2n.z+d = 0
\return success
**/
bool compute3DQuadric(double quadricEquation[10]);
//! Computes the covariance matrix
CCLib::SquareMatrixd computeCovarianceMatrix();
//! Returns the set 'radius' (i.e. the distance between the gravity center and the its farthest point)
PointCoordinateType computeLargestRadius();
protected:
//! 2.5D Quadric equation
/** Array [a,b,c,d,e,f] such that Z = a + b.X + c.Y + d.X^2 + e.X.Y + f.Y^2.
\warning: 'X','Y' and 'Z' are defined by 'm_quadricEquationDirections'
Only valid if 'structuresValidity & QUADRIC != 0'.
**/
PointCoordinateType m_quadricEquation[6];
//! 2.5D Quadric equation dimensions
/** Array (index(X),index(Y),index(Z)) where: 0=x, 1=y, 2=z
Only valid if 'structuresValidity & QUADRIC != 0'.
**/
Tuple3ub m_quadricEquationDirections;
//! Least-square best fitting plane parameters
/** Array [a,b,c,d] such that ax+by+cz = d
Only valid if 'structuresValidity & LS_PLANE != 0'.
**/
PointCoordinateType m_lsPlaneEquation[4];
//! Least-square best fitting plane base vectors
/** Only valid if 'structuresValidity & LS_PLANE != 0'.
**/
CCVector3 m_lsPlaneVectors[3];
//! Gravity center
/** Only valid if 'structuresValidity & GRAVITY_CENTER != 0'.
**/
CCVector3 m_gravityCenter;
//! Geometrical elements validity (flags)
unsigned char m_structuresValidity;
//! Computes the gravity center
void computeGravityCenter();
//! Computes the least-square best fitting plane
bool computeLeastSquareBestFittingPlane();
//! Computes best fitting 2.5D quadric
bool computeQuadric();
//! Associated cloud
GenericIndexedCloudPersist* m_associatedCloud;
};
}
#endif //CC_NEIGHBOURHOOD_HEADER
|