diff --git a/ContextBasedFeature.cpp b/ContextBasedFeature.cpp index 74952ce..09cb1d4 100644 --- a/ContextBasedFeature.cpp +++ b/ContextBasedFeature.cpp @@ -72,7 +72,8 @@ bool ContextBasedFeature::checkValidity(QString corePointRole, QString &error) c bool ContextBasedFeature::prepare( const CorePoints& corePoints, QString& errorMessage, CCCoreLib::GenericProgressCallback* progressCb/*=nullptr*/, - SFCollector* generatedScalarFields/*=nullptr*/) + SFCollector* generatedScalarFields/*=nullptr*/, + bool useExistingScalarFields /*=false*/) { if (!cloud1 || !corePoints.cloud) { @@ -117,9 +118,24 @@ bool ContextBasedFeature::prepare( const CorePoints& corePoints, resultSFName += "@kNN=" + QString::number(kNN); } - //and the scalar field - assert(!sf); - sf = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::CAN_REMOVE); + // check if there exists a scalar field with the same name + int sfIdx = corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName)); + if (sfIdx >= 0) + { + if (useExistingScalarFields) + { + ccLog::Warning("use existing scalar field: " + resultSFName); + sf = corePoints.cloud->getScalarField(sfIdx); + generatedScalarFields->push(corePoints.cloud, sf, SFCollector::ALWAYS_KEEP); + this->valueAlreadyComputed = true; + } + } + else + { + assert(!sf); + sf = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::CAN_REMOVE); + } + if (!sf) { errorMessage = QString("Failed to prepare scalar %1 @ scale %2").arg(resultSFName).arg(scale); @@ -127,7 +143,7 @@ bool ContextBasedFeature::prepare( const CorePoints& corePoints, } source.name = sf->getName(); - if (!scaled()) //with 'kNN' neighbors, we can compute the values right away + if (!scaled() && !this->valueAlreadyComputed) //with 'kNN' neighbors, we can compute the values right away (skip if value is already computed) { unsigned pointCount = corePoints.size(); QString logMessage = QString("Computing %1 on cloud %2 with context cloud %3\n(core points: %4)").arg(typeStr).arg(corePoints.cloud->getName()).arg(cloud2Label).arg(pointCount); diff --git a/ContextBasedFeature.h b/ContextBasedFeature.h index 70b78d0..d4e2d04 100644 --- a/ContextBasedFeature.h +++ b/ContextBasedFeature.h @@ -75,6 +75,7 @@ namespace masc , kNN(p_kNN) , ctxClassLabel(p_ctxClassLabel) , sf(nullptr) + , valueAlreadyComputed(false) { scale = p_scale; } @@ -82,7 +83,8 @@ namespace masc //inherited from Feature virtual Type getType() const override { return Type::ContextBasedFeature; } virtual Feature::Shared clone() const override { return Feature::Shared(new ContextBasedFeature(*this)); } - virtual bool prepare(const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; + virtual bool prepare(const CorePoints& corePoints, QString& error, + CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr, bool useExistingScalarFields = false) override; virtual bool finish(const CorePoints& corePoints, QString& error) override; virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override; @@ -103,5 +105,6 @@ namespace masc int ctxClassLabel; //! The computed scalar CCCoreLib::ScalarField* sf; + bool valueAlreadyComputed; }; -} \ No newline at end of file +} diff --git a/DualCloudFeature.cpp b/DualCloudFeature.cpp index e791e85..04b4fb7 100644 --- a/DualCloudFeature.cpp +++ b/DualCloudFeature.cpp @@ -22,7 +22,8 @@ using namespace masc; bool DualCloudFeature::prepare( const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb/*=nullptr*/, - SFCollector* generatedScalarFields/*=nullptr*/) + SFCollector* generatedScalarFields/*=nullptr*/, + bool useExistingScalarFields /*=false*/) { //TODO return false; @@ -55,4 +56,4 @@ bool DualCloudFeature::checkValidity(QString corePointRole, QString &error) cons } return true; -} \ No newline at end of file +} diff --git a/DualCloudFeature.h b/DualCloudFeature.h index fbbb6db..e120290 100644 --- a/DualCloudFeature.h +++ b/DualCloudFeature.h @@ -67,7 +67,8 @@ namespace masc //inherited from Feature virtual Type getType() const override { return Type::DualCloudFeature; } virtual Feature::Shared clone() const override { return Feature::Shared(new DualCloudFeature(*this)); } - virtual bool prepare(const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; + virtual bool prepare(const CorePoints& corePoints, QString& error, + CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr, bool useExistingScalarFields = false) override; virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override; @@ -76,4 +77,4 @@ namespace masc **/ DualCloudFeatureType type; }; -} \ No newline at end of file +} diff --git a/FeaturesInterface.h b/FeaturesInterface.h index 83ebb52..9ee8f70 100644 --- a/FeaturesInterface.h +++ b/FeaturesInterface.h @@ -174,7 +174,8 @@ namespace masc virtual Feature::Shared clone() const = 0; //! Prepares the feature (compute the scalar field, etc.) - virtual bool prepare(const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) = 0; + virtual bool prepare(const CorePoints& corePoints, QString& error, + CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr, bool useExistingScalarFields = false) = 0; //! Finishes the feature preparation (update the scalar field, etc.) virtual bool finish(const CorePoints& corePoints, QString& error) { /* does nothing by default*/return true; } diff --git a/NeighborhoodFeature.cpp b/NeighborhoodFeature.cpp index e415290..a85f0f5 100644 --- a/NeighborhoodFeature.cpp +++ b/NeighborhoodFeature.cpp @@ -62,7 +62,8 @@ bool NeighborhoodFeature::checkValidity(QString corePointRole, QString &error) c bool NeighborhoodFeature::prepare( const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb/*=nullptr*/, - SFCollector* generatedScalarFields/*=nullptr*/) + SFCollector* generatedScalarFields/*=nullptr*/, + bool useExistingScalarFields /*=false*/) { if (!cloud1 || !corePoints.cloud) { @@ -87,9 +88,25 @@ bool NeighborhoodFeature::prepare( const CorePoints& corePoints, } resultSFName += "@" + QString::number(scale); + // check if there exists a scalar field with the same name + int sfIdx = corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName)); + if (sfIdx >= 0) + { + if (useExistingScalarFields) + { + ccLog::Warning("use existing scalar field: " + resultSFName); + sf1 = corePoints.cloud->getScalarField(sfIdx); + generatedScalarFields->push(corePoints.cloud, sf1, SFCollector::ALWAYS_KEEP); + this->value1AlreadyComputed = true; + } + } + else + { + assert(!sf1); + sf1 = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::CAN_REMOVE); + } + //and the scalar field - assert(!sf1); - sf1 = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::CAN_REMOVE); if (!sf1) { error = QString("Failed to prepare scalar %1 @ scale %2").arg(resultSFName).arg(scale); @@ -102,8 +119,25 @@ bool NeighborhoodFeature::prepare( const CorePoints& corePoints, QString resultSFName2 = ToString(type) + "_" + cloud2Label + "@" + QString::number(scale); keepSF2 = (corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName2)) >= 0); //we remember that the scalar field was already existing! - assert(!sf2); - sf2 = PrepareSF(corePoints.cloud, qPrintable(resultSFName2), generatedScalarFields, SFCollector::ALWAYS_REMOVE); + // check if there exists a scalar field with the same name, if so, use it + int sfIdx = corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName)); + useExistingScalarFields = true; + if (sfIdx >= 0) + { + if (useExistingScalarFields) + { + ccLog::Warning("use existing scalar field: " + resultSFName); + sf2 = corePoints.cloud->getScalarField(sfIdx); + generatedScalarFields->push(corePoints.cloud, sf2, SFCollector::ALWAYS_KEEP); + this->value2AlreadyComputed = true; + } + } + else + { + assert(!sf2); + sf2 = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::ALWAYS_REMOVE); + } + if (!sf2) { error = QString("Failed to prepare scalar field for %1 @ scale %2").arg(cloud2Label).arg(scale); diff --git a/NeighborhoodFeature.h b/NeighborhoodFeature.h index cab58e2..f873659 100644 --- a/NeighborhoodFeature.h +++ b/NeighborhoodFeature.h @@ -152,13 +152,16 @@ namespace masc , sf1(nullptr) , sf2(nullptr) , keepSF2(false) + , value1AlreadyComputed(false) + , value2AlreadyComputed(false) { } //inherited from Feature virtual Type getType() const override { return Type::NeighborhoodFeature; } virtual Feature::Shared clone() const override { return Feature::Shared(new NeighborhoodFeature(*this)); } - virtual bool prepare(const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; + virtual bool prepare(const CorePoints& corePoints, QString& error, + CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr, bool useExistingScalarFields = false) override; virtual bool finish(const CorePoints& corePoints, QString& error) override; virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override; @@ -176,5 +179,7 @@ namespace masc //! Feature values CCCoreLib::ScalarField *sf1, *sf2; bool keepSF2; + bool value1AlreadyComputed; + bool value2AlreadyComputed; }; -} \ No newline at end of file +} diff --git a/PointFeature.cpp b/PointFeature.cpp index a7ba639..e983a4f 100644 --- a/PointFeature.cpp +++ b/PointFeature.cpp @@ -491,7 +491,8 @@ static bool ComputeMathOpWithNearestNeighbor( const CorePoints& corePoints, bool PointFeature::prepare( const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb/*=nullptr*/, - SFCollector* generatedScalarFields/*=nullptr*/) + SFCollector* generatedScalarFields/*=nullptr*/, + bool useExistingScalarFields /*=false*/) { if (!cloud1 || !corePoints.cloud) { @@ -576,8 +577,22 @@ bool PointFeature::prepare( const CorePoints& corePoints, { resultSFName += "@" + QString::number(scale); - //prepare the corresponding scalar field - statSF1 = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::CAN_REMOVE); + // check if there exists a scalar field with the same name + int sfIdx = corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName)); + useExistingScalarFields = true; + if (sfIdx >= 0) + { + if (useExistingScalarFields) + { + ccLog::Warning("use existing scalar field: " + resultSFName); + statSF1 = corePoints.cloud->getScalarField(sfIdx); + generatedScalarFields->push(corePoints.cloud, statSF1, SFCollector::ALWAYS_KEEP); + this->value1AlreadyComputed = true; + } + } + else //prepare the corresponding scalar field + statSF1 = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::CAN_REMOVE); + if (!statSF1) { error = QString("Failed to prepare scalar field for field '%1' @ scale %2").arg(field1->getName()).arg(scale); @@ -590,8 +605,24 @@ bool PointFeature::prepare( const CorePoints& corePoints, QString resultSFName2 = field2->getName() + QString("_") + Feature::StatToString(stat) + "_" + cloud2Label + "@" + QString::number(scale); //keepStatSF2 = (corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName2)) >= 0); //we remember that the scalar field was already existing! - assert(!statSF2); - statSF2 = PrepareSF(corePoints.cloud, qPrintable(resultSFName2), generatedScalarFields, SFCollector::ALWAYS_REMOVE); + // check if there exists a scalar field with the same name, if so, use it + int sfIdx = corePoints.cloud->getScalarFieldIndexByName(qPrintable(resultSFName)); + if (sfIdx >= 0) + { + if (useExistingScalarFields) + { + ccLog::Warning("use existing scalar field: " + resultSFName); + statSF2 = corePoints.cloud->getScalarField(sfIdx); + generatedScalarFields->push(corePoints.cloud, statSF2, SFCollector::ALWAYS_KEEP); + this->value2AlreadyComputed = true; + } + } + else + { + assert(!statSF2); + statSF2 = PrepareSF(corePoints.cloud, qPrintable(resultSFName), generatedScalarFields, SFCollector::ALWAYS_REMOVE); + } + if (!statSF2) { error = QString("Failed to prepare scalar field for field '%1' @ scale %2").arg(field2->getName()).arg(scale); diff --git a/PointFeature.h b/PointFeature.h index c5864ad..1155b1f 100644 --- a/PointFeature.h +++ b/PointFeature.h @@ -148,6 +148,8 @@ namespace masc , field2(nullptr) , statSF1(nullptr) , statSF2(nullptr) + , value1AlreadyComputed(false) + , value2AlreadyComputed(false) //, keepStatSF2(false) { //auomatically set the right source for specific features @@ -180,7 +182,8 @@ namespace masc //inherited from Feature virtual Type getType() const override { return Type::PointFeature; } virtual Feature::Shared clone() const override { return Feature::Shared(new PointFeature(*this)); } - virtual bool prepare(const CorePoints& corePoints, QString& error, CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; + virtual bool prepare(const CorePoints& corePoints, QString& error, + CCCoreLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr, bool useExistingScalarFileds = false) override; virtual bool finish(const CorePoints& corePoints, QString& error) override; virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override; @@ -213,5 +216,8 @@ namespace masc CCCoreLib::ScalarField *statSF1, *statSF2; //bool keepStatSF2; + + bool value1AlreadyComputed; + bool value2AlreadyComputed; }; } diff --git a/q3DMASCClassifier.cpp b/q3DMASCClassifier.cpp index 18028a0..3c77c7a 100644 --- a/q3DMASCClassifier.cpp +++ b/q3DMASCClassifier.cpp @@ -68,7 +68,7 @@ static IScalarFieldWrapper::Shared GetSource(const Feature::Source& fs, const cc } else { - ccLog::Warning(QObject::tr("Internal error: unknwon scalar field '%1'").arg(fs.name)); + ccLog::Warning(QObject::tr("Internal error: unknown scalar field '%1'").arg(fs.name)); return IScalarFieldWrapper::Shared(nullptr); } } diff --git a/q3DMASCTools.cpp b/q3DMASCTools.cpp index c39928d..43c0602 100644 --- a/q3DMASCTools.cpp +++ b/q3DMASCTools.cpp @@ -984,8 +984,10 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features return false; } + // if the feature already exists and if useExistingFeatures is checked, simply populate generatedScalarFields //prepare the feature - if (!feature->prepare(corePoints, errorStr, progressCb, generatedScalarFields)) + bool useExistingScalarFileds = true; + if (!feature->prepare(corePoints, errorStr, progressCb, generatedScalarFields, useExistingScalarFileds)) { //something failed (error should be up to date) return false; @@ -1180,7 +1182,7 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features //Point features for (PointFeature::Shared& feature : fas.pointFeaturesPerScale[currentScale]) { - if (feature->cloud1 == sourceCloud && feature->statSF1 && feature->field1) + if (feature->cloud1 == sourceCloud && feature->statSF1 && feature->field1 && !feature->value1AlreadyComputed) { double outputValue = 0; if (!feature->computeStat(nNSS.pointsInNeighbourhood, feature->field1, outputValue)) @@ -1194,7 +1196,7 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features feature->statSF1->setValue(i, v1); } - if (feature->cloud2 == sourceCloud && feature->statSF2 && feature->field2) + if (feature->cloud2 == sourceCloud && feature->statSF2 && feature->field2 && !feature->value2AlreadyComputed) { assert(feature->op != Feature::NO_OPERATION); double outputValue = 0; @@ -1213,7 +1215,7 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features //Neighborhood features for (NeighborhoodFeature::Shared& feature : fas.neighborhoodFeaturesPerScale[currentScale]) { - if (feature->cloud1 == sourceCloud && feature->sf1) + if (feature->cloud1 == sourceCloud && feature->sf1 && !feature->value1AlreadyComputed) { double outputValue = 0; if (!feature->computeValue(nNSS.pointsInNeighbourhood, nNSS.queryPoint, outputValue)) @@ -1228,7 +1230,7 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features feature->sf1->setValue(i, v1); } - if (feature->cloud2 == sourceCloud && feature->sf2) + if (feature->cloud2 == sourceCloud && feature->sf2 && !feature->value2AlreadyComputed) { assert(feature->op != Feature::NO_OPERATION); double outputValue = 0; @@ -1248,7 +1250,7 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features //Context-based features for (ContextBasedFeature::Shared& feature : fas.contextBasedFeaturesPerScale[currentScale]) { - if (feature->cloud2 == sourceCloud && feature->sf) + if (feature->cloud2 == sourceCloud && feature->sf && !feature->valueAlreadyComputed) { ScalarType outputValue = 0; if (!feature->computeValue(nNSS.pointsInNeighbourhood, nNSS.queryPoint, outputValue))