27 #include <QApplication>
28 #include <QMainWindow>
29 #include <QProgressDialog>
30 #include <QtConcurrentMap>
42 std::vector<PointCoordinateType>
radii;
73 double bestPlanarityCriterion = 0;
74 unsigned bestSamplePointCount = 0;
76 for (
size_t i = 0; i < radiiCount; ++i) {
78 .radii[radiiCount - 1 -
80 double squareRadius = radius * radius;
83 for (
unsigned j = 0; j < static_cast<unsigned>(n); ++j)
84 if (neighbours[j].squareDistd <= squareRadius)
90 if (subset.
size() < 3)
break;
105 std::vector<double> eigValues;
117 for (
unsigned k = 0; k < 3; ++k) {
119 svalues.
u[k] = eigValues[k];
120 svalues.
u[k] = svalues.
u[k] * svalues.
u[k];
121 totalvar += svalues.
u[k];
125 std::sort(svalues.
u, svalues.
u + 3);
135 double b = 2 * svalues[0] + 4 * svalues[1] - 2;
137 if (bestSamplePointCount == 0 || b > bestPlanarityCriterion) {
138 bestPlanarityCriterion = b;
139 bestSamplePointCount = subset.
size();
144 double minEigValue = 0;
146 eigVectors, eigValues, minEigValue, vec);
152 bestScale =
static_cast<ScalarType
>(radius * 2);
157 if (bestSamplePointCount < 3) {
183 const std::vector<PointCoordinateType>& sortedRadii,
190 assert(!sortedRadii.empty());
195 if (corePtsCount == 0)
return false;
209 if (theOctree->
build() == 0) {
219 qPrintable(QString(
"Core points: %1\nSource points: %2")
230 if (!corePointsNormals->
resizeSafe(corePtsCount)) {
231 if (!inputOctree)
delete theOctree;
253 bool useParallelStrategy =
true;
255 useParallelStrategy =
false;
258 std::vector<unsigned> corePointsIndexes;
259 if (useParallelStrategy) {
261 corePointsIndexes.resize(corePtsCount);
262 }
catch (
const std::bad_alloc&) {
264 useParallelStrategy =
false;
268 if (useParallelStrategy) {
269 for (
unsigned i = 0; i < corePtsCount; ++i) {
270 corePointsIndexes[i] = i;
273 if (maxThreadCount == 0) {
274 maxThreadCount = QThread::idealThreadCount();
276 QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
280 for (
unsigned i = 0; i < corePtsCount; ++i) {
293 if (!inputOctree)
delete theOctree;
326 if (j == 0 || squareDist < minSquareDist) {
328 minSquareDist = squareDist;
333 if (N.
dot(orientation) < 0) {
357 "[qM3C2Tools::UpdateNormalOrientationsWithCloud] Cloud/normals "
364 if (orientationCount == 0) {
374 qPrintable(QString(
"Normals: %1\nOrientation points: %2")
390 std::vector<unsigned> pointIndexes;
391 bool useParallelStrategy =
true;
393 useParallelStrategy =
false;
395 if (useParallelStrategy) {
397 pointIndexes.resize(
count);
398 }
catch (
const std::bad_alloc&) {
400 useParallelStrategy =
false;
404 if (useParallelStrategy) {
405 for (
unsigned i = 0; i <
count; ++i) {
409 if (maxThreadCount == 0) {
410 maxThreadCount = QThread::idealThreadCount();
412 QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
416 for (
unsigned i = 0; i <
count; ++i) {
432 for (
unsigned i = 0; i <
count; i++) {
458 size_t nd2 =
count / 2;
459 double midValue = set[begin + nd2].squareDistd;
461 if ((
count & 1) == 0)
463 midValue = (midValue + set[begin + nd2 - 1].squareDistd) / 2;
477 size_t num = set.size();
478 size_t num_pts_each_half = (num + 1) / 2;
479 size_t offset_second_half = num / 2;
481 double q1 =
Median(set, 0, num_pts_each_half);
482 double q3 =
Median(set, offset_second_half, num_pts_each_half);
489 double& meanOrMedian,
490 double& stdDevOrIQR) {
491 size_t count = set.size();
496 }
else if (
count == 1) {
497 meanOrMedian = set.back().squareDistd;
504 std::sort(set.begin(), set.end(),
507 meanOrMedian =
Median(set);
513 for (
size_t i = 0; i <
count; ++i) {
514 const ScalarType&
dist = set[i].squareDistd;
515 sum +=
static_cast<double>(
517 sum2 +=
static_cast<double>(
dist) *
dist;
522 sum2 = sqrt(
fabs(sum2 /
count - sum * sum));
524 meanOrMedian =
static_cast<ScalarType
>(sum);
525 stdDevOrIQR =
static_cast<ScalarType
>(sum2);
531 unsigned minPoints4Stats,
535 unsigned probingCount ) {
537 if (!cloud1 || !cloud2 || minPoints4Stats == 0)
return false;
540 unsigned count1 = cloud1->
size();
552 double baseRadius =
std::min(d1, d2) * 0.01;
580 QProgressDialog pDlg(
"Please wait...",
"Cancel", 0, 0);
581 pDlg.setWindowTitle(
"M3C2");
584 QApplication::processEvents();
587 probingCount = std::max<unsigned>(probingCount, 10);
594 bool hasBestNormLevel =
false;
595 bool hasBestProjLevel =
false;
596 double bestMeanRoughness = -1.0;
597 std::vector<unsigned> populations;
598 populations.resize(probingCount, 0);
600 while (!hasBestNormLevel || !hasBestProjLevel) {
601 unsigned char level =
602 octree1->findBestLevelForAGivenNeighbourhoodSizeExtraction(
605 app->
dispToConsole(QString(
"[M3C2::auto] Test scale: %1 (level "
612 double sumPopulation = 0;
613 double sumPopulation2 = 0;
614 unsigned validLSPlanes = 0;
615 double sumRoughness = 0;
617 for (
unsigned i = 0; i < probingCount; ++i) {
618 unsigned pointIndex =
static_cast<unsigned>(
619 ceil(
static_cast<double>(rand()) / RAND_MAX *
620 static_cast<double>(count1)));
621 if (count1 == pointIndex) count1--;
625 octree1->getPointsInSphericalNeighbourhood(*P, scale, neighbors,
628 size_t n = neighbors.size();
629 populations[i] =
static_cast<unsigned>(n);
630 sumPopulation +=
static_cast<double>(n);
631 sumPopulation2 +=
static_cast<double>(n * n);
633 if (!hasBestNormLevel && n >= 12) {
635 if (!neighborCloud.
resize(
static_cast<unsigned>(n))) {
639 hasBestNormLevel = hasBestProjLevel =
true;
643 for (
unsigned j = 0; j < n; ++j) {
654 sumRoughness += d * d;
661 if (pDlg.wasCanceled()) {
663 hasBestProjLevel =
true;
664 hasBestNormLevel =
true;
668 pDlg.setValue(pDlg.value() + 1);
669 QApplication::processEvents();
675 double meanPopulation =
676 sumPopulation /
static_cast<double>(probingCount);
677 double stdDevPopulation = sqrt(
fabs(
678 sumPopulation2 /
static_cast<double>(probingCount) -
679 meanPopulation * meanPopulation));
683 QString(
"[M3C2::auto] \tPopulation per cell: %1 "
685 .arg(meanPopulation, 0,
'f', 1)
686 .arg(stdDevPopulation, 0,
'f', 1));
688 if (!hasBestProjLevel) {
689 std::sort(populations.begin(),
690 populations.begin() + probingCount);
691 unsigned pop97 = populations[
static_cast<unsigned>(
692 floor(probingCount * 0.03))];
695 "cells above: %1 +/- %2")
701 QString(
"[M3C2::auto] \tThis scale seems "
702 "ok for projection!"));
704 hasBestProjLevel =
true;
709 if (!hasBestNormLevel) {
712 QString(
"[M3C2::auto] \tValid normals: %1/%2")
716 double meanRoughness = sqrt(
717 sumRoughness /
static_cast<double>(std::max<unsigned>(
722 .arg(meanRoughness));
725 0.99 *
static_cast<double>(
730 if (bestMeanRoughness < 0 ||
731 (meanRoughness < bestMeanRoughness &&
732 (!hasBestProjLevel ||
733 scale < 2.1 *
params.projScale))
740 bestMeanRoughness = meanRoughness;
745 unsigned char maxDim = 0;
746 if (
fabs(meanNormal.
x) <
fabs(meanNormal.
y)) maxDim = 1;
747 if (
fabs(meanNormal.
u[maxDim]) <
fabs(meanNormal.
z))
750 params.preferredDimension = maxDim;
754 hasBestNormLevel =
true;
757 QString(
"[M3C2::auto] \tThe previous scale "
758 "was better for normals!"));
763 if (level == 6 && (!hasBestNormLevel || !hasBestProjLevel)) {
766 if (bestMeanRoughness < 0)
params.normScale = scale;
767 if (!hasBestProjLevel)
params.projScale = scale;
768 hasBestProjLevel =
true;
769 hasBestNormLevel =
true;
772 QString(
"[M3C2::auto] We failed to converge for "
773 "the best projection level, so we will "
782 std::max<unsigned>(10, probingCount - (probingCount / 10));
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Jacobi eigen vectors/values decomposition.
static bool GetMinEigenValueAndVector(const SquareMatrix &eigenVectors, const EigenValues &eigenValues, Scalar &minEigenValue, Scalar minEigenVector[])
Returns the smallest eigenvalue and its associated eigenvector.
Array of compressed 3D normals (single index)
void normalize()
Sets vector norm to unity.
Type dot(const Vector3Tpl &v) const
Dot product.
Type norm2() const
Returns vector square norm.
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Type & getValue(size_t index)
bool isAllocated() const
Returns whether some memory has been allocated or not.
bool resizeSafe(size_t count, bool initNewElements=false, const Type *valueForNewElements=nullptr)
Resizes memory (no exception thrown)
void setValue(size_t index, const Type &value)
unsigned currentSize() const
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
virtual ccOctreeProxy * getOctreeProxy() const
Returns the associated octree proxy (if any)
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
ccHObject * getParent() const
Returns parent object.
static const CCVector3 & GetNormal(unsigned normIndex)
Static access to ccNormalVectors::getNormal.
static CompressedNormType GetNormIndex(const PointCoordinateType N[])
Returns the compressed index corresponding to a normal vector.
QSharedPointer< ccOctree > Shared
Shared pointer.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
A scalar field associated to display-related parameters.
double getDiagNormd() const
Returns diagonal length (double precision)
T getDiagNorm() const
Returns diagonal length.
The octree structure used throughout the library.
static const int MAX_OCTREE_LEVEL
Max octree subdivision level.
unsigned char findBestLevelForAGivenNeighbourhoodSizeExtraction(PointCoordinateType radius) const
int build(GenericProgressCallback *progressCb=nullptr)
Builds the structure.
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based point access.
virtual void stop()=0
Notifies the fact that the process has ended.
virtual void setInfo(const char *infoStr)=0
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle)=0
Notifies the algorithm title.
virtual bool textCanBeEdited() const
Returns whether the dialog title and info can be updated or not.
cloudViewer::SquareMatrixd computeCovarianceMatrix()
Computes the covariance matrix.
const PointCoordinateType * getLSPlane()
Returns best interpolating plane equation (Least-square)
unsigned size() const override
const CCVector3 * getPoint(unsigned index) const override
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
unsigned size() const override
Returns the number of points.
virtual void setPointIndex(unsigned localIndex, unsigned globalIndex)
Sets global index for a given element.
virtual bool resize(unsigned n)
Presets the size of the vector used to store point references.
virtual void clear(bool releaseMemory=false)
Clears the cloud.
void fill(ScalarType fillValue=0)
Fills the array with a particular value.
bool resizeSafe(std::size_t count, bool initNewElements=false, ScalarType valueForNewElements=0)
Resizes memory (no exception thrown)
unsigned currentSize() const
Main application interface (for plugins)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
Graphical progress indicator (thread-safe)
static void MakeNormalsHorizontal(NormsIndexesTableType &normsCodes)
Makes all normals horizontal.
static bool ComputeCorePointsNormals(cloudViewer::GenericIndexedCloud *corePoints, NormsIndexesTableType *corePointsNormals, ccGenericPointCloud *sourceCloud, const std::vector< PointCoordinateType > &sortedRadii, bool &invalidNormals, int maxThreadCount=0, ccScalarField *normalScale=nullptr, cloudViewer::GenericProgressCallback *progressCb=nullptr, cloudViewer::DgmOctree *inputOctree=nullptr)
Computes normals on core points only.
static bool UpdateNormalOrientationsWithCloud(cloudViewer::GenericIndexedCloud *normCloud, NormsIndexesTableType &normsCodes, cloudViewer::GenericIndexedCloud *orientationCloud, int maxThreadCount=0, cloudViewer::GenericProgressCallback *progressCb=nullptr)
__host__ __device__ float2 fabs(float2 v)
unsigned int CompressedNormType
Compressed normals type.
static double dist(double x1, double y1, double x2, double y2)
MiniVec< float, N > floor(const MiniVec< float, N > &a)
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
static bool distComp(const PointDescriptor &a, const PointDescriptor &b)
Comparison operator.