init
@@ -0,0 +1,5 @@
|
||||
|
||||
# Add all the subdirs as projects of the named branch
|
||||
OV_ADD_PROJECTS("CONTRIB")
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# Add all the subdirs as projects of the named branch
|
||||
OV_ADD_PROJECTS("CONTRIB_APPLICATIONS")
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 127 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.>
|
||||
|
||||
> 
|
||||
>
|
||||
> 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:
|
||||
|
||||
> 
|
||||
>
|
||||
> 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 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`.
|
||||
|
||||
> 
|
||||
>
|
||||
> Add/Remove/Manage Custom Settings.
|
||||
|
||||
Result in OpenVibe :
|
||||
|
||||
> 
|
||||
>
|
||||
> 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:
|
||||
|
||||
> 
|
||||
>
|
||||
> **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:
|
||||
|
||||
> 
|
||||
> **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>
|
||||
|
||||
> 
|
||||
>
|
||||
> 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 :
|
||||
|
||||
> 
|
||||
>
|
||||
> 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 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`.
|
||||
|
||||
> 
|
||||
>
|
||||
> Add/Remove/Manage Custom Settings.
|
||||
|
||||
Résultat dans OpenVibe :
|
||||
|
||||
> 
|
||||
>
|
||||
> 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
# Add all the subdirs as projects of the named branch
|
||||
# n.b. the folder is currently empty, don't bother adding
|
||||
#OV_ADD_PROJECTS("CONTRIB_APPLICATIONS_PLATFORM")
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# ---------------------------------
|
||||
# Finds GNEEDaccessAPI
|
||||
# Adds library to target
|
||||
# Adds include path
|
||||
# ---------------------------------
|
||||
|
||||
IF(WIN32)
|
||||
|
||||
IF("${PLATFORM_TARGET}" STREQUAL "x64")
|
||||
SET(PATHS_GNEEDaccessAPI "C:/Program Files/gtec/gNEEDaccess Client API/C")
|
||||
SET(PATHS_GNEEDaccessLIB "C:/Program Files/gtec/gNEEDaccess Client API/C/x64")
|
||||
SET(PATHS_GNEEDaccessServer "C:/Program Files/gtec/gNEEDaccess/")
|
||||
ELSE()
|
||||
SET(PATHS_GNEEDaccessAPI "C:/Program Files/gtec/gNEEDaccess Client API/C" "C:/Program Files (x86)/gtec/gNEEDaccess Client API/C")
|
||||
SET(PATHS_GNEEDaccessLIB "C:/Program Files/gtec/gNEEDaccess Client API/C/win32" "C:/Program Files (x86)/gtec/gNEEDaccess Client API/C/win32")
|
||||
SET(PATHS_GNEEDaccessServer "C:/Program Files/gtec/gNEEDaccess/" "C:/Program Files (x86)/gtec/gNEEDaccess/")
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(PATH_GNEEDaccessAPI GDSClientAPI.h PATHS
|
||||
${PATHS_GNEEDaccessAPI}
|
||||
NO_DEFAULT_PATH)
|
||||
IF(PATH_GNEEDaccessAPI)
|
||||
MESSAGE(STATUS " Found gtec gNEEDaccessAPI...")
|
||||
|
||||
# Find GDSClientAPI lib and dll
|
||||
FIND_PATH(PATH_ClientLIB GDSClientAPI.dll PATHS
|
||||
${PATHS_GNEEDaccessLIB}
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
FIND_LIBRARY(LIB_GDSClientAPI GDSClientAPI PATHS ${PATHS_GNEEDaccessLIB} NO_DEFAULT_PATH)
|
||||
IF(LIB_GDSClientAPI)
|
||||
MESSAGE(STATUS " [ OK ] lib ${LIB_GDSClientAPI}")
|
||||
ELSE(LIB_GDSClientAPI)
|
||||
MESSAGE(STATUS " [FAILED] lib GDSClientAPI")
|
||||
ENDIF(LIB_GDSClientAPI)
|
||||
|
||||
# Find GDSServer dll
|
||||
FIND_PATH(PATH_ServerDLL GDSServer.dll PATHS
|
||||
${PATHS_GNEEDaccessServer}
|
||||
NO_DEFAULT_PATH)
|
||||
IF(PATH_ServerDLL)
|
||||
MESSAGE(STATUS " [ OK ] dll ${PATH_ServerDLL}")
|
||||
ELSE(PATH_ServerDLL)
|
||||
MESSAGE(STATUS " [FAILED] dll GDSServer")
|
||||
ENDIF(PATH_ServerDLL)
|
||||
|
||||
# Find GDSServer lib
|
||||
FIND_LIBRARY(LIB_GDSServer GDSServer PATHS ${PATHS_GNEEDaccessLIB} NO_DEFAULT_PATH)
|
||||
IF(LIB_GDSServer)
|
||||
MESSAGE(STATUS " [ OK ] lib ${LIB_GDSServer}")
|
||||
ELSE(LIB_GDSServer)
|
||||
MESSAGE(STATUS " [FAILED] lib GDSServer")
|
||||
ENDIF(LIB_GDSServer)
|
||||
|
||||
# MESSAGE(STATUS "1, ${PATH_ClientLIB} 2, ${LIB_GDSClientAPI} 3, ${PATH_ServerDLL} 4, ${LIB_GDSServer}")
|
||||
|
||||
# Only add the compile/install directive if all necessary components were found
|
||||
IF(PATH_ClientLIB AND LIB_GDSClientAPI AND PATH_ServerDLL AND LIB_GDSServer)
|
||||
# Copy the DLL file at install
|
||||
INSTALL(PROGRAMS "${PATH_ClientLIB}/GDSClientAPI.dll" DESTINATION ${DIST_BINDIR})
|
||||
INSTALL(PROGRAMS "${PATH_ClientLIB}/gAPI.dll" DESTINATION ${DIST_BINDIR})
|
||||
INSTALL(PROGRAMS "${PATH_ClientLIB}/Networking.dll" DESTINATION ${DIST_BINDIR})
|
||||
INSTALL(PROGRAMS "${PATH_ServerDLL}/GDSServer.dll" DESTINATION ${DIST_BINDIR})
|
||||
|
||||
INCLUDE_DIRECTORIES(${PATH_GNEEDaccessAPI})
|
||||
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_GDSClientAPI} )
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_GDSServer} )
|
||||
|
||||
ADD_DEFINITIONS(-DTARGET_HAS_ThirdPartyGNEEDaccessAPI)
|
||||
SET(OV_ThirdPartyGNEEDaccess "YES")
|
||||
ENDIF(PATH_ClientLIB AND LIB_GDSClientAPI AND PATH_ServerDLL AND LIB_GDSServer)
|
||||
ELSE(PATH_GNEEDaccessAPI)
|
||||
MESSAGE(STATUS " FAILED to find gtec gNEEDaccessAPI (optional driver)")
|
||||
ENDIF(PATH_GNEEDaccessAPI)
|
||||
ENDIF(WIN32)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
GET_PROPERTY(OV_PRINTED GLOBAL PROPERTY OV_TRIED_ThirdPartyGtecUnicornCAPI)
|
||||
|
||||
|
||||
IF(WIN32)
|
||||
FIND_PATH(PATH_UNICORN Unicorn.dll PATHS ${LIST_DEPENDENCIES_PATH} PATH_SUFFIXES sdk_gtec_unicorn NO_DEFAULT_PATH)
|
||||
|
||||
IF(PATH_UNICORN)
|
||||
OV_PRINT(OV_PRINTED " Found Gtec Unicorn device API...")
|
||||
INCLUDE_DIRECTORIES(${PATH_UNICORN}/)
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${PATH_UNICORN}/unicorn.lib)
|
||||
|
||||
INSTALL(PROGRAMS "${PATH_UNICORN}/Unicorn.dll" DESTINATION ${DIST_BINDIR})
|
||||
ADD_DEFINITIONS(-DTARGET_HAS_ThirdPartyGtecUnicron)
|
||||
ELSE(PATH_UNICORN)
|
||||
OV_PRINT(OV_PRINTED " FAILED to find Gtec Unicorn device API (optional driver)")
|
||||
ENDIF(PATH_UNICORN)
|
||||
|
||||
ENDIF(WIN32)
|
||||
|
||||
IF (UNIX)
|
||||
OV_PRINT(OV_PRINTED " Gtec Unicorn device API (optional driver): No Linux support")
|
||||
ENDIF(UNIX)
|
||||
|
||||
SET_PROPERTY(GLOBAL PROPERTY OV_TRIED_ThirdPartyGtecUnicornCAPI "Yes")
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/contrib/common")
|
||||
|
||||
SET(ADDITIONAL_PATH "${CMAKE_SOURCE_DIR}/contrib/plugins/server-extensions/external-stimulations/")
|
||||
INCLUDE_DIRECTORIES(${ADDITIONAL_PATH})
|
||||
FILE(GLOB_RECURSE ADDITIONAL_SRC_FILES ${ADDITIONAL_PATH}/*.cpp ${ADDITIONAL_PATH}/*.h)
|
||||
SET(SRC_FILES "${SRC_FILES};${ADDITIONAL_SRC_FILES}")
|
||||
|
||||
SET(ADDITIONAL_PATH "${CMAKE_SOURCE_DIR}/contrib/plugins/server-extensions/tcp-tagging/")
|
||||
INCLUDE_DIRECTORIES(${ADDITIONAL_PATH})
|
||||
FILE(GLOB ADDITIONAL_SRC_FILES ${ADDITIONAL_PATH}/*.cpp ${ADDITIONAL_PATH}/*.h)
|
||||
SET(SRC_FILES "${SRC_FILES};${ADDITIONAL_SRC_FILES}")
|
||||
|
||||
FUNCTION(OV_ADD_CONTRIB_DRIVER DRIVER_PATH)
|
||||
|
||||
SET(ADDITIONAL_PATH ${DRIVER_PATH})
|
||||
INCLUDE_DIRECTORIES(${ADDITIONAL_PATH}/src)
|
||||
FILE(GLOB_RECURSE ADDITIONAL_SRC_FILES ${ADDITIONAL_PATH}/src/*.cpp ${ADDITIONAL_PATH}/src/*.h)
|
||||
SET(SRC_FILES "${SRC_FILES};${ADDITIONAL_SRC_FILES}" PARENT_SCOPE)
|
||||
|
||||
#MESSAGE(STATUS "DO I EXIST: ${ADDITIONAL_PATH}/share/")
|
||||
IF(EXISTS "${ADDITIONAL_PATH}/share/")
|
||||
#MESSAGE(STATUS "I EXIST: ${ADDITIONAL_PATH}/share/")
|
||||
INSTALL(DIRECTORY "${ADDITIONAL_PATH}/share/" DESTINATION "${DIST_DATADIR}/openvibe/applications/acquisition-server/")
|
||||
ENDIF(EXISTS "${ADDITIONAL_PATH}/share/")
|
||||
|
||||
#MESSAGE(STATUS "DO I EXIST: ${ADDITIONAL_PATH}/bin/")
|
||||
IF(EXISTS "${ADDITIONAL_PATH}/bin/")
|
||||
#MESSAGE(STATUS "I EXIST: ${ADDITIONAL_PATH}/bin/")
|
||||
INSTALL(DIRECTORY "${ADDITIONAL_PATH}/bin/" DESTINATION "${DIST_BINDIR}")
|
||||
ENDIF(EXISTS "${ADDITIONAL_PATH}/bin/")
|
||||
|
||||
# Add the dir to be parsed for documentation later.
|
||||
GET_PROPERTY(OV_TMP GLOBAL PROPERTY OV_PROP_CURRENT_PROJECTS)
|
||||
SET(OV_TMP "${OV_TMP};${ADDITIONAL_PATH}")
|
||||
SET_PROPERTY(GLOBAL PROPERTY OV_PROP_CURRENT_PROJECTS ${OV_TMP})
|
||||
|
||||
ENDFUNCTION(OV_ADD_CONTRIB_DRIVER)
|
||||
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/brainmaster-discovery")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/brainproducts-brainvisionrecorder")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/cognionics")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/ctfvsm-meg")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/encephalan")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/gtec-gipsa/common")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/gtec-gipsa/gusbamp")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/gtec-gipsa/unicorn")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/gtec-bcilab")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/gtec-gmobilabplus")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/gtec-gusbamp")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/gtec-gnautilus")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/mbt-smarting")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/mitsarEEG202A")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/openal-mono16bit-audiocapture")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/openeeg-modulareeg")
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/openbci")
|
||||
|
||||
IF(WIN32 AND "${PLATFORM_TARGET}" STREQUAL "x64")
|
||||
MESSAGE(STATUS " SKIPPED fieldtrip on x64")
|
||||
ELSE()
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/field-trip-protocol")
|
||||
ENDIF()
|
||||
|
||||
OV_ADD_CONTRIB_DRIVER("${CMAKE_SOURCE_DIR}/contrib/plugins/server-drivers/eemagine-eego")
|
||||
# The block is used to compile wrapper.cc into Acquisition Server which is not in OV git.
|
||||
# nb. we need to add the wrapper.cc file before the cmake add_executable() directive, and at that
|
||||
# point FindThirdPartyEemagineEEGO has not yet been run on some builds (e.g. win command line build),
|
||||
# nor can we do the adding at that point; it'd be too late. On the other hand, the find script
|
||||
# cannot be called before the executable has been added.
|
||||
if (WIN32)
|
||||
FIND_PATH(PATH_EEGOAPI amplifier.h PATHS ${LIST_DEPENDENCIES_PATH} PATH_SUFFIXES sdk_eemagine_eego/eemagine/sdk/)
|
||||
else()
|
||||
FIND_PATH(PATH_EEGOAPI amplifier.h PATHS /usr/include PATH_SUFFIXES eemagine/sdk/)
|
||||
endif(WIN32)
|
||||
IF(PATH_EEGOAPI)
|
||||
SET(SRC_FILES "${SRC_FILES};${PATH_EEGOAPI}/wrapper.cc")
|
||||
ENDIF(PATH_EEGOAPI)
|
||||
|
||||
IF(OV_COMPILE_TESTS)
|
||||
ADD_SUBDIRECTORY("../../../contrib/plugins/server-extensions/tcp-tagging/test" "./test")
|
||||
ENDIF(OV_COMPILE_TESTS)
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
/*
|
||||
#include "openeeg-modulareeg/src/ovasCDriverOpenEEGModularEEG.h"
|
||||
#include "field-trip-protocol/src/ovasCDriverFieldtrip.h"
|
||||
#include "brainproducts-brainvisionrecorder/src/ovasCDriverBrainProductsBrainVisionRecorder.h"
|
||||
*/
|
||||
#include "ovasCPluginExternalStimulations.h"
|
||||
#include "ovasCPluginTCPTagging.h"
|
||||
|
||||
#include "ovasCDriverBrainmasterDiscovery.h"
|
||||
#include "ovasCDriverBrainProductsBrainVisionRecorder.h"
|
||||
#include "ovasCDriverCognionics.h"
|
||||
#include "ovasCDriverCtfVsmMeg.h"
|
||||
#include "ovasCDriverEncephalan.h"
|
||||
#include "CDriverGTecGUSBamp.hpp"
|
||||
#include "ovasCDriverGTecGUSBampLegacy.h"
|
||||
#include "ovasCDriverGTecGUSBampLinux.h"
|
||||
#include "CDriverGTecUnicorn.hpp"
|
||||
#include "ovasCDriverGTecGMobiLabPlus.h"
|
||||
#include "ovasCDrivergNautilusInterface.h"
|
||||
#include "ovasCDriverMBTSmarting.h"
|
||||
#include "ovasCDriverMitsarEEG202A.h"
|
||||
#include "ovasCDriverOpenALAudioCapture.h"
|
||||
#include "ovasCDriverOpenEEGModularEEG.h"
|
||||
#include "ovasCDriverOpenBCI.h"
|
||||
#include "ovasCDriverEEGO.h"
|
||||
|
||||
#if !defined(WIN32) || defined(TARGET_PLATFORM_i386)
|
||||
#include "ovasCDriverFieldtrip.h"
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Contributions {
|
||||
inline void InitiateContributions(AcquisitionServer::CAcquisitionServerGUI* pGUI, AcquisitionServer::CAcquisitionServer* pAS,
|
||||
const Kernel::IKernelContext& context, std::vector<AcquisitionServer::IDriver*>* drivers)
|
||||
{
|
||||
//No Limitations
|
||||
drivers->push_back(new AcquisitionServer::CDriverBrainProductsBrainVisionRecorder(pAS->getDriverContext()));
|
||||
drivers->push_back(new AcquisitionServer::CDriverCtfVsmMeg(pAS->getDriverContext()));
|
||||
drivers->push_back(new AcquisitionServer::CDriverMBTSmarting(pAS->getDriverContext()));
|
||||
drivers->push_back(new AcquisitionServer::CDriverOpenEEGModularEEG(pAS->getDriverContext()));
|
||||
drivers->push_back(new AcquisitionServer::CDriverOpenBCI(pAS->getDriverContext()));
|
||||
|
||||
//OS Limitations
|
||||
#if defined WIN32
|
||||
drivers->push_back(new AcquisitionServer::CDriverCognionics(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_OS_Windows
|
||||
drivers->push_back(new AcquisitionServer::CDriverEncephalan(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_HAS_PThread
|
||||
#if !defined(WIN32) || defined(TARGET_PLATFORM_i386)
|
||||
drivers->push_back(new AcquisitionServer::CDriverFieldtrip(pAS->getDriverContext()));
|
||||
#endif
|
||||
#endif
|
||||
//Commercial Limitations
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
drivers->push_back(new AcquisitionServer::CDriverGTecGUSBamp(pAS->getDriverContext()));
|
||||
drivers->push_back(new AcquisitionServer::CDriverGTecGUSBampLegacy(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
drivers->push_back(new AcquisitionServer::CDriverGTecGUSBampLinux(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_HAS_ThirdPartyGtecUnicron
|
||||
drivers->push_back(new AcquisitionServer::CDriverGTecUnicorn(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
drivers->push_back(new AcquisitionServer::CDriverGTecGMobiLabPlus(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_HAS_ThirdPartyGNEEDaccessAPI
|
||||
drivers->push_back(new AcquisitionServer::CDrivergNautilusInterface(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
drivers->push_back(new AcquisitionServer::CDriverBrainmasterDiscovery(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined(TARGET_HAS_ThirdPartyMitsar)
|
||||
drivers->push_back(new AcquisitionServer::CDriverMitsarEEG202A(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined TARGET_HAS_ThirdPartyOpenAL
|
||||
drivers->push_back(new AcquisitionServer::CDriverOpenALAudioCapture(pAS->getDriverContext()));
|
||||
#endif
|
||||
#if defined(TARGET_HAS_ThirdPartyEEGOAPI)
|
||||
drivers->push_back(new AcquisitionServer::CDriverEEGO(pAS->getDriverContext()));
|
||||
#endif
|
||||
|
||||
pGUI->registerPlugin(new AcquisitionServer::Plugins::CPluginExternalStimulations(context));
|
||||
pGUI->registerPlugin(new AcquisitionServer::Plugins::CPluginTCPTagging(context));
|
||||
}
|
||||
} // namespace Contributions
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
INCLUDE("FindThirdPartyBrainmasterCodeMakerAPI")
|
||||
INCLUDE("FindThirdPartyEemagineEEGO")
|
||||
INCLUDE("FindThirdPartyGMobiLabPlusAPI")
|
||||
INCLUDE("FindThirdPartyGUSBampCAPI")
|
||||
INCLUDE("FindThirdPartyMitsar")
|
||||
|
||||
INCLUDE("FindThirdPartyGNEEDaccessAPI")
|
||||
INCLUDE("FindThirdPartyGtecUnicornCAPI")
|
||||
@@ -0,0 +1,2 @@
|
||||
# These files can be included by non-contrib projects.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
Copyright (c) 2000-2013 Chih-Chung Chang and Chih-Jen Lin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither name of copyright holders nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#define LIBSVM_VERSION 324
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern int libsvm_version;
|
||||
|
||||
struct svm_node
|
||||
{
|
||||
int index;
|
||||
double value;
|
||||
};
|
||||
|
||||
struct svm_problem
|
||||
{
|
||||
int l;
|
||||
double* y;
|
||||
struct svm_node** x;
|
||||
};
|
||||
|
||||
enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR }; // svm_type
|
||||
enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; // kernel_type
|
||||
|
||||
/* Table help you to transforms Enums to strings */
|
||||
//extern static const char *svm_type_table[5];
|
||||
//extern static const char *kernel_type_table[5];
|
||||
inline const char* get_svm_type(const unsigned int code)
|
||||
{
|
||||
static const char* svm_type_table[] = { "c_svc", "nu_svc", "one_class", "epsilon_svr", "nu_svr", nullptr };
|
||||
return svm_type_table[code];
|
||||
}
|
||||
|
||||
inline const char* get_kernel_type(const unsigned int code)
|
||||
{
|
||||
static const char* types[] = { "linear", "polynomial", "rbf", "sigmoid", "precomputed", nullptr };
|
||||
return types[code];
|
||||
}
|
||||
|
||||
struct svm_parameter
|
||||
{
|
||||
int svm_type;
|
||||
int kernel_type;
|
||||
int degree; // for poly
|
||||
double gamma; // for poly/rbf/sigmoid
|
||||
double coef0; // for poly/sigmoid
|
||||
|
||||
// these are for training only
|
||||
double cache_size; // in MB
|
||||
double eps; // stopping criteria
|
||||
double C; // for C_SVC, EPSILON_SVR and NU_SVR
|
||||
int nr_weight; // for C_SVC
|
||||
int* weight_label; // for C_SVC
|
||||
double* weight; // for C_SVC
|
||||
double nu; // for NU_SVC, ONE_CLASS, and NU_SVR
|
||||
double p; // for EPSILON_SVR
|
||||
int shrinking; // use the shrinking heuristics
|
||||
int probability; // do probability estimates
|
||||
};
|
||||
|
||||
//
|
||||
// svm_model
|
||||
//
|
||||
struct svm_model
|
||||
{
|
||||
struct svm_parameter param; // parameter
|
||||
int nr_class; // number of classes, = 2 in regression/one class svm
|
||||
int l; // total #SV
|
||||
struct svm_node** SV; // SVs (SV[l])
|
||||
double** sv_coef; // coefficients for SVs in decision functions (sv_coef[k-1][l])
|
||||
double* rho; // constants in decision functions (rho[k*(k-1)/2])
|
||||
double* probA; // pariwise probability information
|
||||
double* probB;
|
||||
int* sv_indices; // sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set
|
||||
|
||||
// for classification only
|
||||
|
||||
int* label; // label of each class (label[k])
|
||||
int* nSV; // number of SVs for each class (nSV[k])
|
||||
// nSV[0] + nSV[1] + ... + nSV[k-1] = l
|
||||
// XXX
|
||||
int free_sv; // 1 if svm_model is created by svm_load_model
|
||||
// 0 if svm_model is created by svm_train
|
||||
};
|
||||
|
||||
struct svm_model* svm_train(const struct svm_problem* prob, const struct svm_parameter* param);
|
||||
void svm_cross_validation(const struct svm_problem* prob, const struct svm_parameter* param, int nr_fold, double* target);
|
||||
|
||||
int svm_save_model(const char* model_file_name, const struct svm_model* model);
|
||||
struct svm_model* svm_load_model(const char* model_file_name);
|
||||
|
||||
int svm_get_svm_type(const struct svm_model* model);
|
||||
int svm_get_nr_class(const struct svm_model* model);
|
||||
void svm_get_labels(const struct svm_model* model, int* label);
|
||||
void svm_get_sv_indices(const struct svm_model* model, int* indices);
|
||||
int svm_get_nr_sv(const struct svm_model* model);
|
||||
double svm_get_svr_probability(const struct svm_model* model);
|
||||
|
||||
double svm_predict_values(const struct svm_model* model, const struct svm_node* x, double* dec_values);
|
||||
double svm_predict(const struct svm_model* model, const struct svm_node* x);
|
||||
double svm_predict_probability(const struct svm_model* model, const struct svm_node* x, double* prob_estimates);
|
||||
|
||||
void svm_free_model_content(struct svm_model* model_ptr);
|
||||
void svm_free_and_destroy_model(struct svm_model** model_ptr_ptr);
|
||||
void svm_destroy_param(struct svm_parameter* param);
|
||||
|
||||
const char* svm_check_parameter(const struct svm_problem* prob, const struct svm_parameter* param);
|
||||
int svm_check_probability_model(const struct svm_model* model);
|
||||
|
||||
void svm_set_print_string_function(void (*print_func)(const char*));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,18 @@
|
||||
/*Copyright (C) 2011 Rafat Hussain
|
||||
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*You should have received a copy of the GNU General Public License
|
||||
*along with this program; if not, write to the Free Software
|
||||
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
1. This package contains two wavelet libraries- libwavelet2d.so.1.0 (shared) and libwavelet2s.a (static) compiled essentially from the same source code. Source code is available in the 'src' folder.
|
||||
|
||||
2. You may need to link to header files that are included with their resepctive libraries. They are also available in the 'src' folder.
|
||||
|
||||
3. You may want to install shared library in one of your existing paths to make compilation easier. You can create sym links once inside the folder( say /usr/local/lib) by using following commands
|
||||
|
||||
ln -sf libwavelet2d.so.1.0 libwavelet2d.so
|
||||
ln -sf libwavelet2d.so.1.0 libwavelet2d.so.1
|
||||
ldconfig
|
||||
|
||||
You will probably need superuser privileges to perform previous steps. If you don't have su privilege then you can move libwavelet2d.so.1.0 to your work folder or where your source code is, create sym links as before and then put your folder in the path during runtime.
|
||||
|
||||
ln -sf libwavelet2d.so.1.0 libwavelet2d.so
|
||||
ln -sf libwavelet2d.so.1.0 libwavelet2d.so.1
|
||||
export LD_LIBRARY_PATH=.
|
||||
|
||||
libwavelet2s.a : Working with static library is pretty straightforward. You will only need to include wavelet2s.h in your program and specify the library (-lwavelet2s flag) , include path (-I<path to wavelet2s.h> flag) and library path (-L<path to libwavelet2s.a> flag).
|
||||
|
||||
|
||||
4. These libraries are licensed under GNU-GPL v2.0 (or any later version). See COPYRIGHT and COPYING files for more information.
|
||||
|
||||
5. These libraries statically link to FFTW-3.2.2 static library. More information, fftw libraries and associated files for this version are available at www.fftw.org. I have not modified fftw source codes in any way shape or form so you may want to download copies of source code and other files from the FFTW website itself if you are so inclined. However, you will find FFTW3 licensing and copyright information in the fftw3 folder.
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyFFTW3)
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
|
||||
// 1D Functions
|
||||
// void* dwt(std::vector<double> &, int ,std::string , std::vector<double> &, std::vector<double> &);
|
||||
void* dwt1(const std::string& wname, std::vector<double>& signal, std::vector<double>& cA, std::vector<double>& cD);
|
||||
void* dyadic_zpad_1d(std::vector<double>& signal);
|
||||
double convol(std::vector<double>& a1, std::vector<double>& b1, std::vector<double>& c);
|
||||
int filtcoef(const std::string& name, std::vector<double>& lp1, std::vector<double>& hp1, std::vector<double>& lp2, std::vector<double>& hp2);
|
||||
void downsamp(std::vector<double>& sig, int m, std::vector<double>& sigD);
|
||||
void upsamp(std::vector<double>& sig, int m, std::vector<double>& sigU);
|
||||
void circshift(std::vector<double>& sigCir, int l);
|
||||
int sign(int x);
|
||||
void* idwt1(const std::string& wname, std::vector<double>& X, std::vector<double>& cA, std::vector<double>& cD);
|
||||
int vecsum(std::vector<double>& a, std::vector<double>& b, std::vector<double>& c);
|
||||
|
||||
// 1D Symmetric Extension DWT Functions
|
||||
void* dwt_sym(std::vector<double>& signal, int J, const std::string& nm, std::vector<double>& dwtOutput, std::vector<double>& flag,
|
||||
std::vector<size_t>& length);
|
||||
void* dwt1_sym(const std::string& wname, std::vector<double>& signal, std::vector<double>& cA, std::vector<double>& cD);
|
||||
void* idwt_sym(std::vector<double>& dwtop, std::vector<double>& flag, const std::string& nm, std::vector<double>& idwtOutput, std::vector<size_t>& length);
|
||||
void* symm_ext(std::vector<double>& sig, int a);
|
||||
void* idwt1_sym(const std::string& wname, std::vector<double>& x, std::vector<double>& app, std::vector<double>& detail); // Not Tested
|
||||
|
||||
// 1D Stationary Wavelet Transform
|
||||
void* swt(std::vector<double>& signal1, int J, const std::string& nm, std::vector<double>& swtOutput, int& length);
|
||||
void* iswt(std::vector<double>& swtop, int J, const std::string& nm, std::vector<double>& iswtOutput);
|
||||
void* per_ext(std::vector<double>& sig, int a);
|
||||
|
||||
// 2D Functions
|
||||
void* branch_lp_dn(const std::string& wname, std::vector<double>& signal, std::vector<double>& sigop);
|
||||
void* branch_hp_dn(const std::string& wname, std::vector<double>& signal, std::vector<double>& sigop);
|
||||
void* branch_lp_hp_up(const std::string& wname, std::vector<double>& cA, std::vector<double>& cD, std::vector<double>& x);
|
||||
// void* dwt_2d(std::vector<std::vector<double> > &, int , std::string , std::vector<std::vector<double> > &, std::vector<double> &) ;
|
||||
// void* idwt_2d(std::vector<std::vector<double> > &,std::vector<double> &, std::string ,std::vector<std::vector<double> > &);
|
||||
void* dyadic_zpad_2d(std::vector<std::vector<double>>& signal, std::vector<std::vector<double>>& mod);
|
||||
void* dwt_output_dim(std::vector<std::vector<double>>& signal, int& r, int& c);
|
||||
void* zero_remove(std::vector<std::vector<double>>& input, std::vector<std::vector<double>>& output);
|
||||
void* getcoeff2d(std::vector<std::vector<double>>& dwtoutput, std::vector<std::vector<double>>& cH, std::vector<std::vector<double>>& cV,
|
||||
std::vector<std::vector<double>>& cD, std::vector<double>& flag, int& n);
|
||||
void* idwt2(const std::string& name, std::vector<std::vector<double>>& signal, std::vector<std::vector<double>>& cLL, std::vector<std::vector<double>>& cLH,
|
||||
std::vector<std::vector<double>>& cHL, std::vector<std::vector<double>>& cHH);
|
||||
void* dwt2(const std::string& name, std::vector<std::vector<double>>& signal, std::vector<std::vector<double>>& cLL, std::vector<std::vector<double>>& cLH,
|
||||
std::vector<std::vector<double>>& cHL, std::vector<std::vector<double>>& cHH);
|
||||
void* downsamp2(std::vector<std::vector<double>>& vec1, std::vector<std::vector<double>>& vec2, int rowsDn, int colsDn);
|
||||
void* upsamp2(std::vector<std::vector<double>>& vec1, std::vector<std::vector<double>>& vec2, int rowsUp, int colsUp);
|
||||
|
||||
// 2D DWT (Symmetric Extension) Functions
|
||||
void* dwt_2d_sym(std::vector<std::vector<double>>& origsig, int J, const std::string& nm, std::vector<double>& dwtOutput, std::vector<double>& flag,
|
||||
std::vector<size_t>& length);
|
||||
void* dwt2_sym(const std::string& name, std::vector<std::vector<double>>& signal, std::vector<std::vector<double>>& cLL, std::vector<std::vector<double>>& cLH,
|
||||
std::vector<std::vector<double>>& cHL, std::vector<std::vector<double>>& cHH);
|
||||
void* idwt_2d_sym(std::vector<double>& dwtop, std::vector<double>& flag, const std::string& nm, std::vector<std::vector<double>>& idwtOutput,
|
||||
std::vector<size_t>& length);
|
||||
void* circshift2d(std::vector<std::vector<double>>& signal, int x, int y);
|
||||
void symm_ext2d(std::vector<std::vector<double>>& signal, std::vector<std::vector<double>>& temp2, int a);
|
||||
void* dispDWT(std::vector<double>& output, std::vector<std::vector<double>>& dwtdisp, std::vector<size_t>& length, std::vector<size_t>& length2, int J);
|
||||
|
||||
//2D Stationary Wavelet Transform
|
||||
void* swt_2d(std::vector<std::vector<double>>& sig, int J, const std::string& nm, std::vector<double>& swtOutput);
|
||||
void* per_ext2d(std::vector<std::vector<double>>& signal, std::vector<std::vector<double>>& temp2, int a);
|
||||
|
||||
// FFT functions
|
||||
double convfft(std::vector<double>& a, std::vector<double>& b, std::vector<double>& c);
|
||||
double convfftm(std::vector<double>& a, std::vector<double>& b, std::vector<double>& c);
|
||||
void* fft(std::vector<std::complex<double>>& data, int sign, size_t n);
|
||||
void* bitreverse(std::vector<std::complex<double>>& sig);
|
||||
void* freq(std::vector<double>& sig, std::vector<double>& freqResp);
|
||||
|
||||
//New
|
||||
void* dwt1_sym_m(const std::string& wname, std::vector<double>& signal, std::vector<double>& cA, std::vector<double>& cD);//FFTW3 for 2D
|
||||
void* idwt1_sym_m(const std::string& wname, std::vector<double>& x, std::vector<double>& app, std::vector<double>& detail);
|
||||
void* dwt(std::vector<double>& sig, int j, const std::string& nm, std::vector<double>& dwtOutput, std::vector<double>& flag, std::vector<size_t>& length);
|
||||
void* idwt(std::vector<double>& dwtop, std::vector<double>& flag, const std::string& nm, std::vector<double>& idwtOutput, std::vector<size_t>& length);
|
||||
void* dwt_2d(std::vector<std::vector<double>>& origsig, int J, const std::string& nm, std::vector<double>& dwtOutput, std::vector<double>& flag,
|
||||
std::vector<size_t>& length);
|
||||
void* dwt1_m(const std::string& wname, std::vector<double>& signal, std::vector<double>& cA, std::vector<double>& cD);
|
||||
void* idwt_2d(std::vector<double>& dwtop, std::vector<double>& flag, const std::string& nm, std::vector<std::vector<double>>& idwtOutput,
|
||||
std::vector<size_t>& length);
|
||||
void* idwt1_m(const std::string& wname, std::vector<double>& x, std::vector<double>& cA, std::vector<double>& cD);
|
||||
void* dwt_output_dim2(std::vector<size_t>& length, std::vector<size_t>& length2, int j);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
# Add all the subdirs as projects of the named branch
|
||||
OV_ADD_PROJECTS("CONTRIB_PLUGINS")
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
# Add all the subdirs as projects of the named branch
|
||||
OV_ADD_PROJECTS("CONTRIB_PLUGINS_PROCESSING")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# fill this once there is a contribution to this category...
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
// Global defines //
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
#include <vector>
|
||||
#include <openvibe/ov_all.h>
|
||||
#include "ovp_defines.h"
|
||||
|
||||
OVP_Declare_Begin();
|
||||
|
||||
// For the moment this plugin is empty
|
||||
|
||||
OVP_Declare_End()
|
||||
@@ -0,0 +1,27 @@
|
||||
PROJECT(openvibe-plugins-contrib-file-io)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl src/*.c)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared -D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE")
|
||||
|
||||
# ---------------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
INCLUDE("FindThirdPartyBoost")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_EDFFileWriter EDF File Writer
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Description|
|
||||
|
||||
* Writes EEG data in European Data Format (EDF).
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Input1|
|
||||
* Experiment Information stream, coming from an acquisition client/server.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Input1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Input2|
|
||||
* EEG signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Input2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Input3|
|
||||
* Stimulation stream.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Input3|
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Setting1|
|
||||
* The file name to be written.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Setting1|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Examples|
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Miscellaneous|
|
||||
EDF format can be read by several programs, (e.g EDF browser).
|
||||
This box is based on the free software EDFlib by Teunis van Beelen.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Miscellaneous|
|
||||
*/
|
||||
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2009, 2010, 2011 Teunis van Beelen
|
||||
* All rights reserved.
|
||||
*
|
||||
* email: teuniz@gmail.com
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Teunis van Beelen ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Teunis van Beelen BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
/* compile with options "-D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE" */
|
||||
|
||||
|
||||
#ifndef EDFLIB_INCLUDED
|
||||
#define EDFLIB_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define EDFLIB_TIME_DIMENSION (10000000LL)
|
||||
#define EDFLIB_MAXSIGNALS 256
|
||||
#define EDFLIB_MAX_ANNOTATION_LEN 512
|
||||
|
||||
#define EDFSEEK_SET 0
|
||||
#define EDFSEEK_CUR 1
|
||||
#define EDFSEEK_END 2
|
||||
|
||||
|
||||
/* the following defines are used in the member "filetype" of the edf_hdr_struct */
|
||||
/* and as return value for the function edfopen_file_readonly() */
|
||||
#define EDFLIB_FILETYPE_EDF 0
|
||||
#define EDFLIB_FILETYPE_EDFPLUS 1
|
||||
#define EDFLIB_FILETYPE_BDF 2
|
||||
#define EDFLIB_FILETYPE_BDFPLUS 3
|
||||
#define EDFLIB_MALLOC_ERROR -1
|
||||
#define EDFLIB_NO_SUCH_FILE_OR_DIRECTORY -2
|
||||
#define EDFLIB_FILE_CONTAINS_FORMAT_ERRORS -3
|
||||
#define EDFLIB_MAXFILES_REACHED -4
|
||||
#define EDFLIB_FILE_READ_ERROR -5
|
||||
#define EDFLIB_FILE_ALREADY_OPENED -6
|
||||
#define EDFLIB_FILETYPE_ERROR -7
|
||||
#define EDFLIB_FILE_WRITE_ERROR -8
|
||||
#define EDFLIB_NUMBER_OF_SIGNALS_INVALID -9
|
||||
#define EDFLIB_FILE_IS_DISCONTINUOUS -10
|
||||
#define EDFLIB_INVALID_READ_ANNOTS_VALUE -11
|
||||
|
||||
/* values for annotations */
|
||||
#define EDFLIB_DO_NOT_READ_ANNOTATIONS 0
|
||||
#define EDFLIB_READ_ANNOTATIONS 1
|
||||
#define EDFLIB_READ_ALL_ANNOTATIONS 2
|
||||
|
||||
/* the following defines are possible errors returned by edfopen_file_writeonly() */
|
||||
#define EDFLIB_NO_SIGNALS -20
|
||||
#define EDFLIB_TOO_MANY_SIGNALS -21
|
||||
#define EDFLIB_NO_SAMPLES_IN_RECORD -22
|
||||
#define EDFLIB_DIGMIN_IS_DIGMAX -23
|
||||
#define EDFLIB_DIGMAX_LOWER_THAN_DIGMIN -24
|
||||
#define EDFLIB_PHYSMIN_IS_PHYSMAX -25
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
struct edf_param_struct // this structure contains all the relevant EDF-signal parameters of one signal
|
||||
{
|
||||
char label[17]; // label (name) of the signal, null-terminated string
|
||||
long long smp_in_file; // number of samples of this signal in the file
|
||||
double phys_max; // physical maximum
|
||||
double phys_min; // physical minimum
|
||||
int dig_max; // digital maximum
|
||||
int dig_min; // digital minimum
|
||||
int smp_in_datarecord; // number of samples of this signal in a datarecord
|
||||
char physdimension[9]; // physical dimension (uV, bpm, mA, etc.), null-terminated string
|
||||
char prefilter[81]; // null-terminated string
|
||||
char transducer[81]; // null-terminated string
|
||||
};
|
||||
|
||||
|
||||
struct edf_annotation_struct // this structure is used for annotations
|
||||
{
|
||||
long long onset; // onset time of the event, expressed in units of 100 nanoSeconds
|
||||
char duration[16]; // duration time, this is a null-terminated ASCII text-string
|
||||
char annotation[EDFLIB_MAX_ANNOTATION_LEN + 1]; // description of the event in UTF-8, this is a null terminated string
|
||||
};
|
||||
|
||||
|
||||
struct edf_hdr_struct // this structure contains all the relevant EDF header info and will be filled when calling the function edf_open_file_readonly()
|
||||
{
|
||||
int handle; // a handle (identifier) used to distinguish the different files
|
||||
int filetype; // 0: EDF, 1: EDFplus, 2: BDF, 3: BDFplus, a negative number means an error
|
||||
int edfsignals; // number of EDF signals in the file, annotation channels are NOT included
|
||||
long long file_duration; // duration of the file expressed in units of 100 nanoSeconds
|
||||
int startdate_day;
|
||||
int startdate_month;
|
||||
int startdate_year;
|
||||
long long
|
||||
starttime_subsecond; // starttime offset expressed in units of 100 nanoSeconds. Is always less than 10000000 (one second). Only used by EDFplus and BDFplus
|
||||
int starttime_second;
|
||||
int starttime_minute;
|
||||
int starttime_hour;
|
||||
char patient[81]; // null-terminated string, contains patientfield of header, is always empty when filetype is EDFPLUS or BDFPLUS
|
||||
char recording[81
|
||||
]; // null-terminated string, contains recordingfield of header, is always empty when filetype is EDFPLUS or BDFPLUS
|
||||
char patientcode[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char gender[16]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char birthdate[16]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char patient_name[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char patient_additional[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char admincode[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char technician[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char equipment[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char recording_additional[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
long long datarecord_duration; // duration of a datarecord expressed in units of 100 nanoSeconds
|
||||
long long datarecords_in_file; // number of datarecords in the file
|
||||
long long annotations_in_file; // number of annotations in the file
|
||||
struct edf_param_struct signalparam[EDFLIB_MAXSIGNALS]; // array of structs which contain the relevant signal parameters
|
||||
};
|
||||
|
||||
|
||||
int edflib_version();
|
||||
|
||||
/* Returns the version number of this library, multiplied by hundred. if version is "1.00" than it will return 100 */
|
||||
|
||||
|
||||
/***************** the following functions are used to read files **************************/
|
||||
|
||||
|
||||
int EdfopenFileReadonly(const char* path, struct edf_hdr_struct* edfhdr, const int readAnnotations);
|
||||
|
||||
/* opens an existing file for reading */
|
||||
/* path is a null-terminated string containing the path to the file */
|
||||
/* hdr is a pointer to an edf_hdr_struct, all fields in this struct will be overwritten */
|
||||
/* the edf_hdr_struct will be filled with all the relevant header- and signalinfo/parameters */
|
||||
|
||||
/* read_annotations must have one of the following values: */
|
||||
/* EDFLIB_DO_NOT_READ_ANNOTATIONS annotations will not be read (this saves time when opening a very large EDFplus or BDFplus file */
|
||||
/* EDFLIB_READ_ANNOTATIONS annotations will be read immediately, stops when an annotation has */
|
||||
/* been found which contains the description "Recording ends" */
|
||||
/* EDFLIB_READ_ALL_ANNOTATIONS all annotations will be read immediately */
|
||||
|
||||
/* returns 0 on success, in case of an error it returns -1 and an errorcode will be set in the member "filetype" of struct edf_hdr_struct */
|
||||
/* This function is required if you want to read a file */
|
||||
|
||||
|
||||
int EdfcloseFile(const int handle);
|
||||
|
||||
/* closes and finalizes the file */
|
||||
/* returns -1 in case of an error, 0 on success */
|
||||
/* this function MUST be called when you are finished reading or writing */
|
||||
/* This function is required after reading or writing. Failing to do so will cause */
|
||||
/* unnessecary memory usage and in case of writing it will cause a corrupted and incomplete file */
|
||||
|
||||
|
||||
int EdfreadPhysicalSamples(const int handle, const int edfsignal, int n, double* buf);
|
||||
|
||||
/* reads n samples from edfsignal, starting from the current sample position indicator, into buf (edfsignal starts at 0) */
|
||||
/* the values are converted to their physical values e.g. microVolts, beats per minute, etc. */
|
||||
/* bufsize should be equal to or bigger than sizeof(double[n]) */
|
||||
/* the sample position indicator will be increased with the amount of samples read */
|
||||
/* returns the amount of samples read (this can be less than n or zero!) */
|
||||
/* or -1 in case of an error */
|
||||
|
||||
|
||||
int EdfreadDigitalSamples(const int handle, const int edfsignal, int n, int* buf);
|
||||
|
||||
/* reads n samples from edfsignal, starting from the current sample position indicator, into buf (edfsignal starts at 0) */
|
||||
/* the values are the "raw" digital values */
|
||||
/* bufsize should be equal to or bigger than sizeof(int[n]) */
|
||||
/* the sample position indicator will be increased with the amount of samples read */
|
||||
/* returns the amount of samples read (this can be less than n or zero!) */
|
||||
/* or -1 in case of an error */
|
||||
|
||||
|
||||
long long edfseek(const int handle, const int edfsignal, const long long offset, const int whence);
|
||||
|
||||
/* The edfseek() function sets the sample position indicator for the edfsignal pointed to by edfsignal. */
|
||||
/* The new position, measured in samples, is obtained by adding offset samples to the position specified by whence. */
|
||||
/* If whence is set to EDFSEEK_SET, EDFSEEK_CUR, or EDFSEEK_END, the offset is relative to the start of the file, */
|
||||
/* the current position indicator, or end-of-file, respectively. */
|
||||
/* Returns the current offset. Otherwise, -1 is returned. */
|
||||
/* note that every signal has it's own independent sample position indicator and edfseek() affects only one of them */
|
||||
|
||||
|
||||
long long edftell(const int handle, const int edfsignal);
|
||||
|
||||
/* The edftell() function obtains the current value of the sample position indicator for the edfsignal pointed to by edfsignal. */
|
||||
/* Returns the current offset. Otherwise, -1 is returned */
|
||||
/* note that every signal has it's own independent sample position indicator and edftell() affects only one of them */
|
||||
|
||||
|
||||
void edfrewind(const int handle, const int edfsignal);
|
||||
|
||||
/* The edfrewind() function sets the sample position indicator for the edfsignal pointed to by edfsignal to the beginning of the file. */
|
||||
/* It is equivalent to: () edfseek(int handle, int edfsignal, 0LL, EDFSEEK_SET) */
|
||||
/* note that every signal has it's own independent sample position indicator and edfrewind() affects only one of them */
|
||||
|
||||
|
||||
int EdfGetAnnotation(const int handle, const int n, struct edf_annotation_struct* annot);
|
||||
|
||||
/* Fills the edf_annotation_struct with the annotation n, returns 0 on success, otherwise -1 */
|
||||
/* To obtain the number of annotations in a file, check edf_hdr_struct -> annotations_in_file */
|
||||
|
||||
/*
|
||||
Notes:
|
||||
|
||||
Annotationsignals
|
||||
|
||||
EDFplus and BDFplus store the annotations in one or more signals (in order to be backwards compatibel with EDF and BDF).
|
||||
The counting of the signals in the file starts at 0. Signals used for annotations are skipped by EDFlib.
|
||||
This means that the annotationsignal(s) in the file are hided.
|
||||
Use the function edf_get_annotation() to get the annotations.
|
||||
|
||||
So, when a file contains 5 signals and the third signal is used to store the annotations, the library will
|
||||
report that there are only 4 signals in the file.
|
||||
The library will "map" the signalnumbers as follows: 0->0, 1->1, 2->3, 3->4.
|
||||
This way you don't need to worry about which signals are annotationsignals. The library will do it for you.
|
||||
|
||||
How the library stores time-values
|
||||
|
||||
To avoid rounding errors, the library stores some timevalues in variables of type long long int.
|
||||
In order not to loose the subsecond precision, all timevalues have been multiplied by 10000000.
|
||||
This will limit the timeresolution to 100 nanoSeconds. To calculate the amount of seconds, divide
|
||||
the timevalue by 10000000 or use the macro EDFLIB_TIME_DIMENSION which is declared in edflib.h.
|
||||
The following variables do use this when you open a file in read mode: "file_duration", "starttime_subsecond" and "onset".
|
||||
*/
|
||||
|
||||
|
||||
/***************** the following functions are used to write files **************************/
|
||||
|
||||
|
||||
int EdfopenFileWriteonly(const char* path, const int filetype, const int nSignals);
|
||||
|
||||
/* opens an new file for writing. warning, an already existing file with the same name will be silently overwritten without advance warning!! */
|
||||
/* path is a null-terminated string containing the path and name of the file */
|
||||
/* filetype must be EDFLIB_FILETYPE_EDFPLUS or EDFLIB_FILETYPE_BDFPLUS */
|
||||
/* returns a handle on success, you need this handle for the other functions */
|
||||
/* in case of an error it returns a negative number corresponding to one of the following values: */
|
||||
/* EDFLIB_MALLOC_ERROR */
|
||||
/* EDFLIB_NO_SUCH_FILE_OR_DIRECTORY */
|
||||
/* EDFLIB_MAXFILES_REACHED */
|
||||
/* EDFLIB_FILE_ALREADY_OPENED */
|
||||
/* EDFLIB_NUMBER_OF_SIGNALS_INVALID */
|
||||
/* This function is required if you want to write a file */
|
||||
|
||||
|
||||
int EdfSetSampling(const int handle, const int edfsignal, const int sampling);
|
||||
|
||||
/* Sets the sampling of signal edfsignal. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a */
|
||||
/* file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPhysicalMaximum(const int handle, const int edfsignal, const double max);
|
||||
|
||||
/* Sets the maximum physical value of signal edfsignal. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a */
|
||||
/* file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPhysicalMinimum(const int handle, const int edfsignal, const double min);
|
||||
|
||||
/* Sets the minimum physical value of signal edfsignal. */
|
||||
/* Usually this will be (-(phys_max)) */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a */
|
||||
/* file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetDigitalMaximum(const int handle, const int edfsignal, const int max);
|
||||
|
||||
/* Sets the maximum digital value of signal edfsignal. Usually, the value 32767 is used for EDF+ and 8388607 for BDF+ */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetDigitalMinimum(const int handle, const int edfsignal, const int min);
|
||||
|
||||
/* Sets the minimum digital value of signal edfsignal. Usually, the value -32768 is used for EDF+ and -8388608 for BDF+ */
|
||||
/* Usually this will be (-(dig_max + 1)) */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetLabel(const int handle, const int edfsignal, const char* label);
|
||||
|
||||
/* Sets the label (name) of signal edfsignal. ("FP1", "SaO2", etc.) */
|
||||
/* label is a pointer to a NULL-terminated ASCII-string containing the label (name) of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is recommended for every signal when you want to write a file */
|
||||
/* and can be called only after opening a file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPrefilter(const int handle, const int edfsignal, const char* prefilter);
|
||||
|
||||
/* Sets the prefilter of signal edfsignal ("HP:0.1Hz", "LP:75Hz N:50Hz", etc.). */
|
||||
/* prefilter is a pointer to a NULL-terminated ASCII-string containing the prefilter text of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode and before */
|
||||
/* the first sample write action */
|
||||
|
||||
|
||||
int EdfSetTransducer(const int handle, const int edfsignal, const char* transducer);
|
||||
|
||||
/* Sets the transducer of signal edfsignal ("AgAgCl cup electrodes", etc.). */
|
||||
/* transducer is a pointer to a NULL-terminated ASCII-string containing the transducer text of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode and before */
|
||||
/* the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPhysicalDimension(const int handle, const int edfsignal, const char* dim);
|
||||
|
||||
/* Sets the physical dimension of signal edfsignal. ("uV", "BPM", "mA", "Degr.", etc.) */
|
||||
/* phys_dim is a pointer to a NULL-terminated ASCII-string containing the physical dimension of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is recommended for every signal when you want to write a file */
|
||||
/* and can be called only after opening a file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetStartdatetime(const int handle, const int year, const int month, const int day, const int hour, const int minute, const int second);
|
||||
|
||||
/* Sets the startdate and starttime. */
|
||||
/* year: 1970 - 3000, month: 1 - 12, day: 1 - 31 */
|
||||
/* hour: 0 - 23, minute: 0 - 59, second: 0 - 59 */
|
||||
/* If not called, the library will use the system date and time at runtime */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPatientname(const int handle, const char* patientname);
|
||||
|
||||
/* Sets the patientname. patientname is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPatientcode(const int handle, const char* patientcode);
|
||||
|
||||
/* Sets the patientcode. patientcode is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetGender(const int handle, const int gender);
|
||||
|
||||
/* Sets the gender. 1 is male, 0 is female. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetBirthdate(const int handle, const int year, const int month, const int day);
|
||||
|
||||
/* Sets the birthdate. */
|
||||
/* year: 1800 - 3000, month: 1 - 12, day: 1 - 31 */
|
||||
/* This function is optional */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPatientAdditional(const int handle, const char* additional);
|
||||
|
||||
/* Sets the additional patientinfo. patient_additional is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetAdmincode(const int handle, const char* admincode);
|
||||
|
||||
/* Sets the admincode. admincode is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetTechnician(const int handle, const char* technician);
|
||||
|
||||
/* Sets the technicians name. technician is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetEquipment(const int handle, const char* equipment);
|
||||
|
||||
/* Sets the name of the equipment used during the aquisition. equipment is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetRecordingAdditional(const int handle, const char* additional);
|
||||
|
||||
/* Sets the additional recordinginfo. recording_additional is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfwritePhysicalSamples(const int handle, const double* buf);
|
||||
|
||||
/* Writes n physical samples (uV, mA, Ohm) from *buf belonging to one signal */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* The physical samples will be converted to digital samples using the */
|
||||
/* values of physical maximum, physical minimum, digital maximum and digital minimum */
|
||||
/* The number of samples written is equal to the sampling of the signal */
|
||||
/* Size of buf should be equal to or bigger than sizeof(double[sampling]) */
|
||||
/* Call this function for every signal in the file. The order is important! */
|
||||
/* When there are 4 signals in the file, the order of calling this function */
|
||||
/* must be: signal 0, signal 1, signal 2, signal 3, signal 0, signal 1, signal 2, etc. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfBlockwritePhysicalSamples(const int handle, const double* buf);
|
||||
|
||||
/* Writes physical samples (uV, mA, Ohm) from *buf */
|
||||
/* buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc. */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* buf must be filled with samples from all signals, starting with signal 0, 1, 2, etc. */
|
||||
/* one block equals one second */
|
||||
/* The physical samples will be converted to digital samples using the */
|
||||
/* values of physical maximum, physical minimum, digital maximum and digital minimum */
|
||||
/* The number of samples written is equal to the sum of the samplefrequencies of all signals */
|
||||
/* Size of buf should be equal to or bigger than sizeof(double) multiplied by the sum of the samplefrequencies of all signals */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfwriteDigitalSamples(const int handle, const int* buf);
|
||||
|
||||
/* Writes n "raw" digital samples from *buf belonging to one signal */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* The 16 (or 24 in case of BDF) least significant bits of the sample will be written to the */
|
||||
/* file without any conversion. */
|
||||
/* The number of samples written is equal to the sampling of the signal */
|
||||
/* Size of buf should be equal to or bigger than sizeof(int[sampling]) */
|
||||
/* Call this function for every signal in the file. The order is important! */
|
||||
/* When there are 4 signals in the file, the order of calling this function */
|
||||
/* must be: signal 0, signal 1, signal 2, signal 3, signal 0, signal 1, signal 2, etc. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfBlockwriteDigitalSamples(const int handle, const int* buf);
|
||||
|
||||
/* Writes "raw" digital samples from *buf. */
|
||||
/* buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc. */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* One block equals one second. */
|
||||
/* The 16 (or 24 in case of BDF) least significant bits of the sample will be written to the */
|
||||
/* file without any conversion. */
|
||||
/* The number of samples written is equal to the sum of the samplefrequencies of all signals. */
|
||||
/* Size of buf should be equal to or bigger than sizeof(int) multiplied by the sum of the samplefrequencies of all signals */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfwriteAnnotationUTF8(const int handle, const long long onset, const long long duration, const char* description);
|
||||
|
||||
/* writes an annotation/event to the file */
|
||||
/* onset is relative to the starttime and startdate of the file */
|
||||
/* onset and duration are in units of 100 microSeconds! resolution is 0.0001 second! */
|
||||
/* for example: 34.071 seconds must be written as 340710 */
|
||||
/* if duration is unknown or not applicable: set a negative number (-1) */
|
||||
/* description is a null-terminated UTF8-string containing the text that describes the event */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before closing the file */
|
||||
|
||||
|
||||
int EdfwriteAnnotationLatin1(const int handle, const long long onset, const long long duration, const char* description);
|
||||
|
||||
/* writes an annotation/event to the file */
|
||||
/* onset is relative to the starttime and startdate of the file */
|
||||
/* onset and duration are in units of 100 microSeconds! resolution is 0.0001 second! */
|
||||
/* for example: 34.071 seconds must be written as 340710 */
|
||||
/* if duration is unknown or not applicable: set a negative number (-1) */
|
||||
/* description is a null-terminated Latin1-string containing the text that describes the event */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before closing the file */
|
||||
|
||||
|
||||
int EdfSetDatarecordDuration(const int handle, const int duration);
|
||||
|
||||
/* Sets the datarecord duration. The default value is 1 second. */
|
||||
/* ATTENTION: the argument "duration" is expressed in units of 10 microSeconds! */
|
||||
/* So, if you want to set the datarecord duration to 0.1 second, you must give */
|
||||
/* the argument "duration" a value of "10000". */
|
||||
/* This function is optional, normally you don't need to change the default value. */
|
||||
/* The datarecord duration must be in the range 0.025 to 20.0 seconds. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is NOT REQUIRED but can be called after opening a */
|
||||
/* file in writemode and before the first sample write action. */
|
||||
/* This function can be used when you want to use a samplerate */
|
||||
/* which is not an integer. For example, if you want to use a samplerate of 0.5 Hz, */
|
||||
/* set the sampling to 5 Hz and the datarecord duration to 10 seconds, */
|
||||
/* or set the sampling to 1 Hz and the datarecord duration to 2 seconds. */
|
||||
/* Do not use this function, except when absolutely necessary! */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,315 @@
|
||||
/* Project: Gipsa-lab plugins for OpenVibe
|
||||
* AUTHORS AND CONTRIBUTORS: Andreev A., Barachant A., Congedo M., Ionescu,Gelu,
|
||||
|
||||
* This file is part of "Gipsa-lab plugins for OpenVibe".
|
||||
* You can redistribute it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
* This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License along with Brain Invaders. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include "ovpCBoxAlgorithmBrainampFileWriterGipsa.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
|
||||
|
||||
//documentation Appendix B EEG file format: http://tsgdoc.socsci.ru.nl/images/d/d1/BrainVision_Recorder_UM.pdf
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::initialize()
|
||||
{
|
||||
m_isVmrkHeaderFileWritten = false;
|
||||
|
||||
//init input signal 1
|
||||
m_streamDecoder = new Toolkit::TSignalDecoder<CBoxAlgorithmBrainampFileWriterGipsa>(*this, 0);
|
||||
|
||||
//init input stimulation 1
|
||||
m_stimDecoder = new Toolkit::TStimulationDecoder<CBoxAlgorithmBrainampFileWriterGipsa>(*this, 1);
|
||||
|
||||
//Get parameters:
|
||||
const CString filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
const CString tmp = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
const CIdentifier binaryFormatID = this->getTypeManager().getEnumerationEntryValueFromName(OVP_TypeId_BinaryFormat, tmp);
|
||||
|
||||
if (binaryFormatID == OVP_TypeId_BinaryFormat_int16_t) { m_binaryFormat = BinaryFormat_Integer16; }
|
||||
else if (binaryFormatID == OVP_TypeId_BinaryFormat_uint16_t) { m_binaryFormat = BinaryFormat_UnsignedInteger16; }
|
||||
else if (binaryFormatID == OVP_TypeId_BinaryFormat_float) { m_binaryFormat = BinaryFormat_Float32; }
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unknown binary format: " << binaryFormatID << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//Perform checks:
|
||||
if (std::string(filename).empty())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Header file path is empty!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::string(filename).substr(filename.length() - 5, 5) != std::string(".vhdr"))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "The supplied output file does not end with .vhdr\n";
|
||||
}
|
||||
|
||||
m_headerFilename = filename;
|
||||
|
||||
m_headerFile.open(m_headerFilename.c_str());
|
||||
if (!m_headerFile.good())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not open header file [" << m_headerFilename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//this->getLogManager() << Kernel::LogLevel_ImportantWarning << m_sHeaderFilename << "\n";
|
||||
|
||||
m_dataFilename = m_headerFilename.substr(0, m_headerFilename.length() - 5) + std::string(".eeg");
|
||||
|
||||
m_dataFile.open(m_dataFilename.c_str(), std::ios::binary);
|
||||
if (!m_dataFile.good())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not open data file [" << m_dataFilename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_markerFilename = m_headerFilename.substr(0, m_headerFilename.length() - 5) + std::string(".vmrk");
|
||||
|
||||
m_markerFile.open(m_markerFilename.c_str());
|
||||
if (!m_markerFile.good())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not open marker file [" << m_markerFilename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::uninitialize()
|
||||
{
|
||||
if (m_streamDecoder)
|
||||
{
|
||||
m_streamDecoder->uninitialize();
|
||||
delete m_streamDecoder;
|
||||
}
|
||||
|
||||
// uninit input stimulation
|
||||
if (m_stimDecoder)
|
||||
{
|
||||
m_stimDecoder->uninitialize();
|
||||
delete m_stimDecoder;
|
||||
}
|
||||
|
||||
//close files
|
||||
m_headerFile.flush();
|
||||
m_headerFile.close();
|
||||
|
||||
m_dataFile.flush();
|
||||
m_dataFile.close();
|
||||
|
||||
m_markerFile.flush();
|
||||
m_markerFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//1. Process signal
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i) //first input channel data
|
||||
{
|
||||
m_streamDecoder->decode(i);
|
||||
|
||||
//HEADER
|
||||
if (m_streamDecoder->isHeaderReceived())
|
||||
{
|
||||
m_matrix = m_streamDecoder->getOutputMatrix();
|
||||
m_sampling = m_streamDecoder->getOutputSamplingRate();
|
||||
|
||||
writeHeaderFile();
|
||||
}
|
||||
|
||||
//BUFFER
|
||||
if (m_streamDecoder->isBufferReceived())
|
||||
{
|
||||
// size_t channelCount = m_Matrix->getDimensionSize(0);
|
||||
switch (m_binaryFormat)
|
||||
{
|
||||
case BinaryFormat_Integer16:
|
||||
saveBuffer(int16_t(0));
|
||||
break;
|
||||
|
||||
case BinaryFormat_UnsignedInteger16:
|
||||
saveBuffer(uint16_t(0));
|
||||
break;
|
||||
|
||||
case BinaryFormat_Float32:
|
||||
saveBuffer(float(0));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
//END
|
||||
if (m_streamDecoder->isEndReceived()) { }
|
||||
|
||||
boxContext.markInputAsDeprecated(0, i);
|
||||
}
|
||||
|
||||
// Make sure the marker file header is written in any case
|
||||
if (!m_isVmrkHeaderFileWritten)
|
||||
{
|
||||
const boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
|
||||
const std::string formated(formatTime(now));
|
||||
|
||||
m_markerFile << "Brain Vision Data Exchange Marker File, Version 1.0" << std::endl << std::endl
|
||||
<< "[Common Infos]" << std::endl
|
||||
<< "Codepage=ANSI" << std::endl
|
||||
<< "DataFile=" << getShortName(m_dataFilename) << std::endl << std::endl
|
||||
<< "[Marker Infos]" << std::endl
|
||||
<< "; Each entry: Mk<Marker number>=<Type>,<Description>,<Position in data points>," << std::endl
|
||||
<< "; <Size in data points>, <Channel number (0 = marker is related to all channels)>" << std::endl
|
||||
<< "; Fields are delimited by commas, some fields might be omitted (empty)." << std::endl
|
||||
<< "; Commas in type or description text are coded as \"\\1\"." << std::endl
|
||||
<< "Mk1=New Segment,,1,1,0," << formated << "000000" << std::endl;
|
||||
|
||||
m_isVmrkHeaderFileWritten = true;
|
||||
}
|
||||
|
||||
//2. Process stimulations - input channel 1
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
// uint64_t chunkStartTime =boxContext.getInputChunkStartTime(0, i);
|
||||
|
||||
m_stimDecoder->decode(i);
|
||||
|
||||
//buffer
|
||||
if (m_stimDecoder->isBufferReceived())
|
||||
{
|
||||
IStimulationSet* stimSet = m_stimDecoder->getOutputStimulationSet();
|
||||
|
||||
// Loop on stimulations
|
||||
for (size_t j = 0; j < stimSet->getStimulationCount(); ++j)
|
||||
{
|
||||
const uint64_t code = stimSet->getStimulationIdentifier(j);
|
||||
const uint64_t position = CTime(stimSet->getStimulationDate(j)).toSampleCount(m_sampling) + 1;
|
||||
|
||||
m_markerFile << "Mk" << m_stimulationCounter << "=Stimulus," << "S" << std::right << std::setw(3) << code << "," << position << ",1,0" << std::
|
||||
endl;
|
||||
|
||||
m_stimulationCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(1, i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::writeHeaderFile()
|
||||
{
|
||||
const size_t nChannel = m_matrix->getDimensionSize(0);
|
||||
// size_t samplesPerChunk = m_Matrix->getDimensionSize(1);
|
||||
|
||||
const double samplingInterval = 1000000.0 / double(m_sampling);
|
||||
|
||||
CString format("UNKNOWN");
|
||||
switch (m_binaryFormat)
|
||||
{
|
||||
case BinaryFormat_Integer16:
|
||||
format = "INT_16";
|
||||
break;
|
||||
|
||||
case BinaryFormat_UnsignedInteger16:
|
||||
format = "UINT_16";
|
||||
break;
|
||||
|
||||
case BinaryFormat_Float32:
|
||||
format = "IEEE_FLOAT_32";
|
||||
break;
|
||||
default:
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "EEG format unknown!\n";
|
||||
break;
|
||||
}
|
||||
|
||||
m_headerFile << "Brain Vision Data Exchange Header File Version 1.0" << std::endl
|
||||
<< "; Data created by the Vision Recorder" << std::endl << std::endl
|
||||
<< "[Common Infos]" << std::endl
|
||||
<< "Codepage=ANSI" << std::endl
|
||||
<< "DataFile=" << getShortName(m_dataFilename) << std::endl
|
||||
<< "MarkerFile=" << getShortName(m_markerFilename) << std::endl
|
||||
<< "DataFormat=BINARY" << std::endl
|
||||
<< "; Data orientation: MULTIPLEXED=ch1,pt1, ch2,pt1 ..." << std::endl //sample 1, sample 2 ...
|
||||
<< "DataOrientation=MULTIPLEXED" << std::endl
|
||||
<< "NumberOfChannels=" << nChannel << std::endl
|
||||
<< "; Sampling interval in microseconds" << std::endl
|
||||
<< "SamplingInterval=" << std::fixed << std::setprecision(5) << samplingInterval << std::endl
|
||||
<< std::endl
|
||||
<< "[Binary Infos]" << std::endl
|
||||
<< "BinaryFormat=" << format.toASCIIString() << std::endl
|
||||
<< std::endl;
|
||||
|
||||
m_headerFile << "[Channel Infos]" << std::endl
|
||||
<< "; Each entry: Ch<Channel number>=<Name>,<Reference channel name>," << std::endl
|
||||
<< "; <Resolution in \"Unit\">,<Unit>, Future extensions.." << std::endl
|
||||
<< "; Fields are delimited by commas, some fields might be omitted (empty)." << std::endl
|
||||
<< "; Commas in channel names are coded as \"\\1\"." << std::endl;
|
||||
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
m_headerFile << "Ch" << (i + 1) << "=" << m_matrix->getDimensionLabel(0, i) << ",,1," << std::endl; //resolution = 1
|
||||
}
|
||||
|
||||
m_headerFile << std::endl;
|
||||
|
||||
m_headerFile << "[Comment]" << std::endl << std::endl
|
||||
<< "A m p l i f i e r S e t u p" << std::endl
|
||||
<< "============================" << std::endl
|
||||
<< "Number of channels: " << nChannel << std::endl
|
||||
<< "Sampling Rate [Hz]: " << m_sampling << std::endl
|
||||
<< "Interval [µS]: " << std::fixed << std::setprecision(5) << samplingInterval << std::endl
|
||||
<< std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CBoxAlgorithmBrainampFileWriterGipsa::getShortName(std::string fullpath)
|
||||
{
|
||||
size_t pos = fullpath.find_last_of('\\');
|
||||
if (pos == std::string::npos) { pos = fullpath.find_last_of('/'); }
|
||||
if (pos != std::string::npos) { return fullpath.substr(pos + 1, fullpath.size() - pos); }
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
std::string CBoxAlgorithmBrainampFileWriterGipsa::formatTime(const boost::posix_time::ptime now)
|
||||
{
|
||||
using namespace boost::posix_time;
|
||||
|
||||
static std::locale loc(std::wcout.getloc(), new wtime_facet(L"%Y%m%d%H%M%S"));
|
||||
|
||||
std::basic_stringstream<wchar_t> wss;
|
||||
wss.imbue(loc);
|
||||
wss << now;
|
||||
std::wstring wstr = wss.str();
|
||||
std::string str(wstr.begin(), wstr.end());
|
||||
|
||||
return str;
|
||||
}
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,152 @@
|
||||
/* Project: Gipsa-lab plugins for OpenVibe
|
||||
* AUTHORS AND CONTRIBUTORS: Andreev A., Barachant A., Congedo M., Ionescu,Gelu,
|
||||
|
||||
* This file is part of "Gipsa-lab plugins for OpenVibe".
|
||||
* You can redistribute it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
* This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License along with Brain Invaders. If not, see http://www.gnu.org/licenses/.*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
class CBoxAlgorithmBrainampFileWriterGipsa final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
CBoxAlgorithmBrainampFileWriterGipsa() { }
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
bool writeHeaderFile(); //write the .vhdr file
|
||||
|
||||
static std::string getShortName(std::string fullpath); //of a file
|
||||
static std::string formatTime(boost::posix_time::ptime now);
|
||||
|
||||
|
||||
template <class T>
|
||||
bool saveBuffer(const T /*myDummy*/)
|
||||
{
|
||||
std::vector<T> output(m_matrix->getBufferElementCount());
|
||||
|
||||
if (output.size() != m_matrix->getBufferElementCount()) { return false; }
|
||||
|
||||
const size_t nChannel = m_matrix->getDimensionSize(0);
|
||||
const size_t nSamplesPerChunk = m_matrix->getDimensionSize(1);
|
||||
double* input = m_matrix->getBuffer();
|
||||
|
||||
for (size_t k = 0; k < nChannel; ++k)
|
||||
{
|
||||
for (size_t j = 0; j < nSamplesPerChunk; ++j)
|
||||
{
|
||||
const size_t index = (k * nSamplesPerChunk) + j;
|
||||
output[j * nChannel + k] = T(input[index]);
|
||||
}
|
||||
}
|
||||
|
||||
m_dataFile.write((char*)&output[0], m_matrix->getBufferElementCount() * sizeof(T));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsa)
|
||||
|
||||
protected:
|
||||
|
||||
enum EBinaryFormat
|
||||
{
|
||||
BinaryFormat_Integer16,
|
||||
BinaryFormat_UnsignedInteger16,
|
||||
BinaryFormat_Float32,
|
||||
};
|
||||
|
||||
//input signal 1
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmBrainampFileWriterGipsa>* m_streamDecoder = nullptr;
|
||||
CMatrix* m_matrix = nullptr;
|
||||
uint64_t m_sampling = 0;
|
||||
|
||||
//input stimulation 1
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmBrainampFileWriterGipsa>* m_stimDecoder = nullptr;
|
||||
//Kernel::TParameterHandler <const IMemoryBuffer* > ip_bufferToDecodeTrigger;
|
||||
//Kernel::TParameterHandler < IStimulationSet* > op_pStimulationSetTrigger;
|
||||
|
||||
std::string m_headerFilename;
|
||||
std::string m_dataFilename;
|
||||
std::string m_markerFilename;
|
||||
|
||||
std::ofstream m_headerFile;
|
||||
std::ofstream m_dataFile;
|
||||
std::ofstream m_markerFile;
|
||||
|
||||
size_t m_binaryFormat = 0;
|
||||
size_t m_stimulationCounter = 2; //because the first is outputed manually
|
||||
|
||||
bool m_isVmrkHeaderFileWritten = false; //Is the beginning of the .vmrk (file with stimulations is written)
|
||||
};
|
||||
|
||||
class CBoxAlgorithmBrainampFileWriterGipsaListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onInputTypeChanged(Kernel::IBox& /*box*/, const size_t /*index*/) override { return true; }
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmBrainampFileWriterGipsaDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("BrainVision Format file writer (Gipsa)"); }
|
||||
CString getAuthorName() const override { return CString("Anton Andreev"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Gipsa-lab"); }
|
||||
CString getShortDescription() const override { return CString("Writes signal in the Brainamp file format."); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"You must select the location of the output header file .vhdr. The .eeg and .vmrk files will be created with the same name and in the same folder. Integer codes of OpenVibe stimulations are saved in the .vmrk file. OV Stimulation with code 233 will be saved as S233 on the .vmrk file.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("File reading and writing/BrainVision Format"); }
|
||||
CString getVersion() const override { return CString("1.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-save"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsa; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmBrainampFileWriterGipsa; }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmBrainampFileWriterGipsaListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Streamed matrix", OV_TypeId_Signal);
|
||||
prototype.addInput("Input stimulation channel", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addSetting("Header filename", OV_TypeId_Filename, "record-[$core{date}-$core{time}].vhdr");
|
||||
prototype.addSetting("Binary format", OVP_TypeId_BinaryFormat, "INT_16");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsaDesc)
|
||||
};
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,314 @@
|
||||
#include "ovpCBoxAlgorithmEDFFileWriter.h"
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::initialize()
|
||||
{
|
||||
m_experimentInfoDecoder.initialize(*this, 0);
|
||||
m_signalDecoder.initialize(*this, 1);
|
||||
m_stimDecoder.initialize(*this, 2);
|
||||
|
||||
m_filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
m_sampling = 0;
|
||||
m_nChannels = 0;
|
||||
|
||||
m_isFileOpened = false;
|
||||
m_fileHandle = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::uninitialize()
|
||||
{
|
||||
if (!m_isFileOpened)
|
||||
{
|
||||
// Fine, we didn't manage to write anything
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "Exiting without writing a file (open failed or no signal header received).\n";
|
||||
|
||||
// Clear the queue
|
||||
std::queue<double> empty;
|
||||
std::swap(m_buffer, empty);
|
||||
|
||||
m_experimentInfoDecoder.uninitialize();
|
||||
m_signalDecoder.uninitialize();
|
||||
m_stimDecoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Writing the file, this may take a moment.\n";
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
if (EdfSetSampling(m_fileHandle, c, m_sampling) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_sampling failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetPhysicalMaximum(m_fileHandle, c, m_channelInfo[c].max) == -1)//1.7*10^308)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_physical_maximum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetPhysicalMinimum(m_fileHandle, c, m_channelInfo[c].min) == -1)//-1.7*10^308)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_physical_minimum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetDigitalMaximum(m_fileHandle, c, 32767) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_digital_maximum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetDigitalMinimum(m_fileHandle, c, -32768) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_digital_minimum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetLabel(m_fileHandle, c, m_signalDecoder.getOutputMatrix()->getDimensionLabel(0, c)) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_label failed!\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const size_t n = m_sampling * m_nChannels;
|
||||
std::vector<double> tmpBuffer(n), tmpBufferToWrite(n); // A buffer chunk and Transposed buffer chunk
|
||||
|
||||
// Write out all the complete buffers
|
||||
if (m_buffer.size() >= n)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "((int) buffer.size() >= m_sampling*m_NChannels)\n";
|
||||
while (m_buffer.size() >= n)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "while((int)buffer.size() >= m_sampling*m_NChannels)\n";
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
tmpBuffer[i] = double(m_buffer.front());
|
||||
m_buffer.pop();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
for (size_t s = 0; s < m_sampling; ++s) { tmpBufferToWrite[c * m_sampling + s] = tmpBuffer[s * m_nChannels + c]; }
|
||||
}
|
||||
|
||||
if (EdfBlockwritePhysicalSamples(m_fileHandle, tmpBufferToWrite.data()) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_blockwrite_physical_samples: Could not write samples in file [" << m_filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a partial buffer? If so, write it out padded by zeroes
|
||||
if (!m_buffer.empty())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "if(buffer.size() > 0))\n";
|
||||
for (size_t i = 0; i < n; ++i) { tmpBuffer[i] = 0; }
|
||||
|
||||
for (size_t i = 0; i < m_buffer.size(); ++i)
|
||||
{
|
||||
tmpBuffer[i] = double(m_buffer.front());
|
||||
m_buffer.pop();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
for (size_t s = 0; s < m_sampling; ++s) { tmpBufferToWrite[c * m_sampling + s] = tmpBuffer[s * m_nChannels + c]; }
|
||||
}
|
||||
|
||||
if (EdfBlockwritePhysicalSamples(m_fileHandle, tmpBufferToWrite.data()) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_blockwrite_physical_samples: Could not write samples in file [" << m_filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (EdfcloseFile(m_fileHandle) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edfclose_file: Could not close file [" << m_filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_experimentInfoDecoder.uninitialize();
|
||||
m_signalDecoder.uninitialize();
|
||||
m_stimDecoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//iterate over all chunk on signal input
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_signalDecoder.decode(i);
|
||||
|
||||
if (m_signalDecoder.isHeaderReceived())
|
||||
{
|
||||
m_sampling = size_t(m_signalDecoder.getOutputSamplingRate());
|
||||
m_nChannels = m_signalDecoder.getOutputMatrix()->getDimensionSize(0);
|
||||
m_nSamplesPerChunk = m_signalDecoder.getOutputMatrix()->getDimensionSize(1);
|
||||
|
||||
m_fileHandle = EdfopenFileWriteonly(m_filename.toASCIIString(), EDFLIB_FILETYPE_EDFPLUS, m_nChannels);
|
||||
if (m_fileHandle < 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "Could not open file [" << m_filename << "]\n";
|
||||
switch (m_fileHandle)
|
||||
{
|
||||
case EDFLIB_MALLOC_ERROR:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_MALLOC_ERROR: ";
|
||||
break;
|
||||
case EDFLIB_NO_SUCH_FILE_OR_DIRECTORY:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_NO_SUCH_FILE_OR_DIRECTORY: ";
|
||||
break;
|
||||
case EDFLIB_MAXFILES_REACHED:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_MAXFILES_REACHED: ";
|
||||
break;
|
||||
case EDFLIB_FILE_ALREADY_OPENED:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_FILE_ALREADY_OPENED: ";
|
||||
break;
|
||||
case EDFLIB_NUMBER_OF_SIGNALS_INVALID:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_NUMBER_OF_SIGNALS_INVALID: ";
|
||||
break;
|
||||
default:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_UNKNOWN_ERROR: ";
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//set file parameters
|
||||
EdfSetStartdatetime(m_fileHandle, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
//Creation of one information channel structure per channel
|
||||
channel_info_t channelInfo = { std::numeric_limits<double>::max(), -std::numeric_limits<double>::max() };
|
||||
m_channelInfo.push_back(channelInfo);
|
||||
}
|
||||
|
||||
m_isFileOpened = true;
|
||||
}
|
||||
|
||||
if (m_signalDecoder.isBufferReceived())
|
||||
{
|
||||
//put sample in the buffer
|
||||
CMatrix* matrix = m_signalDecoder.getOutputMatrix();
|
||||
for (size_t s = 0; s < m_nSamplesPerChunk; ++s)
|
||||
{
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
double value = matrix->getBuffer()[c * m_nSamplesPerChunk + s];
|
||||
if (value > m_channelInfo[c].max) { m_channelInfo[c].max = value; }
|
||||
if (value < m_channelInfo[c].min) { m_channelInfo[c].min = value; }
|
||||
m_buffer.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_signalDecoder.isEndReceived()) { }
|
||||
}
|
||||
|
||||
if (m_isFileOpened)
|
||||
{
|
||||
//iterate over all chunk on experiment information input
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_experimentInfoDecoder.decode(i);
|
||||
|
||||
if (m_experimentInfoDecoder.isHeaderReceived())
|
||||
{
|
||||
//set patient code
|
||||
EdfSetPatientcode(m_fileHandle, std::to_string(m_experimentInfoDecoder.getOutputSubjectID()).c_str());
|
||||
|
||||
//set patient gender
|
||||
switch (m_experimentInfoDecoder.getOutputSubjectGender())
|
||||
{
|
||||
case 2: //female
|
||||
EdfSetGender(m_fileHandle, 0);
|
||||
break;
|
||||
case 1: //male
|
||||
EdfSetGender(m_fileHandle, 1);
|
||||
break;
|
||||
case 0: //unknown
|
||||
break;
|
||||
case 9: //unspecified
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//set patient age
|
||||
const char* patientAge = ("Patient age = " + std::to_string(m_experimentInfoDecoder.getOutputSubjectAge())).c_str();
|
||||
EdfSetPatientAdditional(m_fileHandle, patientAge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//iterate over all chunk on stimulation input
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(2); ++i)
|
||||
{
|
||||
m_stimDecoder.decode(i);
|
||||
|
||||
if (m_stimDecoder.isHeaderReceived()) { }
|
||||
|
||||
if (m_stimDecoder.isBufferReceived())
|
||||
{
|
||||
const IStimulationSet* stimSet = m_stimDecoder.getOutputStimulationSet();
|
||||
for (size_t j = 0; j < stimSet->getStimulationCount(); ++j)
|
||||
{
|
||||
const uint64_t date = stimSet->getStimulationDate(j);
|
||||
const int64_t stimDate = int64_t(CTime(date).toSeconds() / 0.0001);
|
||||
|
||||
const uint64_t duration = stimSet->getStimulationDuration(j);
|
||||
const int64_t stimDuration = int64_t(CTime(duration).toSeconds() / 0.0001);
|
||||
|
||||
const uint64_t id = stimSet->getStimulationIdentifier(j);
|
||||
CString stimID = this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, id);
|
||||
if (stimID.length() == 0)
|
||||
{
|
||||
// If the stimulation number has not been registered as an enum, just pass the number
|
||||
std::stringstream ss;
|
||||
ss << id;
|
||||
stimID = ss.str().c_str();
|
||||
}
|
||||
const int result = EdfwriteAnnotationUTF8(m_fileHandle, stimDate, stimDuration, stimID.toASCIIString());
|
||||
if (result == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edfwrite_annotation_utf8 failed!\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stimDecoder.isEndReceived()) { }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "edf/edflib.h"
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
typedef struct
|
||||
{
|
||||
double min;
|
||||
double max;
|
||||
} channel_info_t;
|
||||
|
||||
|
||||
class CBoxAlgorithmEDFFileWriter final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
//CBoxAlgorithmEDFFileWriter();
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_EDFFileWriter)
|
||||
|
||||
protected:
|
||||
|
||||
Toolkit::TExperimentInfoDecoder<CBoxAlgorithmEDFFileWriter> m_experimentInfoDecoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmEDFFileWriter> m_signalDecoder;
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmEDFFileWriter> m_stimDecoder;
|
||||
|
||||
CString m_filename;
|
||||
bool m_isFileOpened = false;
|
||||
int m_fileHandle = 0;
|
||||
size_t m_sampling = 0;
|
||||
size_t m_nChannels = 0;
|
||||
size_t m_nSamplesPerChunk = 0;
|
||||
std::queue<double, std::deque<double>> m_buffer;
|
||||
//int * m_pTemporyBuffer;
|
||||
//int * m_pTemporyBufferToWrite;
|
||||
|
||||
std::vector<channel_info_t> m_channelInfo;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmEDFFileWriterDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("EDF File Writer"); }
|
||||
CString getAuthorName() const override { return CString("Aurelien Van Langhenhove"); }
|
||||
CString getAuthorCompanyName() const override { return CString("CICIT Garches"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Writes experiment information, signal and stimulations in a EDF file"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("File reading and writing/EDF"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-save"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_EDFFileWriter; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmEDFFileWriter; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Experiment information", OV_TypeId_ExperimentInfo);
|
||||
prototype.addInput("Signal", OV_TypeId_Signal);
|
||||
prototype.addInput("Stimulations", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addSetting("Filename", OV_TypeId_Filename, "record-[$core{date}-$core{time}].edf");
|
||||
|
||||
//prototype.addFlag(Kernel::BoxFlag_IsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_EDFFileWriterDesc)
|
||||
};
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_EDFFileWriter OpenViBE::CIdentifier(0x0D454DCE, 0x470A4C02)
|
||||
#define OVP_ClassId_BoxAlgorithm_EDFFileWriterDesc OpenViBE::CIdentifier(0x0D454DCE, 0x470A4C02)
|
||||
#define OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsa OpenViBE::CIdentifier(0x0C7E0BDE, 0x4EC90F95)
|
||||
#define OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsaDesc OpenViBE::CIdentifier(0x0A77142C, 0x316B6E47)
|
||||
|
||||
#define OVP_TypeId_BinaryFormat OpenViBE::CIdentifier(0x567234C5, 0x3D870DC3)
|
||||
#define OVP_TypeId_BinaryFormat_int16_t OpenViBE::CIdentifier(0x1C777556, 0x123861C3)
|
||||
#define OVP_TypeId_BinaryFormat_uint16_t OpenViBE::CIdentifier(0x6A7B6C73, 0x4B9D129D)
|
||||
#define OVP_TypeId_BinaryFormat_float OpenViBE::CIdentifier(0x15183866, 0x7AAC69FC)
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <vector>
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
// @BEGIN CICIT-GARCHES
|
||||
#include "box-algorithms/ovpCBoxAlgorithmEDFFileWriter.h"
|
||||
// @END CICIT-GARCHES
|
||||
|
||||
// @BEGIN GIPSA
|
||||
#include "box-algorithms/ovpCBoxAlgorithmBrainampFileWriterGipsa.h"
|
||||
// @END GIPSA
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
// @BEGIN CICIT-GARCHES
|
||||
OVP_Declare_New(CBoxAlgorithmEDFFileWriterDesc)
|
||||
// @END CICIT_GARCHES
|
||||
|
||||
// @BEGIN GIPSA
|
||||
//Register dropdowns
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_BinaryFormat, "Binary format select");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_BinaryFormat, "INT_16", OVP_TypeId_BinaryFormat_int16_t.id());
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_BinaryFormat, "UINT_16", OVP_TypeId_BinaryFormat_uint16_t.id());
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_BinaryFormat, "IEEE_FLOAT_32", OVP_TypeId_BinaryFormat_float.id());
|
||||
|
||||
OVP_Declare_New(CBoxAlgorithmBrainampFileWriterGipsaDesc)
|
||||
// @END GIPSA
|
||||
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,28 @@
|
||||
PROJECT(openvibe-plugins-contrib-misc)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
# ---------------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
# INCLUDE("FindThirdPartyBoost")
|
||||
INCLUDE("FindThirdPartyOpenAL")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "ovpCMouseControl.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#if defined TARGET_OS_Linux
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools { //Ambiguous without OpenViBEPlugins
|
||||
|
||||
bool CMouseControl::initialize()
|
||||
{
|
||||
m_decoder = new Toolkit::TStreamedMatrixDecoder<CMouseControl>(*this, 0);
|
||||
|
||||
#if !defined(TARGET_OS_Linux)
|
||||
getLogManager() << Kernel::LogLevel_Error << "This box algorithm is not implemented for your operating system\n";
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMouseControl::uninitialize()
|
||||
{
|
||||
m_decoder->uninitialize();
|
||||
delete m_decoder;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMouseControl::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMouseControl::process()
|
||||
{
|
||||
Kernel::IBoxIO* boxContext = getBoxAlgorithmContext()->getDynamicBoxContext();
|
||||
const size_t nInputChunk = boxContext->getInputChunkCount(0);
|
||||
|
||||
for (size_t i = 0; i < nInputChunk; ++i)
|
||||
{
|
||||
m_decoder->decode(i);
|
||||
if (m_decoder->isBufferReceived())
|
||||
{
|
||||
CMatrix* iMatrix = m_decoder->getOutputMatrix();
|
||||
if (iMatrix->getBufferElementCount() != 1)
|
||||
{
|
||||
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error << "Error, dimension size isn't 1 for Amplitude input !\n";
|
||||
return false;
|
||||
}
|
||||
#if defined TARGET_OS_Linux
|
||||
const double* iBuffer = iMatrix->getBuffer();
|
||||
m_pMainDisplay=::XOpenDisplay(NULL);
|
||||
if (!m_pMainDisplay)
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_Error << "Impossible to open Display.\n";
|
||||
return false;
|
||||
}
|
||||
m_oRootWindow=DefaultRootWindow(m_pMainDisplay); //all X11 screens
|
||||
::XSelectInput(m_pMainDisplay, m_oRootWindow, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|OwnerGrabButtonMask);
|
||||
|
||||
int offsetY = 0;
|
||||
int offsetX = int(iBuffer[0]*100.0);
|
||||
|
||||
getLogManager() << Kernel::LogLevel_Debug << "offsetX = " << offsetX << "\n";
|
||||
|
||||
::XWarpPointer(m_pMainDisplay, m_oRootWindow, 0, 0, 0, 0, 0, offsetX, offsetY);
|
||||
::XCloseDisplay(m_pMainDisplay);
|
||||
#endif
|
||||
// TODO
|
||||
// For windows use:
|
||||
// SetCursorPos(int x, int y)
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#if defined TARGET_OS_Linux
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
class CMouseControl : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
CMouseControl() { }
|
||||
|
||||
void release() override { delete this; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_MouseControl)
|
||||
|
||||
protected:
|
||||
|
||||
//codec
|
||||
Toolkit::TStreamedMatrixDecoder<CMouseControl>* m_decoder = nullptr;
|
||||
|
||||
#if defined TARGET_OS_Linux
|
||||
::Display* m_pMainDisplay = nullptr;
|
||||
::Window m_oRootWindow;
|
||||
#endif
|
||||
};
|
||||
|
||||
class CMouseControlDesc : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
CString getName() const override { return CString("Mouse Control"); }
|
||||
CString getAuthorName() const override { return CString("Guillaume Gibert"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INSERM"); }
|
||||
CString getShortDescription() const override { return CString("Mouse Control for Feedback"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("Experimental box to move the mouse in x direction with respect to the input value. Only implemented on Linux.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Tools"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
void release() override { }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_MouseControl; }
|
||||
IPluginObject* create() override { return new CMouseControl(); }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Amplitude", OV_TypeId_StreamedMatrix);
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_MouseControlDesc)
|
||||
};
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_MouseControl OpenViBE::CIdentifier(0xDA4B4EEB, 0x64FC6A16)
|
||||
#define OVP_ClassId_MouseControlDesc OpenViBE::CIdentifier(0xB6B65C98, 0xA756ED0E)
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <vector>
|
||||
#include <openvibe/ov_all.h>
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include "box-algorithms/ovpCMouseControl.h" // inserm
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
// @BEGIN inserm
|
||||
OVP_Declare_New(CMouseControlDesc);
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_BoxAlgorithmFlag, OV_AttributeId_Box_FlagIsUnstable.toString(),
|
||||
OV_AttributeId_Box_FlagIsUnstable.id());
|
||||
// @END inserm
|
||||
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,27 @@
|
||||
PROJECT(openvibe-plugins-contrib-network-io)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
# ---------------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
# INCLUDE("FindOpenViBEModuleEBML")
|
||||
INCLUDE("FindThirdPartyLSL")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_OSCController OSC Controller
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Description|
|
||||
By this box, you can control OSC supporting devices, such as
|
||||
synthesizers and oscillators. It can be used to turn OpenViBE
|
||||
streams into sound, e.g. for brain music or auditory BCI.
|
||||
|
||||
The OSC Controller box simply sends the incoming data as UDP
|
||||
messages to the specified OSC Server. Each message is flagged with
|
||||
an OSC Address specifying the device that the message is intended to
|
||||
control.
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Input1|
|
||||
Data to send to the OSC Server. This input can be either a signal, a matrix, or a stimulation. The signal or matrix must have only 1 channel (row). Signals are sent as float and stimulations as size_t.
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Input1|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Setting1|
|
||||
Server address (IP or DNS)
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Setting2|
|
||||
Server port
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Setting2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Setting3|
|
||||
The OSC Address specifying the device the messages are intended to, e.g. /oscillator/4/frequency
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Setting3|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Examples|
|
||||
Utilities such as OSC DataMonitor by Kasper Kamperman can be used to debug the messages sent out by the box.
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Miscellaneous|
|
||||
In the current implementation, the data is sent when received. OpenViBE internal timing is not passed to the OSC.
|
||||
The box supports only one input, but the input type can be modified. The input must have only one channel. For signals, you can extract a
|
||||
channel by using the Channel Selector box. Also, if input type is a signal, its sampling rate is ignored.
|
||||
However, several OSC Controller boxes can be used at the same time if multiple sources are needed to be
|
||||
sent to the OSC Server(s) or device(s). These limitations are to keep the code simple.
|
||||
|
||||
The box is not intended to transmit large chunks of numeric data. It may be meaningful to limit the
|
||||
amount of data on the OpenViBE side e.g. with boxes such as Signal Average, Downsampling or Signal Decimation, or Stimulation Filter.
|
||||
|
||||
For more information about the OSC protocol and applications that support it, please see http://www.opensoundcontrol.org
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Miscellaneous|
|
||||
*/
|
||||
@@ -0,0 +1,850 @@
|
||||
/**
|
||||
OSCPKT : a minimalistic OSC ( http://opensoundcontrol.org ) c++ library
|
||||
|
||||
Before using this file please take the time to read the OSC spec, it
|
||||
is short and not complicated: http://opensoundcontrol.org/spec-1_0
|
||||
|
||||
Features:
|
||||
- handles basic OSC types: TFihfdsb
|
||||
- handles bundles
|
||||
- handles OSC pattern-matching rules (wildcards etc in message paths)
|
||||
- portable on win / macos / linux
|
||||
- robust wrt malformed packets
|
||||
- optional udp transport for packets
|
||||
- concise, all in a single .h file
|
||||
- does not throw exceptions
|
||||
|
||||
does not:
|
||||
- take into account timestamp values.
|
||||
- provide a cpu-scalable message dispatching.
|
||||
- not suitable for use inside a realtime thread as it allocates memory when
|
||||
building or reading messages.
|
||||
|
||||
|
||||
There are basically 3 classes of interest:
|
||||
- oscpkt::Message : read/write the content of an OSC message
|
||||
- oscpkt::PacketReader : read the bundles/messages embedded in an OSC packet
|
||||
- oscpkt::PacketWriter : write bundles/messages into an OSC packet
|
||||
|
||||
And optionaly:
|
||||
- oscpkt::UdpSocket : read/write OSC packets over UDP.
|
||||
|
||||
example: oscpkt_demo.cc
|
||||
example: oscpkt_test.cc
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2010 Julien Pommier
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
(this is the zlib license)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <stdint.h>
|
||||
#else
|
||||
namespace oscpkt {
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
}
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#if defined(OSCPKT_OSTREAM_OUTPUT) || defined(OSCPKT_TEST)
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
namespace oscpkt {
|
||||
|
||||
/**
|
||||
OSC timetag stuff, the highest 32-bit are seconds, the lowest are fraction of a second.
|
||||
*/
|
||||
class TimeTag
|
||||
{
|
||||
uint64_t v = 1;
|
||||
public:
|
||||
TimeTag() {}
|
||||
explicit TimeTag(const uint64_t w): v(w) {}
|
||||
operator uint64_t() const { return v; }
|
||||
static TimeTag immediate() { return TimeTag(1); }
|
||||
};
|
||||
|
||||
/* the various types that we handle (OSC 1.0 specifies that INT32/FLOAT/STRING/BLOB are the bare minimum) */
|
||||
enum
|
||||
{
|
||||
TYPE_TAG_TRUE = 'T',
|
||||
TYPE_TAG_FALSE = 'F',
|
||||
TYPE_TAG_INT32 = 'i',
|
||||
TYPE_TAG_INT64 = 'h',
|
||||
TYPE_TAG_FLOAT = 'f',
|
||||
TYPE_TAG_DOUBLE = 'd',
|
||||
TYPE_TAG_STRING = 's',
|
||||
TYPE_TAG_BLOB = 'b'
|
||||
};
|
||||
|
||||
/* a few utility functions follow.. */
|
||||
|
||||
// round to the next multiple of 4, works for size_t and pointer arguments
|
||||
template <typename Type>
|
||||
Type ceil4(Type p) { return Type((size_t(p) + 3) & (~size_t(3))); }
|
||||
|
||||
// check that a memory area is zero padded until the next address which is a multiple of 4
|
||||
inline bool isZeroPaddingCorrect(const char* p)
|
||||
{
|
||||
const char* q = ceil4(p);
|
||||
for (; p < q; ++p) { if (*p != 0) { return false; } }
|
||||
return true;
|
||||
}
|
||||
|
||||
// stuff for reading / writing POD ("Plain Old Data") variables to unaligned bytes.
|
||||
template <typename POD>
|
||||
union PodBytes
|
||||
{
|
||||
char bytes[sizeof(POD)];
|
||||
POD value;
|
||||
};
|
||||
|
||||
inline bool isBigEndian()
|
||||
{ // a compile-time constant would certainly improve performances..
|
||||
PodBytes<int32_t> p;
|
||||
p.value = 0x12345678;
|
||||
return p.bytes[0] == 0x12;
|
||||
}
|
||||
|
||||
/** read unaligned bytes into a POD type, assuming the bytes are a little endian representation */
|
||||
template <typename POD>
|
||||
POD bytes2pod(const char* bytes)
|
||||
{
|
||||
PodBytes<POD> p;
|
||||
for (size_t i = 0; i < sizeof(POD); ++i)
|
||||
{
|
||||
if (isBigEndian()) { p.bytes[i] = bytes[i]; }
|
||||
else { p.bytes[i] = bytes[sizeof(POD) - i - 1]; }
|
||||
}
|
||||
return p.value;
|
||||
}
|
||||
|
||||
/** stored a POD type into an unaligned bytes array, using little endian representation */
|
||||
template <typename POD>
|
||||
void pod2bytes(const POD value, char* bytes)
|
||||
{
|
||||
PodBytes<POD> p;
|
||||
p.value = value;
|
||||
for (size_t i = 0; i < sizeof(POD); ++i)
|
||||
{
|
||||
if (isBigEndian()) { bytes[i] = p.bytes[i]; }
|
||||
else { bytes[i] = p.bytes[sizeof(POD) - i - 1]; }
|
||||
}
|
||||
}
|
||||
|
||||
/** internal stuff, handles the dynamic storage with correct alignments to 4 bytes */
|
||||
struct Storage
|
||||
{
|
||||
std::vector<char> data;
|
||||
Storage() { data.reserve(200); }
|
||||
|
||||
char* getBytes(const size_t sz)
|
||||
{
|
||||
assert((data.size() & 3) == 0);
|
||||
if (data.size() + sz > data.capacity()) { data.reserve((data.size() + sz) * 2); }
|
||||
const size_t sz4 = ceil4(sz);
|
||||
const size_t pos = data.size();
|
||||
data.resize(pos + sz4); // resize will fill with zeros, so the zero padding is OK
|
||||
return &(data[pos]);
|
||||
}
|
||||
|
||||
char* begin() { return !data.empty() ? &data.front() : nullptr; }
|
||||
char* end() { return begin() + size(); }
|
||||
const char* begin() const { return !data.empty() ? &data.front() : nullptr; }
|
||||
const char* end() const { return begin() + size(); }
|
||||
size_t size() const { return data.size(); }
|
||||
void assign(const char* beg, const char* end) { data.assign(beg, end); }
|
||||
void clear() { data.resize(0); }
|
||||
};
|
||||
|
||||
/** check if the path matches the supplied path pattern , according to the OSC spec pattern rules ('*' and '//' wildcards, '{}' alternatives, brackets etc) */
|
||||
bool fullPatternMatch(const std::string& pattern, const std::string& path);
|
||||
/** check if the path matches the beginning of pattern */
|
||||
bool partialPatternMatch(const std::string& pattern, const std::string& path);
|
||||
|
||||
#if defined(OSCPKT_DEBUG)
|
||||
#define OSCPKT_SET_ERR(errcode) do { if (!err) { err = errcode; std::cerr << "set " #errcode << " at line " << __LINE__ << "\n"; } } while (0)
|
||||
#else
|
||||
#define OSCPKT_SET_ERR(errcode) do { if (!err) err = errcode; } while (0)
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OK_NO_ERROR=0,
|
||||
// errors raised by the Message class:
|
||||
MALFORMED_ADDRESS_PATTERN,
|
||||
MALFORMED_TYPE_TAGS,
|
||||
MALFORMED_ARGUMENTS,
|
||||
UNHANDLED_TYPE_TAGS,
|
||||
// errors raised by ArgReader
|
||||
TYPE_MISMATCH,
|
||||
NOT_ENOUGH_ARG,
|
||||
PATTERN_MISMATCH,
|
||||
// errors raised by PacketReader/PacketWriter
|
||||
INVALID_BUNDLE,
|
||||
INVALID_PACKET_SIZE,
|
||||
BUNDLE_REQUIRED_FOR_MULTI_MESSAGES
|
||||
} ErrorCode;
|
||||
|
||||
/**
|
||||
struct used to hold an OSC message that will be written or read.
|
||||
|
||||
The list of arguments is exposed as a sort of queue. You "pop"
|
||||
arguments from the front of the queue when reading, you push
|
||||
arguments at the back of the queue when writing.
|
||||
|
||||
Many functions return *this, so they can be chained: init("/foo").pushInt32(2).pushStr("kllk")...
|
||||
|
||||
Example of use:
|
||||
|
||||
creation of a message:
|
||||
@code
|
||||
msg.init("/foo").pushInt32(4).pushStr("bar");
|
||||
@endcode
|
||||
reading a message, with error detection:
|
||||
@code
|
||||
if (msg.match("/foo/b*ar/plop")) {
|
||||
int i; std::string s; std::vector<char> b;
|
||||
if (msg.arg().popInt32(i).popStr(s).popBlob(b).isOkNoMoreArgs()) {
|
||||
process message...;
|
||||
} else arguments mismatch;
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
TimeTag time_tag;
|
||||
std::string address;
|
||||
std::string type_tags;
|
||||
std::vector<std::pair<size_t, size_t>> arguments; // array of pairs (pos,size), pos being an index into the 'storage' array.
|
||||
Storage storage; // the arguments data is stored here
|
||||
ErrorCode err;
|
||||
public:
|
||||
/** ArgReader is used for popping arguments from a Message, holds a
|
||||
pointer to the original Message, and maintains a local error code */
|
||||
class ArgReader
|
||||
{
|
||||
const Message* msg;
|
||||
ErrorCode err;
|
||||
size_t arg_idx; // arg index of the next arg that will be popped out.
|
||||
public:
|
||||
ArgReader(const Message& m, const ErrorCode e = OK_NO_ERROR) : msg(&m), err(msg->getErr()), arg_idx(0)
|
||||
{
|
||||
if (e != OK_NO_ERROR && err == OK_NO_ERROR) { err = e; }
|
||||
}
|
||||
|
||||
ArgReader(const ArgReader& other) : msg(other.msg), err(other.err), arg_idx(other.arg_idx) {}
|
||||
bool isBool() { return currentTypeTag() == TYPE_TAG_TRUE || currentTypeTag() == TYPE_TAG_FALSE; }
|
||||
bool isInt32() { return currentTypeTag() == TYPE_TAG_INT32; }
|
||||
bool isInt64() { return currentTypeTag() == TYPE_TAG_INT64; }
|
||||
bool isFloat() { return currentTypeTag() == TYPE_TAG_FLOAT; }
|
||||
bool isDouble() { return currentTypeTag() == TYPE_TAG_DOUBLE; }
|
||||
bool isStr() { return currentTypeTag() == TYPE_TAG_STRING; }
|
||||
bool isBlob() { return currentTypeTag() == TYPE_TAG_BLOB; }
|
||||
|
||||
size_t nbArgRemaining() const { return msg->arguments.size() - arg_idx; }
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
operator bool() const { return isOk(); } // implicit bool conversion is handy here
|
||||
/** call this at the end of the popXXX() chain to make sure everything is ok and
|
||||
all arguments have been popped */
|
||||
bool isOkNoMoreArgs() const { return err == OK_NO_ERROR && nbArgRemaining() == 0; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
/** retrieve an int32_t argument */
|
||||
ArgReader& popInt32(int32_t& i) { return popPod<int32_t>(TYPE_TAG_INT32, i); }
|
||||
/** retrieve an int64_t argument */
|
||||
ArgReader& popInt64(int64_t& i) { return popPod<int64_t>(TYPE_TAG_INT64, i); }
|
||||
/** retrieve a single precision floating point argument */
|
||||
ArgReader& popFloat(float& f) { return popPod<float>(TYPE_TAG_FLOAT, f); }
|
||||
/** retrieve a double precision floating point argument */
|
||||
ArgReader& popDouble(double& d) { return popPod<double>(TYPE_TAG_DOUBLE, d); }
|
||||
/** retrieve a string argument (no check performed on its content, so it may contain any byte value except 0) */
|
||||
ArgReader& popStr(std::string& s)
|
||||
{
|
||||
if (precheck(TYPE_TAG_STRING)) { s = argBeg(arg_idx++); }
|
||||
return *this;
|
||||
}
|
||||
/** retrieve a binary blob */
|
||||
ArgReader& popBlob(std::vector<char>& b)
|
||||
{
|
||||
if (precheck(TYPE_TAG_BLOB))
|
||||
{
|
||||
b.assign(argBeg(arg_idx) + 4, argEnd(arg_idx));
|
||||
++arg_idx;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/** retrieve a bool argument */
|
||||
ArgReader& popBool(bool& b)
|
||||
{
|
||||
b = false;
|
||||
if (arg_idx >= msg->arguments.size()) { OSCPKT_SET_ERR(NOT_ENOUGH_ARG); }
|
||||
else if (currentTypeTag() == TYPE_TAG_TRUE) { b = true; }
|
||||
else if (currentTypeTag() == TYPE_TAG_FALSE) { b = false; }
|
||||
else { OSCPKT_SET_ERR(TYPE_MISMATCH); }
|
||||
++arg_idx;
|
||||
return *this;
|
||||
}
|
||||
/** skip whatever comes next */
|
||||
ArgReader& pop()
|
||||
{
|
||||
if (arg_idx >= msg->arguments.size()) { OSCPKT_SET_ERR(NOT_ENOUGH_ARG); }
|
||||
else { ++arg_idx; }
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* argBeg(const size_t idx) const
|
||||
{
|
||||
if (err || idx >= msg->arguments.size()) { return nullptr; }
|
||||
return msg->storage.begin() + msg->arguments[idx].first;
|
||||
}
|
||||
|
||||
const char* argEnd(const size_t idx) const
|
||||
{
|
||||
if (err || idx >= msg->arguments.size()) { return nullptr; }
|
||||
return msg->storage.begin() + msg->arguments[idx].first + msg->arguments[idx].second;
|
||||
}
|
||||
|
||||
int currentTypeTag()
|
||||
{
|
||||
if (!err && arg_idx < msg->type_tags.size()) { return msg->type_tags[arg_idx]; }
|
||||
OSCPKT_SET_ERR(NOT_ENOUGH_ARG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename POD>
|
||||
ArgReader& popPod(const int tag, POD& v)
|
||||
{
|
||||
if (precheck(tag))
|
||||
{
|
||||
v = bytes2pod<POD>(argBeg(arg_idx));
|
||||
++arg_idx;
|
||||
}
|
||||
else { v = POD(0); }
|
||||
return *this;
|
||||
}
|
||||
/* pre-check stuff before popping an argument from the message */
|
||||
bool precheck(const int tag)
|
||||
{
|
||||
if (arg_idx >= msg->arguments.size()) { OSCPKT_SET_ERR(NOT_ENOUGH_ARG); }
|
||||
else if (!err && currentTypeTag() != tag) { OSCPKT_SET_ERR(TYPE_MISMATCH); }
|
||||
return err == OK_NO_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
Message() { clear(); }
|
||||
Message(const std::string& s, const TimeTag tt = TimeTag::immediate()) : time_tag(tt), address(s), err(OK_NO_ERROR) {}
|
||||
|
||||
Message(const void* ptr, const size_t sz, const TimeTag tt = TimeTag::immediate())
|
||||
{
|
||||
buildFromRawData(ptr, sz);
|
||||
time_tag = tt;
|
||||
}
|
||||
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
/** return the type_tags string, with its initial ',' stripped. */
|
||||
const std::string& typeTags() const { return type_tags; }
|
||||
/** retrieve the address pattern. If you want to follow to the whole OSC spec, you
|
||||
have to handle its matching rules for address specifications -- this file does
|
||||
not provide this functionality */
|
||||
const std::string& addressPattern() const { return address; }
|
||||
TimeTag timeTag() const { return time_tag; }
|
||||
/** clear the message and start a new message with the supplied address and time_tag. */
|
||||
Message& init(const std::string& addr, const TimeTag tt = TimeTag::immediate())
|
||||
{
|
||||
clear();
|
||||
address = addr;
|
||||
time_tag = tt;
|
||||
if (address.empty() || address[0] != '/') { OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** start a matching test. The typical use-case is to follow this by
|
||||
a sequence of calls to popXXX() and a final call to
|
||||
isOkNoMoreArgs() which will allow to check that everything went
|
||||
fine. For example:
|
||||
@code
|
||||
if (msg.match("/foo").popInt32(i).isOkNoMoreArgs()) { blah(i); }
|
||||
else if (msg.match("/bar").popStr(s).popInt32(i).isOkNoMoreArgs()) { plop(s,i); }
|
||||
else std::cerr << "unhandled message: " << msg << "\n";
|
||||
@endcode
|
||||
*/
|
||||
ArgReader match(const std::string& test) const { return ArgReader(*this, fullPatternMatch(address, test) ? OK_NO_ERROR : PATTERN_MISMATCH); }
|
||||
/** return true if the 'test' path matched by the first characters of addressPattern().
|
||||
For ex. ("/foo/bar").partialMatch("/foo/") is true */
|
||||
ArgReader partialMatch(const std::string& test) const { return ArgReader(*this, partialPatternMatch(address, test) ? OK_NO_ERROR : PATTERN_MISMATCH); }
|
||||
|
||||
ArgReader arg() const { return ArgReader(*this, OK_NO_ERROR); }
|
||||
|
||||
/** build the osc message for raw data (the message will keep a copy of that data) */
|
||||
void buildFromRawData(const void* ptr, const size_t sz)
|
||||
{
|
||||
clear();
|
||||
storage.assign((const char*)ptr, (const char*)ptr + sz);
|
||||
const char* address_beg = storage.begin();
|
||||
const char* address_end = (const char*)memchr(address_beg, 0, storage.end() - address_beg);
|
||||
if (!address_end || !isZeroPaddingCorrect(address_end + 1) || address_beg[0] != '/')
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN);
|
||||
return;
|
||||
}
|
||||
address.assign(address_beg, address_end);
|
||||
|
||||
const char* type_tags_beg = ceil4(address_end + 1);
|
||||
const char* type_tags_end = (const char*)memchr(type_tags_beg, 0, storage.end() - type_tags_beg);
|
||||
if (!type_tags_end || !isZeroPaddingCorrect(type_tags_end + 1) || type_tags_beg[0] != ',')
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_TYPE_TAGS);
|
||||
return;
|
||||
}
|
||||
type_tags.assign(type_tags_beg + 1, type_tags_end);
|
||||
// we do not copy the initial ','
|
||||
|
||||
const char* arg = ceil4(type_tags_end + 1);
|
||||
assert(arg <= storage.end());
|
||||
size_t iarg = 0;
|
||||
while (isOk() && iarg < type_tags.size())
|
||||
{
|
||||
assert(arg <= storage.end());
|
||||
size_t len = getArgSize(type_tags[iarg], arg);
|
||||
if (isOk()) { arguments.push_back(std::make_pair(arg - storage.begin(), len)); }
|
||||
arg += ceil4(len);
|
||||
++iarg;
|
||||
}
|
||||
if (iarg < type_tags.size() || arg != storage.end()) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); }
|
||||
}
|
||||
|
||||
/* below are all the functions that serve when *writing* a message */
|
||||
Message& pushBool(const bool b)
|
||||
{
|
||||
type_tags += (b ? TYPE_TAG_TRUE : TYPE_TAG_FALSE);
|
||||
arguments.push_back(std::make_pair(storage.size(), storage.size()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& pushInt32(const int32_t i) { return pushPod(TYPE_TAG_INT32, i); }
|
||||
Message& pushInt64(const int64_t h) { return pushPod(TYPE_TAG_INT64, h); }
|
||||
Message& pushFloat(const float f) { return pushPod(TYPE_TAG_FLOAT, f); }
|
||||
Message& pushDouble(const double d) { return pushPod(TYPE_TAG_DOUBLE, d); }
|
||||
|
||||
Message& pushStr(const std::string& s)
|
||||
{
|
||||
assert(s.size() < 2147483647); // insane values are not welcome
|
||||
type_tags += TYPE_TAG_STRING;
|
||||
arguments.push_back(std::make_pair(storage.size(), s.size() + 1));
|
||||
strcpy(storage.getBytes(s.size() + 1), s.c_str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& pushBlob(void* ptr, const size_t size)
|
||||
{
|
||||
assert(size < 2147483647); // insane values are not welcome
|
||||
type_tags += TYPE_TAG_BLOB;
|
||||
arguments.push_back(std::make_pair(storage.size(), size + 4));
|
||||
pod2bytes<int32_t>(int32_t(size), storage.getBytes(4));
|
||||
if (size) { memcpy(storage.getBytes(size), ptr, size); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** reset the message to a clean state */
|
||||
void clear()
|
||||
{
|
||||
address.clear();
|
||||
type_tags.clear();
|
||||
storage.clear();
|
||||
arguments.clear();
|
||||
err = OK_NO_ERROR;
|
||||
time_tag = TimeTag::immediate();
|
||||
}
|
||||
|
||||
/** write the raw message data (used by PacketWriter) */
|
||||
void packMessage(Storage& s, const bool size) const
|
||||
{
|
||||
if (!isOk()) { return; }
|
||||
const size_t addr = address.size() + 1;
|
||||
const size_t type = type_tags.size() + 2;
|
||||
if (size) { pod2bytes<uint32_t>(uint32_t(ceil4(addr) + ceil4(type) + ceil4(storage.size())), s.getBytes(4)); }
|
||||
strcpy(s.getBytes(addr), address.c_str());
|
||||
strcpy(s.getBytes(type), ("," + type_tags).c_str());
|
||||
if (storage.size()) { memcpy(s.getBytes(storage.size()), const_cast<Storage&>(storage).begin(), storage.size()); }
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/* get the number of bytes occupied by the argument */
|
||||
size_t getArgSize(const int type, const char* p)
|
||||
{
|
||||
if (err) { return 0; }
|
||||
size_t sz = 0;
|
||||
assert(p >= storage.begin() && p <= storage.end());
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_TAG_TRUE:
|
||||
case TYPE_TAG_FALSE: sz = 0;
|
||||
break;
|
||||
case TYPE_TAG_INT32:
|
||||
case TYPE_TAG_FLOAT: sz = 4;
|
||||
break;
|
||||
case TYPE_TAG_INT64:
|
||||
case TYPE_TAG_DOUBLE: sz = 8;
|
||||
break;
|
||||
case TYPE_TAG_STRING:
|
||||
{
|
||||
const char* q = (const char*)memchr(p, 0, storage.end() - p);
|
||||
if (!q) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); }
|
||||
else { sz = (q - p) + 1; }
|
||||
}
|
||||
break;
|
||||
case TYPE_TAG_BLOB:
|
||||
{
|
||||
if (p == storage.end())
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
|
||||
return 0;
|
||||
}
|
||||
sz = 4 + bytes2pod<uint32_t>(p);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
OSCPKT_SET_ERR(UNHANDLED_TYPE_TAGS);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (p + sz > storage.end() || /* string or blob too large.. */
|
||||
p + sz < p /* or even blob so large that it did overflow */)
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
|
||||
return 0;
|
||||
}
|
||||
if (!isZeroPaddingCorrect(p + sz))
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
|
||||
return 0;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
template <typename POD>
|
||||
Message& pushPod(const int tag, POD v)
|
||||
{
|
||||
type_tags += char(tag);
|
||||
arguments.push_back(std::make_pair(storage.size(), sizeof(POD)));
|
||||
pod2bytes(v, storage.getBytes(sizeof(POD)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef OSCPKT_OSTREAM_OUTPUT
|
||||
friend std::ostream &operator<<(std::ostream &os, const Message &msg) {
|
||||
os << "osc_address: '" << msg.address << "', types: '" << msg.type_tags << "', timetag=" << msg.time_tag << ", args=[";
|
||||
Message::ArgReader arg(msg);
|
||||
while (arg.nbArgRemaining() && arg.isOk()) {
|
||||
if (arg.isBool()) { bool b; arg.popBool(b); os << (b?"True":"False"); }
|
||||
else if (arg.isInt32()) { int32_t i; arg.popInt32(i); os << i; }
|
||||
else if (arg.isInt64()) { int64_t h; arg.popInt64(h); os << h << "ll"; }
|
||||
else if (arg.isFloat()) { float f; arg.popFloat(f); os << f << "f"; }
|
||||
else if (arg.isDouble()) { double d; arg.popDouble(d); os << d; }
|
||||
else if (arg.isStr()) { std::string s; arg.popStr(s); os << "'" << s << "'"; }
|
||||
else if (arg.isBlob()) { std::vector<char> b; arg.popBlob(b); os << "Blob " << b.size() << " bytes"; }
|
||||
else {
|
||||
assert(0); // I forgot a case..
|
||||
}
|
||||
if (arg.nbArgRemaining()) os << ", ";
|
||||
}
|
||||
if (!arg.isOk()) { os << " ERROR#" << arg.getErr(); }
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
parse an OSC packet and extracts the embedded OSC messages.
|
||||
*/
|
||||
class PacketReader
|
||||
{
|
||||
public:
|
||||
PacketReader() { err = OK_NO_ERROR; }
|
||||
/** pointer and size of the osc packet to be parsed. */
|
||||
PacketReader(const void* ptr, size_t sz) { init(ptr, sz); }
|
||||
|
||||
void init(const void* ptr, const size_t sz)
|
||||
{
|
||||
err = OK_NO_ERROR;
|
||||
messages.clear();
|
||||
if ((sz % 4) == 0) { parse((const char*)ptr, (const char*)ptr + sz, TimeTag::immediate()); }
|
||||
else { OSCPKT_SET_ERR(INVALID_PACKET_SIZE); }
|
||||
it_messages = messages.begin();
|
||||
}
|
||||
|
||||
/** extract the next osc message from the packet. return 0 when all messages have been read, or in case of error. */
|
||||
Message* popMessage()
|
||||
{
|
||||
if (!err && !messages.empty() && it_messages != messages.end()) { return &*it_messages++; }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
private:
|
||||
std::list<Message> messages;
|
||||
std::list<Message>::iterator it_messages;
|
||||
ErrorCode err;
|
||||
|
||||
void parse(const char* beg, const char* end, const TimeTag time_tag)
|
||||
{
|
||||
assert(beg <= end && !err);
|
||||
assert(((end-beg)%4)==0);
|
||||
|
||||
if (beg == end) { return; }
|
||||
if (*beg == '#')
|
||||
{
|
||||
/* it's a bundle */
|
||||
if (end - beg >= 20
|
||||
&& memcmp(beg, "#bundle\0", 8) == 0)
|
||||
{
|
||||
const TimeTag timeTag2(bytes2pod<uint64_t>(beg + 8));
|
||||
const char* pos = beg + 16;
|
||||
do
|
||||
{
|
||||
const uint32_t sz = bytes2pod<uint32_t>(pos);
|
||||
pos += 4;
|
||||
if ((sz & 3) != 0 || pos + sz > end || pos + sz < pos) { OSCPKT_SET_ERR(INVALID_BUNDLE); }
|
||||
else
|
||||
{
|
||||
parse(pos, pos + sz, timeTag2);
|
||||
pos += sz;
|
||||
}
|
||||
} while (!err && pos != end);
|
||||
}
|
||||
else { OSCPKT_SET_ERR(INVALID_BUNDLE); }
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.push_back(Message(beg, end - beg, time_tag));
|
||||
if (!messages.back().isOk()) { OSCPKT_SET_ERR(messages.back().getErr()); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Assemble messages into an OSC packet. Example of use:
|
||||
@code
|
||||
PacketWriter pkt;
|
||||
Message msg;
|
||||
pkt.startBundle();
|
||||
pkt.addMessage(msg.init("/foo").pushBool(true).pushStr("plop").pushFloat(3.14f));
|
||||
pkt.addMessage(msg.init("/bar").pushBool(false));
|
||||
pkt.endBundle();
|
||||
if (pkt.isOk()) {
|
||||
send(pkt.data(), pkt.size());
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class PacketWriter
|
||||
{
|
||||
public:
|
||||
PacketWriter() { init(); }
|
||||
|
||||
PacketWriter& init()
|
||||
{
|
||||
err = OK_NO_ERROR;
|
||||
storage.clear();
|
||||
bundles.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** begin a new bundle. If you plan to pack more than one message in the Osc packet, you have to
|
||||
put them in a bundle. Nested bundles inside bundles are also allowed. */
|
||||
PacketWriter& startBundle(const TimeTag ts = TimeTag::immediate())
|
||||
{
|
||||
char* p;
|
||||
if (!bundles.empty()) { p = storage.getBytes(4); } // hold the bundle size
|
||||
p = storage.getBytes(8);
|
||||
strcpy(p, "#bundle");
|
||||
bundles.push_back(p - storage.begin());
|
||||
p = storage.getBytes(8);
|
||||
pod2bytes<uint64_t>(ts, p);
|
||||
return *this;
|
||||
}
|
||||
/** close the current bundle. */
|
||||
PacketWriter& endBundle()
|
||||
{
|
||||
if (!bundles.empty())
|
||||
{
|
||||
if (storage.size() - bundles.back() == 16) { pod2bytes<uint32_t>(0, storage.getBytes(4)); } // the 'empty bundle' case, not very elegant
|
||||
if (bundles.size() > 1) // no size stored for the top-level bundle
|
||||
{
|
||||
pod2bytes<uint32_t>(uint32_t(storage.size() - bundles.back()), storage.begin() + bundles.back() - 4);
|
||||
}
|
||||
bundles.pop_back();
|
||||
}
|
||||
else { OSCPKT_SET_ERR(INVALID_BUNDLE); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** insert an Osc message into the current bundle / packet.
|
||||
*/
|
||||
PacketWriter& addMessage(const Message& msg)
|
||||
{
|
||||
if (storage.size() != 0 && bundles.empty()) { OSCPKT_SET_ERR(BUNDLE_REQUIRED_FOR_MULTI_MESSAGES); }
|
||||
else { msg.packMessage(storage, !bundles.empty()); }
|
||||
if (!msg.isOk()) { OSCPKT_SET_ERR(msg.getErr()); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** the error flag will be raised if an opened bundle is not closed, or if more than one message is
|
||||
inserted in the packet without a bundle */
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
/** return the number of bytes of the osc packet -- will always be a
|
||||
multiple of 4 -- returns 0 if the construction of the packet has
|
||||
failed. */
|
||||
size_t packetSize() const { return err ? 0 : storage.size(); }
|
||||
|
||||
/** return the bytes of the osc packet (NULL if the construction of the packet has failed) */
|
||||
char* packetData() { return err ? nullptr : storage.begin(); }
|
||||
private:
|
||||
std::vector<size_t> bundles; // hold the position in the storage array of the beginning marker of each bundle
|
||||
Storage storage;
|
||||
ErrorCode err;
|
||||
};
|
||||
|
||||
// see the OSC spec for the precise pattern matching rules
|
||||
inline const char* internalPatternMatch(const char* pattern, const char* path)
|
||||
{
|
||||
while (*pattern)
|
||||
{
|
||||
const char* p = pattern;
|
||||
if (*p == '?' && *path)
|
||||
{
|
||||
++p;
|
||||
++path;
|
||||
}
|
||||
else if (*p == '[' && *path)
|
||||
{ // bracketted range, e.g. [a-zABC]
|
||||
++p;
|
||||
bool reverse = false;
|
||||
if (*p == '!')
|
||||
{
|
||||
reverse = true;
|
||||
++p;
|
||||
}
|
||||
bool match = reverse;
|
||||
for (; *p && *p != ']'; ++p)
|
||||
{
|
||||
const char c0 = *p;
|
||||
char c1 = c0;
|
||||
if (p[1] == '-' && p[2])
|
||||
{
|
||||
p += 2;
|
||||
c1 = *p;
|
||||
}
|
||||
if (*path >= c0 && *path <= c1) { match = !reverse; }
|
||||
}
|
||||
if (!match || *p != ']') { return pattern; }
|
||||
++p;
|
||||
++path;
|
||||
}
|
||||
else if (*p == '*')
|
||||
{ // wildcard '*'
|
||||
while (*p == '*') { ++p; }
|
||||
const char* best = nullptr;
|
||||
while (true)
|
||||
{
|
||||
const char* ret = internalPatternMatch(p, path);
|
||||
if (ret && ret > best) { best = ret; }
|
||||
if (*path == 0 || *path == '/') { break; }
|
||||
++path;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
else if (*p == '/' && *(p + 1) == '/')
|
||||
{ // the super-wildcard '//'
|
||||
while (*(p + 1) == '/') { ++p; }
|
||||
const char* best = nullptr;
|
||||
while (true)
|
||||
{
|
||||
const char* ret = internalPatternMatch(p, path);
|
||||
if (ret && ret > best) { best = ret; }
|
||||
if (*path == 0) { break; }
|
||||
if (*path == 0 || (path = strchr(path + 1, '/')) == nullptr) { break; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
else if (*p == '{')
|
||||
{ // braced list {foo,bar,baz}
|
||||
const char *end = strchr(p, '}'), *q;
|
||||
if (!end) { return nullptr; } // syntax error in brace list..
|
||||
bool match = false;
|
||||
do
|
||||
{
|
||||
++p;
|
||||
q = strchr(p, ',');
|
||||
if (q == nullptr || q > end) { q = end; }
|
||||
if (strncmp(p, path, q - p) == 0)
|
||||
{
|
||||
path += (q - p);
|
||||
p = end + 1;
|
||||
match = true;
|
||||
}
|
||||
else { p = q; }
|
||||
} while (q != end && !match);
|
||||
if (!match) { return pattern; }
|
||||
}
|
||||
else if (*p == *path)
|
||||
{
|
||||
++p;
|
||||
++path;
|
||||
} // any other character
|
||||
else { break; }
|
||||
pattern = p;
|
||||
}
|
||||
return (*path == 0 ? pattern : nullptr);
|
||||
}
|
||||
|
||||
inline bool partialPatternMatch(const std::string& pattern, const std::string& path)
|
||||
{
|
||||
const char* q = internalPatternMatch(pattern.c_str(), path.c_str());
|
||||
return q != nullptr;
|
||||
}
|
||||
|
||||
inline bool fullPatternMatch(const std::string& pattern, const std::string& path)
|
||||
{
|
||||
const char* q = internalPatternMatch(pattern.c_str(), path.c_str());
|
||||
return q && *q == 0;
|
||||
}
|
||||
} // namespace oscpkt
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
This file provides a dumb c++ wrapper for sending OSC packets over UDP.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2010 Julien Pommier
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
(this is the zlib license)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER) || defined(WIN32)
|
||||
/*
|
||||
if windows.h has been already included, be prepared for tons of
|
||||
compile errors. winsock2 must be included BEFORE windows.h . -- OR
|
||||
define WIN32_LEAN_AND_MEAN before the first #include <windows.h> to
|
||||
prevent it from including tons of crap (winsock.h etc)
|
||||
*/
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#if defined(_MSC_VER)
|
||||
# pragma comment(lib, "ws2_32.lib")
|
||||
#endif
|
||||
#else
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace oscpkt {
|
||||
|
||||
/** a wrapper class for holding an ip address, mostly used internnally */
|
||||
class SockAddr
|
||||
{
|
||||
union
|
||||
{
|
||||
sockaddr_storage ss; // hold an IPv4 or IPv6 address
|
||||
struct sockaddr sa;
|
||||
} addr_;
|
||||
|
||||
public:
|
||||
|
||||
struct sockaddr& addr() { return addr_.sa; }
|
||||
const struct sockaddr& addr() const { return addr_.sa; }
|
||||
size_t maxLen() const { return sizeof addr_; }
|
||||
|
||||
size_t actualLen() const
|
||||
{
|
||||
if (addr().sa_family == AF_UNSPEC) { return 0; }
|
||||
if (addr().sa_family == AF_INET) { return sizeof(struct sockaddr_in); }
|
||||
if (addr().sa_family == AF_INET6) { return sizeof(struct sockaddr_in6); }
|
||||
return sizeof addr_;
|
||||
}
|
||||
|
||||
SockAddr() { memset(&addr_, 0, sizeof addr_); }
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return addr().sa_family == AF_UNSPEC; /* this is the 0 value */
|
||||
}
|
||||
/** retrieve the current port number, -1 in case of error */
|
||||
int getPort() const
|
||||
{
|
||||
char servname[512];
|
||||
const int err = getnameinfo(&addr_.sa, sizeof addr_, nullptr, 0, servname, sizeof servname, NI_NUMERICSERV);
|
||||
return (err == 0 ? atoi(servname) : -1);
|
||||
}
|
||||
/* convert to a string representation (ip:port) */
|
||||
std::string asString() const
|
||||
{
|
||||
std::string s;
|
||||
if (addr().sa_family)
|
||||
{
|
||||
char hostname[512], servname[512];
|
||||
const int err = getnameinfo(&addr_.sa, sizeof addr_, hostname, sizeof hostname, servname, sizeof servname, NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if (err == 0)
|
||||
{
|
||||
s = hostname;
|
||||
s += ":";
|
||||
s += servname;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* NOTE: This breaks build with OpenViBE
|
||||
friend std::ostream &operator<<(std::ostream &os, const SockAddr &ip) {
|
||||
os << "[";
|
||||
switch (ip.addr().sa_family) {
|
||||
case AF_UNSPEC: os << "AF_UNSPEC"; break;
|
||||
case AF_INET: os << "IPv4"; break;
|
||||
case AF_INET6: os << "IPv6"; break;
|
||||
default: os << "unknown family '" << ip.addr().sa_family << "'"; break;
|
||||
}
|
||||
os << " " << ip.asString() << "]";
|
||||
return os;
|
||||
}*/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
just a wrapper over the classical socket stuff
|
||||
|
||||
should be robust, simple to use, IPv6 ready (avoids all deprecated
|
||||
stuff such as gethostbyname etc), and portable (mac/linux/windows)
|
||||
|
||||
Try to avoid sending packets larger than 8192 because some other
|
||||
implementation may truncate them (python's DatagramRequestHandler
|
||||
of OSC.py for example).
|
||||
*/
|
||||
struct UdpSocket
|
||||
{
|
||||
std::string error_message;
|
||||
int handle; /* the file descriptor for the socket */
|
||||
SockAddr local_addr /* initialised only for bound sockets */;
|
||||
SockAddr remote_addr; /* initialised for connected sockets. Also updated for bound sockets after each datagram received */
|
||||
|
||||
std::vector<char> buffer;
|
||||
|
||||
UdpSocket() : handle(-1)
|
||||
{
|
||||
#ifdef WIN32
|
||||
WSADATA wsa_data;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { setErr("winsock failed to initialise"); }
|
||||
#endif
|
||||
}
|
||||
|
||||
~UdpSocket()
|
||||
{
|
||||
close();
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (handle != -1)
|
||||
{
|
||||
#ifdef WIN32
|
||||
closesocket(handle);
|
||||
#else
|
||||
::close(handle);
|
||||
#endif
|
||||
handle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool isOk() const { return error_message.empty(); }
|
||||
const std::string& errorMessage() const { return error_message; }
|
||||
|
||||
bool isBound() const { return !local_addr.empty(); }
|
||||
int boundPort() const { return local_addr.getPort(); }
|
||||
|
||||
std::string boundPortAsString() const
|
||||
{
|
||||
char s[512];
|
||||
#ifndef _MSC_VER
|
||||
snprintf(s, 512, "%d", boundPort());
|
||||
#else
|
||||
_snprintf_s(s, 512, 512, "%d", boundPort());
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
int socketHandle() const { return handle; }
|
||||
|
||||
static std::string localHostName()
|
||||
{
|
||||
/* this stuff is not very nice but this is what liblo does in order to
|
||||
find out a sensible name for the local host */
|
||||
char hostname_buf[512];
|
||||
if (gethostname(hostname_buf, sizeof hostname_buf) != 0) { hostname_buf[0] = 0; }
|
||||
hostname_buf[sizeof hostname_buf - 1] = 0;
|
||||
struct hostent* host = gethostbyname(hostname_buf);
|
||||
if (host) { return host->h_name; }
|
||||
return hostname_buf[0] ? hostname_buf : "localhost";
|
||||
}
|
||||
|
||||
std::string localHostNameWithPort() const { return (localHostName() + ":") + boundPortAsString(); }
|
||||
|
||||
enum
|
||||
{
|
||||
OPTION_UNSPEC = 0,
|
||||
OPTION_FORCE_IPV4 = 1,
|
||||
OPTION_FORCE_IPV6 = 2,
|
||||
OPTION_DEFAULT = OPTION_FORCE_IPV4 // according to liblo's README, using ipv6 sockets causes issues with other non-ipv6 enabled osc software
|
||||
};
|
||||
|
||||
/** open the socket, and prepare for sending datagrams to the specified host:port */
|
||||
bool connectTo(const std::string& host, const std::string& port, const int options = OPTION_DEFAULT) { return openSocket(host, port, options); }
|
||||
|
||||
bool connectTo(const std::string& host, const int port, const int options = OPTION_DEFAULT) { return openSocket(host, port, options); }
|
||||
|
||||
void setErr(const std::string& msg) { if (error_message.empty()) { error_message = msg; } }
|
||||
|
||||
void* packetData() { return buffer.empty() ? nullptr : &buffer[0]; }
|
||||
size_t packetSize() const { return buffer.size(); }
|
||||
SockAddr& packetOrigin() { return remote_addr; }
|
||||
|
||||
bool sendPacket(const void* ptr, const size_t sz) { return sendPacketTo(ptr, sz, remote_addr); }
|
||||
|
||||
bool sendPacketTo(const void* ptr, const size_t sz, SockAddr& addr)
|
||||
{
|
||||
if (!isOk() || handle == -1)
|
||||
{
|
||||
setErr("not opened..");
|
||||
return false;
|
||||
}
|
||||
if (!ptr || sz == 0) { return false; }
|
||||
|
||||
int sent = 0;
|
||||
do
|
||||
{
|
||||
int res;
|
||||
errno = 0;
|
||||
if (isBound()) { res = sendto(handle, (const char*)ptr, int(sz), 0, &addr.addr(), int(addr.actualLen())); }
|
||||
else
|
||||
{
|
||||
res = send(handle, (const char*)ptr, int(sz), 0);
|
||||
// res = write(handle, ptr, sz);
|
||||
}
|
||||
#ifdef WIN32
|
||||
if (res == -1 && WSAGetLastError() == WSAEINTR) { continue; }
|
||||
sent = res;
|
||||
#else
|
||||
//if (res == -1) std::cerr << "sendto handle=" << handle << ", res:" << res << ", sz=" << sz << ", errno=" << errno << " " << strerror(errno) << "\n";
|
||||
if (res == -1 && errno == EINTR) continue;
|
||||
else sent = res;
|
||||
#endif
|
||||
} while (false);
|
||||
|
||||
return size_t(sent) == sz;
|
||||
}
|
||||
|
||||
private:
|
||||
bool openSocket(const std::string& hostname, const int port, const int options)
|
||||
{
|
||||
char port_string[64];
|
||||
#ifdef _MSC_VER
|
||||
_snprintf_s(port_string, 64, 64, "%d", port);
|
||||
#else
|
||||
snprintf(port_string, 64, "%d", port);
|
||||
#endif
|
||||
return openSocket(hostname, port_string, options);
|
||||
}
|
||||
|
||||
bool openSocket(const std::string& hostname, const std::string& port, const int options)
|
||||
{
|
||||
const bool binding = hostname.empty();
|
||||
close();
|
||||
error_message.clear();
|
||||
|
||||
struct addrinfo hints;
|
||||
struct addrinfo* result = nullptr;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
if (options == OPTION_FORCE_IPV4) { hints.ai_family = AF_INET; }
|
||||
else if (options == OPTION_FORCE_IPV6) { hints.ai_family = AF_INET6; }
|
||||
else { hints.ai_family = AF_UNSPEC; } // Allow IPv4 or IPv6 -- in case of problem, try with AF_INET ...
|
||||
hints.ai_socktype = SOCK_DGRAM; // Datagram socket
|
||||
hints.ai_flags = (binding ? AI_PASSIVE : 0); // AI_PASSIVE means socket address is intended for bind
|
||||
|
||||
const int err = getaddrinfo(binding ? nullptr : hostname.c_str(), port.empty() ? nullptr : port.c_str(), &hints, &result);
|
||||
if (err != 0)
|
||||
{
|
||||
setErr(gai_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct addrinfo* rp = result;
|
||||
for (; rp && handle == -1; rp = rp->ai_next)
|
||||
{
|
||||
handle = socket(rp->ai_family, rp->ai_socktype,
|
||||
rp->ai_protocol);
|
||||
if (handle == -1) { continue; }
|
||||
|
||||
if (binding)
|
||||
{
|
||||
if (bind(handle, rp->ai_addr, socklen_t(rp->ai_addrlen)) != 0) { close(); }
|
||||
else
|
||||
{
|
||||
socklen_t len = socklen_t(local_addr.maxLen());
|
||||
if (getsockname(handle, &local_addr.addr(), &len) == 0)
|
||||
{
|
||||
/* great */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connect(handle, rp->ai_addr, socklen_t(rp->ai_addrlen)) != 0) { close(); }
|
||||
else
|
||||
{
|
||||
assert(size_t(rp->ai_addrlen) <= sizeof remote_addr);
|
||||
memcpy(&remote_addr.addr(), rp->ai_addr, rp->ai_addrlen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
freeaddrinfo(result);
|
||||
result = nullptr;
|
||||
|
||||
if (!rp)
|
||||
{ // we failed miserably
|
||||
setErr(binding ? "bind failed" : "connect failed");
|
||||
assert(handle == -1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace oscpkt
|
||||
@@ -0,0 +1,173 @@
|
||||
#include "ovpCBoxAlgorithmOSCController.h"
|
||||
|
||||
// #include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
|
||||
bool CBoxAlgorithmOSCController::initialize()
|
||||
{
|
||||
const CString address = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
const uint64_t port = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
m_oscAddress = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
|
||||
const char* tmp = m_oscAddress.toASCIIString();
|
||||
if (!tmp || !tmp[0] || tmp[0] != '/')
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "OSC Address must start with a '/'\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect the socket
|
||||
const std::string str = std::string(address.toASCIIString());
|
||||
m_udpSocket.connectTo(str, uint32_t(port));
|
||||
|
||||
if (!m_udpSocket.isOk())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Error connecting to socket\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get appropriate decoder
|
||||
CIdentifier streamType;
|
||||
this->getStaticBoxContext().getInputType(0, streamType);
|
||||
|
||||
m_decoder = nullptr;
|
||||
if (this->getTypeManager().isDerivedFromStream(streamType,OV_TypeId_StreamedMatrix))
|
||||
{
|
||||
m_decoder = &this->getAlgorithmManager().getAlgorithm(
|
||||
this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixDecoder));
|
||||
}
|
||||
else if (streamType == OV_TypeId_Stimulations)
|
||||
{
|
||||
m_decoder = &this->getAlgorithmManager().getAlgorithm(
|
||||
this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationDecoder));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unsupported type\n";
|
||||
return false;
|
||||
}
|
||||
m_decoder->initialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOSCController::uninitialize()
|
||||
{
|
||||
if (m_udpSocket.isOk()) { m_udpSocket.close(); }
|
||||
|
||||
if (m_decoder)
|
||||
{
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_decoder);
|
||||
m_decoder = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOSCController::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOSCController::process()
|
||||
{
|
||||
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
CIdentifier streamType;
|
||||
this->getStaticBoxContext().getInputType(0, streamType);
|
||||
|
||||
oscpkt::PacketWriter pw;
|
||||
oscpkt::Message msg;
|
||||
bool haveData = false;
|
||||
|
||||
for (size_t j = 0; j < boxContext.getInputChunkCount(0); ++j)
|
||||
{
|
||||
if (this->getTypeManager().isDerivedFromStream(streamType,OV_TypeId_StreamedMatrix))
|
||||
{
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer(
|
||||
m_decoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
Kernel::TParameterHandler<const CMatrix*> op_pMatrix(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputParameterId_Matrix));
|
||||
|
||||
ip_buffer = boxContext.getInputChunk(0, j);
|
||||
m_decoder->process();
|
||||
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
// Check that the dimensions are acceptable
|
||||
const CMatrix* matrix = op_pMatrix;
|
||||
if (matrix->getDimensionCount() < 1 || matrix->getDimensionCount() > 2)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Only matrixes of 1 or 2 dimensions are supported\n";
|
||||
return false;
|
||||
}
|
||||
if (matrix->getDimensionCount() == 2 && matrix->getDimensionSize(0) != 1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The matrix should have only 1 channel. Use e.g. Channel Selector to prune\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!haveData)
|
||||
{
|
||||
haveData = true;
|
||||
pw.startBundle();
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < matrix->getBufferElementCount(); ++k)
|
||||
{
|
||||
const float inputVal = float(matrix->getBuffer()[k]);
|
||||
pw.addMessage(msg.init(m_oscAddress.toASCIIString()).pushFloat(inputVal));
|
||||
// std::cout << "Add float " << inputVal << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (streamType == OV_TypeId_Stimulations)
|
||||
{
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer(
|
||||
m_decoder->getInputParameter(OVP_GD_Algorithm_StimulationDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
const Kernel::TParameterHandler<const IStimulationSet*> op_pStimulationSet(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_StimulationDecoder_OutputParameterId_StimulationSet));
|
||||
|
||||
ip_buffer = boxContext.getInputChunk(0, j);
|
||||
m_decoder->process();
|
||||
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
if (!haveData)
|
||||
{
|
||||
haveData = true;
|
||||
pw.startBundle();
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < op_pStimulationSet->getStimulationCount(); ++k)
|
||||
{
|
||||
const uint64_t stimulus = op_pStimulationSet->getStimulationIdentifier(k);
|
||||
pw.addMessage(msg.init(m_oscAddress.toASCIIString()).pushInt32(int32_t(stimulus)));
|
||||
// std::cout << "Add stimulus " << stimulus << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unknown stream type\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(0, j);
|
||||
}
|
||||
|
||||
if (haveData)
|
||||
{
|
||||
pw.endBundle();
|
||||
if (!m_udpSocket.sendPacket(pw.packetData(), pw.packetSize())) { this->getLogManager() << Kernel::LogLevel_Warning << "Error sending out UDP packet\n"; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "oscpkt.h"
|
||||
#include "oscpkt_udp.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
/**
|
||||
* \class CBoxAlgorithmOSCController
|
||||
* \author Ozan Caglayan (Galatasaray University)
|
||||
* \date Thu May 8 20:57:24 2014
|
||||
* \brief The class CBoxAlgorithmOSCController describes the box OSC Controller.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmOSCController final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
//Here is the different process callbacks possible
|
||||
// - On new input received (the most common behaviour for signal processing) :
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
// As we do with any class in openvibe, we use the macro below
|
||||
// to associate this box to an unique identifier.
|
||||
// The inheritance information is also made available,
|
||||
// as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_OSCController)
|
||||
|
||||
private:
|
||||
|
||||
// Decodes the stream
|
||||
Kernel::IAlgorithmProxy* m_decoder = nullptr;
|
||||
|
||||
// UDP Socket (oscpkt_udp.h)
|
||||
oscpkt::UdpSocket m_udpSocket;
|
||||
|
||||
// OSC Address to some device
|
||||
CString m_oscAddress;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmOSCControllerDesc
|
||||
* \author Ozan Caglayan (Galatasaray University)
|
||||
* \date Thu May 8 20:57:24 2014
|
||||
* \brief Descriptor of the box OSC Controller.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmOSCControllerDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("OSC Controller"); }
|
||||
CString getAuthorName() const override { return CString("Ozan Caglayan"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Galatasaray University"); }
|
||||
// + Stimulation support & some code refactoring in v1.1 by Jussi T. Lindgren / Inria
|
||||
CString getShortDescription() const override { return CString("Sends OSC messages to an OSC controller"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"This box allows OpenViBE to send OSC (Open Sound Control) messages to an OSC server. See http://www.opensoundcontrol.org to learn about the OSC protocol and its use cases.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Acquisition and network IO"); }
|
||||
CString getVersion() const override { return CString("1.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-network"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_OSCController; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmOSCController; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input",OV_TypeId_Signal);
|
||||
|
||||
prototype.addInputSupport(OV_TypeId_Signal);
|
||||
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
|
||||
prototype.addInputSupport(OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
|
||||
|
||||
prototype.addSetting("OSC Server IP",OV_TypeId_String, "127.0.0.1");
|
||||
prototype.addSetting("OSC Server Port",OV_TypeId_Integer, "9001");
|
||||
prototype.addSetting("OSC Address",OV_TypeId_String, "/a/b/c");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_OSCControllerDesc)
|
||||
};
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,184 @@
|
||||
#if defined TARGET_HAS_ThirdPartyLSL
|
||||
|
||||
#include "ovpCBoxLSLExportGipsa.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::initialize()
|
||||
{
|
||||
m_inputChannel1.initialize(this);
|
||||
|
||||
m_streamName = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_streamType = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
m_outlet = nullptr;
|
||||
m_stims.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::uninitialize()
|
||||
{
|
||||
m_inputChannel1.uninitialize();
|
||||
m_stims.clear();
|
||||
delete m_outlet;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::process()
|
||||
{
|
||||
if (!m_inputChannel1.isWorking())
|
||||
{
|
||||
m_inputChannel1.waitForSignalHeader();
|
||||
|
||||
if (m_inputChannel1.isWorking())
|
||||
{
|
||||
try
|
||||
{
|
||||
//if it fails here then most likely you are using the wrong dll - e.x debug instead of release or vice-versa
|
||||
lsl::stream_info info(m_streamName.toASCIIString(), m_streamType.toASCIIString(), int(m_inputChannel1.getNChannels()) + 1,
|
||||
double(m_inputChannel1.getSamplingRate()), lsl::cf_float32);
|
||||
|
||||
lsl::xml_element channels = info.desc().append_child("channels");
|
||||
|
||||
for (size_t i = 0; i < m_inputChannel1.getNChannels(); ++i)
|
||||
{
|
||||
channels.append_child("channel")
|
||||
.append_child_value("label", m_inputChannel1.getChannelName(i))
|
||||
.append_child_value("type", "EEG")
|
||||
.append_child_value("unit", "microvolts");
|
||||
}
|
||||
|
||||
channels.append_child("channel")
|
||||
.append_child_value("label", "Stimulations")
|
||||
.append_child_value("type", "marker");
|
||||
|
||||
if (m_outlet != nullptr) { this->getLogManager() << Kernel::LogLevel_Error << "Possible double initialization!\n"; }
|
||||
|
||||
m_outlet = new lsl::stream_outlet(info); //here the length of the buffered signal can be specified
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not initialize LSL library: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//stimulations
|
||||
for (size_t i = 0; i < m_inputChannel1.getNStimulationBuffers(); ++i)
|
||||
{
|
||||
uint64_t tStart, tEnd;
|
||||
IStimulationSet* set = m_inputChannel1.getStimulation(tStart, tEnd, i);
|
||||
|
||||
for (size_t j = 0; j < set->getStimulationCount(); ++j)
|
||||
{
|
||||
uint64_t time = m_inputChannel1.getStartTimestamp() + set->getStimulationDate(j);
|
||||
const uint64_t identifier = set->getStimulationIdentifier(j);
|
||||
|
||||
|
||||
if (m_stims.empty())
|
||||
{
|
||||
m_stims.push_back(std::pair<float, uint64_t>(float(identifier), time));
|
||||
//std::cout<< "added: " << m_stims[m_stims.size()-1].first << " " << m_stims[m_stims.size()-1].second<< "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto last = m_stims[m_stims.size() - 1];
|
||||
if (last.first != identifier && last.second != time)
|
||||
{
|
||||
m_stims.push_back(std::pair<float, uint64_t>(float(identifier), time));
|
||||
//std::cout<< "added: " << m_stims[m_stims.size()-1].first << " " << m_stims[m_stims.size()-1].second<< "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout<< "duplicate: " << m_stims[m_stims.size()-1].first << " " << m_stims[m_stims.size()-1].second<< "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//signal
|
||||
for (size_t i = 0; i < m_inputChannel1.getNSignalBuffers(); ++i)
|
||||
{
|
||||
uint64_t tStart, tEnd;
|
||||
double* inputBuffer = m_inputChannel1.getSignal(tStart, tEnd, i);
|
||||
|
||||
if (inputBuffer)
|
||||
{
|
||||
const size_t samplesPerChannelInput = m_inputChannel1.getNSamples();
|
||||
std::vector<std::vector<float>> mychunk(samplesPerChannelInput);
|
||||
|
||||
for (size_t k = 0; k < samplesPerChannelInput; ++k) { mychunk[k] = std::vector<float>(m_inputChannel1.getNChannels() + 1); }
|
||||
|
||||
//Fill a matrix - OpenVibe provides the data ch1 (all values from all samples), ch2(all values from all samples) ... chN,
|
||||
//In the generated chunk every row is a single sample (containing the data from all channels) and every column number is the number of the channel
|
||||
for (size_t k = 0; k < m_inputChannel1.getNChannels(); ++k)
|
||||
{
|
||||
for (size_t j = 0; j < samplesPerChannelInput; ++j)
|
||||
{
|
||||
const size_t index = (k * samplesPerChannelInput) + j;
|
||||
mychunk[j][k] = float(inputBuffer[index]); // @note 64bit->32bit conversion
|
||||
}
|
||||
}
|
||||
|
||||
//Process stimulations and add them to the output in a dedicated channel
|
||||
std::vector<float> stimChan = std::vector<float>(samplesPerChannelInput);
|
||||
|
||||
auto it = m_stims.begin();
|
||||
while (it != m_stims.end())
|
||||
{
|
||||
auto current = *it;
|
||||
|
||||
if (!(current.second >= tStart && current.second <= tEnd))
|
||||
{
|
||||
// not in current time range, do not send now.
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
const uint64_t posCurrent = CTime(current.second).toSampleCount(m_inputChannel1.getSamplingRate());
|
||||
const uint64_t posStart = CTime(tStart).toSampleCount(m_inputChannel1.getSamplingRate());
|
||||
//uint64_t posEnd = CTime(tStart).toSampleCount(m_inputChannel1.getSamplingRate());
|
||||
|
||||
int pos = int(posCurrent) - int(posStart);
|
||||
if (pos < 0) { pos = 0; } //fix position
|
||||
if (pos == int(stimChan.size())) { pos = int(stimChan.size() - 1); } //fix position
|
||||
|
||||
if (pos >= 0 && pos < int(stimChan.size()))
|
||||
{
|
||||
stimChan[pos] = float(current.first);
|
||||
//std::cout<< "pos relative: " << pos << " value: " << stim_chan[pos] << " time:" << CTime(current.second).toSeconds()<< "\n";
|
||||
}
|
||||
else { this->getLogManager() << Kernel::LogLevel_Warning << "Bad stimulation position: " << pos << "stim code: " << current.first << "\n"; }
|
||||
|
||||
// processed, erase
|
||||
it = m_stims.erase(it);
|
||||
}
|
||||
|
||||
//add the stim channel at the end of the matrix
|
||||
const size_t k = m_inputChannel1.getNChannels();
|
||||
for (size_t j = 0; j < samplesPerChannelInput; ++j) { mychunk[j][k] = stimChan[j]; }
|
||||
|
||||
//send all channels
|
||||
m_outlet->push_chunk(mychunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyLSL
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <vector>
|
||||
|
||||
#include "../ovpCInputChannel.h"
|
||||
#include <lsl_cpp.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
class CBoxAlgorithmLSLExportGipsa final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
CBoxAlgorithmLSLExportGipsa() : m_inputChannel1(0) {}
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsa)
|
||||
|
||||
protected:
|
||||
|
||||
int64_t m_decimationFactor = 0;
|
||||
uint64_t m_outputSampling = 0;
|
||||
CString m_streamName;
|
||||
CString m_streamType;
|
||||
|
||||
SignalProcessing::CInputChannel m_inputChannel1;
|
||||
|
||||
lsl::stream_outlet* m_outlet = nullptr;
|
||||
std::vector<std::pair<float, uint64_t>> m_stims;//identifier,time
|
||||
};
|
||||
|
||||
class CBoxAlgorithmLSLExportGipsaDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("LSL Export (Gipsa)"); }
|
||||
CString getAuthorName() const override { return CString("Anton Andreev"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Gipsa-lab"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Streams signal outside OpenVibe using Lab Streaming Layer library"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("More on how to read the signal in your application: https://code.google.com/p/labstreaminglayer/");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Acquisition and network IO"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-connect"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsa; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmLSLExportGipsa; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input signal", OV_TypeId_Signal);
|
||||
prototype.addInput("Input stimulations", OV_TypeId_Stimulations);
|
||||
prototype.addSetting("Stream name", OV_TypeId_String, "OpenViBE Stream");
|
||||
prototype.addSetting("Stream type", OV_TypeId_String, "EEG");
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsaDesc)
|
||||
};
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
#include "ovpCInputChannel.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CInputChannel::initialize(Toolkit::TBoxAlgorithm<IBoxAlgorithm>* boxAlgorithm)
|
||||
{
|
||||
m_isWorking = false;
|
||||
|
||||
m_startTimestamp = 0;
|
||||
m_endTimestamp = 0;
|
||||
|
||||
m_stimulationSet = nullptr;
|
||||
m_boxAlgorithm = boxAlgorithm;
|
||||
|
||||
m_signalDecoder = new Toolkit::TSignalDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>();
|
||||
m_signalDecoder->initialize(*m_boxAlgorithm, 0);
|
||||
|
||||
m_stimDecoder = new Toolkit::TStimulationDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>();
|
||||
m_stimDecoder->initialize(*m_boxAlgorithm, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CInputChannel::uninitialize() const
|
||||
{
|
||||
m_stimDecoder->uninitialize();
|
||||
delete m_stimDecoder;
|
||||
|
||||
m_signalDecoder->uninitialize();
|
||||
delete m_signalDecoder;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CInputChannel::waitForSignalHeader()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
|
||||
if (boxContext.getInputChunkCount(m_signalChannel))
|
||||
{
|
||||
m_signalDecoder->decode(0);
|
||||
|
||||
if (m_signalDecoder->isHeaderReceived())
|
||||
{
|
||||
m_isWorking = true;
|
||||
|
||||
m_startTimestamp = boxContext.getInputChunkStartTime(m_signalChannel, 0);
|
||||
m_endTimestamp = boxContext.getInputChunkEndTime(m_signalChannel, 0);
|
||||
|
||||
boxContext.markInputAsDeprecated(m_signalChannel, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IStimulationSet* CInputChannel::getStimulation(uint64_t& startTime, uint64_t& endTime, const size_t index)
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
|
||||
m_stimDecoder->decode(index);
|
||||
m_stimulationSet = m_stimDecoder->getOutputStimulationSet();
|
||||
|
||||
startTime = boxContext.getInputChunkStartTime(m_stimulationChannel, index);
|
||||
endTime = boxContext.getInputChunkEndTime(m_stimulationChannel, index);
|
||||
|
||||
boxContext.markInputAsDeprecated(m_stimulationChannel, index);
|
||||
|
||||
return m_stimulationSet;
|
||||
}
|
||||
|
||||
IStimulationSet* CInputChannel::discardStimulation(const size_t index)
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
|
||||
m_stimDecoder->decode(index);
|
||||
m_stimulationSet = m_stimDecoder->getOutputStimulationSet();
|
||||
|
||||
boxContext.markInputAsDeprecated(m_stimulationChannel, index);
|
||||
|
||||
return m_stimulationSet;
|
||||
}
|
||||
|
||||
|
||||
double* CInputChannel::getSignal(uint64_t& startTime, uint64_t& endTime, const size_t index) const
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
m_signalDecoder->decode(index);
|
||||
if (!m_signalDecoder->isBufferReceived()) { return nullptr; }
|
||||
|
||||
startTime = boxContext.getInputChunkStartTime(m_signalChannel, index);
|
||||
endTime = boxContext.getInputChunkEndTime(m_signalChannel, index);
|
||||
|
||||
boxContext.markInputAsDeprecated(m_signalChannel, index);
|
||||
|
||||
return m_signalDecoder->getOutputMatrix()->getBuffer();
|
||||
}
|
||||
|
||||
|
||||
double* CInputChannel::discardSignal(const size_t index) const
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
m_signalDecoder->decode(index);
|
||||
if (!m_signalDecoder->isBufferReceived()) { return nullptr; }
|
||||
|
||||
boxContext.markInputAsDeprecated(m_signalChannel, index);
|
||||
|
||||
return m_signalDecoder->getOutputMatrix()->getBuffer();
|
||||
}
|
||||
|
||||
#if 0
|
||||
void CInputChannel::copyData(const bool copyFirstBlock, size_t index)
|
||||
{
|
||||
CMatrix*& matrixBuffer = m_oMatrixBuffer[index & 1];
|
||||
|
||||
double* srcData = m_signalDecoder->getOutputMatrix()->getBuffer() + (copyFirstBlock ? 0 : m_firstBlock);
|
||||
double* dstData = matrixBuffer->getBuffer() + (copyFirstBlock ? m_secondBlock : 0);
|
||||
size_t size = (copyFirstBlock ? m_firstBlock : m_secondBlock)*sizeof(double);
|
||||
|
||||
for (size_t i=0; i < m_nChannels; i++, srcData += m_nSamples, dstData += m_nSamples) { System::Memory::copy(dstData, srcData, size); }
|
||||
}
|
||||
#endif
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
// @author Gipsa-lab
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
/**
|
||||
Use this class to receive send and stimulations channels
|
||||
*/
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CInputChannel
|
||||
{
|
||||
typedef enum
|
||||
{
|
||||
SIGNAL_CHANNEL,
|
||||
STIMULATION_CHANNEL,
|
||||
NB_CHANNELS,
|
||||
} channel_t;
|
||||
|
||||
public:
|
||||
|
||||
explicit CInputChannel(const uint16_t index = 0)
|
||||
: m_signalChannel(index * NB_CHANNELS + SIGNAL_CHANNEL), m_stimulationChannel(index * NB_CHANNELS + STIMULATION_CHANNEL) {}
|
||||
|
||||
~CInputChannel() { }
|
||||
|
||||
bool initialize(Toolkit::TBoxAlgorithm<IBoxAlgorithm>* boxAlgorithm);
|
||||
bool uninitialize() const;
|
||||
|
||||
bool isLastChannel(const size_t index) const { return index == m_stimulationChannel; }
|
||||
bool isWorking() const { return m_isWorking; }
|
||||
bool waitForSignalHeader();
|
||||
size_t getNStimulationBuffers() const { return m_boxAlgorithm->getDynamicBoxContext().getInputChunkCount(m_stimulationChannel); }
|
||||
size_t getNSignalBuffers() const { return m_boxAlgorithm->getDynamicBoxContext().getInputChunkCount(m_signalChannel); }
|
||||
IStimulationSet* getStimulation(uint64_t& startTime, uint64_t& endTime, size_t index);
|
||||
IStimulationSet* discardStimulation(size_t index);
|
||||
double* getSignal(uint64_t& startTime, uint64_t& endTime, size_t index) const;
|
||||
double* discardSignal(size_t index) const;
|
||||
uint64_t getSamplingRate() const { return m_signalDecoder->getOutputSamplingRate(); }
|
||||
size_t getNChannels() const { return m_signalDecoder->getOutputMatrix()->getDimensionSize(0); }
|
||||
size_t getNSamples() const { return m_signalDecoder->getOutputMatrix()->getDimensionSize(1); }
|
||||
uint64_t getStartTimestamp() const { return m_startTimestamp; }
|
||||
uint64_t getEndTimestamp() const { return m_endTimestamp; }
|
||||
const char* getChannelName(const size_t index) const { return m_signalDecoder->getOutputMatrix()->getDimensionLabel(0, index); }
|
||||
const Kernel::TParameterHandler<CMatrix*>& getOpMatrix() const { return m_signalDecoder->getOutputMatrix(); }
|
||||
|
||||
protected:
|
||||
size_t m_signalChannel = 0;
|
||||
size_t m_stimulationChannel = 0;
|
||||
bool m_isWorking = false;
|
||||
|
||||
uint64_t m_startTimestamp = 0;
|
||||
uint64_t m_endTimestamp = 0;
|
||||
|
||||
IStimulationSet* m_stimulationSet = nullptr;
|
||||
|
||||
// parent memory
|
||||
Toolkit::TBoxAlgorithm<IBoxAlgorithm>* m_boxAlgorithm = nullptr;
|
||||
|
||||
// signal section
|
||||
//Kernel::IAlgorithmProxy* m_signalDecoder;
|
||||
Toolkit::TSignalDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>* m_signalDecoder = nullptr;
|
||||
|
||||
// stimulation section
|
||||
Toolkit::TStimulationDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>* m_stimDecoder = nullptr;
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_OSCController OpenViBE::CIdentifier(0xC66F2F0C, 0x3BA5B424)
|
||||
#define OVP_ClassId_BoxAlgorithm_OSCControllerDesc OpenViBE::CIdentifier(0xF7A35BD7, 0x6331C7D9)
|
||||
#define OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsa OpenViBE::CIdentifier(0x591D2E94, 0x221C23AD)
|
||||
#define OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsaDesc OpenViBE::CIdentifier(0x22AF11F5, 0x58F2787D)
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||