init
This commit is contained in:
+218
@@ -0,0 +1,218 @@
|
||||
#include "ovpCAlgorithmConfusionMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
#ifdef DEBUG
|
||||
static void dumpMatrix(Kernel::ILogManager& mng, const CMatrix& mat, const CString& desc)
|
||||
{
|
||||
mng << Kernel::LogLevel_Info << desc << "\n";
|
||||
for (size_t i = 0; i < mat.getDimensionSize(0); i++)
|
||||
{
|
||||
mng << Kernel::LogLevel_Info << "Row " << i << ": ";
|
||||
for (size_t j = 0; j < mat.getDimensionSize(1); j++) { mng << mat.getBuffer()[i * mat.getDimensionSize(1) + j] << " "; }
|
||||
mng << "\n";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CAlgorithmConfusionMatrix::initialize()
|
||||
{
|
||||
ip_targetStimSet.initialize(getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_TargetStimulationSet));
|
||||
ip_classifierStimSet.initialize(getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassifierStimulationSet));
|
||||
ip_classesCodes.initialize(getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassCodes));
|
||||
ip_usePercentages.initialize(getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Percentage));
|
||||
ip_useSums.initialize(getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Sums));
|
||||
op_confusionMatrix.initialize(getOutputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputParameterId_ConfusionMatrix));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmConfusionMatrix::uninitialize()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
dumpMatrix(this->getLogManager(), m_confusionMatrix, "Confusion matrix");
|
||||
#endif
|
||||
|
||||
ip_targetStimSet.uninitialize();
|
||||
ip_classifierStimSet.uninitialize();
|
||||
ip_classesCodes.uninitialize();
|
||||
ip_usePercentages.uninitialize();
|
||||
ip_useSums.uninitialize();
|
||||
op_confusionMatrix.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmConfusionMatrix::process()
|
||||
{
|
||||
const size_t nClass = size_t(ip_classesCodes->getStimulationCount());
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetTarget))
|
||||
{
|
||||
for (size_t i = 0; i < ip_classesCodes->getStimulationCount(); ++i)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "class code " << i << ": " << ip_classesCodes->getStimulationIdentifier(i) << "\n";
|
||||
}
|
||||
|
||||
m_nClassificationAttemptPerClass.clear();
|
||||
for (size_t i = 0; i < ip_classesCodes->getStimulationCount(); ++i)
|
||||
{
|
||||
m_nClassificationAttemptPerClass.insert(std::make_pair(ip_classesCodes->getStimulationIdentifier(i), 0));
|
||||
}
|
||||
|
||||
if (ip_useSums) { op_confusionMatrix->resize(nClass + 1, nClass + 1); }
|
||||
else { op_confusionMatrix->resize(nClass, nClass); }
|
||||
|
||||
for (size_t i = 0; i < nClass; ++i)
|
||||
{
|
||||
const char* name = this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation,
|
||||
ip_classesCodes->getStimulationIdentifier(i)).toASCIIString();
|
||||
op_confusionMatrix->setDimensionLabel(0, i, (std::string("Target Class\n") + name));
|
||||
op_confusionMatrix->setDimensionLabel(1, i, (std::string("Result Class\n") + name));
|
||||
}
|
||||
|
||||
if (ip_useSums)
|
||||
{
|
||||
op_confusionMatrix->setDimensionLabel(0, nClass, "Sums");
|
||||
op_confusionMatrix->setDimensionLabel(1, nClass, "Sums");
|
||||
}
|
||||
|
||||
m_confusionMatrix.resize(nClass, nClass);
|
||||
|
||||
// initialization
|
||||
for (size_t i = 0; i < op_confusionMatrix->getDimensionSize(0); ++i)
|
||||
{
|
||||
for (size_t j = 0; j < op_confusionMatrix->getDimensionSize(1); ++j)
|
||||
{
|
||||
op_confusionMatrix->getBuffer()[i * op_confusionMatrix->getDimensionSize(1) + j] = 0.0;
|
||||
if (i < m_confusionMatrix.getDimensionSize(0) && j < m_confusionMatrix.getDimensionSize(1))
|
||||
{
|
||||
m_confusionMatrix.getBuffer()[i * m_confusionMatrix.getDimensionSize(1) + j] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetClassifier)) { }
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedTarget))
|
||||
{
|
||||
for (size_t s = 0; s < ip_targetStimSet->getStimulationCount(); ++s)
|
||||
{
|
||||
uint64_t id = ip_targetStimSet->getStimulationIdentifier(s);
|
||||
if (isClass(id))
|
||||
{
|
||||
uint64_t date = ip_targetStimSet->getStimulationDate(s);
|
||||
m_targetsTimeLines.insert(std::pair<uint64_t, uint64_t>(date, id));
|
||||
getLogManager() << Kernel::LogLevel_Trace << "Current target is " << m_targetsTimeLines.rbegin()->second << "\n";
|
||||
}
|
||||
else { getLogManager() << Kernel::LogLevel_Trace << "The target received is not a valid class: " << id << "\n"; }
|
||||
}
|
||||
}
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedClassifier))
|
||||
{
|
||||
for (size_t s = 0; s < ip_classifierStimSet->getStimulationCount(); ++s)
|
||||
{
|
||||
//We need to locate the stimulation on the timeline
|
||||
uint64_t id = ip_classifierStimSet->getStimulationIdentifier(s);
|
||||
if (!isClass(id))//If we don't have
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_Trace << "The result received is not a valid class: " << id << "\n";
|
||||
continue;
|
||||
}
|
||||
uint64_t targeted = 0;
|
||||
const uint64_t date = ip_classifierStimSet->getStimulationDate(s);
|
||||
|
||||
bool found = false;
|
||||
for (auto it = m_targetsTimeLines.begin(); it != m_targetsTimeLines.end() && !found; ++it)
|
||||
{
|
||||
auto nextTarget = it;
|
||||
++nextTarget;
|
||||
if ((nextTarget == m_targetsTimeLines.end() || date < nextTarget->first) && date > it->first)
|
||||
{
|
||||
targeted = it->second;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Result received : " << id << ". Corresponding target : " << targeted << ".\n";
|
||||
|
||||
if (!op_confusionMatrix->getBuffer())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The confusion matrix buffer has not yet been initialized\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// now we found the target, let's update the confusion matrix
|
||||
// we need to update the whole line vector for the targeted class
|
||||
const size_t nOldAttempt = m_nClassificationAttemptPerClass[targeted];
|
||||
m_nClassificationAttemptPerClass[targeted]++; // the confusion matrix can treat this result
|
||||
|
||||
size_t i = getClassIndex(targeted);// the good line index
|
||||
const size_t resultIdx = getClassIndex(id);
|
||||
for (size_t j = 0; j < nClass; ++j)
|
||||
{
|
||||
double newValue = 0.0;
|
||||
const double oldValue = op_confusionMatrix->getBuffer()[i * op_confusionMatrix->getDimensionSize(0) + j];
|
||||
if (j == resultIdx)
|
||||
{
|
||||
newValue = (oldValue * nOldAttempt + 1) / (m_nClassificationAttemptPerClass[targeted]);
|
||||
m_confusionMatrix.getBuffer()[i * nClass + j]++;
|
||||
}
|
||||
else { newValue = (oldValue * nOldAttempt) / (m_nClassificationAttemptPerClass[targeted]); }
|
||||
if (ip_usePercentages) { op_confusionMatrix->getBuffer()[i * op_confusionMatrix->getDimensionSize(0) + j] = newValue; }
|
||||
else // the count value
|
||||
{
|
||||
op_confusionMatrix->getBuffer()[i * op_confusionMatrix->getDimensionSize(0) + j] = m_confusionMatrix.getBuffer()[i * nClass + j];
|
||||
}
|
||||
}
|
||||
|
||||
//we compute the sums if needed
|
||||
if (ip_useSums)
|
||||
{
|
||||
const size_t size = op_confusionMatrix->getDimensionSize(0);
|
||||
double total = 0.0;
|
||||
for (i = 0; i < nClass; ++i)
|
||||
{
|
||||
double sumRow = 0.0;
|
||||
double sumCol = 0.0;
|
||||
for (size_t j = 0; j < nClass; ++j)
|
||||
{
|
||||
sumRow += op_confusionMatrix->getBuffer()[i * size + j];
|
||||
sumCol += op_confusionMatrix->getBuffer()[j * size + i];
|
||||
}
|
||||
op_confusionMatrix->getBuffer()[i * size + size - 1] = sumRow;
|
||||
op_confusionMatrix->getBuffer()[(size - 1) * size + i] = sumCol;
|
||||
total += sumRow;
|
||||
}
|
||||
op_confusionMatrix->getBuffer()[(size - 1) * size + size - 1] =
|
||||
total; // the lower-right entry, i.e. the last in the buffer
|
||||
}
|
||||
}
|
||||
else { getLogManager() << Kernel::LogLevel_Warning << " No target available.\n"; }
|
||||
}
|
||||
this->activateOutputTrigger(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputTriggerId_ConfusionPerformed, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmConfusionMatrix::isClass(const uint64_t id) const
|
||||
{
|
||||
for (size_t i = 0; i < ip_classesCodes->getStimulationCount(); ++i) { if (ip_classesCodes->getStimulationIdentifier(i) == id) { return true; } }
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t CAlgorithmConfusionMatrix::getClassIndex(const uint64_t id) const
|
||||
{
|
||||
for (size_t i = 0; i < ip_classesCodes->getStimulationCount(); ++i) { if (ip_classesCodes->getStimulationIdentifier(i) == id) { return i; } }
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
class CAlgorithmConfusionMatrix final : virtual public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_ConfusionMatrix)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<bool> ip_usePercentages;
|
||||
Kernel::TParameterHandler<bool> ip_useSums;
|
||||
|
||||
// input TARGET
|
||||
Kernel::TParameterHandler<IStimulationSet*> ip_targetStimSet;
|
||||
// deduced timeline:
|
||||
std::map<uint64_t, uint64_t> m_targetsTimeLines;
|
||||
|
||||
// input CLASSIFIER
|
||||
Kernel::TParameterHandler<IStimulationSet*> ip_classifierStimSet;
|
||||
|
||||
//CONFUSION MATRIX computing
|
||||
Kernel::TParameterHandler<IStimulationSet*> ip_classesCodes;
|
||||
Kernel::TParameterHandler<CMatrix*> op_confusionMatrix;
|
||||
|
||||
CMatrix m_confusionMatrix; // the values, not percentage
|
||||
std::map<uint64_t, size_t> m_nClassificationAttemptPerClass;
|
||||
|
||||
private:
|
||||
|
||||
bool isClass(const uint64_t id) const;
|
||||
size_t getClassIndex(const uint64_t id) const;
|
||||
};
|
||||
|
||||
class CAlgorithmConfusionMatrixDesc final : virtual public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Confusion Matrix Algorithm"); }
|
||||
CString getAuthorName() const override { return CString("Laurent Bonnet"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Make a confusion matrix out of classification results coming from one classifier."); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Classification"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_ConfusionMatrix; }
|
||||
IPluginObject* create() override { return new CAlgorithmConfusionMatrix; }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_TargetStimulationSet, "Targets",
|
||||
Kernel::ParameterType_StimulationSet);
|
||||
prototype.addInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassifierStimulationSet, "Classification results",
|
||||
Kernel::ParameterType_StimulationSet);
|
||||
prototype.addInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassCodes, "Class codes", Kernel::ParameterType_StimulationSet);
|
||||
prototype.addInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Percentage, "Percentage", Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Sums, "Sums", Kernel::ParameterType_Boolean);
|
||||
|
||||
prototype.addOutputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputParameterId_ConfusionMatrix, "Confusion matrix",
|
||||
Kernel::ParameterType_Matrix);
|
||||
|
||||
prototype.addInputTrigger(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetTarget, "Reset Target");
|
||||
prototype.addInputTrigger(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetClassifier, "Reset Classifier");
|
||||
prototype.addInputTrigger(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedTarget, "Feed Target");
|
||||
prototype.addInputTrigger(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedClassifier, "Feed Classifier");
|
||||
|
||||
prototype.addOutputTrigger(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputTriggerId_ConfusionPerformed, "Confusion computing performed");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_ConfusionMatrixDesc)
|
||||
};
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
#include "ovpCBoxAlgorithmClassifierAccuracyMeasure.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
static void reset_scores_button_cb(GtkToolButton* /*button*/, gpointer data)
|
||||
{
|
||||
for (auto& progress : static_cast<CBoxAlgorithmClassifierAccuracyMeasure*>(data)->m_ProgressBar)
|
||||
{
|
||||
progress.score = 0;
|
||||
progress.nStimulation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_percentages_toggle_button_cb(GtkToggleToolButton* button, gpointer data)
|
||||
{
|
||||
static_cast<CBoxAlgorithmClassifierAccuracyMeasure*>(data)->m_ShowPercentages = (gtk_toggle_tool_button_get_active(button) ? true : false);
|
||||
}
|
||||
|
||||
static void show_scores_toggle_button_cb(GtkToggleToolButton* button, gpointer data)
|
||||
{
|
||||
static_cast<CBoxAlgorithmClassifierAccuracyMeasure*>(data)->m_ShowScores = (gtk_toggle_tool_button_get_active(button) ? true : false);
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmClassifierAccuracyMeasure::initialize()
|
||||
{
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
|
||||
m_ProgressBar.resize(getStaticBoxContext().getInputCount() - 1); //-1 because the first input is the target
|
||||
|
||||
//classifier decoders
|
||||
for (size_t i = 1; i < nInput; ++i)
|
||||
{
|
||||
m_classifierStimDecoders.push_back(new Toolkit::TStimulationDecoder<CBoxAlgorithmClassifierAccuracyMeasure>());
|
||||
m_classifierStimDecoders.back()->initialize(*this, i);
|
||||
}
|
||||
|
||||
m_targetStimDecoder.initialize(*this, 0);
|
||||
|
||||
//widgets
|
||||
m_mainWidgetInterface = gtk_builder_new();
|
||||
gtk_builder_add_from_file(m_mainWidgetInterface,
|
||||
Directories::getDataDir() + "/plugins/evaluation/openvibe-simple-visualization-ClassifierAccuracyMeasure.ui", nullptr);
|
||||
|
||||
m_toolbarWidgetInterface = gtk_builder_new();
|
||||
gtk_builder_add_from_file(m_toolbarWidgetInterface,
|
||||
Directories::getDataDir() + "/plugins/evaluation/openvibe-simple-visualization-ClassifierAccuracyMeasure.ui", nullptr);
|
||||
|
||||
gtk_builder_connect_signals(m_mainWidgetInterface, nullptr);
|
||||
gtk_builder_connect_signals(m_toolbarWidgetInterface, nullptr);
|
||||
|
||||
g_signal_connect(G_OBJECT(gtk_builder_get_object(m_toolbarWidgetInterface, "reset-score-button")), "clicked", G_CALLBACK(reset_scores_button_cb), this);
|
||||
g_signal_connect(G_OBJECT(gtk_builder_get_object(m_toolbarWidgetInterface, "show-percentages-toggle-button")), "toggled",
|
||||
G_CALLBACK(show_percentages_toggle_button_cb), this);
|
||||
g_signal_connect(G_OBJECT(gtk_builder_get_object(m_toolbarWidgetInterface, "show-scores-toggle-button")), "toggled",
|
||||
G_CALLBACK(show_scores_toggle_button_cb), this);
|
||||
g_signal_connect(G_OBJECT(gtk_builder_get_object(m_toolbarWidgetInterface, "classifier-accuracy-measure-toolbar")), "delete_event",
|
||||
G_CALLBACK(gtk_widget_hide), nullptr);
|
||||
|
||||
m_mainWidget = GTK_WIDGET(gtk_builder_get_object(m_mainWidgetInterface, "classifier-accuracy-measure-table"));
|
||||
m_toolbarWidget = GTK_WIDGET(gtk_builder_get_object(m_toolbarWidgetInterface, "classifier-accuracy-measure-toolbar"));
|
||||
|
||||
m_visualizationCtx = dynamic_cast<VisualizationToolkit::IVisualizationContext*>(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx));
|
||||
m_visualizationCtx->setWidget(*this, m_mainWidget);
|
||||
m_visualizationCtx->setToolbar(*this, m_toolbarWidget);
|
||||
|
||||
m_ShowPercentages = (gtk_toggle_tool_button_get_active(
|
||||
GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(m_toolbarWidgetInterface, "show-percentages-toggle-button"))) ? true : false);
|
||||
m_ShowScores = (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(m_toolbarWidgetInterface, "show-scores-toggle-button")))
|
||||
? true : false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmClassifierAccuracyMeasure::uninitialize()
|
||||
{
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
//decoders
|
||||
for (size_t i = 0; i < nInput - 1; ++i)
|
||||
{
|
||||
m_classifierStimDecoders[i]->uninitialize();
|
||||
delete m_classifierStimDecoders[i];
|
||||
}
|
||||
m_classifierStimDecoders.clear();
|
||||
|
||||
m_targetStimDecoder.uninitialize();
|
||||
|
||||
//widgets
|
||||
g_object_unref(m_toolbarWidgetInterface);
|
||||
m_toolbarWidgetInterface = nullptr;
|
||||
|
||||
g_object_unref(m_mainWidgetInterface);
|
||||
m_mainWidgetInterface = nullptr;
|
||||
|
||||
if (m_visualizationCtx)
|
||||
{
|
||||
this->releasePluginObject(m_visualizationCtx);
|
||||
m_visualizationCtx = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmClassifierAccuracyMeasure::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmClassifierAccuracyMeasure::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
const Kernel::IBox& staticBoxContext = this->getStaticBoxContext();
|
||||
const size_t nInput = staticBoxContext.getInputCount();
|
||||
|
||||
//input chunk 0 = targets
|
||||
// we iterate over the "target" chunks and update the timeline
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_targetStimDecoder.decode(i);
|
||||
|
||||
if (m_targetStimDecoder.isHeaderReceived())
|
||||
{
|
||||
//header received
|
||||
//adding the progress bars to the window
|
||||
GtkTable* table = GTK_TABLE(gtk_builder_get_object(m_mainWidgetInterface, "classifier-accuracy-measure-table"));
|
||||
gtk_table_resize(table, 1, guint(nInput - 1));
|
||||
|
||||
//@TODO i variable redefine replace alll i in the loop ( it's logical but must be verified
|
||||
for (guint j = 0; j < nInput - 1; ++j)
|
||||
{
|
||||
GtkBuilder* builderBar = gtk_builder_new();
|
||||
gtk_builder_add_from_file(
|
||||
builderBar, Directories::getDataDir() + "/plugins/evaluation/openvibe-simple-visualization-ClassifierAccuracyMeasure.ui", nullptr);
|
||||
|
||||
GtkBuilder* builderLabel = gtk_builder_new();
|
||||
gtk_builder_add_from_file(
|
||||
builderLabel, Directories::getDataDir() + "/plugins/evaluation/openvibe-simple-visualization-ClassifierAccuracyMeasure.ui", nullptr);
|
||||
|
||||
GtkWidget* bar = GTK_WIDGET(gtk_builder_get_object(builderBar, "progress-bar-classifier-accuracy"));
|
||||
GtkWidget* label = GTK_WIDGET(gtk_builder_get_object(builderLabel, "label-classifier-name"));
|
||||
|
||||
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(bar)), bar);
|
||||
gtk_table_attach(table, bar, j, j + 1, 0, 6, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0, 0);
|
||||
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(label)), label);
|
||||
gtk_table_attach(table, label, j, j + 1, 6, 7, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0, 0);
|
||||
|
||||
g_object_unref(builderBar);
|
||||
g_object_unref(builderLabel);
|
||||
|
||||
progress_bar_t progressBar;
|
||||
progressBar.progressBar = GTK_PROGRESS_BAR(bar);
|
||||
progressBar.score = 0;
|
||||
progressBar.nStimulation = 0;
|
||||
progressBar.labelClassifier = GTK_LABEL(label);
|
||||
|
||||
gtk_progress_bar_set_fraction(progressBar.progressBar, 0);
|
||||
CString inputName;
|
||||
staticBoxContext.getInputName(j + 1, inputName);
|
||||
gtk_progress_bar_set_text(progressBar.progressBar, inputName.toASCIIString());
|
||||
gtk_label_set_text(progressBar.labelClassifier, inputName.toASCIIString());
|
||||
m_ProgressBar[j] = (progressBar);
|
||||
}
|
||||
|
||||
m_currentProcessingTimeLimit = 0;
|
||||
}
|
||||
|
||||
if (m_targetStimDecoder.isBufferReceived())
|
||||
{
|
||||
//buffer received
|
||||
//A new target comes, let's update the timeline with it
|
||||
const IStimulationSet* dstStimSet = m_targetStimDecoder.getOutputStimulationSet();
|
||||
for (size_t s = 0; s < dstStimSet->getStimulationCount(); ++s)
|
||||
{
|
||||
const uint64_t id = dstStimSet->getStimulationIdentifier(s);
|
||||
const uint64_t date = dstStimSet->getStimulationDate(s);
|
||||
m_targetsTimeLines.insert(std::pair<uint64_t, uint64_t>(date, id));
|
||||
getLogManager() << Kernel::LogLevel_Trace << "New target inserted (" << id << "," << CTime(date) << ")\n";
|
||||
}
|
||||
|
||||
//we updtae the time limit for processing classifier stim
|
||||
const uint64_t chunkEndTime = boxContext.getInputChunkEndTime(0, i);
|
||||
m_currentProcessingTimeLimit = MAX(chunkEndTime, m_currentProcessingTimeLimit);
|
||||
}
|
||||
|
||||
if (m_targetStimDecoder.isEndReceived()) { }
|
||||
|
||||
boxContext.markInputAsDeprecated(0, i);
|
||||
}
|
||||
|
||||
//input index 1-n = n classifier results
|
||||
for (size_t ip = 1; ip < staticBoxContext.getInputCount(); ++ip)
|
||||
{
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(ip); ++i)
|
||||
{
|
||||
// lets get the chunck end time
|
||||
const uint64_t chunkEndTime = boxContext.getInputChunkEndTime(ip, i);
|
||||
// if the incoming chunk is in the timeline
|
||||
if (chunkEndTime <= m_currentProcessingTimeLimit)
|
||||
{
|
||||
if (!m_targetsTimeLines.empty())
|
||||
{
|
||||
// we can process it
|
||||
m_classifierStimDecoders[ip - 1]->decode(i);
|
||||
|
||||
if (m_classifierStimDecoders[ip - 1]->isHeaderReceived()) { } //header received
|
||||
if (m_classifierStimDecoders[ip - 1]->isBufferReceived())
|
||||
{
|
||||
//buffer received
|
||||
const IStimulationSet* stimSet = m_classifierStimDecoders[ip - 1]->getOutputStimulationSet();
|
||||
for (size_t s = 0; s < stimSet->getStimulationCount(); ++s)
|
||||
{
|
||||
//We need to locate the stimulation on the timeline
|
||||
uint64_t id = stimSet->getStimulationIdentifier(s);
|
||||
const uint64_t date = stimSet->getStimulationDate(s);
|
||||
|
||||
getLogManager() << Kernel::LogLevel_Trace << "New Classifier state received (" << id << "," << CTime(date) << ") from Classifier "
|
||||
<< ip << "\n";
|
||||
|
||||
auto it = m_targetsTimeLines.begin();
|
||||
bool cont = true;
|
||||
while (it != m_targetsTimeLines.end() && cont)
|
||||
{
|
||||
auto nextTarget = it;
|
||||
++nextTarget;
|
||||
if ((nextTarget == m_targetsTimeLines.end() || date < nextTarget->first)
|
||||
&& date > it->first)
|
||||
{
|
||||
if (id == it->second)
|
||||
{
|
||||
//+1 for this classifier !
|
||||
m_ProgressBar[ip - 1].score++;
|
||||
}
|
||||
m_ProgressBar[ip - 1].nStimulation++;
|
||||
cont = false;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
//auto it = m_targetsTimeLines.lower_bound(l_stimulationFromClassifierDate);
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::fixed;
|
||||
ss << std::setprecision(2);
|
||||
if (m_ShowScores) { ss << "score : " << m_ProgressBar[ip - 1].score << "/" << m_ProgressBar[ip - 1].nStimulation << "\n"; }
|
||||
double percent = 0.0;
|
||||
if (m_ProgressBar[ip - 1].nStimulation != 0) { percent = m_ProgressBar[ip - 1].score * 1. / m_ProgressBar[ip - 1].nStimulation; }
|
||||
if (m_ShowPercentages) { ss << percent * 100 << "%\n"; }
|
||||
|
||||
gtk_progress_bar_set_fraction(m_ProgressBar[ip - 1].progressBar, percent);
|
||||
gtk_progress_bar_set_text(m_ProgressBar[ip - 1].progressBar, ss.str().c_str());
|
||||
}
|
||||
|
||||
if (m_targetStimDecoder.isEndReceived()) { }
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(ip, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <visualization-toolkit/ovviz_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
class CBoxAlgorithmClassifierAccuracyMeasure final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ClassifierAccuracyMeasure)
|
||||
|
||||
protected:
|
||||
|
||||
//codecs
|
||||
// for the TARGET
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmClassifierAccuracyMeasure> m_targetStimDecoder;
|
||||
// For the CLASSIFIERS
|
||||
std::vector<Toolkit::TStimulationDecoder<CBoxAlgorithmClassifierAccuracyMeasure>*> m_classifierStimDecoders;
|
||||
|
||||
|
||||
// deduced timeline:
|
||||
std::map<uint64_t, uint64_t> m_targetsTimeLines;
|
||||
uint64_t m_currentProcessingTimeLimit = 0;
|
||||
|
||||
|
||||
// Outputs: visualization in a gtk window
|
||||
GtkBuilder* m_mainWidgetInterface = nullptr;
|
||||
GtkBuilder* m_toolbarWidgetInterface = nullptr;
|
||||
GtkWidget* m_mainWidget = nullptr;
|
||||
GtkWidget* m_toolbarWidget = nullptr;
|
||||
|
||||
public:
|
||||
typedef struct
|
||||
{
|
||||
GtkLabel* labelClassifier;
|
||||
GtkProgressBar* progressBar;
|
||||
size_t score;
|
||||
size_t nStimulation;
|
||||
} progress_bar_t;
|
||||
|
||||
std::vector<progress_bar_t> m_ProgressBar;
|
||||
bool m_ShowPercentages = false;
|
||||
bool m_ShowScores = false;
|
||||
|
||||
private:
|
||||
VisualizationToolkit::IVisualizationContext* m_visualizationCtx = nullptr;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmClassifierAccuracyMeasureListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
bool onInputNameChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
if (index == 0) { box.setInputName(0, "Targets"); } // forced
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onInputAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.setInputType(index, OV_TypeId_Stimulations); // all inputs must be stimulations
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmClassifierAccuracyMeasureDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Classifier Accuracy Measure"); }
|
||||
CString getAuthorName() const override { return CString("Laurent Bonnet"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Displays real-time classifier accuracies as vertical progress bars"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Evaluation/Classification"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-sort-ascending"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ClassifierAccuracyMeasure; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmClassifierAccuracyMeasure; }
|
||||
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmClassifierAccuracyMeasureListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool hasFunctionality(const EPluginFunctionality functionality) const override { return functionality == EPluginFunctionality::Visualization; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Targets", OV_TypeId_Stimulations);
|
||||
prototype.addInput("Classifier 1", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
|
||||
|
||||
prototype.addInputSupport(OV_TypeId_Stimulations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ClassifierAccuracyMeasureDesc)
|
||||
};
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGTK
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#include "ovpCBoxAlgorithmConfusionMatrix.h"
|
||||
|
||||
#include "../algorithms/ovpCAlgorithmConfusionMatrix.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
bool CBoxAlgorithmConfusionMatrix::initialize()
|
||||
{
|
||||
//Initialize input/output
|
||||
m_targetStimDecoder.initialize(*this, 0);
|
||||
m_classifierStimDecoder.initialize(*this, 1);
|
||||
|
||||
m_encoder.initialize(*this, 0);
|
||||
|
||||
//CONFUSION MATRIX ALGORITHM
|
||||
m_algorithm = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_ClassId_Algorithm_ConfusionMatrix));
|
||||
m_algorithm->initialize();
|
||||
|
||||
Kernel::TParameterHandler<bool> percentHandler(m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Percentage));
|
||||
percentHandler = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
Kernel::TParameterHandler<bool> sumsHandler(m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Sums));
|
||||
if (!bool(percentHandler)) { sumsHandler = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1); }
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Asking for percentage. The value of the setting \"Sums\" will be ignored.\n";
|
||||
sumsHandler = false;
|
||||
}
|
||||
|
||||
|
||||
const size_t nClass = getBoxAlgorithmContext()->getStaticBoxContext()->getSettingCount() - FIRST_CLASS_SETTING_INDEX;
|
||||
std::vector<size_t> classCodes;
|
||||
classCodes.resize(nClass);
|
||||
for (size_t i = 0; i < nClass; ++i)
|
||||
{
|
||||
// classes are settings from 2 to n
|
||||
classCodes[i] = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i + FIRST_CLASS_SETTING_INDEX));
|
||||
}
|
||||
// verification...
|
||||
for (size_t i = 0; i < nClass; ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < nClass; ++j)
|
||||
{
|
||||
if (classCodes[i] == classCodes[j])
|
||||
{
|
||||
const CString classValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i + FIRST_CLASS_SETTING_INDEX);
|
||||
getLogManager() << Kernel::LogLevel_Error << "You must use unique classes to compute a confusion matrix. Class " << i + 1 << " and " << j + 1 <<
|
||||
" are the same (" << classValue << ").\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kernel::TParameterHandler<IStimulationSet*> classesCodesHandler(m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassCodes));
|
||||
for (size_t i = 0; i < classCodes.size(); ++i) { classesCodesHandler->appendStimulation(classCodes[i], 0, 0); }
|
||||
|
||||
//Link all input/output
|
||||
Kernel::TParameterHandler<IStimulationSet*> classifierStimSetHandler(
|
||||
m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassifierStimulationSet));
|
||||
classifierStimSetHandler.setReferenceTarget(m_classifierStimDecoder.getOutputStimulationSet());
|
||||
|
||||
Kernel::TParameterHandler<IStimulationSet*> targetStimSetHandler(
|
||||
m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_TargetStimulationSet));
|
||||
targetStimSetHandler.setReferenceTarget(m_targetStimDecoder.getOutputStimulationSet());
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> matrixHandler(m_algorithm->getOutputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputParameterId_ConfusionMatrix));
|
||||
m_encoder.getInputMatrix().setReferenceTarget(matrixHandler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmConfusionMatrix::uninitialize()
|
||||
{
|
||||
m_algorithm->uninitialize();
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_algorithm);
|
||||
|
||||
m_encoder.uninitialize();
|
||||
m_targetStimDecoder.uninitialize();
|
||||
m_classifierStimDecoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmConfusionMatrix::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmConfusionMatrix::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//Input 0: Targets
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_targetStimDecoder.decode(i);
|
||||
|
||||
if (m_targetStimDecoder.isHeaderReceived())
|
||||
{
|
||||
m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetTarget);
|
||||
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
m_currentProcessingTimeLimit = 0;
|
||||
}
|
||||
|
||||
if (m_targetStimDecoder.isBufferReceived())
|
||||
{
|
||||
uint64_t chunkEndTime = boxContext.getInputChunkEndTime(0, i);
|
||||
m_currentProcessingTimeLimit = (chunkEndTime > m_currentProcessingTimeLimit ? chunkEndTime : m_currentProcessingTimeLimit);
|
||||
m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedTarget);
|
||||
}
|
||||
|
||||
if (m_targetStimDecoder.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
//Input 1: Classifier results
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
const uint64_t tEnd = boxContext.getInputChunkEndTime(1, i);
|
||||
if (tEnd <= m_currentProcessingTimeLimit)
|
||||
{
|
||||
m_classifierStimDecoder.decode(i);
|
||||
|
||||
if (m_classifierStimDecoder.isHeaderReceived()) { m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetClassifier); }
|
||||
|
||||
if (m_classifierStimDecoder.isBufferReceived())
|
||||
{
|
||||
m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedClassifier);
|
||||
if (m_algorithm->isOutputTriggerActive(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputTriggerId_ConfusionPerformed))
|
||||
{
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(1, i), boxContext.getInputChunkEndTime(1, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_classifierStimDecoder.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(1, i), boxContext.getInputChunkEndTime(1, i));
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(1, i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
class CBoxAlgorithmConfusionMatrix final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ConfusionMatrix)
|
||||
|
||||
protected:
|
||||
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmConfusionMatrix> m_targetStimDecoder;
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmConfusionMatrix> m_classifierStimDecoder;
|
||||
|
||||
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmConfusionMatrix> m_encoder;
|
||||
|
||||
Kernel::IAlgorithmProxy* m_algorithm = nullptr;
|
||||
|
||||
uint64_t m_currentProcessingTimeLimit = 0;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmConfusionMatrixListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onSettingAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
std::stringstream value;
|
||||
value << "OVTK_StimulationId_Label_" << std::setfill('0') << std::setw(2) << index - 2;
|
||||
box.setSettingName(index, ("Class " + std::to_string(index - 1)).c_str());
|
||||
box.setSettingType(index, OV_TypeId_Stimulation);
|
||||
box.setSettingValue(index, value.str().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onSettingRemoved(Kernel::IBox& box, const size_t /*index*/) override
|
||||
{
|
||||
const size_t nSetting = box.getSettingCount();
|
||||
const size_t nClass = nSetting - FIRST_CLASS_SETTING_INDEX;
|
||||
|
||||
for (size_t i = 0; i < nClass; ++i) { box.setSettingName(FIRST_CLASS_SETTING_INDEX + i, ("Class " + std::to_string(i + 1)).c_str()); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmConfusionMatrixDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Confusion Matrix"); }
|
||||
CString getAuthorName() const override { return CString("Laurent Bonnet"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Make a confusion matrix out of classification results coming from one classifier."); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Evaluation/Classification"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-execute"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ConfusionMatrix; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmConfusionMatrix; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Targets", OV_TypeId_Stimulations);
|
||||
prototype.addInput("Classification results", OV_TypeId_Stimulations);
|
||||
prototype.addOutput("Confusion Matrix", OV_TypeId_StreamedMatrix);
|
||||
|
||||
prototype.addSetting("Percentages", OV_TypeId_Boolean, "true");
|
||||
prototype.addSetting("Sums", OV_TypeId_Boolean, "false");
|
||||
|
||||
prototype.addSetting("Class 1", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_00");
|
||||
prototype.addSetting("Class 2", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_01");
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddSetting);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmConfusionMatrixListener; }
|
||||
virtual void releaseBoxListener(IBoxListener* listener) { delete listener; }
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ConfusionMatrixDesc)
|
||||
};
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
#include "ovpCBoxAlgorithmKappaCoefficient.h"
|
||||
#include "../algorithms/ovpCAlgorithmConfusionMatrix.h"
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
static const size_t CLASS_LABEL_OFFSET = 1;
|
||||
|
||||
bool CBoxAlgorithmKappaCoef::initialize()
|
||||
{
|
||||
//Initialize input/output
|
||||
m_targetStimDecoder.initialize(*this, 0);
|
||||
m_classifierStimDecoder.initialize(*this, 1);
|
||||
|
||||
m_encoder.initialize(*this, 0);
|
||||
|
||||
//Confusion matrix algorithm
|
||||
m_algorithm = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_ClassId_Algorithm_ConfusionMatrix));
|
||||
m_algorithm->initialize();
|
||||
|
||||
Kernel::TParameterHandler<bool> percentHandler(m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Percentage));
|
||||
percentHandler = false;
|
||||
|
||||
Kernel::TParameterHandler<bool> sumsHandler(m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Sums));
|
||||
sumsHandler = true;
|
||||
|
||||
m_amountClass = getBoxAlgorithmContext()->getStaticBoxContext()->getSettingCount() - CLASS_LABEL_OFFSET;
|
||||
std::vector<size_t> classCodes;
|
||||
classCodes.resize(m_amountClass);
|
||||
for (size_t i = 0; i < m_amountClass; ++i)
|
||||
{
|
||||
// classes are settings from 2 to n
|
||||
classCodes[i] = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i + CLASS_LABEL_OFFSET));
|
||||
}
|
||||
|
||||
// Let's check that each identifier is unique
|
||||
for (size_t i = 0; i < m_amountClass; ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < m_amountClass; ++j)
|
||||
{
|
||||
if (classCodes[i] == classCodes[j])
|
||||
{
|
||||
const CString value = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i + CLASS_LABEL_OFFSET);
|
||||
getLogManager() << Kernel::LogLevel_Error << "You must use unique classes to compute a Kappa coefficient. Class " << i + 1 << " and " << j + 1
|
||||
<< " are the same (" << value.toASCIIString() << ").\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kernel::TParameterHandler<IStimulationSet*> classesCodesHandler(
|
||||
m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassCodes));
|
||||
for (size_t i = 0; i < classCodes.size(); ++i) { classesCodesHandler->appendStimulation(classCodes[i], 0, 0); }
|
||||
|
||||
//Link all input/output
|
||||
Kernel::TParameterHandler<IStimulationSet*> classifierStimSetHandler(
|
||||
m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassifierStimulationSet));
|
||||
classifierStimSetHandler.setReferenceTarget(m_classifierStimDecoder.getOutputStimulationSet());
|
||||
|
||||
Kernel::TParameterHandler<IStimulationSet*> targetStimSetHandler(
|
||||
m_algorithm->getInputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_TargetStimulationSet));
|
||||
targetStimSetHandler.setReferenceTarget(m_targetStimDecoder.getOutputStimulationSet());
|
||||
|
||||
op_confusionMatrix.initialize(m_algorithm->getOutputParameter(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputParameterId_ConfusionMatrix));
|
||||
|
||||
GtkTable* table = GTK_TABLE(gtk_table_new(2, 1, false));
|
||||
|
||||
m_kappaLabel = gtk_label_new("x");
|
||||
gtk_table_attach(table, m_kappaLabel, 0, 1, 0, 5, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0, 0);
|
||||
|
||||
|
||||
m_visualizationCtx = dynamic_cast<VisualizationToolkit::IVisualizationContext*>(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx));
|
||||
m_visualizationCtx->setWidget(*this, GTK_WIDGET(table));
|
||||
|
||||
PangoContext* ctx = gtk_widget_get_pango_context(GTK_WIDGET(m_kappaLabel));
|
||||
PangoFontDescription* fontDesc = pango_context_get_font_description(ctx);
|
||||
pango_font_description_set_size(fontDesc, 40 * PANGO_SCALE);
|
||||
gtk_widget_modify_font(m_kappaLabel, fontDesc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmKappaCoef::uninitialize()
|
||||
{
|
||||
//Log for the automatic test
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Final value of Kappa " << m_kappaCoef << "\n";
|
||||
m_algorithm->uninitialize();
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_algorithm);
|
||||
|
||||
m_encoder.uninitialize();
|
||||
m_targetStimDecoder.uninitialize();
|
||||
m_classifierStimDecoder.uninitialize();
|
||||
|
||||
if (m_visualizationCtx)
|
||||
{
|
||||
this->releasePluginObject(m_visualizationCtx);
|
||||
m_visualizationCtx = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmKappaCoef::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmKappaCoef::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//Input 0: Targets
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_targetStimDecoder.decode(i);
|
||||
|
||||
if (m_targetStimDecoder.isHeaderReceived())
|
||||
{
|
||||
m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetTarget);
|
||||
|
||||
m_encoder.getInputMatrix()->resize(1);
|
||||
m_encoder.getInputMatrix()->setDimensionLabel(0, 0, "Kappa");
|
||||
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
m_currentProcessingTimeLimit = 0;
|
||||
}
|
||||
|
||||
if (m_targetStimDecoder.isBufferReceived())
|
||||
{
|
||||
uint64_t end = boxContext.getInputChunkEndTime(0, i);
|
||||
m_currentProcessingTimeLimit = (end > m_currentProcessingTimeLimit ? end : m_currentProcessingTimeLimit);
|
||||
m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedTarget);
|
||||
}
|
||||
|
||||
if (m_targetStimDecoder.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
//Input 1: Classifier results
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
const uint64_t end = boxContext.getInputChunkEndTime(1, i);
|
||||
if (end <= m_currentProcessingTimeLimit)
|
||||
{
|
||||
m_classifierStimDecoder.decode(i);
|
||||
|
||||
if (m_classifierStimDecoder.isHeaderReceived()) { m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetClassifier); }
|
||||
|
||||
if (m_classifierStimDecoder.isBufferReceived())
|
||||
{
|
||||
m_algorithm->process(OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedClassifier);
|
||||
if (m_algorithm->isOutputTriggerActive(OVP_Algorithm_ConfusionMatrixAlgorithm_OutputTriggerId_ConfusionPerformed))
|
||||
{
|
||||
//The confusion matrix has changed so we need to update the kappa coefficient
|
||||
double* matrix = op_confusionMatrix->getBuffer();
|
||||
//First we need the amount of sample that have been classified
|
||||
const size_t total = size_t(matrix[(m_amountClass + 1) * (m_amountClass + 1) - 1]);
|
||||
|
||||
//Now we gonna compute the two sum we need to compute the kappa coefficient
|
||||
//It's more easy to use a double loop
|
||||
double observed = 0;
|
||||
double expected = 0;
|
||||
|
||||
for (size_t j = 0; j < m_amountClass; ++j)
|
||||
{
|
||||
//We need to take the column sum in account
|
||||
observed += matrix[j * (m_amountClass + 1) + j];
|
||||
expected += (matrix[(m_amountClass + 1) * j + m_amountClass] * matrix[(m_amountClass + 1) * m_amountClass + j]);
|
||||
}
|
||||
observed /= total;
|
||||
expected /= (total * total);
|
||||
|
||||
m_kappaCoef = (observed - expected) / (1 - expected);
|
||||
|
||||
updateKappaValue();
|
||||
m_encoder.getInputMatrix()->getBuffer()[0] = m_kappaCoef;
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(1, i), boxContext.getInputChunkEndTime(1, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_classifierStimDecoder.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(1, i), boxContext.getInputChunkEndTime(1, i));
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(1, i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBoxAlgorithmKappaCoef::updateKappaValue() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(2) << m_kappaCoef;
|
||||
gtk_label_set(GTK_LABEL(m_kappaLabel), ss.str().c_str());
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <visualization-toolkit/ovviz_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
/**
|
||||
* \class CBoxAlgorithmKappaCoef
|
||||
* \author Serrière Guillaume (Inria)
|
||||
* \date Tue May 5 12:45:13 2015
|
||||
* \brief The class CBoxAlgorithmKappaCoef describes the box Kappa coefficient.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmKappaCoef final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_KappaCoef)
|
||||
|
||||
protected:
|
||||
void updateKappaValue() const;
|
||||
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmKappaCoef> m_targetStimDecoder;
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmKappaCoef> m_classifierStimDecoder;
|
||||
|
||||
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmKappaCoef> m_encoder;
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> op_confusionMatrix;
|
||||
|
||||
Kernel::IAlgorithmProxy* m_algorithm = nullptr;
|
||||
|
||||
size_t m_amountClass = 0;
|
||||
uint64_t m_currentProcessingTimeLimit = 0;
|
||||
double m_kappaCoef = 0;
|
||||
|
||||
GtkWidget* m_kappaLabel = nullptr;
|
||||
private:
|
||||
VisualizationToolkit::IVisualizationContext* m_visualizationCtx = nullptr;
|
||||
};
|
||||
|
||||
|
||||
// The box listener can be used to call specific callbacks whenever the box structure changes : input added, name changed, etc.
|
||||
// Please uncomment below the callbacks you want to use.
|
||||
class CBoxAlgorithmKappaCoefListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onSettingValueChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
CString nClass;
|
||||
box.getSettingValue(index, nClass);
|
||||
|
||||
if (nClass.length() == 0) { return true; }
|
||||
|
||||
size_t nSetting;
|
||||
std::stringstream ss(nClass.toASCIIString());
|
||||
ss >> nSetting;
|
||||
|
||||
//First of all we prevent for the value to goes under 1.
|
||||
if (nSetting < 1)
|
||||
{
|
||||
box.setSettingValue(index, "1");
|
||||
nSetting = 1;
|
||||
}
|
||||
size_t nCurrent = box.getSettingCount() - 1;
|
||||
//We have two choice 1/We need to add class, 2/We need to remove some
|
||||
if (nCurrent < nSetting)
|
||||
{
|
||||
while (nCurrent < nSetting)
|
||||
{
|
||||
box.addSetting(("Stimulation of class " + std::to_string(nCurrent + 1)).c_str(), OVTK_TypeId_Stimulation, "");
|
||||
++nCurrent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (nCurrent > nSetting)
|
||||
{
|
||||
box.removeSetting(box.getSettingCount() - 1);
|
||||
--nCurrent;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmKappaCoefDesc
|
||||
* \author Serrière Guillaume (Inria)
|
||||
* \date Tue May 5 12:45:13 2015
|
||||
* \brief Descriptor of the box Kappa coefficient.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmKappaCoefDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Kappa coefficient"); }
|
||||
CString getAuthorName() const override { return CString("Serrière Guillaume"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Compute the kappa coefficient for the classifier."); }
|
||||
CString getDetailedDescription() const override { return CString("The box computes kappa coefficient for a classifier."); }
|
||||
CString getCategory() const override { return CString("Evaluation/Classification"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-yes"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_KappaCoef; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmKappaCoef; }
|
||||
|
||||
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmKappaCoefListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool hasFunctionality(const EPluginFunctionality functionality) const override { return functionality == EPluginFunctionality::Visualization; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Expected stimulations", OV_TypeId_Stimulations);
|
||||
prototype.addInput("Found stimulations", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addOutput("Confusion Matrix", OV_TypeId_StreamedMatrix);
|
||||
|
||||
prototype.addSetting("Number of classes", OV_TypeId_Integer, "2");
|
||||
prototype.addSetting("Stimulation of class 1", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_01");
|
||||
prototype.addSetting("Stimulation of class 2", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_02");
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_KappaCoefDesc)
|
||||
};
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGTK
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
#include "ovpCBoxAlgorithmROCCurve.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
static bool compareCTimelineStimulationPair(const CTimestampLabelPair& rElt1, const CTimestampLabelPair& rElt2) { return rElt1.first < rElt2.first; }
|
||||
|
||||
static bool compareValueAndStimulationTimelinePair(const CTimestampLabelPair& rElt1, const CTimestampValuesPair& rElt2) { return rElt1.first < rElt2.first; }
|
||||
|
||||
static bool compareRocValuePair(const CRocPairValue& rElt1, const CRocPairValue& rElt2) { return rElt1.second > rElt2.second; }
|
||||
|
||||
static bool isPositive(const CRocPairValue& rElt1) { return rElt1.first; }
|
||||
|
||||
bool CBoxAlgorithmROCCurve::initialize()
|
||||
{
|
||||
m_expectedDecoder.initialize(*this, 0);
|
||||
m_classificationDecoder.initialize(*this, 1);
|
||||
|
||||
m_computationTrigger = CIdentifier(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0));
|
||||
|
||||
m_widget = GTK_WIDGET(gtk_notebook_new());
|
||||
|
||||
for (size_t i = 2; i < this->getStaticBoxContext().getSettingCount(); ++i)
|
||||
{
|
||||
CIdentifier classLabel(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i));
|
||||
CString className = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i);
|
||||
|
||||
m_classStimSet.insert(classLabel);
|
||||
|
||||
m_drawerList.push_back(new CROCCurveDraw(GTK_NOTEBOOK(m_widget), i - 1, className));
|
||||
}
|
||||
|
||||
m_visualizationCtx = dynamic_cast<VisualizationToolkit::IVisualizationContext*>(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx));
|
||||
m_visualizationCtx->setWidget(*this, m_widget);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmROCCurve::uninitialize()
|
||||
{
|
||||
m_expectedDecoder.uninitialize();
|
||||
m_classificationDecoder.uninitialize();
|
||||
|
||||
for (size_t i = 0; i < m_drawerList.size(); ++i) { delete m_drawerList[i]; }
|
||||
|
||||
//The m_valueTimeline vector contains each dynamically instantiate values that need to be free'd
|
||||
for (size_t i = 0; i < m_valueTimeline.size(); ++i) { delete m_valueTimeline[i].second; }
|
||||
|
||||
if (m_visualizationCtx)
|
||||
{
|
||||
this->releasePluginObject(m_visualizationCtx);
|
||||
m_visualizationCtx = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmROCCurve::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmROCCurve::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//First let's deal with the expected.
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_expectedDecoder.decode(i);
|
||||
|
||||
if (m_expectedDecoder.isHeaderReceived()) { m_stimTimeline.clear(); }
|
||||
|
||||
if (m_expectedDecoder.isBufferReceived())
|
||||
{
|
||||
IStimulationSet* stimSet = m_expectedDecoder.getOutputStimulationSet();
|
||||
for (size_t k = 0; k < stimSet->getStimulationCount(); ++k)
|
||||
{
|
||||
CIdentifier id = stimSet->getStimulationIdentifier(k);
|
||||
if (m_classStimSet.find(id) != m_classStimSet.end()) { m_stimTimeline.push_back(CTimestampLabelPair(stimSet->getStimulationDate(k), id.id())); }
|
||||
//We need to check if we receive the computation trigger
|
||||
if (id == m_computationTrigger) { computeROCCurves(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_classificationDecoder.decode(i);
|
||||
if (m_classificationDecoder.isHeaderReceived()) { m_valueTimeline.clear(); }
|
||||
if (m_classificationDecoder.isBufferReceived())
|
||||
{
|
||||
CMatrix* matrixValue = m_classificationDecoder.getOutputMatrix();
|
||||
//The matrix is suppose to have only one dimension
|
||||
double* arrayValue;
|
||||
|
||||
if (matrixValue->getBufferElementCount() == 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Received zero-sized buffer\n";
|
||||
return false;
|
||||
}
|
||||
if (matrixValue->getBufferElementCount() > 1)
|
||||
{
|
||||
arrayValue = new double[matrixValue->getBufferElementCount()];
|
||||
for (size_t k = 0; k < matrixValue->getBufferElementCount(); ++k) { arrayValue[k] = matrixValue->getBuffer()[k]; }
|
||||
}
|
||||
else
|
||||
{
|
||||
arrayValue = new double[2];
|
||||
arrayValue[0] = matrixValue->getBuffer()[0];
|
||||
arrayValue[1] = 1 - matrixValue->getBuffer()[0];
|
||||
}
|
||||
|
||||
uint64_t timestamp = boxContext.getInputChunkEndTime(1, i); //the time in stimulation correspond to the end of the chunck (cf processorbox code)
|
||||
m_valueTimeline.push_back(CTimestampValuesPair(timestamp, arrayValue));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmROCCurve::computeROCCurves()
|
||||
{
|
||||
//Now we assiociate all values to the corresponding label
|
||||
std::sort(m_stimTimeline.begin(), m_stimTimeline.end(), compareCTimelineStimulationPair);//ensure the timeline is ok
|
||||
|
||||
for (auto& val : m_valueTimeline)
|
||||
{
|
||||
auto bound = std::lower_bound(m_stimTimeline.begin(), m_stimTimeline.end(), val, compareValueAndStimulationTimelinePair);
|
||||
if (bound != m_stimTimeline.begin())
|
||||
{
|
||||
--bound;
|
||||
m_labelValueList.push_back(CLabelValuesPair(bound->second, val.second));
|
||||
}
|
||||
else
|
||||
{
|
||||
//Impossible to find the corresponding stimulation
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "A result of classification cannot be connected to a class. The result will be discarded.\n";
|
||||
}
|
||||
}
|
||||
|
||||
//We cannot use the set because we need the correct order
|
||||
for (size_t i = 2; i < this->getStaticBoxContext().getSettingCount(); ++i)
|
||||
{
|
||||
CIdentifier classLabel(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i));
|
||||
computeOneROCCurve(classLabel, i - 2);
|
||||
}
|
||||
//Now we ask to the current page to draw itself
|
||||
const gint currPage = gtk_notebook_current_page(GTK_NOTEBOOK(m_widget));
|
||||
if (currPage < 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace <<
|
||||
"No page is selected. The designer is probably in no visualization mode. Skipping the drawing phase\n";
|
||||
}
|
||||
else { m_drawerList[currPage]->forceRedraw(); }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmROCCurve::computeOneROCCurve(const CIdentifier& classID, const size_t classIdx)
|
||||
{
|
||||
std::vector<CRocPairValue> values;
|
||||
for (const auto& v : m_labelValueList)
|
||||
{
|
||||
CRocPairValue value;
|
||||
value.first = v.first == classID.id();
|
||||
value.second = v.second[classIdx];
|
||||
values.push_back(value);
|
||||
}
|
||||
std::sort(values.begin(), values.end(), compareRocValuePair);
|
||||
|
||||
size_t nTruePositive = 0;
|
||||
size_t nFalsePositive = 0;
|
||||
|
||||
const size_t nPositive = std::count_if(values.begin(), values.end(), isPositive);
|
||||
const size_t nNegative = values.size() - nPositive;
|
||||
|
||||
std::vector<CCoordinate>& coordinateVector = m_drawerList[classIdx]->getCoordinateVector();
|
||||
|
||||
for (const auto& value : values)
|
||||
{
|
||||
value.first ? ++nTruePositive : ++nFalsePositive;
|
||||
coordinateVector.push_back(CCoordinate(double(nFalsePositive) / nNegative, double(nTruePositive) / nPositive));
|
||||
}
|
||||
m_drawerList[classIdx]->generateCurve();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "ovpCROCCurveDraw.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <visualization-toolkit/ovviz_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
typedef std::pair<uint64_t, uint64_t> CTimestampLabelPair;
|
||||
typedef std::pair<uint64_t, double*> CTimestampValuesPair;
|
||||
typedef std::pair<uint64_t, double*> CLabelValuesPair;
|
||||
|
||||
typedef std::pair<bool, double> CRocPairValue;
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmROCCurve
|
||||
* \author Serrière Guillaume (Inria)
|
||||
* \date Thu May 28 11:49:24 2015
|
||||
* \brief The class CBoxAlgorithmROCCurve describes the box ROC curve.
|
||||
* The roc curve is a graphical plot that represents the performance of a classifier. This curve is created by plotting the true positive
|
||||
* rate against the false positive rate at various threshold settings.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmROCCurve final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ROCCurve)
|
||||
|
||||
private:
|
||||
bool computeROCCurves();
|
||||
bool computeOneROCCurve(const CIdentifier& classID, const size_t classIdx);
|
||||
|
||||
// Input decoder:
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmROCCurve> m_expectedDecoder;
|
||||
Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmROCCurve> m_classificationDecoder;
|
||||
|
||||
std::set<CIdentifier> m_classStimSet;
|
||||
CIdentifier m_computationTrigger = CIdentifier::undefined();
|
||||
|
||||
std::vector<CTimestampLabelPair> m_stimTimeline;
|
||||
std::vector<CTimestampValuesPair> m_valueTimeline;
|
||||
|
||||
std::vector<CLabelValuesPair> m_labelValueList;
|
||||
|
||||
//Display section
|
||||
GtkWidget* m_widget = nullptr;
|
||||
std::vector<CROCCurveDraw*> m_drawerList;
|
||||
|
||||
VisualizationToolkit::IVisualizationContext* m_visualizationCtx = nullptr;
|
||||
};
|
||||
|
||||
// The box listener can be used to call specific callbacks whenever the box structure changes : input added, name changed, etc.
|
||||
// Please uncomment below the callbacks you want to use.
|
||||
class CBoxAlgorithmROCCurveListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onSettingValueChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
if (index == 1)
|
||||
{
|
||||
CString nClass;
|
||||
box.getSettingValue(index, nClass);
|
||||
//Could happen if we rewritte a number
|
||||
if (nClass.length() == 0) { return true; }
|
||||
|
||||
size_t nSetting;
|
||||
std::stringstream ss(nClass.toASCIIString());
|
||||
ss >> nSetting;
|
||||
|
||||
//First of all we prevent for the value to goes under 1.
|
||||
if (nSetting < 1)
|
||||
{
|
||||
box.setSettingValue(index, "1");
|
||||
nSetting = 1;
|
||||
}
|
||||
|
||||
size_t nCurrent = box.getSettingCount() - 2;
|
||||
//We have two choice 1/We need to add class, 2/We need to remove some
|
||||
if (nCurrent < nSetting)
|
||||
{
|
||||
while (nCurrent < nSetting)
|
||||
{
|
||||
box.addSetting(("Class " + std::to_string(nCurrent + 1) + " identifier").c_str(), OVTK_TypeId_Stimulation, "");
|
||||
++nCurrent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (nCurrent > nSetting)
|
||||
{
|
||||
box.removeSetting(box.getSettingCount() - 1);
|
||||
--nCurrent;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmROCCurveDesc
|
||||
* \author Serrière Guillaume (Inria)
|
||||
* \date Thu May 28 11:49:24 2015
|
||||
* \brief Descriptor of the box ROC curve.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmROCCurveDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("ROC curve"); }
|
||||
CString getAuthorName() const override { return CString("Serrière Guillaume"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Compute the ROC curve for each class."); }
|
||||
CString getDetailedDescription() const override { return CString("The box computes the ROC curve for each class."); }
|
||||
CString getCategory() const override { return CString("Evaluation/Classification"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-yes"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ROCCurve; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmROCCurve; }
|
||||
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmROCCurveListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool hasFunctionality(const EPluginFunctionality functionality) const override { return functionality == EPluginFunctionality::Visualization; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Expected labels", OV_TypeId_Stimulations);
|
||||
prototype.addInput("Probability values", OV_TypeId_StreamedMatrix);
|
||||
|
||||
prototype.addSetting("Computation trigger", OV_TypeId_Stimulation, "OVTK_StimulationId_ExperimentStop");
|
||||
prototype.addSetting("Number of classes", OV_TypeId_Integer, "2");
|
||||
prototype.addSetting("Class 1 identifier", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_01");
|
||||
prototype.addSetting("Class 2 identifier", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_02");
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ROCCurveDesc)
|
||||
};
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGTK
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
#include "ovpCBoxAlgorithmStatisticGenerator.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <xml/IXMLHandler.h>
|
||||
#include <xml/IXMLNode.h>
|
||||
#include <limits>
|
||||
#include <iomanip>
|
||||
|
||||
namespace {
|
||||
const char* const STATISTIC_ROOT_NODE_NAME = "Statistic";
|
||||
const char* const STIMULATION_LIST_NODE_NAME = "Stimulations-list";
|
||||
const char* const STIMULATION_NODE_NAME = "Stimulation";
|
||||
const char* const IDENTIFIER_CODE_NODE_NAME = "Identifier";
|
||||
const char* const IDENTIFIER_LABEL_NODE_NAME = "Label";
|
||||
const char* const AMOUNT_NODE_NAME = "Count";
|
||||
|
||||
const char* const CHANNEL_LIST_NODE_NAME = "Channel-list";
|
||||
const char* const CHANNEL_NODE_NAME = "Channel";
|
||||
const char* const CHANNEL_LABEL_NODE_NAME = "Name";
|
||||
const char* const CHANNEL_MIN_NODE_NAME = "Minimum";
|
||||
const char* const CHANNEL_MAX_NODE_NAME = "Maximum";
|
||||
const char* const CHANNEL_MEAN_NODE_NAME = "Mean";
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
bool CBoxAlgorithmStatisticGenerator::initialize()
|
||||
{
|
||||
m_signalDecoder.initialize(*this, 0);
|
||||
m_stimDecoder.initialize(*this, 1);
|
||||
|
||||
m_stimulations.clear();
|
||||
m_hasBeenStreamed = false;
|
||||
m_filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
if (m_filename == CString(""))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The filename is empty\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmStatisticGenerator::uninitialize()
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
m_signalDecoder.uninitialize();
|
||||
m_stimDecoder.uninitialize();
|
||||
|
||||
if (m_hasBeenStreamed)
|
||||
{
|
||||
XML::IXMLNode* rootNode = XML::createNode(STATISTIC_ROOT_NODE_NAME);
|
||||
XML::IXMLNode* stimNode = XML::createNode(STIMULATION_LIST_NODE_NAME);
|
||||
for (const auto& s : m_stimulations)
|
||||
{
|
||||
XML::IXMLNode* node = XML::createNode(STIMULATION_NODE_NAME);
|
||||
XML::IXMLNode* idNode = XML::createNode(IDENTIFIER_CODE_NODE_NAME);
|
||||
XML::IXMLNode* labelNode = XML::createNode(IDENTIFIER_LABEL_NODE_NAME);
|
||||
XML::IXMLNode* amountNode = XML::createNode(AMOUNT_NODE_NAME);
|
||||
|
||||
CIdentifier id = s.first;
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(10) << m_stimulations[id];
|
||||
|
||||
idNode->setPCData(id.str().c_str());
|
||||
labelNode->setPCData(this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, id.id()).toASCIIString());
|
||||
amountNode->setPCData(ss.str().c_str());
|
||||
|
||||
node->addChild(idNode);
|
||||
node->addChild(labelNode);
|
||||
node->addChild(amountNode);
|
||||
stimNode->addChild(node);
|
||||
}
|
||||
rootNode->addChild(stimNode);
|
||||
|
||||
|
||||
XML::IXMLNode* channelsNode = XML::createNode(CHANNEL_LIST_NODE_NAME);
|
||||
for (size_t i = 0; i < m_signalInfos.size(); ++i)
|
||||
{
|
||||
signal_info_t& signalInfo = m_signalInfos[i];
|
||||
XML::IXMLNode* node = XML::createNode(CHANNEL_NODE_NAME);
|
||||
XML::IXMLNode* nodeName = XML::createNode(CHANNEL_LABEL_NODE_NAME);
|
||||
|
||||
nodeName->setPCData(signalInfo.name.toASCIIString());
|
||||
node->addChild(nodeName);
|
||||
node->addChild(getDoubleNode(CHANNEL_MAX_NODE_NAME, signalInfo.max));
|
||||
node->addChild(getDoubleNode(CHANNEL_MIN_NODE_NAME, signalInfo.min));
|
||||
node->addChild(getDoubleNode(CHANNEL_MEAN_NODE_NAME, signalInfo.sum / signalInfo.nSample));
|
||||
|
||||
channelsNode->addChild(node);
|
||||
}
|
||||
rootNode->addChild(channelsNode);
|
||||
|
||||
XML::IXMLHandler* handler = XML::createXMLHandler();
|
||||
if (!handler->writeXMLInFile(*rootNode, m_filename.toASCIIString())) { res = false; }
|
||||
|
||||
handler->release();
|
||||
rootNode->release();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmStatisticGenerator::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmStatisticGenerator::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_signalDecoder.decode(i);
|
||||
if (m_signalDecoder.isHeaderReceived())
|
||||
{
|
||||
const size_t mountChannel = m_signalDecoder.getOutputMatrix()->getDimensionSize(0);
|
||||
m_hasBeenStreamed = true;
|
||||
for (size_t j = 0; j < mountChannel; ++j)
|
||||
{
|
||||
signal_info_t info = {
|
||||
m_signalDecoder.getOutputMatrix()->getDimensionLabel(0, j), std::numeric_limits<double>::max(), -std::numeric_limits<double>::max(), 0, 0
|
||||
};
|
||||
m_signalInfos.push_back(info);
|
||||
}
|
||||
}
|
||||
if (m_signalDecoder.isBufferReceived())
|
||||
{
|
||||
const size_t nSample = m_signalDecoder.getOutputMatrix()->getDimensionSize(1);
|
||||
double* buffer = m_signalDecoder.getOutputMatrix()->getBuffer();
|
||||
for (size_t j = 0; j < m_signalInfos.size(); ++j)
|
||||
{
|
||||
signal_info_t& info = m_signalInfos[j];
|
||||
for (size_t k = 0; k < nSample; ++k)
|
||||
{
|
||||
const double sample = buffer[j * nSample + k];
|
||||
info.sum += sample;
|
||||
|
||||
if (sample < info.min) { info.min = sample; }
|
||||
if (sample > info.max) { info.max = sample; }
|
||||
}
|
||||
info.nSample += nSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_stimDecoder.decode(i);
|
||||
if (m_stimDecoder.isHeaderReceived()) { m_hasBeenStreamed = true; }
|
||||
if (m_stimDecoder.isBufferReceived())
|
||||
{
|
||||
IStimulationSet& stimSet = *(m_stimDecoder.getOutputStimulationSet());
|
||||
for (size_t j = 0; j < stimSet.getStimulationCount(); ++j) { m_stimulations[stimSet.getStimulationIdentifier(j)]++; }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
XML::IXMLNode* CBoxAlgorithmStatisticGenerator::getDoubleNode(const char* const nodeName, const double value)
|
||||
{
|
||||
XML::IXMLNode* tmp = XML::createNode(nodeName);
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(10) << value;
|
||||
tmp->setPCData(ss.str().c_str());
|
||||
return tmp;
|
||||
}
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
typedef struct
|
||||
{
|
||||
CString name;
|
||||
double min;
|
||||
double max;
|
||||
double sum;
|
||||
size_t nSample;
|
||||
} signal_info_t;
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmStatisticGenerator
|
||||
* \author Serrière Guillaume (Inria)
|
||||
* \date Thu Apr 30 15:24:39 2015
|
||||
* \brief The class CBoxAlgorithmStatisticGenerator describes the box Statistic generator.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmStatisticGenerator final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_StatisticGenerator)
|
||||
|
||||
private:
|
||||
static XML::IXMLNode* getDoubleNode(const char* nodeName, double value);
|
||||
|
||||
// Input decoder:
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmStatisticGenerator> m_signalDecoder;
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmStatisticGenerator> m_stimDecoder;
|
||||
|
||||
CString m_filename;
|
||||
std::map<CIdentifier, size_t> m_stimulations;
|
||||
std::vector<signal_info_t> m_signalInfos;
|
||||
|
||||
bool m_hasBeenStreamed = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmStatisticGeneratorDesc
|
||||
* \author Serrière Guillaume (Inria)
|
||||
* \date Thu Apr 30 15:24:39 2015
|
||||
* \brief Descriptor of the box Statistic generator.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmStatisticGeneratorDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("General statistics generator"); }
|
||||
CString getAuthorName() const override { return CString("Serrière Guillaume"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Generate statistics on signal."); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString("Generate some general purpose statistics on signal and store them in a file."); }
|
||||
|
||||
CString getCategory() const override { return CString("Evaluation"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-yes"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_StatisticGenerator; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmStatisticGenerator; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Signal",OV_TypeId_Signal);
|
||||
prototype.addInput("Stimulations",OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addSetting("Filename for saving",OV_TypeId_Filename, "${Path_UserData}/statistics-dump.xml");
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_StatisticGeneratorDesc)
|
||||
};
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
#include "ovpCROCCurveDraw.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
static void size_allocate_cb(GtkWidget* /*widget*/, GdkRectangle* rectangle, gpointer data) { static_cast<CROCCurveDraw*>(data)->resizeEvent(rectangle); }
|
||||
static void area_expose_cb(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, gpointer data) { static_cast<CROCCurveDraw*>(data)->exposeEnvent(); }
|
||||
|
||||
CROCCurveDraw::CROCCurveDraw(GtkNotebook* notebook, const size_t classIndex, CString& className)
|
||||
{
|
||||
m_margin = 50;
|
||||
m_classIdx = classIndex;
|
||||
m_hasBeenInit = false;
|
||||
m_hasBeenExposed = false;
|
||||
m_drawableArea = gtk_drawing_area_new();
|
||||
gtk_widget_set_size_request(m_drawableArea, 700, 600);
|
||||
|
||||
g_signal_connect(G_OBJECT(m_drawableArea), "expose_event", G_CALLBACK(area_expose_cb), this);
|
||||
g_signal_connect(G_OBJECT(m_drawableArea), "size-allocate", G_CALLBACK(size_allocate_cb), this);
|
||||
|
||||
GtkWidget* label = gtk_label_new(className.toASCIIString());
|
||||
gtk_notebook_append_page(notebook, m_drawableArea, label);
|
||||
|
||||
|
||||
//get left ruler widget's font description
|
||||
PangoContext* ctx = gtk_widget_get_pango_context(m_drawableArea);
|
||||
PangoFontDescription* fontDesc = pango_context_get_font_description(ctx);
|
||||
|
||||
//adapt the allocated height per label to the font's height (plus 4 pixel to add some spacing)
|
||||
if (pango_font_description_get_size_is_absolute(fontDesc)) { m_pixelsPerLeftRulerLabel = pango_font_description_get_size(fontDesc) + 4; }
|
||||
else { m_pixelsPerLeftRulerLabel = pango_font_description_get_size(fontDesc) / PANGO_SCALE + 4; }
|
||||
}
|
||||
|
||||
void CROCCurveDraw::generateCurve()
|
||||
{
|
||||
GtkAllocation allocation;
|
||||
gtk_widget_get_allocation(m_drawableArea, &allocation);
|
||||
|
||||
const size_t width = allocation.width - 2 * m_margin;
|
||||
const size_t height = allocation.height - 2 * m_margin;
|
||||
|
||||
m_pointList.clear();
|
||||
for (size_t i = 0; i < m_coordinateList.size(); ++i)
|
||||
{
|
||||
GdkPoint point;
|
||||
point.x = gint(m_coordinateList[i].first * width + m_margin);
|
||||
point.y = gint((allocation.height - m_margin) - m_coordinateList[i].second * height);
|
||||
m_pointList.push_back(point);
|
||||
}
|
||||
m_hasBeenInit = true;
|
||||
}
|
||||
|
||||
void CROCCurveDraw::exposeEnvent()
|
||||
{
|
||||
m_hasBeenExposed = true;
|
||||
redraw();
|
||||
}
|
||||
|
||||
void CROCCurveDraw::resizeEvent(GdkRectangle* /*rectangle*/)
|
||||
{
|
||||
GtkAllocation alloc;
|
||||
gtk_widget_get_allocation(m_drawableArea, &alloc);
|
||||
|
||||
if (!m_hasBeenInit) { return; }
|
||||
|
||||
generateCurve();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void CROCCurveDraw::redraw()
|
||||
{
|
||||
if (!m_hasBeenInit || !m_hasBeenExposed) { return; }
|
||||
|
||||
GtkAllocation allocation;
|
||||
gtk_widget_get_allocation(m_drawableArea, &allocation);
|
||||
|
||||
gdk_draw_rectangle(m_drawableArea->window, GTK_WIDGET(m_drawableArea)->style->white_gc, TRUE, 0, 0, allocation.width, allocation.height);
|
||||
|
||||
|
||||
GdkColor lineColor = { 0, 35000, 35000, 35000 };
|
||||
GdkGC* gc = gdk_gc_new((m_drawableArea)->window);
|
||||
gdk_gc_set_rgb_fg_color(gc, &lineColor);
|
||||
|
||||
|
||||
//Left ruler
|
||||
gdk_draw_line((m_drawableArea)->window, gc, gint(m_margin), gint(m_margin), gint(m_margin), gint(allocation.height - m_margin));
|
||||
drawLeftMark(m_margin, m_margin, "1");
|
||||
drawLeftMark(m_margin, allocation.height / 2, "0.5");
|
||||
drawLeftMark(m_margin, allocation.height - m_margin, "0");
|
||||
|
||||
//*** Black magic section to rotate the text of the left ruler. The solution comes from the internet (gtk doc), it works so
|
||||
// don't touch it unless you are sure of what you are doing
|
||||
PangoContext* context = gtk_widget_get_pango_context(m_drawableArea);
|
||||
GdkScreen* screen = gdk_drawable_get_screen(m_drawableArea->window);
|
||||
PangoRenderer* renderer = gdk_pango_renderer_get_default(screen);
|
||||
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), m_drawableArea->window);
|
||||
GdkGC* rotationGc = gdk_gc_new(m_drawableArea->window);
|
||||
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), rotationGc);
|
||||
int width, height;
|
||||
PangoMatrix matrix = PANGO_MATRIX_INIT;
|
||||
pango_matrix_translate(&matrix, 0, double(allocation.height + 100) / 2);
|
||||
PangoLayout* layout = pango_layout_new(context);
|
||||
pango_layout_set_text(layout, "True Positive Rate", -1);
|
||||
PangoFontDescription* desc = pango_context_get_font_description(context);
|
||||
pango_layout_set_font_description(layout, desc);
|
||||
GdkColor color = { 0, 0, 0, 0 };
|
||||
gdk_pango_renderer_set_override_color(GDK_PANGO_RENDERER(renderer), PANGO_RENDER_PART_FOREGROUND, &color);
|
||||
|
||||
pango_matrix_rotate(&matrix, 90);
|
||||
pango_context_set_matrix(context, &matrix);
|
||||
pango_layout_context_changed(layout);
|
||||
pango_layout_get_size(layout, &width, &height);
|
||||
pango_renderer_draw_layout(renderer, layout, 15, (allocation.height + height) / 2);
|
||||
|
||||
gdk_pango_renderer_set_override_color(GDK_PANGO_RENDERER(renderer), PANGO_RENDER_PART_FOREGROUND, nullptr);
|
||||
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), nullptr);
|
||||
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), nullptr);
|
||||
|
||||
pango_matrix_rotate(&matrix, -90);
|
||||
pango_context_set_matrix(context, &matrix);
|
||||
pango_layout_context_changed(layout);
|
||||
|
||||
g_object_unref(layout);
|
||||
g_object_unref(context);
|
||||
g_object_unref(rotationGc);
|
||||
//** End of black magic section
|
||||
|
||||
//Bottom ruler
|
||||
gdk_draw_line((m_drawableArea)->window, gc, gint(m_margin), gint(allocation.height - m_margin), gint(allocation.width - m_margin),
|
||||
gint(allocation.height - m_margin));
|
||||
drawBottomMark(m_margin, allocation.height - m_margin, "0");
|
||||
drawBottomMark(allocation.width / 2, allocation.height - m_margin, "0.5");
|
||||
drawBottomMark(allocation.width - m_margin, allocation.height - m_margin, "1");
|
||||
|
||||
int textW;
|
||||
int textH;
|
||||
PangoLayout* text = gtk_widget_create_pango_layout(m_drawableArea, "False positive rate");
|
||||
pango_layout_set_justify(text, PANGO_ALIGN_CENTER);
|
||||
pango_layout_get_pixel_size(text, &textW, &textH);
|
||||
gdk_draw_layout(m_drawableArea->window, GTK_WIDGET(m_drawableArea)->style->black_gc, allocation.width / 2 - textW / 2, allocation.height - 15, text);
|
||||
g_object_unref(text);
|
||||
|
||||
|
||||
if (m_pointList.empty())
|
||||
{
|
||||
gdk_draw_lines((m_drawableArea)->window, GTK_WIDGET(m_drawableArea)->style->black_gc, &(m_pointList[0]), gint(m_pointList.size()));
|
||||
}
|
||||
|
||||
gdk_gc_set_line_attributes(gc, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_BEVEL);
|
||||
gdk_draw_line((m_drawableArea)->window, gc, gint(m_margin), gint(allocation.height - m_margin), gint(allocation.width - m_margin), gint(m_margin));
|
||||
|
||||
g_object_unref(gc);
|
||||
}
|
||||
|
||||
void CROCCurveDraw::drawLeftMark(const size_t w, const size_t h, const char* label) const
|
||||
{
|
||||
gint textW;
|
||||
gint textH;
|
||||
PangoLayout* text = gtk_widget_create_pango_layout(m_drawableArea, label);
|
||||
pango_layout_set_width(text, 28);
|
||||
pango_layout_set_justify(text, PANGO_ALIGN_LEFT);
|
||||
|
||||
pango_layout_get_pixel_size(text, &textW, &textH);
|
||||
|
||||
gdk_draw_layout(m_drawableArea->window, GTK_WIDGET(m_drawableArea)->style->black_gc, gint(w - 20) - (textW / 2), gint(h) - (textH / 2), text);
|
||||
|
||||
GdkColor lineColor = { 0, 35000, 35000, 35000 };
|
||||
GdkGC* gc = gdk_gc_new((m_drawableArea)->window);
|
||||
gdk_gc_set_rgb_fg_color(gc, &lineColor);
|
||||
gdk_draw_line(m_drawableArea->window, gc, w - 5, h, w, h);
|
||||
|
||||
g_object_unref(gc);
|
||||
}
|
||||
|
||||
void CROCCurveDraw::drawBottomMark(const size_t w, const size_t h, const char* label) const
|
||||
{
|
||||
int textW;
|
||||
int textH;
|
||||
PangoLayout* text = gtk_widget_create_pango_layout(m_drawableArea, label);
|
||||
pango_layout_set_width(text, 28);
|
||||
pango_layout_set_justify(text, PANGO_ALIGN_LEFT);
|
||||
|
||||
pango_layout_get_pixel_size(text, &textW, &textH);
|
||||
|
||||
gdk_draw_layout(m_drawableArea->window, GTK_WIDGET(m_drawableArea)->style->black_gc, gint(w) - (textW / 2), gint(h + 14), text);
|
||||
|
||||
GdkColor lineColor = { 0, 35000, 35000, 35000 };
|
||||
GdkGC* gc = gdk_gc_new((m_drawableArea)->window);
|
||||
gdk_gc_set_rgb_fg_color(gc, &lineColor);
|
||||
gdk_draw_line(m_drawableArea->window, gc, gint(w), gint(h + 5), gint(w), gint(h));
|
||||
|
||||
g_object_unref(gc);
|
||||
}
|
||||
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGTK
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
typedef std::pair<double, double> CCoordinate;
|
||||
|
||||
//The aim of the class is to handle the graphical part of a RocCurve
|
||||
class CROCCurveDraw final
|
||||
{
|
||||
public:
|
||||
CROCCurveDraw(GtkNotebook* notebook, size_t classIndex, CString& className);
|
||||
~CROCCurveDraw() { }
|
||||
std::vector<CCoordinate>& getCoordinateVector() { return m_coordinateList; }
|
||||
|
||||
void generateCurve();
|
||||
|
||||
//Callbak functions, should not be called
|
||||
void resizeEvent(GdkRectangle* rectangle);
|
||||
void exposeEnvent();
|
||||
|
||||
//This function is called when the cruve should be redraw for an external reason
|
||||
void forceRedraw() { redraw(); }
|
||||
|
||||
private:
|
||||
size_t m_margin = 0;
|
||||
size_t m_classIdx = 0;
|
||||
std::vector<GdkPoint> m_pointList;
|
||||
std::vector<CCoordinate> m_coordinateList;
|
||||
size_t m_pixelsPerLeftRulerLabel = 0;
|
||||
|
||||
GtkWidget* m_drawableArea = nullptr;
|
||||
bool m_hasBeenInit = false;
|
||||
|
||||
//For a mytical reason, gtk says that the DrawableArea is not a DrawableArea unless it's been exposed at least once...
|
||||
// So we need to if the DrawableArea as been exposed
|
||||
bool m_hasBeenExposed = false;
|
||||
|
||||
void redraw();
|
||||
void drawLeftMark(size_t w, size_t h, const char* label) const;
|
||||
void drawBottomMark(size_t w, size_t h, const char* label) const;
|
||||
};
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGTK
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_ClassifierAccuracyMeasure OpenViBE::CIdentifier(0x48395CE7, 0x17D62550)
|
||||
#define OVP_ClassId_BoxAlgorithm_ClassifierAccuracyMeasureDesc OpenViBE::CIdentifier(0x067F38CC, 0x084A6ED3)
|
||||
#define OVP_ClassId_Algorithm_ConfusionMatrix OpenViBE::CIdentifier(0x699F416B, 0x3BAE4324)
|
||||
#define OVP_ClassId_Algorithm_ConfusionMatrixDesc OpenViBE::CIdentifier(0x4CDD225D, 0x6C9A59DB)
|
||||
#define OVP_ClassId_BoxAlgorithm_ConfusionMatrix OpenViBE::CIdentifier(0x1AB625DA, 0x3B2502CE)
|
||||
#define OVP_ClassId_BoxAlgorithm_ConfusionMatrixDesc OpenViBE::CIdentifier(0x52237A64, 0x63555613)
|
||||
#define OVP_ClassId_BoxAlgorithm_KappaCoef OpenViBE::CIdentifier(0x160D8F1B, 0xD864C5BB)
|
||||
#define OVP_ClassId_BoxAlgorithm_KappaCoefDesc OpenViBE::CIdentifier(0xD8BA2199, 0xD252BECB)
|
||||
#define OVP_ClassId_BoxAlgorithm_ROCCurve OpenViBE::CIdentifier(0x06FE5B1B, 0xDE066FEC)
|
||||
#define OVP_ClassId_BoxAlgorithm_ROCCurveDesc OpenViBE::CIdentifier(0xCB5DFCEA, 0xAF41EAB2)
|
||||
#define OVP_ClassId_BoxAlgorithm_StatisticGenerator OpenViBE::CIdentifier(0x83EDA40B, 0x425FBFFE)
|
||||
#define OVP_ClassId_BoxAlgorithm_StatisticGeneratorDesc OpenViBE::CIdentifier(0x35A0CB63, 0x78882C28)
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Sums OpenViBE::CIdentifier(0x75502E8E, 0x05D838EE)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_Percentage OpenViBE::CIdentifier(0x7E504E8E, 0x058858EE)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_TargetStimulationSet OpenViBE::CIdentifier(0x7E504E8F, 0x058858EF)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassifierStimulationSet OpenViBE::CIdentifier(0x45220B61, 0x13FD7491)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputParameterId_ClassCodes OpenViBE::CIdentifier(0x67780C91, 0x2A556C51)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_OutputParameterId_ConfusionMatrix OpenViBE::CIdentifier(0x67780C91, 0x2A556C51)
|
||||
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetTarget OpenViBE::CIdentifier(0x4D390BDA, 0x6A180667)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_ResetClassifier OpenViBE::CIdentifier(0x3C132C38, 0x557D2503)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedTarget OpenViBE::CIdentifier(0x6B1E76B3, 0x06741B21)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_InputTriggerId_FeedClassifier OpenViBE::CIdentifier(0x3EFC64B8, 0x5ACC3125)
|
||||
#define OVP_Algorithm_ConfusionMatrixAlgorithm_OutputTriggerId_ConfusionPerformed OpenViBE::CIdentifier(0x790C2277, 0x3D041A63)
|
||||
|
||||
|
||||
#define FIRST_CLASS_SETTING_INDEX 2
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmStatisticGenerator.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmKappaCoefficient.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmConfusionMatrix.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmROCCurve.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmClassifierAccuracyMeasure.h"
|
||||
|
||||
#include "algorithms/ovpCAlgorithmConfusionMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Evaluation {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_BoxAlgorithmFlag, OV_AttributeId_Box_FlagIsUnstable.toString(),
|
||||
OV_AttributeId_Box_FlagIsUnstable.id());
|
||||
|
||||
OVP_Declare_New(CBoxAlgorithmStatisticGeneratorDesc);
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
OVP_Declare_New(CBoxAlgorithmKappaCoefDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmROCCurveDesc);
|
||||
#endif
|
||||
|
||||
OVP_Declare_New(CAlgorithmConfusionMatrixDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmConfusionMatrixDesc);
|
||||
#if defined(TARGET_HAS_ThirdPartyGTK)
|
||||
OVP_Declare_New(CBoxAlgorithmClassifierAccuracyMeasureDesc);
|
||||
#endif
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Evaluation
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Reference in New Issue
Block a user