From e9547a724aac11962f66bc07a8a16d77ed93cfc1 Mon Sep 17 00:00:00 2001 From: Daniel Girardeau-Montaut Date: Sun, 4 Nov 2018 18:15:49 +0100 Subject: [PATCH] Glitch fix --- Features.cpp | 240 +++++++++++++++++++++++++++++++++----------- Features.h | 24 +++-- FeaturesInterface.h | 10 +- 3 files changed, 203 insertions(+), 71 deletions(-) diff --git a/Features.cpp b/Features.cpp index 185d2db..e804c66 100644 --- a/Features.cpp +++ b/Features.cpp @@ -28,6 +28,9 @@ #include #include +//CCLib +#include + //system #include @@ -40,6 +43,122 @@ static const char* s_normDipDirSFName = "Norm dip dir."; using namespace masc; +bool PointFeature::checkValidity(QString &error) const +{ + if (!Feature::checkValidity(error)) + { + return false; + } + + assert(cloud1); + + switch (type) + { + case PointFeature::Intensity: + { + if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_INTENSITY]) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_INTENSITY]); + return false; + } + return true; + } + case PointFeature::X: + case PointFeature::Y: + case PointFeature::Z: + return true; + case PointFeature::NbRet: + { + if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]); + return false; + } + return true; + } + case PointFeature::RetNb: + { + if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]); + return false; + } + return true; + } + case PointFeature::EchoRat: + { + if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]); + return false; + } + if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]); + return false; + } + return true; + } + case PointFeature::R: + case PointFeature::G: + case PointFeature::B: + if (!cloud1->hasColors()) + { + error = "Cloud has no RGB color"; + return false; + } + return true; + case PointFeature::NIR: + { + if (cloud1->getScalarFieldIndexByName(s_NIRSFName) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(s_NIRSFName); + return false; + } + return true; + } + case PointFeature::DipAng: + case PointFeature::DipDir: + { + if (!cloud1->hasNormals()) + { + error = "Cloud has no normals"; + return false; + } + return true; + } + case PointFeature::M3C2: + { + if (cloud1->getScalarFieldIndexByName(s_M3C2SFName) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(s_M3C2SFName); + return false; + } + return true; + } + case PointFeature::PCV: + { + if (cloud1->getScalarFieldIndexByName(s_PCVSFName) < 0) + { + error = QString("Cloud has no '%1' scalar field").arg(s_PCVSFName); + return false; + } + return true; + } + case PointFeature::SF: + if (sourceSFIndex >= static_cast(cloud1->getNumberOfScalarFields())) + { + error = QString("Cloud has no scalar field #%1").arg(sourceSFIndex); + return false; + } + return true; + default: + break; + } + + return true; +} + QSharedPointer PointFeature::retrieveField(ccPointCloud* cloud, QString& error) { if (!cloud) @@ -183,7 +302,7 @@ static bool ExtractStatFromSF( const CCVector3& queryPoint, assert(false); return false; } - outputValue = std::numeric_limits::quiet_NaN(); + std::numeric_limits::quiet_NaN(); //spherical neighborhood extraction structure CCLib::DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; @@ -230,85 +349,90 @@ static bool ExtractStatFromSF( const CCVector3& queryPoint, outputValue = maxValue - minValue; return true; } - - bool withSums = (stat == Feature::MEAN || stat == Feature::STD || stat == Feature::SKEW); - bool withMode = (stat == Feature::MODE || stat == Feature::SKEW); - double sum = 0.0; - double sum2 = 0.0; - QMap modeCounter; - - for (unsigned k = 0; k < kNN; ++k) + else { - unsigned index = nNSS.pointsInNeighbourhood[k].pointIndex; - double v = inputField.pointValue(index); + bool withSums = (stat == Feature::MEAN || stat == Feature::STD); + bool storeValues = (stat == Feature::MODE || stat == Feature::SKEW); + double sum = 0.0; + double sum2 = 0.0; - if (withSums) + CCLib::WeibullDistribution::ScalarContainer values; + if (storeValues) { - //compute average and std. dev. - sum += v; - sum2 += v * v; - } - - if (withMode) - { - //store the number of occurences of each value - //DGM TODO: it would be better with a custom 'resolution' if the field is not an integer one - float vf = static_cast(v); - if (modeCounter.contains(vf)) + try { - ++modeCounter[vf]; + values.resize(kNN); } - else + catch (const std::bad_alloc&) { - modeCounter[vf] = 1; + ccLog::Warning("Not enough memory"); + return false; } } - } - double mode = std::numeric_limits::quiet_NaN(); - if (withMode) - { - //look for the value with the highest frequency - unsigned maxCounter = 0; - for (QMap::const_iterator it = modeCounter.begin(); it != modeCounter.end(); ++it) + for (unsigned k = 0; k < kNN; ++k) { - if (it.value() > maxCounter) + unsigned index = nNSS.pointsInNeighbourhood[k].pointIndex; + double v = inputField.pointValue(index); + + if (withSums) { - maxCounter = it.value(); - mode = it.key(); + //compute average and std. dev. + sum += v; + sum2 += v * v; + } + + if (storeValues) + { + values[k] = static_cast(v); } } - } - switch (stat) - { - case Feature::MEAN: - outputValue = sum / kNN; + switch (stat) + { + case Feature::MEAN: + { + outputValue = sum / kNN; + } break; - case Feature::MODE: - outputValue = mode; + + case Feature::MODE: + { + CCLib::WeibullDistribution w; + w.computeParameters(values); + outputValue = w.computeMode(); + } break; - case Feature::STD: - outputValue = sqrt(std::abs(sum2 * kNN - sum * sum)) / kNN; + + case Feature::STD: + { + outputValue = sqrt(std::abs(sum2 * kNN - sum * sum)) / kNN; + } break; - case Feature::RANGE: - //we can't be here - assert(false); + + case Feature::RANGE: + { + //we can't be here + assert(false); + } return false; - case Feature::SKEW: - { - double mean = sum / kNN; - double std = sqrt(std::abs(sum2 / kNN - mean * mean)); - if (std > std::numeric_limits::epsilon()) //arbitrary epsilon + + case Feature::SKEW: { - outputValue = (mean - mode) / std; + CCLib::WeibullDistribution w; + w.computeParameters(values); + outputValue = w.computeSkewness(); } break; - } - default: - ccLog::Warning("Unhandled STAT measure"); - assert(false); + + default: + { + ccLog::Warning("Unhandled STAT measure"); + assert(false); + } return false; + + } } return true; diff --git a/Features.h b/Features.h index df197e2..0ce73fd 100644 --- a/Features.h +++ b/Features.h @@ -85,9 +85,9 @@ namespace masc case NIR: return "NIR"; case DipAng: - return "DipAng"; + return "NormDip"; case DipDir: - return "DipDir"; + return "NormDipDir"; case M3C2: return "M3C2"; case PCV: @@ -126,7 +126,7 @@ namespace masc return B; else if (token == "NIR") return NIR; - else if (token == "NORMDIPANG") + else if (token == "NORMDIP") return DipAng; else if (token == "NORMDIPDIR") return DipDir; @@ -184,9 +184,11 @@ namespace masc //! Returns the feature type virtual Type getType() const override { return Type::PointFeature; } //! Clones this feature - virtual Feature::Shared clone() const { return Feature::Shared(new PointFeature(*this)); } + virtual Feature::Shared clone() const override { return Feature::Shared(new PointFeature(*this)); } //! Prepares the feature (compute the scalar field, etc.) - virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr); + virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr) override; + //! Checks the feature definition validity + virtual bool checkValidity(QString &error) const override; //! Returns the descriptor for this particular feature virtual QString toString() const override { @@ -347,9 +349,9 @@ namespace masc //! Returns the feature type virtual Type getType() const override { return Type::NeighborhoodFeature; } //! Clones this feature - virtual Feature::Shared clone() const { return Feature::Shared(new NeighborhoodFeature(*this)); } + virtual Feature::Shared clone() const override { return Feature::Shared(new NeighborhoodFeature(*this)); } //! Prepares the feature (compute the scalar field, etc.) - virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr); + virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr) override; //! Returns the descriptor for this particular feature virtual QString toString() const override { @@ -423,9 +425,9 @@ namespace masc //! Returns the feature type virtual Type getType() const override { return Type::ContextBasedFeature; } //! Clones this feature - virtual Feature::Shared clone() const { return Feature::Shared(new ContextBasedFeature(*this)); } + virtual Feature::Shared clone() const override { return Feature::Shared(new ContextBasedFeature(*this)); } //! Prepares the feature (compute the scalar field, etc.) - virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr); + virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr) override; //! Returns the descriptor for this particular feature virtual QString toString() const override { @@ -490,9 +492,9 @@ namespace masc //! Returns the feature type virtual Type getType() const override { return Type::DualCloudFeature; } //! Clones this feature - virtual Feature::Shared clone() const { return Feature::Shared(new DualCloudFeature(*this)); } + virtual Feature::Shared clone() const override { return Feature::Shared(new DualCloudFeature(*this)); } //! Prepares the feature (compute the scalar field, etc.) - virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr); + virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr) override; //! Returns the descriptor for this particular feature virtual QString toString() const override { diff --git a/FeaturesInterface.h b/FeaturesInterface.h index 4509985..cfb463b 100644 --- a/FeaturesInterface.h +++ b/FeaturesInterface.h @@ -142,9 +142,15 @@ namespace masc inline bool scaled() const { return std::isfinite(scale); } //! Checks the feature definition validity - bool checkValidity(QString &error) const + virtual bool checkValidity(QString &error) const { - int cloudCount = (cloud1 ? (cloud2 ? 2 : 1) : 0); + unsigned char cloudCount = (cloud1 ? (cloud2 ? 2 : 1) : 0); + + if (cloudCount == 0) + { + error = "feature has no associated cloud"; + return false; + } if (scaled() && stat == NO_STAT) {