mirror of
https://github.com/dgirardeau/q3DMASC.git
synced 2026-08-29 08:34:48 +08:00
File parser and feature computer works for Point Features
This commit is contained in:
+112
@@ -0,0 +1,112 @@
|
||||
//##########################################################################
|
||||
//# #
|
||||
//# CLOUDCOMPARE PLUGIN: q3DMASC #
|
||||
//# #
|
||||
//# This program is free software; you can redistribute it and/or modify #
|
||||
//# it under the terms of the GNU General Public License as published by #
|
||||
//# the Free Software Foundation; version 2 or later of the License. #
|
||||
//# #
|
||||
//# This program is distributed in the hope that it will be useful, #
|
||||
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
//# GNU General Public License for more details. #
|
||||
//# #
|
||||
//# COPYRIGHT: Dimitri Lague / CNRS / UEB #
|
||||
//# #
|
||||
//##########################################################################
|
||||
|
||||
#include "CorePoints.h"
|
||||
|
||||
//qCC_db
|
||||
#include <ccPointCloud.h>
|
||||
|
||||
//CCLib
|
||||
#include <CloudSamplingTools.h>
|
||||
|
||||
//system
|
||||
#include <assert.h>
|
||||
|
||||
using namespace masc;
|
||||
|
||||
bool CorePoints::prepare(CCLib::GenericProgressCallback* progressCb/*=nullptr*/)
|
||||
{
|
||||
if (!origin)
|
||||
{
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selection)
|
||||
{
|
||||
//nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
//now we can compute the subsampled version
|
||||
CCLib::ReferenceCloud* ref = nullptr;
|
||||
switch (selectionMethod)
|
||||
{
|
||||
case SPATIAL:
|
||||
{
|
||||
//we'll need an octree
|
||||
if (!origin->getOctree())
|
||||
{
|
||||
if (!origin->computeOctree(progressCb))
|
||||
{
|
||||
ccLog::Warning("[CorePoints::prepare] Failed to compute the octree");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CCLib::CloudSamplingTools::SFModulationParams modParams;
|
||||
modParams.enabled = false;
|
||||
ref = CCLib::CloudSamplingTools::resampleCloudSpatially(
|
||||
origin,
|
||||
static_cast<PointCoordinateType>(selectionParam),
|
||||
modParams,
|
||||
origin->getOctree().data(),
|
||||
progressCb);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RANDOM:
|
||||
{
|
||||
if (selectionParam <= 0.0 || selectionParam >= 1.0)
|
||||
{
|
||||
ccLog::Warning("[CorePoints::prepare] Random subsampling ration must be between 0 and 1 (excluded)");
|
||||
return false;
|
||||
}
|
||||
int targetCount = static_cast<int>(origin->size() * selectionParam);
|
||||
ref = CCLib::CloudSamplingTools::subsampleCloudRandomly(origin, targetCount, progressCb);
|
||||
break;
|
||||
}
|
||||
|
||||
case NONE:
|
||||
//nothing to do
|
||||
cloud = origin;
|
||||
return true;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
//store the references
|
||||
if (!ref)
|
||||
{
|
||||
ccLog::Warning("[CorePoints::prepare] Failed to subsampled the origin cloud");
|
||||
return false;
|
||||
}
|
||||
selection.reset(ref);
|
||||
|
||||
//and create the subsampled version of the cloud
|
||||
cloud = origin->partialClone(ref);
|
||||
if (!cloud)
|
||||
{
|
||||
ccLog::Warning("[CorePoints::prepare] Failed to subsampled the origin cloud (not enough memory)");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
//##########################################################################
|
||||
//# #
|
||||
//# CLOUDCOMPARE PLUGIN: q3DMASC #
|
||||
//# #
|
||||
//# This program is free software; you can redistribute it and/or modify #
|
||||
//# it under the terms of the GNU General Public License as published by #
|
||||
//# the Free Software Foundation; version 2 or later of the License. #
|
||||
//# #
|
||||
//# This program is distributed in the hope that it will be useful, #
|
||||
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
//# GNU General Public License for more details. #
|
||||
//# #
|
||||
//# COPYRIGHT: Dimitri Lague / CNRS / UEB #
|
||||
//# #
|
||||
//##########################################################################
|
||||
|
||||
//qCC_db
|
||||
#include <ccPointCloud.h>
|
||||
|
||||
//CCLib
|
||||
#include <ReferenceCloud.h>
|
||||
#include <GenericProgressCallback.h>
|
||||
|
||||
//Qt
|
||||
#include <QSharedPointer>
|
||||
|
||||
//! 3DMASC classifier
|
||||
namespace masc
|
||||
{
|
||||
//! Core points descriptor
|
||||
struct CorePoints
|
||||
{
|
||||
//origin cloud
|
||||
ccPointCloud* origin = nullptr;
|
||||
|
||||
//core points cloud
|
||||
ccPointCloud* cloud = nullptr;
|
||||
|
||||
//! Return the size
|
||||
inline unsigned size() const { return (cloud ? cloud->size() : 0); }
|
||||
//! Return the point index
|
||||
inline unsigned originIndex(unsigned i) const { return selection ? selection->getPointGlobalIndex(i) : i; }
|
||||
|
||||
//selection (if any)
|
||||
QSharedPointer<CCLib::ReferenceCloud> selection;
|
||||
enum SubSamplingMethod { NONE, RANDOM, SPATIAL };
|
||||
SubSamplingMethod selectionMethod = NONE;
|
||||
double selectionParam = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
//! Prepares the selection (must be called once)
|
||||
bool prepare(CCLib::GenericProgressCallback* progressCb = nullptr);
|
||||
};
|
||||
|
||||
}; //namespace masc
|
||||
+34
-12
@@ -19,6 +19,7 @@
|
||||
|
||||
//Local
|
||||
#include "FeaturesInterface.h"
|
||||
#include "CorePoints.h"
|
||||
|
||||
//qCC_db
|
||||
#include <ccPointCloud.h>
|
||||
@@ -151,12 +152,12 @@ public: //methods
|
||||
//auomatically set the right source for specific features
|
||||
switch (type)
|
||||
{
|
||||
case Z:
|
||||
source = DimZ;
|
||||
case X:
|
||||
source = DimX;
|
||||
sourceName = "X";
|
||||
break;
|
||||
case Z:
|
||||
source = DimZ;
|
||||
case Y:
|
||||
source = DimY;
|
||||
sourceName = "Y";
|
||||
break;
|
||||
case Z:
|
||||
@@ -597,7 +598,7 @@ struct FeatureRule
|
||||
int sourceSFIndex = -1;
|
||||
|
||||
//! Checks the rule validity
|
||||
bool checkValidity(QString &error) const
|
||||
bool checkValidity(/*const masc::CorePoints& corePoints, */QString &error) const
|
||||
{
|
||||
int cloudCount = (cloud1 ? (cloud2 ? 2 : 1) : 0);
|
||||
|
||||
@@ -606,29 +607,50 @@ struct FeatureRule
|
||||
error = "feature rule has no associated feature";
|
||||
return false;
|
||||
}
|
||||
if (scales != nullptr && scales->values.empty())
|
||||
if (scales != nullptr)
|
||||
{
|
||||
error = "invalid scales definition";
|
||||
return false;
|
||||
if (scales->values.empty())
|
||||
{
|
||||
error = "invalid scales definition";
|
||||
return false;
|
||||
}
|
||||
if (stat == FeatureRule::NO_STAT)
|
||||
{
|
||||
error = "scaled features need a STAT measure to be defined";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else //no scales
|
||||
{
|
||||
//if (corePoints.origin != cloud1)
|
||||
//{
|
||||
// error = "feature with no scale must be computed/extracted from the core points origin cloud";
|
||||
// return false;
|
||||
//}
|
||||
}
|
||||
if (stat != FeatureRule::NO_STAT)
|
||||
{
|
||||
if (feature->getType() != Feature::Type::PointFeature)
|
||||
{
|
||||
error = "stat. measures can only be defined on Point features";
|
||||
error = "STAT measures can only be defined on Point features";
|
||||
return false;
|
||||
}
|
||||
if (!scales)
|
||||
{
|
||||
error = "stat. measures need at least one scale to be defined";
|
||||
error = "STAT measures need at least one scale to be defined";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (stat != FeatureRule::NO_OPERATION)
|
||||
if (op != FeatureRule::NO_OPERATION)
|
||||
{
|
||||
if (!scales)
|
||||
{
|
||||
error = "math operations can't be defined on scale-less features (SC0)";
|
||||
return false;
|
||||
}
|
||||
if (feature->getType() == Feature::Type::DualCloudFeature)
|
||||
{
|
||||
error = "math operation can't be defined on dual-cloud features";
|
||||
error = "math operations can't be defined on dual-cloud features";
|
||||
return false;
|
||||
}
|
||||
if (cloudCount < 2)
|
||||
|
||||
+1
-3
@@ -63,9 +63,7 @@ struct Feature
|
||||
, source(p_source)
|
||||
, scale(p_scale)
|
||||
, sourceName(p_sourceName)
|
||||
{
|
||||
assert(cloud);
|
||||
}
|
||||
{}
|
||||
|
||||
//! Associated cloud
|
||||
ccPointCloud* cloud;
|
||||
|
||||
@@ -27,6 +27,8 @@ class IScalarFieldWrapper
|
||||
public:
|
||||
virtual double pointValue(unsigned index) const = 0;
|
||||
virtual bool isValid() const = 0;
|
||||
virtual QString getName() const = 0;
|
||||
virtual unsigned size() const = 0;
|
||||
};
|
||||
|
||||
class ScalarFieldWrapper : public IScalarFieldWrapper
|
||||
@@ -38,11 +40,64 @@ public:
|
||||
|
||||
virtual inline double pointValue(unsigned index) const override { return m_sf->at(index); }
|
||||
virtual inline bool isValid() const { return m_sf != nullptr; }
|
||||
virtual inline QString getName() const { return m_sf->getName(); }
|
||||
virtual unsigned size() const { return m_sf->size(); }
|
||||
|
||||
protected:
|
||||
CCLib::ScalarField* m_sf;
|
||||
};
|
||||
|
||||
class ScalarFieldRatioWrapper : public IScalarFieldWrapper
|
||||
{
|
||||
public:
|
||||
ScalarFieldRatioWrapper(CCLib::ScalarField* sfp, CCLib::ScalarField* sfq, QString name)
|
||||
: m_sfp(sfp)
|
||||
, m_sfq(sfq)
|
||||
, m_name(name)
|
||||
{}
|
||||
|
||||
virtual inline double pointValue(unsigned index) const override
|
||||
{
|
||||
ScalarType p = m_sfp->getValue(index);
|
||||
ScalarType q = m_sfq->getValue(index);
|
||||
ScalarType ratio = (std::abs(q) > std::numeric_limits<ScalarType>::epsilon() ? p / q : NAN_VALUE);
|
||||
return ratio;
|
||||
}
|
||||
virtual inline bool isValid() const { return (m_sfp != nullptr && m_sfq != nullptr); }
|
||||
virtual inline QString getName() const { return m_name; }
|
||||
virtual inline unsigned size() const { return std::min(m_sfp->size(), m_sfq->size()); }
|
||||
|
||||
protected:
|
||||
CCLib::ScalarField *m_sfp, *m_sfq;
|
||||
QString m_name;
|
||||
};
|
||||
|
||||
class NormDipAndDipDirFieldWrapper : public IScalarFieldWrapper
|
||||
{
|
||||
public:
|
||||
enum Mode { Dip = 0, DipDir = 1 };
|
||||
|
||||
NormDipAndDipDirFieldWrapper(ccPointCloud* cloud, Mode mode)
|
||||
: m_cloud(cloud)
|
||||
, m_mode(mode)
|
||||
{}
|
||||
|
||||
virtual double pointValue(unsigned index) const override
|
||||
{
|
||||
const CCVector3& N = m_cloud->getPointNormal(index);
|
||||
PointCoordinateType dip_deg, dipDir_deg;
|
||||
ccNormalVectors::ConvertNormalToDipAndDipDir(N, dip_deg, dipDir_deg);
|
||||
return (m_mode == Dip ? dip_deg : dipDir_deg);
|
||||
}
|
||||
virtual inline bool isValid() const { return m_cloud != nullptr && m_cloud->hasNormals(); }
|
||||
virtual inline QString getName() const { static const char s_names[][14] = { "Norm dip", "Norm dip dir." }; return s_names[m_mode]; }
|
||||
virtual inline unsigned size() const { return m_cloud->size(); }
|
||||
|
||||
protected:
|
||||
ccPointCloud* m_cloud;
|
||||
Mode m_mode;
|
||||
};
|
||||
|
||||
class DimScalarFieldWrapper : public IScalarFieldWrapper
|
||||
{
|
||||
public:
|
||||
@@ -55,6 +110,8 @@ public:
|
||||
|
||||
virtual inline double pointValue(unsigned index) const override { return m_cloud->getPoint(index)->u[m_dim]; }
|
||||
virtual inline bool isValid() const { return m_cloud != nullptr; }
|
||||
virtual inline QString getName() const { static const char s_names[][5] = { "DimX", "DimY", "DimZ" }; return s_names[m_dim]; }
|
||||
virtual inline unsigned size() const { return m_cloud->size(); }
|
||||
|
||||
protected:
|
||||
ccPointCloud* m_cloud;
|
||||
@@ -73,6 +130,8 @@ public:
|
||||
|
||||
virtual inline double pointValue(unsigned index) const override { return m_cloud->getPointColor(index).rgb[m_band]; }
|
||||
virtual inline bool isValid() const { return m_cloud != nullptr && m_cloud->hasColors(); }
|
||||
virtual inline QString getName() const { static const char s_names[][6] = { "Red", "Green", "Blue" }; return s_names[m_band]; }
|
||||
virtual inline unsigned size() const { return m_cloud->size(); }
|
||||
|
||||
protected:
|
||||
ccPointCloud* m_cloud;
|
||||
|
||||
+141
-32
@@ -25,11 +25,13 @@
|
||||
|
||||
//qCC_db
|
||||
#include <ccPointCloud.h>
|
||||
#include <ccProgressDialog.h>
|
||||
|
||||
//Qt
|
||||
#include <QtGui>
|
||||
#include <QtCore>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
|
||||
q3DMASCPlugin::q3DMASCPlugin(QObject* parent/*=0*/)
|
||||
: QObject(parent)
|
||||
@@ -49,7 +51,8 @@ void q3DMASCPlugin::onNewSelection(const ccHObject::Container& selectedEntities)
|
||||
|
||||
if (m_trainAction)
|
||||
{
|
||||
m_trainAction->setEnabled(m_app && m_app->dbRootObject() && m_app->dbRootObject()->getChildrenNumber() != 0); //need some loaded entities to train the classifier!
|
||||
//m_trainAction->setEnabled(m_app && m_app->dbRootObject() && m_app->dbRootObject()->getChildrenNumber() != 0); //need some loaded entities to train the classifier!
|
||||
m_trainAction->setEnabled(true);
|
||||
}
|
||||
|
||||
m_selectedEntities = selectedEntities;
|
||||
@@ -114,14 +117,6 @@ void q3DMASCPlugin::doTrainAction()
|
||||
//ccPointCloud* cloud1 = static_cast<ccPointCloud*>(m_selectedEntities[0]);
|
||||
//ccPointCloud* cloud2 = static_cast<ccPointCloud*>(m_selectedEntities[1]);
|
||||
|
||||
if (m_selectedEntities.empty() || !m_selectedEntities.front()->isA(CC_TYPES::POINT_CLOUD))
|
||||
{
|
||||
m_app->dispToConsole("Select one and only one point cloud!", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
ccPointCloud* cloud = static_cast<ccPointCloud*>(m_selectedEntities.front());
|
||||
|
||||
masc::TrainParameters params;
|
||||
if (params.testDataRatio < 0 || params.testDataRatio > 0.99f)
|
||||
{
|
||||
@@ -130,33 +125,129 @@ void q3DMASCPlugin::doTrainAction()
|
||||
}
|
||||
|
||||
Feature::Set features;
|
||||
#if 0
|
||||
if (m_selectedEntities.empty() || !m_selectedEntities.front()->isA(CC_TYPES::POINT_CLOUD))
|
||||
{
|
||||
features.push_back(Feature::Shared(new PointFeature(cloud, PointFeature::Z, Feature::DimZ, "Z")));
|
||||
features.push_back(Feature::Shared(new PointFeature(cloud, PointFeature::Intensity, Feature::ScalarField, "Intensity")));
|
||||
m_app->dispToConsole("Select one and only one point cloud!", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
QString outputFilename = QCoreApplication::applicationDirPath() + "/classifier.yaml";
|
||||
ccPointCloud* cloud = static_cast<ccPointCloud*>(m_selectedEntities.front());
|
||||
|
||||
//features
|
||||
{
|
||||
Feature::Shared featureZ(new PointFeature(PointFeature::Z, cloud));
|
||||
features.push_back(featureZ);
|
||||
|
||||
Feature::Shared featureIntensity(new PointFeature(PointFeature::Intensity, cloud));
|
||||
features.push_back(featureIntensity);
|
||||
}
|
||||
#else
|
||||
QString inputFilename;
|
||||
{
|
||||
QSettings settings;
|
||||
settings.beginGroup("3DMASC");
|
||||
QString inputPath = settings.value("FilePath", QCoreApplication::applicationDirPath()).toString();
|
||||
inputFilename = QFileDialog::getOpenFileName(m_app->getMainWindow(), "Load 3DMASC script file", inputPath, "*.txt");
|
||||
if (inputFilename.isNull())
|
||||
{
|
||||
//process cancelled by the user
|
||||
return;
|
||||
}
|
||||
settings.setValue("FilePath", QFileInfo(inputFilename).absolutePath());
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
FeatureRule::Set rules;
|
||||
std::vector<ccPointCloud*> loadedClouds;
|
||||
masc::CorePoints corePoints;
|
||||
if (!masc::Tools::LoadFile(inputFilename, rules, loadedClouds, corePoints))
|
||||
{
|
||||
while (!loadedClouds.empty())
|
||||
{
|
||||
delete loadedClouds.back();
|
||||
loadedClouds.pop_back();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//add the loaded clouds to the main DB (so that we don't need to handle them anymore)
|
||||
ccHObject* group = new ccHObject("3DMASC");
|
||||
for (ccPointCloud* pc : loadedClouds)
|
||||
{
|
||||
group->addChild(pc);
|
||||
}
|
||||
|
||||
ccProgressDialog pDlg(true, m_app->getMainWindow());
|
||||
if (!corePoints.prepare(&pDlg))
|
||||
{
|
||||
m_app->dispToConsole("Failed to compute/prepare the core points!", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
delete group;
|
||||
return;
|
||||
}
|
||||
if (corePoints.cloud != corePoints.origin)
|
||||
{
|
||||
//auto-hide the other clouds
|
||||
for (ccPointCloud* pc : loadedClouds)
|
||||
{
|
||||
pc->setEnabled(false);
|
||||
}
|
||||
//set an explicit name for the core points
|
||||
QString corePointsName = corePoints.origin->getName();
|
||||
switch (corePoints.selectionMethod)
|
||||
{
|
||||
case masc::CorePoints::NONE:
|
||||
break;
|
||||
case masc::CorePoints::RANDOM:
|
||||
corePointsName += "_SS_Random@" + QString::number(corePoints.selectionParam);
|
||||
break;
|
||||
case masc::CorePoints::SPATIAL:
|
||||
corePointsName += "_SS_Spatial@" + QString::number(corePoints.selectionParam);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
corePoints.cloud->setName(QString("Core points (%1)").arg(corePointsName));
|
||||
group->addChild(corePoints.cloud);
|
||||
}
|
||||
m_app->addToDB(group);
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
pDlg.setAutoClose(false); //we don't want the progress dialog to 'pop' for each feature
|
||||
QString error;
|
||||
if (!masc::Tools::PrepareFeatures(rules, corePoints, features, error, &pDlg))
|
||||
{
|
||||
m_app->dispToConsole(error, ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
delete group;
|
||||
return;
|
||||
}
|
||||
pDlg.close();
|
||||
QCoreApplication::processEvents();
|
||||
pDlg.setAutoClose(true); //restore the default behavior of the progress dialog
|
||||
|
||||
#endif
|
||||
|
||||
//randomly select the training points
|
||||
QScopedPointer<CCLib::ReferenceCloud> trainSubset(new CCLib::ReferenceCloud(cloud));
|
||||
QScopedPointer<CCLib::ReferenceCloud> testSubset(new CCLib::ReferenceCloud(cloud));
|
||||
if (!masc::Tools::RandomSubset(cloud, params.testDataRatio, trainSubset.data(), testSubset.data()))
|
||||
QScopedPointer<CCLib::ReferenceCloud> trainSubset(new CCLib::ReferenceCloud(corePoints.cloud));
|
||||
QScopedPointer<CCLib::ReferenceCloud> testSubset(new CCLib::ReferenceCloud(corePoints.cloud));
|
||||
if (!masc::Tools::RandomSubset(corePoints.cloud, params.testDataRatio, trainSubset.data(), testSubset.data()))
|
||||
{
|
||||
m_app->dispToConsole("Not enough memory", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
masc::Classifier classifier;
|
||||
if (QFile(outputFilename).exists())
|
||||
{
|
||||
if (!classifier.fromFile(outputFilename, m_app->getMainWindow()))
|
||||
{
|
||||
m_app->dispToConsole("Failed to load previous classifier file", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
return;
|
||||
}
|
||||
m_app->dispToConsole("Previous classifier loaded", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
|
||||
}
|
||||
else
|
||||
//QString outputFilename = QCoreApplication::applicationDirPath() + "/classifier.yaml";
|
||||
//if (QFile(outputFilename).exists())
|
||||
//{
|
||||
// if (!classifier.fromFile(outputFilename, m_app->getMainWindow()))
|
||||
// {
|
||||
// m_app->dispToConsole("Failed to load previous classifier file", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
// return;
|
||||
// }
|
||||
// m_app->dispToConsole("Previous classifier loaded", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
|
||||
//}
|
||||
//else
|
||||
{
|
||||
QString errorMessage;
|
||||
if (!classifier.train(params.rt, features, errorMessage, trainSubset.data(), m_app->getMainWindow()))
|
||||
@@ -165,20 +256,38 @@ void q3DMASCPlugin::doTrainAction()
|
||||
return;
|
||||
}
|
||||
|
||||
QString outputFilename;
|
||||
{
|
||||
QSettings settings;
|
||||
settings.beginGroup("3DMASC");
|
||||
QString outputPath = settings.value("FilePath", QCoreApplication::applicationDirPath()).toString();
|
||||
outputFilename = QFileDialog::getSaveFileName(m_app->getMainWindow(), "Save 3DMASC classifier", outputPath, "*.yaml");
|
||||
if (outputFilename.isNull())
|
||||
{
|
||||
//process cancelled by the user
|
||||
return;
|
||||
}
|
||||
settings.setValue("FilePath", QFileInfo(outputFilename).absolutePath());
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
//save the classifier
|
||||
classifier.toFile(outputFilename, m_app->getMainWindow());
|
||||
m_app->dispToConsole("Classifier succesfully created", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
|
||||
}
|
||||
|
||||
masc::Classifier::AccuracyMetrics metrics;
|
||||
QString errorMessage;
|
||||
if (!classifier.evaluate(features, testSubset.data(), metrics, errorMessage, m_app->getMainWindow()))
|
||||
//test classifier
|
||||
{
|
||||
m_app->dispToConsole(errorMessage, ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
return;
|
||||
}
|
||||
masc::Classifier::AccuracyMetrics metrics;
|
||||
QString errorMessage;
|
||||
if (!classifier.evaluate(features, testSubset.data(), metrics, errorMessage, m_app->getMainWindow()))
|
||||
{
|
||||
m_app->dispToConsole(errorMessage, ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
m_app->dispToConsole(QString("Correct = %1 / %2 --> accuracy = %3").arg(metrics.goodGuess).arg(metrics.sampleCount).arg(metrics.ratio), ccMainAppInterface::STD_CONSOLE_MESSAGE);
|
||||
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)
|
||||
|
||||
+513
-296
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -19,12 +19,13 @@
|
||||
|
||||
//Local
|
||||
#include "Features.h"
|
||||
|
||||
//qCC_db
|
||||
#include <ccPointCloud.h>
|
||||
#include "CorePoints.h"
|
||||
|
||||
//CCLib
|
||||
#include <ReferenceCloud.h>
|
||||
#include <GenericProgressCallback.h>
|
||||
|
||||
//qCC_db
|
||||
class ccPointCloud;
|
||||
|
||||
class QWidget;
|
||||
|
||||
@@ -35,9 +36,9 @@ namespace masc
|
||||
{
|
||||
public:
|
||||
|
||||
static bool LoadFile(QString filename, ccPointCloud* pc1, ccPointCloud* pc2, FeatureRule::Set& features);
|
||||
static bool LoadFile(QString filename, FeatureRule::Set& features, std::vector<ccPointCloud*>& loadedClouds, CorePoints& corePoints);
|
||||
|
||||
static bool PrepareFeatures(const FeatureRule::Set& rules, Feature::Set& features, QString& error);
|
||||
static bool PrepareFeatures(const FeatureRule::Set& rules, const CorePoints& corePoints, Feature::Set& features, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr);
|
||||
|
||||
static bool RandomSubset(ccPointCloud* cloud, float ratio, CCLib::ReferenceCloud* inRatioSubset, CCLib::ReferenceCloud* outRatioSubset);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user