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,3 @@
# Add all the subdirs as projects of the named branch
OV_ADD_PROJECTS("CONTRIB_APPLICATIONS_DEVELOPER-TOOLS")
@@ -0,0 +1,113 @@
compilation.log
# Created by https://www.gitignore.io/api/python
# Edit at https://www.gitignore.io/?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# End of https://www.gitignore.io/api/python
@@ -0,0 +1,98 @@
///-------------------------------------------------------------------------------------------------
///
/// \file NewBoxPattern.h
/// \brief Class NewBoxPattern
/// \author Thibaut Monseigne (Inria) & Jimmy Leblanc (Polymont) & Yannis Bendi-Ouis (Polymont)
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/%22%3EGNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "CPolyBox.hpp"
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
namespace OpenViBE { namespace Plugins
{
namespace PyBox
{
class CBoxAlgorithmNewBoxPattern final : public CPolyBox
{
public:
CBoxAlgorithmNewBoxPattern() { m_script = "NewScript.py";}
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm < IBoxAlgorithm >, OVP_ClassId_BoxAlgorithm_NewBoxPattern)
};
class CBoxAlgorithmNewBoxPatternListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
box.setInputType(index, OV_TypeId_StreamedMatrix);
return true;
}
bool onOutputAdded(Kernel::IBox& box, const size_t index) override
{
box.setOutputType(index, OV_TypeId_StreamedMatrix);
return true;
}
_IsDerivedFromClass_Final_(Toolkit::TBoxListener < IBoxListener >, CIdentifier::undefined())
};
class CBoxAlgorithmNewBoxPatternDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("NewBoxPattern"); }
CString getAuthorName() const override { return CString("NewAuthor"); }
CString getAuthorCompanyName() const override { return CString("NewCompany"); }
CString getShortDescription() const override { return CString("Default Python Description"); }
CString getDetailedDescription() const override { return CString(""); }
CString getCategory() const override { return CString("Scripting/Pybox/"); }
CString getVersion() const override { return CString("0.1"); }
CString getStockItemName() const override { return CString("gtk-convert"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_NewBoxPattern; }
IPluginObject* create() override { return new CBoxAlgorithmNewBoxPattern; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmNewBoxPatternListener; }
void releaseBoxListener(IBoxListener* pBoxListener) const override{ delete pBoxListener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addSetting("Clock frequency (Hz)", OV_TypeId_Integer, "64");
// <tag> settings
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
prototype.addFlag(Kernel::BoxFlag_CanAddOutput);
prototype.addFlag(Kernel::BoxFlag_CanModifyOutput);
prototype.addFlag(Kernel::BoxFlag_CanAddSetting);
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
prototype.addInputSupport(OV_TypeId_Signal);
prototype.addInputSupport(OV_TypeId_Stimulations);
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
prototype.addOutputSupport(OV_TypeId_Signal);
prototype.addOutputSupport(OV_TypeId_Stimulations);
prototype.addOutputSupport(OV_TypeId_StreamedMatrix);
// <tag> input & output
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_NewBoxPatternDesc)
};
}
}
}
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,57 @@
PROJECT(openvibe-plugins-contrib-pybox)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
FILE(GLOB_RECURSE source_files src/*.cpp src/*.hpp src/*.h src/*.inl)
ADD_LIBRARY(${PROJECT_NAME} SHARED ${source_files})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${PLUGINS_FOLDER}
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
INCLUDE_DIRECTORIES("src")
# ---------------------------------
# OpenVibe Modules (uncomment usefull package)
# ---------------------------------
# OpenViBE Base
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEToolkit")
# OpenViBE Module
INCLUDE("FindOpenViBEModuleEBML")
INCLUDE("FindOpenViBEModuleSystem")
# OpenViBE Third Party
INCLUDE("FindThirdPartyBoost")
INCLUDE("FindThirdPartyPython3")
# ---------------------------------
# Target macros
# Defines target operating system
# Defines target architecture
# Defines target compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY box-tutorials DESTINATION ${DIST_DATADIR}/openvibe/scenarios/)
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/plugins/python3)
# ---------------------------------
# Test applications (uncomment to enable your test directory)
# ---------------------------------
#IF(OV_COMPILE_TESTS)
# ADD_SUBDIRECTORY(test)
#ENDIF(OV_COMPILE_TESTS)
@@ -0,0 +1,287 @@
# OpenViBE Python
This Project aims to bring modifications to OpenVibe and to widen its data-oriented functionnalities. OpenVibe is a signal processing software allowing the use of machine learning algorithms, however their number is reduced. Thus, we woud like to benefit from the Openvibe Python scripting box (which allows the use of python scripts in OV) in order to allow users to use Scikit-learn Machine Learning algorithms.
## Dependancies
`pip install pyqt5 pandas numpy natsort pygame sklearn matplotlib pyriemann`
## What OpenViBE Python allows
- The use of components already implemented in OpenVibe that will allow you to :
- The use of ML algorithms from Scikit-learn and Pyriemann (Library centered on the use of Riemannian geometry) that will allow you to train and store models.
- Visualization of your data in 2D/3D via a PCA or LDA.
- Easily create datasets compatible with OpenVibe's operation
- The use of a manager that allows you to simply :
- Create new boxes in OpenVibe
- Create new types of openvibe settings that can be used in your new boxes.
- The reuse of our scripts in order to simply implement your own python data management
1. The new boxes
TrainerML / ML Class Boxes
Box ProcessML
Box DataViz
DatasetCreator Box
2. The Pybox Manager
Box Manager
Stimulations / Labels Manager
Custom Settings Manager
3. Internal functioning and details
Our box model: PolyBox
Input management with PolyBox, two modes possible
Duplicating the Python Scripting Box
PolyBox: Automatic data storage
Managing Custom Settings
Translated with www.DeepL.com/Translator (free version)
## Table des matières
- [OpenViBE Python](#openvibe-python)
- [Dependancies](#dependancies)
- [What OpenViBE Python allows](#what-openvibe-python-allows)
- [Table des matières](#table-des-mati%c3%a8res)
- [1. The new boxes](#1-the-new-boxes)
- [TrainerML Class / ML Boxes](#trainerml-class--ml-boxes)
- [Scikit-learn](#scikit-learn)
- [Pyriemann](#pyriemann)
- [Box ProcessML](#box-processml)
- [Box DataViz](#box-dataviz)
- [Box DatasetCreator](#box-datasetcreator)
- [2. The Pybox Manager](#2-the-pybox-manager)
- [Box Manager](#box-manager)
- [Stimulations / Labels Manager](#stimulations--labels-manager)
- [Custom Settings Manager](#custom-settings-manager)
- [3. Internal Functioning and Details](#3-internal-functioning-and-details)
- [Our Box model : PolyBox](#our-box-model--polybox)
- [Input management with PolyBox, two possible modes](#input-management-with-polybox-two-possible-modes)
- [Duplicating the Python Scripting Box](#duplicating-the-python-scripting-box)
- [Automatic data storage](#automatic-data-storage)
- [Managing Custom Settings](#managing-custom-settings)
## 1. The new boxes
### TrainerML Class / ML Boxes
The TrainerML class defined in TrainerML.py is a class that inherits from [PolyBox](#our-box-model-polybox), its purpose is to be used in boxes that will be configured to allow the use of certain learning machine algorithms. The following parameters can be passed to it:
- **Filename to save model to** : Path to the file in which to save the model. If no file is indicated, then the model will not be saved.
- **Filename to load model from** : Path to the file in which the model to be loaded is saved. If a file is specified and exists, no model will be created and the model contained in the file will be loaded and used for the current session.
- **classifier** : Algorithm you wish to use. The various Algorithms are from Scikit-learn or Pyriemann.
- **discriminator** : In case the chosen classifier is `TangentSpace`, it is necessary to provide a second algorithm which will be used to classify after the projection on the tangent space. All the previous algorithms can be used except `TangentSpace` and `MDM`. If no algorithm is specified, `LinearDiscriminantAnalysis` will be used by default.
Similarly for `MDM`, a second algorithm may be used, but is not mandatory.
- **labels**: If the read mode is Poly-Mode, you must specify the list of labels as `my label1, my label2, my label3`, otherwise, the labels will be as follows `1, 2, 3, 4, ...`. If the read mode is ov-mode, you can leave the field blank, the labels will be extracted from the stimuli.
- **Test set share**: float between 0 and 1 (1 not included) which represents the proportion of the input dataset transformed into a test set, allowing us to evaluate our model once the training on the set train is done. If 0 is filled in, then all the data will constitute the train set and no metrics will be displayed.
Here are the new algorithms / boxes implemented in OV :
#### Scikit-learn
<https://scikit-learn.org/stable/>
> | Box Name | Algorithm |
> | :-: | :-: |
> | Nearest Centroid | NearestCentroid |
> | Nearest Neighbors Classifier | KNeighborsClassifier |
> | Gaussian Naive Bayes | GaussianNB |
> | Stochastic Gradient Descent | SGDClassifier |
> | Logistic Regression | LogisticRegression |
> | Decision Tree Classifier | DecisionTreeClassifier |
> | Extra Trees | ExtraTreesClassifier |
> | Bagging | BaggingClassifier |
> | Random Forest | RandomForestClassifier |
> | Support Vector Machine | LinearSVC |
> | Linear Discriminant Analysis | LinearDiscriminantAnalysis |
> | AdaBoost | AdaBoostClassifier |
> | Multi Layer Perceptron | MLPClassifier |
> | Linear SVC | LinearSVC |
#### Pyriemann
<https://pyriemann.readthedocs.io/en/latest/index.html>
> | String | Algorithm |
> | :-: | :-: |
> | Riemann Minimum Distance to Mean | MDM |
> | Riemann Tangent Space | TangentSpace |
You can easily find information on each of these methods in the docs of their library.
For each of these classifiers, an Openvibe box using TrainerML with the appropriate parameters has been created. So, if you want to train with the Random Forest algorithm of Scikit-learn for example, you just have to look for the associated box in Openvibe and you will be able to use it directly, and modify the parameters related to the algorithm. You will find the information concerning all these parameters on the respective pages of the algorithms, ex: <https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html.>
> ![random-forest.PNG](Assets/Doc/random-forest.PNG)
>
> Example of parameterization of a box implementing Random Forest
### Box ProcessML
This box allows you to make classification predictions on new data using a previously trained model (via TrainerML or the boxes that inherit it, e.g. SVM, LDA, RandomForest ...).
- **Model Filename**: Path where the model you want to use is stored.
- **Filename to save predictions** : Path to the file where to save the predictions. If no file is specified, then the predictions will not be saved. The predictions are saved as a string where each prediction is separated from the others by a comma: `pred1,pred2,pred3 ...`.
Once the model is loaded, the predictions will be made on each chunk of data received, they can be used in real time, and if a path is given, they can be saved for later use.
### Box DataViz
The DataViz Box allows the visualization of our data. To do this, it applies a dimension reduction via an LDA or PCA and then displays our data using the matplotlib library. It inherits from PolyBox. It requires the following parameters:
- **Path to save the model**: Path to the file in which to save the model. If no file is specified, then the model will not be saved.
- **Path to load the model** : Path to the file in which is saved the model you want to load. If a file is specified and exists, then no model will be created and the model contained in the file will be loaded and used for the current session.
- **Algorithm (PCA or LDA)** : Name of the algorithm to be used to reduce dimensions. Accepted values are `PCA` or `LDA`. The default algorithm used is LDA.
- **Dimension reduction**: Number of dimensions to display. The different possible values are 2 and 3. By default, if no number is given or if the field is filled incorrectly, the number of dimension is 2.
- **labels** : List of labels to indicate as `my label1, my label2, my label3` if the read mode is poly-mode. Otherwise, the labels will be in the form `1, 2, 3, 4, ...`.
### Box DatasetCreator
To facilitate data acquisition during our experiments, we created the python box `DatasetCreator`. This box takes a signal as an input and outputs a `OVTK_StimulationId_ExperimentStop' stimulation when it has finished creating the dataset.
It works in the following way: the user first chooses some labels, then the box will randomly determine an order between them. It will then record the user's brain activity while verbally indicating the current action. In this way, we will be able to create a labeled dataset that can be used for learning.
The recording of an action takes place in the following way:
1. Audible warning of the beginning of the action.
2. 2 second wait.
3. Recording for 10 seconds.
4. Audible warning of the end of the action.
5. Wait for 3 seconds.
It can be configured by indicating :
- The path to the directory that will contain the data.
- The number of folds you wish to obtain.
- The number of actions you wish to record. That is to say 30 if you wish to obtain 30 recordings of 10 seconds distributed among the different labels.
- The names of the labels you want to record (/!\ Attention, these names must have a corresponding mp3 file in `pybox-manager/Assets/Sounds/`, you can create new labels with the manager).
- A boolean indicating whether you want several CSVs or only one CSV. If you enter "true", then the data will be split into as many CSVs as there are actions, one CSV per action. If you enter "false", then the data will all be recorded in a single CSV, in which the start of a new recording for a label will be indicated in the stimuli.
## 2. The Pybox Manager
The Pybox Manager allows you to simply create and incorporate new boxes running a Python script of your choice, new labels/stimulations and new Custom Settings for your python boxes into OpenVibe.
To run it: `python pybox_manager.py`.
(Python 2.7 and python 3.X compatible.)
An option is available to enable the so-called "developer settings" i.e. the ones you created.
To do this, run the manager with the `-mode=developer` option.
### Box Manager
The Box Manager looks like this:
> ![box_manager.png](Assets/Doc/box_manager.png)
>
> PyBox Manager.
- New: Create a new box
- Duplicate: duplicates the currently selected box.
- Reset Box: resets the selected box, cancels all changes made since the last compilation.
- Category : category in which the box will be stored in OV.
- Author : add the author's name in the box's references.
- Settings: Allows you to manage all the settings necessary to use the box. These can be types from Openvibe (String, Float etc.) or types created by yourself with the [Custom settings Manager](#custom-settings-manager)
- Inputs: Used to fill in the different inputs that your box will receive.
- Outputs : Used to fill in the different outputs that your box will send.
- Enable settings / inputs / outputs modifications : Allows you to prevent the users of your box from modifying these elements later on.
- Mode : allows you to quickly configure the inputs of your box according to the mode you want to use. [Inputs Polybox](#input-management-with-polybox-two-possible-modes)
Once your modifications are finished you can press Build to have the modifications taken into account, and the compilation is done (mandatory to have your modifications and new boxes). The compilation.log file contains information from the last compilation.
Delete Box deletes the currently selected box (a compilation is necessary to take the deletion into account).
### Stimulations / Labels Manager
When reading and processing our signals, OpenViBE can handle stimulations. These can be used to label our data during a classification/training procedure.
All these stimulations are indicated in the file `pybox-manager/share/PolyStimulations.py`.
The DatasetCreator box we have created allows you to monitor the creation of a labeled dataset for the user. To do this, the box plays a sound during each action, indicating the action the user should think about. Basically, the user will have at his disposal about ten different labels.
We allow the user to add labels (stimulations) via the `Stimulations/Labels Manager` interface present in the manager.
To do this, the manager must provide the name of the label, and a corresponding `.mp3` file, which will be played by the DatasetCreator.
> ![add_label.jpeg](Assets/Doc/add_label.jpeg)
>
> Add a label/stimulation to OpenViBE.
### Custom Settings Manager
The `Custom Settings Manager` allows you to create or delete special types of settings, as well as possible values for these settings, according to your needs.
This is especially useful to allow easy use on OpenVibe using a drop-down list. For example, if you want to be able to choose one algorithm among several, directly in your OpenVibe box configuration, you just have to create this new type as well as the associated values via the manager as shown in the following illustration.
To be able to use them with the manager when defining the parameters of a box, you have to launch the manager with the option `mode=developer`.
> ![manage_custom_settings.png](Assets/Doc/manage_custom_settings.png)
>
> Add/Remove/Manage Custom Settings.
Result in OpenVibe :
> ![classifiers.png](Assets/Doc/classifiers.png)
>
> Example with a new type 'classifier'.
## 3. Internal Functioning and Details
### Our Box model : PolyBox
To facilitate our development phases, we have added in `pybox-manager/share/PolyBox.py` a class called `PolyBox`. This class inherits from the OVBox class. It automates the reception and storage of an input signal and allows the development of simple methods called at key moments:
- **on_initialize(self) :** called at initialization, this method aims at allowing the user to define a particular behavior during initialization.
- **on_header_received(self, header) :** called at each reception of a header, this method aims to allow the user to define a particular behavior when receiving a header.
- **on_chunk_received(self, chunk, label, shape) :** called at each reception of a chunk, this method aims to allow the user to define a particular behavior when receiving a chunk.
- **on_end_box(self) :** called just before the box finishes its work, this method aims to allow the user to define a particular behavior when the box finishes.
We have created this box in order to be able to attribute behaviors adapted to our use cases, in particular to easily create boxes proposing Machine Learning algorithms based on its architecture.
#### Input management with PolyBox, two possible modes
Any box inheriting the PolyBox automatically has two possible read modes to retrieve input data.
The first mode (`ov-mode`) corresponds to the classic OpenViBE reading mode: the different classes are all included in the same .csv file, and we use input stimuli to separate our data into different classes. To use this mode, you just have to give the box only two inputs: 1 StreamedMatrix and 1 Stimulation.
For example:
> ![ov-mode.jpeg](Assets/Doc/ov-mode.jpeg)
>
> **Figure 1** - ov-mode
The second mode (`poly-mode`) consists in considering as many files as there are classes, i.e. one .csv file per class. To read all these files, the box then needs to have at least as many StreamedMatrix inputs as there are different classes. To use this mode, you just have to create only StreamedMatrix inputs.
(Often, this mode requires a `label` parameter in which to fill in the labels of our different classes in the form: `my label1, my label2, my label3`).
For example:
> ![poly-mode.jpeg](Assets/Doc/poly-mode.jpeg)
> **Figure 2** - poly-mode
Thus, these two reading modes are transparent for the user and allow him to operate the `PolyBoxes` either with a single file containing all the actions, or with several files: one per action.
The user doesn't need to indicate anything for the box to choose the right behavior to adopt, the box chooses its behavior according to its inputs.
### Duplicating the Python Scripting Box
Toutes les modifications ont été faite, on peut maintenant compiler. Toutes ces étapes sont implémentées dans `pybox-manager/ov-manager.py`.
All the modifications we have made to the software are based on the duplication of the Python Scripting box. Since the latter allows the use of a python script, we decided to create a manager that allows us to automatically make changes in the OV code to duplicate the C++ files needed to duplicate the Python Scripting Box. This way, we can definitively associate a script to a box, and let the user configure it and integrate it into OpenViBE.
Each time a python box is created, the manager performs the following tasks:
1. Go to the root directory of the python boxes: `pybox-manager/src/`.
2. We duplicate `pybox-manager/Assets/BoxManager/NewBoxPattern-skeletton.h` into `ovpBoxName.h`
3. Insert the CIdentifier declaration into `src/defines.hpp`.
4. Add to `src/main.cpp` the imports of the newly created files for the box creation as well as the declarations.
5. In `src/box-algorithms/ovpBoxName.h`, replace the box name and includes/declarations.
6. Change the box name and description in `src/box-algorithms/ovpBoxName.h`.
7. Set the path of the script to be executed in `src/box-algorithms/ovpBoxName.h`.
8. Remove the possibility to change the path.
9. Add the parameters of our box to `src/box-algorithms/ovpBoxName.h`.
10. Add the box's inputs and outputs to `src/box-algorithms/ovpBoxName.h`.
All changes have been made, we can now compile. All these steps are implemented in `pybox-manager/ov-manager.py`.
### Automatic data storage
Automatically, the box stores all received chunk in `self.data`. This can be handy if the user wants to use all the data at the end of the box (to train a learning machine model for example).
However, it is possible to prevent this behavior voluntarily. To do this, simply give as a parameter when creating the PolyBox `record=False`. By default, record is set to True.
### Managing Custom Settings
We add to `meta/sdk/toolkit/include/toolkit/ovtk_defines.hpp` the declaration of our new Custom Settings and to `meta/sdk/toolkit/src/ovtk_main.cpp` the creation of the custom setting and the declaration of its different values.
@@ -0,0 +1,266 @@
# OpenViBE Python
Ce projet a pour but d'apporter des modifications à OpenViBE et d'ouvrir ses possibilités d'un point de vue Data Science. OpenViBE est un logiciel de traitement du signal permettant l'utilisation d'algorithmes de machine learning, cependant ces algorithmes sont limités en choix. Ainsi, nous souhaitons utiliser l'ouverture laissée par la Python scripting box d'OpenViBE (qui permet d'utiliser des scripts python dans OV) pour permettre aux utilisateurs d'utiliser les algorithmes implémentés par des librairies tierces.
## Dépendances
`pip install pyqt5 pandas numpy natsort pygame sklearn matplotlib pyriemann`
## Ce que permet OpenViBE Python
- L'utilisation de composants déjà implémentés dans OpenVibe qui vous permettront :
- l'utilisation d'algorithmes de ML issus de Scikit-learn ainsi que Pyriemann (Librarie centrée sur l'utilisation de la géométrie Riemannienne) qui vous permettront d'entrainer et de stocker des modèles.
- La visualisation de vos données en 2D/3D via une PCA ou une LDA.
- Créer facilement des datasets compatibles avec le fonctionnement d'OpenVibe
- L'utilisation d'un manager qui permet de simplement :
- Créer des nouvelles boites dans OpenVibe
- Créer des nouveaux types de "settings" openvibe qui pourront être utilisés dans vos nouvelles boites
- La réutilisation de nos scripts afin d'implenter simplement votre propre gestion de la donnée en python
## Table des matières
- [OpenViBE Python](#openvibe-python)
- [Dépendances](#d%c3%a9pendances)
- [Ce que permet OpenViBE Python](#ce-que-permet-openvibe-python)
- [Table des matières](#table-des-mati%c3%a8res)
- [1. Les nouvelles boites](#1-les-nouvelles-boites)
- [Classe TrainerML / ML Boxes](#classe-trainerml--ml-boxes)
- [Scikit-learn](#scikit-learn)
- [Pyriemann](#pyriemann)
- [Box ProcessML](#box-processml)
- [Box DataViz](#box-dataviz)
- [Box DatasetCreator](#box-datasetcreator)
- [2. Le Pybox Manager](#2-le-pybox-manager)
- [Box Manager](#box-manager)
- [Stimulations / Labels Manager](#stimulations--labels-manager)
- [Custom Settings Manager](#custom-settings-manager)
- [3. Fonctionnement interne et détails](#3-fonctionnement-interne-et-d%c3%a9tails)
- [Notre modèle de boite : PolyBox](#notre-mod%c3%a8le-de-boite--polybox)
- [Gestion des inputs avec PolyBox, deux modes possibles](#gestion-des-inputs-avec-polybox-deux-modes-possibles)
- [Duplication de la Python Scripting Box](#duplication-de-la-python-scripting-box)
- [PolyBox : Stockage automatique des données](#polybox--stockage-automatique-des-donn%c3%a9es)
- [Gestion des Custom Settings](#gestion-des-custom-settings)
## 1. Les nouvelles boites
### Classe TrainerML / ML Boxes
La classe TrainerML définie dans TrainerML.py est une classe qui hérite de [PolyBox](#notre-modèle-de-boite-polybox), son but est d'être utilisée dans des boîtes qui vont être paramétrées afin de permettre l'utilisation de certains algorithmes de machine learning. On peut lui passer les paramètres suivants :
- **Filename to save model to** : Chemin vers le fichier dans lequel sauvegarder le modèle. Si aucun fichier n'est indiqué, alors le modèle ne sera pas enregistré.
- **Filename to load model from** : Chemin vers le fichier dans lequel est sauvegardé le modèle que l'on souhaite charger. Si un fichier est renseigné et qu'il existe, aucun modèle ne sera créé et le modèle contenu dans le fichier sera chargé et utilisé pour la session en cours.
- **classifier** : Algorithme que l'on souhaite utiliser. Les différents Algorithmes sont issus de Scikit-learn ou bien de Pyriemann.
- **discriminator** : Dans le cas où le classifier choisi est `TangentSpace`, il est nécessaire de fournir un second algorithme qui servira de classifier après la projection sur l'espace tangent. Tous les algorithmes précédents peuvent être utilisés sauf `TangentSpace` et `MDM`. Si aucun algorithme n'est indiqué, `LinearDiscriminantAnalysis` sera utilisé par défault.
De même pour `MDM`, un second algorithme peut être utilisé, mais n'est pas obligatoire.
- **labels** : Si le mode de lecture est Poly-Mode, vous devez indiquer la liste de labels sous la forme `mon label1, mon label2, mon label3`, sinon, les labels seront de la forme `1, 2, 3, 4, ...`. Si le mode de lecture est ov-mode, vous pouvez laissez le champs vide, les labels seront extraits des stimulations.
- **Test set share** : float compris entre 0 et 1 (1 non inclus) qui représente la proportion du dataset d'entrée transformée en test set, nous permettant d'évaluer notre modèle une fois l'entrainement sur le train set effectué. Si 0 est renseigné, alors toutes les données constitueront le train set et aucune métrique ne sera affichée.
Voici les nouveaux algorithmes / boites implémentés dans OV :
#### Scikit-learn
<https://scikit-learn.org/stable/>
| Nom de la Box | Algorithme |
| :-: | :-: |
| Nearest Centroid | NearestCentroid |
| Nearest Neighbors Classifier | KNeighborsClassifier |
| Gaussian Naive Bayes | GaussianNB |
| Stochastic Gradient Descent | SGDClassifier |
| Logistic Regression | LogisticRegression |
| Decision Tree Classifier | DecisionTreeClassifier |
| Extra Trees | ExtraTreesClassifier |
| Bagging | BaggingClassifier |
| Random Forest | RandomForestClassifier |
| Support Vector Machine | LinearSVC |
| Linear Discriminant Analysis | LinearDiscriminantAnalysis |
| AdaBoost | AdaBoostClassifier |
| Multi Layer Perceptron | MLPClassifier |
| Linear SVC | LinearSVC |
#### Pyriemann
<https://pyriemann.readthedocs.io/en/latest/index.html>
| String | Algorithme |
| :-: | :-: |
| Riemann Minimum Distance to Mean | MDM |
| Riemann Tangent Space | TangentSpace |
Vous trouverez facilement des informations sur chacune de ces méthodes dans la doc de leur librairies.
Pour chacun de ces classifiers, une boite Openvibe utilisant TrainerML avec les paramètres adéquats a été créé. Ainsi, si vous souhaitez réaliser un entrainement avec l'algorithme Random Forest de Scikit-learn par exemple, il vous suffit de chercher la boite associée dans Openvibe et vous pourrez directement l'utiliser, et modifier les paramètres relatifs à l'algorithme. Vous trouverez les informations concernant tous ces paramètres sur les pages respectives des algorithmes, ex : <https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html>
> ![random-forest.PNG](Assets/Doc/random-forest.PNG)
>
> Exemple de paramétrisation d'une boite implémentant Random Forest
### Box ProcessML
Cette box permet de réaliser des prédictions de classification sur des nouvelles données en utilisant un modèle préalablement entraîné (via TrainerML ou les boites qui en héritent, e.g. SVM, LDA, RandomForest ... )
- **Model Filename** : Chemin où le modèle que l'on souhaite utiliser est stocké.
- **Filename to save predictions** : Chemin vers le fichier dans lequel sauvegarder les prédictions. Si aucun fichier n'est indiqué, alors les prédictions ne seront pas sauvegardées. Les prédictions sont enregistrées sous la forme d'une chaîne de caractère où chaque prédiction est séparée des autres par une virgule : `pred1,pred2,pred3 ...`
Une fois le modèle chargé, les prédictions seront réalisées sur chaque chunk de donnée reçu, elles pourront être utilisées en temps réel, et si un chemin est indiqué, elles pourront être sauvegardées pour un usage ultérieur.
### Box DataViz
La Box DataViz permet la visualisation de nos données. Pour ce faire, elle applique une réduction de dimension via une LDA ou une PCA, puis affiche nos données en utilisant la bibliothèque matplotlib. Elle hérite de PolyBox.
Elle nécessite les paramètres suivants :
- **Path to save the model** : Chemin vers le fichier dans lequel sauvegarder le modèle. Si aucun fichier n'est indiqué, alors le modèle ne sera pas enregistré.
- **Path to load the model** : Chemin vers le fichier dans lequel est sauvegardé le modèle que l'on souhaite charger. Si un fichier est renseigné et qu'il existe, aucun modèle ne sera créé et le modèle contenu dans le fichier sera chargé et utilisé pour la session en cours.
- **Algorithm (PCA or LDA)** : Nom de l'algorithme à utiliser pour réduire les dimensions. Les valeurs acceptées sont `PCA` ou `LDA`. L'algorithme utilisé par défaut est LDA.
- **Dimension reduction** : Nombre de dimension à afficher. Les différentes valeurs possibles sont 2 et 3. Par défaut, si aucun nombre n'est indiqué ou si le champs est mal rempli, le nombre de dimension est 2.
- **labels** : Si le mode de lecture est Poly-Mode, vous devez indiquer la liste de labels sous la forme `mon label1, mon label2, mon label3`, sinon, les labels seront de la forme `1, 2, 3, 4, ...`. Si le mode de lecture est ov-mode, vous pouvez laissez le champs vide, les labels seront extraits des stimulations.
### Box DatasetCreator
Pour faciliter l'acquisition de données lors de nos expériences, nous avons créé la box python `DatasetCreator`. Cette dernière prend en entrée un signal et émet en sortie une stimulation de type `OVTK_StimulationId_ExperimentStop` lorsqu'elle a fini de créer le dataset.
Elle fonctionne de la manière suivante : l'utilisateur choisit au préalable certains labels, puis la box va aléatoirement déterminer un ordre entre ceux-ci. Elle va ensuite monitorer l'utilisateur sur les actions à penser en indiquant oralement l'action courante. De cette manière, nous allons pouvoir créer un jeu de données labelisées qui pourra être utilisé pour de l'apprentissage.
L'enregistrement d'une action se déroule de la manière suivante :
1. Avertissement sonore du début de l'action.
2. Attente de 2 secondes.
3. Enregistrement pendant 10 secondes.
4. Avertissement sonore de la fin de l'action.
5. Attente de 3 secondes.
On peut la configurer en indiquant :
- Le path jusqu'au répertoire devant contenir les données.
- Le nombre de folds que l'on souhaite obtenir.
- Le nombre d'action que l'on souhaite enregistrer. C'est à dire 30 si vous souhaitez obtenir 30 enregistrement de 10 secondes répartis parmi les différents labels.
- Le nom des labels que vous souhaitez enregistrer. (/!\ Attention, ces noms doivent avoir un fichier mp3 correspondant dans `pybox-manager/Assets/Sounds/`, vous pouvez créer de nouveaux labels avec le manager.)
- Un boolean indiquant si vous souhaitez plusieurs CSV ou un seul CSV. Si vous renseignez "true", alors les données seront réparties en autant de CSV qu'il y a d'action, un CSV par action. Si vous renseignez "false", alors les données seront toutes enregistrées en un seul CSV, dans lequel le début d'un nouvel enregistrement pour un label sera indiqué dans les stimulations.
## 2. Le Pybox Manager
Le Pybox Manager permet de simplement créer et incorporer à OpenVibe de nouvelles boites éxécutant un script Python de votre choix, de nouveaux labels/stimulations et de nouveau Custom Settings pour vos box python.
Pour l'éxécuter : `python pybox_manager.py`
(Compatible python 2.7 et python 3.X.)
Une option est disponible pour activer les settings dit "développeurs" c-à-d ceux que vous avez créés.
Pour ceci lancer le manager avec l'option `mode=developer`
### Box Manager
Le Box Manager se présente comme ceci :
> ![box_manager.png](Assets/Doc/box_manager.png)
>
> Visualisation du PyBox Manager.
- New : Créer une nouvelle boite
- Duplicate : duplique la boite actuellement séléctionnée.
- Reset Box : réinitialise la box séléctionnée, annule toutes les modifications effectuées depuis la dernière compilation.
- Category : catégorie dans laquelle la boîte sera rangée dans OV.
- Author : ajoute le nom des auteurs dans les références de la boite.
- Settings : Permet d'administrer tous les settings nécessaires afin d'utiliser la box. Ceux-ci peuvent être des types issus d'Openvibe (String, Float etc.) ou bien des types créés par vous même grâce au [Custom settings Manager](#custom-settings-manager)
- Inputs : Sert à renseigner les différents inputs que va recevoir votre boite
- Outputs : : Sert à renseigner les différents outputs que va envoyer votre boite.
- Enable settings / inputs / outputs modifications : permet d'empêcher les utilisateurs de votre boite de modifier ultérieurement ces éléments.
- Mode : permet de configurer rapidement les inputs de votre box selon le mode que vous souhaitez utiliser. [Inputs Polybox](#gestion-des-inputs-avec-polybox-deux-modes-possibles)
Une fois que vos modifications sont terminées vous pouvez appuyer sur Build pour que les modifications soient prises en compte, et que la compilation s'effectue (obligatoire pour avoir vos modifications et nouvelles boites). Le fichier compilation.log contient les informations issus de la dernière compilation.
Delete Box supprime la boite actuellement séléctionnée (une compilation est nécéssaire pour prendre la suppression en compte)
### Stimulations / Labels Manager
Lors de la lecture et du traitement de nos signaux, OpenViBE peut gérer des stimulations. Ces dernières peuvent servir à indiquer le label de nos données lors d'une procédure de classification/entraînement.
L'ensemble de ces stimulations sont indiquées dans le fichier `pybox-manager/share/PolyStimulations.py`.
La box DatasetCreator que nous avons créée permet de monitorer la création d'un dataset labelisé pour l'utilisateur. Pour ce faire, la box joue un son lors de chaque action, indiquant l'action auquel l'utilisateur doit penser. De base, l'utilisateur aura a disposition une dizaine de labels différents.
Nous permettons à l'utilisateur d'ajouter des labels (stimulations) via l'interface `Stimulations/Labels Manager` présent dans le manager.
Pour ce faire, ce dernier doit fournir le nom du label, et un fichier `.mp3` correspondant, qui sera joué par le DatasetCreator.
> ![add_label.jpeg](Assets/Doc/add_label.jpeg)
>
> Add a label/stimulation to OpenViBE.
### Custom Settings Manager
Le `Custom Settings Manager` permet de créer ou de supprimer des types de paramètres spéciaux, ainsi que des valeurs possibles pour ces paramètres, selon vos besoins.
C'est notamment utile pour permettre une utilisation facile sur OpenVibe à l'aide d'une liste déroulante. Par exemple, vous voulez pouvoir choisir un algorithme parmis plusieurs, directement dans la configuration de votre boite sur openvibe, il vous suffit de créer ce nouveau type ainsi que les valeurs associées via le manager comme dans l'illustration suivante.
Pour pouvoir les utiliser avec le manager lors de la définition des paramètres d'une box, il faut lancer le manager avec l'option `mode=developer`.
> ![manage_custom_settings.png](Assets/Doc/manage_custom_settings.png)
>
> Add/Remove/Manage Custom Settings.
Résultat dans OpenVibe :
> ![classifiers.png](Assets/Doc/classifiers.png)
>
> Exemple avec un nouveau type 'classifier'.
## 3. Fonctionnement interne et détails
### Notre modèle de boite : PolyBox
Pour faciliter nos phases de développement, nous avons ajouté dans `pybox-manager/share/PolyBox.py` une classe appelée `PolyBox`. Cette dernière hérite de la classe OVBox. Elle automatise la réception et le stockage d'un signal en entrée et permet le développement de méthodes simples appelées à des moments clés :
- **on_initialize(self) :** appelée à l'initialisation, cette méthode a pour but de permettre à l'utilisateur de définir un comportement particulier lors de l'initialisation.
- **on_header_received(self, header) :** appelée à chaque réception d'un header, cette méthode a pour but de permettre à l'utilisateur de définir un comportement particulier lors de la réception d'un header.
- **on_chunk_received(self, chunk, label, shape) :** appelée à chaque réception d'un chunk, cette méthode a pour but de permettre à l'utilisateur de définir un comportement particulier lors de la réception d'un chunk.
- **on_end_box(self) :** appelée juste avant que la box finisse son travail, cette méthode a pour but de permettre à l'utilisateur de définir un comportement particulier lors de la fin de la box.
Nous avons créée cette box afin de pouvoir lui attribuer des comportements adaptés à nos cas d'utilisations, en particulier pour facilement créer des boites proposant des algorithmes de Machine Learning en se basant sur son architecture.
### Gestion des inputs avec PolyBox, deux modes possibles
Toute box héritant de la PolyBox possède automatiquement deux modes de lectures possibles pour récupérer des données en entrée.
Le premier mode (`ov-mode`) correspond au mode de lecture classique de OpenViBE : les différentes classes sont toutes comprises dans un même fichier .csv, et on utilise des stimulations reçues en entré pour séparer nos données en différentes classes. Pour utiliser ce mode, il suffit de n'accorder à la box que deux inputs : 1 StreamedMatrix et 1 Stimulation.
Exemple :
> ![ov-mode.jpeg](Assets/Doc/ov-mode.jpeg)
>
> ov-mode
Le second mode (`poly-mode`) consiste à considérer autant de fichiers qu'il y a de classes, soit un fichier .csv par classe. Pour lire tous ces fichiers, la box a alors besoin d'avoir au moins autant d'input StreamedMatrix qu'il existe de classes différentes. Pour utiliser ce mode, il suffit donc de ne créer que des entrées StreamedMatrix.
(Souvent, ce mode nécessite un paramètre `label` dans lequel renseigner les labels de nos différentes classes sous la forme : `mon label1, mon label2, mon label3`.)
Exemple :
> ![poly-mode.jpeg](Assets/Doc/poly-mode.jpeg)
>
> poly-mode
Ainsi, ces deux modes de lectures sont transparents pour l'utilisateur et lui permettent d'exploiter les box `PolyBox` soit avec un seul fichier contenant l'ensemble des actions, soit avec plusieurs fichiers : un par action.
L'utilisateur n'a donc rien besoin d'indiquer pour que la box choisisse le bon comportement à adopter, celle-ci choisit son comportement en fonction de ses inputs.
### Duplication de la Python Scripting Box
L'ensemble des modifications que nous avons apportées au logiciel repose sur la duplication de la box Python Scripting. Cette dernière permettant l'utilisation d'un script python, nous avons décidé de créer un manager permettant d'effectuer automatiquement des modifications dans le code d'OV pour dupliquer les fichiers C++ nécessaires à la duplication de la Python Scripting Box. De cette manière, nous pouvons associer définitivement un script à une box, et laisser l'utilisateur la configurer et l'intégrer à OpenViBE.
Lors de chaque création de box python, le manager effectue les tâches suivantes :
1. On se place à dans le dossier racine des box python : `pybox-manager/src/`.
2. On duplique `pybox-manager/Assets/BoxManager/NewBoxPattern-skeletton.h` en `box-algorithms/ovpBoxName.h`
3. On insère dans `src/defines.hpp` la déclaration des CIdentifier.
4. On ajoute dans `src/main.cpp` les imports des fichiers récemment créés pour la création de la box ainsi que les déclarations.
5. On remplace dans `src/box-algorithms/ovpBoxName.h` le nom de la boite et celui des includes/déclarations.
6. On change le nom, la description, les auteurs et la catégorie de la boite dans `src/box-algorithms/ovpBoxName.h`.
7. On définit le path du script à éxécuter dans `src/box-algorithms/ovpBoxName.h`.
8. On efface la possibilité de modifier le path.
9. On ajoute les paramètres de notre boite dans `src/box-algorithms/ovpBoxName.h`
10. On ajoute dans `src/box-algorithms/ovpBoxName.h` les inputs et outputs de la boite.
### PolyBox : Stockage automatique des données
Automatiquement, les box héritant de PolyBox stockent tous les chunk reçus dans `self.data`. Cela peut s'avérer pratique si l'utilisateur souhaite utiliser l'ensemble des données à la fin de la boite (pour entraîner un modèle de machine learning par exemple).
Il est cependant possible d'empêcher ce comportement. Pour ce faire, il suffit de donner comme paramètre lors de la création de la PolyBox `record=False`. Par défaut, record est à True.
### Gestion des Custom Settings
On ajoute à `meta/sdk/toolkit/include/toolkit/ovtk_defines.hpp` la déclaration de nos nouveaux Custom Settings et à `meta/sdk/toolkit/src/ovtk_main.cpp` la création du custom setting ainsi que la déclaration de ses différentes valeurs.
@@ -0,0 +1,263 @@
<OpenViBE-Scenario>
<FormatVersion>2</FormatVersion>
<Creator>OpenViBE Designer</Creator>
<CreatorVersion>2.2.0</CreatorVersion>
<Settings></Settings>
<Inputs></Inputs>
<Outputs></Outputs>
<Boxes>
<Box>
<Identifier>(0x00001a44, 0x00001322)</Identifier>
<Name>CSV File Reader</Name>
<AlgorithmClassIdentifier>(0x336a3d9a, 0x753f1ba4)</AlgorithmClassIdentifier>
<Outputs>
<Output>
<TypeIdentifier>(0x5ba36127, 0x195feae1)</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}/datas/data-tutorial.csv</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>32</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>496</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>(0x00001ddb, 0x0000547b)</Identifier>
<Name>DataViz</Name>
<AlgorithmClassIdentifier>(0x057b49ad, 0x040868cf)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>input_StreamMatrix</Name>
</Input>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>input_Stimulations</Name>
</Input>
</Inputs>
<Outputs>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>stim_out</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Clock frequency (Hz)</Name>
<DefaultValue>64</DefaultValue>
<Value>64</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Path to save the model</Name>
<DefaultValue></DefaultValue>
<Value></Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Path to load the model</Name>
<DefaultValue></DefaultValue>
<Value></Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>Algorithm (PCA or LDA)</Name>
<DefaultValue>PCA</DefaultValue>
<Value>PCA</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Dimension reduction</Name>
<DefaultValue>2</DefaultValue>
<Value>2</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>Labels</Name>
<DefaultValue></DefaultValue>
<Value></Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>96</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>496</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x434a6f9c, 0xa4ed45b3)</Value>
</Attribute>
<Attribute>
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>6</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00002a38, 0x00000aed)</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_ExperimentStop</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>160</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>496</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x568d148e, 0x650792b3)</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>(0x00006692, 0x000071be)</Identifier>
<Source>
<BoxIdentifier>(0x00001ddb, 0x0000547b)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00002a38, 0x00000aed)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00007e63, 0x00007a47)</Identifier>
<Source>
<BoxIdentifier>(0x00001a44, 0x00001322)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00001ddb, 0x0000547b)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
</Links>
<Comments>
<Comment>
<Identifier>(0x00007426, 0x0000717e)</Identifier>
<Text>You must wait the end of reading file to have display</Text>
<Attributes>
<Attribute>
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
<Value>720</Value>
</Attribute>
<Attribute>
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
<Value>32</Value>
</Attribute>
</Attributes>
</Comment>
</Comments>
<Metadata>
<Entry>
<Identifier>(0x000070c2, 0x000055b1)</Identifier>
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":1,"identifier":"(0x00005a2c, 0x00002f6b)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":1},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x000042d2, 0x00004a25)","index":0,"name":"Default tab","parentIdentifier":"(0x00005a2c, 0x00002f6b)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x00003ebf, 0x000013aa)","index":0,"name":"Empty","parentIdentifier":"(0x000042d2, 0x00004a25)","type":0}]</Data>
</Entry>
</Metadata>
</OpenViBE-Scenario>
@@ -0,0 +1,271 @@
<OpenViBE-Scenario>
<FormatVersion>2</FormatVersion>
<Creator>OpenViBE Designer</Creator>
<CreatorVersion>2.2.0</CreatorVersion>
<Settings></Settings>
<Inputs></Inputs>
<Outputs></Outputs>
<Boxes>
<Box>
<Identifier>(0x00002553, 0x00001145)</Identifier>
<Name>DatasetCreator</Name>
<AlgorithmClassIdentifier>(0x7b6738c4, 0x2c5156ed)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>input_StreamMatrix</Name>
</Input>
</Inputs>
<Outputs>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>stim_out</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Clock frequency (Hz)</Name>
<DefaultValue>64</DefaultValue>
<Value>64</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Path directory</Name>
<DefaultValue>${Player_ScenarioDirectory}/datas/</DefaultValue>
<Value>${Player_ScenarioDirectory}/datas/</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>Label_1</Name>
<DefaultValue>right</DefaultValue>
<Value>right</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>Label_2</Name>
<DefaultValue>left</DefaultValue>
<Value>left</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>Label_3</Name>
<DefaultValue>up</DefaultValue>
<Value>up</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>Label_4</Name>
<DefaultValue>down</DefaultValue>
<Value>down</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
<Name>Several CSV</Name>
<DefaultValue>false</DefaultValue>
<Value>false</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Number of folds</Name>
<DefaultValue>1</DefaultValue>
<Value>1</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Number of actions</Name>
<DefaultValue>4</DefaultValue>
<Value>4</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>176</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>496</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xd79a853e, 0x3396ae60)</Value>
</Attribute>
<Attribute>
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>9</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00002a38, 0x00000aed)</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_ExperimentStop</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>240</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>496</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x568d148e, 0x650792b3)</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00003615, 0x000063b8)</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>112</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>496</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x0b214ed8, 0x1f9ad83a)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>3</Value>
</Attribute>
</Attributes>
</Box>
</Boxes>
<Links>
<Link>
<Identifier>(0x00004d38, 0x00007bd2)</Identifier>
<Source>
<BoxIdentifier>(0x00002553, 0x00001145)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00002a38, 0x00000aed)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00007380, 0x00007709)</Identifier>
<Source>
<BoxIdentifier>(0x00003615, 0x000063b8)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00002553, 0x00001145)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
</Links>
<Comments></Comments>
<Metadata>
<Entry>
<Identifier>(0x0000716e, 0x00000d37)</Identifier>
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x0000227a, 0x00001a2b)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x00005d03, 0x00003bf1)","index":0,"name":"Default tab","parentIdentifier":"(0x0000227a, 0x00001a2b)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x0000792d, 0x0000021b)","index":0,"name":"Empty","parentIdentifier":"(0x00005d03, 0x00003bf1)","type":0}]</Data>
</Entry>
</Metadata>
</OpenViBE-Scenario>
@@ -0,0 +1,223 @@
<OpenViBE-Scenario>
<FormatVersion>2</FormatVersion>
<Creator>OpenViBE Designer</Creator>
<CreatorVersion>2.2.0</CreatorVersion>
<Settings></Settings>
<Inputs></Inputs>
<Outputs></Outputs>
<Boxes>
<Box>
<Identifier>(0x00001a44, 0x00001322)</Identifier>
<Name>CSV File Reader</Name>
<AlgorithmClassIdentifier>(0x336a3d9a, 0x753f1ba4)</AlgorithmClassIdentifier>
<Outputs>
<Output>
<TypeIdentifier>(0x5ba36127, 0x195feae1)</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}/datas/data-tutorial.csv</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>64</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480</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>(0x00002a38, 0x00000aed)</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_ExperimentStop</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>208</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x568d148e, 0x650792b3)</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00004501, 0x000079b6)</Identifier>
<Name>Process Sklearn</Name>
<AlgorithmClassIdentifier>(0x681d3dfe, 0x060e2afb)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>input_StreamMatrix</Name>
</Input>
</Inputs>
<Outputs>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>stim_out</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Clock frequency (Hz)</Name>
<DefaultValue>64</DefaultValue>
<Value>64</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Model filename</Name>
<DefaultValue>${Player_ScenarioDirectory}/model.clf</DefaultValue>
<Value>${Player_ScenarioDirectory}/model.clf</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Predictions filename</Name>
<DefaultValue></DefaultValue>
<Value></Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>144</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xb942ac8a, 0x83cd7760)</Value>
</Attribute>
<Attribute>
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
<Value></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>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
</Boxes>
<Links>
<Link>
<Identifier>(0x00002056, 0x00007dc0)</Identifier>
<Source>
<BoxIdentifier>(0x00004501, 0x000079b6)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00002a38, 0x00000aed)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00002361, 0x0000065a)</Identifier>
<Source>
<BoxIdentifier>(0x00001a44, 0x00001322)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00004501, 0x000079b6)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
</Links>
<Comments></Comments>
<Metadata>
<Entry>
<Identifier>(0x00005f34, 0x0000794a)</Identifier>
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x00005a92, 0x00001b40)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x000023fa, 0x00007573)","index":0,"name":"Default tab","parentIdentifier":"(0x00005a92, 0x00001b40)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x00001a40, 0x00000ba9)","index":0,"name":"Empty","parentIdentifier":"(0x000023fa, 0x00007573)","type":0}]</Data>
</Entry>
</Metadata>
</OpenViBE-Scenario>
@@ -0,0 +1,271 @@
<OpenViBE-Scenario>
<FormatVersion>2</FormatVersion>
<Creator>OpenViBE Designer</Creator>
<CreatorVersion>2.2.0</CreatorVersion>
<Settings></Settings>
<Inputs></Inputs>
<Outputs></Outputs>
<Boxes>
<Box>
<Identifier>(0x00000206, 0x00006ab1)</Identifier>
<Name>Trainer Sklearn</Name>
<AlgorithmClassIdentifier>(0x44b85a3a, 0xabfc390a)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
<Name>input_StreamMatrix</Name>
</Input>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>input_Stimulations</Name>
</Input>
</Inputs>
<Outputs>
<Output>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>stim_out</Name>
</Output>
</Outputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Clock frequency (Hz)</Name>
<DefaultValue>64</DefaultValue>
<Value>64</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Filename to save model to</Name>
<DefaultValue>${Player_ScenarioDirectory}/model.clf</DefaultValue>
<Value>${Player_ScenarioDirectory}/model.clf</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
<Name>Filename to load model from</Name>
<DefaultValue></DefaultValue>
<Value></Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x73ae164d, 0xea21ab0a)</TypeIdentifier>
<Name>Classifier</Name>
<DefaultValue>None</DefaultValue>
<Value>AdaBoost</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
<Name>Test set share</Name>
<DefaultValue>0.2</DefaultValue>
<Value>0.2</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>Labels</Name>
<DefaultValue></DefaultValue>
<Value></Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>n_estimators</Name>
<DefaultValue>50</DefaultValue>
<Value>50</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
<Name>learning_rate</Name>
<DefaultValue>1.0</DefaultValue>
<Value>1.0</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x27025481, 0x09bedc31)</TypeIdentifier>
<Name>algorithm</Name>
<DefaultValue>SAMME.R</DefaultValue>
<Value>SAMME.R</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
<Name>random_state</Name>
<DefaultValue>None</DefaultValue>
<Value>None</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>128</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xbe66982a, 0x597f92cf)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>4</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>2</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00001a44, 0x00001322)</Identifier>
<Name>CSV File Reader</Name>
<AlgorithmClassIdentifier>(0x336a3d9a, 0x753f1ba4)</AlgorithmClassIdentifier>
<Outputs>
<Output>
<TypeIdentifier>(0x5ba36127, 0x195feae1)</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}/datas/data-tutorial.csv</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>48</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480</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>(0x00002a38, 0x00000aed)</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_ExperimentStop</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>192</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0x568d148e, 0x650792b3)</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>(0x00003f8b, 0x00002044)</Identifier>
<Source>
<BoxIdentifier>(0x00001a44, 0x00001322)</BoxIdentifier>
<BoxOutputIndex>1</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00000206, 0x00006ab1)</BoxIdentifier>
<BoxInputIndex>1</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00005663, 0x000061f4)</Identifier>
<Source>
<BoxIdentifier>(0x00000206, 0x00006ab1)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00002a38, 0x00000aed)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
<Link>
<Identifier>(0x00006e58, 0x000045c5)</Identifier>
<Source>
<BoxIdentifier>(0x00001a44, 0x00001322)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00000206, 0x00006ab1)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
</Link>
</Links>
<Comments></Comments>
<Metadata>
<Entry>
<Identifier>(0x00004b79, 0x00004f0d)</Identifier>
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":1,"identifier":"(0x000033dc, 0x0000473a)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":1},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x00006ab8, 0x00002aa8)","index":0,"name":"Default tab","parentIdentifier":"(0x000033dc, 0x0000473a)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x00006208, 0x00003307)","index":0,"name":"Empty","parentIdentifier":"(0x00006ab8, 0x00002aa8)","type":0}]</Data>
</Entry>
</Metadata>
</OpenViBE-Scenario>
@@ -0,0 +1,61 @@
/**
* \page BoxAlgorithmDataViz DataViz
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDataViz_Description|
This box aims to transform the data with a LDA or a PCA and plot the data in 2D or 3D.
* |OVP_DocEnd_BoxAlgorithmDataViz_Description|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDataViz_Settings|
* |OVP_DocEnd_BoxAlgorithmDataViz_Settings|
* |OVP_DocBegin_BoxAlgorithmDataViz_Setting1|
The box clock frequency. The Python process function is called at
each tick.
* |OVP_DocEnd_BoxAlgorithmDataViz_Setting1|
* |OVP_DocBegin_BoxAlgorithmDataViz_Setting2|
If specified, path to save the model which transform the data.
* |OVP_DocEnd_BoxAlgorithmDataViz_Setting2|
* |OVP_DocBegin_BoxAlgorithmDataViz_Setting3|
If specified, path to load the model which transform the data.
If empty, a new model will be created depending on the input's data.
* |OVP_DocEnd_BoxAlgorithmDataViz_Setting3|
* |OVP_DocBegin_BoxAlgorithmDataViz_Setting4|
Algorithm to use to reduce the data's dimensions. Two options are available :
PCA or LDA. By default, the box will use PCA.
* |OVP_DocEnd_BoxAlgorithmDataViz_Setting4|
* |OVP_DocBegin_BoxAlgorithmDataViz_Setting5|
Number of dimensions to plot the data. The user can only choose between 2 or 3.
* |OVP_DocEnd_BoxAlgorithmDataViz_Setting5|
* |OVP_DocBegin_BoxAlgorithmDataViz_Setting6|
Labels of the data, if not specified then labels will be "1, 2, 3, etc".
* |OVP_DocEnd_BoxAlgorithmDataViz_Setting6|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDataViz_Examples|
See <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">this page</a> for commented examples.
* |OVP_DocEnd_BoxAlgorithmDataViz_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDataViz_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithmDataViz_Miscellaneous|
*/
@@ -0,0 +1,84 @@
/**
* \page BoxAlgorithmDatasetCreator DatasetCreator
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Description|
This box aims to monitor the user to create a dataset.
The user first configure the box and chooses some labels, then
the box will randomly determine an order between them. It will
then record the user's brain activity while verbally indicating
the current action. In this way, we will be able to create a
labeled dataset that can be used for learning.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Description|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Settings|
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Settings|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting1|
The box clock frequency. The Python process function is called at
each tick.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting1|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting2|
The path to the dir where the user want to save the data.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting2|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting3|
Name of the first label. That label has to be configured with the
manager to associate a sound.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting3|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting4|
Name of the second label. That label has to be configured with the
manager to associate a sound.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting4|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting5|
Name of the third label. That label has to be configured with the
manager to associate a sound.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting5|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting6|
Name of the fourth label. That label has to be configured with the
manager to associate a sound.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting6|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting7|
True if you want one csv per label, false if you want one csv
containing all the labels.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting7|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting3|
Number of folds you want to create.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting3|
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Setting3|
Number of action to record. For exemple, if you want two record for
4 labels, then number of actions if 8.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Setting3|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Examples|
See <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">this page</a> for commented examples.
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmDatasetCreator_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithmDatasetCreator_Miscellaneous|
*/
@@ -0,0 +1,49 @@
/**
* \page BoxAlgorithmProcessML Process Sklearn
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmProcessML_Description|
This box aims to load a machine learning model from scikit-learn
or pyriemann already trained and to use it to predict its input's data.
* |OVP_DocEnd_BoxAlgorithmProcessML_Description|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmProcessML_Settings|
* |OVP_DocEnd_BoxAlgorithmProcessML_Settings|
* |OVP_DocBegin_BoxAlgorithmProcessML_Setting1|
The box clock frequency. The Python process function is called at
each tick.
* |OVP_DocEnd_BoxAlgorithmProcessML_Setting1|
* |OVP_DocBegin_BoxAlgorithmProcessML_Setting2|
The path to the model the user want to load.
* |OVP_DocEnd_BoxAlgorithmProcessML_Setting2|
* |OVP_DocBegin_BoxAlgorithmProcessML_Setting3|
If not empty, the path where the user want to save the predictions
results.
* |OVP_DocEnd_BoxAlgorithmProcessML_Setting3|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmProcessML_Examples|
See <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">this page</a> for commented examples.
* |OVP_DocEnd_BoxAlgorithmProcessML_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmProcessML_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithmProcessML_Miscellaneous|
*/
@@ -0,0 +1,85 @@
/**
* \page BoxAlgorithmTrainerML Trainer Sklearn
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmTrainerML_Description|
This box aims to create a machine learning model based on algorithm implemented in scikit-learn.
The user can modify nearly all the settings from scikit-learn.
It is possible for the user to test his model with a test set.
He can also save the model in a file which he can use with the ProcessML box.
An AdaBoost classifier is a meta-estimator that begins by fitting
a classifier on the original dataset and then fits additional
copies of the classifier on the same dataset but where the weights
of incorrectly classified instances are adjusted such that
subsequent classifiers focus more on difficult cases.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Description|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmTrainerML_Settings|
* |OVP_DocEnd_BoxAlgorithmTrainerML_Settings|
* |OVP_DocBegin_BoxAlgorithmTrainerML_Setting1|
The box clock frequency. The Python process function is called at
each tick.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Setting1|
* |OVP_DocBegin_BoxAlgorithmTrainerML_Setting2|
The path to the file where the user want to save the model.
If empty, the model is not saved.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Setting2|
* |OVP_DocBegin_BoxAlgorithmTrainerML_Setting3|
The path to the file where the user want to load a model.
If empty, a new model will be created.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Setting3|
* |OVP_DocBegin_BoxAlgorithmTrainerML_Setting4|
The name of the scikit-learn algorithm. The user should not try
to modify this setting.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Setting4|
* |OVP_DocBegin_BoxAlgorithmTrainerML_Setting5|
Float between 0 and 1 (1 not included) which represents the proportion
of the input dataset transformed into a test set, allowing us to
evaluate our model once the training on the set train is done. If 0
is filled in, then all the data will constitute the train set and no
metrics will be displayed.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Setting5|
* |OVP_DocBegin_BoxAlgorithmTrainerML_Setting6|
Names of the labels of input's data. This parameter is only usefull
when the PolyMode is used (several streamed matrix in input, one for
each label). Then the labels as to be described as follows :
"my_label_1, mylabel_2, my_label_3, etc"
Otherwise the labels will be "1, 2, 3, etc".
If the input mode is ov-mode (one input streamed matrix and one
stimulation), the labels will be read by the stimulations.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Setting6|
The rest of the settings are from scikit-learn. You can find
their purpose here :
https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostClassifier.html
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmTrainerML_Examples|
See <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">this page</a> for commented examples.
* |OVP_DocEnd_BoxAlgorithmTrainerML_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithmTrainerML_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithmTrainerML_Miscellaneous|
*/
@@ -0,0 +1,725 @@
# -*- coding: utf-8 -*-
import re
import os
import sys
import shutil
import platform
import subprocess
import time
import inspect
import ast
import copy
from pathlib import Path
from collections import namedtuple
from shutil import copyfile
from filecmp import cmp as compare_file
from PyQt5.QtWidgets import QMessageBox
system = platform.system()
if system != 'Linux' and system != 'Windows':
raise Exception("OS {} is not handled with that script.".format(system))
def find_folders() :
"""Find openvibe and the manager folders."""
current_file = inspect.getfile(lambda : None)
current_file = Path(current_file)
manager_folder = str(current_file.parent) + '/'
openvibe_folder = str(current_file.parent.parent.parent.parent.parent.parent) + '/'
return manager_folder, openvibe_folder
def find_all_stims():
"""Retrieve all stimulation from Poly_Stimulation"""
prefixe_stim = 'OVPoly_'
stims = [k for k in Poly_stimulation.keys() if prefixe_stim in k]
stims = [k[len(prefixe_stim):] for k in stims]
return stims
def find_all_custom_settings(manager_folder) :
""" Generate a list containing all custom settings and their values"""
def get_custom_settings(path_header, prefixe) :
with open(path_header, 'r') as f :
text = f.read()
matchs = re.findall(r"{}[a-zA-Z0-9_]+".format(prefixe), text)
return matchs
def get_custom_settings_confirmed(path_cpp, custom_settings) :
with open(path_cpp, 'r') as f :
text = f.read()
custom_settings_confirmed = []
for ct in custom_settings :
match = re.search(r"rPluginModuleContext\.getTypeManager\(\)\.registerEnumerationType\({}, \"[a-zA-Z0-9_ \.]+\"\);".format(ct), text)
if match is not None :
custom_settings_confirmed += [ct]
return custom_settings_confirmed
def get_custom_values(path_cpp, custom_settings, prefixe) :
custom_values = {}
with open(path_cpp, 'r') as f :
text = f.read()
for ct in custom_settings :
matchs = re.findall(r'rPluginModuleContext\.getTypeManager\(\)\.registerEnumerationEntry\({}, "[0-9a-zA-Z_ \.]+", [0-9]+\);'.format(ct), text)
values = []
for match in matchs :
m_text = match.split('"')[1]
m_id = int(match.split(',')[-1][1:-2])
values += [Value(text=m_text, id=m_id)]
cs_name = ct[len(prefixe):]
custom_values[cs_name] = Custom_Setting(ct, values)
return custom_values
prefixe = 'OVPoly_ClassId_'
path_header = "{}/src/defines.hpp".format(manager_folder)
path_cpp = "{}/src/main.cpp".format(manager_folder)
custom_settings = get_custom_settings(path_header, prefixe)
custom_settings = get_custom_settings_confirmed(path_cpp, custom_settings)
custom_values = get_custom_values(path_cpp, custom_settings, prefixe)
return custom_values
def find_all_boxes(manager_folder, io_dic_type, settings_dic_type):
"""Generate a list containing all the existing box inside"""
path_all_boxes = "{}/src/box-algorithms/".format(manager_folder)
list_all_files = os.listdir(path_all_boxes)
directories = {}
for elem in list_all_files:
if elem[:3] == 'ovp' :
with open(path_all_boxes + elem, 'r') as f:
file_h = f.read()
# Path Script
script = re.findall(
r"m_script = \"(.*.py)\";", file_h)[0]
# Description
desc = re.findall(r"virtual CString getShortDescription\(void\) const "
r"{ return CString\(\"(.*?)\"\); }", file_h, flags=re.DOTALL)[0]
desc = desc.replace("\"", "\\\"")
author = re.search(r'virtual CString getAuthorName\(void\) const { return CString\(\"[a-zA-Z 0-9\-&]*\"\); }', file_h).group()
author = author[89:-5]
category = re.search(r'virtual CString getCategory\(void\) const { return CString\(\"[/a-zA-Z ]+\"\); }', file_h).group()
category = category[89:-5]
category = category[16:]
# Creation of the object
# workaround to allow creation of box with spaces
boxname = elem[3:-2]
name = boxname.replace('_',' ')
box = BoxPython(name=name, filename= boxname, desc=desc, path_script=script)
box.author = author
box.category = category
# Settings
setts = re.findall(r"(?<!//)prototype.addSetting\(\"(.*)\", "
r"((OV_TypeId|OVPoly)_[a-z_A-Z]*), \"(.*)\"\);", file_h)
# r"(OV_TypeId_[a-z_A-Z]*), \"(.*)\"\);", file_h)
# Same value as the one in counters in the display part
settings_compt = 2
for sett in setts:
if sett[0] != 'Clock frequency (Hz)':
keys = list(settings_dic_type.keys())
values = list(settings_dic_type.values())
box.settings[settings_compt] = [
sett[0], keys[values.index(sett[1])], sett[3]]
settings_compt += 1
# inputs
inputs = re.findall(
r"(?<!\/\/)prototype\.addInput\(\"(.*)\", (OV_TypeId_[a-zA-Z]*)\);", file_h)
# Same value as the one in counters in the display part
inputs_compt = 2
keys = list(io_dic_type.keys())
values = list(io_dic_type.values())
for inp in inputs:
box.inputs[inputs_compt] = [
inp[0], keys[values.index(inp[1])]]
inputs_compt += 1
# outputs
outputs = re.findall(
r"(?<!\/\/)prototype\.addOutput\(\"(.*)\", (OV_TypeId_[a-zA-Z]*)\);", file_h)
# Same value as the one in counters in the display part
outputs_compt = 2
for out in outputs:
box.outputs[outputs_compt] = [
out[0], keys[values.index(out[1])]]
outputs_compt += 1
# modify permission
permission_input = re.search(r"(\/\/)?prototype\.addFlag\(Kernel::BoxFlag_CanModifyInput\);", file_h)
permission_output = re.search(r"(\/\/)?prototype\.addFlag\(Kernel::BoxFlag_CanModifyOutput\);", file_h)
permission_setting = re.search(r"(\/\/)?prototype\.addFlag\(Kernel::BoxFlag_CanModifySetting\);", file_h)
if permission_input.group()[:2] == '//' :
box.modify_inputs = False
if permission_output.group()[:2] == '//' :
box.modify_outputs = False
if permission_setting.group()[:2] == '//' :
box.modify_settings = False
directories[box.name] = box
return directories
def warning_msg(msg) :
"""Show a warning message : msg in the manager to the user."""
box = QMessageBox()
box.setIcon(QMessageBox.Warning)
box.setText(msg)
box.setWindowTitle('Warning')
box.exec_()
def info_msg(msg) :
"""Show an information msg in the manager to the user."""
box = QMessageBox()
box.setIcon(QMessageBox.Information)
box.setText(msg)
box.setWindowTitle('Information')
box.exec_()
def insert_line_in_file(filename, string, tag):
"""Insert the line string in the file filename just after the first
line containing tag."""
with open(filename, 'r+') as f:
text = f.read()
i = text.index(tag) + len(tag)
f.seek(0)
f.write(text[:i] + '\n' + string + text[i:])
def replace_in_file(filename, old, new):
"""Replace in filename old by new."""
with open(filename, 'r') as f:
text = f.read()
text = text.replace(old, new)
with open(filename, 'w') as f:
f.write(text)
def remove_line_from_file(filename, tag):
"""Delete the first line in the file with the tag inside."""
flag = False
with open(filename, 'r') as f:
text = f.read()
tab = text.split('\n')
for i, line in enumerate(tab):
if tag in line:
flag = True
break
if flag:
tab = tab[:i] + tab[i + 1:]
text = '\n'.join(tab)
if flag:
with open(filename, 'w') as f:
f.write(text)
def generate_new_id(openvibe_folder):
"""Generate a set of 4 different random ids for openvibe"""
def get_folder_sdk_releae(openvibe_folder) :
path = '{}/build/'.format(openvibe_folder)
dirs = os.listdir(path)
path_to_return = path + [f for f in dirs if 'sdk-Release' in f][0]
return path_to_return
def get_path_ov_id_generator(openvibe_folder) :
path_sdk = get_folder_sdk_releae(openvibe_folder)
path_to_return = '{}/applications/developer-tools/id-generator/openvibe-id-generator'.format(path_sdk)
if system == 'Windows' :
path_to_return += '.exe'
return path_to_return
path_generator = get_path_ov_id_generator(openvibe_folder)
filename_tmp = 'tmp_result'
os.system('{} > {}'.format(path_generator, filename_tmp))
with open(filename_tmp, 'r') as f:
text = f.read()
text = text.split('\n')
line = text[0]
tab = line.split(',')
val1 = tab[0][-10:]
val2 = tab[1][1:11]
line = text[1]
tab = line.split(',')
val3 = tab[0][-10:]
val4 = tab[1][1:11]
if system == 'Linux' :
os.system('rm {}'.format(filename_tmp))
elif system == 'Windows' :
os.system('del {}'.format(filename_tmp))
return val1, val2, val3, val4
def create_box(openvibe_folder, manager_folder, setting_type, io_type, box_name, desc, path_script, category, author, settings, inputs, outputs, modify_settings, modify_inputs, modify_outputs):
"""Create and modify the files to create/modify a new box in openvibe."""
global system
# preventing the use of spaces which would cause problems for c++ filenames
box_name = box_name.replace(' ','_')
# force category to be in scripting
category = 'Scripting/PyBox/' + category
# 1/ We place ourselves at the root of the python boxe
old_location = os.getcwd()
os.chdir("{}/src/".format(manager_folder))
# 2/ We create the corresponding directory
path_dir_box = 'box-algorithms/'
full_path_dir_box = os.getcwd() + '/' + path_dir_box
# We duplicate files from the original box
path_file_header = path_dir_box + 'ovp{}.h'.format(box_name)
path_pattern_header = '{}Assets/BoxManager/NewBoxPattern-skeletton.h'.format(
manager_folder)
copyfile(path_pattern_header, path_file_header)
# 3/ We insert in defines.hpp the declaration of CIdentifiers
filename = 'defines.hpp'
tag = '// <tag> Tag Box Declaration'
time.sleep(1)
new_id1, new_id2, new_id3, new_id4 = generate_new_id(openvibe_folder)
new_line = "#define OVP_ClassId_BoxAlgorithm_{} OpenViBE::CIdentifier({}, {})"\
.format(box_name, new_id1, new_id2)
insert_line_in_file(filename, new_line, tag)
new_line = "#define OVP_ClassId_BoxAlgorithm_{}Desc OpenViBE::CIdentifier({}, {})"\
.format(box_name, new_id3, new_id4)
insert_line_in_file(filename, new_line, tag)
# 4/ We add our lines in main.cpp
filename = 'main.cpp'
tag = '#include "box-algorithms/CPolyBox.hpp"'
new_line = '#include "{}"'.format(path_file_header)
insert_line_in_file(filename, new_line, tag)
tag = '// <tag> OVP_Declare_New'
new_line = '\t\tOVP_Declare_New(Python::CBoxAlgorithm{}Desc);'.format(
box_name)
insert_line_in_file(filename, new_line, tag)
# 5/ We replace in ovpmyBox.h
replace_in_file(path_file_header, 'CBoxAlgorithmNewBoxPattern',
'CBoxAlgorithm{}'.format(box_name))
replace_in_file(path_file_header, 'OVP_ClassId_BoxAlgorithm_NewBoxPattern',
'OVP_ClassId_BoxAlgorithm_{}'.format(box_name))
replace_in_file(path_file_header, 'OVP_ClassId_BoxAlgorithm_NewBoxPatternDesc',
'OVP_ClassId_BoxAlgorithm_{}Desc'.format(box_name))
# 6/ We change the name, the description, the author name and the category
desc = desc.replace("\"", "\\\"")
# we reset the spaces for the name of the box
box_name = box_name.replace('_',' ')
replace_in_file(path_file_header,
'CString getName() const override { return CString("NewBoxPattern"); }',
'CString getName() const override { return CString("' + box_name + '"); }')
replace_in_file(path_file_header,
'CString getShortDescription() const override { return CString("Default Python Description"); }',
'CString getShortDescription() const override { return CString("' + desc + '"); }')
replace_in_file(path_file_header,
'CString getAuthorName() const override { return CString("NewAuthor"); }',
'CString getAuthorName() const override { return CString("' + author + '"); }')
replace_in_file(path_file_header,
'CString getCategory() const override { return CString("Scripting/Pybox/"); }',
'CString getCategory() const override { return CString("' + category + '"); }')
# 7/ We set the python script to execute
# On set le script python a executer
replace_in_file(path_file_header,
'm_script = "NewScript.py";',
'm_script = "{}";'.format(path_script.replace('\\', '/')))
# 8/ We can then add our params
tag = '// <tag> settings'
for number, (key, kind, value) in reversed(list(settings.items())):
if key:
new_line = ' prototype.addSetting("{}", {}, "{}");'.format(
key, all_settings_type[kind], value)
insert_line_in_file(path_file_header, new_line, tag)
# 9/ We can then add our inputs and our outputs
tag_inoutset = '// <tag> input & output'
for number, (name, kind) in reversed(list(inputs.items())):
if name:
new_line = ' prototype.addInput("{}", {});'.format(
name, io_type[kind])
insert_line_in_file(path_file_header, new_line, tag_inoutset)
for number, (name, kind) in reversed(list(outputs.items())):
new_line = ' prototype.addOutput("{}", {});'.format(
name, io_type[kind])
insert_line_in_file(path_file_header, new_line, tag_inoutset)
# 10/ Permissions to modify boxes in OV
if not modify_settings :
replace_in_file(path_file_header,
"prototype.addFlag(Kernel::BoxFlag_CanModifySetting);",
"//prototype.addFlag(Kernel::BoxFlag_CanModifySetting);")
if not modify_inputs :
replace_in_file(path_file_header,
"prototype.addFlag(Kernel::BoxFlag_CanModifyInput);",
"//prototype.addFlag(Kernel::BoxFlag_CanModifyInput);")
if not modify_outputs :
replace_in_file(path_file_header,
"prototype.addFlag(Kernel::BoxFlag_CanModifyOutput);",
"//prototype.addFlag(Kernel::BoxFlag_CanModifyOutput);")
os.chdir(old_location)
def delete_box(manager_folder, box_name):
"""Delete and modify files to delete an existing box."""
global system
box_name = box_name.replace(' ', '_')
# 1/ Change directory to src/
old_location = os.getcwd()
os.chdir("{}src/".format(manager_folder))
# 2/ Remove directory with the box algorithms
path_box = 'box-algorithms/ovp{}.h'.format(box_name)
os.remove(path_box)
# 3/ Remove lines from defines.hpp
path = 'defines.hpp'
tag = 'OVP_ClassId_BoxAlgorithm_{}Desc'.format(box_name)
remove_line_from_file(path, tag)
tag = 'OVP_ClassId_BoxAlgorithm_{}'.format(box_name)
remove_line_from_file(path, tag)
# 4/ Remove lines from main.cpp
path = 'main.cpp'
tag = 'OVP_Declare_New(Python::CBoxAlgorithm{}Desc);'.format(box_name)
remove_line_from_file(path, tag)
tag = '#include "box-algorithms/ovp{}.h"'.format(box_name)
remove_line_from_file(path, tag)
os.chdir(old_location)
# ------------------------------------------------------------
def create_custom_setting(manager_folder, openvibe_folder, cs) :
""" Make all modification to openvibe to create a custom setting."""
prefixe = 'OVPoly_ClassId_'
path_cpp = '{}/src/main.cpp'.format(manager_folder)
path_header = '{}/src/defines.hpp'.format(manager_folder)
tag = '// <tag> Custom Type Settings'
cs_define = "{}{}".format(prefixe, cs.name)
# header
id_1, id_2, _, _ = generate_new_id(openvibe_folder)
line_header = '#define {} OpenViBE::CIdentifier({}, {})'.format(cs_define, id_1, id_2)
insert_line_in_file(path_header, line_header, tag)
# cpp
for value in reversed(cs.values) :
line_entry = '\trKernelContext.getTypeManager().registerEnumerationEntry({}, "{}", {});'.format(cs_define, value.text, value.id)
insert_line_in_file(path_cpp, line_entry, tag)
line_type = '\trKernelContext.getTypeManager().registerEnumerationType({}, "{}");'.format(cs_define, cs.name)
insert_line_in_file(path_cpp, line_type, tag)
def delete_custom_setting(manager_folder, cs) :
"""Make all the modification to openvibe to delete a custom setting."""
def remove_lines_with_tag_in_file(file, tag) :
with open(file, 'r') as f :
text = f.read()
new_text = []
for line in text.split('\n') :
if not (tag in line) :
new_text += [line]
new_text = '\n'.join(new_text)
with open(file, 'w') as f :
f.write(new_text)
prefixe = 'OVPoly_ClassId_'
path_cpp = '{}/src/main.cpp'.format(manager_folder)
path_header = '{}/src/defines.hpp'.format(manager_folder)
tag = '{}{}'.format(prefixe, cs.name)
remove_lines_with_tag_in_file(path_header, tag)
remove_lines_with_tag_in_file(path_cpp, tag)
def compile(manager_folder, openvibe_folder):
"""Compile OpenVibe."""
# Go to openvibe folder directory
old_location = os.getcwd()
os.chdir(openvibe_folder)
path_log = manager_folder + 'compilation.log'
if system == 'Linux':
os.system('./build.sh | tee {}'.format(path_log))
elif system == 'Windows':
os.system('build.cmd > {}'.format(path_log))
# Set back to normal
os.chdir(old_location)
def add_stimulation(manager_folder, label, file_sound) :
"""Make all the modifications to add a stimulation to openvibe."""
def find_next_id(path_file_stim) :
# We open the PolyStimulations.py to find the next id
with open(path_file_stim, 'r') as f :
text = f.read()
# On charge de dictionnaire de stimulation
dico_string = ''
flag = False
for c in text :
if c == '{' :
flag = True
elif c == '}' :
dico_string += c
break
if flag :
dico_string += c
dico = ast.literal_eval(dico_string)
ids = [int(value) for key, value in Poly_stimulation.items() if 'OVPoly' in key]
ids.sort()
try :
current_id = ids[0]
except IndexError :
return 0x10001
while True :
if not current_id in ids :
return hex(current_id)
current_id += 1
def get_line_to_add(label, path_file_stim) :
key = '\'OVPoly_' + label[0].upper() + label[1:].lower() + '\''+ ' '*(29-len(label))
value = find_next_id(path_file_stim)
line = '{}: {},'.format(key, value)
return line
path_file_stim = '{}/share/PolyStimulations.py'.format(manager_folder)
tag = '# <Flag> New Stims'
line = get_line_to_add(label, path_file_stim)
insert_line_in_file(path_file_stim, line, tag)
copyfile(file_sound, '{}/Assets/Sounds/{}.mp3'.format(manager_folder, label))
def get_name_duplicate(dict, name) :
"""Find the name for a duplicated box."""
def regex_handling(regex, name):
res = re.search(regex, name)
try:
prefix_1 = res.group(1)
idx_1 = res.group(2) # useless ?
max_idx = 1
# Does we already have a duplicate ?
# we do so we find the max index and increment it
for bn in dict.keys() :
regex = '(' + prefix_1 + ')(\d+)'
res_2 = re.search(regex,bn)
try:
idx = int(res_2.group(2))
if idx >= max_idx:
max_idx = idx + 1
except:
pass
current_name = prefix_1 + str(max_idx)
except AttributeError:
# current_name = 'rr'
for bn in dict.keys() :
if bn == name :
current_name = '{}_{}'.format(name, 1)
finally:
return current_name
current_name = name
regex = '(.+?_)(\d+)'
res = re.search(regex, current_name) # useless ?
# We already have doublon
for bn in dict.keys():
# if we have the "original" name
if current_name in bn:
current_name = regex_handling(regex, bn)
break
else:
current_name = regex_handling(regex, current_name)
return current_name
def delete_stimulation(manager_folder, label) :
"""Make the modification to remove a stimulation from openvibe."""
path_file_stim = '{}/share/PolyStimulations.py'.format(manager_folder)
remove_line_from_file(path_file_stim, label)
path_sound = '{}/Assets/Sounds/{}.mp3'.format(manager_folder, label.lower())
os.remove(path_sound)
def retrieve_settings_type(custom_settings, all_settings=False) :
"""If 'mode=developer' in args : load all default and custom settings,
else load only default settings"""
settings = {
'Integer': 'OV_TypeId_Integer',
'Float': 'OV_TypeId_Float',
'String': 'OV_TypeId_String',
'Boolean': 'OV_TypeId_Boolean',
'Filename': 'OV_TypeId_Filename',
'Stimulation': 'OV_TypeId_Stimulation',
}
flag = False
for arg in sys.argv[1:] :
if 'mode=developer' in arg :
flag = True
if flag or all_settings :
for k,v in custom_settings.items() :
settings[k] = v.name
return settings
Setting = namedtuple('Setting', ['name', 'type', 'value', 'button_delete'])
Input = namedtuple('Input', ['name', 'type', 'button_delete'])
Output = namedtuple('Output', ['name', 'type', 'button_delete'])
Custom_Setting_Line = namedtuple('CSV', ['name', 'value', 'button_delete'])
Custom_Setting = namedtuple("Custom_Setting", ['name', 'values'])
Value = namedtuple("Value", ["text", "id"])
class BoxPython:
"""This class aim to contain all the data needed to create a box in openvibe."""
def __init__(self, name='Default Box', filename='Default filename', desc='Default Python Description', path_script='/home/', old_name=None,):
self.name = name
self.filename = filename
self.description = desc
self.py_script = path_script
self.category = ''
self.author = ''
self.settings = {}
self.inputs = {}
self.outputs = {}
self.modify_settings = True
self.modify_inputs = True
self.modify_outputs = True
self.to_be_updated = False
if old_name is None :
self.old_name = name
else :
self.old_name = old_name
def __eq__(self, obj):
equals = False
if isinstance(obj, BoxPython):
equals = self.name == obj.name and \
self.description == obj.description and \
self.py_script == obj.py_script and \
cmp(self.settings, obj.settings) == 0 and \
cmp(self.inputs, obj.inputs) == 0 and \
cmp(self.outputs, obj.outputs) == 0
return equals
def __ne__(self, obj):
return not self == obj
# Load all the data necessary to make the manager work.
manager_folder, openvibe_folder = find_folders()
io_type = {
'Signal': 'OV_TypeId_Signal',
'Stimulations': 'OV_TypeId_Stimulations',
'Streamed Matrix': 'OV_TypeId_StreamedMatrix'
}
modes = ['ov-mode', 'poly-mode']
category = ['Acquisition and network IO',
'Advanced Visualization',
'Classification',
'Data generation',
'Evaluation',
'Examples',
'Feature extraction',
'File reading and writing',
'Signal processing',
'Stimulation',
'Streaming',
'Tests',
'Tools',
'Visualization'
]
sys.path.append('{}/share/'.format(manager_folder))
from PolyStimulations import Poly_stimulation
custom_settings = find_all_custom_settings(manager_folder)
all_settings_type = retrieve_settings_type(custom_settings, all_settings=True)
settings_type = retrieve_settings_type(custom_settings)
boxes = find_all_boxes(manager_folder, io_type, all_settings_type)
stims = find_all_stims()
@@ -0,0 +1,203 @@
# -*- coding: utf-8 -*-
# File Name : PolyBox.py
# Created By : Yannis Bendi-Ouis
from StimulationsCodes import *
from PolyStimulations import *
from openvibe import *
import sys, traceback, collections
from io import StringIO
import re
def get_label_from_stim(stim):
stims_1 = list(OpenViBE_stimulation.items())
stims_2 = list(Poly_stimulation.items())
inv_dictstim = {v: k for k, v in stims_1 + stims_2}
label = inv_dictstim[stim.identifier][7:].lower()
label = ' '.join(label.split('_'))
return label
class PolyBox(OVBox):
def __init__(self, record=True):
OVBox.__init__(self)
self.acquiring_channel = []
self.signalHeader = []
self.data = {}
self.record = record
self.mode = ''
self.current_stimulation = None
self.labels = []
def initialize(self):
def verify_entry(self):
# Verify that the inputs corresponds to ov-mode or poly-mode
# ov-mode : 1 stimulations and 1 streamed matrix
# poly-mode : several streamed-matrix
# others : others -> warning
list_entry_type = [entry.type() for entry in self.input]
nb_matrix = list_entry_type.count('StreamedMatrix')
nb_stim = list_entry_type.count('Stimulations')
nb_signal = list_entry_type.count('Signal')
if nb_stim == 1 and nb_matrix == 1 and nb_signal == 0:
# ov-mode
self.mode = 'ov-mode'
elif nb_stim == 0 and nb_matrix >= 1 and nb_signal == 0:
# poly-mode
self.mode = 'poly-mode'
else:
raise Exception("ERROR : Entry of the box does not corresponds to any mode. \
You can use 1 stimulations and 1 streamed matrix, or several streamed matrix. \
But you can not use {} StreamedMatrix, {} Stimulations and {} Signal entry.".format(nb_matrix, nb_stim, nb_signal))
def verify_stim_output(self):
# Verify that an output stim exist, otherwise prevent the user that the program won't stop
flag = False
for out in self.output:
if out.type() == 'Stimulations':
flag = True
break
if not flag:
print('WARNING : The DatasetCreator does not have any output Stimulation. The program may never stop.')
def get_labels(self):
# retrieve labels in form : label1, label2, label3, mon label4
# Useless if you are in OV-MODE
if 'Labels' in self.setting.keys():
string = self.setting['Labels']
if len(string) > 0:
labels_cut = string.lower().split(',')
for label in labels_cut:
self.labels += ['_'.join([w for w in label.split(' ') if w != ''])]
def init_acquiring_channel(self):
# We get data for every input channel
for _ in range(len(self.input)):
self.acquiring_channel += [False]
self.signalHeader += [None]
verify_entry(self)
verify_stim_output(self)
get_labels(self)
init_acquiring_channel(self)
self.on_initialize()
def process(self):
# we go through every input
for inputIndex in range(len(self.input)):
for chunkIndex in range(len(self.input[inputIndex])):
# Signal init
if type(self.input[inputIndex][chunkIndex]) == OVStreamedMatrixHeader:
self.header_received(inputIndex, chunkIndex)
# Process every chunk received
elif type(self.input[inputIndex][chunkIndex]) == OVStreamedMatrixBuffer:
self.chunk_received(inputIndex, chunkIndex)
# End of signal
elif type(self.input[inputIndex][chunkIndex]) == OVStreamedMatrixEnd:
self.end_received(inputIndex, chunkIndex)
# Stimulations init
elif type(self.input[inputIndex][chunkIndex]) == OVStimulationHeader:
self.header_received(inputIndex, chunkIndex)
# Process every stimulation
elif type(self.input[inputIndex][chunkIndex]) == OVStimulationSet:
self.stimulation_received(inputIndex, chunkIndex)
# End of stim
elif type(self.input[inputIndex][chunkIndex]) == OVStimulationEnd:
self.end_received(inputIndex, chunkIndex)
def uninitialize(self):
pass
# ------- * -------- * ---------
def header_received(self, inputIndex, chunkIndex):
header = self.input[inputIndex].pop()
self.signalHeader[inputIndex] = header
self.acquiring_channel[inputIndex] = True
self.on_header_received(header)
def chunk_received(self, inputIndex, chunkIndex):
chunk = list(self.input[inputIndex].pop())
if self.acquiring_channel[inputIndex]:
# We look for the best key to use in function of mode and settings labels.
key = None
if self.mode == 'poly-mode':
if len(self.labels) > 0:
key = self.labels[inputIndex]
else:
key = inputIndex
elif self.mode == 'ov-mode':
key = get_label_from_stim(self.current_stimulation)
if self.record:
try:
self.data[key].append(chunk)
except KeyError:
self.data[key] = [chunk]
shape = tuple(self.signalHeader[inputIndex].dimensionSizes)
self.on_chunk_received(chunk, key, shape)
def stimulation_received(self, inputIndex, chunkIndex):
stim_list = self.input[inputIndex].pop()
if len(stim_list) > 0:
self.current_stimulation = stim_list[0]
def end_received(self, inputIndex, chunkIndex):
self.acquiring_channel[inputIndex] = False
self.input[inputIndex].pop()
if not self.is_acquiring():
print("Fin de l'acquisition des données...")
self.on_end_box()
self.send_end_stim()
def is_acquiring(self):
# Return false when all inputs of type StreamedMatrix received End flag.
nb_inputs = len(self.input)
for i in range(nb_inputs):
acquiring = self.acquiring_channel[i]
kind = self.input[i].type()
if kind == 'StreamedMatrix' and acquiring:
return True
return False
def send_end_stim(self):
indice = -1
for i, out in enumerate(self.output):
if out.type() == 'Stimulations':
indice = i
if indice != -1:
stimLabel = 'OVTK_StimulationId_ExperimentStop'
stimCode = OpenViBE_stimulation[stimLabel]
stimSet = OVStimulationSet(0, self.getCurrentTime())
stimSet.append(OVStimulation(stimCode, self.getCurrentTime(), 0.))
self.output[indice].append(stimSet)
# ---------- * -------------- * ---------------
def on_initialize(self):
pass
def on_header_received(self, header):
pass
def on_chunk_received(self, chunk, label, shape):
pass
def on_end_box(self):
pass
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
#File Name : StimulationsCodes.py
#Created By :
## Stimulation codes
# Avoid the declaration of new stimulation (added in new tab here). Only Stimulation contains gdf in original list is really standard...
Poly_stimulation = {
'OVPoly_Down' : 0x10001, #Useless use 'OVTK_GDF_Down'
'OVPoly_Up' : 0x10002, #Useless use 'OVTK_GDF_Up'
'OVPoly_Right' : 0x10003, #Useless use 'OVTK_GDF_Right'
'OVPoly_Left' : 0x10004, #Useless use 'OVTK_GDF_Left'
'OVPoly_Neutral' : 0x10005,
'OVPoly_Push' : 0x10006,
'OVPoly_Pull' : 0x10007,
'OVPoly_Left_Wink' : 0x10008,
'OVPoly_Right_Wink' : 0x10009,
# <Flag> New Stims
}
@@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
from matplotlib import pyplot as plt
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.decomposition import PCA
import numpy as np
import pandas as pd
import pickle
from PolyBox import PolyBox
import warnings
warnings.filterwarnings("ignore")
flag_3D = True
try:
from mpl_toolkits.mplot3d import Axes3D
except:
print('Unable to import Axes3D from mpl_toolkits.mplot3d, 3D visualization disabled.')
flag_3D = False
class DataViz(PolyBox):
def __init__(self):
PolyBox.__init__(self)
self.x_data = []
self.y_data = []
self.model = None
self.path_load_model = ''
self.path_save_model = ''
self.algo = ''
self.dimension_reduction = -1
def on_initialize(self):
# We retrieve the setting from OpenViBE
def retrieve_path_save_model(self):
try:
self.path_save_model = self.setting["Path to save the model"]
except KeyError:
pass
if self.path_save_model == '':
print('No path has been given to save the model, thus it won\'t be saved.')
def retrieve_path_load_model(self):
try:
self.path_load_model = self.setting["Path to load the model"]
except KeyError:
pass
if self.path_load_model == '':
print('No path has been given to load the model, thus a new model will be created.')
def retrieve_algo(self):
try:
self.algo = self.setting['Algorithm (PCA or LDA)'].upper()
except KeyError:
pass
if self.algo == '':
print('No algo has been given to the model, default algo is PCA.')
self.algo = 'PCA'
def retrieve_dimension_reduction(self):
try:
self.dimension_reduction = int(self.setting['Dimension reduction'])
except KeyError:
pass
except ValueError:
print('{} is not a number. Please use 2 or 3 dimensions only.'.format(self.setting['Dimension reduction']))
if self.dimension_reduction == -1:
print('No dimension reduction has been given, default value is 2.')
self.dimension_reduction = 2
elif self.dimension_reduction == 3 and not flag_3D:
print('3D disabled, cannot show data in 3 dimensions. Default dimension is 2.')
self.dimension_reduction = 3
retrieve_path_save_model(self)
retrieve_path_load_model(self)
retrieve_algo(self)
retrieve_dimension_reduction(self)
def on_end_box(self):
self.prepare_data()
self.make_model_and_transform()
self.make_plot()
# ---------
def prepare_data(self):
for label in self.data.keys():
self.x_data += self.data[label]
self.y_data += [label for _ in range(len(self.data[label]))]
def make_model_and_transform(self):
# load the model if it exists, else create a new one
# then transform the data
def load_model(self):
model = pickle.load(open(self.path_load_model, 'rb'))
print('Model load from {}.'.format(self.path_load_model))
return model
def save_model(self):
pickle.dump(self.model, open(self.path_save_model, 'wb'))
print('Dataviz model saved in {}'.format(self.path_save_model))
def map_algo(self):
switcher = { 'LDA': LDA, 'PCA': PCA }
clf = switcher.get(self.algo)
return clf, switcher
def create_fit_model(self):
clf, _ = map_algo(self)
clf = clf(n_components=self.dimension_reduction)
if self.algo == 'PCA':
clf.fit(self.x_data)
elif self.algo == 'LDA':
clf.fit(self.x_data, self.y_data)
else:
raise Exception('{} is not known as an Algorithm. Please use PCA or LDA.'.format(self.algo))
return clf
# Load or create the model
if len(self.path_load_model) > 0:
self.model = load_model(self)
else:
self.model = create_fit_model(self)
# Save the model
if self.path_save_model != '':
save_model(self)
# Transform data
self.x_data = self.model.transform(self.x_data)
self.y_data = np.array(self.y_data)
def make_plot(self):
fig = plt.figure(figsize=(12, 12))
all_labels = list(self.data.keys())
colors = np.array([all_labels.index(label) for label in self.y_data])
if self.dimension_reduction == 2:
ax = plt.axes()
for label in all_labels:
ax.text(self.x_data[self.y_data == label, 0].mean(), self.x_data[self.y_data == label, 1].mean(),
label, horizontalalignment='center', bbox=dict(alpha=0.5, edgecolor='w', facecolor='w'))
ax.scatter(self.x_data[:, 0], self.x_data[:, 1], alpha=0.5, c=colors, cmap='Spectral', edgecolor='g')
plt.show()
elif self.dimension_reduction == 3:
ax = Axes3D(fig)
for label in all_labels:
ax.text3D(self.x_data[self.y_data == label, 0].mean(),
self.x_data[self.y_data == label, 1].mean(),
self.x_data[self.y_data == label, 2].mean(),
label, horizontalalignment='center', bbox=dict(alpha=0.5, edgecolor='w', facecolor='w'))
ax.scatter(self.x_data[:, 0], self.x_data[:, 1], self.x_data[:, 2],
alpha=0.5, c=colors, cmap='Spectral', edgecolor='g')
plt.show()
box = DataViz()
@@ -0,0 +1,421 @@
# -*- coding: utf-8 -*-
'''
 * Software License Agreement (AGPL-3 License)
 *
 * OpenViBE
 * Copyright (C) Inria, 2006-2019
 *
 * Authors
 *
* 2019, Yannis Bendi-Ouis <yannis.bendiouis@gmail.com>
* 2019, Jimmy Leblanc <jimmy.leblanc01@gmail.com>
*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 *
'''
from pandas import Series, DataFrame, read_csv
import numpy as np
import pandas as pd
from natsort import natsorted
from PolyStimulations import Poly_stimulation
import os
import pickle
import random
import inspect
# Dans les settings :
# - "Path directory" : path qui mène au directory de sauvegarde des données. Donc finir par un '/'.
# - "Label_X" : Nom des labels, où X est un nombre. Il en faut autant qu'il y a de labels.
# - "Several CSV" : Boolean ou string qui return "true" ou "false".
# - "Number of folds" : Integer qui indique en combien de différents folds les data doivent être séparées.
# - "Number of actions" : Integer qui indique le nombre d'actions à enregistrer lors d'une session.
#------------------------------------------------------------
ACTION_DURATION = 12 # in seconds
TAMPON_DURATION = 3 # in seconds
BEGIN_RECORD = 2 # in seconds
END_RECORD = ACTION_DURATION
FREQ = 128
NB_POINTS_ONE_RECORD = ACTION_DURATION * FREQ
NB_POINTS_ONE_TAMPON = TAMPON_DURATION * FREQ
CHANNELS_NAME = ['AF3', 'F7', 'F3', 'FC5', 'T7', 'P7', 'O1', 'O2', 'P8', 'T8', 'FC6', 'F4', 'F8', 'AF4'] # 14
CHANNELS_STIMULATION = ['Event Id', 'Event Date', 'Event Duration']
PREFIXE_STIM = 'OVPoly_'
#------------------------------------------------------------
def ovdf(df, rewrite_stim=True):
shape = df.shape
timer = Series([float(i)/FREQ for i in range(shape[0])])
epochs = Series([int(i/64) for i in range(shape[0])])
df.insert(0, 'Time:{}Hz'.format(FREQ), timer)
df.insert(1, 'Epoch', epochs)
if rewrite_stim:
df['Event Id'] = Series([None for i in range(shape[0])])
df['Event Date'] = Series([None for i in range(shape[0])])
df['Event Duration'] = Series([None for i in range(shape[0])])
else:
# re-compute the time informations for stimulations
stim = df['Event Id'].values.tolist()
date = []
duration = []
for i, event_id in enumerate(stim):
if not pd.isnull(event_id):
date += [timer[i]]
duration += [0]
else:
date += [None]
duration += [None]
df['Event Date'] = Series(date)
df['Event Duration'] = Series(duration)
#------------------------------------------------------------
def get_stim_code_from_label(label):
key = label.split('_')
key = "_".join([w[0].upper() + w[1:] for w in key])
key = PREFIXE_STIM + key
return Poly_stimulation[key]
#------------------------------------------------------------
class DatasetCreator(OVBox):
#----------------------------------------
def __init__(self):
OVBox.__init__(self)
self.dir_name = None
self.url = None
self.signalHeader = None
self.labels_queue = None
self.current_label = None
self.is_tampon = False
self.several_csv = False
self.nb_fold = 0
self.nb_action_per_session = 0
self.labels = []
self.data_recorded = []
self.labels_order = []
self.dic_fold = {}
self.dic_dicount = {}
#----------------------------------------
def initialize(self):
def verify_labels_correct(self):
for label in self.labels:
try:
get_stim_code_from_label(label)
except KeyError:
raise Exception('Label {} not defined in PolyStimulations. You may want to add it with the manager.'.format(label))
def get_labels(self):
param_names = self.setting.keys()
param_names = natsorted(param_names)
for n in param_names:
if 'Label_' in n:
label = self.setting[n]
label = label.replace(' ', '_')
if len(label) > 0:
self.labels += [label.lower()]
def init_dict(self):
self.dic_fold = {'fold_{}'.format(i): None for i in range(1, self.nb_fold+1)}
self.dic_dicount = {'fold_{}'.format(i): None for i in range(1, self.nb_fold+1)}
def retrieve_settings(self):
# On récupère la booleen indiquant si l'on souhaite plusieurs ou un seul csv par fold
self.several_csv = self.setting['Several CSV']
if self.several_csv == 'true':
self.several_csv = True
elif self.several_csv == 'false':
self.several_csv = False
# On récupère le path du directory où l'on créé les fold
self.dir_name = self.setting['Path directory']
if self.dir_name[-1] != '/':
self.dir_name += '/'
# On récupère le nombre de fold
self.nb_fold = int(self.setting['Number of folds'])
# On récupère le nombre d'action à record par session
self.nb_action_per_session = int(self.setting['Number of actions'])
# On récupère les labels
get_labels(self)
def verify_stim_output(self):
# Verify that an output stim exist, otherwise prevent the user that the program won't stop
flag = False
for out in self.output:
if out.type() == 'Stimulations':
flag = True
break
if not flag:
print('WARNING : The DatasetCreator does not have any output Stimulation. The program may never stop.')
retrieve_settings(self)
verify_stim_output(self)
verify_labels_correct(self)
init_dict(self)
self.verify_arborescence()
self.prepare_session()
self.new_record()
#----------------------------------------
def process(self):
# On parcours tous les inputs
for inputIndex in range(len(self.input)):
for chunkIndex in range(len(self.input[inputIndex])):
# Initialisation pour le signal
if type(self.input[inputIndex][chunkIndex]) == OVStreamedMatrixHeader:
self.header_received(inputIndex, chunkIndex)
# Traitement à effectuer pour chaque chunk reçu
elif type(self.input[inputIndex][chunkIndex]) == OVStreamedMatrixBuffer:
self.chunk_received(inputIndex, chunkIndex)
# Fin du signal
elif type(self.input[inputIndex][chunkIndex]) == OVStreamedMatrixEnd:
self.end_received(inputIndex, chunkIndex)
#----------------------------------------
def uninitialize(self):
pass
# -------------- * ------------- * -------------
#----------------------------------------
def header_received(self, inputIndex, chunkIndex):
self.signalHeader = self.input[inputIndex].pop()
#----------------------------------------
def chunk_received(self, inputIndex, chunkIndex):
chunk = self.input[inputIndex].pop()
# Plusieurs lignes sont envoyées en même temps, il faut les séparer
indices = [(len(CHANNELS_NAME)*i, len(CHANNELS_NAME)*(i+1)) for i in range(int(len(chunk)/len(CHANNELS_NAME)))]
for begin, end in indices:
self.data_recorded += [chunk[begin:end]]
# Fin d'un tampon
if self.is_tampon and len(self.data_recorded) >= NB_POINTS_ONE_TAMPON:
self.empty_tampon()
self.new_record()
# Fin d'un record
if not self.is_tampon and len(self.data_recorded) >= NB_POINTS_ONE_RECORD:
if not self.end_record():
self.end_of_session()
self.end_of_box()
#----------------------------------------
def end_received(self, inputIndex, chunkIndex):
self.input[inputIndex].pop()
print("Error : entry signal stoped.")
self.end_of_box()
#----------------------------------------
def end_of_box(self):
indice = -1
for i, out in enumerate(self.output):
if out.type() == 'Stimulations':
indice = i
if indice != -1:
stimLabel = 'OVTK_StimulationId_ExperimentStop'
stimCode = OpenViBE_stimulation[stimLabel]
stimSet = OVStimulationSet(0, self.getCurrentTime())
stimSet.append(OVStimulation(stimCode, self.getCurrentTime(), 0.))
self.output[0].append(stimSet)
# -------------- * ------------- * -------------
#----------------------------------------
def verify_arborescence(self):
# On vérifie que chaque dossier du path existe, sinon on les créé
# et on vérifie les dicount
def verify_dir(self):
# On vérifie que le dossier existe et ses sous-dossiers, sinon on les créé
if not os.path.exists(self.dir_name):
os.mkdir(self.dir_name, 0o775)
def verify_folds(self):
# On vérifie que les dossiers des différents folds existent, sinon on les créés
for i in range(1, self.nb_fold+1):
name = self.dir_name + 'fold_{}/'.format(i)
if not os.path.exists(name):
os.mkdir(name, 0o775)
def verify_and_load_dicount(self):
# On vérifie que les fichiers contenant les compteurs par label existent, sinon on les créé
for i in range(1, self.nb_fold+1):
fold = 'fold_{}'.format(i)
filename = self.dir_name + fold + '/dicount.pick'
if not os.path.isfile(filename):
dicount = {l: 0 for l in self.labels}
self.dic_dicount[fold] = dicount
pickle.dump(dicount, open(filename, 'wb'))
else:
self.dic_dicount[fold] = pickle.load(open(filename, 'rb'))
verify_dir(self)
verify_folds(self)
verify_and_load_dicount(self)
#----------------------------------------
def prepare_session(self):
def create_labels_queue(self):
nb_label = len(self.labels)
nb_total = int(self.nb_action_per_session / nb_label) # in Python 3 / create float automatically and range hate that
nb_reste = self.nb_action_per_session - nb_total*nb_label
choice_label = []
tmp = [l for l in self.labels]
for _ in range(nb_reste):
l = random.choice(tmp)
tmp.remove(l)
choice_label += [l]
tmp = [l for l in self.labels]
self.labels_queue = tmp*nb_total + choice_label
random.shuffle(self.labels_queue)
# Prepare la suite aléatoire d'action a exectuer
create_labels_queue(self)
# Initialise self.dic_fold
for key in self.dic_fold.keys():
self.dic_fold[key] = {l: [] for l in self.labels}
#----------------------------------------
def empty_tampon(self):
# Supprime les données tampons entre deux records
self.is_tampon = False
end = TAMPON_DURATION * FREQ
self.data_recorded = self.data_recorded[end:]
#----------------------------------------
def new_record(self):
# Prevent the user a new record begin
if len(self.labels_queue) > 0:
self.current_label = self.labels_queue.pop(0)
print('Current label : {}'.format(self.current_label))
#----------------------------------------
def end_record(self):
# Démarre un nouvel enregistrement de 10 sec. Si le dernier est fini, l'enregistre.
# Return True s'il y a encore d'autres label à étudié pour la session, False sinon.
def retrieve_record(self):
def add_to_fold(self, data, label):
# Ajoute les éléments dans la liste data au dic_fold label et mets à jour dic_dicount
print('hello', self.dic_dicount)
tmp = [(key, value[label]) for key, value in self.dic_dicount.items()]
fold = min(tmp, key=lambda x: x[1])[0]
self.dic_fold[fold][label] += [data]
self.dic_dicount[fold][label] += len(data)
self.labels_order += [label]
# Extract the 10s recorded
record = self.data_recorded[:NB_POINTS_ONE_RECORD]
self.data_recorded = self.data_recorded[NB_POINTS_ONE_RECORD:]
# Extract data between begin and end
begin = FREQ*BEGIN_RECORD
end = FREQ*END_RECORD
data = record[begin:end]
add_to_fold(self, data, self.current_label)
# End record
print('Stop.')
retrieve_record(self)
self.is_tampon = True
return len(self.labels_queue) > 0
#----------------------------------------
def end_of_session(self):
def maj_dicount(self, fold):
filename = self.dir_name + fold + '/dicount.pick'
dicount = self.dic_dicount[fold]
pickle.dump(dicount, open(filename, 'wb'))
def add_label_stimulation(self, data, label):
# Add the stimulations indicating the begginning of a label
length = len(data)
event_id = [None for _ in range(length)]
event_date = [None for _ in range(length)]
event_duration = [None for _ in range(length)]
event_id[0] = get_stim_code_from_label(label)
dict_event = {"Event Id": event_id, "Event Date": event_date, "Event Duration": event_duration}
dict_data = {c: np.array(data)[:, i].tolist() for i, c in enumerate(CHANNELS_NAME)}
dict_data.update(dict_event)
return dict_data
def append_data_to_csv(self, data, fold, label, several_csv=False):
# Create all csv, either you can us one CSV with stimulations, either one CSV per label
if several_csv:
filename = self.dir_name + fold + '/' + label + '.csv'
columns = CHANNELS_NAME
else:
filename = self.dir_name + fold + '/' + fold + '.csv'
columns = CHANNELS_NAME + CHANNELS_STIMULATION
data = add_label_stimulation(self, data, label)
old_df = DataFrame()
if os.path.isfile(filename):
old_df = read_csv(filename).filter(columns)
add_df = DataFrame(data, columns=columns)
new_df = old_df.append(add_df, ignore_index=True)
ovdf(new_df, rewrite_stim=several_csv)
new_df.to_csv(filename, index=False)
labels_count = {label: 0 for label in self.labels}
self.labels_order = {'fold_{}'.format(n): self.labels_order[self.nb_action_per_session*i: self.nb_action_per_session*(i+1)]
for i in range(self.nb_fold) for n in range(1, self.nb_fold+1)}
for i in range(1, self.nb_fold+1):
fold = 'fold_{}'.format(i)
for label in self.labels_order[fold]:
count = labels_count[label]
data = self.dic_fold[fold][label][count]
labels_count[label] += 1
append_data_to_csv(self, data, fold, label, several_csv=self.several_csv)
maj_dicount(self, fold)
box = DatasetCreator()
@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
from pyriemann.classification import MDM
from pyriemann.tangentspace import TangentSpace
from pyriemann.estimation import Covariances
from sklearn.pipeline import make_pipeline
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression, SGDClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier, ExtraTreesClassifier, BaggingClassifier
from sklearn.neighbors import KNeighborsClassifier, NearestCentroid
from sklearn.metrics import confusion_matrix, classification_report
from collections import defaultdict
import numpy as np
import pickle
import os
from PolyBox import PolyBox
import warnings
warnings.filterwarnings("ignore")
class ProcessML(PolyBox):
#----------------------------------------
def __init__(self):
PolyBox.__init__(self, record=False)
self.model_path = None
self.pred_saving_path = None
self.model = None
self.predictions = []
self.shape = None
#----------------------------------------
def on_initialize(self):
# we get the model file
self.model_path = self.setting['Model filename']
# we load the model
try:
self.model = pickle.load(open(self.model_path, 'rb'))
except IOError as err:
print(err)
print('Please indicate an existing model.')
self.send_end_stim()
try:
self.pred_saving_path = self.setting['Predictions filename']
if len(self.pred_saving_path.replace(' ', '')) > 0:
self.pred_saving_path = os.path.abspath(self.pred_saving_path)
except IOError:
print('No filename to save predictions has been given, they will not be saved')
#----------------------------------------
def on_end_box(self):
self.save_preds()
self.make_stats()
#----------------------------------------
def list_to_str(self, preds):
string = ''
for x in preds:
string += str(x) + ','
return string
#----------------------------------------
def save_preds(self):
if self.pred_saving_path is not None and self.pred_saving_path != "":
preds_str = self.list_to_str(self.predictions)
with open(self.pred_saving_path, 'wb') as file:
file.write(preds_str)
print('Predictions saved in {}\n'.format(self.pred_saving_path))
#----------------------------------------
def on_chunk_received(self, chunk, label, shape):
chunk = np.array(chunk)
if self.shape is None:
# Riemanian Geometry
if self.model.custom_classifier == 'Riemann Minimum Distance to Mean' or self.model.custom_classifier == 'Riemann Tangent Space':
self.shape = (1, shape[0], shape[1])
else:
self.shape = (1, chunk.shape[0])
chunk = chunk.reshape(self.shape)
pred = self.model.predict(chunk)
self.predictions += list(pred)
#----------------------------------------
def make_stats(self):
length = len(self.predictions)
dictpred = defaultdict(int)
for elem in self.predictions:
dictpred[elem] += 1
print("Metrics : \n")
print('\n'.join(['{} : {}'.format(l, float(v)/length) for l, v in dictpred.items()]))
box = ProcessML()
@@ -0,0 +1,247 @@
# -*- coding: utf-8 -*-
from pyriemann.classification import MDM
from pyriemann.tangentspace import TangentSpace
from pyriemann.estimation import Covariances
import time
from sklearn.pipeline import make_pipeline
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression, SGDClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import LinearSVC
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier, ExtraTreesClassifier, BaggingClassifier
from sklearn.neighbors import KNeighborsClassifier, NearestCentroid
from sklearn.metrics import confusion_matrix, classification_report
from natsort import natsorted
import random
import pickle
import numpy as np
from collections import defaultdict
from PolyBox import PolyBox
import warnings
warnings.filterwarnings("ignore")
#----------------------------------------
def train_test_split(data_dict, test_size=0.2):
def create_xy(data_dict):
# used to shuffle data in a way that prevent any training data to be in the test set
y = np.array([])
for label in data_dict:
y_tmp = np.array([label for _ in data_dict[label]])
y = np.concatenate((y, y_tmp))
array = []
for label in data_dict:
array.append(data_dict[label])
x = np.concatenate(array, axis=0)
# shuffling
permutation = np.random.permutation(x.shape[0])
permut = True
if permut:
x = x[permutation]
y = y[permutation]
return x, y
dict_train = {}
dict_test = {}
for key, data in data_dict.items():
icut = int(len(data)*test_size)
dict_test[key] = data[:icut]
dict_train[key] = data[icut:]
x_train, y_train = create_xy(dict_train)
x_test, y_test = create_xy(dict_test)
return np.array(x_train), np.array(y_train), np.array(x_test), np.array(y_test)
#----------------------------------------
class TrainerML(PolyBox):
#----------------------------------------
# is exec first
def __init__(self):
PolyBox.__init__(self)
self.model_path = None
self.config_path = None
self.save_path = None
self.model = None
self.clf = None
self.config = None
self.std_settings = []
self.clf_dependant_settings = None
#----------------------------------------
def on_initialize(self):
try:
self.model_path = self.setting['Filename to save model to']
except KeyError:
self.model_path = ''
if self.model_path == '':
print('No correct file location has been given for saving the model, thus it won\'t be saved')
try:
self.test_set_share = float(self.setting['Test set share'])
# wrong value
diff = (1 - self.test_set_share)
if diff <= 0 or diff > 1:
self.test_set_share = 0
print('The value of the test set share must be between 0 and 1 (1 not included), no prediction will be performed')
except KeyError:
self.test_set_share = 0
print('The value of the test set share must be between 0 and 1 (1 not included), no prediction will be performed')
try:
self.save_path = self.setting['Filename to load model from']
self.model = pickle.load(open(self.save_path, 'rb'))
except KeyError:
self.save_path = ''
print('No correct location has been given to load the model from, thus a new model will be created.')
# if model doesn't exist we will init a new one with params from the box
# FileNotFoundError doesn't exist in Python 2.7
except IOError:
print('No correct location has been given to load the model from, thus a new model will be created.')
# special case for Riemannian Geometry because it needs a pipeline
clf = self.setting['Classifier']
try:
discriminator, _ = self.map_clf(self.setting['Discriminator'])
except KeyError:
discriminator = None
if clf == 'Riemann Tangent Space':
if discriminator is not None:
self.clf = make_pipeline(Covariances(), TangentSpace(metric='riemann'), discriminator())
else:
self.clf = make_pipeline(Covariances(), TangentSpace(metric='riemann'), LinearDiscriminantAnalysis())
elif clf == 'Riemann Minimum Distance to Mean':
if discriminator is not None:
self.clf = make_pipeline(Covariances(), MDM(metric=dict(mean='riemann', distance='riemann')), discriminator())
else:
self.clf = make_pipeline(Covariances(), MDM(metric=dict(mean='riemann', distance='riemann')))
else:
self.clf, _ = self.map_clf(clf)
self.init_params()
try:
self.clf = self.clf(**self.clf_dependant_settings)
except TypeError:
self.clf = self.clf()
#----------------------------------------
def init_params(self):
# default settings
self.std_settings = ['Classifier', 'Discriminator', 'Filename to load model from', 'Test set share',
'Filename to save configuration to', 'Filename to save model to', 'Clock frequency (Hz)', 'Labels']
settings = [key for key in self.setting.keys()]
# we get only settings that are clf relevant
clf_dependant_settings = list(set(settings) - set(self.std_settings))
clf_dependant_settings = dict((k, v) for k, v in self.setting.items() if (k in clf_dependant_settings and len(v) > 0))
# we convert values that need to be
for k, v in clf_dependant_settings.items():
try:
expr = eval(v)
clf_dependant_settings[k] = expr
except:
if v.lower() == 'true':
clf_dependant_settings[k] = True
elif v.lower() == 'false':
clf_dependant_settings[k] = False
elif v.lower() == 'none':
clf_dependant_settings[k] = None
else:
pass
self.clf_dependant_settings = clf_dependant_settings
#----------------------------------------
def map_clf(self, classifier):
"""
Returns the correct algorithm according to the classifier string
"""
switcher = {
'': None,
'None': None,
'Nearest Centroid': NearestCentroid,
'Nearest Neighbors Classifier': KNeighborsClassifier,
'Gaussian Naive Bayes': GaussianNB,
'Stochastic Gradient Descent': SGDClassifier,
'Logistic Regression': LogisticRegression,
'Decision Tree Classifier': DecisionTreeClassifier,
'Extra Trees': ExtraTreesClassifier,
'Bagging': BaggingClassifier,
'Random Forest': RandomForestClassifier,
'Support Vector Machine': LinearSVC,
'Linear Discriminant Analysis': LinearDiscriminantAnalysis,
'AdaBoost': AdaBoostClassifier,
'Multi Layer Perceptron': MLPClassifier,
'Linear SVC': LinearSVC,
}
clf = switcher.get(classifier, lambda: 'unknown classifier')
return clf, switcher
#----------------------------------------
def on_chunk_received(self, chunk, label, shape):
# special case for riemannian geometry
if self.setting['Classifier'] == 'Riemann Minimum Distance to Mean' or self.setting['Classifier'] == 'Riemann Tangent Space':
numpyBuffer = np.array(chunk).reshape(shape)
self.data[label][-1] = numpyBuffer
#----------------------------------------
def on_end_box(self):
try:
self.train()
self.save()
except Exception as e:
print(e)
self.send_end_stim()
#----------------------------------------
def train(self):
x_train, y_train, x_test, y_test = train_test_split(self.data, self.test_set_share)
if self.model != None:
self.clf = self.model
self.clf.fit(x_train, y_train)
# to be used in ProcessML
self.clf.custom_classifier = self.setting['Classifier']
if x_test.shape[0] > 0:
predictions = self.clf.predict(x_test)
report = classification_report(y_test, predictions, labels=list(self.data.keys()))
matrix = confusion_matrix(y_test, predictions)
print("Report :\n{}\n".format(report))
print("Confusion Matrix : \n{}\n".format(matrix))
print("Fin de l'entrainement...")
#----------------------------------------
def save(self):
if self.model_path != "":
pickle.dump(self.clf, open(self.model_path, 'wb'))
print('Model saved in {}\n'.format(self.model_path))
box = TrainerML()
@@ -0,0 +1,120 @@
///-------------------------------------------------------------------------------------------------
///
/// \file ovpADA.h
/// \brief Class NewBoxPattern
/// \author Thibaut Monseigne (Inria) & Jimmy Leblanc (Polymont) & Yannis Bendi-Ouis (Polymont)
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/%22%3EGNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
// Windows debug build doesn't typically link as most people don't have the python debug library.
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#include <Python.h>
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <vector>
namespace OpenViBE {
namespace Plugins {
namespace PyBox {
/// <summary> Enumeration of Adaptation Methods for classifier. </summary>
enum class EClassifier
{
NearestCentroid, NearestNeighbors, GaussianNaiveBayes, StochasticGradientDescent, LogisticRegression, DecisionTree, ExtraTrees,
Bagging, RandomForest, SVM, LDA, AdaBoost, MultiLayerPerceptron, MDM, TangentSpace, None
};
/// <summary> Convert classifier to string.</summary>
/// <param name="type"> The type of classifier.</param>
/// <returns> std::string </returns>
inline std::string toString(const EClassifier type)
{
switch (type)
{
case EClassifier::None: return "None";
case EClassifier::NearestCentroid: return "Nearest Centroid";
case EClassifier::NearestNeighbors: return "Nearest Neighbors";
case EClassifier::GaussianNaiveBayes: return "Gaussian Naive Bayes";
case EClassifier::StochasticGradientDescent: return "Stochastic Gradient Descent";
case EClassifier::LogisticRegression: return "Logistic Regression";
case EClassifier::DecisionTree: return "Decision Tree";
case EClassifier::ExtraTrees: return "Extra Trees";
case EClassifier::Bagging: return "Bagging";
case EClassifier::RandomForest: return "Random Forest";
case EClassifier::SVM: return "Support Vector Machine";
case EClassifier::LDA: return "Linear Discriminant Analysis";
case EClassifier::AdaBoost: return "AdaBoost";
case EClassifier::MultiLayerPerceptron: return "Multi Layer Perceptron";
case EClassifier::MDM: return "Riemann Minimum Distance to Mean";
case EClassifier::TangentSpace: return "Riemann Tangent Space";
default: return "Invalid";
}
}
class CPolyBox : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
uint64_t getClockFrequency() override { return m_clockFrequency << 32; }
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool processInput(size_t index) override;
bool process() override;
protected:
uint64_t m_clockFrequency = 0;
CString m_script;
std::vector<Toolkit::TDecoder<CPolyBox>*> m_decoders;
std::vector<Toolkit::TEncoder<CPolyBox>*> m_encoders;
// These are all borrowed references in python v2.7. Do not free them.
static bool m_isInitialized;
static PyObject *m_mainModule, *m_mainDictionnary;
static PyObject *m_matrixHeader, *m_matrixBuffer, *m_matrixEnd;
static PyObject *m_signalHeader, *m_signalBuffer, *m_signalEnd;
static PyObject *m_stimulationHeader, *m_stimulation, *m_stimulationSet, *m_stimulationEnd;
static PyObject* m_buffer;
static PyObject* m_execFileFunction;
static PyObject *m_stdout, *m_stderr;
//std::map<char,PyObject *> m_PyObjectMap;
PyObject *m_box = nullptr, *m_boxInput = nullptr, *m_boxOutput = nullptr, *m_boxSetting = nullptr, *m_boxTime = nullptr;
PyObject *m_boxInitialize = nullptr, *m_boxProcess = nullptr, *m_boxUninitialize = nullptr;
bool m_initializeSucceeded = false;
bool logSysStd(const bool out);
bool logSysStdout() { return logSysStd(true); }
bool logSysStderr() { return logSysStd(false); }
void buildPythonSettings();
bool initializePythonSafely();
bool transferStreamedMatrixInputChunksToPython(const size_t index);
bool transferStreamedMatrixOutputChunksFromPython(const size_t index);
bool transferSignalInputChunksToPython(const size_t index);
bool transferSignalOutputChunksFromPython(const size_t index);
bool transferStimulationInputChunksToPython(const size_t index);
bool transferStimulationOutputChunksFromPython(const size_t index);
};
} // namespace PyBox
} // namespace Plugins
} // namespace OpenViBE
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3
@@ -0,0 +1,99 @@
///-------------------------------------------------------------------------------------------------
///
/// \file DataViz.hpp
/// \brief Class NewBoxPattern
/// \author Thibaut Monseigne (Inria) & Jimmy Leblanc (Polymont) & Yannis Bendi-Ouis (Polymont)
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/%22%3EGNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "CPolyBox.hpp"
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
namespace OpenViBE {
namespace Plugins {
namespace PyBox {
class CBoxAlgorithmDataViz final : public CPolyBox
{
public:
CBoxAlgorithmDataViz() { m_script = Directories::getDataDir() + "/plugins/python3/pybox/DataViz.py"; }
_IsDerivedFromClass_Final_(OpenViBE::Toolkit::TBoxAlgorithm < OpenViBE::Plugins::IBoxAlgorithm >, OVP_ClassId_BoxAlgorithm_DataViz)
};
class CBoxAlgorithmDataVizListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
box.setInputType(index, OV_TypeId_StreamedMatrix);
return true;
}
_IsDerivedFromClass_Final_(OpenViBE::Toolkit::TBoxListener < OpenViBE::Plugins::IBoxListener >, CIdentifier::undefined())
};
class CBoxAlgorithmDataVizDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("DataViz"); }
CString getAuthorName() const override { return CString("Yannis Bendi-Ouis & Jimmy Leblanc"); }
CString getAuthorCompanyName() const override { return CString("Polymont IT Services"); }
CString getShortDescription() const override { return CString("Transform the data with a LDA or a PCA and plot the data in 2D or 3D."); }
CString getDetailedDescription() const override { return CString(""); }
CString getCategory() const override { return CString("Scripting/PyBox"); }
CString getVersion() const override { return CString("0.1"); }
CString getStockItemName() const override { return CString("gtk-convert"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_DataViz; }
IPluginObject* create() override { return new CBoxAlgorithmDataViz; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmDataVizListener; }
void releaseBoxListener(IBoxListener* pBoxListener) const override { delete pBoxListener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addSetting("Clock frequency (Hz)", OV_TypeId_Integer, "64");
// <tag> settings
prototype.addSetting("Path to save the model", OV_TypeId_Filename, "");
prototype.addSetting("Path to load the model", OV_TypeId_Filename, "");
prototype.addSetting("Algorithm (PCA or LDA)", OV_TypeId_String, "PCA");
prototype.addSetting("Dimension reduction", OV_TypeId_Integer, "2");
prototype.addSetting("Labels", OV_TypeId_String, "");
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
prototype.addFlag(Kernel::BoxFlag_CanAddSetting);
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
prototype.addInputSupport(OV_TypeId_Signal);
prototype.addInputSupport(OV_TypeId_Stimulations);
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
prototype.addOutputSupport(OV_TypeId_Signal);
prototype.addOutputSupport(OV_TypeId_Stimulations);
prototype.addOutputSupport(OV_TypeId_StreamedMatrix);
// <tag> input & output
prototype.addOutput("stim_out", OV_TypeId_Stimulations);
prototype.addInput("input_StreamMatrix", OV_TypeId_StreamedMatrix);
prototype.addInput("input_Stimulations", OV_TypeId_Stimulations);
return true;
}
_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_DataVizDesc)
};
} // namespace PyBox
} // namespace Plugins
} // namespace OpenViBE
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3
@@ -0,0 +1,101 @@
///-------------------------------------------------------------------------------------------------
///
/// \file DatasetCreator.hpp
/// \brief Class NewBoxPattern
/// \author Thibaut Monseigne (Inria) & Jimmy Leblanc (Polymont) & Yannis Bendi-Ouis (Polymont)
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/%22%3EGNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "CPolyBox.hpp"
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
namespace OpenViBE {
namespace Plugins {
namespace PyBox {
class CBoxAlgorithmDatasetCreator final : public CPolyBox
{
public:
CBoxAlgorithmDatasetCreator() { m_script = Directories::getDataDir() + "/plugins/python3/pybox/DatasetCreator.py"; }
_IsDerivedFromClass_Final_(OpenViBE::Toolkit::TBoxAlgorithm < OpenViBE::Plugins::IBoxAlgorithm >, OVP_ClassId_BoxAlgorithm_DatasetCreator)
};
class CBoxAlgorithmDatasetCreatorListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
box.setInputType(index, OV_TypeId_StreamedMatrix);
return true;
}
_IsDerivedFromClass_Final_(OpenViBE::Toolkit::TBoxListener < OpenViBE::Plugins::IBoxListener >, CIdentifier::undefined())
};
class CBoxAlgorithmDatasetCreatorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("DatasetCreator"); }
CString getAuthorName() const override { return CString("Yannis Bendi-Ouis & Jimmy LeBlanc"); }
CString getAuthorCompanyName() const override { return CString("Polymont IT Services"); }
CString getShortDescription() const override { return CString("Monitor the user to create a dataset."); }
CString getDetailedDescription() const override { return CString(""); }
CString getCategory() const override { return CString("Scripting/PyBox"); }
CString getVersion() const override { return CString("0.1"); }
CString getStockItemName() const override { return CString("gtk-convert"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_DatasetCreator; }
IPluginObject* create() override { return new CBoxAlgorithmDatasetCreator; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmDatasetCreatorListener; }
void releaseBoxListener(IBoxListener* pBoxListener) const override { delete pBoxListener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addSetting("Clock frequency (Hz)", OV_TypeId_Integer, "64");
// <tag> settings
prototype.addSetting("Path directory", OV_TypeId_Filename, "${Player_ScenarioDirectory}/datas/");
prototype.addSetting("Label_1", OV_TypeId_String, "right");
prototype.addSetting("Label_2", OV_TypeId_String, "left");
prototype.addSetting("Label_3", OV_TypeId_String, "up");
prototype.addSetting("Label_4", OV_TypeId_String, "down");
prototype.addSetting("Several CSV", OV_TypeId_Boolean, "false");
prototype.addSetting("Number of folds", OV_TypeId_Integer, "1");
prototype.addSetting("Number of actions", OV_TypeId_Integer, "4");
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
prototype.addFlag(Kernel::BoxFlag_CanAddSetting);
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
prototype.addInputSupport(OV_TypeId_Signal);
prototype.addInputSupport(OV_TypeId_Stimulations);
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
prototype.addOutputSupport(OV_TypeId_Signal);
prototype.addOutputSupport(OV_TypeId_Stimulations);
prototype.addOutputSupport(OV_TypeId_StreamedMatrix);
// <tag> input & output
prototype.addOutput("stim_out", OV_TypeId_Stimulations);
prototype.addInput("input_StreamMatrix", OV_TypeId_StreamedMatrix);
return true;
}
_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_DatasetCreatorDesc)
};
} // namespace PyBox
} // namespace Plugins
} // namespace OpenViBE
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3
@@ -0,0 +1,98 @@
///-------------------------------------------------------------------------------------------------
///
/// \file ProcessML.hpp
/// \brief Class NewBoxPattern
/// \author Thibaut Monseigne (Inria) & Jimmy Leblanc (Polymont) & Yannis Bendi-Ouis (Polymont)
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/%22%3EGNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "CPolyBox.hpp"
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
namespace OpenViBE {
namespace Plugins {
namespace PyBox {
class CBoxAlgorithmProcessML final : public CPolyBox
{
public:
CBoxAlgorithmProcessML() { m_script = Directories::getDataDir() + "/plugins/python3/pybox/ProcessML.py"; }
_IsDerivedFromClass_Final_(OpenViBE::Toolkit::TBoxAlgorithm < OpenViBE::Plugins::IBoxAlgorithm >, OVP_ClassId_BoxAlgorithm_ProcessML)
};
class CBoxAlgorithmProcessMLListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
box.setInputType(index, OV_TypeId_StreamedMatrix);
return true;
}
_IsDerivedFromClass_Final_(OpenViBE::Toolkit::TBoxListener < OpenViBE::Plugins::IBoxListener >, CIdentifier::undefined())
};
class CBoxAlgorithmProcessMLDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Process Sklearn"); }
CString getAuthorName() const override { return CString("Jimmy Leblanc & Yannis Bendi-Ouis"); }
CString getAuthorCompanyName() const override { return CString("Polymont IT Services"); }
CString getShortDescription() const override
{
return CString("This box aim to use a machine learning model previously trained to predict labels of input's data.");
}
CString getDetailedDescription() const override { return CString(""); }
CString getCategory() const override { return CString("Scripting/PyBox"); }
CString getVersion() const override { return CString("0.1"); }
CString getStockItemName() const override { return CString("gtk-convert"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ProcessML; }
IPluginObject* create() override { return new CBoxAlgorithmProcessML; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmProcessMLListener; }
void releaseBoxListener(IBoxListener* pBoxListener) const override { delete pBoxListener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addSetting("Clock frequency (Hz)", OV_TypeId_Integer, "64");
// <tag> settings
prototype.addSetting("Model filename", OV_TypeId_Filename, "${Player_ScenarioDirectory}/model.clf");
prototype.addSetting("Predictions filename", OV_TypeId_Filename, "");
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
prototype.addFlag(Kernel::BoxFlag_CanAddSetting);
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
prototype.addInputSupport(OV_TypeId_Signal);
prototype.addInputSupport(OV_TypeId_Stimulations);
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
prototype.addOutputSupport(OV_TypeId_Signal);
prototype.addOutputSupport(OV_TypeId_Stimulations);
prototype.addOutputSupport(OV_TypeId_StreamedMatrix);
// <tag> input & output
prototype.addOutput("stim_out", OV_TypeId_Stimulations);
prototype.addInput("input_StreamMatrix", OV_TypeId_StreamedMatrix);
return true;
}
_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ProcessMLDesc)
};
} // namespace PyBox
} // namespace Plugins
} // namespace OpenViBE
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3
@@ -0,0 +1,257 @@
#include "TrainerML.hpp"
#include <tuple>
#include <vector>
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
namespace OpenViBE {
namespace Plugins {
namespace PyBox {
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> ADA_BOOST_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("n_estimators", OV_TypeId_Integer, "50"),
std::make_tuple("learning_rate", OV_TypeId_Float, "1.0"),
std::make_tuple("algorithm", OVPoly_ClassId_ADA_algorithm, "SAMME.R"),
std::make_tuple("random_state", OV_TypeId_String, "None")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> BAGGING_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("max_features", OV_TypeId_Float, "1.0"),
std::make_tuple("random_state", OV_TypeId_String, "None"),
std::make_tuple("n_estimators", OV_TypeId_Integer, "10"),
std::make_tuple("bootstrap", OV_TypeId_Boolean, "true"),
std::make_tuple("bootstrap_features", OV_TypeId_Boolean, "false"),
std::make_tuple("oob_score", OV_TypeId_Boolean, "false"),
std::make_tuple("warm_start", OV_TypeId_Boolean, "false"),
std::make_tuple("n_jobs", OV_TypeId_String, "None"),
std::make_tuple("verbose", OV_TypeId_Integer, "0")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> TREE_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("criterion", OVPoly_ClassId_Criterion, "gini"),
std::make_tuple("splitter", OVPoly_ClassId_DecisionTree_splitter, "best"),
std::make_tuple("max_depth", OV_TypeId_String, "None"),
std::make_tuple("min_samples_split", OV_TypeId_Float, "2"),
std::make_tuple("min_samples_leaf", OV_TypeId_Float, "1"),
std::make_tuple("min_weight_fraction_leaf", OV_TypeId_Float, "0"),
std::make_tuple("max_features", OV_TypeId_String, "None"),
std::make_tuple("random_state", OV_TypeId_String, "None"),
std::make_tuple("max_leaf_nodes", OV_TypeId_String, "None"),
std::make_tuple("min_impurity_decrease", OV_TypeId_Float, "0"),
std::make_tuple("min_impurity_split", OV_TypeId_Float, "1e-7")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> XTREE_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("criterion", OVPoly_ClassId_Criterion, "gini"),
std::make_tuple("max_depth", OV_TypeId_String, "None"),
std::make_tuple("min_samples_split", OV_TypeId_Float, "2"),
std::make_tuple("min_samples_leaf", OV_TypeId_Float, "1"),
std::make_tuple("min_weight_fraction_leaf", OV_TypeId_Float, "0"),
std::make_tuple("max_features", OV_TypeId_String, "auto"),
std::make_tuple("random_state", OV_TypeId_String, "None"),
std::make_tuple("max_leaf_nodes", OV_TypeId_String, "None"),
std::make_tuple("min_impurity_decrease", OV_TypeId_Float, "0"),
std::make_tuple("min_impurity_split", OV_TypeId_Float, "1e-7"),
std::make_tuple("bootstrap", OV_TypeId_Boolean, "false"),
std::make_tuple("oob_score", OV_TypeId_Boolean, "false"),
std::make_tuple("verbose", OV_TypeId_Integer, "0"),
std::make_tuple("warm_start", OV_TypeId_Boolean, "false"),
std::make_tuple("max_samples", OV_TypeId_String, "None"),
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> GAUSSIAN_SETTING = {
std::make_tuple("Discriminator", OV_TypeId_String, ""),
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("var_smoothing", OV_TypeId_Float, "0.000000001"),
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> KNN_SETTING = {
std::make_tuple("Discriminator", OV_TypeId_String, ""),
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("n_neighbors", OV_TypeId_Integer, "5"),
std::make_tuple("weights", OVPoly_ClassId_Knn_Weights, "uniform"),
std::make_tuple("algorithm", OVPoly_ClassId_Knn_Algorithm, "auto"),
std::make_tuple("leaf_size", OV_TypeId_Integer, "30"),
std::make_tuple("p", OV_TypeId_Integer, "2"),
std::make_tuple("metric", OVPoly_ClassId_Metric, "minkowski"),
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> LDA_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("solver", OVPoly_ClassId_LDA_solver, "svd"),
std::make_tuple("n_components", OV_TypeId_String, ""),
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> LR_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("penalty", OVPoly_ClassId_Penalty, "l2"),
std::make_tuple("dual", OV_TypeId_Boolean, "false"),
std::make_tuple("tol", OV_TypeId_Float, "1e-4"),
std::make_tuple("fit_intercept", OV_TypeId_Boolean, "true"),
std::make_tuple("intercept_scaling", OV_TypeId_Float, "1"),
std::make_tuple("random_state", OV_TypeId_String, "None"),
std::make_tuple("solver", OVPoly_ClassId_Log_reg_solver, "lbfgs"),
std::make_tuple("max_iter", OV_TypeId_Integer, "100"),
std::make_tuple("verbose", OV_TypeId_Integer, "0"),
std::make_tuple("warm_start", OV_TypeId_Boolean, "false"),
std::make_tuple("n_jobs", OV_TypeId_String, "None"),
std::make_tuple("multi_class", OVPoly_ClassId_Log_reg_multi_class, "auto"),
std::make_tuple("C", OV_TypeId_Float, "1.0")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> MLP_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("hidden_layer_sizes", OV_TypeId_String, "(100,)"),
std::make_tuple("activation", OVPoly_ClassId_MLP_activation, "relu"),
std::make_tuple("learning_rate", OVPoly_ClassId_MLP_learning_rate, "constant"),
std::make_tuple("solver", OVPoly_ClassId_MLP_solver, "adam"),
std::make_tuple("alpha", OV_TypeId_Float, "0.0001"),
std::make_tuple("batch_size", OV_TypeId_String, "auto"),
std::make_tuple("learning_rate_init", OV_TypeId_Float, "0.001"),
std::make_tuple("power_t", OV_TypeId_Float, "0.5"),
std::make_tuple("max_iter", OV_TypeId_Integer, "200"),
std::make_tuple("shuffle", OV_TypeId_Boolean, "true"),
std::make_tuple("random_state", OV_TypeId_String, "None"),
std::make_tuple("tol", OV_TypeId_Float, "1e-4"),
std::make_tuple("verbose", OV_TypeId_Boolean, "true"),
std::make_tuple("warm_start", OV_TypeId_Boolean, "false"),
std::make_tuple("momentum", OV_TypeId_Float, "0.9"),
std::make_tuple("nesterovs_momentum", OV_TypeId_Boolean, "true"),
std::make_tuple("early_stopping", OV_TypeId_Boolean, "false"),
std::make_tuple("validation_fraction", OV_TypeId_Float, "0.1"),
std::make_tuple("beta_1", OV_TypeId_Float, "0.9"),
std::make_tuple("beta_2", OV_TypeId_Float, "0.999"),
std::make_tuple("epsilon", OV_TypeId_Float, "1e-8"),
std::make_tuple("n_iter_no_change", OV_TypeId_Integer, "10")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> NC_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("metric", OVPoly_ClassId_Metric, "euclidean"),
std::make_tuple("shrink_threshold", OV_TypeId_String, "")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> RF_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("criterion", OVPoly_ClassId_Criterion, "gini"),
std::make_tuple("max_depth", OV_TypeId_String, "None"),
std::make_tuple("min_samples_split", OV_TypeId_Float, "2"),
std::make_tuple("min_samples_leaf", OV_TypeId_Float, "1"),
std::make_tuple("min_weight_fraction_leaf", OV_TypeId_Float, "0"),
std::make_tuple("max_features", OV_TypeId_String, "auto"),
std::make_tuple("random_state", OV_TypeId_String, "None"),
std::make_tuple("max_leaf_nodes", OV_TypeId_String, "None"),
std::make_tuple("min_impurity_decrease", OV_TypeId_Float, "0"),
std::make_tuple("min_impurity_split", OV_TypeId_Float, "1e-7"),
std::make_tuple("bootstrap", OV_TypeId_Boolean, "false"),
std::make_tuple("oob_score", OV_TypeId_Boolean, "false"),
std::make_tuple("verbose", OV_TypeId_Integer, "0"),
std::make_tuple("warm_start", OV_TypeId_Boolean, "false"),
std::make_tuple("max_samples", OV_TypeId_String, "None"),
std::make_tuple("n_estimators", OV_TypeId_Integer, "100")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> RTS_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("Discriminator", OVPoly_ClassId_Classifier_Algorithm, "Linear Discriminant Analysis")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> RMDM_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("Discriminator", OVPoly_ClassId_Classifier_Algorithm, "None")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> SGD_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("loss", OVPoly_ClassId_SGD_loss, "hinge"),
std::make_tuple("penalty", OVPoly_ClassId_Penalty, "l2"),
std::make_tuple("alpha", OV_TypeId_Float, "0.0001"),
std::make_tuple("l1_ratio", OV_TypeId_Float, "0.15"),
std::make_tuple("fit_intercept", OV_TypeId_Boolean, "true"),
std::make_tuple("max_iter", OV_TypeId_Integer, "1000"),
std::make_tuple("tol", OV_TypeId_Float, "0.001"),
std::make_tuple("shuffle", OV_TypeId_Boolean, "true"),
std::make_tuple("verbose", OV_TypeId_Integer, "0"),
std::make_tuple("epsilon", OV_TypeId_Float, "0.1"),
std::make_tuple("random_state", OV_TypeId_String, ""),
std::make_tuple("learning_rate", OVPoly_ClassId_SGD_learning_rate, "optimal"),
std::make_tuple("early_stopping", OV_TypeId_Boolean, "false"),
std::make_tuple("n_iter_no_change", OV_TypeId_Integer, "5")
};
static const std::vector<std::tuple<std::string, CIdentifier, std::string>> SVM_SETTING = {
std::make_tuple("Test set share", OV_TypeId_Float, "0.2"),
std::make_tuple("Labels", OV_TypeId_String, ""),
std::make_tuple("penalty", OVPoly_ClassId_Penalty, "l2"),
std::make_tuple("loss", OVPoly_ClassId_SVM_Loss, "squared_hinge"),
std::make_tuple("dual", OV_TypeId_Boolean, "true"),
std::make_tuple("tol", OV_TypeId_Float, "0.0001"),
std::make_tuple("C", OV_TypeId_Float, "1.0"),
std::make_tuple("multi_class", OVPoly_ClassId_SVM_MultiClass, "ovr"),
std::make_tuple("fit_intercept", OV_TypeId_Boolean, "true"),
std::make_tuple("intercept_scaling", OV_TypeId_Float, "1"),
std::make_tuple("verbose", OV_TypeId_Integer, "0"),
std::make_tuple("max_iter", OV_TypeId_Integer, "1000")
};
static void ClearSetting(Kernel::IBox& box) { while (box.getSettingCount() > 4) { box.removeSetting(4); } }
static bool SetSetting(Kernel::IBox& box, const std::vector<std::tuple<std::string, CIdentifier, std::string>>& settings)
{
for (const auto& t : settings) { box.addSetting(std::get<0>(t).c_str(), std::get<1>(t), std::get<2>(t).c_str()); }
return true;
}
bool CBoxAlgorithmTrainerMLListener::onSettingValueChanged(Kernel::IBox& box, const size_t index)
{
if (index == 3)
{
CString value;
box.getSettingValue(index, value);
ClearSetting(box);
if (std::string(value.toASCIIString()) == toString(EClassifier::NearestCentroid)) { return SetSetting(box, NC_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::NearestNeighbors)) { return SetSetting(box, KNN_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::GaussianNaiveBayes)) { return SetSetting(box, GAUSSIAN_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::StochasticGradientDescent)) { return SetSetting(box, SGD_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::LogisticRegression)) { return SetSetting(box, LR_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::DecisionTree)) { return SetSetting(box, TREE_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::ExtraTrees)) { return SetSetting(box, XTREE_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::Bagging)) { return SetSetting(box, BAGGING_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::RandomForest)) { return SetSetting(box, RF_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::SVM)) { return SetSetting(box, SVM_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::LDA)) { return SetSetting(box, LDA_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::AdaBoost)) { return SetSetting(box, ADA_BOOST_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::MultiLayerPerceptron)) { return SetSetting(box, MLP_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::MDM)) { return SetSetting(box, RMDM_SETTING); }
if (std::string(value.toASCIIString()) == toString(EClassifier::TangentSpace)) { return SetSetting(box, RTS_SETTING); }
return true;
}
}
} // namespace PyBox
} // namespace Plugins
} // namespace OpenViBE
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3
@@ -0,0 +1,94 @@
///-------------------------------------------------------------------------------------------------
///
/// \file TrainerML.hpp
/// \brief Class TrainerML
/// \author Thibaut Monseigne (Inria) & Jimmy Leblanc (Polymont) & Yannis Bendi-Ouis (Polymont)
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/%22%3EGNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "CPolyBox.hpp"
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
namespace OpenViBE {
namespace Plugins {
namespace PyBox {
class CBoxAlgorithmTrainerML final : public CPolyBox
{
public:
CBoxAlgorithmTrainerML() { m_script = Directories::getDataDir() + "/plugins/python3/pybox/TrainerML.py"; }
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_TrainerML)
};
class CBoxAlgorithmTrainerMLListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
box.setInputType(index, OV_TypeId_StreamedMatrix);
return true;
}
bool onSettingValueChanged(Kernel::IBox& box, const size_t index) override;
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
class CBoxAlgorithmTrainerMLDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Trainer Sklearn"); }
CString getAuthorName() const override { return CString("Jimmy Leblanc & Yannis Bendi-Ouis"); }
CString getAuthorCompanyName() const override { return CString("Polymont IT Services"); }
CString getShortDescription() const override { return CString("Train an TrainerML Classifier from Sklearn."); }
CString getDetailedDescription() const override { return CString(""); }
CString getCategory() const override { return CString("Scripting/PyBox"); }
CString getVersion() const override { return CString("0.1"); }
CString getStockItemName() const override { return CString("gtk-convert"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_TrainerML; }
IPluginObject* create() override { return new CBoxAlgorithmTrainerML; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmTrainerMLListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addSetting("Clock frequency (Hz)", OV_TypeId_Integer, "64");
// <tag> settings
prototype.addSetting("Filename to save model to", OV_TypeId_Filename, "${Player_ScenarioDirectory}/model.clf");
prototype.addSetting("Filename to load model from", OV_TypeId_Filename, "");
prototype.addSetting("Classifier", OVPoly_ClassId_Classifier_Algorithm, "None");
prototype.addInputSupport(OV_TypeId_Signal);
prototype.addInputSupport(OV_TypeId_Stimulations);
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
prototype.addOutputSupport(OV_TypeId_Signal);
prototype.addOutputSupport(OV_TypeId_Stimulations);
prototype.addOutputSupport(OV_TypeId_StreamedMatrix);
// <tag> input & output
prototype.addOutput("stim_out", OV_TypeId_Stimulations);
prototype.addInput("input_StreamMatrix", OV_TypeId_StreamedMatrix);
prototype.addInput("input_Stimulations", OV_TypeId_Stimulations);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_TrainerMLDesc)
};
} // namespace PyBox
} // namespace Plugins
} // namespace OpenViBE
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3
@@ -0,0 +1,58 @@
#pragma once
//__________________________________________________________________//
// //
// Box //
//__________________________________________________________________//
// //
// <tag> Tag Box Declaration
#define OVP_ClassId_BoxAlgorithm_DatasetCreatorDesc OpenViBE::CIdentifier(0x6EBF2A07, 0x52EA5F3E)
#define OVP_ClassId_BoxAlgorithm_DatasetCreator OpenViBE::CIdentifier(0x7B6738C4, 0x2C5156ED)
#define OVP_ClassId_BoxAlgorithm_DataVizDesc OpenViBE::CIdentifier(0x44E07FAC, 0x57BE15DB)
#define OVP_ClassId_BoxAlgorithm_DataViz OpenViBE::CIdentifier(0x057B49AD, 0x040868CF)
#define OVP_ClassId_BoxAlgorithm_ProcessMLDesc OpenViBE::CIdentifier(0x764A11C8, 0x04006876)
#define OVP_ClassId_BoxAlgorithm_ProcessML OpenViBE::CIdentifier(0x681D3DFE, 0x060E2AFB)
#define OVP_ClassId_BoxAlgorithm_TrainerMLDesc OpenViBE::CIdentifier(0x6b14815c, 0x41da26d4)
#define OVP_ClassId_BoxAlgorithm_TrainerML OpenViBE::CIdentifier(0x44b85a3a, 0xabfc390a)
//__________________________________________________________________//
// //
// Custom Settings //
//__________________________________________________________________//
// //
// <tag> Custom Type Settings
#define OVPoly_ClassId_Classifier_Algorithm OpenViBE::CIdentifier(0x73AE164D, 0xEA21AB0A)
#define OVPoly_ClassId_Knn_Algorithm OpenViBE::CIdentifier(0xBF763F3B, 0x303EC694)
#define OVPoly_ClassId_Knn_Weights OpenViBE::CIdentifier(0x178AF88A, 0xEC44DFF3)
#define OVPoly_ClassId_Metric OpenViBE::CIdentifier(0xA04E99E9, 0x020A6874)
#define OVPoly_ClassId_SVM_Loss OpenViBE::CIdentifier(0xF05612EC, 0x139AFC45)
#define OVPoly_ClassId_Penalty OpenViBE::CIdentifier(0xBA4193F2, 0xCD152A47)
#define OVPoly_ClassId_SVM_MultiClass OpenViBE::CIdentifier(0x743F2BC9, 0xA84BC9DF)
#define OVPoly_ClassId_Criterion OpenViBE::CIdentifier(0x7455C643, 0x5D1E74E7)
// Logistic regression
#define OVPoly_ClassId_Log_reg_solver OpenViBE::CIdentifier(0x8CF680DF, 0x9EA77031)
#define OVPoly_ClassId_Log_reg_multi_class OpenViBE::CIdentifier(0xC0B45F28, 0x13AEBFD2)
// Decision Tree
#define OVPoly_ClassId_DecisionTree_splitter OpenViBE::CIdentifier(0x4E3C6F6F, 0x9AC56CEA)
// MLP
#define OVPoly_ClassId_MLP_activation OpenViBE::CIdentifier(0x324DA925, 0x58A1BCD9)
#define OVPoly_ClassId_MLP_solver OpenViBE::CIdentifier(0xAD038F27, 0x9B0AB84F)
#define OVPoly_ClassId_MLP_learning_rate OpenViBE::CIdentifier(0x5B10D3BC, 0x57658821)
// SGD
#define OVPoly_ClassId_SGD_loss OpenViBE::CIdentifier(0x57CA4145, 0xB25841A5)
#define OVPoly_ClassId_SGD_learning_rate OpenViBE::CIdentifier(0xB6075C69, 0x93BE696B)
// LDA
#define OVPoly_ClassId_LDA_solver OpenViBE::CIdentifier(0x2B78F491, 0xB86A6DE7)
// ADA
#define OVPoly_ClassId_ADA_algorithm OpenViBE::CIdentifier(0x27025481, 0x09BEDC31)
@@ -0,0 +1,167 @@
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
#include "box-algorithms/CPolyBox.hpp"
#include "box-algorithms/DataViz.hpp"
#include "box-algorithms/DatasetCreator.hpp"
#include "box-algorithms/ProcessML.hpp"
#include "box-algorithms/TrainerML.hpp"
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#ifdef TARGET_OS_Windows
#include "windows.h"
#endif
#include <string>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace PyBox {
OVP_Declare_Begin()
// <tag> OVP_Declare_New
OVP_Declare_New(CBoxAlgorithmDatasetCreatorDesc);
OVP_Declare_New(CBoxAlgorithmDataVizDesc);
OVP_Declare_New(CBoxAlgorithmProcessMLDesc);
OVP_Declare_New(CBoxAlgorithmTrainerMLDesc);
// <tag> Custom Type Settings
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Classifier_Algorithm, "Classifier_Algorithm");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Nearest Centroid", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Nearest Neighbors", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Gaussian Naive Bayes", 2);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Stochastic Gradient Descent", 3);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Logistic Regression", 4);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Decision Tree", 5);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Extra Trees", 6);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Bagging", 7);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Random Forest", 8);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Support Vector Machine", 9);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Linear Discriminant Analysis", 10);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "AdaBoost", 11);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Multi Layer Perceptron", 12);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Riemann Minimum Distance to Mean", 13);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "Riemann Tangent Space", 14);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Classifier_Algorithm, "None", 15);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Knn_Algorithm, "Knn_Algorithm");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Knn_Algorithm, "auto", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Knn_Algorithm, "ball_tree", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Knn_Algorithm, "kd_tree", 2);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Knn_Algorithm, "brute", 3);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Knn_Weights, "Knn_Weights");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Knn_Weights, "uniform", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Knn_Weights, "distance", 1);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Metric, "Metric");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "cityblock", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "cosine", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "euclidean", 2);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "l1", 3);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "l2", 4);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "manhattan", 5);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "braycurtis", 6);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "canberra", 7);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "chebyshev", 8);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "correlation", 9);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "dice", 10);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "hamming", 11);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "jaccard", 12);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "kulsinski", 13);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "minkowski", 14);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "mahalanobis", 15);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "rogerstanimoto", 16);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "russellrao", 17);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "seuclidean", 18);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "sokalmichener", 19);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "sokalsneath", 20);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "sqeuclidean", 21);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Metric, "yule", 22);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Penalty, "Penalty");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Penalty, "l1", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Penalty, "l2", 1);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_SVM_Loss, "SVM_Loss");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SVM_Loss, "hinge", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SVM_Loss, "squared_hinge", 1);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_SVM_MultiClass, "SVM_MultiClass");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SVM_MultiClass, "ovr", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SVM_MultiClass, "crammer_singer", 1);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Criterion, "Criterion");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Criterion, "gini", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Criterion, "entropy", 1);
// Logistic regression
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Log_reg_solver, "Solver");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_solver, "newton-cg", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_solver, "lbfgs", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_solver, "liblinear", 2);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_solver, "sag", 3);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_solver, "saga", 4);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_Log_reg_multi_class, "Multi_class");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_multi_class, "auto", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_multi_class, "ovr", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_Log_reg_multi_class, "multinominal", 2);
// Decision Tree Classifier
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_DecisionTree_splitter, "Splitter");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_DecisionTree_splitter, "best", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_DecisionTree_splitter, "random", 1);
// MLP
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_MLP_activation, "Activation");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_activation, "identity", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_activation, "logistic", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_activation, "tanh", 2);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_activation, "relu", 3);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_MLP_solver, "Solver");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_solver, "lbfgs", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_solver, "sgd", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_solver, "adam", 2);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_MLP_learning_rate, "Learning rate");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_learning_rate, "constant", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_learning_rate, "invscaling", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_MLP_learning_rate, "adaptive", 2);
// SGD
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_SGD_loss, "Loss");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_loss, "hinge", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_loss, "log", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_loss, "modified_huber", 2);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_loss, "squared_hinge", 3);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_loss, "perceptron", 4);
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_SGD_learning_rate, "Learning rate");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_learning_rate, "optimal", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_learning_rate, "constant", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_learning_rate, "invscaling", 2);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_SGD_learning_rate, "adaptive", 3);
// LDA
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_LDA_solver, "Loss");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_LDA_solver, "svd", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_LDA_solver, "lsqr", 1);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_LDA_solver, "eigen", 2);
// ADA
context.getTypeManager().registerEnumerationType(OVPoly_ClassId_ADA_algorithm, "Algorithm");
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_ADA_algorithm, "SAMME", 0);
context.getTypeManager().registerEnumerationEntry(OVPoly_ClassId_ADA_algorithm, "SAMME.R", 1);
OVP_Declare_End()
} // namespace PyBox
} // namespace Plugins
} // namespace OpenViBE
#else
#pragma message ("WARNING: Python 3.7 headers are required to build the Python plugin, different includes found, skipped")
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
#endif // TARGET_HAS_ThirdPartyPython3