init
This commit is contained in:
+88
@@ -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
|
||||
+93
@@ -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
|
||||
+134
@@ -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
|
||||
+93
@@ -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
|
||||
+171
@@ -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
|
||||
+119
@@ -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
|
||||
+134
@@ -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
|
||||
+93
@@ -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
|
||||
+101
@@ -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
|
||||
+97
@@ -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
|
||||
+251
@@ -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
|
||||
+105
@@ -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
|
||||
+231
@@ -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
|
||||
+128
@@ -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);
|
||||
Reference in New Issue
Block a user