24 #include "../../../core/IO/qPDALIO/include/LASFields.h"
27 #include <QCoreApplication>
45 const QString corePointsRole,
51 QString yamlFilename = fi.baseName() +
".yaml";
52 QString yamlAbsoluteFilename =
53 fi.absoluteDir().absoluteFilePath(yamlFilename);
54 if (!classifier.
toFile(yamlAbsoluteFilename, parent)) {
60 if (!file.open(QFile::Text | QFile::WriteOnly)) {
62 QString(
"Can't open file '%1' for writing").arg(
filename));
66 QTextStream stream(&file);
72 QList<QString> cloudLabels;
74 if (f->cloud1 && !cloudLabels.contains(f->cloud1Label))
75 cloudLabels.push_back(f->cloud1Label);
76 if (f->cloud2 && !cloudLabels.contains(f->cloud2Label))
77 cloudLabels.push_back(f->cloud2Label);
79 if (!corePointsRole.isEmpty() && !cloudLabels.contains(corePointsRole)) {
80 cloudLabels.push_back(corePointsRole);
84 for (
const QString& label : cloudLabels) {
88 if (!corePointsRole.isEmpty()) {
102 QList<QString>& labels,
103 QString& corePointsLabel,
104 bool& filenamesSpecified,
105 QMap<QString, QString>& rolesAndNames) {
107 corePointsLabel.clear();
109 rolesAndNames.clear();
112 if (!file.open(QFile::Text | QFile::ReadOnly)) {
117 QTextStream stream(&file);
118 int filenameCount = 0;
119 for (
int lineNumber = 0;; ++lineNumber) {
120 QString line = stream.readLine();
127 line = line.toUpper();
128 if (line.startsWith(
"CLOUD:")) {
129 QString command = line.mid(6).trimmed();
130 QStringList tokens = command.split(
'=');
131 if (tokens.size() == 0) {
133 "Malformed file: expecting some tokens after 'cloud:' "
135 QString::number(lineNumber));
139 QString label = tokens.front();
140 if (labels.contains(label)) {
142 "defined/used on line #%2")
147 labels.push_back(label);
148 rolesAndNames[label] = tokens.back();
150 if (tokens.size() > 1) ++filenameCount;
151 }
else if (line.startsWith(
"CORE_POINTS:")) {
152 if (!corePointsLabel.isEmpty()) {
156 QString command = line.mid(12);
157 QStringList tokens = command.split(
'_');
158 if (tokens.empty()) {
160 "Malformed file: expecting tokens after 'core_points:' "
162 QString::number(lineNumber));
165 corePointsLabel = tokens[0].trimmed();
169 filenamesSpecified = (filenameCount > 0 && filenameCount == labels.size());
176 if (!feature)
return false;
179 for (
const auto& feat : rawFeatures) {
180 if (feat->toString() == feature->toString()) {
188 QString corePointsRole,
191 std::vector<Feature::Shared>& rawFeatures,
192 std::vector<double>& scales) {
193 QStringList tokens = command.split(
'_');
194 if (tokens.empty()) {
196 "Malformed file: expecting at least one token after 'feature:' "
198 QString::number(lineNumber));
205 QString typeStr = tokens[0].trimmed().toUpper();
207 for (
int iteration = 0; iteration < 1;
218 QString sfIndexStr = typeStr.mid(2);
220 int sfIndex = sfIndexStr.toInt(&ok);
223 QString(
"Malformed file: expecting a valid "
224 "integer value after 'SF' on line #%1")
232 feature.reset(pointFeature);
237 neighborhoodFeatureType =
247 contextBasedFeatureType =
254 if (featureStr.length() < typeStr.length()) {
256 kNN = typeStr.mid(featureStr.length()).toInt(&ok);
257 if (!ok || kNN <= 0) {
259 "valid and positive number "
260 "after '%1' on line #%2")
282 "'%1' after 'feature:' on line #%2")
292 bool useAllScales =
false;
294 QString scaleStr = tokens[1].toUpper();
295 if (!scaleStr.startsWith(
"SC")) {
297 "(expecting the scale descriptor 'SC...' on "
304 if (scaleStr ==
"SC0") {
306 }
else if (scaleStr ==
"SCX") {
312 feature->scale = scaleStr.mid(2).toDouble(&ok);
315 "number after 'SC:' on line #%1")
324 bool statDefined =
false;
325 bool mathDefined =
false;
326 bool contextBasedFeatureDeprecatedSyntax =
false;
327 for (
int i = 2; i < tokens.size(); ++i) {
328 QString token = tokens[i].trimmed().toUpper();
332 if (token ==
"MEAN") {
335 }
else if (token ==
"MODE") {
338 }
else if (token ==
"MEDIAN") {
341 }
else if (token ==
"STD") {
344 }
else if (token ==
"RANGE") {
347 }
else if (token ==
"SKEW") {
358 if (cloudCount < 2) {
359 bool cloudNameMatches =
false;
360 for (QMap<QString, ccPointCloud*>::const_iterator it =
362 it != clouds.end(); ++it) {
363 QString key = it.key().toUpper();
365 if (cloudCount == 0) {
366 feature->cloud1 = it.value();
367 feature->cloud1Label = key;
370 feature->getType() ==
375 if (i + 1 < tokens.size()) {
377 int classLabel = tokens[i + 1].toInt(&ok);
379 contextBasedFeatureDeprecatedSyntax =
383 qSharedPointerCast<ContextBasedFeature>(
385 ->ctxClassLabel = classLabel;
390 QString(
"Malformed context based "
391 "features at line %1")
397 }
else if (cloudCount == 1) {
398 if (contextBasedFeatureDeprecatedSyntax) {
399 feature->cloud1 = it.value();
400 feature->cloud1Label = key;
402 feature->cloud2 = it.value();
403 feature->cloud2Label = key;
407 feature->getType() ==
412 if (i + 1 < tokens.size()) {
414 int classLabel = tokens[i + 1].toInt(&ok);
417 QString(
"ContextBasedFeature: "
418 "expecting a class number "
419 "after the context cloud "
426 "ContextBasedFeature: you are "
427 "using the DEPRECATED syntax, the "
428 "feature should contain only one "
429 "cloud, as in DZ1_SC0_CTX_10)");
430 qSharedPointerCast<ContextBasedFeature>(
432 ->ctxClassLabel = classLabel;
437 QString(
"Malformed context based "
438 "features at line %1")
448 cloudNameMatches =
true;
453 if (cloudNameMatches) {
460 if (token ==
"MINUS") {
463 }
else if (token ==
"PLUS") {
466 }
else if (token ==
"DIVIDE") {
469 }
else if (token ==
"MULTIPLY") {
499 CVLog::Warning(QString(
"Malformed file: unrecognized or unexpected "
500 "token '%1' on line #%2")
508 if (scales.empty()) {
510 "Malformed file: 'SCx' token used while no scale is "
512 QString(
" (line %1)").arg(lineNumber));
515 feature->scale = scales.front();
522 QString errorMessage;
523 if (!feature->checkValidity(corePointsRole, errorMessage)) {
525 QString(
" (line %1)").arg(lineNumber));
533 CVLog::Warning(
"[3DMASC] duplicated feature " + feature->toString() +
534 ", check your parameter file");
538 rawFeatures.push_back(feature);
542 for (
size_t i = 1; i < scales.size(); ++i) {
545 newFeature->scale = scales.at(i);
549 assert(newFeature->checkValidity(corePointsRole, errorMessage));
556 newFeature->toString() +
557 ", check your parameter file");
561 rawFeatures.push_back(newFeature);
570 std::vector<double>& scales,
572 assert(scales.empty());
574 QStringList tokens = command.split(
';');
575 if (tokens.empty()) {
577 "Malformed file: expecting at least one token after 'scales:' "
579 QString::number(lineNumber));
583 for (
const QString& token : tokens) {
584 if (token.contains(
':')) {
586 QStringList subTokens = token.trimmed().split(
':');
587 if (subTokens.size() != 3) {
589 "a range of scales (%1)")
593 bool ok[3] = {
true,
true,
true};
594 double start = subTokens[0].trimmed().toDouble(ok);
595 double step = subTokens[1].toDouble(ok + 1);
596 double stop = subTokens[2].toDouble(ok + 2);
597 if (!ok[0] || !ok[1] || !ok[2]) {
599 "scales range (%1) on line #%2")
604 if (stop < start || step <= 1.0 - 6) {
612 for (
double v = start; v <= stop + 1.0e-6; v += step) {
617 double v = token.trimmed().toDouble(&ok);
629 scales.shrink_to_fit();
637 QStringList tokens = command.split(
'_');
638 if (tokens.empty()) {
640 "Malformed file: expecting tokens after 'core_points:' on line "
642 QString::number(lineNumber));
645 QString pcName = tokens[0].trimmed();
646 if (!clouds.contains(pcName)) {
648 QString(
"Malformed file: unknown cloud '%1' on line #%2 (make "
649 "sure it is declared before the core points)")
658 if (tokens.size() > 1) {
659 if (tokens[1].toUpper() ==
"SS") {
660 if (tokens.size() < 3) {
662 "Malformed file: missing token after 'SS' on line #" +
663 QString::number(lineNumber));
666 QString options = tokens[2];
667 if (options.startsWith(
'R')) {
669 }
else if (options.startsWith(
'S')) {
673 "Malformed file: unknown option after 'SS' on line #" +
674 QString::number(lineNumber));
680 corePoints.selectionParam = options.mid(1).toDouble(&ok);
683 "Malformed file: expecting a number after 'SS_X' on "
685 QString::number(lineNumber));
697 const QDir& defaultDir,
700 QStringList tokens = command.split(
'=');
701 if (tokens.size() != 2) {
703 "Malformed file: expecting 2 tokens after 'cloud:' on line #" +
704 QString::number(lineNumber));
708 QString pcName = tokens[0].trimmed();
709 QString pcFilename = defaultDir.absoluteFilePath(tokens[1].trimmed());
717 if (
object)
delete object;
723 if (cloudsInFile.empty()) {
727 }
else if (cloudsInFile.size() > 1) {
729 "File contains more than one cloud, only the first one "
733 for (
size_t i = 1; i < cloudsInFile.size(); ++i) {
734 delete cloudsInFile[i];
739 clouds.insert(pcName, pc);
748 bool cloudsAreProvided,
749 std::vector<Feature::Shared>*
751 std::vector<double>* rawScales ,
763 if (!file.open(QFile::Text | QFile::ReadOnly)) {
770 bool loadCoordinatesTransEnabled =
false;
772 if (!cloudsAreProvided) {
783 assert(!rawFeatures || rawFeatures->empty());
784 std::vector<double> scales;
786 QTextStream stream(&file);
787 bool badFeatures =
false;
788 for (
int lineNumber = 1;; ++lineNumber) {
789 if (stream.atEnd())
break;
790 QString line = stream.readLine();
791 if (line.isEmpty()) {
795 if (line.startsWith(
"#")) {
801 int commentIndex = line.indexOf(
'#');
802 if (commentIndex >= 0) line = line.left(commentIndex);
804 QString upperLine = line.toUpper();
805 if (upperLine.startsWith(
"CLASSIFIER:"))
813 "Malformed file: can't declare the classifier file "
815 QString::number(lineNumber) +
")");
818 QString yamlFilename = line.mid(11).trimmed();
819 QString yamlAbsoluteFilename =
820 fi.absoluteDir().absoluteFilePath(yamlFilename);
821 if (!classifier->
fromFile(yamlAbsoluteFilename, parent)) {
823 yamlAbsoluteFilename);
827 yamlAbsoluteFilename);
828 }
else if (upperLine.startsWith(
"CLOUD:"))
830 if (!clouds || cloudsAreProvided) {
834 QString command = line.mid(6);
835 if (!
ReadCloud(command, *clouds, fi.absoluteDir(), lineNumber,
839 }
else if (upperLine.startsWith(
"TEST:"))
841 if (!clouds || cloudsAreProvided) {
845 QString command = line.mid(5);
846 if (!
ReadCloud(
"TEST=" + command, *clouds, fi.absoluteDir(),
854 }
else if (upperLine.startsWith(
"CORE_POINTS:"))
862 "Core points already defined (those declared on "
864 QString::number(lineNumber) +
" will be ignored)");
866 QString command = line.mid(12);
872 }
else if (upperLine.startsWith(
"SCALES:"))
874 if (!scales.empty()) {
876 "Malformed file: scales defined twice (line #" +
877 QString::number(lineNumber) +
")");
881 QString command = line.mid(7);
882 if (!
ReadScales(command, scales, lineNumber)) {
886 for (
auto scale : scales) rawScales->push_back(scale);
888 }
else if (upperLine.startsWith(
"FEATURE:"))
890 QString command = line.mid(8);
892 if (rawFeatures && clouds) {
896 lineNumber, *clouds, *rawFeatures, scales)) {
903 }
else if (upperLine.startsWith(
"PARAM_"))
908 QStringList tokens = upperLine.split(
"=");
909 if (tokens.size() != 2) {
911 QString(
"Line #%1: malformed parameter command "
912 "(expecting param_XXX=Y)")
917 if (tokens[0] ==
"PARAM_MAX_DEPTH") {
918 parameters->
rt.
maxDepth = tokens[1].toInt(&ok);
919 }
else if (tokens[0] ==
"PARAM_MAX_TREE_COUNT") {
921 }
else if (tokens[0] ==
"PARAM_ACTIVE_VAR_COUNT") {
923 }
else if (tokens[0] ==
"PARAM_MIN_SAMPLE_COUNT") {
925 }
else if (tokens[0] ==
"PARAM_TEST_DATA_RATIO") {
929 QString(
"Line #%1: unrecognized parameter: ")
942 QString(
"Line #%1: unrecognized token/command: ")
944 (line.length() < 10 ? line : line.left(10) +
"..."));
952 }
catch (
const std::bad_alloc&) {
957 if (rawFeatures) rawFeatures->shrink_to_fit();
968 &classifier,
nullptr, parent);
973 std::vector<double>& rawScales,
977 QWidget* parentWidget ) {
978 bool cloudsWereProvided = !loadedClouds.empty();
980 &rawScales,
corePoints,
nullptr, ¶meters, parentWidget)) {
983 if (!cloudsWereProvided) {
985 for (NamedClouds::iterator it = loadedClouds.begin();
986 it != loadedClouds.end(); ++it)
994 const QString& sfName,
995 bool caseSensitive ) {
1001 if (caseSensitive) {
1004 QString sfNameUpper = sfName.toUpper();
1008 sfIdx =
static_cast<int>(i);
1023 size_t featureCount = 0;
1025 QMap<double, std::vector<NeighborhoodFeature::Shared>>
1027 QMap<double, std::vector<ContextBasedFeature::Shared>>
1037 if (features.empty() || !
corePoints.origin) {
1044 QMap<ccPointCloud*, FeaturesAndScales> cloudsWithScaledFeatures;
1047 QString errorMessage(
"invalid pointer");
1050 !feature->checkValidity(
corePoints.role, errorMessage)) {
1051 errorStr =
"Invalid rule/feature: " + errorMessage;
1056 if (!feature->prepare(
corePoints, errorStr, progressCb,
1057 generatedScalarFields)) {
1062 if (feature->scaled()) {
1064 switch (feature->getType()) {
1069 if (feature->cloud1 &&
1070 !feature->sf1WasAlreadyExisting)
1077 cloudsWithScaledFeatures[feature->cloud1];
1079 qSharedPointerCast<PointFeature>(feature));
1082 feature->scale) == fas.
scales.end()) {
1083 fas.
scales.push_back(feature->scale);
1088 if (feature->cloud2 &&
1089 feature->cloud2 != feature->cloud1 &&
1091 if (!feature->sf1WasAlreadyExisting)
1098 if (!feature->sf2WasAlreadyExisting) {
1100 cloudsWithScaledFeatures
1104 .push_back(qSharedPointerCast<
1106 if (std::find(fas.
scales.begin(),
1110 fas.
scales.push_back(feature->scale);
1121 if (feature->cloud1 &&
1122 !feature->sf1WasAlreadyExisting)
1129 cloudsWithScaledFeatures[feature->cloud1];
1131 .push_back(qSharedPointerCast<
1135 feature->scale) == fas.
scales.end()) {
1136 fas.
scales.push_back(feature->scale);
1142 if (feature->cloud2 &&
1143 feature->cloud2 != feature->cloud1 &&
1145 if (!feature->sf1WasAlreadyExisting)
1152 if (!feature->sf2WasAlreadyExisting) {
1154 cloudsWithScaledFeatures
1159 .push_back(qSharedPointerCast<
1163 if (std::find(fas.
scales.begin(),
1167 fas.
scales.push_back(feature->scale);
1178 if (feature->cloud1 &&
1179 !feature->sf1WasAlreadyExisting)
1186 cloudsWithScaledFeatures[feature->cloud1];
1188 .push_back(qSharedPointerCast<
1192 feature->scale) == fas.
scales.end()) {
1193 fas.
scales.push_back(feature->scale);
1202 }
catch (
const std::bad_alloc&) {
1203 errorStr =
"Not enough memory";
1209 bool success =
true;
1212 if (!cloudsWithScaledFeatures.empty()) {
1214 for (QMap<ccPointCloud*, FeaturesAndScales>::iterator it =
1215 cloudsWithScaledFeatures.begin();
1216 success && it != cloudsWithScaledFeatures.end(); ++it) {
1226 CVLog::Print(QString(
"Computing octree of cloud %1 (%2 points)")
1229 if (progressCb) progressCb->
start();
1230 QCoreApplication::processEvents();
1234 "[Tools::PrepareFeatures] Failed to compute octree "
1235 "(not enough memory?)";
1242 double largetScale = fas.
scales.back();
1251 QString logMessage = QString(
"Computing %1 features on cloud %2 at "
1258 progressCb->
setInfo(qPrintable(logMessage));
1263 bool cancelled =
false;
1266 #if defined(_OPENMP)
1267 #pragma omp parallel for num_threads(std::max(1, omp_get_max_threads() - 2))
1270 for (
int i = 0; i < static_cast<int>(pointCount); ++i) {
1272 QString localErrorStr;
1273 bool localSuccess =
true;
1276 cloudViewer::DgmOctree::
1277 NearestNeighboursSphericalSearchStruct nNSS;
1290 nNSS, largestRadius,
true);
1295 for (
size_t scaleIndex = 0;
1296 scaleIndex < fas.
scales.size(); ++scaleIndex) {
1297 double currentScale =
1302 if (scaleIndex != 0) {
1303 double radius = currentScale /
1305 double sqRadius = radius * radius;
1307 for (; kNN > 0; --kNN) {
1309 .squareDistd <= sqRadius) {
1325 feature->statSF1 && feature->field1 &&
1327 double outputValue = 0;
1328 if (!feature->computeStat(
1330 feature->field1, outputValue)) {
1333 "An error occurred during the "
1334 "computation of feature " +
1335 feature->toString() +
1337 feature->cloud1->getName();
1338 localSuccess =
false;
1342 ScalarType v1 =
static_cast<ScalarType
>(
1344 feature->statSF1->setValue(i, v1);
1348 feature->statSF2 && feature->field2 &&
1350 assert(feature->op !=
1352 double outputValue = 0;
1353 if (!feature->computeStat(
1355 feature->field2, outputValue)) {
1358 "An error occurred during the "
1359 "computation of feature " +
1360 feature->toString() +
1362 feature->cloud2->getName();
1363 localSuccess =
false;
1367 ScalarType v2 =
static_cast<ScalarType
>(
1369 feature->statSF2->setValue(i, v2);
1378 feature->sf1 && localSuccess) {
1379 double outputValue = 0;
1380 if (!feature->computeValue(
1385 "An error occurred during the "
1386 "computation of feature " +
1387 feature->toString() +
1389 feature->cloud1->getName();
1390 localSuccess =
false;
1394 ScalarType v1 =
static_cast<ScalarType
>(
1396 feature->sf1->setValue(i, v1);
1400 feature->sf2 && localSuccess) {
1401 assert(feature->op !=
1403 double outputValue = 0;
1404 if (!feature->computeValue(
1409 "An error occurred during the "
1410 "computation of feature " +
1411 feature->toString() +
1413 feature->cloud2->getName();
1414 localSuccess =
false;
1418 ScalarType v2 =
static_cast<ScalarType
>(
1420 feature->sf2->setValue(i, v2);
1429 feature->sf && localSuccess) {
1430 ScalarType outputValue = 0;
1431 if (!feature->computeValue(
1436 "An error occurred during the "
1437 "computation of feature " +
1438 feature->toString() +
1440 feature->cloud1->getName();
1441 localSuccess =
false;
1445 feature->sf->setValue(i, outputValue);
1449 if (!localSuccess) {
1450 localErrorStr = localErrorStr +
" at scale " +
1451 QString::number(currentScale) +
1460 if (!localSuccess) {
1463 #if defined(_OPENMP)
1464 errorStr =
"Feature computation failed for point " +
1465 QString::number(i) +
" (using OpenMP with " +
1466 QString::number(omp_get_num_threads()) +
1469 errorStr =
"Feature computation failed for point " +
1480 #if defined(_OPENMP)
1482 "Process cancelled at point " +
1483 QString::number(i) +
1484 " (using OpenMP with " +
1485 QString::number(omp_get_num_threads()) +
1488 errorStr =
"Process cancelled at point " +
1504 if (feature->scaled() && !feature->finish(
corePoints, errorStr)) {
1520 if (!inRatioSubset || !outRatioSubset) {
1527 "Invalid input reference clouds (associated cloud is wrong)");
1530 if (ratio < 0.0f || ratio > 1.0f) {
1531 CVLog::Warning(QString(
"Invalid parameter (ratio: %1)").arg(ratio));
1535 unsigned inSampleCount =
1536 static_cast<unsigned>(
floor(cloud->
size() * ratio));
1537 assert(inSampleCount <= cloud->
size());
1538 unsigned outSampleCount = cloud->
size() - inSampleCount;
1541 unsigned targetCount = inSampleCount;
1542 bool defaultState =
false;
1543 if (outSampleCount < inSampleCount) {
1544 targetCount = outSampleCount;
1545 defaultState =
true;
1549 std::vector<bool> pointInsideRatio;
1551 pointInsideRatio.resize(cloud->
size(), defaultState);
1552 }
catch (
const std::bad_alloc&) {
1557 if (!inRatioSubset->
reserve(inSampleCount) ||
1558 !outRatioSubset->
reserve(outSampleCount)) {
1560 inRatioSubset->
clear();
1561 outRatioSubset->
clear();
1567 unsigned randomCount = 0;
1568 while (randomCount < targetCount) {
1569 randIndex = ((randIndex + std::rand()) % cloud->
size());
1570 if (pointInsideRatio[randIndex] == defaultState) {
1571 pointInsideRatio[randIndex] = !defaultState;
1578 for (
unsigned i = 0; i < cloud->
size(); ++i) {
1579 if (pointInsideRatio[i])
1584 assert(inRatioSubset->
size() == inSampleCount);
1585 assert(outRatioSubset->
size() == outSampleCount);
1603 if (classifSFIdx < 0) {
float PointCoordinateType
Type of the coordinates of a (N-D) point.
CC_FILE_ERROR
Typical I/O filter errors.
const char LAS_FIELD_NAMES[][28]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
static void ResetSesionCounter()
static ccHObject * LoadFromFile(const QString &filename, LoadParameters ¶meters, Shared filter, CC_FILE_ERROR &result)
Loads one or more entities from a file with a known filter.
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)
Hierarchical CLOUDVIEWER Object.
void detachChild(ccHObject *child)
Detaches a specific child.
ccHObject * getParent() const
Returns parent object.
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
virtual QString getName() const
Returns object name.
virtual void setName(const QString &name)
Sets object name.
QSharedPointer< ccOctree > Shared
Shared pointer.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
unsigned char findBestLevelForAGivenNeighbourhoodSizeExtraction(PointCoordinateType radius) const
int findNeighborsInASphereStartingFromCell(NearestNeighboursSearchStruct &nNSS, double radius, bool sortValues=true) const
Advanced form of the nearest neighbours search algorithm (in a sphere)
void getTheCellPosWhichIncludesThePoint(const CCVector3 *thePoint, Tuple3i &cellPos) const
void computeCellCenter(CellCode code, unsigned char level, CCVector3 ¢er, bool isCodeTruncated=false) const
virtual unsigned size() const =0
Returns the number of points.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
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.
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
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
virtual GenericIndexedCloudPersist * getAssociatedCloud()
Returns the associated (source) cloud.
unsigned size() const override
Returns the number of points.
virtual void clear(bool releaseMemory=false)
Clears the cloud.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
A simple scalar field (to be associated to a point cloud)
const char * getName() const
Returns scalar field name.
bool toFile(QString filename, QWidget *parentWidget=nullptr) const
Saves the classifier to file.
bool isValid() const
Returns whether the classifier is valid or not.
bool fromFile(QString filename, QWidget *parentWidget=nullptr)
Loads the classifier from file.
static void error(char *msg)
QTextStream & endl(QTextStream &stream)
MiniVec< float, N > floor(const MiniVec< float, N > &a)
QMap< double, std::vector< NeighborhoodFeature::Shared > > neighborhoodFeaturesPerScale
std::vector< double > scales
QMap< double, std::vector< ContextBasedFeature::Shared > > contextBasedFeaturesPerScale
QMap< double, std::vector< PointFeature::Shared > > pointFeaturesPerScale
Generic loading parameters.
CCVector3d * coordinatesShift
If applicable, applied shift on load (optional)
ecvGlobalShiftManager::Mode shiftHandlingMode
How to handle big coordinates.
QWidget * parentWidget
Parent widget (if any)
bool alwaysDisplayLoadDialog
bool * coordinatesShiftEnabled
Whether shift on load has been applied after loading (optional)
static QString ToString(ContextBasedFeatureType type)
static ContextBasedFeatureType FromUpperString(const QString &token)
QSharedPointer< ContextBasedFeature > Shared
static DualCloudFeatureType FromUpperString(const QString &token)
std::vector< Shared > Set
Set of features.
QSharedPointer< Feature > Shared
Shared type.
Neighborhood-based feature.
QSharedPointer< NeighborhoodFeature > Shared
static NeighborhoodFeatureType FromUpperString(const QString &token)
static PointFeatureType FromUpperString(const QString &token)
QSharedPointer< PointFeature > Shared
int sourceSFIndex
Source scalar field index (if the feature source is 'ScalarField')