ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PointFeature.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "PointFeature.h"
9 
10 // Local
11 #include "q3DMASCTools.h"
12 
13 #if defined(_OPENMP)
14 #include <omp.h>
15 #endif
16 
17 // qPDALIO
18 #ifdef PLUGIN_IO_QPDAL
19 #include "../../../core/IO/qPDALIO/include/LASFields.h"
20 #else
21 #include "../../../core/IO/qLASIO/include/LasDetails.h"
22 enum LAS_FIELDS {
23  LAS_X = 0,
24  LAS_Y = 1,
25  LAS_Z = 2,
35  LAS_RED = 12,
36  LAS_GREEN = 13,
37  LAS_BLUE = 14,
38  LAS_TIME = 15,
39  LAS_EXTRA = 16,
40  // Sub fields
46  // Invald flag
47  LAS_INVALID = 255
48 };
49 
50 constexpr const char* LAS_FIELD_NAMES[22] = {
51  "X",
52  "Y",
53  "Z",
63  "Red",
64  "Green",
65  "Blue",
67  "extra",
68  "[Classif] Value",
69  "[Classif] Synthetic flag",
70  "[Classif] Key-point flag",
71  "[Classif] Withheld flag",
72  "[Classif] Overlap flag",
73 };
74 #endif
75 
76 // qCC_db
77 #include <ecvPointCloud.h>
78 #include <ecvScalarField.h>
79 
80 // CCLib
81 #include <WeibullDistribution.h>
82 
83 // system
84 #include <assert.h>
85 
86 // Qt
87 #include <QCoreApplication>
88 #include <QMutex>
89 
90 static const char* s_echoRatioSFName = "EchoRat";
91 static const char* s_NIRSFName = "NIR";
92 static const char* s_M3C2SFName = "M3C2 distance";
93 static const char* s_PCVSFName = "Illuminance (PCV)";
94 static const char* s_normDipSFName = "Norm dip";
95 static const char* s_normDipDirSFName = "Norm dip dir.";
96 
97 using namespace masc;
98 
99 bool PointFeature::checkValidity(QString corePointRole, QString& error) const {
100  if (!Feature::checkValidity(corePointRole, error)) {
101  return false;
102  }
103 
104  if (type == Invalid) {
105  assert(false);
106  error = "invalid feature type";
107  return false;
108  }
109 
110  assert(cloud1);
111 
112  if (!corePointRole.isEmpty() && !scaled() &&
113  cloud1Label != corePointRole) // in some cases, we don't know the role
114  // of the core points yet!
115  {
116  error = "Scale-less features can only be computed on the core points / "
117  "classified cloud";
118  return false;
119  }
120 
121  if (scaled() && stat == NO_STAT) {
122  error = "scaled point features need a STAT measure to be defined";
123  return false;
124  }
125 
126  if (op != NO_OPERATION && !cloud2) {
127  error = "math operations require two clouds";
128  return false;
129  }
130 
131  switch (type) {
135  error = QString("Cloud %0 has no '%1' scalar field")
136  .arg(cloud1->getName())
138  return false;
139  }
140  return true;
141  }
142  case PointFeature::X:
143  case PointFeature::Y:
144  case PointFeature::Z:
145  return true;
146  case PointFeature::NbRet: {
149  error = QString("Cloud %0 has no '%1' scalar field")
150  .arg(cloud1->getName())
152  return false;
153  }
154  return true;
155  }
156  case PointFeature::RetNb: {
159  error = QString("Cloud %0 has no '%1' scalar field")
160  .arg(cloud1->getName())
162  return false;
163  }
164  return true;
165  }
166  case PointFeature::EchoRat: {
169  error = QString("Cloud %0 has no '%1' scalar field")
170  .arg(cloud1->getName())
172  return false;
173  }
176  error = QString("Cloud %0 has no '%1' scalar field")
177  .arg(cloud1->getName())
179  return false;
180  }
181  return true;
182  }
183  case PointFeature::R:
184  case PointFeature::G:
185  case PointFeature::B:
186  if (!cloud1->hasColors()) {
187  error = QString("Cloud %0 has no RGB color")
188  .arg(cloud1->getName());
189  return false;
190  }
191  return true;
192  case PointFeature::NIR: {
194  error = QString("Cloud %0 has no '%1' scalar field")
195  .arg(cloud1->getName())
196  .arg(s_NIRSFName);
197  return false;
198  }
199  return true;
200  }
201  case PointFeature::Dip:
202  case PointFeature::DipDir: {
203  if (!cloud1->hasNormals()) {
204  error = QString("Cloud %0 has no normals")
205  .arg(cloud1->getName());
206  return false;
207  }
208  return true;
209  }
210  case PointFeature::M3C2: {
212  error = QString("Cloud %0 has no '%1' scalar field")
213  .arg(cloud1->getName())
214  .arg(s_M3C2SFName);
215  return false;
216  }
217  return true;
218  }
219  case PointFeature::PCV: {
221  error = QString("Cloud %0 has no '%1' scalar field")
222  .arg(cloud1->getName())
223  .arg(s_PCVSFName);
224  return false;
225  }
226  return true;
227  }
228  case PointFeature::SF:
229  if (sourceSFIndex >=
230  static_cast<int>(cloud1->getNumberOfScalarFields())) {
231  error = QString("Cloud %0 has no scalar field #%1")
232  .arg(cloud1->getName())
233  .arg(sourceSFIndex);
234  return false;
235  }
236  return true;
237  default:
238  break;
239  }
240 
241  return true;
242 }
243 
245  QString& error) {
246  if (!cloud) {
247  assert(false);
248  return IScalarFieldWrapper::Shared(nullptr);
249  }
250 
251  switch (type) {
254  cloud, LAS_FIELD_NAMES[LAS_INTENSITY], false);
255  if (!sf) {
256  error = "Cloud has no 'intensity' scalar field";
257  return nullptr;
258  }
260  }
261  case PointFeature::X:
264  case PointFeature::Y:
267  case PointFeature::Z:
270  case PointFeature::NbRet: {
272  cloud, LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS], false);
273  if (!sf) {
274  error = "Cloud has no 'number of returns' scalar field";
275  return nullptr;
276  }
278  }
279  case PointFeature::RetNb: {
281  cloud, LAS_FIELD_NAMES[LAS_RETURN_NUMBER], false);
282  if (!sf) {
283  error = "Cloud has no 'return number' scalar field";
284  return nullptr;
285  }
287  }
288  case PointFeature::EchoRat: {
289  // retrieve the two scalar fields 'p/q'
291  cloud, LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS], false);
292  if (!numberOfRetSF) {
293  error = "Can't compute the 'echo ratio' field: no 'Number of "
294  "Return' SF available";
295  return nullptr;
296  }
298  cloud, LAS_FIELD_NAMES[LAS_RETURN_NUMBER], false);
299  if (!retNumberSF) {
300  error = "Can't compute the 'echo ratio' field: no 'Return "
301  "number' SF available";
302  return nullptr;
303  }
304  if (retNumberSF->size() != numberOfRetSF->size() ||
305  retNumberSF->size() != cloud->size()) {
306  error = "Internal error (inconsistent scalar fields)";
307  return nullptr;
308  }
310  retNumberSF, numberOfRetSF, "EchoRat"));
311  }
312  case PointFeature::R:
315  case PointFeature::G:
318  case PointFeature::B:
321  case PointFeature::NIR: {
323  Tools::RetrieveSF(cloud, s_NIRSFName, false);
324  if (!sf) {
325  error = "Cloud has no 'NIR' scalar field";
326  return nullptr;
327  }
329  }
330  case PointFeature::Dip:
331  case PointFeature::DipDir: {
332  // we need normals to compute the dip and dip direction!
333  if (!cloud->hasNormals()) {
334  error = "Cloud has no normals: can't compute dip or dip dir. "
335  "angles";
336  return nullptr;
337  }
339  cloud, type == PointFeature::Dip
342  }
343  case PointFeature::M3C2: {
345  Tools::RetrieveSF(cloud, s_M3C2SFName, true);
346  if (!sf) {
347  error = "Cloud has no 'm3c2 distance' scalar field";
348  return nullptr;
349  }
351  }
352  case PointFeature::PCV: {
354  Tools::RetrieveSF(cloud, s_PCVSFName, true);
355  if (!sf) {
356  error = "Cloud has no 'PCV/Illuminance' scalar field";
357  return nullptr;
358  }
360  }
361  case PointFeature::SF:
362  if (sourceSFIndex < 0 ||
363  sourceSFIndex >=
364  static_cast<int>(cloud->getNumberOfScalarFields())) {
365  error = QString("Can't retrieve the specified SF: invalid "
366  "index (%1)")
367  .arg(sourceSFIndex);
368  return nullptr;
369  }
371  cloud->getScalarField(sourceSFIndex)));
372  default:
373  break;
374  }
375 
376  error = "Unhandled feature type";
377  return nullptr;
378 }
379 
381  const CorePoints& corePoints,
382  const IScalarFieldWrapper& field1,
384  ccPointCloud& cloud2,
385  const IScalarFieldWrapper& field2,
387  QString& error,
388  cloudViewer::GenericProgressCallback* progressCb = nullptr) {
389  if (op == masc::Feature::NO_OPERATION || !outSF ||
390  outSF->size() != corePoints.size()) {
391  // invalid input parameters
392  assert(false);
393  error = "invalid input parameters";
394  return false;
395  }
396 
397  ccOctree::Shared octree = cloud2.getOctree();
398  if (!octree) {
399  octree = cloud2.computeOctree(progressCb);
400  if (!octree) {
401  error = "failed to compute octree on cloud " + cloud2.getName();
402  return false;
403  }
404  }
405 
406  // now extract the neighborhoods
407  unsigned char octreeLevel =
409  CVLog::Print(QString("[Initial octree level] level = %1").arg(octreeLevel));
410 
411  unsigned pointCount = corePoints.size();
412  QString logMessage =
413  QString("Extracting %1 core points nearest neighbors in cloud %2")
414  .arg(pointCount)
415  .arg(cloud2.getName());
416  if (progressCb) {
417  progressCb->setMethodTitle("Compute math operation");
418  progressCb->setInfo(qPrintable(logMessage));
419  }
420  CVLog::Print(logMessage);
421  cloudViewer::NormalizedProgress nProgress(progressCb, pointCount);
422 
423  double meanNeighborhoodSize = 0;
424  int tenth = pointCount / 10;
425  error.clear();
426  bool cancelled = false;
427 #ifndef _DEBUG
428 #if defined(_OPENMP)
429 #pragma omp parallel for num_threads(std::max(1, omp_get_max_threads() - 2))
430 #endif
431 #endif
432  for (int i = 0; i < static_cast<int>(pointCount); ++i) {
433  if (!cancelled) {
434  const CCVector3* P = corePoints.cloud->getPoint(i);
435  cloudViewer::ReferenceCloud Yk(&cloud2);
436  double maxSquareDist = 0;
437 
438  ScalarType s = NAN_VALUE;
439 
440  int neighborhoodSize = 0;
441  if (octree->findPointNeighbourhood(P, &Yk, 1, octreeLevel,
442  maxSquareDist, 0.0,
443  &neighborhoodSize) >= 1) {
444  double s1 = field1.pointValue(corePoints.originIndex(i));
445  double s2 = field2.pointValue(Yk.getPointGlobalIndex(0));
446  s = masc::Feature::PerformMathOp(s1, s2, op);
447  }
448 
449  outSF->setValue(i, s);
450 
451  if (i && (i % tenth) == 0) {
452  double density = meanNeighborhoodSize / tenth;
453  if (density < 1.1) {
454  if (octreeLevel + 1 <
456  ++octreeLevel;
457  } else
458  while (density > 2.9) {
459  if (octreeLevel <= 5) break;
460  --octreeLevel;
461  density /= 2.0;
462  }
463  CVLog::Print(QString("[Adaptative octree level] Mean "
464  "neighborhood size: %1 --> new level = %2")
465  .arg(meanNeighborhoodSize / tenth)
466  .arg(octreeLevel));
467  meanNeighborhoodSize = 0;
468  } else {
469  meanNeighborhoodSize += neighborhoodSize;
470  }
471 
472  if (progressCb) {
473  cancelled = !nProgress.oneStep();
474  if (cancelled) {
475  // process cancelled by the user
476  error = "[Point feature] Process cancelled";
477  }
478  }
479  }
480  }
481 
482  outSF->computeMinAndMax();
483 
484  if (progressCb) {
485  progressCb->stop();
486  }
487 
488  return error.isEmpty();
489 }
490 
492  const CorePoints& corePoints,
493  QString& error,
494  cloudViewer::GenericProgressCallback* progressCb /*=nullptr*/,
495  SFCollector* generatedScalarFields /*=nullptr*/) {
496  if (!cloud1 || !corePoints.cloud) {
497  // invalid input
498  assert(false);
499  error = "internal error (no input core points)";
500  return false;
501  }
502 
503  // look for the source field
504  assert(!field1);
506  if (!field1) {
507  // error should be up to date
508  return false;
509  }
510 
511  assert(!field2);
512  if (cloud2) {
513  // no need to compute the second scalar field if no MATH operation has
514  // to be performed?!
515  if (op != Feature::NO_OPERATION) {
517  if (!field2) {
518  // error should be up to date
519  return false;
520  }
521  } else {
522  assert(false);
523  error = "Feature has a second cloud associated but no MATH "
524  "operation is defined";
525  return false;
526  }
527  }
528 
529  bool isScaled = scaled();
530 
531  // build the final SF name
532  QString resultSF1Name = field1->getName();
533  if (cloud2 || corePoints.role != cloud1Label) {
534  resultSF1Name += "_" + cloud1Label;
535  }
536 
537  if (isScaled) {
538  // shall we extract a statistical measure? (mandatory for scaled
539  // feature)
540  if (stat == Feature::NO_STAT) {
541  assert(false);
542  error = "Scaled features (SCx) must have an associated STAT "
543  "measure";
544  return false;
545  }
546  resultSF1Name += QString("_") + Feature::StatToString(stat);
547  } else // not scaled
548  {
549  if (cloud1 != corePoints.cloud && cloud1 != corePoints.origin) {
550  assert(false);
551  error = "Scale-less features (SC0) can only be defined on the core "
552  "points (origin) cloud";
553  return false;
554  }
555  }
556 
557  if (field2 && op != Feature::NO_OPERATION) {
558  // include the math operation as well if necessary!
559  resultSF1Name += "_" + Feature::OpToString(op) + "_" +
560  field2->getName() + "_" + cloud2Label;
561  if (isScaled) {
562  assert(stat != Feature::NO_STAT);
563  resultSF1Name += QString("_") + Feature::StatToString(stat);
564  }
565  }
566 
567  if (isScaled) {
568  resultSF1Name += "@" + QString::number(scale);
569 
570  // prepare the corresponding scalar field
572  CheckSFExistence(corePoints.cloud, qPrintable(resultSF1Name));
573  if (sf1WasAlreadyExisting) {
574  // if the SF exists, it is not added to generatedScalarFields
575  statSF1 =
576  PrepareSF(corePoints.cloud, qPrintable(resultSF1Name),
577  generatedScalarFields, SFCollector::ALWAYS_KEEP);
578  if (generatedScalarFields->scalarFields.contains(
579  statSF1)) // i.e. the SF is existing but was not
580  // present at the startup of the plugin
581  generatedScalarFields->setBehavior(statSF1,
583  } else
584  statSF1 = PrepareSF(corePoints.cloud, qPrintable(resultSF1Name),
585  generatedScalarFields, SFCollector::CAN_REMOVE);
586  if (!statSF1) {
587  error = QString("Failed to prepare scalar field for field '%1' @ "
588  "scale %2")
589  .arg(field1->getName())
590  .arg(scale);
591  return false;
592  }
593  source.name = statSF1->getName();
594 
595  if (field2 && op != Feature::NO_OPERATION &&
596  !sf1WasAlreadyExisting) // nothing to do if statSF1 was already
597  // there
598  {
599  QString resultSF2Name =
600  field2->getName() + QString("_") + cloud2Label + "_" +
601  Feature::StatToString(stat) + "@" + QString::number(scale);
602  // keepStatSF2 =
603  // (corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName2))
604  // >= 0); //we remember that the scalar field was already existing!
605 
606  assert(!statSF2);
608  qPrintable(resultSF2Name));
610  statSF2 = PrepareSF(corePoints.cloud, qPrintable(resultSF2Name),
611  generatedScalarFields,
613  else
614  statSF2 = PrepareSF(corePoints.cloud, qPrintable(resultSF2Name),
615  generatedScalarFields,
617  if (!statSF2) {
618  error = QString("Failed to prepare scalar field for field '%1' "
619  "@ scale %2")
620  .arg(field2->getName())
621  .arg(scale);
622  return false;
623  }
624  }
625 
626  return true;
627  } else // non scaled feature
628  {
629  assert(cloud1 == corePoints.cloud || cloud1 == corePoints.origin);
630 
631  // retrieve/create a SF to host the result
632  int sfIdx = corePoints.cloud->getScalarFieldIndexByName(
633  qPrintable(resultSF1Name));
634 
635  cloudViewer::ScalarField* resultSF = nullptr;
636  if (sfIdx >= 0) {
637  // reuse the existing field
638  resultSF = corePoints.cloud->getScalarField(sfIdx);
639  } else {
640  // copy the SF1 field
641  resultSF = new ccScalarField(qPrintable(resultSF1Name));
642  if (!resultSF->resizeSafe(corePoints.cloud->size())) {
643  error = "Not enough memory";
644  resultSF->release();
645  return false;
646  }
647 
648  if (op == NO_OPERATION) {
649  // simply copy the values
650  for (unsigned i = 0; i < corePoints.size(); ++i) {
651  resultSF->setValue(
652  i, field1->pointValue(corePoints.originIndex(i)));
653  }
654  resultSF->computeMinAndMax();
655  } else if (field2) {
657  corePoints, *field1, resultSF, *cloud2, *field2, op,
658  error, progressCb)) {
659  error = "Failed to perform the MATH operation (" + error +
660  ")";
661  resultSF->release();
662  return false;
663  }
664  }
665 
666  int newSFIdx = corePoints.cloud->addScalarField(
667  static_cast<ccScalarField*>(resultSF));
668  if (generatedScalarFields) {
669  // track the generated scalar-field
670  generatedScalarFields->push(corePoints.cloud, resultSF,
672  }
673 
674  corePoints.cloud->setCurrentDisplayedScalarField(newSFIdx);
675  }
676 
677  source.name = resultSF->getName();
678 
679  return true;
680  }
681 }
682 
684  const cloudViewer::DgmOctree::NeighboursSet& pointsInNeighbourhood,
685  const IScalarFieldWrapper::Shared& sourceField,
686  double& outputValue) const {
687  outputValue = std::numeric_limits<double>::quiet_NaN();
688 
689  if (!sourceField || stat == Feature::NO_STAT) {
690  // invalid input parameters
691  assert(false);
692  return false;
693  }
694 
695  size_t kNN = pointsInNeighbourhood.size();
696  if (kNN == 0) {
697  assert(false);
698  return false;
699  }
700 
701  // specific case
702  if (stat == Feature::RANGE) {
703  double minValue = 0;
704  double maxValue = 0;
705 
706  for (size_t k = 0; k < kNN; ++k) {
707  unsigned index = pointsInNeighbourhood[k].pointIndex;
708  double v = sourceField->pointValue(index);
709 
710  // track min and max values
711  if (k != 0) {
712  if (v < minValue)
713  minValue = v;
714  else if (v > maxValue)
715  maxValue = v;
716  } else {
717  minValue = maxValue = v;
718  }
719  }
720 
721  outputValue = maxValue - minValue;
722  return true;
723  } else {
724  bool withSums = (stat == Feature::MEAN || stat == Feature::STD);
725  bool storeValues = (stat == Feature::MEDIAN || stat == Feature::MODE ||
726  stat == Feature::SKEW);
727  double sum = 0.0;
728  double sum2 = 0.0;
729 
731  if (storeValues) {
732  try {
733  values.resize(kNN);
734  } catch (const std::bad_alloc&) {
735  CVLog::Warning("Not enough memory");
736  return false;
737  }
738  }
739 
740  for (unsigned k = 0; k < kNN; ++k) {
741  unsigned index = pointsInNeighbourhood[k].pointIndex;
742  double v = sourceField->pointValue(index);
743 
744  if (withSums) {
745  // compute average and std. dev.
746  sum += v;
747  sum2 += v * v;
748  }
749 
750  if (storeValues) {
751  values[k] = static_cast<ScalarType>(v);
752  }
753  }
754 
755  switch (stat) {
756  case Feature::MEAN: {
757  outputValue = sum / kNN;
758  } break;
759 
760  case Feature::MODE: {
762  if (w.computeParameters(values)) outputValue = w.computeMode();
763  } break;
764 
765  case Feature::MEDIAN: {
766  size_t medianIndex = values.size() / 2;
767  std::nth_element(values.begin(), values.begin() + medianIndex,
768  values.end());
769  outputValue = values[medianIndex];
770  } break;
771 
772  case Feature::STD: {
773  outputValue = sqrt(std::abs(sum2 * kNN - sum * sum)) / kNN;
774  } break;
775 
776  case Feature::RANGE: {
777  // we can't be here
778  assert(false);
779  }
780  return false;
781 
782  case Feature::SKEW: {
784  if (w.computeParameters(values))
785  outputValue = w.computeSkewness();
786  } break;
787 
788  default: {
789  CVLog::Warning("Unhandled STAT measure");
790  assert(false);
791  }
792  return false;
793  }
794  }
795 
796  return true;
797 }
798 
800  if (!scaled()) {
801  // nothing to do
802  return true;
803  }
804 
805  if (!corePoints.cloud) {
806  // invalid input
807  assert(false);
808  error = "internal error (no input core points)";
809  return false;
810  }
811 
812  bool success = true;
813 
814  if (statSF1) {
816 
817  // update display
818  // if (corePoints.cloud->getDisplay())
819  {
820  int sfIndex1 = corePoints.cloud->getScalarFieldIndexByName(
821  statSF1->getName());
822  corePoints.cloud->setCurrentDisplayedScalarField(sfIndex1);
823  // corePoints.cloud->getDisplay()->redraw();
824  // QCoreApplication::processEvents();
825  }
826  }
827 
828  if (statSF2 && !sf1WasAlreadyExisting) {
829  // now perform the math operation
830  if (op != Feature::NO_OPERATION) {
831  if (!PerformMathOp(statSF1, statSF2, op)) {
832  error = "Failed to perform the MATH operation";
833  success = false;
834  }
835  }
836  // statSF2->computeMinAndMax();
837 
838  // DGM: we don't delete it now! As it could be used by other features!
839  // if (!keepStatSF2)
840  //{
841  // int sfIndex2 =
842  // corePoints.cloud->getScalarFieldIndexByName(statSF2->getName());
843  // if (sfIndex2 >= 0)
844  // {
845  // corePoints.cloud->deleteScalarField(sfIndex2);
846  // }
847  // else
848  // {
849  // assert(false);
850  // statSF2->release();
851  // }
852  // statSF2 = nullptr;
853  // }
854  }
855 
856  return success;
857 }
858 
859 QString PointFeature::toString() const {
860  // default keyword otherwise
861  QString description = ToString(type);
862 
863  // special case for the 'SF' type
864  if (type == SF) {
865  //'SF' + sf index
866  description += QString::number(sourceSFIndex);
867  }
868 
869  if (scaled()) {
870  description += QString("_SC%1_%2").arg(scale).arg(StatToString(stat));
871  } else {
872  description += "_SC0";
873  }
874 
875  description += "_" + cloud1Label;
876 
877  if (cloud2 && !cloud2Label.isEmpty()) {
878  description += "_" + cloud2Label;
879 
880  if (op != NO_OPERATION) {
881  description += "_" + OpToString(op);
882  }
883  }
884 
885  // Point features always have a scale equal to 0 by definition
886  return description;
887 }
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
Definition: CVConst.h:76
LAS_FIELDS
Definition: LASFields.h:35
constexpr const char * LAS_FIELD_NAMES[22]
static const char * s_echoRatioSFName
@ LAS_INVALID
@ LAS_CLASSIF_KEYPOINT
@ LAS_Z
@ LAS_SCAN_DIRECTION
@ LAS_POINT_SOURCE_ID
@ LAS_CLASSIF_OVERLAP
@ LAS_RETURN_NUMBER
@ LAS_Y
@ LAS_CLASSIF_VALUE
@ LAS_FLIGHT_LINE_EDGE
@ LAS_TIME
@ LAS_NUMBER_OF_RETURNS
@ LAS_SCAN_ANGLE_RANK
@ LAS_EXTRA
@ LAS_CLASSIF_WITHHELD
@ LAS_BLUE
@ LAS_X
@ LAS_GREEN
@ LAS_RED
@ LAS_CLASSIF_SYNTHETIC
@ LAS_USER_DATA
@ LAS_CLASSIFICATION
@ LAS_INTENSITY
static const char * s_normDipSFName
static const char * s_NIRSFName
static const char * s_PCVSFName
static const char * s_normDipDirSFName
static const char * s_M3C2SFName
static bool ComputeMathOpWithNearestNeighbor(const CorePoints &corePoints, const IScalarFieldWrapper &field1, cloudViewer::ScalarField *outSF, ccPointCloud &cloud2, const IScalarFieldWrapper &field2, masc::Feature::Operation op, QString &error, cloudViewer::GenericProgressCallback *progressCb=nullptr)
virtual void release()
Decrease counter and deletes object when 0.
Definition: CVShareable.cpp:35
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
QSharedPointer< IScalarFieldWrapper > Shared
virtual double pointValue(unsigned index) const =0
SF collector.
void push(ccPointCloud *cloud, cloudViewer::ScalarField *sf, Behavior behavior)
bool setBehavior(cloudViewer::ScalarField *sf, Behavior behavior)
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
QSharedPointer< ccOctree > Shared
Shared pointer.
Definition: ecvOctree.h:32
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool hasColors() const override
Returns whether colors are enabled or not.
A scalar field associated to display-related parameters.
unsigned findPointNeighbourhood(const CCVector3 *_queryPoint, ReferenceCloud *Yk, unsigned maxNumberOfNeighbors, unsigned char level, double &maxSquareDist, double maxSearchDist=0, int *finalNeighbourhoodSize=nullptr) const
Finds the nearest neighbours around a query point.
Definition: DgmOctree.cpp:721
static const int MAX_OCTREE_LEVEL
Max octree subdivision level.
Definition: DgmOctree.h:67
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
Definition: DgmOctree.h:133
unsigned char findBestLevelForAGivenPopulationPerCell(unsigned indicativeNumberOfPointsPerCell) const
Definition: DgmOctree.cpp:2737
virtual unsigned size() const =0
Returns the number of points.
std::vector< ScalarType > ScalarContainer
Scalar values container.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
bool oneStep()
Increments total progress value of a single unit.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
unsigned size() const override
Definition: PointCloudTpl.h:38
A very simple point cloud (no point duplication)
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
virtual void computeMinAndMax()
Determines the min and max values.
Definition: ScalarField.h:123
void setValue(std::size_t index, ScalarType value)
Definition: ScalarField.h:96
const char * getName() const
Returns scalar field name.
Definition: ScalarField.h:43
bool resizeSafe(std::size_t count, bool initNewElements=false, ScalarType valueForNewElements=0)
Resizes memory (no exception thrown)
Definition: ScalarField.cpp:81
The Weibull statistical parametric distribution.
bool computeParameters(const ScalarContainer &values) override
Computes the distribution parameters from a set of values.
double computeSkewness() const
Returns the distribution 'skewness'.
double computeMode() const
Returns the distribution 'mode'.
static cloudViewer::ScalarField * RetrieveSF(const ccPointCloud *cloud, const QString &sfName, bool caseSensitive=true)
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
static void error(char *msg)
Definition: lsd.c:159
constexpr const char * ScanDirectionFlag
Definition: LasDetails.h:62
constexpr const char * EdgeOfFlightLine
Definition: LasDetails.h:63
constexpr const char * NumberOfReturns
Definition: LasDetails.h:61
constexpr const char * UserData
Definition: LasDetails.h:69
constexpr const char * Classification
Definition: LasDetails.h:64
constexpr const char * ReturnNumber
Definition: LasDetails.h:60
constexpr const char * ScanAngleRank
Definition: LasDetails.h:68
constexpr const char * GpsTime
Definition: LasDetails.h:71
constexpr const char * Intensity
Definition: LasDetails.h:59
constexpr const char * PointSourceId
Definition: LasDetails.h:70
3DMASC classifier
cloudViewer::NormalizedProgress * nProgress
cloudViewer::DgmOctree * octree
cloudViewer::GenericIndexedCloud * corePoints
unsigned char octreeLevel
Core points descriptor.
Definition: CorePoints.h:39
static bool CheckSFExistence(ccPointCloud *cloud, const char *resultSFName)
virtual bool checkValidity(QString corePointRole, QString &error) const
Checks the feature definition validity.
ccPointCloud * cloud1
bool scaled() const
Returns whether the feature has an associated scale.
static QString StatToString(Stat stat)
ccPointCloud * cloud2
static ScalarType PerformMathOp(double s1, double s2, Operation op)
Performs a mathematical operation between two scalars.
static QString OpToString(Operation op)
double scale
Scale (diameter)
static cloudViewer::ScalarField * PrepareSF(ccPointCloud *cloud, const char *resultSFName, SFCollector *generatedScalarFields, SFCollector::Behavior behavior)
bool computeStat(const cloudViewer::DgmOctree::NeighboursSet &pointsInNeighbourhood, const IScalarFieldWrapper::Shared &sourceField, double &outputValue) const
IScalarFieldWrapper::Shared field1
First cloud 'source' field.
Definition: PointFeature.h:220
virtual bool checkValidity(QString corePointRole, QString &error) const override
Checks the feature definition validity.
static QString ToString(PointFeatureType type)
Definition: PointFeature.h:60
PointFeatureType type
Point feature type.
Definition: PointFeature.h:214
cloudViewer::ScalarField * statSF1
For scaled features.
Definition: PointFeature.h:226
cloudViewer::ScalarField * statSF2
Definition: PointFeature.h:227
virtual bool finish(const CorePoints &corePoints, QString &error) override
Finishes the feature preparation (update the scalar field, etc.)
virtual QString toString() const override
Returns the formatted description.
virtual bool prepare(const CorePoints &corePoints, QString &error, cloudViewer::GenericProgressCallback *progressCb=nullptr, SFCollector *generatedScalarFields=nullptr) override
Prepares the feature (compute the scalar field, etc.)
IScalarFieldWrapper::Shared retrieveField(ccPointCloud *cloud, QString &error)
Returns the 'source' field from a given cloud.
IScalarFieldWrapper::Shared field2
Second cloud 'source' field (if any)
Definition: PointFeature.h:223
int sourceSFIndex
Source scalar field index (if the feature source is 'ScalarField')
Definition: PointFeature.h:217