mirror of
https://github.com/dgirardeau/q3DMASC.git
synced 2026-08-29 08:34:48 +08:00
Prevent the user from cancelling the training.
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ if (INSTALL_Q3DMASC_PLUGIN)
|
||||
project( Q3DMASC_PLUGIN )
|
||||
|
||||
AddPlugin( NAME ${PROJECT_NAME} )
|
||||
set(Q3DMASC_PLUGIN_VERSION "0.7+")
|
||||
set(Q3DMASC_PLUGIN_VERSION "0.8")
|
||||
|
||||
include( CMakePolicies NO_POLICY_SCOPE )
|
||||
|
||||
|
||||
@@ -51,12 +51,12 @@ CCCoreLib::ScalarField* Feature::PrepareSF(ccPointCloud* cloud, const char* resu
|
||||
int sfIdx = cloud->getScalarFieldIndexByName(resultSFName);
|
||||
if (sfIdx >= 0)
|
||||
{
|
||||
ccLog::Warning("Existing SF: " + QString(resultSFName) + ", do not store in generatedScalarFields");
|
||||
// ccLog::Warning("Existing SF: " + QString(resultSFName) + ", do not store in generatedScalarFields");
|
||||
resultSF = cloud->getScalarField(sfIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
ccLog::Warning("SF does not exist, create it: " + QString(resultSFName) + ", SFCollector::Behavior " + QString::number(behavior));
|
||||
// ccLog::Warning("SF does not exist, create it: " + QString(resultSFName) + ", SFCollector::Behavior " + QString::number(behavior));
|
||||
ccScalarField* newSF = new ccScalarField(resultSFName);
|
||||
if (!newSF->resizeSafe(cloud->size()))
|
||||
{
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
void SFCollector::push(ccPointCloud* cloud, CCCoreLib::ScalarField* sf, Behavior behavior)
|
||||
{
|
||||
assert(!scalarFields.contains(sf));
|
||||
if (scalarFields.contains(sf))
|
||||
ccLog::Warning(QString("[SFCollector] scalar field '%1' HAS ALREADY BEEN COLLECTED").arg(sf->getName()) + ", behaviour " + QString::number(behavior));
|
||||
else
|
||||
ccLog::Warning(QString("[SFCollector] collect scalar field '%1'").arg(sf->getName()) + ", behaviour " + QString::number(behavior));
|
||||
// if (scalarFields.contains(sf))
|
||||
// ccLog::Warning(QString("[SFCollector] scalar field '%1' HAS ALREADY BEEN COLLECTED").arg(sf->getName()) + ", behaviour " + QString::number(behavior));
|
||||
// else
|
||||
// ccLog::Warning(QString("[SFCollector] collect scalar field '%1'").arg(sf->getName()) + ", behaviour " + QString::number(behavior));
|
||||
SFDesc desc;
|
||||
desc.behavior = behavior;
|
||||
desc.cloud = cloud;
|
||||
@@ -48,7 +48,7 @@ void SFCollector::releaseSFs(bool keepByDefault)
|
||||
|
||||
if (desc.behavior == ALWAYS_KEEP || (keepByDefault && desc.behavior == CAN_REMOVE))
|
||||
{
|
||||
ccLog::Warning(QString("[SFCollector] Keep scalar field '%1'").arg(sf->getName()));
|
||||
// ccLog::Warning(QString("[SFCollector] Keep scalar field '%1'").arg(sf->getName()));
|
||||
//keep this SF
|
||||
continue;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ void SFCollector::releaseSFs(bool keepByDefault)
|
||||
int sfIdx = desc.cloud->getScalarFieldIndexByName(sf->getName());
|
||||
if (sfIdx >= 0)
|
||||
{
|
||||
ccLog::Warning(QString("[SFCollector] Remove scalar field '%1'").arg(sf->getName()));
|
||||
// ccLog::Warning(QString("[SFCollector] Remove scalar field '%1'").arg(sf->getName()));
|
||||
desc.cloud->deleteScalarField(sfIdx);
|
||||
}
|
||||
else
|
||||
@@ -72,9 +72,9 @@ bool SFCollector::setBehavior(CCCoreLib::ScalarField *sf, Behavior behavior)
|
||||
{
|
||||
if (scalarFields.contains(sf))
|
||||
{
|
||||
Behavior previousBehavior = scalarFields[sf].behavior;
|
||||
// Behavior previousBehavior = scalarFields[sf].behavior;
|
||||
scalarFields[sf].behavior = behavior;
|
||||
ccLog::Warning("behavior of " + QString(sf->getName()) + " changed from " + QString::number(previousBehavior) + " to " + QString::number(behavior));
|
||||
// ccLog::Warning("behavior of " + QString(sf->getName()) + " changed from " + QString::number(previousBehavior) + " to " + QString::number(behavior));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -229,8 +229,6 @@ void ConfusionMatrix::compute(const std::vector<ScalarType>& actual, const std::
|
||||
// FILL THE QTABLEWIDGET
|
||||
|
||||
// add the confusion matrix values
|
||||
QBrush greenBrush(QColorConstants::Svg::palegreen);
|
||||
QFont f( "Tahoma", 10, QFont::Bold );
|
||||
for (int row = 0; row < nbClasses; row++)
|
||||
for (int column = 0; column < nbClasses; column++)
|
||||
{
|
||||
@@ -239,7 +237,6 @@ void ConfusionMatrix::compute(const std::vector<ScalarType>& actual, const std::
|
||||
if (row == column)
|
||||
{
|
||||
newItem->setBackground(getColor(val / vec_TP_FN.at<int>(row, 0), 0, 128, 255));
|
||||
newItem->setFont(f);
|
||||
}
|
||||
else
|
||||
newItem->setBackground(getColor(val / vec_TP_FN.at<int>(row, 0), 200, 50, 50));
|
||||
|
||||
+2
-2
@@ -507,7 +507,7 @@ void q3DMASCPlugin::doTrainAction()
|
||||
{
|
||||
progressDlg.setAutoClose(false); //we don't want the progress dialog to 'pop' for each feature
|
||||
QString error;
|
||||
if (!masc::Tools::PrepareFeatures(corePoints, toPrepare, error, &progressDlg, &generatedScalarFields))
|
||||
if (!masc::Tools::PrepareFeatures(corePoints, toPrepare, error, &progressDlg, &generatedScalarFields))
|
||||
{
|
||||
m_app->dispToConsole(error, ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
generatedScalarFields.releaseSFs(false);
|
||||
@@ -655,7 +655,7 @@ void q3DMASCPlugin::doTrainAction()
|
||||
errorMessage,
|
||||
trainDlg,
|
||||
testCloud ? nullptr : testSubset.data(),
|
||||
"Classification_pred",
|
||||
testCloud ? "Classification_prediction" : "", // outputSFName, empty is the test cloud is not a separate cloud
|
||||
m_app->getMainWindow()))
|
||||
{
|
||||
m_app->dispToConsole(errorMessage, ccMainAppInterface::ERR_CONSOLE_MESSAGE);
|
||||
|
||||
+71
-37
@@ -37,6 +37,7 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QProgressDialog>
|
||||
#include <QtConcurrent>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "qTrain3DMASCDialog.h"
|
||||
#include "confusionmatrix.h"
|
||||
@@ -123,28 +124,30 @@ bool Classifier::classify( const Feature::Source::Set& featureSources,
|
||||
return false;
|
||||
}
|
||||
|
||||
//look for the classification field
|
||||
CCCoreLib::ScalarField* classificationSF = Tools::GetClassificationSF(cloud);
|
||||
// add a ccConfidence value if needed
|
||||
int cvConfidenceIdx = cloud->getScalarFieldIndexByName("Classification_confidence");
|
||||
if (cvConfidenceIdx > 0) // if the scalar field exists, delete it
|
||||
if (cvConfidenceIdx >= 0) // if the scalar field exists, delete it
|
||||
cloud->deleteScalarField(cvConfidenceIdx);
|
||||
cvConfidenceIdx = cloud->addScalarField("Classification_confidence");
|
||||
CCCoreLib::ScalarField* cvConfidenceSF = cloud->getScalarField(cvConfidenceIdx);
|
||||
|
||||
//look for the classification field
|
||||
CCCoreLib::ScalarField* classificationSF = Tools::GetClassificationSF(cloud);
|
||||
ccScalarField* classifSFBackup = nullptr;
|
||||
|
||||
if (classificationSF)
|
||||
if (classificationSF) //save classification field (if any)
|
||||
{
|
||||
//save previous classification field (if any)
|
||||
int sfIdx = cloud->getScalarFieldIndexByName("Classification_prev");
|
||||
if (sfIdx > 0)
|
||||
ccLog::Warning("Classification SF found: copy it in Classification_backup, a confusion matrix will be generated");
|
||||
// delete Classification_backup field (if any)
|
||||
int sfIdx = cloud->getScalarFieldIndexByName("Classification_backup");
|
||||
if (sfIdx >= 0)
|
||||
cloud->deleteScalarField(sfIdx);
|
||||
|
||||
// backup the classification field
|
||||
try
|
||||
{
|
||||
classifSFBackup = new ccScalarField(*static_cast<ccScalarField*>(classificationSF));
|
||||
classifSFBackup->setName("Classification_prev");
|
||||
classifSFBackup = new ccScalarField(*static_cast<ccScalarField*>(classificationSF)); // copy constructor
|
||||
classifSFBackup->setName("Classification_backup");
|
||||
cloud->addScalarField(classifSFBackup);
|
||||
}
|
||||
catch (const std::bad_alloc)
|
||||
@@ -237,7 +240,7 @@ bool Classifier::classify( const Feature::Source::Set& featureSources,
|
||||
cv::Mat result;
|
||||
m_rtrees->getVotes(test_data, result, cv::ml::DTrees::PREDICT_MAX_VOTE);
|
||||
int classIndex = -1;
|
||||
for (int col = 0; col < result.cols; col++)
|
||||
for (int col = 0; col < result.cols; col++) // look for the index of the predicted class
|
||||
if (predictedClass == result.at<int>(0, col))
|
||||
{
|
||||
classIndex = col;
|
||||
@@ -245,8 +248,8 @@ bool Classifier::classify( const Feature::Source::Set& featureSources,
|
||||
}
|
||||
if (classIndex != -1)
|
||||
{
|
||||
float nbVotes = result.at<int>(1, classIndex);
|
||||
cvConfidenceSF->setValue(i, static_cast<ScalarType>(nbVotes / numberOfTrees));
|
||||
float nbVotes = result.at<int>(1, classIndex); // get the number of votes
|
||||
cvConfidenceSF->setValue(i, static_cast<ScalarType>(nbVotes / numberOfTrees)); // compute the confidence
|
||||
}
|
||||
else
|
||||
cvConfidenceSF->setValue(i, CCCoreLib::NAN_VALUE);
|
||||
@@ -326,28 +329,31 @@ bool Classifier::evaluate(const Feature::Source::Set& featureSources,
|
||||
return false;
|
||||
}
|
||||
|
||||
CCCoreLib::ScalarField* outputSF = nullptr;
|
||||
CCCoreLib::ScalarField* outSF = nullptr;
|
||||
CCCoreLib::ScalarField* cvConfidenceSF = nullptr;
|
||||
|
||||
ccLog::Warning("[evaluate] TEST cloud " + testCloud->getName());
|
||||
|
||||
if (!outputSFName.isEmpty())
|
||||
{
|
||||
int outSFIndex = testCloud->getScalarFieldIndexByName(qPrintable(outputSFName));
|
||||
if (outSFIndex < 0)
|
||||
{
|
||||
ccScalarField* _outputSF = new ccScalarField(qPrintable(outputSFName));
|
||||
if (!_outputSF->resizeSafe(testCloud->size()))
|
||||
{
|
||||
errorMessage = QObject::tr("Not enough memory to create output scalar field");
|
||||
_outputSF->release();
|
||||
return false;
|
||||
}
|
||||
testCloud->addScalarField(_outputSF);
|
||||
outputSF = _outputSF;
|
||||
}
|
||||
int outIdx = testCloud->getScalarFieldIndexByName(qPrintable(outputSFName));
|
||||
if (outIdx >= 0)
|
||||
testCloud->deleteScalarField(outIdx);
|
||||
else
|
||||
{
|
||||
outputSF = testCloud->getScalarField(outSFIndex);
|
||||
}
|
||||
outputSF->fill(CCCoreLib::NAN_VALUE);
|
||||
outputSF->computeMinAndMax();
|
||||
ccLog::Warning("add " + outputSFName + " to the TEST cloud");
|
||||
outIdx = testCloud->addScalarField(qPrintable(outputSFName));
|
||||
outSF = testCloud->getScalarField(outIdx);
|
||||
}
|
||||
|
||||
if (outSF) // add a Classification_confidence value to the test cloud if needed
|
||||
{
|
||||
int cvConfidenceIdx = testCloud->getScalarFieldIndexByName("Classification_confidence");
|
||||
if (cvConfidenceIdx >= 0) // if the scalar field exists, delete it
|
||||
testCloud->deleteScalarField(cvConfidenceIdx);
|
||||
else
|
||||
ccLog::Warning("add Classification_confidence to the TEST cloud");
|
||||
cvConfidenceIdx = testCloud->addScalarField("Classification_confidence");
|
||||
cvConfidenceSF = testCloud->getScalarField(cvConfidenceIdx);
|
||||
}
|
||||
|
||||
unsigned testSampleCount = (testSubset ? testSubset->size() : testCloud->size());
|
||||
@@ -397,6 +403,7 @@ bool Classifier::evaluate(const Feature::Source::Set& featureSources,
|
||||
}
|
||||
}
|
||||
|
||||
int numberOfTrees = m_rtrees->getRoots().size();
|
||||
|
||||
//estimate the efficiency of the classifier
|
||||
std::vector<ScalarType> actualClass(testSampleCount);
|
||||
@@ -424,9 +431,29 @@ bool Classifier::evaluate(const Feature::Source::Set& featureSources,
|
||||
{
|
||||
++metrics.goodGuess;
|
||||
}
|
||||
if (outputSF)
|
||||
if (outSF)
|
||||
{
|
||||
outputSF->setValue(pointIndex, static_cast<ScalarType>(iPredictedClass));
|
||||
outSF->setValue(pointIndex, static_cast<ScalarType>(iPredictedClass));
|
||||
if (cvConfidenceSF)
|
||||
{
|
||||
// compute the confidence
|
||||
cv::Mat result;
|
||||
m_rtrees->getVotes(test_data.row(i), result, cv::ml::DTrees::PREDICT_MAX_VOTE);
|
||||
int classIndex = -1;
|
||||
for (int col = 0; col < result.cols; col++) // look for the index of the predicted class
|
||||
if (iPredictedClass == result.at<int>(0, col))
|
||||
{
|
||||
classIndex = col;
|
||||
break;
|
||||
}
|
||||
if (classIndex != -1)
|
||||
{
|
||||
float nbVotes = result.at<int>(1, classIndex); // get the number of votes
|
||||
cvConfidenceSF->setValue(i, static_cast<ScalarType>(nbVotes / numberOfTrees)); // compute the confidence
|
||||
}
|
||||
else
|
||||
cvConfidenceSF->setValue(i, CCCoreLib::NAN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (pDlg && !nProgress.oneStep())
|
||||
@@ -436,8 +463,10 @@ bool Classifier::evaluate(const Feature::Source::Set& featureSources,
|
||||
}
|
||||
}
|
||||
|
||||
if (outputSF)
|
||||
outputSF->computeMinAndMax();
|
||||
if (outSF)
|
||||
outSF->computeMinAndMax();
|
||||
if (cvConfidenceSF)
|
||||
cvConfidenceSF->computeMinAndMax();
|
||||
|
||||
metrics.ratio = static_cast<float>(metrics.goodGuess) / metrics.sampleCount;
|
||||
}
|
||||
@@ -617,8 +646,13 @@ bool Classifier::train( const ccPointCloud* cloud,
|
||||
{
|
||||
if (pDlg->wasCanceled())
|
||||
{
|
||||
future.cancel();
|
||||
break;
|
||||
// future.cancel();
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("The training is still in progress, not possible to cancel.");
|
||||
msgBox.exec();
|
||||
// break;
|
||||
pDlg->reset();
|
||||
pDlg->show();
|
||||
}
|
||||
pDlg->setValue(pDlg->value() + 1);
|
||||
}
|
||||
|
||||
+25
-19
@@ -1060,9 +1060,8 @@ 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))
|
||||
if (!feature->prepare(corePoints, errorStr, progressCb, generatedScalarFields))
|
||||
{
|
||||
//something failed (error should be up to date)
|
||||
return false;
|
||||
@@ -1089,18 +1088,21 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features
|
||||
fas.scales.push_back(feature->scale);
|
||||
}
|
||||
}
|
||||
|
||||
//build the scaled feature list attached to the second cloud (if any)
|
||||
if (feature->cloud2 && feature->cloud2 != feature->cloud1 && feature->op != Feature::NO_OPERATION
|
||||
&& !static_cast<PointFeature*>(feature.data())->statSF1WasAlreadyExisting
|
||||
&& !static_cast<PointFeature*>(feature.data())->statSF2WasAlreadyExisting) // nothing to compute if the scalar field was already there
|
||||
if (feature->cloud2
|
||||
&& feature->cloud2 != feature->cloud1
|
||||
&& feature->op != Feature::NO_OPERATION
|
||||
&& !static_cast<PointFeature*>(feature.data())->statSF1WasAlreadyExisting) // nothing to compute if the scalar field was already there
|
||||
{
|
||||
FeaturesAndScales& fas = cloudsWithScaledFeatures[feature->cloud2];
|
||||
++fas.featureCount;
|
||||
fas.pointFeaturesPerScale[feature->scale].push_back(qSharedPointerCast<PointFeature>(feature));
|
||||
if (std::find(fas.scales.begin(), fas.scales.end(), feature->scale) == fas.scales.end())
|
||||
if (!static_cast<PointFeature*>(feature.data())->statSF2WasAlreadyExisting)
|
||||
{
|
||||
fas.scales.push_back(feature->scale);
|
||||
FeaturesAndScales& fas = cloudsWithScaledFeatures[feature->cloud2];
|
||||
++fas.featureCount;
|
||||
fas.pointFeaturesPerScale[feature->scale].push_back(qSharedPointerCast<PointFeature>(feature));
|
||||
if (std::find(fas.scales.begin(), fas.scales.end(), feature->scale) == fas.scales.end())
|
||||
{
|
||||
fas.scales.push_back(feature->scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1123,16 +1125,20 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features
|
||||
}
|
||||
|
||||
//build the scaled feature list attached to the second cloud (if any)
|
||||
if (feature->cloud2 && feature->cloud2 != feature->cloud1 && feature->op != Feature::NO_OPERATION
|
||||
&& !static_cast<NeighborhoodFeature*>(feature.data())->sf1WasAlreadyExisting
|
||||
&& !static_cast<NeighborhoodFeature*>(feature.data())->sf2WasAlreadyExisting) // nothing to compute if the scalar field was already there
|
||||
if (feature->cloud2
|
||||
&& feature->cloud2 != feature->cloud1
|
||||
&& feature->op != Feature::NO_OPERATION
|
||||
&& !static_cast<NeighborhoodFeature*>(feature.data())->sf1WasAlreadyExisting) // nothing to compute if the scalar field was already there
|
||||
{
|
||||
FeaturesAndScales& fas = cloudsWithScaledFeatures[feature->cloud2];
|
||||
fas.neighborhoodFeaturesPerScale[feature->scale].push_back(qSharedPointerCast<NeighborhoodFeature>(feature));
|
||||
++fas.featureCount;
|
||||
if (std::find(fas.scales.begin(), fas.scales.end(), feature->scale) == fas.scales.end())
|
||||
if (!static_cast<NeighborhoodFeature*>(feature.data())->sf2WasAlreadyExisting)
|
||||
{
|
||||
fas.scales.push_back(feature->scale);
|
||||
FeaturesAndScales& fas = cloudsWithScaledFeatures[feature->cloud2];
|
||||
fas.neighborhoodFeaturesPerScale[feature->scale].push_back(qSharedPointerCast<NeighborhoodFeature>(feature));
|
||||
++fas.featureCount;
|
||||
if (std::find(fas.scales.begin(), fas.scales.end(), feature->scale) == fas.scales.end())
|
||||
{
|
||||
fas.scales.push_back(feature->scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user