This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,39 @@
PROJECT(openvibe-plugins-evaluation)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${PLUGINS_FOLDER}
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
# ---------------------------------
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEToolkit")
INCLUDE("FindOpenViBEModuleEBML")
INCLUDE("FindOpenViBEModuleXML")
INCLUDE("FindThirdPartyGTK")
INCLUDE("FindOpenViBEVisualizationToolkit")
# ---------------------------------
# Test applications
# ---------------------------------
IF(OV_COMPILE_TESTS)
ADD_SUBDIRECTORY(test)
ENDIF(OV_COMPILE_TESTS)
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/plugins/evaluation)
@@ -0,0 +1,55 @@
/**
* \page BoxAlgorithm_ClassifierAccuracyMeasure Classifier accuracy measure
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierAccuracyMeasure_Description|
The real-time classifier accuracies are displayed on vertical progress bars. The accuracy is computed given the results from classifiers, compared to the targets received.
* |OVP_DocEnd_BoxAlgorithm_ClassifierAccuracyMeasure_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierAccuracyMeasure_Inputs|
This box must have at least 2 inputs: one for the targets, and another one for a classifier processor results. User can add more classifier inputs at will.
* |OVP_DocEnd_BoxAlgorithm_ClassifierAccuracyMeasure_Inputs|
* |OVP_DocBegin_BoxAlgorithm_ClassifierAccuracyMeasure_Input1|
The targets the classifier aims at, using stimulation labels.
* |OVP_DocEnd_BoxAlgorithm_ClassifierAccuracyMeasure_Input1|
* |OVP_DocBegin_BoxAlgorithm_ClassifierAccuracyMeasure_Input2|
The classifier results coming from a classifier processor, using stimulation labels.
* |OVP_DocEnd_BoxAlgorithm_ClassifierAccuracyMeasure_Input2|
__________________________________________________________________
Online visualization settings
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierAccuracyMeasure_OnlineVisualizationSettings|
Online settings :
Setting1 : Reset scores
Setting2 : Show accuracies as percentages
* |OVP_DocEnd_BoxAlgorithm_ClassifierAccuracyMeasure_OnlineVisualizationSettings|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierAccuracyMeasure_Examples|
* |OVP_DocEnd_BoxAlgorithm_ClassifierAccuracyMeasure_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierAccuracyMeasure_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_ClassifierAccuracyMeasure_Miscellaneous|
*/
@@ -0,0 +1,79 @@
/**
* \page BoxAlgorithm_ConfusionMatrix Confusion Matrix
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Description|
The Confusion Matrix box performs real-time computation of the confusion matrix of a given classifier.
Confusion matrix can be used to measure the performance of a classifier.
The confusion matrix output can be filled with either percentages or values. Optional colum and row can be added to
give the sums of each row and column.
The confusion matrix output can be displayed usig a \ref BoxAlgorithm_MatrixDisplay.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Inputs|
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Inputs|
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Input1|
The stimulations that comes from the instruction flow, i.e. the targets that the classifier aims at.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Input1|
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Input2|
The classification results coming from a classifier. These stimulations will be compared to teh target to perform the computation.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Input2|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Outputs|
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Outputs|
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Output1|
The Confusion matrix.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Output1|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Settings|
More box settings can be added for a multi class classifier. The default configuration uses 2 classes.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Settings|
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Setting1|
Tells the box to put percentages or values in the matrix.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Setting1|
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Setting2|
If checked, this option adds one row and one column that gives the sums of each row and column in the matrix.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Setting2|
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Setting3|
The stimulation label for the first class.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Setting3|
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Setting4|
The stimulation label for the second class.
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Setting4|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Examples|
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ConfusionMatrix_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_ConfusionMatrix_Miscellaneous|
*/
@@ -0,0 +1,70 @@
/**
* \page BoxAlgorithm_KappaCoef Kappa Coefficient
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Description|
* This box computes the Cohen kappa coefficient that allows to compare the accordance of two classifiers (https://en.wikipedia.org/wiki/Cohen%27s_kappa),
* The box compares the results of the classifier (second input) to the 100% match classifier (first input).
*
* The result is streamed on the first output, and is displayed in real time in a standalone visualization.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Description|
*
*
_______________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Inputs|
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Inputs|
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Input1|
* The first input receives the expected stimulation.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Input1|
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Input2|
* This input receives the stimualtions found by the classifier.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Input2|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Outputs|
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Outputs|
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Output1|
* This output contains the current value of the Kappa coefficient.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Output1|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Settings|
* Each setting except the first one corresponds to the stimulation code of a class. A stimulation must be unique.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Settings|
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Setting1|
* This setting indicates the amount of classes handled by the box. This setting will change the amount of setting.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Setting1|
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Setting2|
* This setting indicates the stimulation corresponding to the first class.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Setting2|
*
* * |OVP_DocBegin_BoxAlgorithm_KappaCoef_Setting3|
* This setting indicates the stimulation corresponding to the second class.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Setting3|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Miscellaneous|
* All stimulations can be send to the box. They will be filtered.
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Miscellaneous|
*/
@@ -0,0 +1,65 @@
/**
* \page BoxAlgorithm_ROCCurve ROC Curve
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Description|
* This box computes the ROC (Receiver Operating Characteristic) curve for a classifier (https://fr.wikipedia.org/wiki/Receiver_Operating_Characteristic). One curve will be computes
* by class. This box is designed to work with the probability output of the \ref Doc_BoxAlgorithm_ClassifierProcessor.
*
* The box will compute the curve when it receives the computation trigger on the first input.
*
* The result is displayed when computed in a standalone visualization.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Description|
*
*
_______________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Inputs|
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Inputs|
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Input1|
The first input receives the expected stimulations stream.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Input1|
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Input2|
This input receives the probability output stream of the processor box.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Input2|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Settings|
* Each setting after the second one corresponds to the stimulation code of a class. A stimulation must be unique.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Settings|
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Setting1|
* Stimulation trigger for the computation.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Setting1|
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Setting2|
* This setting indicates the amount of classes handled by the box. This setting will change the amount of setting.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Setting2|
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Setting3|
* This setting indicates the stimulation corresponding to the first class.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Setting3|
*
* * |OVP_DocBegin_BoxAlgorithm_ROCCurve_Setting4|
* This setting indicates the stimulation corresponding to the second class.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Setting4|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ROCCurve_Miscellaneous|
* All stimulations can be send to the box. They will be filtered.
* |OVP_DocEnd_BoxAlgorithm_ROCCurve_Miscellaneous|
*/
@@ -0,0 +1,93 @@
/**
* \page BoxAlgorithm_GeneralStatisticsGenerator General Statistic Generator
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_GeneralStatisticsGenerator_Description|
* The box analyses a the two input stream (stimulations and signal).
*
* The box will provide for each channel of the signal the min, the max value and the mean.
* The box will provide a list of stimulations and provide for them the amount of time they appeared.
*
* |OVP_DocEnd_BoxAlgorithm_GeneralStatisticsGenerator_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_GeneralStatisticsGenerator_Inputs|
* |OVP_DocEnd_BoxAlgorithm_GeneralStatisticsGenerator_Inputs|
* |OVP_DocBegin_BoxAlgorithm_GeneralStatisticsGenerator_Input1|
* The signal stream to analyse.
* |OVP_DocEnd_BoxAlgorithm_GeneralStatisticsGenerator_Input1|
*
* |OVP_DocBegin_BoxAlgorithm_GeneralStatisticsGenerator_Input2|
* The stimulation stream to analyse.
* |OVP_DocEnd_BoxAlgorithm_GeneralStatisticsGenerator_Input2|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_GeneralStatisticsGenerator_Settings|
* |OVP_DocEnd_BoxAlgorithm_GeneralStatisticsGenerator_Settings|
* |OVP_DocBegin_BoxAlgorithm_GeneralStatisticsGenerator_Setting1|
* Path to the file where the results will be wrote.
* |OVP_DocEnd_BoxAlgorithm_GeneralStatisticsGenerator_Setting1|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_GeneralStatisticsGenerator_Examples|
* The resulting file should look like this :
\verbatim
<Statistic>
<Stimulations-list>
<Stimulation>
<Identifier>(0x00000000, 0x00008100)</Identifier>
<Label>OVTK_StimulationId_Label_00</Label>
<Count>500</Count>
</Stimulation>
</Stimulations-list>
<Channel-list>
<Channel>
<Name>sinusOsc 1</Name>
<Maximum>2.99595</Maximum>
<Minimum>-2.99582</Minimum>
<Mean>0.00374409</Mean>
</Channel>
<Channel>
<Name>sinusOsc 2</Name>
<Maximum>2.99555</Maximum>
<Minimum>-2.99594</Minimum>
<Mean>0.00215169</Mean>
</Channel>
<Channel>
<Name>sinusOsc 3</Name>
<Maximum>2.99594</Maximum>
<Minimum>-2.99592</Minimum>
<Mean>0.000474523</Mean>
</Channel>
<Channel>
<Name>sinusOsc 4</Name>
<Maximum>2.99591</Maximum>
<Minimum>-2.99594</Minimum>
<Mean>0.00127219</Mean>
</Channel>
</Channel-list>
</Statistic>
\endverbatim
* |OVP_DocEnd_BoxAlgorithm_GeneralStatisticsGenerator_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KappaCoef_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_KappaCoef_Miscellaneous|
*
*/
@@ -0,0 +1,111 @@
<?xml version="1.0"?>
<interface>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkWindow" id="classifier-accuracy-measure">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<object class="GtkTable" id="classifier-accuracy-measure-table">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">8</property>
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<property name="homogeneous">True</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<object class="GtkWindow" id="classifier-accuracy-measure-toolbar">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="title" translatable="yes">Classifier Accuracy Measure</property>
<property name="type_hint">dialog</property>
<child>
<object class="GtkToolbar" id="classifier-accuracy-measure-settings">
<property name="visible">True</property>
<property name="show_arrow">False</property>
<child>
<object class="GtkToolButton" id="reset-score-button">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">reset</property>
<property name="stock_id">gtk-refresh</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkSeparatorToolItem" id="separator">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</object>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<object class="GtkToggleToolButton" id="show-percentages-toggle-button">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">percentages</property>
<property name="stock_id">gtk-justify-center</property>
<property name="active">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToggleToolButton" id="show-scores-toggle-button">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">scores</property>
<property name="stock_id">gtk-justify-center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkWindow" id="dummy-window">
<child>
<object class="GtkVPaned" id="vertical-pannel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkProgressBar" id="progress-bar-classifier-accuracy">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="show_text">True</property>
<property name="text_xalign">0</property>
<property name="text_yalign">0</property>
<property name="fraction">0.20000000298023224</property>
<property name="orientation">bottom-to-top</property>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label-classifier-name">
<property name="visible">True</property>
<property name="label" translatable="yes">Classifier</property>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -0,0 +1,25 @@
PROJECT(test_evaluation)
IF(WIN32)
ADD_DEFINITIONS(-DTARGET_OS_Windows)
ENDIF(WIN32)
IF(UNIX)
ADD_DEFINITIONS(-DTARGET_OS_Linux)
ENDIF(UNIX)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
ADD_DEFINITIONS(-DTARGET_ARCHITECTURE_i386)
INCLUDE_DIRECTORIES(../src)
ADD_EXECUTABLE(${PROJECT_NAME} test_kappa.cpp)
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${TESTS_FOLDER}) # Place project in folder unit-test (for some IDE)
#INCLUDE("FindOpenViBE")
# Unfortunately we need to install the tests as any application to find .dll/.so files
# on both Windows and Linux.
OV_INSTALL_LAUNCH_SCRIPT(SCRIPT_PREFIX "${PROJECT_NAME}" EXECUTABLE_NAME "${PROJECT_NAME}")
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
@@ -0,0 +1,48 @@
#blabla
# @FIXME there is a problem of using the global log, this will cause interference if any tests are run in parallel
IF(WIN32)
SET(EXT cmd)
SET(OS_FLAGS "--no-pause")
ELSE(WIN32)
SET(EXT sh)
SET(OS_FLAGS "")
ENDIF(WIN32)
ADD_TEST(clean_Evaluation_kappa "${CMAKE_COMMAND}" "-E" "remove" "-f" ${OV_LOGFILE})
ADD_TEST(run_Evaluation_kappa "$ENV{OV_BINARY_PATH}/openvibe-designer.${EXT}" ${OS_FLAGS} "--invisible" "--no-session-management" --define Plugin_Classification_RandomizeKFoldTestData true "--play-fast" Test-kappa.xml)
#ADD_TEST(compare_Evaluation_kappa "$ENV{OV_BINARY_PATH}/test_evaluation.${EXT}" "${OS_FLAGS}" "${OV_LOGFILE}")
# It would be better to clean last, but we can't do this as it will delete the
# output we wish to include, and we can't prevent clean from running if a prev. test fails
# We need the clean to be sure that the comparator stage is not getting data from a previous run.
SET_TESTS_PROPERTIES(run_Evaluation_kappa PROPERTIES DEPENDS clean_Evaluation_kappa)
SET_TESTS_PROPERTIES(run_Evaluation_kappa PROPERTIES ATTACHED_FILES_ON_FAIL ${OV_LOGFILE})
#SET_TESTS_PROPERTIES(compare_Evaluation_kappa PROPERTIES DEPENDS run_Evaluation_kappa)
#SET_TESTS_PROPERTIES(compare_Evaluation_kappa PROPERTIES ATTACHED_FILES_ON_FAIL ${OV_LOGFILE})
ADD_TEST(run_Evaluation_statistic_generator "$ENV{OV_BINARY_PATH}/openvibe-designer.${EXT}" ${OS_FLAGS} "--invisible" "--no-session-management" --define Plugin_Classification_RandomizeKFoldTestData true "--play-fast" Test-StatisticGenerator.xml)
ADD_TEST(compare_Evaluation_statistic_generator "git" "diff" "--no-index" "--ignore-space-change" "stat.xml" "data/Statistic_comparison.xml")
ADD_TEST(clean_Evaluation_statistic_generator "${CMAKE_COMMAND}" "-E" "remove" "-f" stat.xml)
SET_TESTS_PROPERTIES(compare_Evaluation_statistic_generator PROPERTIES DEPENDS run_Evaluation_statistic_generator)
SET_TESTS_PROPERTIES(compare_Evaluation_statistic_generator PROPERTIES ATTACHED_FILES_ON_FAIL ${OV_LOGFILE})
### Do not enable the commented out sikuli tests unless you
### or your lab commits to keep them passing in the long term.
#FIND_PROGRAM(SIKULI NAMES sikuli-ide)
#IF(SIKULI)
# IF(UNIX)
# ADD_TEST(sikuli_clean_Evaluation_ROC "${CMAKE_COMMAND}" "-E" "remove" "-f" ${OV_LOGFILE} screenshot.png)
# ADD_TEST(sikuli_run_Evaluation_ROC "${SIKULI}" -t testROCCurve.UNIX.sikuli)
#
# SET_TESTS_PROPERTIES(sikuli_run_Evaluation_ROC PROPERTIES DEPENDS sikuli_clean_Evaluation_ROC)
# SET_TESTS_PROPERTIES(sikuli_run_Evaluation_ROC PROPERTIES ATTACHED_FILES_ON_FAIL ${OV_LOGFILE})
# SET_TESTS_PROPERTIES(sikuli_run_Evaluation_ROC PROPERTIES ATTACHED_FILES_ON_FAIL ${CTEST_SOURCE_DIRECTORY}/plugins/processing/evaluation/test/screenshot.png)
# ENDIF(UNIX)
#ENDIF(SIKULI)
@@ -0,0 +1,451 @@
<OpenViBE-Scenario>
<FormatVersion>1</FormatVersion>
<Creator>openvibe</Creator>
<CreatorVersion>2.0</CreatorVersion>
<Boxes>
<Box>
<Identifier>(0x29c00b5a, 0x38e642d5)</Identifier>
<Name>General statistics generator</Name>
<AlgorithmClassIdentifier>(0x83eda40b, 0x425fbffe)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
<Name>Signal</Name>
</Input>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Stimulations</Name>
</Input>
</Inputs>
<Settings>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Filename for saving</Name>
<DefaultValue></DefaultValue>
<Value>${__volatile_ScenarioDir}/stat.xml</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>240.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
<Value>43</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>512.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xf6674389, 0x42f4fe25)</Value>
</Attribute>
<Attribute>
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
<Value>190</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x002abac0)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc67a01dc, 0x28ce06c1)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x2b584b95, 0x7008d0de)</Identifier>
<Name>Player Controller</Name>
<AlgorithmClassIdentifier>(0x5f426dce, 0x08456e13)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Stimulations</Name>
</Input>
</Inputs>
<Settings>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Stimulation name</Name>
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
<Value>OVTK_StimulationId_Label_00</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0xcc14d8d6, 0xf27ecb73)</TypeIdentifier>
<Name>Action to perform</Name>
<DefaultValue>Pause</DefaultValue>
<Value>Stop</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>256.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
<Value>23</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>800.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x568d148e, 0x650792b3)</Value>
</Attribute>
<Attribute>
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
<Value>118</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x0012be4a)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x2b907080, 0x224d7576)</Identifier>
<Name>Sinus oscillator</Name>
<AlgorithmClassIdentifier>(0x7e33bdb8, 0x68194a4a)</AlgorithmClassIdentifier>
<Outputs>
<Output>
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
<Name>Generated signal</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Channel count</Name>
<DefaultValue>4</DefaultValue>
<Value>4</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Sampling frequency</Name>
<DefaultValue>512</DefaultValue>
<Value>512</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Generated epoch sample count</Name>
<DefaultValue>32</DefaultValue>
<Value>32</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>128.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
<Value>23</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>432.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x0b214ed8, 0x1f9ad83a)</Value>
</Attribute>
<Attribute>
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
<Value>109</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x002cf528)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>3</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x592038f7, 0x3646c9fe)</Identifier>
<Name>Clock stimulator</Name>
<AlgorithmClassIdentifier>(0x4f756d3f, 0x29ff0b96)</AlgorithmClassIdentifier>
<Outputs>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Generated stimulations</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
<Name>Interstimulation interval (in sec)</Name>
<DefaultValue>1.0</DefaultValue>
<Value>0.2</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Stimulation</Name>
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
<Value>OVTK_StimulationId_Label_00</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>126.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
<Value>23</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>608.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x27b3ee3c, 0xc50527e6)</Value>
</Attribute>
<Attribute>
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
<Value>114</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x0020b3d0)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x625d022b, 0x4c72dfaa)</Identifier>
<Name>Timeout</Name>
<AlgorithmClassIdentifier>(0x24fcd292, 0x5c8f6aa8)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>Input Stream</Name>
</Input>
</Inputs>
<Outputs>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Output Stimulations</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Timeout delay</Name>
<DefaultValue>5</DefaultValue>
<Value>100</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Output Stimulation</Name>
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
<Value>OVTK_StimulationId_Label_00</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>160.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
<Value>33</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>800.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x1eaee00e, 0xdb05d34e)</Value>
</Attribute>
<Attribute>
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
<Value>62</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x0011d368)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
</Boxes>
<Links>
<Link>
<Identifier>(0x0ff86487, 0x6c18ccc2)</Identifier>
<Source>
<BoxIdentifier>(0x592038f7, 0x3646c9fe)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x29c00b5a, 0x38e642d5)</BoxIdentifier>
<BoxInputIndex>1</BoxInputIndex>
</Target>
<Attributes>
<Attribute>
<Identifier>(0x1b32c44c, 0x1905e0e9)</Identifier>
<Value>144</Value>
</Attribute>
<Attribute>
<Identifier>(0x358ae8b5, 0x0f8bacd1)</Identifier>
<Value>608</Value>
</Attribute>
<Attribute>
<Identifier>(0x3f0a3b27, 0x570913d2)</Identifier>
<Value>214</Value>
</Attribute>
<Attribute>
<Identifier>(0x6267b5c5, 0x676e3e42)</Identifier>
<Value>519</Value>
</Attribute>
</Attributes>
</Link>
<Link>
<Identifier>(0x6299b60b, 0x63373c3e)</Identifier>
<Source>
<BoxIdentifier>(0x625d022b, 0x4c72dfaa)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x2b584b95, 0x7008d0de)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
<Attributes>
<Attribute>
<Identifier>(0x1b32c44c, 0x1905e0e9)</Identifier>
<Value>183</Value>
</Attribute>
<Attribute>
<Identifier>(0x358ae8b5, 0x0f8bacd1)</Identifier>
<Value>800</Value>
</Attribute>
<Attribute>
<Identifier>(0x3f0a3b27, 0x570913d2)</Identifier>
<Value>240</Value>
</Attribute>
<Attribute>
<Identifier>(0x6267b5c5, 0x676e3e42)</Identifier>
<Value>800</Value>
</Attribute>
</Attributes>
</Link>
<Link>
<Identifier>(0x7ed28c93, 0x1209a735)</Identifier>
<Source>
<BoxIdentifier>(0x2b907080, 0x224d7576)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x29c00b5a, 0x38e642d5)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
<Attributes>
<Attribute>
<Identifier>(0x1b32c44c, 0x1905e0e9)</Identifier>
<Value>146</Value>
</Attribute>
<Attribute>
<Identifier>(0x358ae8b5, 0x0f8bacd1)</Identifier>
<Value>432</Value>
</Attribute>
<Attribute>
<Identifier>(0x3f0a3b27, 0x570913d2)</Identifier>
<Value>214</Value>
</Attribute>
<Attribute>
<Identifier>(0x6267b5c5, 0x676e3e42)</Identifier>
<Value>504</Value>
</Attribute>
</Attributes>
</Link>
</Links>
<Comments></Comments>
<Metadata>
<Entry>
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x1586c3d8, 0x35a029ef)","index":0,"name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x4cfffb67, 0x1d6c7d8c)","index":0,"name":"Default tab","parentIdentifier":"(0x1586c3d8, 0x35a029ef)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x3e00f59d, 0x6e47aad3)","index":0,"name":"Empty","parentIdentifier":"(0x4cfffb67, 0x1d6c7d8c)","type":0}]</Data>
</Entry>
</Metadata>
</OpenViBE-Scenario>
@@ -0,0 +1,434 @@
<OpenViBE-Scenario>
<FormatVersion>2</FormatVersion>
<Creator>OpenViBE Designer</Creator>
<CreatorVersion>2.2.0</CreatorVersion>
<Settings></Settings>
<Inputs></Inputs>
<Outputs></Outputs>
<Boxes>
<Box>
<Identifier>(0x00001d5f, 0x00007a11)</Identifier>
<Name>Expected</Name>
<AlgorithmClassIdentifier>(0x336a3d9a, 0x753f1ba4)</AlgorithmClassIdentifier>
<Outputs>
<Output>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>Output stream</Name>
</Output>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Output stimulation</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Filename</Name>
<DefaultValue></DefaultValue>
<Value>${Player_ScenarioDirectory}/data/Expected_stimulation.csv</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>192</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>432</Value>
</Attribute>
<Attribute>
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xa9cdc629, 0xb153eb33)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00001d5f, 0x00007a12)</Identifier>
<Name>Found</Name>
<AlgorithmClassIdentifier>(0x336a3d9a, 0x753f1ba4)</AlgorithmClassIdentifier>
<Outputs>
<Output>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>Output stream</Name>
</Output>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Output stimulation</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Filename</Name>
<DefaultValue></DefaultValue>
<Value>${Player_ScenarioDirectory}/data/Found_stimulation.csv</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>160</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>560</Value>
</Attribute>
<Attribute>
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xa9cdc629, 0xb153eb33)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x0000555d, 0x00004472)</Identifier>
<Name>Stimulation listener</Name>
<AlgorithmClassIdentifier>(0x65731e1d, 0x47de5276)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Stimulation stream 1</Name>
</Input>
</Inputs>
<Settings>
<Setting>
<TypeIdentifier>(0xa88b3667, 0x0871638c)</TypeIdentifier>
<Name>Log level to use</Name>
<DefaultValue>Information</DefaultValue>
<Value>Information</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>272</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>640</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xf451ad91, 0x14c75f86)</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x107f1920, 0x151fda5f)</Identifier>
<Name>Player Controller</Name>
<AlgorithmClassIdentifier>(0x5f426dce, 0x08456e13)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Stimulations</Name>
</Input>
</Inputs>
<Settings>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Stimulation name</Name>
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
<Value>OVTK_StimulationId_Label_00</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0xcc14d8d6, 0xf27ecb73)</TypeIdentifier>
<Name>Action to perform</Name>
<DefaultValue>Pause</DefaultValue>
<Value>Stop</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>336</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>368</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x568d148e, 0x650792b3)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x006cdafc)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x2cc3b99b, 0x2d88ee53)</Identifier>
<Name>Kappa coefficient</Name>
<AlgorithmClassIdentifier>(0x160d8f1b, 0xd864c5bb)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Expected stimulations</Name>
</Input>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Found stimulations</Name>
</Input>
</Inputs>
<Outputs>
<Output>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>Confusion Matrix</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Amount of class</Name>
<DefaultValue>2</DefaultValue>
<Value>3</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Stimulation of class 1</Name>
<DefaultValue>OVTK_StimulationId_Number_00</DefaultValue>
<Value>OVTK_StimulationId_Label_00</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Stimulation of class 2</Name>
<DefaultValue>OVTK_StimulationId_Number_00</DefaultValue>
<Value>OVTK_StimulationId_Label_01</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Stimulation of class 3</Name>
<DefaultValue>OVTK_StimulationId_Number_00</DefaultValue>
<Value>OVTK_StimulationId_Label_02</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>272</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x08859f27, 0x4f7b5879)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x017d49da)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>3</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x5bdbea30, 0x7c64f6a8)</Identifier>
<Name>Timeout</Name>
<AlgorithmClassIdentifier>(0x24fcd292, 0x5c8f6aa8)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>Input Stream</Name>
</Input>
</Inputs>
<Outputs>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Output Stimulations</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Timeout delay</Name>
<DefaultValue>5</DefaultValue>
<Value>1</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
<Name>Output Stimulation</Name>
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
<Value>OVTK_StimulationId_Label_00</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>272</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>368</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x1eaee00e, 0xdb05d34e)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x02337a82)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
</Boxes>
<Links>
<Link>
<Identifier>(0x00001201, 0x00006cc6)</Identifier>
<Source>
<BoxIdentifier>(0x00001d5f, 0x00007a12)</BoxIdentifier>
<BoxOutputIndex>1</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x0000555d, 0x00004472)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00004a37, 0x00005687)</Identifier>
<Source>
<BoxIdentifier>(0x00001d5f, 0x00007a12)</BoxIdentifier>
<BoxOutputIndex>1</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x2cc3b99b, 0x2d88ee53)</BoxIdentifier>
<BoxInputIndex>1</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00004cd4, 0x00001a75)</Identifier>
<Source>
<BoxIdentifier>(0x00001d5f, 0x00007a11)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x5bdbea30, 0x7c64f6a8)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00005a6a, 0x000006b5)</Identifier>
<Source>
<BoxIdentifier>(0x00001d5f, 0x00007a11)</BoxIdentifier>
<BoxOutputIndex>1</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x2cc3b99b, 0x2d88ee53)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x49781978, 0x10193d32)</Identifier>
<Source>
<BoxIdentifier>(0x5bdbea30, 0x7c64f6a8)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x107f1920, 0x151fda5f)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
</Links>
<Comments></Comments>
<Metadata>
<Entry>
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
<Data>[{"boxIdentifier":"(0x2cc3b99b, 0x2d88ee53)","childCount":0,"identifier":"(0x086c5aaa, 0x55baf27b)","parentIdentifier":"(0xffffffff, 0xffffffff)","type":3},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x2f9f0580, 0x412699f4)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x010246fe, 0x0eacec10)","index":0,"name":"Default tab","parentIdentifier":"(0x2f9f0580, 0x412699f4)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x1fbe5a88, 0x65840069)","index":0,"name":"Empty","parentIdentifier":"(0x010246fe, 0x0eacec10)","type":0}]</Data>
</Entry>
</Metadata>
</OpenViBE-Scenario>
@@ -0,0 +1,5 @@
<OpenViBE-SettingsOverride>
<SettingValue>2.693394e-001 -2.082757e-001 2.391615e-001 2.344401e-001 3.927157e-001 2.381559e-001 1.105450e-001 5.748063e-001 3.098411e-001 2.888676e-001 1.972432e-001 -2.803966e-001 8.610541e-002 -2.669093e-001 -2.873429e-001 -3.080204e-001 -2.313958e-001 -3.049153e-001 -3.205304e-001 -4.424281e-001 -2.981316e-001 -3.624890e-001 -2.713850e-002 -7.049823e-001 -1.550608e-002 -1.321822e-001 1.119925e-001 1.023369e-002 5.826165e-002 -2.615791e-001 -2.637424e-001 -3.941850e-001 4.185020e-001 1.442614e-001 3.069018e-001 3.027673e-001 3.956899e-001 2.814762e-001 2.776705e-001 3.540791e-001 2.828738e-001 3.027926e-001 3.106658e-001 2.946954e-001 8.165735e-001 3.035239e-001 1.979707e-001 2.325919e-001 1.767888e-001 -1.389007e-001 -2.142701e-001 6.029197e-002 1.522051e-001 1.241781e-001 9.543869e-002 2.155692e-001 2.680507e-001 3.076862e-001 3.604533e-001 3.075757e-001 2.513993e-001 2.252315e-001 3.295531e-001 2.891706e-001 4.002560e-001 3.100291e-001 </SettingValue>
<SettingValue>6</SettingValue>
<SettingValue>11</SettingValue>
</OpenViBE-SettingsOverride>
@@ -0,0 +1,81 @@
Time:1x1,End Time,Noise 1:,Event Id,Event Date,Event Duration
0.2705078125,0.3398437500,0.0,33026,0.2705078125,0.0
0.3398437500,0.5419921875,0.0,33024,0.3398437500,0.0
0.5419921875,0.6796875000,0.0,33026,0.5419921875,0.0
0.6796875000,0.7099609375,0.0,33024,0.6796875000,0.0
0.7099609375,0.8125000000,0.0,33025,0.7099609375,0.0
0.8125000000,1.0195312500,0.0,33026,0.8125000000,0.0
1.0195312500,1.0839843750,0.0,33024,1.0195312500,0.0
1.0839843750,1.3544921875,0.0,33026,1.0839843750,0.0
1.3544921875,1.3593750000,0.0,33026,1.3544921875,0.0
1.3593750000,1.4199218750,0.0,33024,1.3593750000,0.0
1.4199218750,1.6259765625,0.0,33025,1.4199218750,0.0
1.6259765625,1.6992187500,0.0,33026,1.6259765625,0.0
1.6992187500,1.8964843750,0.0,33024,1.6992187500,0.0
1.8964843750,2.0390625000,0.0,33026,1.8964843750,0.0
2.0390625000,2.1298828125,0.0,33024,2.0390625000,0.0
2.1298828125,2.1679687500,0.0,33025,2.1298828125,0.0
2.1679687500,2.3798828125,0.0,33026,2.1679687500,0.0
2.3798828125,2.4384765625,0.0,33024,2.3798828125,0.0
2.4384765625,2.7099609375,0.0,33026,2.4384765625,0.0
2.7099609375,2.7197265625,0.0,33026,2.7099609375,0.0
2.7197265625,2.8398437500,0.0,33024,2.7197265625,0.0
2.8398437500,2.9804687500,0.0,33025,2.8398437500,0.0
2.9804687500,3.0595703125,0.0,33026,2.9804687500,0.0
3.0595703125,3.2519531250,0.0,33024,3.0595703125,0.0
3.2519531250,3.3994140625,0.0,33026,3.2519531250,0.0
3.3994140625,3.5224609375,0.0,33024,3.3994140625,0.0
3.5224609375,3.5498046875,0.0,33026,3.5224609375,0.0
3.5498046875,3.7392578125,0.0,33025,3.5498046875,0.0
3.7392578125,3.7939453125,0.0,33024,3.7392578125,0.0
3.7939453125,4.0644531250,0.0,33026,3.7939453125,0.0
4.0644531250,4.0791015625,0.0,33026,4.0644531250,0.0
4.0791015625,4.2597656250,0.0,33024,4.0791015625,0.0
4.2597656250,4.3359375000,0.0,33025,4.2597656250,0.0
4.3359375000,4.4199218750,0.0,33026,4.3359375000,0.0
4.4199218750,4.6064453125,0.0,33024,4.4199218750,0.0
4.6064453125,4.7597656250,0.0,33026,4.6064453125,0.0
4.7597656250,4.8779296875,0.0,33024,4.7597656250,0.0
4.8779296875,4.9697265625,0.0,33026,4.8779296875,0.0
4.9697265625,5.0996093750,0.0,33025,4.9697265625,0.0
5.0996093750,5.1484375000,0.0,33024,5.0996093750,0.0
5.1484375000,5.4199218750,0.0,33026,5.1484375000,0.0
5.4199218750,5.4394531250,0.0,33026,5.4199218750,0.0
5.4394531250,5.6796875000,0.0,33024,5.4394531250,0.0
5.6796875000,5.6904296875,0.0,33025,5.6796875000,0.0
5.6904296875,5.7792968750,0.0,33026,5.6904296875,0.0
5.7792968750,5.9619140625,0.0,33024,5.7792968750,0.0
5.9619140625,6.1191406250,0.0,33026,5.9619140625,0.0
6.1191406250,6.2324218750,0.0,33024,6.1191406250,0.0
6.2324218750,6.3896484375,0.0,33026,6.2324218750,0.0
6.3896484375,6.4599609375,0.0,33025,6.3896484375,0.0
6.4599609375,6.5039062500,0.0,33024,6.4599609375,0.0
6.5039062500,6.7744140625,0.0,33026,6.5039062500,0.0
6.7744140625,6.7998046875,0.0,33026,6.7744140625,0.0
6.7998046875,7.0458984375,0.0,33024,6.7998046875,0.0
7.0458984375,7.0996093750,0.0,33026,7.0458984375,0.0
7.0996093750,7.1396484375,0.0,33025,7.0996093750,0.0
7.1396484375,7.3164062500,0.0,33024,7.1396484375,0.0
7.3164062500,7.4794921875,0.0,33026,7.3164062500,0.0
7.4794921875,7.5878906250,0.0,33024,7.4794921875,0.0
7.5878906250,7.8095703125,0.0,33026,7.5878906250,0.0
7.8095703125,7.8193359375,0.0,33025,7.8095703125,0.0
7.8193359375,7.8583984375,0.0,33024,7.8193359375,0.0
7.8583984375,8.1298828125,0.0,33026,7.8583984375,0.0
8.1298828125,8.1591796875,0.0,33026,8.1298828125,0.0
8.1591796875,8.4003906250,0.0,33024,8.1591796875,0.0
8.4003906250,8.4990234375,0.0,33026,8.4003906250,0.0
8.4990234375,8.5195312500,0.0,33024,8.4990234375,0.0
8.5195312500,8.6718750000,0.0,33025,8.5195312500,0.0
8.6718750000,8.8398437500,0.0,33026,8.6718750000,0.0
8.8398437500,8.9423828125,0.0,33024,8.8398437500,0.0
8.9423828125,9.1796875000,0.0,33026,8.9423828125,0.0
9.1796875000,9.2138671875,0.0,33024,9.1796875000,0.0
9.2138671875,9.2294921875,0.0,33026,9.2138671875,0.0
9.2294921875,9.4843750000,0.0,33025,9.2294921875,0.0
9.4843750000,9.5195312500,0.0,33026,9.4843750000,0.0
9.5195312500,9.7558593750,0.0,33024,9.5195312500,0.0
9.7558593750,9.8593750000,0.0,33026,9.7558593750,0.0
9.8593750000,9.9394531250,0.0,33024,9.8593750000,0.0
9.9394531250,10.026367188,0.0,33025,9.9394531250,0.0
10.026367188,10.106445313,0.0,33026,10.026367188,0.0
1 Time:1x1 End Time Noise 1: Event Id Event Date Event Duration
2 0.2705078125 0.3398437500 0.0 33026 0.2705078125 0.0
3 0.3398437500 0.5419921875 0.0 33024 0.3398437500 0.0
4 0.5419921875 0.6796875000 0.0 33026 0.5419921875 0.0
5 0.6796875000 0.7099609375 0.0 33024 0.6796875000 0.0
6 0.7099609375 0.8125000000 0.0 33025 0.7099609375 0.0
7 0.8125000000 1.0195312500 0.0 33026 0.8125000000 0.0
8 1.0195312500 1.0839843750 0.0 33024 1.0195312500 0.0
9 1.0839843750 1.3544921875 0.0 33026 1.0839843750 0.0
10 1.3544921875 1.3593750000 0.0 33026 1.3544921875 0.0
11 1.3593750000 1.4199218750 0.0 33024 1.3593750000 0.0
12 1.4199218750 1.6259765625 0.0 33025 1.4199218750 0.0
13 1.6259765625 1.6992187500 0.0 33026 1.6259765625 0.0
14 1.6992187500 1.8964843750 0.0 33024 1.6992187500 0.0
15 1.8964843750 2.0390625000 0.0 33026 1.8964843750 0.0
16 2.0390625000 2.1298828125 0.0 33024 2.0390625000 0.0
17 2.1298828125 2.1679687500 0.0 33025 2.1298828125 0.0
18 2.1679687500 2.3798828125 0.0 33026 2.1679687500 0.0
19 2.3798828125 2.4384765625 0.0 33024 2.3798828125 0.0
20 2.4384765625 2.7099609375 0.0 33026 2.4384765625 0.0
21 2.7099609375 2.7197265625 0.0 33026 2.7099609375 0.0
22 2.7197265625 2.8398437500 0.0 33024 2.7197265625 0.0
23 2.8398437500 2.9804687500 0.0 33025 2.8398437500 0.0
24 2.9804687500 3.0595703125 0.0 33026 2.9804687500 0.0
25 3.0595703125 3.2519531250 0.0 33024 3.0595703125 0.0
26 3.2519531250 3.3994140625 0.0 33026 3.2519531250 0.0
27 3.3994140625 3.5224609375 0.0 33024 3.3994140625 0.0
28 3.5224609375 3.5498046875 0.0 33026 3.5224609375 0.0
29 3.5498046875 3.7392578125 0.0 33025 3.5498046875 0.0
30 3.7392578125 3.7939453125 0.0 33024 3.7392578125 0.0
31 3.7939453125 4.0644531250 0.0 33026 3.7939453125 0.0
32 4.0644531250 4.0791015625 0.0 33026 4.0644531250 0.0
33 4.0791015625 4.2597656250 0.0 33024 4.0791015625 0.0
34 4.2597656250 4.3359375000 0.0 33025 4.2597656250 0.0
35 4.3359375000 4.4199218750 0.0 33026 4.3359375000 0.0
36 4.4199218750 4.6064453125 0.0 33024 4.4199218750 0.0
37 4.6064453125 4.7597656250 0.0 33026 4.6064453125 0.0
38 4.7597656250 4.8779296875 0.0 33024 4.7597656250 0.0
39 4.8779296875 4.9697265625 0.0 33026 4.8779296875 0.0
40 4.9697265625 5.0996093750 0.0 33025 4.9697265625 0.0
41 5.0996093750 5.1484375000 0.0 33024 5.0996093750 0.0
42 5.1484375000 5.4199218750 0.0 33026 5.1484375000 0.0
43 5.4199218750 5.4394531250 0.0 33026 5.4199218750 0.0
44 5.4394531250 5.6796875000 0.0 33024 5.4394531250 0.0
45 5.6796875000 5.6904296875 0.0 33025 5.6796875000 0.0
46 5.6904296875 5.7792968750 0.0 33026 5.6904296875 0.0
47 5.7792968750 5.9619140625 0.0 33024 5.7792968750 0.0
48 5.9619140625 6.1191406250 0.0 33026 5.9619140625 0.0
49 6.1191406250 6.2324218750 0.0 33024 6.1191406250 0.0
50 6.2324218750 6.3896484375 0.0 33026 6.2324218750 0.0
51 6.3896484375 6.4599609375 0.0 33025 6.3896484375 0.0
52 6.4599609375 6.5039062500 0.0 33024 6.4599609375 0.0
53 6.5039062500 6.7744140625 0.0 33026 6.5039062500 0.0
54 6.7744140625 6.7998046875 0.0 33026 6.7744140625 0.0
55 6.7998046875 7.0458984375 0.0 33024 6.7998046875 0.0
56 7.0458984375 7.0996093750 0.0 33026 7.0458984375 0.0
57 7.0996093750 7.1396484375 0.0 33025 7.0996093750 0.0
58 7.1396484375 7.3164062500 0.0 33024 7.1396484375 0.0
59 7.3164062500 7.4794921875 0.0 33026 7.3164062500 0.0
60 7.4794921875 7.5878906250 0.0 33024 7.4794921875 0.0
61 7.5878906250 7.8095703125 0.0 33026 7.5878906250 0.0
62 7.8095703125 7.8193359375 0.0 33025 7.8095703125 0.0
63 7.8193359375 7.8583984375 0.0 33024 7.8193359375 0.0
64 7.8583984375 8.1298828125 0.0 33026 7.8583984375 0.0
65 8.1298828125 8.1591796875 0.0 33026 8.1298828125 0.0
66 8.1591796875 8.4003906250 0.0 33024 8.1591796875 0.0
67 8.4003906250 8.4990234375 0.0 33026 8.4003906250 0.0
68 8.4990234375 8.5195312500 0.0 33024 8.4990234375 0.0
69 8.5195312500 8.6718750000 0.0 33025 8.5195312500 0.0
70 8.6718750000 8.8398437500 0.0 33026 8.6718750000 0.0
71 8.8398437500 8.9423828125 0.0 33024 8.8398437500 0.0
72 8.9423828125 9.1796875000 0.0 33026 8.9423828125 0.0
73 9.1796875000 9.2138671875 0.0 33024 9.1796875000 0.0
74 9.2138671875 9.2294921875 0.0 33026 9.2138671875 0.0
75 9.2294921875 9.4843750000 0.0 33025 9.2294921875 0.0
76 9.4843750000 9.5195312500 0.0 33026 9.4843750000 0.0
77 9.5195312500 9.7558593750 0.0 33024 9.5195312500 0.0
78 9.7558593750 9.8593750000 0.0 33026 9.7558593750 0.0
79 9.8593750000 9.9394531250 0.0 33024 9.8593750000 0.0
80 9.9394531250 10.026367188 0.0 33025 9.9394531250 0.0
81 10.026367188 10.106445313 0.0 33026 10.026367188 0.0
@@ -0,0 +1,81 @@
Time:1x1,End Time,Noise 1:,Event Id,Event Date,Event Duration
0.2715078125,0.3408437500,0.0,33026,0.2715078125,0.0
0.3408437500,0.5429921875,0.0,33025,0.3408437500,0.0
0.5429921875,0.6806875000,0.0,33026,0.5429921875,0.0
0.6806875000,0.7109609375,0.0,33025,0.6806875000,0.0
0.7109609375,0.8135000000,0.0,33025,0.7109609375,0.0
0.8135000000,1.0295312500,0.0,33026,0.8135000000,0.0
1.0295312500,1.0939843750,0.0,33024,1.0295312500,0.0
1.0939843750,1.3569921875,0.0,33026,1.0939843750,0.0
1.3569921875,1.3693750000,0.0,33026,1.3569921875,0.0
1.3693750000,1.4299218750,0.0,33026,1.3693750000,0.0
1.4299218750,1.6359765625,0.0,33025,1.4299218750,0.0
1.6359765625,1.7092187500,0.0,33026,1.6359765625,0.0
1.7092187500,1.9064843750,0.0,33024,1.7092187500,0.0
1.9064843750,2.0490625000,0.0,33026,1.9064843750,0.0
2.0490625000,2.1398828125,0.0,33024,2.0490625000,0.0
2.1398828125,2.1779687500,0.0,33024,2.1398828125,0.0
2.1779687500,2.3898828125,0.0,33026,2.1779687500,0.0
2.3898828125,2.4484765625,0.0,33024,2.3898828125,0.0
2.4484765625,2.7159609375,0.0,33026,2.4484765625,0.0
2.7159609375,2.7297265625,0.0,33026,2.7159609375,0.0
2.7297265625,2.8498437500,0.0,33024,2.7297265625,0.0
2.8498437500,2.9904687500,0.0,33026,2.8498437500,0.0
2.9904687500,3.0695703125,0.0,33026,2.9904687500,0.0
3.0695703125,3.2619531250,0.0,33024,3.0695703125,0.0
3.2619531250,3.4094140625,0.0,33024,3.2619531250,0.0
3.4094140625,3.5324609375,0.0,33024,3.4094140625,0.0
3.5324609375,3.5598046875,0.0,33024,3.5324609375,0.0
3.5598046875,3.7492578125,0.0,33025,3.5598046875,0.0
3.7492578125,3.8039453125,0.0,33024,3.7492578125,0.0
3.8039453125,4.0744531250,0.0,33025,3.8039453125,0.0
4.0744531250,4.0891015625,0.0,33026,4.0744531250,0.0
4.0891015625,4.2697656250,0.0,33024,4.0891015625,0.0
4.2697656250,4.3459375000,0.0,33025,4.2697656250,0.0
4.3459375000,4.4299218750,0.0,33026,4.3459375000,0.0
4.4299218750,4.6164453125,0.0,33024,4.4299218750,0.0
4.6164453125,4.7697656250,0.0,33026,4.6164453125,0.0
4.7697656250,4.8879296875,0.0,33024,4.7697656250,0.0
4.8879296875,4.9797265625,0.0,33026,4.8879296875,0.0
4.9797265625,5.1096093750,0.0,33025,4.9797265625,0.0
5.1096093750,5.1584375000,0.0,33024,5.1096093750,0.0
5.1584375000,5.4299218750,0.0,33026,5.1584375000,0.0
5.4299218750,5.4494531250,0.0,33026,5.4299218750,0.0
5.4494531250,5.6896875000,0.0,33024,5.4494531250,0.0
5.6896875000,5.7004296875,0.0,33025,5.6896875000,0.0
5.7004296875,5.7892968750,0.0,33026,5.7004296875,0.0
5.7892968750,5.9719140625,0.0,33024,5.7892968750,0.0
5.9719140625,6.1291406250,0.0,33026,5.9719140625,0.0
6.1291406250,6.2424218750,0.0,33024,6.1291406250,0.0
6.2424218750,6.3996484375,0.0,33026,6.2424218750,0.0
6.3996484375,6.4699609375,0.0,33025,6.3996484375,0.0
6.4699609375,6.5139062500,0.0,33024,6.4699609375,0.0
6.5139062500,6.7844140625,0.0,33026,6.5139062500,0.0
6.7844140625,6.8098046875,0.0,33026,6.7844140625,0.0
6.8098046875,7.0558984375,0.0,33024,6.8098046875,0.0
7.0558984375,7.1096093750,0.0,33026,7.0558984375,0.0
7.1096093750,7.1496484375,0.0,33025,7.1096093750,0.0
7.1496484375,7.3264062500,0.0,33024,7.1496484375,0.0
7.3264062500,7.4894921875,0.0,33026,7.3264062500,0.0
7.4894921875,7.5978906250,0.0,33024,7.4894921875,0.0
7.5978906250,7.8155703125,0.0,33026,7.5978906250,0.0
7.8155703125,7.8293359375,0.0,33025,7.8155703125,0.0
7.8293359375,7.8683984375,0.0,33024,7.8293359375,0.0
7.8683984375,8.1398828125,0.0,33026,7.8683984375,0.0
8.1398828125,8.1691796875,0.0,33026,8.1398828125,0.0
8.1691796875,8.4103906250,0.0,33024,8.1691796875,0.0
8.4103906250,8.5090234375,0.0,33026,8.4103906250,0.0
8.5090234375,8.5295312500,0.0,33024,8.5090234375,0.0
8.5295312500,8.6818750000,0.0,33025,8.5295312500,0.0
8.6818750000,8.8498437500,0.0,33026,8.6818750000,0.0
8.8498437500,8.9523828125,0.0,33024,8.8498437500,0.0
8.9523828125,9.1896875000,0.0,33026,8.9523828125,0.0
9.1896875000,9.2238671875,0.0,33024,9.1896875000,0.0
9.2238671875,9.2394921875,0.0,33026,9.2238671875,0.0
9.2394921875,9.4943750000,0.0,33025,9.2394921875,0.0
9.4943750000,9.5295312500,0.0,33026,9.4943750000,0.0
9.5295312500,9.7658593750,0.0,33024,9.5295312500,0.0
9.7658593750,9.8693750000,0.0,33026,9.7658593750,0.0
9.8693750000,9.9494531250,0.0,33024,9.8693750000,0.0
9.9494531250,10.126367188,0.0,33025,9.9494531250,0.0
10.126367188,10.206445313,0.0,33026,100126367188,0.0
1 Time:1x1 End Time Noise 1: Event Id Event Date Event Duration
2 0.2715078125 0.3408437500 0.0 33026 0.2715078125 0.0
3 0.3408437500 0.5429921875 0.0 33025 0.3408437500 0.0
4 0.5429921875 0.6806875000 0.0 33026 0.5429921875 0.0
5 0.6806875000 0.7109609375 0.0 33025 0.6806875000 0.0
6 0.7109609375 0.8135000000 0.0 33025 0.7109609375 0.0
7 0.8135000000 1.0295312500 0.0 33026 0.8135000000 0.0
8 1.0295312500 1.0939843750 0.0 33024 1.0295312500 0.0
9 1.0939843750 1.3569921875 0.0 33026 1.0939843750 0.0
10 1.3569921875 1.3693750000 0.0 33026 1.3569921875 0.0
11 1.3693750000 1.4299218750 0.0 33026 1.3693750000 0.0
12 1.4299218750 1.6359765625 0.0 33025 1.4299218750 0.0
13 1.6359765625 1.7092187500 0.0 33026 1.6359765625 0.0
14 1.7092187500 1.9064843750 0.0 33024 1.7092187500 0.0
15 1.9064843750 2.0490625000 0.0 33026 1.9064843750 0.0
16 2.0490625000 2.1398828125 0.0 33024 2.0490625000 0.0
17 2.1398828125 2.1779687500 0.0 33024 2.1398828125 0.0
18 2.1779687500 2.3898828125 0.0 33026 2.1779687500 0.0
19 2.3898828125 2.4484765625 0.0 33024 2.3898828125 0.0
20 2.4484765625 2.7159609375 0.0 33026 2.4484765625 0.0
21 2.7159609375 2.7297265625 0.0 33026 2.7159609375 0.0
22 2.7297265625 2.8498437500 0.0 33024 2.7297265625 0.0
23 2.8498437500 2.9904687500 0.0 33026 2.8498437500 0.0
24 2.9904687500 3.0695703125 0.0 33026 2.9904687500 0.0
25 3.0695703125 3.2619531250 0.0 33024 3.0695703125 0.0
26 3.2619531250 3.4094140625 0.0 33024 3.2619531250 0.0
27 3.4094140625 3.5324609375 0.0 33024 3.4094140625 0.0
28 3.5324609375 3.5598046875 0.0 33024 3.5324609375 0.0
29 3.5598046875 3.7492578125 0.0 33025 3.5598046875 0.0
30 3.7492578125 3.8039453125 0.0 33024 3.7492578125 0.0
31 3.8039453125 4.0744531250 0.0 33025 3.8039453125 0.0
32 4.0744531250 4.0891015625 0.0 33026 4.0744531250 0.0
33 4.0891015625 4.2697656250 0.0 33024 4.0891015625 0.0
34 4.2697656250 4.3459375000 0.0 33025 4.2697656250 0.0
35 4.3459375000 4.4299218750 0.0 33026 4.3459375000 0.0
36 4.4299218750 4.6164453125 0.0 33024 4.4299218750 0.0
37 4.6164453125 4.7697656250 0.0 33026 4.6164453125 0.0
38 4.7697656250 4.8879296875 0.0 33024 4.7697656250 0.0
39 4.8879296875 4.9797265625 0.0 33026 4.8879296875 0.0
40 4.9797265625 5.1096093750 0.0 33025 4.9797265625 0.0
41 5.1096093750 5.1584375000 0.0 33024 5.1096093750 0.0
42 5.1584375000 5.4299218750 0.0 33026 5.1584375000 0.0
43 5.4299218750 5.4494531250 0.0 33026 5.4299218750 0.0
44 5.4494531250 5.6896875000 0.0 33024 5.4494531250 0.0
45 5.6896875000 5.7004296875 0.0 33025 5.6896875000 0.0
46 5.7004296875 5.7892968750 0.0 33026 5.7004296875 0.0
47 5.7892968750 5.9719140625 0.0 33024 5.7892968750 0.0
48 5.9719140625 6.1291406250 0.0 33026 5.9719140625 0.0
49 6.1291406250 6.2424218750 0.0 33024 6.1291406250 0.0
50 6.2424218750 6.3996484375 0.0 33026 6.2424218750 0.0
51 6.3996484375 6.4699609375 0.0 33025 6.3996484375 0.0
52 6.4699609375 6.5139062500 0.0 33024 6.4699609375 0.0
53 6.5139062500 6.7844140625 0.0 33026 6.5139062500 0.0
54 6.7844140625 6.8098046875 0.0 33026 6.7844140625 0.0
55 6.8098046875 7.0558984375 0.0 33024 6.8098046875 0.0
56 7.0558984375 7.1096093750 0.0 33026 7.0558984375 0.0
57 7.1096093750 7.1496484375 0.0 33025 7.1096093750 0.0
58 7.1496484375 7.3264062500 0.0 33024 7.1496484375 0.0
59 7.3264062500 7.4894921875 0.0 33026 7.3264062500 0.0
60 7.4894921875 7.5978906250 0.0 33024 7.4894921875 0.0
61 7.5978906250 7.8155703125 0.0 33026 7.5978906250 0.0
62 7.8155703125 7.8293359375 0.0 33025 7.8155703125 0.0
63 7.8293359375 7.8683984375 0.0 33024 7.8293359375 0.0
64 7.8683984375 8.1398828125 0.0 33026 7.8683984375 0.0
65 8.1398828125 8.1691796875 0.0 33026 8.1398828125 0.0
66 8.1691796875 8.4103906250 0.0 33024 8.1691796875 0.0
67 8.4103906250 8.5090234375 0.0 33026 8.4103906250 0.0
68 8.5090234375 8.5295312500 0.0 33024 8.5090234375 0.0
69 8.5295312500 8.6818750000 0.0 33025 8.5295312500 0.0
70 8.6818750000 8.8498437500 0.0 33026 8.6818750000 0.0
71 8.8498437500 8.9523828125 0.0 33024 8.8498437500 0.0
72 8.9523828125 9.1896875000 0.0 33026 8.9523828125 0.0
73 9.1896875000 9.2238671875 0.0 33024 9.1896875000 0.0
74 9.2238671875 9.2394921875 0.0 33026 9.2238671875 0.0
75 9.2394921875 9.4943750000 0.0 33025 9.2394921875 0.0
76 9.4943750000 9.5295312500 0.0 33026 9.4943750000 0.0
77 9.5295312500 9.7658593750 0.0 33024 9.5295312500 0.0
78 9.7658593750 9.8693750000 0.0 33026 9.7658593750 0.0
79 9.8693750000 9.9494531250 0.0 33024 9.8693750000 0.0
80 9.9494531250 10.126367188 0.0 33025 9.9494531250 0.0
81 10.126367188 10.206445313 0.0 33026 100126367188 0.0
@@ -0,0 +1,35 @@
<Statistic>
<Stimulations-list>
<Stimulation>
<Identifier>(0x00000000, 0x00008100)</Identifier>
<Label>OVTK_StimulationId_Label_00</Label>
<Count>500</Count>
</Stimulation>
</Stimulations-list>
<Channel-list>
<Channel>
<Name>sinusOsc 1</Name>
<Maximum>2.9959498935</Maximum>
<Minimum>-2.9958162250</Minimum>
<Mean>0.0037440891</Mean>
</Channel>
<Channel>
<Name>sinusOsc 2</Name>
<Maximum>2.9955453841</Maximum>
<Minimum>-2.9959417073</Minimum>
<Mean>0.0021516936</Mean>
</Channel>
<Channel>
<Name>sinusOsc 3</Name>
<Maximum>2.9959425273</Maximum>
<Minimum>-2.9959158246</Minimum>
<Mean>0.0004745227</Mean>
</Channel>
<Channel>
<Name>sinusOsc 4</Name>
<Maximum>2.9959140852</Maximum>
<Minimum>-2.9959417073</Minimum>
<Mean>0.0012721927</Mean>
</Channel>
</Channel-list>
</Statistic>
@@ -0,0 +1,16 @@
<OpenViBE-Classifier-Box XMLVersion="3">
<Strategy-Identifier class-id="(0xffffffff, 0xffffffff)">Native</Strategy-Identifier>
<Algorithm-Identifier class-id="(0x2ba17a3c, 0x1bd46d84)">Linear Discrimimant Analysis (LDA)</Algorithm-Identifier>
<Stimulations>
<Class-Stimulation class-id="1">OVTK_GDF_Left</Class-Stimulation>
<Class-Stimulation class-id="2">OVTK_GDF_Right</Class-Stimulation>
</Stimulations>
<OpenViBE-Classifier>
<LDA>
<Classes>1 2</Classes>
<Weights> -1.390939e+01 -3.837006e+01 -3.951661e+00 1.267055e+02 8.129262e+00 1.130540e+02</Weights>
<Bias-distance>1.04657</Bias-distance>
<Coefficient-probability>1.15779</Coefficient-probability>
</LDA>
</OpenViBE-Classifier>
</OpenViBE-Classifier-Box>
@@ -0,0 +1,50 @@
g_offset = nil
g_duration = nil
-- this function is called when the box is initialized
function initialize(box)
dofile(box:get_config("${Path_Data}") .. "/plugins/stimulation/lua-stimulator-stim-codes.lua")
g_offset = box:get_setting(2)
g_duration = box:get_setting(3)
end
-- this function is called when the box is uninitialized
function uninitialize(box)
end
function wait_until(box, time)
while box:get_current_time() < time do
box:sleep()
end
end
function wait_for(box, duration)
wait_until(box, box:get_current_time() + duration)
end
function process(box)
-- loops on every received stimulation for a given input
while box:keep_processing() do
for stimulation = 1, box:get_stimulation_count(1) do
-- gets the received stimulation
identifier, date, duration = box:get_stimulation(1, 1)
-- discards it
box:remove_stimulation(1, 1)
-- delay the OVTK_GDF_Left and Right
if identifier == OVTK_GDF_Left or identifier == OVTK_GDF_Right then
box:send_stimulation(1, OVTK_GDF_Correct, date+g_offset, 0)
box:send_stimulation(1, OVTK_GDF_Incorrect, date+g_offset+g_duration, 0)
end
end
box:sleep()
end
end
@@ -0,0 +1,29 @@
def setUp(self):
import os
import shutil
ov_binany_path=os.environ['OV_BINARY_PATH']
self.terminal = App.open("xterm -e " + ov_binany_path +"/openvibe-designer.sh --no-session-management --play-fast Test_ROCCurve.xml")
while not self.terminal.window():
wait(1)
#def takepicture:
#dir = os.path.dirname(getBundlePath()) # the folder, where your script is stored
#img = capture(SCREEN) # snapshots the screen
#shutil.move(img, os.path.join(dir, "shot.png"))
def testROCCurve(self):
import os
import shutil
try:
wait("ROCCurveResult.png",60)
assert(exists("ROCCurveResult.png"))
except (FindFailed, AssertionError):
print "Unable to find the required png"
dir = os.path.dirname(getBundlePath()) # the folder, where your script is stored
img = capture(SCREEN) # snapshots the screen
shutil.move(img, os.path.join(dir, "screenshot.png"))
raise
def tearDown(self):
self.terminal.close()
@@ -0,0 +1,46 @@
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cout << "Usage: test_evaluation <filename>\n";
return 3;
}
std::ifstream file(argv[1], std::ios::in);
if (file.good() && !file.bad() && file.is_open()) // ...
{
std::string line;
while (getline(file, line))
{
if (line.find("Final value of Kappa") != std::string::npos)
{
std::cout << "Found kappa line " << line << std::endl;
const size_t pos = line.rfind(' ');
const std::string cutline = line.substr(pos);
std::stringstream kappa(cutline);
double coefficient;
kappa >> coefficient;
if (coefficient != 0.840677)
{
std::cout << "Wrong Kappa coefficient. Found " << coefficient << " instead of 0.840677" << std::endl;
return 1;
}
std::cout << "Test ok" << std::endl;
return 0;
}
}
}
std::cout << "Error: Problem opening [" << argv[1] << "]\n";
return 2;
}