This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,88 @@
#include "CBoxAlgorithmCovarianceMatrixCalculator.hpp"
#include "utils/misc.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixCalculator::initialize()
{
m_i0SignalCodec.initialize(*this, 0);
m_o0MatrixCodec.initialize(*this, 0);
m_iMatrix = m_i0SignalCodec.getOutputMatrix();
m_oMatrix = m_o0MatrixCodec.getInputMatrix();
//***** Settings *****
m_est = Geometry::EEstimator(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0)));
m_center = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2)));
this->getLogManager() << m_logLevel << toString(m_est) << " Estimator" << (m_center ? ", Center Data " : "") << "\n";
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixCalculator::uninitialize()
{
m_i0SignalCodec.uninitialize();
m_o0MatrixCodec.uninitialize();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixCalculator::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixCalculator::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_i0SignalCodec.decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_iMatrix->getDimensionCount() == 2, "Invalid Input Signal", Kernel::ErrorType::BadInput);
const uint64_t tStart = boxContext.getInputChunkStartTime(0, i), // Time Code Chunk Start
tEnd = boxContext.getInputChunkEndTime(0, i); // Time Code Chunk End
const auto nChannels = size_t(m_iMatrix->getDimensionSize(0));
if (m_i0SignalCodec.isHeaderReceived()) // Header received
{
m_oMatrix->resize(nChannels, nChannels); // Update Size and set to 0
m_oMatrix->setNumLabels(); // Change label to have 1 to N label on each dim
m_o0MatrixCodec.encodeHeader(); // Header encoded
}
else if (m_i0SignalCodec.isBufferReceived()) // Buffer received
{
OV_ERROR_UNLESS_KRF(covarianceMatrix(), "Covariance Matrix Processing Error", Kernel::ErrorType::BadProcessing); // Compute Covariance
m_o0MatrixCodec.encodeBuffer(); // Buffer encoded
}
else if (m_i0SignalCodec.isEndReceived()) { m_o0MatrixCodec.encodeEnd(); } // End receivded and encoded
boxContext.markOutputAsReadyToSend(0, tStart, tEnd); // Makes the output available
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixCalculator::covarianceMatrix() const
{
Eigen::MatrixXd mS, mCov;
if (!MatrixConvert(*m_iMatrix, mS)) { return false; }
const Geometry::EStandardization s = m_center ? Geometry::EStandardization::Center : Geometry::EStandardization::None;
if (!CovarianceMatrix(mS, mCov, m_est, s)) { return false; }
if (!MatrixConvert(mCov, *m_oMatrix)) { return false; }
return true;
}
//---------------------------------------------------------------------------------------------------
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,93 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmCovarianceMatrixCalculator.hpp
/// \brief Class of the box computing the covariance matrix
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 16/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
# pragma once
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <geometry/Covariance.hpp>
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
/// <summary> The class CBoxAlgorithmCovarianceMatrixCalculator describes the box Covariance Matrix Calculator. </summary>
class CBoxAlgorithmCovarianceMatrixCalculator final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, ClassId_BoxAlgorithm_CovarianceMatrixCalculator)
protected:
bool covarianceMatrix() const;
//***** Codecs *****
Toolkit::TSignalDecoder<CBoxAlgorithmCovarianceMatrixCalculator> m_i0SignalCodec; // Input Signal Codec
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmCovarianceMatrixCalculator> m_o0MatrixCodec; // Output Matrix Codec
//***** Matrices *****
CMatrix* m_iMatrix = nullptr; // Input Matrix pointer
CMatrix* m_oMatrix = nullptr; // Output Matrix pointer
//***** Settings *****
Geometry::EEstimator m_est = Geometry::EEstimator::COV; // Covariance Estimator
bool m_center = true; // Center data
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; // Log Level
};
/// <summary> Descriptor of the box Covariance Matrix Calculator. </summary>
class CBoxAlgorithmCovarianceMatrixCalculatorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Covariance Matrix Calculator"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Calculation of the covariance matrix of the input signal."; }
CString getDetailedDescription() const override
{
return "Calculation of the covariance matrix of the input signal.\nReturns a covariance matrix of size NxN per each input chunk. Where N is the number of channels.";
}
CString getCategory() const override { return "Riemannian Geometry"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-execute"; }
CIdentifier getCreatedClass() const override { return ClassId_BoxAlgorithm_CovarianceMatrixCalculator; }
IPluginObject* create() override { return new CBoxAlgorithmCovarianceMatrixCalculator; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input Signal", OV_TypeId_Signal);
prototype.addOutput("Output Covariance Matrix", OV_TypeId_StreamedMatrix);
prototype.addSetting("Estimator", TypeId_Estimator, toString(Geometry::EEstimator::COV).c_str());
prototype.addSetting("Center Data", OV_TypeId_Boolean, "true");
prototype.addSetting("Log Level", OV_TypeId_LogLevel, "Information");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, ClassId_BoxAlgorithm_CovarianceMatrixCalculatorDesc)
};
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,134 @@
#include "CBoxAlgorithmCovarianceMatrixToFeatureVector.hpp"
#include "utils/misc.hpp"
#include <fstream>
#include "geometry/Basics.hpp"
#include "geometry/Featurization.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixToFeatureVector::initialize()
{
//***** Codec Initialization *****
m_i0MatrixCodec.initialize(*this, 0);
m_o0FeatureCodec.initialize(*this, 0);
m_iMatrix = m_i0MatrixCodec.getOutputMatrix();
m_oMatrix = m_o0FeatureCodec.getInputMatrix();
//***** Settings Initialization *****
m_tangentSpace = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2)));
if (m_tangentSpace)
{
this->getLogManager() << m_logLevel << "Tangent Space\n";
OV_ERROR_UNLESS_KRF(initRef(), "Error Reference Matrix Creation", Kernel::ErrorType::BadSetting);
}
else { this->getLogManager() << m_logLevel << "Squeeze Upper Matrix\n"; }
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixToFeatureVector::uninitialize()
{
m_i0MatrixCodec.uninitialize();
m_o0FeatureCodec.uninitialize();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixToFeatureVector::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixToFeatureVector::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_i0MatrixCodec.decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_iMatrix->getDimensionCount() == 2 && m_iMatrix->getDimensionSize(0) == m_iMatrix->getDimensionSize(1),
"Invalid Input Signal", Kernel::ErrorType::BadInput);
const size_t nChannels = size_t(m_iMatrix->getDimensionSize(0));
const uint64_t tStart = boxContext.getInputChunkStartTime(0, i), // Time Code Chunk Start
tEnd = boxContext.getInputChunkEndTime(0, i); // Time Code Chunk End
if (m_i0MatrixCodec.isHeaderReceived()) // Header received
{
m_oMatrix->resize(nChannels * (nChannels + 1) / 2); // Update Size and set to 0
m_o0FeatureCodec.encodeHeader(); // Header encoded
}
else if (m_i0MatrixCodec.isBufferReceived()) // Buffer received
{
OV_ERROR_UNLESS_KRF(featurization(), "Featurization Processing Error", Kernel::ErrorType::BadProcessing); // Transformation
m_o0FeatureCodec.encodeBuffer(); // Buffer encoded
}
else if (m_i0MatrixCodec.isEndReceived()) { m_o0FeatureCodec.encodeEnd(); } // End receivded and encoded
boxContext.markOutputAsReadyToSend(0, tStart, tEnd); // Makes the output available
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixToFeatureVector::featurization() const
{
Eigen::MatrixXd cov;
Eigen::RowVectorXd v;
if (!MatrixConvert(*m_iMatrix, cov)) { return false; }
if (!Geometry::Featurization(cov, v, m_tangentSpace, m_ref)) { return false; }
if (!MatrixConvert(v, *m_oMatrix)) { return false; }
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMatrixToFeatureVector::initRef()
{
//***** Open the CSV *****
const CString name = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
if (name.length() == 0)
{
this->getLogManager() << m_logLevel << "Empty reference Matrix\n";
return true;
}
std::ifstream file(name, std::ifstream::in);
OV_ERROR_UNLESS_KRF(file.is_open(),
"Error opening file [" << name << "] for reading", Kernel::ErrorType::BadFileRead);
//***** Parse the CSV *****
std::string line;
getline(file, line); // Header
getline(file, line); // matrix line
std::vector<std::string> data = Geometry::Split(line, ",");
//***** Transform to MatrixXd *****
const auto first = data.begin() + 2, last = data.end() - 3;
const std::vector<std::string> mat(first, last);
const size_t n = size_t(sqrt(mat.size()));
OV_ERROR_UNLESS_KRF(n*n == mat.size(), "Error Reference Matrix Format", Kernel::ErrorType::BadFileParsing);
m_ref.resize(n, n);
size_t idx = 0;
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < n; ++j) { m_ref(i, j) = stod(mat[idx++]); } }
//***** Log Information *****
this->getLogManager() << m_logLevel << "REF Matrix : \n" << Geometry::MatrixPrint(m_ref) << "\n";
//***** Close the CSV *****
file.close();
return true;
}
//---------------------------------------------------------------------------------------------------
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,93 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmCovarianceMatrixToFeatureVector.hpp
/// \brief Class of the box computing the Feature vector with the covariance matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 17/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <Eigen/Dense>
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
/// <summary> The class CBoxAlgorithmCovarianceMatrixToFeatureVector describes the box Covariance Matrix To Feature Vector. </summary>
class CBoxAlgorithmCovarianceMatrixToFeatureVector final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, ClassId_BoxAlgorithm_CovarianceMatrixToFeatureVector)
protected:
bool featurization() const;
bool initRef();
//***** Codecs *****
Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmCovarianceMatrixToFeatureVector> m_i0MatrixCodec; // Input Matrix Codec
Toolkit::TFeatureVectorEncoder<CBoxAlgorithmCovarianceMatrixToFeatureVector> m_o0FeatureCodec; // Output Feature Codec
//***** Matrices *****
CMatrix* m_iMatrix = nullptr; // Input Matrix pointer
CMatrix* m_oMatrix = nullptr; // Output Matrix pointer
//***** Settings *****
bool m_tangentSpace = true; // Method to use (only tangent or squeeze now)
Eigen::MatrixXd m_ref; // Reference matrix for tangent space compute
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; // Log Level
};
/// <summary> Descriptor of the box Covariance Matrix To Feature Vector. </summary>
class CBoxAlgorithmCovarianceMatrixToFeatureVectorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Covariance Matrix To Feature Vector"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Transforms a covariance matrix into a feature std::vector"; }
CString getDetailedDescription() const override
{
return "Transforms a covariance matrix (size : NxN) into a feature std::vector (size : N(N+1)/2).\nThe Setting Tangent Space define if the transformation into std::vector is done in the tanget space or if it is a squeeze of the upper triangular matrix";
}
CString getCategory() const override { return "Riemannian Geometry"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-jump-to"; }
CIdentifier getCreatedClass() const override { return ClassId_BoxAlgorithm_CovarianceMatrixToFeatureVector; }
IPluginObject* create() override { return new CBoxAlgorithmCovarianceMatrixToFeatureVector; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input Covariance Matrix",OV_TypeId_StreamedMatrix);
prototype.addOutput("Output Feature Vector",OV_TypeId_FeatureVector);
prototype.addSetting("Tangent Space", OV_TypeId_Boolean, "true");
prototype.addSetting("Filename to Reference Matrix (CSV, empty for Identity)", OV_TypeId_Filename, "${Player_ScenarioDirectory}/Mean.csv");
prototype.addSetting("Log Level", OV_TypeId_LogLevel, "Information");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, ClassId_BoxAlgorithm_CovarianceMatrixToFeatureVectorDesc)
};
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,171 @@
#include "CBoxAlgorithmCovarianceMeanCalculator.hpp"
#include <geometry/Mean.hpp>
#include "utils/misc.hpp"
#include <fstream>
#include "geometry/Basics.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMeanCalculator::initialize()
{
// Stimulations
m_i0StimulationCodec.initialize(*this, 0);
m_iStimulation = m_i0StimulationCodec.getOutputStimulationSet();
// Classes
const Kernel::IBox& boxContext = this->getStaticBoxContext();
m_nbClass = size_t(boxContext.getInputCount() - 1);
m_i1MatrixCodec.resize(m_nbClass);
m_iMatrix.resize(m_nbClass);
for (size_t k = 0; k < m_nbClass; ++k)
{
m_i1MatrixCodec[k].initialize(*this, k + 1);
m_iMatrix[k] = m_i1MatrixCodec[k].getOutputMatrix();
}
m_o0MatrixCodec.initialize(*this, 0);
m_oMatrix = m_o0MatrixCodec.getInputMatrix();
// Settings
m_metric = Geometry::EMetric(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0)));
m_filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_stimulationName = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3)));
this->getLogManager() << m_logLevel << toString(m_metric) << " Metric\n";
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMeanCalculator::uninitialize()
{
this->getLogManager() << m_logLevel << m_covs.size() << " Matrices Registered, Mean Matrix : \n" << Geometry::MatrixPrint(m_mean) << "\n";
m_i0StimulationCodec.uninitialize();
for (auto& codec : m_i1MatrixCodec) { codec.uninitialize(); }
m_i1MatrixCodec.clear();
m_iMatrix.clear();
m_covs.clear();
m_o0MatrixCodec.uninitialize();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMeanCalculator::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMeanCalculator::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
//***** Stimulations *****
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_i0StimulationCodec.decode(i); // Decode the chunk
if (m_i0StimulationCodec.isBufferReceived()) // Buffer received
{
for (size_t j = 0; j < m_iStimulation->getStimulationCount(); ++j)
{
if (m_iStimulation->getStimulationIdentifier(j) == m_stimulationName)
{
OV_ERROR_UNLESS_KRF(Mean(m_covs, m_mean, m_metric), "Mean Compute Error", Kernel::ErrorType::BadProcessing); // Compute the mean
MatrixConvert(m_mean, *m_oMatrix);
const uint64_t tStart = boxContext.getInputChunkStartTime(0, i),// Time Code Chunk Start
tEnd = boxContext.getInputChunkEndTime(0, i); // Time Code Chunk End
m_o0MatrixCodec.encodeBuffer(); // Buffer encoded
boxContext.markOutputAsReadyToSend(0, tStart, tEnd); // Makes the output available
OV_ERROR_UNLESS_KRF(saveCSV(), "CSV Writing Error", Kernel::ErrorType::BadFileWrite);
}
}
}
}
//***** Matrix *****
for (size_t k = 0; k < m_nbClass; ++k)
{
for (size_t i = 0; i < boxContext.getInputChunkCount(k + 1); ++i)
{
m_i1MatrixCodec[k].decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_iMatrix[k]->getDimensionCount() == 2, "Invalid Input Signal", Kernel::ErrorType::BadInput);
if (m_i1MatrixCodec[k].isHeaderReceived() && k == 0) // First Header received
{
const uint64_t tStart = boxContext.getInputChunkStartTime(1, i), // Time Code Chunk Start
tEnd = boxContext.getInputChunkEndTime(1, i); // Time Code Chunk End
const size_t n = m_iMatrix[0]->getDimensionSize(0);
m_oMatrix->resize(n, n); // Update Size and set to 0
m_oMatrix->setNumLabels();
m_o0MatrixCodec.encodeHeader(); // Header encoded
boxContext.markOutputAsReadyToSend(0, tStart, tEnd); // Makes the output available
}
else if (m_i1MatrixCodec[k].isBufferReceived()) // Buffer received
{
Eigen::MatrixXd cov;
MatrixConvert(*m_iMatrix[k], cov);
m_covs.push_back(cov);
}
else if (m_i1MatrixCodec[k].isEndReceived() && k == 0) // First End received
{
const uint64_t tStart = boxContext.getInputChunkStartTime(1, i), // Time Code Chunk Start
tEnd = boxContext.getInputChunkEndTime(1, i); // Time Code Chunk End
m_o0MatrixCodec.encodeEnd();
boxContext.markOutputAsReadyToSend(0, tStart, tEnd); // Makes the output available
}
}
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMeanCalculator::saveCSV()
{
if (m_filename.length() == 0) { return true; }
std::ofstream file;
file.open(m_filename.toASCIIString(), std::ios::trunc);
OV_ERROR_UNLESS_KRF(file.is_open(),
"Error opening file [" << m_filename << "] for writing", Kernel::ErrorType::BadFileWrite);
// Header
const size_t s = m_mean.rows();
file << "Time:" << s << "x" << s << ",End Time,";
for (size_t i = 1; i <= s; ++i) { for (size_t j = 0; j < s; ++j) { file << i << ":,"; } }
file << "Event Id,Event Date,Event Duration\n";
// Matrix
file << "0.0000000000,0.0000000000,"; // Time
const Eigen::IOFormat fmt(Eigen::FullPrecision, 0, ", ", ", ", "", "", "", ",,,\n");
file << m_mean.format(fmt);
file.close();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmCovarianceMeanCalculatorListener::onInputAdded(Kernel::IBox& box, const size_t index)
{
box.setInputType(index, OV_TypeId_StreamedMatrix);
std::stringstream name;
name << "Input Covariance Matrix " << index;
box.setInputName(index, name.str().c_str());
return true;
}
//---------------------------------------------------------------------------------------------------
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,119 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmCovarianceMeanCalculator.hpp
/// \brief Class of the box computing the mean of the covariance matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 12/11/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <Eigen/Dense>
#include <geometry/Metrics.hpp>
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
/// <summary> The class CBoxAlgorithmCovarianceMeanCalculator describes the box Covariance Mean Calculator. </summary>
class CBoxAlgorithmCovarianceMeanCalculator final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, ClassId_BoxAlgorithm_CovarianceMeanCalculator)
protected:
//***** Codecs *****
Toolkit::TStimulationDecoder<CBoxAlgorithmCovarianceMeanCalculator> m_i0StimulationCodec; // Input Stimulation Codec
std::vector<Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmCovarianceMeanCalculator>> m_i1MatrixCodec; // Input Signal Codec
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmCovarianceMeanCalculator> m_o0MatrixCodec; // Output Codec
//***** Matrices *****
size_t m_nbClass = 1; // Number of input classes
std::vector<CMatrix*> m_iMatrix; // Input Matrix pointer
CMatrix* m_oMatrix = nullptr; // Output Matrix pointer
std::vector<Eigen::MatrixXd> m_covs; // List of Covariance Matrix
Eigen::MatrixXd m_mean; // Mean
Geometry::EMetric m_metric = Geometry::EMetric::Euclidian; // Metric Used
//***** Settings *****
IStimulationSet* m_iStimulation = nullptr; // Stimulation receiver
uint64_t m_stimulationName = OVTK_StimulationId_TrainCompleted; // Name of stimulation to check
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; // Log Level
// File
CString m_filename;
bool saveCSV();
};
/// <summary> Listener of the box Covariance Mean Calculator. </summary>
class CBoxAlgorithmCovarianceMeanCalculatorListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override;
bool onInputRemoved(Kernel::IBox& box, const size_t index) override { return true; }
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
/// <summary> Descriptor of the box Covariance Mean Calculator. </summary>
class CBoxAlgorithmCovarianceMeanCalculatorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Covariance Mean Calculator"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Calculation of the mean of covariance matrix."; }
CString getDetailedDescription() const override
{
return "Calculation of the mean of covariance matrix.\nThe Calculation is done when \"OVTK_StimulationId_TrainCompleted\" is received.\nThe Mean is saved in a CSV File.";
}
CString getCategory() const override { return "Riemannian Geometry"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-execute"; }
CIdentifier getCreatedClass() const override { return ClassId_BoxAlgorithm_CovarianceMeanCalculator; }
IPluginObject* create() override { return new CBoxAlgorithmCovarianceMeanCalculator; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmCovarianceMeanCalculatorListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input Stimulation", OV_TypeId_Stimulations);
prototype.addInput("Input Covariance Matrix 1", OV_TypeId_StreamedMatrix);
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addOutput("Output Mean Matrix", OV_TypeId_StreamedMatrix);
prototype.addSetting("Metric", TypeId_Metric, toString(Geometry::EMetric::Riemann).c_str());
prototype.addSetting("Filename to save Matrix (CSV, empty to not save)",OV_TypeId_Filename, "${Player_ScenarioDirectory}/Mean.csv");
prototype.addSetting("Stimulation name that triggers the compute",OV_TypeId_Stimulation, "OVTK_StimulationId_TrainCompleted");
prototype.addSetting("Log Level", OV_TypeId_LogLevel, "Information");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, ClassId_BoxAlgorithm_CovarianceMeanCalculatorDesc)
};
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,134 @@
#include "CBoxAlgorithmFeatureVectorToCovarianceMatrix.hpp"
#include "utils/misc.hpp"
#include <fstream>
#include "geometry/Basics.hpp"
#include "geometry/Featurization.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeatureVectorToCovarianceMatrix::initialize()
{
//***** Codec Initialization *****
m_featureDecoder.initialize(*this, 0);
m_matrixEncoder.initialize(*this, 0);
m_iMatrix = m_featureDecoder.getOutputMatrix();
m_oMatrix = m_matrixEncoder.getInputMatrix();
//***** Settings Initialization *****
m_tangentSpace = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2)));
if (m_tangentSpace)
{
this->getLogManager() << m_logLevel << "Tangent Space\n";
OV_ERROR_UNLESS_KRF(initRef(), "Error Reference Matrix Creation", Kernel::ErrorType::BadSetting);
}
else { this->getLogManager() << m_logLevel << "Squeeze Upper Matrix\n"; }
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeatureVectorToCovarianceMatrix::uninitialize()
{
m_matrixEncoder.uninitialize();
m_featureDecoder.uninitialize();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeatureVectorToCovarianceMatrix::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeatureVectorToCovarianceMatrix::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_featureDecoder.decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_iMatrix->getDimensionCount() == 1, "Invalid Input Signal", Kernel::ErrorType::BadInput);
const uint64_t start = boxContext.getInputChunkStartTime(0, i), // Time Code Chunk Start
end = boxContext.getInputChunkEndTime(0, i); // Time Code Chunk End
const size_t nChannels = size_t(m_iMatrix->getDimensionSize(0));
const size_t nDim = int((sqrt(1 + 8 * nChannels) - 1) / 2);
if (m_featureDecoder.isHeaderReceived()) // Header received
{
m_oMatrix->resize(nDim, nDim); // Update Size and set to 0
m_oMatrix->setNumLabels();
m_matrixEncoder.encodeHeader(); // Header encoded
}
else if (m_featureDecoder.isBufferReceived()) // Buffer received
{
OV_ERROR_UNLESS_KRF(unFeaturization(), "Featurization Processing Error", Kernel::ErrorType::BadProcessing); // Transformation
m_matrixEncoder.encodeBuffer(); // Buffer encoded
}
else if (m_featureDecoder.isEndReceived()) { m_matrixEncoder.encodeEnd(); } // End receivded and encoded
boxContext.markOutputAsReadyToSend(0, start, end); // Makes the output available
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeatureVectorToCovarianceMatrix::unFeaturization() const
{
Eigen::MatrixXd cov;
Eigen::RowVectorXd v;
if (!MatrixConvert(*m_iMatrix, v)) { return false; }
if (!Geometry::UnFeaturization(v, cov, m_tangentSpace, m_ref)) { return false; }
if (!MatrixConvert(cov, *m_oMatrix)) { return false; }
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeatureVectorToCovarianceMatrix::initRef()
{
//***** Open the CSV *****
const CString name = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
if (name.length() == 0)
{
this->getLogManager() << m_logLevel << "Empty reference Matrix\n";
return true;
}
std::ifstream file(name, std::ifstream::in);
OV_ERROR_UNLESS_KRF(file.is_open(), "Error opening file [" << name << "] for reading", Kernel::ErrorType::BadFileRead);
//***** Parse the CSV *****
std::string line;
getline(file, line); // Header
getline(file, line); // matrix line
std::vector<std::string> data = Geometry::Split(line, ",");
//***** Transform to MatrixXd *****
const auto first = data.begin() + 2, last = data.end() - 3;
const std::vector<std::string> mat(first, last);
const size_t n = size_t(sqrt(mat.size()));
OV_ERROR_UNLESS_KRF(n*n == mat.size(), "Error Reference Matrix Format", Kernel::ErrorType::BadFileParsing);
m_ref.resize(n, n);
size_t idx = 0;
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < n; ++j) { m_ref(i, j) = std::stod(mat[idx++]); } }
//***** Log Information *****
this->getLogManager() << m_logLevel << "REF Matrix : \n" << Geometry::MatrixPrint(m_ref) << "\n";
//***** Close the CSV *****
file.close();
return true;
}
//---------------------------------------------------------------------------------------------------
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,93 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmFeatureVectorToCovarianceMatrix.hpp
/// \brief Class of the box computing the Feature vector with the covariance matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 17/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <Eigen/Dense>
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
/// <summary> The class CBoxAlgorithmFeatureVectorToCovarianceMatrix describes the box Covariance Matrix To Feature Vector. </summary>
class CBoxAlgorithmFeatureVectorToCovarianceMatrix final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, ClassId_BoxAlgorithm_FeatureVectorToCovarianceMatrix)
protected:
bool unFeaturization() const;
bool initRef();
//***** Codecs *****
Toolkit::TFeatureVectorDecoder<CBoxAlgorithmFeatureVectorToCovarianceMatrix> m_featureDecoder; // Input Feature Codec
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmFeatureVectorToCovarianceMatrix> m_matrixEncoder; // Output Matrix Codec
//***** Matrices *****
CMatrix* m_iMatrix = nullptr; // Input Matrix pointer
CMatrix* m_oMatrix = nullptr; // Output Matrix pointer
//***** Settings *****
bool m_tangentSpace = true; // Method to use (only tangent or squeeze now)
Eigen::MatrixXd m_ref; // Reference matrix for tangent space compute
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; // Log Level
};
/// <summary> Descriptor of the box Covariance Matrix To Feature Vector. </summary>
class CBoxAlgorithmFeatureVectorToCovarianceMatrixDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Feature Vector To Covariance Matrix"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Transforms a feature std::vector into a covariance matrix"; }
CString getDetailedDescription() const override
{
return "Transforms a feature std::vector (size : N(N+1)/2) into a covariance matrix (size : NxN).\nThe Setting Tangent Space define if the transformation into std::vector is done in the tanget space or if it is a squeeze of the upper triangular matrix";
}
CString getCategory() const override { return "Riemannian Geometry"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-jump-to"; }
CIdentifier getCreatedClass() const override { return ClassId_BoxAlgorithm_FeatureVectorToCovarianceMatrix; }
IPluginObject* create() override { return new CBoxAlgorithmFeatureVectorToCovarianceMatrix; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input Feature Vector", OV_TypeId_FeatureVector);
prototype.addOutput("Output Covariance Matrix", OV_TypeId_StreamedMatrix);
prototype.addSetting("Tangent Space", OV_TypeId_Boolean, "true");
prototype.addSetting("Filename to Reference Matrix (CSV, empty for Identity)", OV_TypeId_Filename, "${Player_ScenarioDirectory}/Mean.csv");
prototype.addSetting("Log Level", OV_TypeId_LogLevel, "Information");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, ClassId_BoxAlgorithm_FeatureVectorToCovarianceMatrixDesc)
};
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,101 @@
#include "CBoxAlgorithmMatrixAffineTransformation.hpp"
#include "utils/misc.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixAffineTransformation::initialize()
{
// Matrix
m_iMatrixCodec.initialize(*this, 0);
m_iMatrix = m_iMatrixCodec.getOutputMatrix();
m_oMatrixCodec.initialize(*this, 0);
m_oMatrix = m_oMatrixCodec.getInputMatrix();
m_ifilename = CString(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0)).toASCIIString();
m_ofilename = CString(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1)).toASCIIString();
m_continuous = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
OV_ERROR_UNLESS_KRF(loadXML(), "Loading XML Error", Kernel::ErrorType::BadFileRead);
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixAffineTransformation::uninitialize()
{
if (!m_continuous && !m_samples.empty() && m_ofilename.length() != 0)
{
OV_ERROR_UNLESS_KRF(m_bias.computeBias(m_samples), "Bias Compute Error", Kernel::ErrorType::BadProcessing);
}
OV_ERROR_UNLESS_KRF(saveXML(), "Saving XML Error", Kernel::ErrorType::BadFileWrite);
m_iMatrixCodec.uninitialize();
m_oMatrixCodec.uninitialize();
m_samples.clear();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixAffineTransformation::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixAffineTransformation::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
//***** Matrix *****
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_iMatrixCodec.decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_iMatrix->getDimensionCount() == 2, "Invalid Input Signal", Kernel::ErrorType::BadInput);
if (m_iMatrixCodec.isHeaderReceived()) // Header received
{
m_oMatrix->copyDescription(*m_iMatrix); // Update Size and set to 0
m_oMatrixCodec.encodeHeader();
}
if (m_iMatrixCodec.isBufferReceived()) // Buffer received
{
Eigen::MatrixXd in, out;
MatrixConvert(*m_iMatrix, in);
m_samples.push_back(in);
if (m_continuous) { m_bias.updateBias(in); } // We update each time
if (m_bias.getBias().size() == 0
) { m_bias.setBias(Eigen::MatrixXd::Identity(in.rows(), in.cols())); } // We wan't to apply a bias without bias Identity matrix is used
m_bias.applyBias(in, out);
MatrixConvert(out, *m_oMatrix);
m_oMatrixCodec.encodeBuffer();
}
else if (m_iMatrixCodec.isEndReceived()) { m_oMatrixCodec.encodeEnd(); } // End received
const uint64_t tStart = boxContext.getInputChunkStartTime(0, i); // Time Code Chunk Start
const uint64_t tEnd = boxContext.getInputChunkEndTime(0, i); // Time Code Chunk End
boxContext.markOutputAsReadyToSend(0, tStart, tEnd);
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixAffineTransformation::loadXML()
{
if (m_ifilename.length() == 0) { return true; } // The bias haven't initialization
return m_bias.loadXML(m_ifilename); // The bias have initialization
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixAffineTransformation::saveXML() const
{
if (m_ofilename.length() == 0) { return true; } // The bias isn't saved
return m_bias.saveXML(m_ofilename); // The bias is saved
}
//---------------------------------------------------------------------------------------------------
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,97 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmMatrixAffineTransformation.hpp
/// \brief Class of the box Affine Transformation.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 28/08/2019.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <geometry/classifier/CBias.hpp>
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
/// <summary> The class CBoxAlgorithmMatrixAffineTransformation describes the box Matrix Affine Transformation. </summary>
class CBoxAlgorithmMatrixAffineTransformation final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, ClassId_BoxAlgorithm_MatrixAffineTransformation)
protected:
bool loadXML();
bool saveXML() const;
//***** Codecs *****
Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatrixAffineTransformation> m_iMatrixCodec; // Input Signal Codec
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmMatrixAffineTransformation> m_oMatrixCodec; // Output Signal Codec
CMatrix *m_iMatrix = nullptr, *m_oMatrix = nullptr; // Input/Output Matrix pointer
//***** Settings *****
std::string m_ifilename, m_ofilename; // Input/Output Filename
bool m_continuous = false;
//***** Variable *****
Geometry::CBias m_bias;
std::vector<Eigen::MatrixXd> m_samples;
};
/// <summary> Descriptor of the box Matrix Classifier Trainer. </summary>
class CBoxAlgorithmMatrixAffineTransformationDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Matrix Affine Transformation"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Compute and Apply the Bias matrix for Affine Transformation on square matrix."; }
CString getDetailedDescription() const override
{
return "Compute and Apply the Reference matrix for Affine Transformation on square matrix (isR * M * isR^(-1) = I).\nYou can load an existing matrix.\nContinuous update is to update Bias at each chunk or at the end.";
}
CString getCategory() const override { return "Riemannian Geometry"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-execute"; }
CIdentifier getCreatedClass() const override { return ClassId_BoxAlgorithm_MatrixAffineTransformation; }
IPluginObject* create() override { return new CBoxAlgorithmMatrixAffineTransformation; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Square Matrix",OV_TypeId_StreamedMatrix);
prototype.addOutput("Transformed Square Matrix", OV_TypeId_StreamedMatrix);
prototype.addSetting("Filename to load transformation", OV_TypeId_Filename, "${Player_ScenarioDirectory}/my-transformation-input.xml");
prototype.addSetting("Filename to save transformation",OV_TypeId_Filename, "${Player_ScenarioDirectory}/my-transformation-output.xml");
prototype.addSetting("Continuous Update", OV_TypeId_Boolean, "false");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, ClassId_BoxAlgorithm_MatrixAffineTransformationDesc)
};
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,251 @@
#include "CBoxAlgorithmMatrixClassifierProcessor.hpp"
#include <geometry/classifier/CMatrixClassifierMDMRebias.hpp>
#include <geometry/classifier/CMatrixClassifierFgMDMRTRebias.hpp>
#include "utils/misc.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierProcessor::initialize()
{
//***** Codecs *****
m_i0StimulationCodec.initialize(*this, 0);
m_i1MatrixCodec.initialize(*this, 1);
m_o0StimulationCodec.initialize(*this, 0);
m_o1MatrixCodec.initialize(*this, 1);
m_o2MatrixCodec.initialize(*this, 2);
//***** Pointers *****
m_i0Stimulation = m_i0StimulationCodec.getOutputStimulationSet();
m_i1Matrix = m_i1MatrixCodec.getOutputMatrix();
m_o0Stimulation = m_o0StimulationCodec.getInputStimulationSet();
m_o1Matrix = m_o1MatrixCodec.getInputMatrix();
m_o2Matrix = m_o2MatrixCodec.getInputMatrix();
// Settings
m_ifilename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_ofilename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_adaptation = Geometry::EAdaptations(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2)));
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3)));
OV_ERROR_UNLESS_KRF(m_ifilename.length() != 0, "Invalid empty model filename", Kernel::ErrorType::BadSetting);
OV_ERROR_UNLESS_KRF(loadXML(), "Loading XML Error", Kernel::ErrorType::BadFileRead);
// Change matrix size
m_o1Matrix->resize(m_classifier->getClassCount());
m_o2Matrix->resize(m_classifier->getClassCount());
// Printing info
std::stringstream msg;
msg << std::endl << "Input Filename : " << m_ifilename << std::endl << "Output Filename : " << m_ofilename << std::endl
<< "Method : " << m_classifier->getType() << " with " << toString(m_adaptation) << " adaptation" << std::endl
<< "Number of classes : " << m_classifier->getClassCount() << std::endl;
for (size_t k = 0; k < m_classifier->getClassCount(); ++k)
{
msg << "Stimulation for class " << k << " : " << m_stimulationClassName[k] << " => ["
<< this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, m_stimulationClassName[k]) << "]\n";
}
this->getLogManager() << m_logLevel << msg.str();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierProcessor::uninitialize()
{
if (m_ofilename.length() != 0) { saveXML(); }
m_i0StimulationCodec.uninitialize();
m_i1MatrixCodec.uninitialize();
m_o0StimulationCodec.uninitialize();
m_o1MatrixCodec.uninitialize();
m_o2MatrixCodec.uninitialize();
delete m_classifier; // check if pointeur is null is useless now.
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierProcessor::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierProcessor::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
//**** Stimulation *****
if (m_adaptation != Geometry::EAdaptations::None)
{
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_i0StimulationCodec.decode(i);
if (m_i0StimulationCodec.isBufferReceived()) // Buffer received
{
bool finish = false;
for (size_t j = 0; j < m_i0Stimulation->getStimulationCount() && !finish; ++j)
{
const uint64_t stim = m_i0Stimulation->getStimulationIdentifier(j);
for (size_t k = 0; k < m_stimulationClassName.size() && !finish; ++k)
{
if (stim == this->getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_Stimulation, "OVTK_GDF_End_Of_Trial"))
{
m_lastLabelReceived = std::numeric_limits<size_t>::max();
finish = true;
}
else if (stim == m_stimulationClassName[k])
{
m_lastLabelReceived = k;
finish = true;
}
}
}
}
}
}
//***** Matrix *****
if (m_adaptation == Geometry::EAdaptations::None || m_lastLabelReceived < m_stimulationClassName.size())
{
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
{
m_i1MatrixCodec.decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_i1Matrix->getDimensionCount() == 2, "Invalid Input Signal", Kernel::ErrorType::BadInput);
const uint64_t tStart = boxContext.getInputChunkStartTime(1, i), // Time Code Chunk Start
tEnd = boxContext.getInputChunkEndTime(1, i); // Time Code Chunk End
if (m_i1MatrixCodec.isHeaderReceived()) // Header received
{
m_o0StimulationCodec.encodeHeader();
m_o1MatrixCodec.encodeHeader();
m_o2MatrixCodec.encodeHeader();
}
else if (m_i1MatrixCodec.isBufferReceived()) // Buffer received
{
OV_ERROR_UNLESS_KRF(classify(tEnd), "Classify Error", Kernel::ErrorType::BadProcessing);
m_o0StimulationCodec.encodeBuffer();
m_o1MatrixCodec.encodeBuffer();
m_o2MatrixCodec.encodeBuffer();
}
else if (m_i1MatrixCodec.isEndReceived()) // End received
{
m_o0StimulationCodec.encodeEnd();
m_o1MatrixCodec.encodeEnd();
m_o2MatrixCodec.encodeEnd();
}
for (size_t j = 0; j < 3; ++j) { boxContext.markOutputAsReadyToSend(j, tStart, tEnd); }
}
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierProcessor::classify(const uint64_t tEnd)
{
std::vector<double> distance, probability;
Eigen::MatrixXd cov;
size_t classId;
MatrixConvert(*m_i1Matrix, cov);
OV_ERROR_UNLESS_KRF(m_classifier->classify(cov, classId, distance, probability, m_adaptation, m_lastLabelReceived), "Classify Error",
Kernel::ErrorType::BadProcessing);
//Fill Output
m_o0Stimulation->setStimulationCount(1); //No append stimulation only one is used
m_o0Stimulation->setStimulationIdentifier(0, m_stimulationClassName[classId]);
m_o0Stimulation->setStimulationDate(0, tEnd);
m_o0Stimulation->setStimulationDuration(0, 0);
MatrixConvert(distance, *m_o1Matrix);
MatrixConvert(probability, *m_o2Matrix);
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierProcessor::loadXML()
{
delete m_classifier; // if (m_classifer != nullptr) useless now
tinyxml2::XMLDocument xmlDoc;
// Load File
OV_ERROR_UNLESS_KRF(xmlDoc.LoadFile(m_ifilename.toASCIIString()) == 0, "Unable to load xml file : " << m_ifilename.toASCIIString(),
Kernel::ErrorType::BadFileRead);
// Load Root
tinyxml2::XMLNode* root = xmlDoc.FirstChild();
OV_ERROR_UNLESS_KRF(root != nullptr, "Unable to get xml root node", Kernel::ErrorType::BadFileParsing);
// Load Data
tinyxml2::XMLElement* data = root->FirstChildElement("Classifier-data");
OV_ERROR_UNLESS_KRF(data != nullptr, "Unable to get xml classifier node", Kernel::ErrorType::BadFileParsing);
const std::string classifierType = data->Attribute("type");
// Check Type
if (classifierType == toString(Geometry::EMatrixClassifiers::MDM)) { m_classifier = new Geometry::CMatrixClassifierMDM; }
else if (classifierType == toString(Geometry::EMatrixClassifiers::MDM_Rebias)) { m_classifier = new Geometry::CMatrixClassifierMDMRebias; }
else if (classifierType == toString(Geometry::EMatrixClassifiers::FgMDM_RT)) { m_classifier = new Geometry::CMatrixClassifierFgMDMRT; }
else if (classifierType == toString(Geometry::EMatrixClassifiers::FgMDM_RT_Rebias)) { m_classifier = new Geometry::CMatrixClassifierFgMDMRTRebias; }
else { OV_ERROR_UNLESS_KRF(false, "Incorrect Classifier", Kernel::ErrorType::BadFileParsing); }
// Object Load
m_classifier->loadXML(m_ifilename.toASCIIString());
// Load Stimulation
m_stimulationClassName.resize(m_classifier->getClassCount());
tinyxml2::XMLElement* element = data->FirstChildElement("Class"); // Get Fist Class Node
for (size_t k = 0; k < m_classifier->getClassCount(); ++k) // for each class
{
OV_ERROR_UNLESS_KRF(element != nullptr, "Invalid class node", Kernel::ErrorType::BadFileParsing);
const size_t idx = element->IntAttribute("class-id"); // Get Id (normally idx = k)
OV_ERROR_UNLESS_KRF(idx == k, "Invalid Class id", Kernel::ErrorType::BadFileParsing);
m_stimulationClassName[k] = this->getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_Stimulation, element->Attribute("stimulation"));
element = element->NextSiblingElement("Class"); // Next Class
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierProcessor::saveXML()
{
OV_ERROR_UNLESS_KRF(m_classifier->saveXML(m_ofilename.toASCIIString()), "Save failed", Kernel::ErrorType::BadFileWrite);
//***** Add Stimulation to XML *****
tinyxml2::XMLDocument xmlDoc;
// Load File
OV_ERROR_UNLESS_KRF(xmlDoc.LoadFile(m_ofilename.toASCIIString()) == 0, "Unable to load xml file : " << m_ofilename.toASCIIString(),
Kernel::ErrorType::BadFileRead);
// Load Root
tinyxml2::XMLNode* root = xmlDoc.FirstChild();
OV_ERROR_UNLESS_KRF(root != nullptr, "Unable to get xml root node", Kernel::ErrorType::BadFileParsing);
// Load Data
tinyxml2::XMLElement* data = root->FirstChildElement("Classifier-data");
OV_ERROR_UNLESS_KRF(data != nullptr, "Unable to get xml classifier node", Kernel::ErrorType::BadFileParsing);
tinyxml2::XMLElement* element = data->FirstChildElement("Class"); // Get Fist Class Node
for (size_t k = 0; k < m_classifier->getClassCount(); ++k) // for each class
{
OV_ERROR_UNLESS_KRF(element != nullptr, "Invalid class node", Kernel::ErrorType::BadFileParsing);
const size_t idx = element->IntAttribute("class-id"); // Get Id (normally idx = k)
OV_ERROR_UNLESS_KRF(idx == k, "Invalid Class id", Kernel::ErrorType::BadFileParsing);
const CString stimulationName = this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, m_stimulationClassName[k]);
element->SetAttribute("stimulation", stimulationName.toASCIIString());
element = element->NextSiblingElement("Class"); // Next Class
}
return xmlDoc.SaveFile(m_ofilename.toASCIIString()) == 0; // save XML (if != 0 it means error)
}
//---------------------------------------------------------------------------------------------------
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,105 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmMatrixClassifierProcessor.hpp
/// \brief Class of the box Process a Matrix Classifier.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 17/01/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <geometry/classifier/IMatrixClassifier.hpp>
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
/// <summary> The class CBoxAlgorithmMatrixClassifierProcessor describes the box Matrix Classifier Processor. </summary>
class CBoxAlgorithmMatrixClassifierProcessor final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, ClassId_BoxAlgorithm_MatrixClassifierProcessor)
protected:
//***** Codecs *****
Toolkit::TStimulationDecoder<CBoxAlgorithmMatrixClassifierProcessor> m_i0StimulationCodec;
Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatrixClassifierProcessor> m_i1MatrixCodec;
Toolkit::TStimulationEncoder<CBoxAlgorithmMatrixClassifierProcessor> m_o0StimulationCodec;
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmMatrixClassifierProcessor> m_o1MatrixCodec, m_o2MatrixCodec;
//***** Matrices *****
CMatrix *m_i1Matrix = nullptr, *m_o1Matrix = nullptr, *m_o2Matrix = nullptr; // Matrix Pointer
Eigen::MatrixXd m_distance, m_probability; // Eigen Matrix
//***** Stimulations *****
IStimulationSet *m_i0Stimulation = nullptr, // Stimulation receiver
*m_o0Stimulation = nullptr; // Stimulation sender
std::vector<uint64_t> m_stimulationClassName; // Name of stimulation to check for each class
Geometry::IMatrixClassifier* m_classifier = nullptr; // Classifier
size_t m_lastLabelReceived = std::numeric_limits<size_t>::max(); // Last label received for Supervised Adaptation
Geometry::EAdaptations m_adaptation = Geometry::EAdaptations::None; // Adaptation Method
//***** Setting *****
CString m_ifilename, m_ofilename;
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; // Log Level
bool classify(uint64_t tEnd);
bool loadXML();
bool saveXML();
};
/// <summary> Descriptor of the box Matrix Classifier Processor. </summary>
class CBoxAlgorithmMatrixClassifierProcessorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Matrix Classifier Processor"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Matrix classifier Processor."; }
CString getDetailedDescription() const override { return "Matrix classifier Processor."; }
CString getCategory() const override { return "Riemannian Geometry"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-execute"; }
CIdentifier getCreatedClass() const override { return ClassId_BoxAlgorithm_MatrixClassifierProcessor; }
IPluginObject* create() override { return new CBoxAlgorithmMatrixClassifierProcessor; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Expected Label", OV_TypeId_Stimulations);
prototype.addInput("Input Matrix",OV_TypeId_StreamedMatrix);
prototype.addOutput("Label",OV_TypeId_Stimulations);
prototype.addOutput("Distance",OV_TypeId_StreamedMatrix);
prototype.addOutput("Probability",OV_TypeId_StreamedMatrix);
prototype.addSetting("Filename to load classifier model", OV_TypeId_Filename, "${Player_ScenarioDirectory}/input-classifier.xml");
prototype.addSetting("Filename to save classifier model",OV_TypeId_Filename, "${Player_ScenarioDirectory}/output-classifier.xml");
prototype.addSetting("Adaptation", TypeId_Classifier_Adaptation, toString(Geometry::EAdaptations::None).c_str());
prototype.addSetting("Log Level", OV_TypeId_LogLevel, "Information");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, ClassId_BoxAlgorithm_MatrixClassifierProcessorDesc)
};
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,231 @@
#include "CBoxAlgorithmMatrixClassifierTrainer.hpp"
#include <geometry/3rd-party/tinyxml2.h>
#include <geometry/classifier/CMatrixClassifierMDMRebias.hpp>
#include <geometry/classifier/CMatrixClassifierFgMDMRTRebias.hpp>
#include "utils/misc.hpp"
#include "boost/format.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainer::initialize()
{
// Stimulations
m_i0StimulationCodec.initialize(*this, 0);
m_iStimulation = m_i0StimulationCodec.getOutputStimulationSet();
m_o0StimulationCodec.initialize(*this, 0);
m_oStimulation = m_o0StimulationCodec.getInputStimulationSet();
// Classes
const Kernel::IBox& boxContext = this->getStaticBoxContext();
m_nbClass = boxContext.getInputCount() - 1;
m_i1MatrixCodec.resize(m_nbClass);
m_iMatrix.resize(m_nbClass);
m_covs.resize(m_nbClass);
m_stimulationClassName.resize(m_nbClass);
for (size_t k = 0; k < m_nbClass; ++k)
{
m_i1MatrixCodec[k].initialize(*this, k + 1);
m_iMatrix[k] = m_i1MatrixCodec[k].getOutputMatrix();
m_stimulationClassName[k] = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), k + NON_CLASS_SETTINGS_COUNT));
}
// Settings
m_stimulationName = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_method = Geometry::EMatrixClassifiers(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2)));
m_metric = Geometry::EMetric(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3)));
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 4)));
OV_ERROR_UNLESS_KRF(m_filename.length() != 0, "Invalid empty model filename", Kernel::ErrorType::BadSetting);
// Printing info
this->getLogManager() << m_logLevel << "\nNumber of classes : " << m_nbClass << "\nFilename : " << m_filename << "\nMethod : "
<< toString(m_method) << "\n";
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainer::uninitialize()
{
m_i0StimulationCodec.uninitialize();
for (auto& codec : m_i1MatrixCodec) { codec.uninitialize(); }
m_i1MatrixCodec.clear();
m_iMatrix.clear();
for (auto& cov : m_covs) { cov.clear(); }
m_covs.clear();
m_stimulationClassName.clear();
m_o0StimulationCodec.uninitialize();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainer::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainer::process()
{
if (!m_isTrain)
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
//***** Stimulations *****
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_i0StimulationCodec.decode(i); // Decode the chunk
const uint64_t start = boxContext.getInputChunkStartTime(0, i), // Time Code Chunk Start
end = boxContext.getInputChunkEndTime(0, i); // Time Code Chunk End
if (m_i0StimulationCodec.isHeaderReceived())
{
m_o0StimulationCodec.encodeHeader();
boxContext.markOutputAsReadyToSend(0, 0, 0);
}
if (m_i0StimulationCodec.isBufferReceived()) // Buffer received
{
for (size_t j = 0; j < m_iStimulation->getStimulationCount(); ++j)
{
if (m_iStimulation->getStimulationIdentifier(j) == m_stimulationName)
{
OV_ERROR_UNLESS_KRF(train(), "Train failed", Kernel::ErrorType::BadProcessing);
const uint64_t stim = this->getTypeManager().getEnumerationEntryValueFromName(
OV_TypeId_Stimulation, "OVTK_StimulationId_TrainCompleted");
m_oStimulation->appendStimulation(stim, m_iStimulation->getStimulationDate(j), 0);
m_isTrain = true;
}
}
m_o0StimulationCodec.encodeBuffer();
boxContext.markOutputAsReadyToSend(0, start, end);
}
if (m_i0StimulationCodec.isEndReceived())
{
m_o0StimulationCodec.encodeEnd();
boxContext.markOutputAsReadyToSend(0, start, end);
}
}
//***** Matrix *****
for (size_t k = 0; k < m_nbClass; ++k)
{
for (size_t i = 0; i < boxContext.getInputChunkCount(k + 1); ++i)
{
m_i1MatrixCodec[k].decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_iMatrix[k]->getDimensionCount() == 2, "Invalid Input Signal", Kernel::ErrorType::BadInput);
if (m_i1MatrixCodec[k].isBufferReceived()) // Buffer received
{
Eigen::MatrixXd cov;
MatrixConvert(*m_iMatrix[k], cov);
m_covs[k].push_back(cov);
}
}
}
}
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainer::train()
{
Geometry::IMatrixClassifier* matrixClassifier;
if (m_method == Geometry::EMatrixClassifiers::MDM) { matrixClassifier = new Geometry::CMatrixClassifierMDM; }
else if (m_method == Geometry::EMatrixClassifiers::MDM_Rebias) { matrixClassifier = new Geometry::CMatrixClassifierMDMRebias; }
else if (m_method == Geometry::EMatrixClassifiers::FgMDM_RT) { matrixClassifier = new Geometry::CMatrixClassifierFgMDMRT; }
else if (m_method == Geometry::EMatrixClassifiers::FgMDM_RT_Rebias) { matrixClassifier = new Geometry::CMatrixClassifierFgMDMRTRebias; }
else { OV_ERROR_UNLESS_KRF(false, "Incorrect Selected Method", Kernel::ErrorType::BadSetting); }
this->getLogManager() << m_logLevel << "Train Beginning...\n";
OV_ERROR_UNLESS_KRF(matrixClassifier->train(m_covs), "Train failed", Kernel::ErrorType::BadProcessing);
this->getLogManager() << m_logLevel << "Train Finished. Save Beginning...\n";
OV_ERROR_UNLESS_KRF(saveXML(matrixClassifier), "Save failed", Kernel::ErrorType::BadProcessing);
this->getLogManager() << m_logLevel << "Save Finished.\n";
delete matrixClassifier;
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainer::saveXML(Geometry::IMatrixClassifier* classifier)
{
OV_ERROR_UNLESS_KRF(classifier->saveXML(m_filename.toASCIIString()), "Save failed", Kernel::ErrorType::BadFileWrite);
//***** Add Stimulation to XML *****
tinyxml2::XMLDocument xmlDoc;
// Load File
OV_ERROR_UNLESS_KRF(xmlDoc.LoadFile(m_filename.toASCIIString()) == 0, "Unable to load xml file : " << m_filename.toASCIIString(),
Kernel::ErrorType::BadFileRead);
// Load Root
tinyxml2::XMLNode* root = xmlDoc.FirstChild();
OV_ERROR_UNLESS_KRF(root != nullptr, "Unable to get xml root node", Kernel::ErrorType::BadFileParsing);
// Load Data
tinyxml2::XMLElement* data = root->FirstChildElement("Classifier-data");
OV_ERROR_UNLESS_KRF(data != nullptr, "Unable to get xml classifier node", Kernel::ErrorType::BadFileParsing);
tinyxml2::XMLElement* element = data->FirstChildElement("Class"); // Get Fist Class Node
for (size_t k = 0; k < classifier->getClassCount(); ++k) // for each class
{
OV_ERROR_UNLESS_KRF(element != nullptr, "Invalid class node", Kernel::ErrorType::BadFileParsing);
const size_t idx = element->IntAttribute("class-id"); // Get Id (normally idx = k)
OV_ERROR_UNLESS_KRF(idx == k, "Invalid Class id", Kernel::ErrorType::BadFileParsing);
const CString stimulationName = this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, m_stimulationClassName[k]);
element->SetAttribute("stimulation", stimulationName.toASCIIString());
element = element->NextSiblingElement("Class"); // Next Class
}
return xmlDoc.SaveFile(m_filename.toASCIIString()) == 0; // save XML (if != 0 it means error)
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainerListener::onInputAdded(Kernel::IBox& box, const size_t index)
{
box.setInputType(index, OV_TypeId_StreamedMatrix);
box.setInputName(index, ("Matrix for class " + std::to_string(index)).c_str());
const boost::format stimulation = boost::format("OVTK_StimulationId_Label_%02u") % index;
box.addSetting(("Class " + std::to_string(index) + " label").c_str(), OV_TypeId_Stimulation, stimulation.str().c_str());
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmMatrixClassifierTrainerListener::onInputRemoved(Kernel::IBox& box, const size_t index)
{
const size_t offset = CBoxAlgorithmMatrixClassifierTrainer::NON_CLASS_SETTINGS_COUNT - 1;
// (avoid the 5 first setting but class begin at input 1 so + 4)
box.removeSetting(index + offset);
//check if the removed class is not the last
if (index != box.getInputCount())
{
for (size_t k = index; k < box.getInputCount(); ++k)
{
box.setInputName(k, ("Matrix for class " + std::to_string(k)).c_str());
std::string stimulation = (boost::format("OVTK_StimulationId_Label_%02u") % k).str();
box.setSettingName(k + offset - 1, ("Class " + std::to_string(k) + " label").c_str()); // -1 because one is removed
box.setSettingDefaultValue(k + offset - 1, stimulation.c_str());
box.setSettingValue(k + offset - 1, stimulation.c_str());
}
}
return true;
}
//---------------------------------------------------------------------------------------------------
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,128 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmMatrixClassifierProcessor.hpp
/// \brief Class of the box Train a Matrix Classifier.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 17/01/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <geometry/classifier/IMatrixClassifier.hpp>
#include <geometry/Metrics.hpp>
namespace OpenViBE {
namespace Plugins {
namespace Riemannian {
/// <summary> The class CBoxAlgorithmMatrixClassifierTrainer describes the box Matrix Classifier Trainer. </summary>
class CBoxAlgorithmMatrixClassifierTrainer final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, ClassId_BoxAlgorithm_MatrixClassifierTrainer)
static const size_t NON_CLASS_SETTINGS_COUNT = 5; // Train trigger + Filename + Method + Metric + Log Level
protected:
//***** Codecs *****
Toolkit::TStimulationDecoder<CBoxAlgorithmMatrixClassifierTrainer> m_i0StimulationCodec;
std::vector<Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatrixClassifierTrainer>> m_i1MatrixCodec; // Input Signal Codec
Toolkit::TStimulationEncoder<CBoxAlgorithmMatrixClassifierTrainer> m_o0StimulationCodec;
//***** Matrices *****
size_t m_nbClass = 2; // Number of input classes
std::vector<CMatrix*> m_iMatrix; // Input Matrix pointer
std::vector<std::vector<Eigen::MatrixXd>> m_covs; // List of Covariance Matrix one class by row
//***** Stimulations *****
IStimulationSet *m_iStimulation = nullptr, // Stimulation receiver
*m_oStimulation = nullptr; // Stimulation sender
uint64_t m_stimulationName = OVTK_StimulationId_Train; // Name of stimulation to check for train launch
std::vector<uint64_t> m_stimulationClassName; // Name of stimulation to check for each class
bool m_isTrain = false;
//***** Settings *****
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; // Log Level
Geometry::EMatrixClassifiers m_method = Geometry::EMatrixClassifiers::MDM;
Geometry::EMetric m_metric = Geometry::EMetric::Riemann;
//***** File *****
CString m_filename;
bool train();
bool saveXML(Geometry::IMatrixClassifier* classifier);
};
/// <summary> Listener of the box Matrix Classifier Trainer. </summary>
class CBoxAlgorithmMatrixClassifierTrainerListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override;
bool onInputRemoved(Kernel::IBox& box, const size_t index) override;
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
/// <summary> Descriptor of the box Matrix Classifier Trainer. </summary>
class CBoxAlgorithmMatrixClassifierTrainerDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Matrix Classifier Trainer"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Matrix classifier trainer."; }
CString getDetailedDescription() const override { return "Matrix classifier trainer."; }
CString getCategory() const override { return "Riemannian Geometry"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-execute"; }
CIdentifier getCreatedClass() const override { return ClassId_BoxAlgorithm_MatrixClassifierTrainer; }
IPluginObject* create() override { return new CBoxAlgorithmMatrixClassifierTrainer; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmMatrixClassifierTrainerListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Stimulations",OV_TypeId_Stimulations);
prototype.addInput("Matrix for class 1",OV_TypeId_StreamedMatrix);
prototype.addInput("Matrix for class 2",OV_TypeId_StreamedMatrix);
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addOutput("Tran-completed Flag",OV_TypeId_Stimulations);
prototype.addSetting("Train trigger", OV_TypeId_Stimulation, "OVTK_StimulationId_Train");
prototype.addSetting("Filename to save classifier model",OV_TypeId_Filename, "${Player_ScenarioDirectory}/my-classifier.xml");
prototype.addSetting("Method", TypeId_Matrix_Classifier, toString(Geometry::EMatrixClassifiers::MDM).c_str());
prototype.addSetting("Metric", TypeId_Metric, toString(Geometry::EMetric::Riemann).c_str());
prototype.addSetting("Log Level", OV_TypeId_LogLevel, "Information");
prototype.addSetting("Class 1 label", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_01");
prototype.addSetting("Class 2 label", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_02");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, ClassId_BoxAlgorithm_MatrixClassifierTrainerDesc)
};
} // namespace Riemannian
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,41 @@
///-------------------------------------------------------------------------------------------------
///
/// \file defines.hpp
/// \brief Defines list for Setting, Shortcut Macro and const.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks
/// - List of Estimator inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
/// - List of Metrics inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
///
///-------------------------------------------------------------------------------------------------
#pragma once
namespace OpenViBE {
// Boxes
//---------------------------------------------------------------------------------------------------
#define ClassId_BoxAlgorithm_CovarianceMeanCalculator CIdentifier(0x67955ea4, 0x7c643c0f)
#define ClassId_BoxAlgorithm_CovarianceMeanCalculatorDesc CIdentifier(0x62e8f759, 0xd59d82a9)
#define ClassId_BoxAlgorithm_MatrixClassifierTrainer CIdentifier(0xc0b79b42, 0x4150c837)
#define ClassId_BoxAlgorithm_MatrixClassifierTrainerDesc CIdentifier(0x26f4fa93, 0x704d39dd)
#define ClassId_BoxAlgorithm_MatrixClassifierProcessor CIdentifier(0x918f6952, 0xb22ddf0d)
#define ClassId_BoxAlgorithm_MatrixClassifierProcessorDesc CIdentifier(0x8cf29eec, 0x223fbfc5)
#define ClassId_BoxAlgorithm_CovarianceMatrixToFeatureVector CIdentifier(0x7c265dba, 0x202c1f70)
#define ClassId_BoxAlgorithm_CovarianceMatrixToFeatureVectorDesc CIdentifier(0xc0fb0445, 0x0d1cd546)
#define ClassId_BoxAlgorithm_FeatureVectorToCovarianceMatrix CIdentifier(0x7c265dba, 0x202c1f71)
#define ClassId_BoxAlgorithm_FeatureVectorToCovarianceMatrixDesc CIdentifier(0xc0fb0445, 0x0d1cd541)
#define ClassId_BoxAlgorithm_CovarianceMatrixCalculator CIdentifier(0x9a93af80, 0x6449c826)
#define ClassId_BoxAlgorithm_CovarianceMatrixCalculatorDesc CIdentifier(0x12fcd91f, 0xd1d8f678)
#define ClassId_BoxAlgorithm_MatrixAffineTransformation CIdentifier(0x1BAA7180, 0x52CB19B8)
#define ClassId_BoxAlgorithm_MatrixAffineTransformationDesc CIdentifier(0x0AF511E5, 0x27137BBA)
// Méthodes/Types Lists
//---------------------------------------------------------------------------------------------------
#define TypeId_Estimator CIdentifier(0x5261636B, 0x45535449)
#define TypeId_Metric CIdentifier(0x5261636B, 0x4D455452)
#define TypeId_Matrix_Classifier CIdentifier(0x5261636B, 0x436C6173)
#define TypeId_Classifier_Adaptation CIdentifier(0x5261636B, 0x41646170)
//---------------------------------------------------------------------------------------------------
} // namespace OpenViBE
@@ -0,0 +1,64 @@
#include <openvibe/ov_all.h>
#include "defines.hpp"
// Boxes Includes
#include "boxes/CBoxAlgorithmCovarianceMatrixCalculator.hpp"
#include "boxes/CBoxAlgorithmCovarianceMatrixToFeatureVector.hpp"
#include "boxes/CBoxAlgorithmFeatureVectorToCovarianceMatrix.hpp"
#include "boxes/CBoxAlgorithmCovarianceMeanCalculator.hpp"
#include "boxes/CBoxAlgorithmMatrixClassifierTrainer.hpp"
#include "boxes/CBoxAlgorithmMatrixClassifierProcessor.hpp"
#include "boxes/CBoxAlgorithmMatrixAffineTransformation.hpp"
namespace OpenViBE {
namespace Plugins {
template <typename T>
static void setEnumeration(const Kernel::IPluginModuleContext& context, const CIdentifier& typeID, const std::string& name, const std::vector<T>& enumeration)
{
context.getTypeManager().registerEnumerationType(typeID, name.c_str());
for (const auto& e : enumeration) { context.getTypeManager().registerEnumerationEntry(typeID, toString(e).c_str(), size_t(e)); }
}
OVP_Declare_Begin()
// Register boxes
OVP_Declare_New(Riemannian::CBoxAlgorithmCovarianceMatrixCalculatorDesc);
OVP_Declare_New(Riemannian::CBoxAlgorithmCovarianceMatrixToFeatureVectorDesc);
OVP_Declare_New(Riemannian::CBoxAlgorithmFeatureVectorToCovarianceMatrixDesc);
OVP_Declare_New(Riemannian::CBoxAlgorithmCovarianceMeanCalculatorDesc);
OVP_Declare_New(Riemannian::CBoxAlgorithmMatrixClassifierTrainerDesc);
OVP_Declare_New(Riemannian::CBoxAlgorithmMatrixClassifierProcessorDesc);
OVP_Declare_New(Riemannian::CBoxAlgorithmMatrixAffineTransformationDesc);
// Enumeration Estimator
const std::vector<Geometry::EEstimator> estimators = {
Geometry::EEstimator::COV, Geometry::EEstimator::COR, Geometry::EEstimator::LWF,
Geometry::EEstimator::SCM, Geometry::EEstimator::OAS, Geometry::EEstimator::IDE
};
setEnumeration(context, TypeId_Estimator, "Estimator", estimators);
// Enumeration Metric
const std::vector<Geometry::EMetric> metrics = {
Geometry::EMetric::Riemann, Geometry::EMetric::Euclidian, Geometry::EMetric::LogEuclidian, Geometry::EMetric::LogDet,
Geometry::EMetric::Kullback, Geometry::EMetric::Harmonic, Geometry::EMetric::Identity
};
setEnumeration(context, TypeId_Metric, "Metric", metrics);
// Enumeration Classifier
const std::vector<Geometry::EMatrixClassifiers> classifiers = {
Geometry::EMatrixClassifiers::MDM, Geometry::EMatrixClassifiers::MDM_Rebias,
Geometry::EMatrixClassifiers::FgMDM_RT, Geometry::EMatrixClassifiers::FgMDM_RT_Rebias
};
setEnumeration(context, TypeId_Matrix_Classifier, "Matrix Classifier", classifiers);
// Enumeration Classifier Adaptater
const std::vector<Geometry::EAdaptations> adaptations = {
Geometry::EAdaptations::None, Geometry::EAdaptations::Supervised, Geometry::EAdaptations::Unsupervised
};
setEnumeration(context, TypeId_Classifier_Adaptation, "Classifier Adaptation", adaptations);
OVP_Declare_End()
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,67 @@
#include "utils/misc.hpp"
//*****************************************************
//******************** CONVERSIONS ********************
//*****************************************************
//---------------------------------------------------------------------------------------------------
bool MatrixConvert(const OpenViBE::CMatrix& in, Eigen::MatrixXd& out)
{
if (in.getDimensionCount() != 2) { return false; }
out.resize(in.getDimensionSize(0), in.getDimensionSize(1));
// double loop to avoid the problem of row major and column major storage
size_t idx = 0;
const double* buffer = in.getBuffer();
for (size_t i = 0, nR = out.rows(); i < nR; ++i) { for (size_t j = 0, nC = out.cols(); j < nC; ++j) { out(i, j) = buffer[idx++]; } }
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool MatrixConvert(const Eigen::MatrixXd& in, OpenViBE::CMatrix& out)
{
if (in.rows() == 0 || in.cols() == 0) { return false; }
const size_t nR = in.rows(), nC = in.cols();
out.resize(nR, nC);
out.setNumLabels();
// double loop to avoid the problem of row major and column major storage
size_t idx = 0;
double* buffer = out.getBuffer();
for (size_t i = 0; i < nR; ++i) { for (size_t j = 0; j < nC; ++j) { buffer[idx++] = in(i, j); } }
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool MatrixConvert(const Eigen::RowVectorXd& in, OpenViBE::CMatrix& out)
{
if (in.size() == 0) { return false; }
out.resize(in.size());
//one row system copy doesn't cause problem
std::copy_n(in.data(), out.getSize(), out.getBuffer());
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool MatrixConvert(const OpenViBE::CMatrix& in, Eigen::RowVectorXd& out)
{
if (in.getDimensionCount() != 1) { return false; }
out.resize(in.getDimensionSize(0));
//one row system copy doesn't cause problem
std::copy_n(in.getBuffer(), in.getSize(), out.data());
return true;
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool MatrixConvert(const std::vector<double>& in, OpenViBE::CMatrix& out)
{
if (in.empty()) { return false; }
out.resize(in.size());
//one row system copy doesn't cause problem
std::copy_n(in.data(), out.getSize(), out.getBuffer());
return true;
}
//---------------------------------------------------------------------------------------------------
@@ -0,0 +1,43 @@
///-------------------------------------------------------------------------------------------------
///
/// \file misc.hpp
/// \brief All functions to Convert OpenViBE::CMatrix and Eigen::MatrixXd, links to Eigen function, manipulate OpenVibe::CMatrix and more.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <openvibe/ov_all.h>
#include <Eigen/Dense>
//*****************************************************
//******************** Conversions ********************
//*****************************************************
/// <summary> Convert OpenViBE Matrix to Eigen Matrix. </summary>
/// <param name="in"> The Eigen Matrix. </param>
/// <param name="out"> The OpenVibe Matrix. </param>
bool MatrixConvert(const OpenViBE::CMatrix& in, Eigen::MatrixXd& out);
/// <summary> Convert Eigen Matrix to OpenViBE Matrix (It doesn't use Memory::copy because of Eigne store in column major by default). </summary>
/// <param name="in"> The Eigen Matrix. </param>
/// <param name="out"> The OpenVibe Matrix. </param>
bool MatrixConvert(const Eigen::MatrixXd& in, OpenViBE::CMatrix& out);
/// <summary> Convert Eigen Row Vector to OpenViBE Matrix with one dimension. </summary>
/// <param name="in"> The Eigen Row Vector. </param>
/// <param name="out"> The OpenVibe Matrix. </param>
bool MatrixConvert(const Eigen::RowVectorXd& in, OpenViBE::CMatrix& out);
/// <summary> Convert OpenViBE Matrix with one dimension to Eigen Row Vector. </summary>
/// <param name="in"> The OpenVibe Matrix. </param>
/// <param name="out"> The Eigen Row Vector. </param>
bool MatrixConvert(const OpenViBE::CMatrix& in, Eigen::RowVectorXd& out);
/// <summary> Convertvector double to OpenViBE Matrix with one dimension. </summary>
/// <param name="in"> The Vector of double. </param>
/// <param name="out"> The OpenVibe Matrix. </param>
bool MatrixConvert(const std::vector<double>& in, OpenViBE::CMatrix& out);