From 62f3f6721045a667c92296f2138a2addbcbcf284 Mon Sep 17 00:00:00 2001 From: Daniel Girardeau-Montaut Date: Sun, 28 Oct 2018 21:22:04 +0100 Subject: [PATCH] Load feature list from a file (work in progress) --- Features.h | 29 ++++++++ FeaturesInterface.h | 3 +- q3DMASC.cpp | 1 - q3DMASCTools.cpp | 167 ++++++++++++++++++++++++++++++++++++++++++++ q3DMASCTools.h | 6 ++ 5 files changed, 204 insertions(+), 2 deletions(-) diff --git a/Features.h b/Features.h index 4182f19..a1ffc08 100644 --- a/Features.h +++ b/Features.h @@ -495,3 +495,32 @@ public: //methods //! Scale (optional) double scale; }; + +struct Scales +{ + typedef QSharedPointer Shared; + std::vector values; +}; + +struct FeatureRule +{ + typedef std::vector Set; + + enum Stat + { + NO_STAT, MEAN, STD, MIN, MAX + }; + Stat stat = NO_STAT; //only considered if a scale is defined + + enum Operation + { + NO_OPERATION, MINUS, PLUS, DIVIDE, MULTIPLY + }; + Operation op = NO_OPERATION; //only considered if 2 clouds are defined + + Scales::Shared scales; + Feature::Type featureType = Feature::Type::Invalid; + Feature::Shared feature; + ccPointCloud* cloud1 = nullptr; + ccPointCloud* cloud2 = nullptr; +}; diff --git a/FeaturesInterface.h b/FeaturesInterface.h index fb42b66..519f426 100644 --- a/FeaturesInterface.h +++ b/FeaturesInterface.h @@ -41,7 +41,8 @@ struct Feature PointFeature, /*!< Point features (scalar field, etc.) */ NeighborhoodFeature, /*!< Neighborhood based features for a given scale */ ContextBasedFeature, /*!< Contextual based features */ - DualCloudFeature /*!< Dual Cloud features: requires 2 point clouds */ + DualCloudFeature, /*!< Dual Cloud features: requires 2 point clouds */ + Invalid /*!< Invalid feature */ }; //! Returns the type (must be reimplemented by child struct) diff --git a/q3DMASC.cpp b/q3DMASC.cpp index 6ddc967..237ce01 100644 --- a/q3DMASC.cpp +++ b/q3DMASC.cpp @@ -179,7 +179,6 @@ void q3DMASCPlugin::doTrainAction() } m_app->dispToConsole(QString("Correct = %1 / %2 --> accuracy = %3").arg(metrics.goodGuess).arg(metrics.sampleCount).arg(metrics.ratio), ccMainAppInterface::STD_CONSOLE_MESSAGE); - } void q3DMASCPlugin::registerCommands(ccCommandLineInterface* cmd) diff --git a/q3DMASCTools.cpp b/q3DMASCTools.cpp index a4b269c..d96b8c8 100644 --- a/q3DMASCTools.cpp +++ b/q3DMASCTools.cpp @@ -17,11 +17,177 @@ #include "q3DMASCTools.h" +//qCC_io +#include + +//Qt +#include +#include + //system #include using namespace masc; +bool Tools::LoadFile(QString filename, ccPointCloud* pc1, ccPointCloud* pc2, FeatureRule::Set& features) +{ + QFile file(filename); + if (!file.exists()) + { + ccLog::Warning(QString("Can't find file '%1'").arg(filename)); + return false; + } + if (!file.open(QFile::Text | QFile::ReadOnly)) + { + ccLog::Warning(QString("Can't open file '%1'").arg(filename)); + return false; + } + + Scales::Shared scales(new Scales); + assert(features.empty()); + + QMap > clouds; + + QTextStream stream(&file); + while (true) + { + QString line = stream.readLine(); + if (line.isNull()) + { + //eof + break; + } + + if (line.startsWith("#")) + { + //comment + continue; + } + + //strip out the potential comment at the end of the mine as well + int commentIndex = line.indexOf('#'); + if (commentIndex >= 0) + line = line.left(commentIndex); + + QString upperLine = line.toUpper(); + if (upperLine.startsWith("CLOUD:")) //clouds + { + QString command = line.mid(6); + QStringList tokens = command.split(':'); + if (tokens.size() != 2) + { + ccLog::Warning("Malformed file: expecting 2 tokens after 'cloud:'"); + return false; + } + QString pcName = tokens[0]; + QString pcFilename = tokens[1]; + //try to open the cloud + { + FileIOFilter::LoadParameters parameters; + parameters.alwaysDisplayLoadDialog = false; + CC_FILE_ERROR error = CC_FERR_NO_ERROR; + ccHObject* object = FileIOFilter::LoadFromFile(pcFilename, parameters, error); + if (error != CC_FERR_NO_ERROR || !object) + { + ccLog::Warning("Failed to open the file (see console)"); + if (object) + delete object; + return false; + } + if (!object->isA(CC_TYPES::POINT_CLOUD)) + { + ccLog::Warning("File doesn't contain a single cloud"); + delete object; + return false; + } + clouds.insert(pcName, QScopedPointer(static_cast(object))); + } + } + else if (upperLine.startsWith("SCALES:")) //scales + { + QString command = line.mid(7); + QStringList tokens = command.split(';'); + if (tokens.empty()) + { + ccLog::Warning("Malformed file: expecting at least one token after 'scales:'"); + return false; + } + + try + { + for (const QString& token : tokens) + { + if (token.contains(':')) + { + //it's probably a range + QStringList subTokens = token.split(':'); + if (subTokens.size() != 3) + { + ccLog::Warning(QString("Malformed file: expecting 3 tokens for a range of scales (%1)").arg(token)); + return false; + } + bool ok[3] = { true, true, true }; + double start = subTokens[0].toDouble(ok); + double step = subTokens[1].toDouble(ok + 1); + double stop = subTokens[2].toDouble(ok + 2); + if (!ok[0] || !ok[1] || !ok[2]) + { + ccLog::Warning(QString("Malformed file: invalid values in scales range (%1)").arg(token)); + return false; + } + if (stop < start || step <= 1.0-6) + { + ccLog::Warning(QString("Malformed file: invalid range (%1)").arg(token)); + return false; + } + for (double v = start; v <= stop + 1.0 - 6; v += step) + { + scales->values.push_back(v); + } + } + else + { + bool ok = true; + double v = token.toDouble(&ok); + if (!ok) + { + ccLog::Warning(QString("Malformed file: invalid scale value (%1)").arg(token)); + return false; + } + scales->values.push_back(v); + } + } + } + catch (const std::bad_alloc&) + { + ccLog::Warning("Not enough memory"); + return false; + } + } + else if (upperLine.startsWith("FEATURE:")) //feature + { + QString command = line.mid(8); + QStringList tokens = command.split('_'); + if (tokens.empty()) + { + ccLog::Warning("Malformed file: expecting at least one token after 'feature:'"); + return false; + } + + QString typeStr = tokens[0].toUpper(); + //TODO + + } + else + { + ccLog::Warning("Unrecognized token/line: " + (line.length() < 10 ? line : line.left(10) + "...")); + return false; + } + } + + return true; +} + bool Tools::RandomSubset(ccPointCloud* cloud, float ratio, CCLib::ReferenceCloud* inRatioSubset, CCLib::ReferenceCloud* outRatioSubset) { if (!cloud) @@ -106,3 +272,4 @@ bool Tools::RandomSubset(ccPointCloud* cloud, float ratio, CCLib::ReferenceCloud return true; } + diff --git a/q3DMASCTools.h b/q3DMASCTools.h index f83c486..48123ad 100644 --- a/q3DMASCTools.h +++ b/q3DMASCTools.h @@ -17,6 +17,9 @@ //# # //########################################################################## +//Local +#include "Features.h" + //qCC_db #include @@ -31,6 +34,9 @@ namespace masc class Tools { public: + + static bool LoadFile(QString filename, ccPointCloud* pc1, ccPointCloud* pc2, FeatureRule::Set& features); + static bool RandomSubset(ccPointCloud* cloud, float ratio, CCLib::ReferenceCloud* inRatioSubset, CCLib::ReferenceCloud* outRatioSubset); };