init
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "ovpCAlgorithmARBurgMethod.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CAlgorithmARBurgMethod::initialize()
|
||||
{
|
||||
ip_pMatrix.initialize(this->getInputParameter(OVP_Algorithm_ARBurgMethod_InputParameterId_Matrix));
|
||||
op_pMatrix.initialize(this->getOutputParameter(OVP_Algorithm_ARBurgMethod_OutputParameterId_Matrix));
|
||||
ip_Order.initialize(this->getInputParameter(OVP_Algorithm_ARBurgMethod_InputParameterId_UInteger));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmARBurgMethod::uninitialize()
|
||||
{
|
||||
op_pMatrix.uninitialize();
|
||||
ip_pMatrix.uninitialize();
|
||||
ip_Order.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmARBurgMethod::process()
|
||||
{
|
||||
m_order = size_t(ip_Order);
|
||||
|
||||
const size_t nChannel = ip_pMatrix->getDimensionSize(0);
|
||||
const size_t samplesPerChannel = ip_pMatrix->getDimensionSize(1);
|
||||
|
||||
CMatrix* iMatrix = ip_pMatrix;
|
||||
CMatrix* oMatrix = op_pMatrix;
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_ARBurgMethod_InputTriggerId_Initialize))
|
||||
{
|
||||
if (iMatrix->getDimensionCount() != 2)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The input matrix must have 2 dimensions";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iMatrix->getDimensionSize(1) < 2 * m_order)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The input vector must be greater than twice the order";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setting size of output
|
||||
|
||||
oMatrix->resize(nChannel, m_order + 1); // The number of coefficients per channel is equal to the order + 1
|
||||
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
const std::string label = "Channel " + std::to_string(i + 1);
|
||||
oMatrix->setDimensionLabel(0, i, label);
|
||||
}
|
||||
for (size_t i = 0; i < (m_order + 1); ++i)
|
||||
{
|
||||
const std::string label = "ARCoeff " + std::to_string(i + 1);
|
||||
oMatrix->setDimensionLabel(1, i, label);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_ARBurgMethod_InputTriggerId_Process))
|
||||
{
|
||||
// Compute the coefficients for each channel
|
||||
for (size_t j = 0; j < nChannel; ++j)
|
||||
{
|
||||
// Initialization of all needed vectors
|
||||
|
||||
m_errForwardPrediction = Eigen::RowVectorXd::Zero(samplesPerChannel); // Error Forward prediction
|
||||
m_errBackwardPrediction = Eigen::RowVectorXd::Zero(samplesPerChannel); //Error Backward prediction
|
||||
|
||||
m_errForward = Eigen::RowVectorXd::Zero(samplesPerChannel); // Error Forward
|
||||
m_errBackward = Eigen::RowVectorXd::Zero(samplesPerChannel); // Error Backward
|
||||
|
||||
m_arCoefs = Eigen::RowVectorXd::Zero(m_order + 1); // Vector containing the AR coefficients for each channel, it will be our output vector
|
||||
m_error = Eigen::RowVectorXd::Zero(m_order + 1); // Total error
|
||||
|
||||
m_k = 0.0;
|
||||
m_arCoefs(0) = 1.0;
|
||||
|
||||
Eigen::VectorXd arReversed;
|
||||
arReversed = Eigen::VectorXd::Zero(m_order + 1);
|
||||
|
||||
// Retrieving input datas
|
||||
for (size_t i = 0; i < samplesPerChannel; ++i)
|
||||
{
|
||||
m_errForward(i) = iMatrix->getBuffer()[i + j * (samplesPerChannel)]; // Error Forward is the input matrix at first
|
||||
m_errBackward(i) = iMatrix->getBuffer()[i + j * (samplesPerChannel)]; //Error Backward is the input matrix at first
|
||||
|
||||
m_error(0) += (iMatrix->getBuffer()[i + j * (samplesPerChannel)] * iMatrix->getBuffer()[i + j * (samplesPerChannel)]) / samplesPerChannel;
|
||||
}
|
||||
|
||||
// we iterate over the order
|
||||
for (size_t n = 1; n <= m_order; ++n)
|
||||
{
|
||||
const size_t length = samplesPerChannel - n;
|
||||
|
||||
m_errForwardPrediction.resize(length);
|
||||
m_errBackwardPrediction.resize(length);
|
||||
|
||||
m_errForwardPrediction = m_errForward.tail(length);
|
||||
m_errBackwardPrediction = m_errBackward.head(length);
|
||||
|
||||
const double num = -2.0 * m_errBackwardPrediction.dot(m_errForwardPrediction);
|
||||
const double den = (m_errForwardPrediction.dot(m_errForwardPrediction)) + (m_errBackwardPrediction.dot(m_errBackwardPrediction));
|
||||
|
||||
m_k = num / den;
|
||||
|
||||
// Update errors forward and backward vectors
|
||||
|
||||
m_errForward = m_errForwardPrediction + m_k * m_errBackwardPrediction;
|
||||
m_errBackward = m_errBackwardPrediction + m_k * m_errForwardPrediction;
|
||||
|
||||
// Compute the AR coefficients
|
||||
|
||||
for (size_t i = 1; i <= n; ++i) { arReversed(i) = m_arCoefs(n - i); }
|
||||
|
||||
m_arCoefs = m_arCoefs + m_k * arReversed;
|
||||
|
||||
// Update Total Error
|
||||
m_error(n) = (1 - m_k * m_k) * m_error(n - 1);
|
||||
}
|
||||
for (size_t i = 0; i <= m_order; ++i) { oMatrix->getBuffer()[i + j * (m_order + 1)] = m_arCoefs(i); }
|
||||
}
|
||||
this->activateOutputTrigger(OVP_Algorithm_ARBurgMethod_OutputTriggerId_ProcessDone, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyEIGEN
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
# pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CAlgorithmARBurgMethod final : public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_ARBurgMethod)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> ip_pMatrix; // input matrix
|
||||
Kernel::TParameterHandler<CMatrix*> op_pMatrix; // output matrix
|
||||
Kernel::TParameterHandler<uint64_t> ip_Order;
|
||||
|
||||
private:
|
||||
|
||||
Eigen::VectorXd m_errForward; // Error Forward
|
||||
Eigen::VectorXd m_errBackward; //Error Backward
|
||||
Eigen::VectorXd m_arCoefs; // AutoRegressive Coefficents
|
||||
|
||||
Eigen::VectorXd m_errForwardPrediction; // Error Forward prediction
|
||||
Eigen::VectorXd m_errBackwardPrediction; //Error Backward prediction
|
||||
|
||||
Eigen::VectorXd m_error; // Total error vector
|
||||
|
||||
double m_k = 0;
|
||||
size_t m_order = 0;
|
||||
};
|
||||
|
||||
class CAlgorithmARBurgMethodDesc final : public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("AR Burg's Method algorithm"); }
|
||||
CString getAuthorName() const override { return CString("Alison Cellard"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
CString getShortDescription() const override { return CString("Extract AR coefficient using Burg's Method"); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Signal Processing"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
virtual CString getStockItemName() const { return CString("gtk-execute"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_ARBurgMethod; }
|
||||
IPluginObject* create() override { return new CAlgorithmARBurgMethod; }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_ARBurgMethod_InputParameterId_Matrix, "Vector", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_ARBurgMethod_OutputParameterId_Matrix, "Coefficents Vector", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputParameter(OVP_Algorithm_ARBurgMethod_InputParameterId_UInteger, "Order", Kernel::ParameterType_UInteger);
|
||||
|
||||
prototype.addInputTrigger(OVP_Algorithm_ARBurgMethod_InputTriggerId_Initialize, "Initialize");
|
||||
prototype.addInputTrigger(OVP_Algorithm_ARBurgMethod_InputTriggerId_Process, "Process");
|
||||
prototype.addOutputTrigger(OVP_Algorithm_ARBurgMethod_OutputTriggerId_ProcessDone, "Process done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_ARBurgMethodDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyEIGEN
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "ovpCHilbertTransform.h"
|
||||
#include <complex>
|
||||
#include <Eigen/Dense>
|
||||
#include <unsupported/Eigen/FFT>
|
||||
|
||||
bool HilbertTransform::transform(const Eigen::VectorXcd& in, Eigen::VectorXcd& out)
|
||||
{
|
||||
const size_t nSamples = in.size();
|
||||
|
||||
// Resize our buffers if input size has changed
|
||||
if (size_t(m_signalFourier.size()) != nSamples)
|
||||
{
|
||||
m_signalFourier = Eigen::VectorXcd::Zero(nSamples);
|
||||
m_hilbert = Eigen::VectorXcd::Zero(nSamples);
|
||||
|
||||
//Initialization of vector h used to compute analytic signal
|
||||
m_hilbert(0) = 1.0;
|
||||
|
||||
if (nSamples % 2 == 0)
|
||||
{
|
||||
m_hilbert(nSamples / 2) = 1.0;
|
||||
m_hilbert.segment(1, (nSamples / 2) - 1).setOnes();
|
||||
m_hilbert.segment(1, (nSamples / 2) - 1) *= 2.0;
|
||||
m_hilbert.tail((nSamples / 2) + 1).setZero();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hilbert((nSamples + 1) / 2) = 1.0;
|
||||
m_hilbert.segment(1, (nSamples / 2)).setOnes();
|
||||
m_hilbert.segment(1, (nSamples / 2)) *= 2.0;
|
||||
m_hilbert.tail(((nSamples + 1) / 2) + 1).setZero();
|
||||
}
|
||||
}
|
||||
|
||||
// Always resize output for safety
|
||||
out.resize(nSamples);
|
||||
|
||||
//Fast Fourier Transform of input signal
|
||||
m_fft.fwd(m_signalFourier, in);
|
||||
|
||||
//Apply Hilbert transform by element-wise multiplying fft vector by h
|
||||
m_signalFourier = m_signalFourier.cwiseProduct(m_hilbert);
|
||||
|
||||
//Inverse Fast Fourier transform
|
||||
m_fft.inv(out, m_signalFourier); // m_vecXcdSignalBuffer is now the analytical signal of the initial input signal
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CAlgorithmHilbertTransform::initialize()
|
||||
{
|
||||
ip_matrix.initialize(this->getInputParameter(OVP_Algorithm_HilbertTransform_InputParameterId_Matrix));
|
||||
op_hilbertMatrix.initialize(this->getOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_HilbertMatrix));
|
||||
op_envelopeMatrix.initialize(this->getOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_EnvelopeMatrix));
|
||||
op_phaseMatrix.initialize(this->getOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_PhaseMatrix));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmHilbertTransform::uninitialize()
|
||||
{
|
||||
op_hilbertMatrix.uninitialize();
|
||||
op_envelopeMatrix.uninitialize();
|
||||
op_phaseMatrix.uninitialize();
|
||||
ip_matrix.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmHilbertTransform::process()
|
||||
{
|
||||
const size_t nChannel = ip_matrix->getDimensionSize(0);
|
||||
const size_t samplesPerChannel = ip_matrix->getDimensionSize(1);
|
||||
|
||||
CMatrix* matrix = ip_matrix;
|
||||
CMatrix* hilbert = op_hilbertMatrix;
|
||||
CMatrix* envelope = op_envelopeMatrix;
|
||||
CMatrix* phase = op_phaseMatrix;
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_HilbertTransform_InputTriggerId_Initialize)) //Check if the input is correct
|
||||
{
|
||||
if (matrix->getDimensionCount() != 2)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The input matrix must have 2 dimensions, here the dimension is " << matrix->getDimensionCount()
|
||||
<< "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matrix->getDimensionSize(1) < 2)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Can't compute Hilbert transform on data length " << matrix->getDimensionSize(1) << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//Setting size of outputs
|
||||
|
||||
hilbert->resize(nChannel, samplesPerChannel);
|
||||
envelope->resize(nChannel, samplesPerChannel);
|
||||
phase->resize(nChannel, samplesPerChannel);
|
||||
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
hilbert->setDimensionLabel(0, i, matrix->getDimensionLabel(0, i));
|
||||
envelope->setDimensionLabel(0, i, matrix->getDimensionLabel(0, i));
|
||||
phase->setDimensionLabel(0, i, matrix->getDimensionLabel(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_HilbertTransform_InputTriggerId_Process))
|
||||
{
|
||||
//Compute Hilbert transform for each channel separately
|
||||
for (size_t c = 0; c < nChannel; ++c)
|
||||
{
|
||||
// We cannot do a simple ptr assignment here as we need to convert real input to a complex vector
|
||||
Eigen::VectorXcd vecXcdSingleChannel = Eigen::VectorXcd::Zero(samplesPerChannel);
|
||||
const double* buffer = &matrix->getBuffer()[c * samplesPerChannel];
|
||||
for (size_t samples = 0; samples < samplesPerChannel; ++samples)
|
||||
{
|
||||
vecXcdSingleChannel(samples) = buffer[samples];
|
||||
vecXcdSingleChannel(samples).imag(0.0);
|
||||
}
|
||||
|
||||
Eigen::VectorXcd vecXcdSingleChannelTransformed;
|
||||
m_hilbert.transform(vecXcdSingleChannel, vecXcdSingleChannelTransformed);
|
||||
|
||||
//Compute envelope and phase and pass them to the corresponding outputs
|
||||
for (size_t s = 0; s < samplesPerChannel; ++s)
|
||||
{
|
||||
hilbert->getBuffer()[s + c * samplesPerChannel] = vecXcdSingleChannelTransformed(s).imag();
|
||||
envelope->getBuffer()[s + c * samplesPerChannel] = abs(vecXcdSingleChannelTransformed(s));
|
||||
phase->getBuffer()[s + c * samplesPerChannel] = arg(vecXcdSingleChannelTransformed(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif //TARGET_HAS_ThirdPartyEIGEN
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <Eigen/Dense>
|
||||
#include <unsupported/Eigen/FFT>
|
||||
|
||||
// This class could be in its own file
|
||||
class HilbertTransform
|
||||
{
|
||||
public:
|
||||
|
||||
bool transform(const Eigen::VectorXcd& in, Eigen::VectorXcd& out);
|
||||
|
||||
private:
|
||||
Eigen::VectorXcd m_signalFourier; // Fourier Transform of the input signal
|
||||
Eigen::VectorXcd m_hilbert; // Vector h used to apply Hilbert transform
|
||||
|
||||
Eigen::FFT<double, Eigen::internal::kissfft_impl<double>> m_fft; // Instance of the fft transform
|
||||
};
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CAlgorithmHilbertTransform final : public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_HilbertTransform)
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix; //input matrix
|
||||
Kernel::TParameterHandler<CMatrix*> op_hilbertMatrix; //output matrix 1 : Hilbert transform of the signal
|
||||
Kernel::TParameterHandler<CMatrix*> op_envelopeMatrix; //output matrix 2 : Envelope of the signal
|
||||
Kernel::TParameterHandler<CMatrix*> op_phaseMatrix; //output matrix 3 : Phase of the signal
|
||||
|
||||
HilbertTransform m_hilbert; // Instance of the Hilbert transform doing the actual computation
|
||||
};
|
||||
|
||||
class CAlgorithmHilbertTransformDesc final : public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Hilbert Transform"); }
|
||||
CString getAuthorName() const override { return CString("Alison Cellard"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Computes the Hilbert transform of a signal"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("Give the analytic signal ua(t) = u(t) + iH(u(t)) of the input signal u(t) using Hilbert transform");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("0.2"); }
|
||||
virtual CString getStockItemName() const { return CString("gtk-execute"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_HilbertTransform; }
|
||||
IPluginObject* create() override { return new CAlgorithmHilbertTransform; }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_HilbertTransform_InputParameterId_Matrix, "Matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_HilbertMatrix, "Hilbert Matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_EnvelopeMatrix, "Envelope Matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_PhaseMatrix, "Phase Matrix", Kernel::ParameterType_Matrix);
|
||||
|
||||
prototype.addInputTrigger(OVP_Algorithm_HilbertTransform_InputTriggerId_Initialize, "Initialize");
|
||||
prototype.addInputTrigger(OVP_Algorithm_HilbertTransform_InputTriggerId_Process, "Process");
|
||||
prototype.addOutputTrigger(OVP_Algorithm_HilbertTransform_OutputTriggerId_ProcessDone, "Process done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_HilbertTransformDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif //TARGET_HAS_ThirdPartyEIGEN
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyITPP)
|
||||
|
||||
#include "ovpCMatrixVariance.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// the boost version used at the moment of writing this caused 4800 by internal call to "int _isnan" in a bool-returning function.
|
||||
#if defined(WIN32)
|
||||
#pragma warning (disable : 4800)
|
||||
#endif
|
||||
|
||||
#include <boost/math/distributions/students_t.hpp>
|
||||
#include <itpp/base/vec.h>
|
||||
#include <itpp/base/math/elem_math.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CMatrixVariance::initialize()
|
||||
{
|
||||
ip_averagingMethod.initialize(getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_AveragingMethod));
|
||||
ip_matrixCount.initialize(getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_MatrixCount));
|
||||
ip_significanceLevel.initialize(getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_SignificanceLevel));
|
||||
ip_matrix.initialize(getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_Matrix));
|
||||
op_averagedMatrix.initialize(getOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_AveragedMatrix));
|
||||
op_varianceMatrix.initialize(getOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_Variance));
|
||||
op_confidenceBound.initialize(getOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_ConfidenceBound));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMatrixVariance::uninitialize()
|
||||
{
|
||||
for (auto it = m_history.begin(); it != m_history.end(); ++it) { delete *it; }
|
||||
m_history.clear();
|
||||
|
||||
op_averagedMatrix.uninitialize();
|
||||
op_varianceMatrix.uninitialize();
|
||||
op_confidenceBound.uninitialize();
|
||||
ip_matrix.uninitialize();
|
||||
ip_matrixCount.uninitialize();
|
||||
ip_averagingMethod.uninitialize();
|
||||
ip_significanceLevel.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CMatrixVariance::process()
|
||||
{
|
||||
CMatrix* iMatrix = ip_matrix;
|
||||
//CMatrix* oMatrix=op_pAveragedMatrix;
|
||||
|
||||
bool shouldPerformAverage = false;
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_MatrixVariance_InputTriggerId_Reset))
|
||||
{
|
||||
m_mean.set_size(int(ip_matrix->getBufferElementCount()));
|
||||
m_mean.zeros();
|
||||
m_m.set_size(int(ip_matrix->getBufferElementCount()));
|
||||
m_m.zeros();
|
||||
m_variance.set_size(int(ip_matrix->getBufferElementCount()));
|
||||
m_variance.zeros();
|
||||
m_inputCounter = 0;
|
||||
|
||||
for (auto it = m_history.begin(); it != m_history.end(); ++it) { delete *it; }
|
||||
m_history.clear();
|
||||
|
||||
op_averagedMatrix->copyDescription(*iMatrix);
|
||||
op_varianceMatrix->copyDescription(*iMatrix);
|
||||
op_confidenceBound->copyDescription(*iMatrix);
|
||||
}
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_MatrixVariance_InputTriggerId_FeedMatrix))
|
||||
{
|
||||
const int nElement = int(iMatrix->getBufferElementCount());
|
||||
if (ip_averagingMethod == size_t(EEpochAverageMethod::Moving))
|
||||
{
|
||||
//CMatrix* swapMatrix= nullptr;
|
||||
|
||||
if (m_history.size() >= ip_matrixCount)
|
||||
{
|
||||
delete m_history.front();
|
||||
m_history.pop_front();
|
||||
}
|
||||
/*else
|
||||
{
|
||||
swapMatrix=new CMatrix();
|
||||
swapMatrix->copyDescription(*iMatrix);
|
||||
}*/
|
||||
//swapMatrix->copyContent(*iMatrix);
|
||||
|
||||
itpp::Vec<double>* matrices = new itpp::Vec<double>(iMatrix->getBuffer(), nElement);
|
||||
m_history.push_back(matrices);
|
||||
shouldPerformAverage = (m_history.size() == ip_matrixCount);
|
||||
}
|
||||
else if (ip_averagingMethod == size_t(EEpochAverageMethod::MovingImmediate))
|
||||
{
|
||||
//CMatrix* swapMatrix= nullptr;
|
||||
|
||||
if (m_history.size() >= ip_matrixCount)
|
||||
{
|
||||
delete m_history.front();
|
||||
m_history.pop_front();
|
||||
}
|
||||
/*else
|
||||
{
|
||||
swapMatrix=new CMatrix();
|
||||
swapMatrix->copyDescription(*iMatrix);
|
||||
}*/
|
||||
|
||||
//swapMatrix->copyContent(*iMatrix);
|
||||
|
||||
itpp::Vec<double>* matrices = new itpp::Vec<double>(iMatrix->getBuffer(), nElement);
|
||||
m_history.push_back(matrices);
|
||||
shouldPerformAverage = (!m_history.empty());
|
||||
}
|
||||
else if (ip_averagingMethod == size_t(EEpochAverageMethod::Block))
|
||||
{
|
||||
//CMatrix* swapMatrix=new CMatrix();
|
||||
|
||||
if (m_history.size() >= ip_matrixCount)
|
||||
{
|
||||
for (auto it = m_history.begin(); it != m_history.end(); ++it) { delete *it; }
|
||||
m_history.clear();
|
||||
}
|
||||
|
||||
//swapMatrix->copy(*iMatrix);
|
||||
|
||||
itpp::Vec<double>* matrices = new itpp::Vec<double>(iMatrix->getBuffer(), nElement);
|
||||
m_history.push_back(matrices);
|
||||
shouldPerformAverage = (m_history.size() == ip_matrixCount);
|
||||
}
|
||||
else if (ip_averagingMethod == size_t(EEpochAverageMethod::Cumulative))
|
||||
{
|
||||
if (!m_history.empty())
|
||||
{
|
||||
//std::cout << "size of history " << m_history.size() << "\n";
|
||||
delete m_history.front();
|
||||
m_history.pop_front();
|
||||
}
|
||||
//else { std::cout << "history empty \n"; }
|
||||
//CMatrix* swapMatrix=new CMatrix();
|
||||
//swapMatrix->copy(*iMatrix);
|
||||
|
||||
itpp::Vec<double>* matrices = new itpp::Vec<double>(iMatrix->getBuffer(), nElement);
|
||||
m_history.push_back(matrices);
|
||||
shouldPerformAverage = (!m_history.empty());
|
||||
}
|
||||
else { shouldPerformAverage = false; }
|
||||
}
|
||||
|
||||
if (shouldPerformAverage)
|
||||
{
|
||||
if (!m_history.empty())
|
||||
{
|
||||
boost::math::students_t_distribution<double> distrib(2);
|
||||
if (ip_averagingMethod == size_t(EEpochAverageMethod::Cumulative))
|
||||
{
|
||||
//incremental estimation of mean and variance
|
||||
for (auto it = m_history.begin(); it != m_history.end(); ++it)
|
||||
{
|
||||
m_inputCounter++;
|
||||
itpp::Vec<double> buffer = **it;
|
||||
itpp::Vec<double> delta = buffer - m_mean;
|
||||
m_mean += delta / double(m_inputCounter);
|
||||
m_m += elem_mult(delta, (buffer - m_mean));
|
||||
if (m_inputCounter > 1) { m_variance = m_m / double(m_inputCounter - 1); }
|
||||
}
|
||||
distrib = boost::math::students_t_distribution<double>(m_inputCounter <= 1 ? 1 : m_inputCounter - 1);
|
||||
//CMatrix swapMatrix();
|
||||
//getLogManager() << Kernel::LogLevel_Info << "Variance first element " << m_Variance[0] << ", last element " << m_Variance[iMatrix->getBufferElementCount()-1] << "\n";
|
||||
|
||||
memcpy(op_averagedMatrix->getBuffer(), m_mean._data(), iMatrix->getBufferElementCount() * sizeof(double));
|
||||
memcpy(op_varianceMatrix->getBuffer(), m_variance._data(), iMatrix->getBufferElementCount() * sizeof(double));
|
||||
}
|
||||
else
|
||||
{
|
||||
distrib = boost::math::students_t_distribution<double>(double(ip_matrixCount) - 1);
|
||||
|
||||
op_varianceMatrix->resetBuffer();
|
||||
op_averagedMatrix->resetBuffer();
|
||||
|
||||
const size_t count = op_averagedMatrix->getBufferElementCount();
|
||||
const double scale = 1. / m_history.size();
|
||||
|
||||
for (auto& h : m_history)
|
||||
{
|
||||
//batch computation of mean
|
||||
itpp::Vec<double> buffer = *h;
|
||||
double* averageBuffer = op_averagedMatrix->getBuffer();
|
||||
for (int i = 0; i < int(count); ++i)
|
||||
{
|
||||
*averageBuffer += buffer[i] * scale;
|
||||
averageBuffer++;
|
||||
}
|
||||
//batch computation of variance
|
||||
averageBuffer = op_averagedMatrix->getBuffer();
|
||||
double* matrixVarianceBuffer = op_varianceMatrix->getBuffer();
|
||||
for (int i = 0; i < int(count); ++i)
|
||||
{
|
||||
*matrixVarianceBuffer += (buffer[i] - *(averageBuffer + i)) * (buffer[i] - *(averageBuffer + i)) / (m_history.size() - 1.0F);
|
||||
matrixVarianceBuffer++;
|
||||
}
|
||||
}
|
||||
m_variance = itpp::Vec<double>(op_averagedMatrix->getBuffer(), int(count));
|
||||
}
|
||||
|
||||
//computing confidence bounds
|
||||
const double q = double(quantile(complement(distrib, ip_significanceLevel / 2.0)));
|
||||
getLogManager() << Kernel::LogLevel_Debug << "Quantile at " << ip_significanceLevel << " is " << q << "\n";
|
||||
itpp::Vec<double> bound;
|
||||
if (ip_averagingMethod == size_t(EEpochAverageMethod::Cumulative)) { bound = (q / sqrt(double(m_inputCounter))) * itpp::sqrt(m_variance); }
|
||||
else { bound = (q / double(ip_matrixCount)) * itpp::sqrt(m_variance); }
|
||||
memcpy(op_confidenceBound->getBuffer(), bound._data(), iMatrix->getBufferElementCount() * sizeof(double));
|
||||
}
|
||||
|
||||
this->activateOutputTrigger(OVP_Algorithm_MatrixVariance_OutputTriggerId_AveragePerformed, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyITPP)
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <itpp/base/vec.h>
|
||||
#include <deque>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CMatrixVariance final : public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_MatrixVariance)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<uint64_t> ip_averagingMethod;
|
||||
Kernel::TParameterHandler<uint64_t> ip_matrixCount;
|
||||
Kernel::TParameterHandler<double> ip_significanceLevel;
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_averagedMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_varianceMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_confidenceBound;
|
||||
|
||||
std::deque<itpp::Vec<double>*> m_history;
|
||||
|
||||
itpp::Vec<double> m_mean;
|
||||
itpp::Vec<double> m_m;
|
||||
itpp::Vec<double> m_variance;
|
||||
size_t m_inputCounter = 0;
|
||||
};
|
||||
|
||||
class CMatrixVarianceDesc final : public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Matrix variance"); }
|
||||
CString getAuthorName() const override { return CString("Dieter Devlaminck"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
CString getShortDescription() const override { return CString(""); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_MatrixVariance; }
|
||||
IPluginObject* create() override { return new CMatrixVariance(); }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_Matrix, "Matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_MatrixCount, "Matrix count", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_SignificanceLevel, "Significance Level", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_AveragingMethod, "Averaging Method", Kernel::ParameterType_UInteger);
|
||||
|
||||
prototype.addOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_AveragedMatrix, "Averaged matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_Variance, "Matrix variance", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_ConfidenceBound, "Confidence bound", Kernel::ParameterType_Matrix);
|
||||
|
||||
prototype.addInputTrigger(OVP_Algorithm_MatrixVariance_InputTriggerId_Reset, "Reset");
|
||||
prototype.addInputTrigger(OVP_Algorithm_MatrixVariance_InputTriggerId_FeedMatrix, "Feed matrix");
|
||||
prototype.addInputTrigger(OVP_Algorithm_MatrixVariance_InputTriggerId_ForceAverage, "Force average");
|
||||
|
||||
prototype.addOutputTrigger(OVP_Algorithm_MatrixVariance_OutputTriggerId_AveragePerformed, "Average performed");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_MatrixVarianceDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file windowFunctions.cpp
|
||||
/// \brief Implementation of Windowing functions
|
||||
/// \author Alison Cellard
|
||||
/// \version 1.0
|
||||
/// \date 13/11/2013
|
||||
///
|
||||
/// \copyright (C) 2021 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "windowFunctions.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
namespace WindowFunctions {
|
||||
|
||||
bool bartlett(Eigen::VectorXd& window, const size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if (i <= (size - 1) / 2)
|
||||
{
|
||||
window(i) = 2. * i / (size - 1);
|
||||
}
|
||||
else if (i < size) {
|
||||
window(i) = 2. * ((size - 1) - i) / (size - 1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hamming(Eigen::VectorXd& window, const size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
window(i) = 0.54 - 0.46 * cos(2. * M_PI * i / (size - 1));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hann(Eigen::VectorXd& window, const size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
window(i) = 0.5 - 0.5 * cos(2. * M_PI * i / (size - 1));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool parzen(Eigen::VectorXd& window, const size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
window(i) = 1. - pow((i - (size - 1.) / 2.) / ((size + 1.) / 2.), 2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool welch(Eigen::VectorXd& window, const size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
window(i) = 1.0 - fabs((i - (size - 1.0) / 2.0) / ((size + 1.0) / 2.0));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} //namespace WindowFunctions
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif //TARGET_HAS_ThirdPartyEIGEN
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file windowFunctions.hpp
|
||||
/// \brief Windowing functions and helpers for Connectivity Measure
|
||||
/// \author Alison Cellard
|
||||
/// \version 1.0
|
||||
/// \date 13/11/2013
|
||||
///
|
||||
/// \copyright (C) 2021 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <Eigen/Dense>
|
||||
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
namespace WindowFunctions {
|
||||
|
||||
///
|
||||
/// \brief Generate Bartlett window
|
||||
/// \param windowBuffer The buffer in which the window is stored
|
||||
/// \param size The size of the window
|
||||
/// \return True
|
||||
bool bartlett(Eigen::VectorXd& window, const size_t size);
|
||||
|
||||
///
|
||||
/// \brief Generate Hamming window
|
||||
/// \param windowBuffer The buffer in which the window is stored
|
||||
/// \param size The size of the window
|
||||
/// \return True
|
||||
bool hamming(Eigen::VectorXd& window, const size_t size);
|
||||
|
||||
///
|
||||
/// \brief Generate Hann window
|
||||
/// \param windowBuffer The buffer in which the window is stored
|
||||
/// \param size The size of the window
|
||||
/// \return True
|
||||
bool hann(Eigen::VectorXd& window, const size_t size);
|
||||
|
||||
///
|
||||
/// \brief Generate Parzen window
|
||||
/// \param windowBuffer The buffer in which the window is stored
|
||||
/// \param size The size of the window
|
||||
/// \return True
|
||||
bool parzen(Eigen::VectorXd& window, const size_t size);
|
||||
|
||||
///
|
||||
/// \brief Generate Welch window
|
||||
/// \param windowBuffer The buffer in which the window is stored
|
||||
/// \param size The size of the window
|
||||
/// \return True
|
||||
bool welch(Eigen::VectorXd& window, const size_t size);
|
||||
|
||||
} // namespace WindowFunctions
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif //TARGET_HAS_ThirdPartyEIGEN
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file connectivityMeasureMetrics.cpp
|
||||
/// \brief All connectivity metrics.
|
||||
/// \author Arthur Desbois (Inria).
|
||||
/// \version 1.0
|
||||
/// \date 30/10/2020
|
||||
///
|
||||
/// \copyright (C) 2020 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <unsupported/Eigen/FFT>
|
||||
|
||||
#include "connectivityMeasure.hpp"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <windowFunctions.hpp>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
//************************************************************************
|
||||
//******* Connectivity measurements & associated helper functions ********
|
||||
//************************************************************************
|
||||
|
||||
bool ConnectivityMeasure::initialize(const EConnectMetric metric,
|
||||
EConnectWindowMethod windowMethod,
|
||||
int windowLength,
|
||||
int windowOverlap,
|
||||
size_t nbChannels,
|
||||
size_t fftSize,
|
||||
bool dcRemoval)
|
||||
{
|
||||
m_metric = metric;
|
||||
m_windowMethod = windowMethod;
|
||||
|
||||
m_windowLength = windowLength; // size of one welch window (samples)
|
||||
m_windowOverlap = windowOverlap; // overlap btw windows (%)
|
||||
m_windowOverlapSamples = std::floor(double(m_windowLength * windowOverlap) / 100.0);
|
||||
|
||||
m_nbChannels = nbChannels;
|
||||
m_fftSize = fftSize;
|
||||
m_dcRemoval = dcRemoval;
|
||||
|
||||
m_window = Eigen::VectorXd::Zero(m_windowLength);
|
||||
switch (m_windowMethod)
|
||||
{
|
||||
case EConnectWindowMethod::Hamming:
|
||||
WindowFunctions::hamming(m_window, m_windowLength);
|
||||
break;
|
||||
case EConnectWindowMethod::Hann:
|
||||
WindowFunctions::hann(m_window, m_windowLength);
|
||||
break;
|
||||
case EConnectWindowMethod::Welch:
|
||||
WindowFunctions::welch(m_window, m_windowLength);
|
||||
break;
|
||||
}
|
||||
|
||||
// window normalization constant
|
||||
m_u = 0;
|
||||
for (size_t i = 0; i < size_t(m_window.size()); ++i)
|
||||
{
|
||||
m_u += std::pow(m_window(i), 2);
|
||||
}
|
||||
|
||||
// Instantiate the big spectra & x-spectra arrays
|
||||
m_dft.resize(m_nbChannels); // vector of channels x fftsize
|
||||
m_psd.resize(m_nbChannels); // vector of channels x fftsize
|
||||
m_cpsd.resize(m_nbChannels); // matrix of channels x channels x fftsize
|
||||
for (size_t chan = 0; chan < m_nbChannels; chan++)
|
||||
{
|
||||
m_cpsd[chan].resize(m_nbChannels);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectivityMeasure::process(const std::vector <Eigen::VectorXd>& samples,
|
||||
std::vector <Eigen::MatrixXd>& connectivityMatrix)
|
||||
{
|
||||
// Windowing
|
||||
size_t windowShift = m_window.size() - m_windowOverlapSamples;
|
||||
size_t nbWindows = std::floor(double(samples[0].size() - windowShift) / double(windowShift));
|
||||
|
||||
// Remove the DC component of each channel
|
||||
std::vector <Eigen::VectorXd> mySamples;
|
||||
if (m_dcRemoval)
|
||||
{
|
||||
for (size_t chan = 0; chan < m_nbChannels; chan++)
|
||||
{
|
||||
Eigen::VectorXd temp = samples[chan] - Eigen::VectorXd::Ones(samples[chan].size()) * samples[chan].mean();
|
||||
mySamples.push_back(temp);
|
||||
}
|
||||
} else
|
||||
{
|
||||
mySamples = samples;
|
||||
}
|
||||
|
||||
// TODO : don't recompute the whole set of DFTs, because connectivity measures overlap !
|
||||
// Periodigrams (DFTs)
|
||||
for (size_t chan = 0; chan < m_nbChannels; chan++)
|
||||
{
|
||||
periodogram(mySamples[chan], m_dft[chan], nbWindows);
|
||||
}
|
||||
|
||||
// PSDs
|
||||
for (size_t chan = 0; chan < m_nbChannels; chan++)
|
||||
{
|
||||
powerSpectralDensity(m_dft[chan], m_psd[chan], nbWindows);
|
||||
}
|
||||
|
||||
|
||||
// Cross-SPECTRA
|
||||
for (size_t chan1 = 0; chan1 < m_nbChannels; chan1++)
|
||||
{
|
||||
for (size_t chan2 = chan1; chan2 < m_nbChannels; chan2++)
|
||||
{
|
||||
crossSpectralDensity(m_dft[chan1], m_dft[chan2], m_cpsd[chan1][chan2], nbWindows);
|
||||
}
|
||||
}
|
||||
|
||||
// Use PSDs & x-spectra to compute the connectivity matrix
|
||||
switch (m_metric)
|
||||
{
|
||||
case EConnectMetric::Coherence:
|
||||
return coherence(connectivityMatrix);
|
||||
case EConnectMetric::MagnitudeSquaredCoherence:
|
||||
return magnitudeSquaredCoherence(connectivityMatrix);
|
||||
case EConnectMetric::ImaginaryCoherence:
|
||||
return imaginaryCoherence(connectivityMatrix);
|
||||
case EConnectMetric::AbsImaginaryCoherence:
|
||||
return absImaginaryCoherence(connectivityMatrix);
|
||||
default:
|
||||
return coherence(connectivityMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectivityMeasure::periodogram(const Eigen::VectorXd& input,
|
||||
Eigen::MatrixXcd& periodograms,
|
||||
const size_t& nSegments)
|
||||
{
|
||||
periodograms = Eigen::MatrixXcd::Zero(m_fftSize, nSegments);
|
||||
|
||||
// Cut input vector into segments, and apply window to each segment
|
||||
for (size_t k = 0; k < nSegments; ++k)
|
||||
{
|
||||
Eigen::VectorXd segment = Eigen::VectorXd::Zero(m_fftSize);
|
||||
|
||||
for (size_t i = 0; i < size_t(m_windowLength); ++i)
|
||||
{
|
||||
segment(i) = input(i + k * m_windowOverlapSamples) * m_window(i);
|
||||
}
|
||||
|
||||
Eigen::VectorXcd dft = Eigen::VectorXcd::Zero(m_fftSize);
|
||||
|
||||
m_fft.fwd(dft, segment, m_fftSize);
|
||||
periodograms.col(k) = dft;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ConnectivityMeasure::powerSpectralDensity(const Eigen::MatrixXcd& dft,
|
||||
Eigen::VectorXd& output,
|
||||
const size_t& nSegments)
|
||||
{
|
||||
// output(i) will be the power for the band i across segments (time) as summed from the periodogram
|
||||
output = Eigen::VectorXd::Zero(m_fftSize);
|
||||
|
||||
for (size_t k = 0; k < nSegments; ++k)
|
||||
{
|
||||
output += dft.col(k).cwiseAbs2();
|
||||
}
|
||||
double factor = double(nSegments) * m_u;
|
||||
output /= factor;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ConnectivityMeasure::crossSpectralDensity(const Eigen::MatrixXcd& dft1,
|
||||
const Eigen::MatrixXcd& dft2,
|
||||
Eigen::VectorXcd& output,
|
||||
const size_t& nSegments)
|
||||
{
|
||||
output = Eigen::VectorXcd::Zero(m_fftSize);
|
||||
|
||||
for (size_t k = 0; k < nSegments; ++k)
|
||||
{
|
||||
output += dft2.col(k).cwiseProduct(dft1.col(k).conjugate());
|
||||
}
|
||||
double factor = double(nSegments) * m_u;
|
||||
output /= factor;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ConnectivityMeasure::coherence(std::vector <Eigen::MatrixXd>& connectivityMatrix)
|
||||
{
|
||||
for (size_t chan1 = 0; chan1 < m_nbChannels; chan1++)
|
||||
{
|
||||
|
||||
connectivityMatrix[chan1].row(chan1) = Eigen::VectorXd::Zero(m_psd[0].size());
|
||||
|
||||
for (size_t chan2 = chan1 + 1; chan2 < m_nbChannels; chan2++)
|
||||
{
|
||||
connectivityMatrix[chan1].row(chan2) = m_cpsd[chan1][chan2].cwiseAbs2().cwiseQuotient(
|
||||
m_psd[chan1].cwiseProduct(m_psd[chan2])).cwiseSqrt();
|
||||
connectivityMatrix[chan2].row(chan1) = connectivityMatrix[chan1].row(chan2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectivityMeasure::magnitudeSquaredCoherence(std::vector <Eigen::MatrixXd>& connectivityMatrix)
|
||||
{
|
||||
for (size_t chan1 = 0; chan1 < m_nbChannels; chan1++)
|
||||
{
|
||||
|
||||
connectivityMatrix[chan1].row(chan1) = Eigen::VectorXd::Zero(m_psd[0].size());
|
||||
|
||||
for (size_t chan2 = chan1 + 1; chan2 < m_nbChannels; chan2++)
|
||||
{
|
||||
|
||||
connectivityMatrix[chan1].row(chan2) = m_cpsd[chan1][chan2].cwiseAbs2().cwiseQuotient( m_psd[chan1].cwiseProduct(m_psd[chan2]) );
|
||||
connectivityMatrix[chan2].row(chan1) = connectivityMatrix[chan1].row(chan2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectivityMeasure::imaginaryCoherence(std::vector <Eigen::MatrixXd>& connectivityMatrix)
|
||||
{
|
||||
for (size_t chan1 = 0; chan1 < m_nbChannels; chan1++)
|
||||
{
|
||||
|
||||
connectivityMatrix[chan1].row(chan1) = Eigen::VectorXd::Zero(m_psd[0].size());
|
||||
|
||||
for (size_t chan2 = chan1 + 1; chan2 < m_nbChannels; chan2++)
|
||||
{
|
||||
|
||||
connectivityMatrix[chan1].row(chan2) = m_cpsd[chan1][chan2].imag().cwiseQuotient(
|
||||
m_psd[chan1].cwiseProduct(m_psd[chan2]).cwiseSqrt());
|
||||
connectivityMatrix[chan2].row(chan1) = connectivityMatrix[chan1].row(chan2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectivityMeasure::absImaginaryCoherence(std::vector <Eigen::MatrixXd>& connectivityMatrix)
|
||||
{
|
||||
for (size_t chan1 = 0; chan1 < m_nbChannels; chan1++)
|
||||
{
|
||||
|
||||
connectivityMatrix[chan1].row(chan1) = Eigen::VectorXd::Zero(m_psd[0].size());
|
||||
|
||||
for (size_t chan2 = chan1 + 1; chan2 < m_nbChannels; chan2++)
|
||||
{
|
||||
|
||||
connectivityMatrix[chan1].row(chan2) = m_cpsd[chan1][chan2].imag().cwiseAbs().cwiseQuotient(
|
||||
m_psd[chan1].cwiseProduct(m_psd[chan2]).cwiseSqrt());
|
||||
connectivityMatrix[chan2].row(chan1) = connectivityMatrix[chan1].row(chan2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file connectivityMeasureMetrics.hpp
|
||||
/// \brief All connectivity metrics.
|
||||
/// \author Arthur Desbois (Inria).
|
||||
/// \version 1.0
|
||||
/// \date 30/10/2020
|
||||
///
|
||||
/// \copyright (C) 2020 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <unsupported/Eigen/FFT>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
/// \brief Enumeration of metrics.
|
||||
enum class EConnectMetric
|
||||
{
|
||||
Coherence,
|
||||
MagnitudeSquaredCoherence,
|
||||
ImaginaryCoherence,
|
||||
AbsImaginaryCoherence
|
||||
};
|
||||
|
||||
/// \brief Convert Metrics to string.
|
||||
/// \param metric The metric.
|
||||
/// \return The metric as human readable string
|
||||
inline std::string toString(const EConnectMetric metric)
|
||||
{
|
||||
switch (metric)
|
||||
{
|
||||
case EConnectMetric::Coherence:
|
||||
return "Coherence";
|
||||
case EConnectMetric::MagnitudeSquaredCoherence:
|
||||
return "MagnitudeSquaredCoherence";
|
||||
case EConnectMetric::ImaginaryCoherence:
|
||||
return "ImaginaryCoherence";
|
||||
case EConnectMetric::AbsImaginaryCoherence:
|
||||
return "AbsImaginaryCoherence";
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Convert string to Metric.
|
||||
/// \param metric The metric as a string
|
||||
/// \return \ref EConnectMetric
|
||||
inline EConnectMetric StringToMetric(const std::string& metric)
|
||||
{
|
||||
if (metric == "Coherence")
|
||||
{ return EConnectMetric::Coherence; }
|
||||
if (metric == "MagnitudeSquaredCoherence")
|
||||
{ return EConnectMetric::MagnitudeSquaredCoherence; }
|
||||
if (metric == "ImaginaryCoherence")
|
||||
{ return EConnectMetric::ImaginaryCoherence; }
|
||||
if (metric == "AbsImaginaryCoherence")
|
||||
{ return EConnectMetric::AbsImaginaryCoherence; }
|
||||
|
||||
return EConnectMetric::Coherence; // default
|
||||
}
|
||||
|
||||
/// \brief Enumeration of windowing methods
|
||||
enum class EConnectWindowMethod
|
||||
{
|
||||
Hamming,
|
||||
Hann,
|
||||
Welch
|
||||
};
|
||||
|
||||
/// \brief Convert Window method to string.
|
||||
/// \param metric The metric.
|
||||
/// \return the window method as human readable string
|
||||
inline std::string toString(const EConnectWindowMethod winMethod)
|
||||
{
|
||||
switch (winMethod)
|
||||
{
|
||||
case EConnectWindowMethod::Hamming:
|
||||
return "Hamming";
|
||||
case EConnectWindowMethod::Hann:
|
||||
return "Hann";
|
||||
case EConnectWindowMethod::Welch:
|
||||
return "Welch";
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectivityMeasure
|
||||
{
|
||||
public:
|
||||
ConnectivityMeasure()
|
||||
{};
|
||||
|
||||
~ConnectivityMeasure()
|
||||
{};
|
||||
|
||||
bool initialize(const EConnectMetric metric,
|
||||
EConnectWindowMethod windowMethod,
|
||||
int windowLength, int windowOverlap,
|
||||
size_t nbChannels, size_t fftSize,
|
||||
bool dcRemoval);
|
||||
|
||||
/// \brief Select the function to call for the connectivity measurement.
|
||||
/// \param samples The input data set \f$x\f$. With \f$ nChan \f$ channels and \f$ nSamp \f$ samples per channel
|
||||
/// \param connectivityMatrix The connectivity matrix
|
||||
/// \param metric The chosen metric
|
||||
/// \param windowsMethod The windowing method
|
||||
/// \param windowLength The length of one window of processing (eg for Welch)
|
||||
/// \param windowOverlap The overlap btw windows
|
||||
/// \param connectLength The length of one connectivity measurement window
|
||||
/// \param connectOverlap The overlap btw connectivity windows
|
||||
bool process(const std::vector <Eigen::VectorXd>& samples,
|
||||
std::vector <Eigen::MatrixXd>& connectivityMatrix);
|
||||
|
||||
private:
|
||||
/// \brief Calculation of the Coherence
|
||||
/// \f$ Pxx = PSD of x \f$ // \f$ Pyy = PSD of y \f$ // \f$ Sxy = cross spectral density of x and y \f$
|
||||
/// \f$ MSC = \frac{\left| Sxy \right|}{sqrt{(Pxx.Pyy)} } \f$
|
||||
///
|
||||
/// The connectivity matrix is symmetrical, so we only need to compute its top part
|
||||
/// and discard the diagonal (no need to compute connectivity of a single channel...)
|
||||
/// eg [0 1 2 3] :
|
||||
/// [ 01 02 03]
|
||||
/// [ 12 13]
|
||||
/// [ 23]
|
||||
/// [ ]
|
||||
/// \param connectivityMatrix The connectivity matrix as a vector of 2D Matrices of size nbChannels x (nbChannles x frequency_taps)
|
||||
/// \return True if it succeeds, false if it fails.
|
||||
/// \todo don't recompute the whole table, because connectivity measures overlap !
|
||||
bool coherence(std::vector <Eigen::MatrixXd>& connectivityMatrix);
|
||||
|
||||
/// \brief Calculation of the Magnitude Squared Coherence
|
||||
/// \f$ Pxx = PSD of x \f$ // \f$ Pyy = PSD of y \f$ // \f$ Sxy = cross spectral density of x and y \f$
|
||||
/// \f$ MSC = \frac{\left| Sxy \right|^2}{(Pxx.Pyy)} \f$
|
||||
///
|
||||
/// The connectivity matrix is symmetrical, so we only need to compute its top part
|
||||
/// and discard the diagonal (no need to compute connectivity of a single channel...)
|
||||
/// eg [0 1 2 3] :
|
||||
/// [ 01 02 03]
|
||||
/// [ 12 13]
|
||||
/// [ 23]
|
||||
/// [ ]
|
||||
/// \param connectivityMatrix The connectivity matrix as a vector of 2D Matrices of size nbChannels x (nbChannles x frequency_taps)
|
||||
/// \return True if it succeeds, false if it fails.
|
||||
/// \todo don't recompute the whole table, because connectivity measures overlap !
|
||||
bool magnitudeSquaredCoherence(std::vector <Eigen::MatrixXd>& connectivityMatrix);
|
||||
|
||||
/// \brief Calculation of the Imaginary part of the coherence
|
||||
/// \f$ Pxx = PSD of x \f$ // \f$ Pyy = PSD of y \f$ // \f$ Sxy = cross spectral density of x and y \f$
|
||||
/// \f$ ImC = \frac{Im(Sxy)}{sqrt{Pxx.Pyy} } \f$
|
||||
/// The connectivity matrix is symmetrical, so we only need to compute
|
||||
/// its top part, and discard the diagonal (no need to compute connectivity of a single channel...)
|
||||
/// eg [0 1 2 3] :
|
||||
/// [ 01 02 03]
|
||||
/// [ 12 13]
|
||||
/// [ 23]
|
||||
/// [ ]
|
||||
/// \param connectivityMatrix The connectivity matrix as a vector of 2D Matrices of size nbChannels x (nbChannles x frequency_taps)
|
||||
/// \return True if it succeeds, false if it fails.
|
||||
/// \todo don't recompute the whole table, because connectivity measures overlap !
|
||||
bool imaginaryCoherence(std::vector <Eigen::MatrixXd>& connectivityMatrix);
|
||||
|
||||
/// \brief Calculation of the absolute value of the Imaginary part of the coherence
|
||||
/// \f$ Pxx = PSD of x \f$ // \f$ Pyy = PSD of y \f$ // \f$ Sxy = cross spectral density of x and y \f$
|
||||
/// \f$ ImC = \frac{\left| Im(Sxy) \right|}{sqrt{Pxx.Pyy} } \f$
|
||||
/// The connectivity matrix is symmetrical, so we only need to compute
|
||||
/// its top part, and discard the diagonal (no need to compute connectivity of a single channel...)
|
||||
/// eg [0 1 2 3] :
|
||||
/// [ 01 02 03]
|
||||
/// [ 12 13]
|
||||
/// [ 23]
|
||||
/// [ ]
|
||||
/// \param connectivityMatrix The connectivity matrix as a vector of 2D Matrices of size nbChannels x (nbChannles x frequency_taps)
|
||||
/// \return True if it succeeds, false if it fails.
|
||||
/// \todo don't recompute the whole table, because connectivity measures overlap !
|
||||
bool absImaginaryCoherence(std::vector <Eigen::MatrixXd>& connectivityMatrix);
|
||||
|
||||
///
|
||||
/// \brief Generates periodigram of signal
|
||||
/// \param input The signal
|
||||
/// \param periodograms The generated periodigram
|
||||
/// \param nSegments
|
||||
/// \return True on succes, false otherwise
|
||||
bool periodogram(const Eigen::VectorXd& input,
|
||||
Eigen::MatrixXcd& periodograms,
|
||||
const size_t& nSegments);
|
||||
|
||||
///
|
||||
/// \brief Computes the spectral density of a spectrum
|
||||
/// \param dft The discrete fourier transform
|
||||
/// \param output The generated spectral density
|
||||
/// \param nSegments
|
||||
/// \return True on success, false otherwise
|
||||
bool powerSpectralDensity(const Eigen::MatrixXcd& dft,
|
||||
Eigen::VectorXd& output,
|
||||
const size_t& nSegments);
|
||||
|
||||
///
|
||||
/// \brief Computes cross spectral density of 2 spectra
|
||||
/// Only computes the top diagonal of the CPSD matrix
|
||||
/// eg [0 1 2 3] :
|
||||
/// [00 01 02 03]
|
||||
/// [ 11 12 13]
|
||||
/// [ 22 23]
|
||||
/// [ 33]
|
||||
/// \param dft1 The first discrete fourier transform
|
||||
/// \param dft2 The second discrete fourier transform
|
||||
/// \param output The generated cross spectral density matrix
|
||||
/// \param nSegments
|
||||
/// \return True on success, false otherwise
|
||||
///
|
||||
/// \todo : don't compute only the top part, because some connectivity measurements are not symmetrical ?
|
||||
bool crossSpectralDensity(const Eigen::MatrixXcd& dft1,
|
||||
const Eigen::MatrixXcd& dft2,
|
||||
Eigen::VectorXcd& output,
|
||||
const size_t& nSegments);
|
||||
|
||||
// Parameters / Members
|
||||
Eigen::FFT<double, Eigen::internal::kissfft_impl<double>> m_fft; // Instance of the fft transform
|
||||
|
||||
EConnectMetric m_metric = EConnectMetric::Coherence;
|
||||
EConnectWindowMethod m_windowMethod = EConnectWindowMethod::Hann;
|
||||
Eigen::VectorXd m_window; // Window used for Welch method
|
||||
double m_u = 0; // Window normalization factor
|
||||
|
||||
std::vector <Eigen::MatrixXcd> m_dft; // Discrete Fourier Transform
|
||||
std::vector <Eigen::VectorXd> m_psd; // Power Spectral Density
|
||||
std::vector <std::vector<Eigen::VectorXcd>> m_cpsd; // Cross Power Spectral Density
|
||||
|
||||
int m_windowLength = 128; // size of one Welch window (samples)
|
||||
int m_windowOverlap = 50; // overlap btw windows (%)
|
||||
int m_windowOverlapSamples = 64;
|
||||
int m_nbWindows = 8;
|
||||
|
||||
size_t m_fftSize = 256;
|
||||
size_t m_nbChannels = 1;
|
||||
|
||||
bool m_dcRemoval = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file CBoxAlgorithmMatrix3dTo2d.cpp
|
||||
/// \brief Implementation of the box Matrix3dTo2d
|
||||
/// \author Arthur DESBOIS (INRIA).
|
||||
/// \version 0.0.1.
|
||||
/// \date Fri Feb 12 15:13:00 2021.
|
||||
///
|
||||
/// \copyright (C) 2021 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#include "CBoxAlgorithmMatrix3dTo2d.hpp"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmMatrix3dTo2d::initialize()
|
||||
{
|
||||
m_matrixDecoder.initialize(*this, 0);
|
||||
m_matrixEncoder.initialize(*this, 0);
|
||||
|
||||
m_iMatrix = m_matrixDecoder.getOutputMatrix();
|
||||
m_oMatrix = m_matrixEncoder.getInputMatrix();
|
||||
|
||||
m_dimensionToRemove = int(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0));
|
||||
m_removedDimensionIdx = int(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1));
|
||||
|
||||
OV_ERROR_UNLESS_KRF(m_dimensionToRemove == 0 || m_dimensionToRemove == 1 || m_dimensionToRemove == 2, "Invalid dimension number", Kernel::ErrorType::BadInput);
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmMatrix3dTo2d::uninitialize()
|
||||
{
|
||||
m_matrixDecoder.uninitialize();
|
||||
m_matrixEncoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
bool CBoxAlgorithmMatrix3dTo2d::processInput(const size_t index)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
bool CBoxAlgorithmMatrix3dTo2d::process()
|
||||
{
|
||||
const Kernel::IBox& staticBoxContext=this->getStaticBoxContext();
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_matrixDecoder.decode(i);
|
||||
|
||||
if(m_matrixDecoder.isHeaderReceived())
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(m_iMatrix->getDimensionCount() == 3, "Input matrix should have 3 dimensions", Kernel::ErrorType::BadInput);
|
||||
|
||||
m_dim0Size = m_iMatrix->getDimensionSize(0);
|
||||
m_dim1Size = m_iMatrix->getDimensionSize(1);
|
||||
m_dim2Size = m_iMatrix->getDimensionSize(2);
|
||||
|
||||
switch(m_dimensionToRemove) {
|
||||
case 0:
|
||||
OV_ERROR_UNLESS_KRF(m_dim0Size > m_removedDimensionIdx, "Idx in removed dimension over dimension size", Kernel::ErrorType::BadInput);
|
||||
MatrixInit(*m_oMatrix, m_dim1Size, m_dim2Size);
|
||||
break;
|
||||
case 1:
|
||||
OV_ERROR_UNLESS_KRF(m_dim1Size > m_removedDimensionIdx, "Idx in removed dimension over dimension size", Kernel::ErrorType::BadInput);
|
||||
MatrixInit(*m_oMatrix, m_dim0Size, m_dim2Size);
|
||||
break;
|
||||
case 2:
|
||||
OV_ERROR_UNLESS_KRF(m_dim2Size > m_removedDimensionIdx, "Idx in removed dimension over dimension size", Kernel::ErrorType::BadInput);
|
||||
MatrixInit(*m_oMatrix, m_dim0Size, m_dim1Size);
|
||||
break;
|
||||
}
|
||||
|
||||
m_matrixEncoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if(m_matrixDecoder.isBufferReceived())
|
||||
{
|
||||
RemoveDimension(*m_iMatrix, *m_oMatrix);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Debug<< "Received matrix with dimensions " << m_iMatrix->getDimensionSize(0) << " x " << m_iMatrix->getDimensionSize(1) << " x " << m_iMatrix->getDimensionSize(2) << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Output matrix has dimensions " << m_oMatrix->getDimensionSize(0) << " x " << m_oMatrix->getDimensionSize(1) << "\n";
|
||||
|
||||
m_matrixEncoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
|
||||
}
|
||||
if(m_matrixDecoder.isEndReceived())
|
||||
{
|
||||
m_matrixEncoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrix3dTo2d::MatrixInit(IMatrix& out, const size_t dim0, const size_t dim1)
|
||||
{
|
||||
out.setDimensionCount(2);
|
||||
out.setDimensionSize(0, dim0);
|
||||
out.setDimensionSize(1, dim1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrix3dTo2d::RemoveDimension(const IMatrix& in, IMatrix& out)
|
||||
{
|
||||
size_t idxOutBuffer = 0;
|
||||
|
||||
const double* inBuffer = in.getBuffer();
|
||||
double* outBuffer = out.getBuffer();
|
||||
|
||||
switch(m_dimensionToRemove) {
|
||||
case 0:
|
||||
for(size_t idx1 = 0; idx1 < m_dim1Size; idx1++) {
|
||||
for(size_t idx2 = 0; idx2 < m_dim2Size; idx2++) {
|
||||
outBuffer[idxOutBuffer++] = inBuffer[m_removedDimensionIdx*m_dim1Size*m_dim2Size + idx1*m_dim2Size + idx2];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
for(size_t idx0 = 0; idx0 < m_dim0Size; idx0++) {
|
||||
for(size_t idx2 = 0; idx2 < m_dim2Size; idx2++) {
|
||||
outBuffer[idxOutBuffer++] = inBuffer[idx0*m_dim1Size*m_dim2Size + m_removedDimensionIdx*m_dim2Size + idx2];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for(size_t idx0 = 0; idx0 < m_dim0Size; idx0++) {
|
||||
for(size_t idx1 = 0; idx1 < m_dim1Size; idx1++) {
|
||||
outBuffer[idxOutBuffer++] = inBuffer[idx0*m_dim1Size*m_dim2Size + idx1*m_dim2Size + m_removedDimensionIdx];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file CBoxAlgorithmMatrix3dTo2d.hpp
|
||||
/// \brief Classes of the box Matrix3dTo2d
|
||||
/// \author Arthur DESBOIS (INRIA).
|
||||
/// \version 0.0.1.
|
||||
/// \date Fri Feb 12 15:13:00 2021.
|
||||
///
|
||||
/// \copyright (C) 2021 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
/// \brief The class CBoxAlgorithmMatrix3dTo2d describes the box Matrix 3D to 2D.
|
||||
class CBoxAlgorithmMatrix3dTo2d final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_Matrix3dTo2d)
|
||||
|
||||
protected:
|
||||
// Codecs
|
||||
Toolkit::TStreamedMatrixDecoder <CBoxAlgorithmMatrix3dTo2d> m_matrixDecoder;
|
||||
Toolkit::TStreamedMatrixEncoder <CBoxAlgorithmMatrix3dTo2d> m_matrixEncoder;
|
||||
|
||||
// Matrices
|
||||
CMatrix* m_iMatrix = nullptr;
|
||||
CMatrix* m_oMatrix = nullptr;
|
||||
|
||||
// Parameters
|
||||
int m_dimensionToRemove;
|
||||
int m_removedDimensionIdx;
|
||||
|
||||
size_t m_dim0Size;
|
||||
size_t m_dim1Size;
|
||||
size_t m_dim2Size;
|
||||
|
||||
private:
|
||||
bool MatrixInit(CMatrix& m, const size_t dim0, const size_t dim1);
|
||||
|
||||
bool RemoveDimension(const CMatrix& in, CMatrix& out);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// \brief Descriptor of the box Matrix 3D to 2D.
|
||||
class CBoxAlgorithmMatrix3dTo2dDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override
|
||||
{}
|
||||
|
||||
CString getName() const override { return CString("Matrix 3D to 2D"); }
|
||||
CString getAuthorName() const override { return CString("Arthur Desbois"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
CString getShortDescription() const override { return CString("Convert 3D matrices to 2D"); }
|
||||
CString getDetailedDescription() const override { return CString("Convert 3 dimensional matrices to 2D matrices, by selecting a dimension to remove"); }
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("0.0.1"); }
|
||||
CString getStockItemName() const override { return CString(""); }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Matrix3dTo2d; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmMatrix3dTo2d; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("input", OV_TypeId_StreamedMatrix);
|
||||
prototype.addOutput("output", OV_TypeId_StreamedMatrix);
|
||||
|
||||
prototype.addSetting("Dimension to remove", OV_TypeId_Integer, "");
|
||||
prototype.addSetting("Index in removed dimension", OV_TypeId_Integer, "");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_Matrix3dTo2dDesc)
|
||||
};
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "ovpCBoxAlgorithmARCoefficients.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmARCoefficients::initialize()
|
||||
{
|
||||
// Signal stream decoder
|
||||
m_decoder.initialize(*this, 0);
|
||||
|
||||
m_method = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_ClassId_Algorithm_ARBurgMethod));
|
||||
m_method->initialize();
|
||||
|
||||
ip_matrix.initialize(m_method->getInputParameter(OVP_Algorithm_ARBurgMethod_InputParameterId_Matrix));
|
||||
op_matrix.initialize(m_method->getOutputParameter(OVP_Algorithm_ARBurgMethod_OutputParameterId_Matrix));
|
||||
|
||||
ip_order.initialize(m_method->getInputParameter(OVP_Algorithm_ARBurgMethod_InputParameterId_UInteger));
|
||||
ip_order = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0));
|
||||
|
||||
// Feature vector stream encoder
|
||||
m_encoder.initialize(*this, 0);
|
||||
|
||||
// The AR Burg's Method algorithm will take the matrix coming from the signal decoder:
|
||||
ip_matrix.setReferenceTarget(m_decoder.getOutputMatrix());
|
||||
|
||||
// The feature vector encoder will take the matrix from the AR Burg's Method algorithm:
|
||||
m_encoder.getInputMatrix().setReferenceTarget(op_matrix);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmARCoefficients::uninitialize()
|
||||
{
|
||||
m_decoder.uninitialize();
|
||||
|
||||
|
||||
ip_matrix.uninitialize();
|
||||
ip_order.uninitialize();
|
||||
op_matrix.uninitialize();
|
||||
|
||||
m_method->uninitialize();
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_method);
|
||||
|
||||
m_encoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
bool CBoxAlgorithmARCoefficients::processInput(const size_t /*index*/)
|
||||
{
|
||||
// ready to process !
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmARCoefficients::process()
|
||||
{
|
||||
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
// we decode the input signal chunks
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_decoder.decode(i);
|
||||
|
||||
if (m_decoder.isHeaderReceived())
|
||||
{
|
||||
// Header received
|
||||
m_method->process(OVP_Algorithm_ARBurgMethod_InputTriggerId_Initialize);
|
||||
|
||||
// Make sure the algo initialization was successful
|
||||
if (!m_method->process(OVP_Algorithm_ARBurgMethod_InputTriggerId_Initialize))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Initialization was unsuccessful\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass the header to the next boxes, by encoding a header on the output 0:
|
||||
m_encoder.encodeHeader();
|
||||
|
||||
// send the output chunk containing the header. The dates are the same as the input chunk:
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
// we process the signal matrix with our algorithm
|
||||
m_method->process(OVP_Algorithm_ARBurgMethod_InputTriggerId_Process);
|
||||
|
||||
// If the process is done successfully, we can encode the buffer
|
||||
if (m_method->isOutputTriggerActive(OVP_Algorithm_ARBurgMethod_OutputTriggerId_ProcessDone))
|
||||
{
|
||||
// Encode the output buffer :
|
||||
m_encoder.encodeBuffer();
|
||||
|
||||
// and send it to the next boxes :
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
if (m_decoder.isEndReceived())
|
||||
{
|
||||
// End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // #if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmARCoefficients
|
||||
* \author Alison Cellard (Inria)
|
||||
* \date Wed Nov 28 10:40:52 2012
|
||||
* \brief The class CBoxAlgorithmARCoefficients describes the box AR Features.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmARCoefficients final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
// Process callbacks on new input received
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
|
||||
bool process() override;
|
||||
|
||||
// As we do with any class in openvibe, we use the macro below
|
||||
// to associate this box to an unique identifier.
|
||||
// The inheritance information is also made available,
|
||||
// as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ARCoefficients)
|
||||
|
||||
protected:
|
||||
|
||||
// Signal stream decoder
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmARCoefficients> m_decoder;
|
||||
// Feature vector stream encoder
|
||||
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmARCoefficients> m_encoder;
|
||||
|
||||
Kernel::IAlgorithmProxy* m_method = nullptr;
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_matrix;
|
||||
Kernel::TParameterHandler<uint64_t> ip_order;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmARCoefficientsDesc
|
||||
* \author Alison Cellard (Inria)
|
||||
* \date Wed Nov 28 10:40:52 2012
|
||||
* \brief Descriptor of the box AR Features.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmARCoefficientsDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("AutoRegressive Coefficients"); }
|
||||
CString getAuthorName() const override { return CString("Alison Cellard"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Estimates autoregressive (AR) coefficients from a set of signals"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString("Estimates autoregressive (AR) linear model coefficients using Burg's method"); }
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-convert"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ARCoefficients; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmARCoefficients; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("EEG Signal",OV_TypeId_Signal);
|
||||
prototype.addOutput("AR Features",OV_TypeId_StreamedMatrix);
|
||||
prototype.addSetting("Order",OV_TypeId_Integer, "1");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ARCoefficientsDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyEIGEN
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
#include "ovpCBoxAlgorithmDifferentialIntegral.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmDifferentialIntegral::initialize()
|
||||
{
|
||||
// Signal stream decoder
|
||||
m_decoder.initialize(*this, 0);
|
||||
// Signal stream encoder
|
||||
m_encoder.initialize(*this, 0);
|
||||
|
||||
// If you need to, you can manually set the reference targets to link the codecs input and output. To do so, you can use :
|
||||
m_encoder.getInputMatrix().setReferenceTarget(m_decoder.getOutputMatrix());
|
||||
m_encoder.getInputSamplingRate().setReferenceTarget(m_decoder.getOutputSamplingRate());
|
||||
|
||||
m_operation = EDifferentialIntegralOperation(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0)));
|
||||
m_filterOrder = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1));
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmDifferentialIntegral::uninitialize()
|
||||
{
|
||||
m_decoder.uninitialize();
|
||||
m_encoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmDifferentialIntegral::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
double CBoxAlgorithmDifferentialIntegral::operation(const double a, const double b) const
|
||||
{
|
||||
if (m_operation == EDifferentialIntegralOperation::Differential) { return a - b; }
|
||||
if (m_operation == EDifferentialIntegralOperation::Integral) { return a + b; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmDifferentialIntegral::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//iterate over all chunk on input 0
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
// decode the chunk i on input 0
|
||||
m_decoder.decode(i);
|
||||
|
||||
if (m_decoder.isHeaderReceived())
|
||||
{
|
||||
// initialize the past data array
|
||||
CMatrix* matrix = m_decoder.getOutputMatrix(); // the StreamedMatrix of samples.
|
||||
|
||||
|
||||
// initialize all of the tables according to the number of channels
|
||||
m_pastData = new double*[matrix->getDimensionSize(0)];
|
||||
m_tmpData = new double*[matrix->getDimensionSize(0)];
|
||||
m_stabilized = new bool[matrix->getDimensionSize(0)];
|
||||
m_step = new size_t[matrix->getDimensionSize(0)];
|
||||
|
||||
for (size_t k = 0; k < matrix->getDimensionSize(0); ++k)
|
||||
{
|
||||
m_stabilized[k] = false;
|
||||
m_step[k] = 0;
|
||||
m_pastData[k] = new double[m_filterOrder];
|
||||
m_tmpData[k] = new double[m_filterOrder];
|
||||
m_tmpData[k][0] = 0;
|
||||
}
|
||||
|
||||
// Encode the output header
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
CMatrix* matrix = m_decoder.getOutputMatrix(); // the StreamedMatrix of samples.
|
||||
|
||||
const size_t nChannel = matrix->getDimensionSize(0);
|
||||
const size_t samplesPerChannel = matrix->getDimensionSize(1);
|
||||
|
||||
// ... do some process on the matrix ...
|
||||
|
||||
double* buffer = matrix->getBuffer();
|
||||
|
||||
for (size_t c = 0; c < nChannel; ++c)
|
||||
{
|
||||
for (size_t s = 0; s < samplesPerChannel; ++s)
|
||||
{
|
||||
// save the results of the previous step in a temporary array
|
||||
for (size_t step = 0; step < m_step[c]; ++step) { m_tmpData[c][step] = m_pastData[c][step]; }
|
||||
|
||||
// save the current sample as f^0(x)
|
||||
m_pastData[c][0] = buffer[s + c * samplesPerChannel];
|
||||
|
||||
// save all of the f^n(x)
|
||||
for (size_t step = 1; step < m_step[c]; ++step) { m_pastData[c][step] = operation(m_pastData[c][step - 1], m_tmpData[c][step - 1]); }
|
||||
|
||||
// if the filter is not yet stabilized we increase the step and use 0 as a return value
|
||||
if (!m_stabilized[c])
|
||||
{
|
||||
if (m_step[c] == m_filterOrder) { m_stabilized[c] = true; }
|
||||
else { m_step[c]++; }
|
||||
|
||||
buffer[s + c * samplesPerChannel] = 0;
|
||||
}
|
||||
// otherwise use f^order(x)
|
||||
else { buffer[s + c * samplesPerChannel] = operation(m_pastData[c][m_filterOrder - 1], m_tmpData[c][m_filterOrder - 1]); }
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the output buffer
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
if (m_decoder.isEndReceived())
|
||||
{
|
||||
// End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
// The unique identifiers for the box and its descriptor.
|
||||
// Identifier are randomly chosen by the skeleton-generator.
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmDifferentialIntegral
|
||||
* \author Jozef Legeny (INRIA)
|
||||
* \date Thu Oct 27 15:24:05 2011
|
||||
* \brief The class CBoxAlgorithmDifferentialIntegral describes the box DifferentialIntegral.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmDifferentialIntegral final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_DifferentialIntegral)
|
||||
|
||||
protected:
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmDifferentialIntegral> m_decoder;
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmDifferentialIntegral> m_encoder;
|
||||
|
||||
private:
|
||||
double operation(const double a, const double b) const;
|
||||
EDifferentialIntegralOperation m_operation = EDifferentialIntegralOperation::Differential;
|
||||
uint64_t m_filterOrder = 0;
|
||||
|
||||
/// Holds the differentials/integrals of all orders from the previous step
|
||||
double** m_pastData = nullptr;
|
||||
double** m_tmpData = nullptr;
|
||||
|
||||
/// Is true when the filter is stabilized
|
||||
bool* m_stabilized = nullptr;
|
||||
/// Counts the samples up to the filter order, used to stabilize the filter
|
||||
size_t* m_step = nullptr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmDifferentialIntegralDesc
|
||||
* \author Jozef Legeny (INRIA)
|
||||
* \date Thu Oct 27 15:24:05 2011
|
||||
* \brief Descriptor of the box DifferentialIntegral.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmDifferentialIntegralDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Signal Differential/Integral"); }
|
||||
CString getAuthorName() const override { return CString("Jozef Legeny"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
CString getShortDescription() const override { return CString("Calculates a differential or an integral of a signal"); }
|
||||
CString getDetailedDescription() const override { return CString("Calculates a differential or an integral of a signal."); }
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-execute"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_DifferentialIntegral; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmDifferentialIntegral; }
|
||||
|
||||
/*
|
||||
virtual IBoxListener* createBoxListener() const { return new CBoxAlgorithmDifferentialIntegralListener; }
|
||||
virtual void releaseBoxListener(IBoxListener* listener) const { delete listener; }
|
||||
*/
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input Signal",OV_TypeId_Signal);
|
||||
|
||||
prototype.addOutput("Output Signal",OV_TypeId_Signal);
|
||||
|
||||
prototype.addSetting("Operation", OVP_TypeId_DifferentialIntegralOperation, "Differential");
|
||||
prototype.addSetting("Order", OV_TypeId_Integer, "1");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_DifferentialIntegralDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#include "ovpCBoxAlgorithmERSPAverage.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmERSPAverage::initialize()
|
||||
{
|
||||
m_epochingStim = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_computeStim = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
m_decoderSpectrum.initialize(*this, 0);
|
||||
m_decoderStimulations.initialize(*this, 1);
|
||||
|
||||
m_encoder.initialize(*this, 0);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmERSPAverage::uninitialize()
|
||||
{
|
||||
m_encoder.uninitialize();
|
||||
m_decoderSpectrum.uninitialize();
|
||||
m_decoderStimulations.uninitialize();
|
||||
|
||||
for (auto& v : m_cachedSpectra) { for (auto m : v) { delete m; } }
|
||||
m_cachedSpectra.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmERSPAverage::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmERSPAverage::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_decoderStimulations.decode(i);
|
||||
if (m_decoderStimulations.isBufferReceived())
|
||||
{
|
||||
const auto stims = m_decoderStimulations.getOutputStimulationSet();
|
||||
for (size_t j = 0; j < stims->getStimulationCount(); ++j)
|
||||
{
|
||||
if (stims->getStimulationIdentifier(j) == m_epochingStim)
|
||||
{
|
||||
m_currentChunk = 0;
|
||||
m_numTrials++;
|
||||
}
|
||||
if (stims->getStimulationIdentifier(j) == m_computeStim) { computeAndSend(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_decoderSpectrum.decode(i);
|
||||
|
||||
if (m_decoderSpectrum.isHeaderReceived())
|
||||
{
|
||||
const uint64_t samplingRate = m_decoderSpectrum.getOutputSamplingRate();
|
||||
m_encoder.getInputSamplingRate() = samplingRate;
|
||||
m_encoder.getInputFrequencyAbscissa()->copy(*m_decoderSpectrum.getOutputFrequencyAbscissa());
|
||||
m_encoder.getInputMatrix()->copyDescription(*m_decoderSpectrum.getOutputMatrix());
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
if (m_decoderSpectrum.isBufferReceived())
|
||||
{
|
||||
const CMatrix* input = m_decoderSpectrum.getOutputMatrix();
|
||||
appendChunk(*input, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
if (m_decoderSpectrum.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmERSPAverage::appendChunk(const CMatrix& chunk, const uint64_t startTime, const uint64_t endTime)
|
||||
{
|
||||
if (m_cachedSpectra.size() <= m_currentChunk)
|
||||
{
|
||||
m_cachedSpectra.resize(m_currentChunk + 1);
|
||||
m_timestamps.resize(m_currentChunk + 1);
|
||||
}
|
||||
|
||||
CMatrix* matrixCopy = new CMatrix();
|
||||
matrixCopy->copy(chunk);
|
||||
m_cachedSpectra[m_currentChunk].push_back(matrixCopy);
|
||||
m_timestamps[m_currentChunk].start = startTime;
|
||||
m_timestamps[m_currentChunk].end = endTime;
|
||||
|
||||
m_currentChunk++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmERSPAverage::computeAndSend()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
double* outptr = m_encoder.getInputMatrix()->getBuffer();
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Counted " << m_numTrials << " trials and " << m_cachedSpectra.size() << " spectra per trial.\n";
|
||||
|
||||
for (size_t i = 0; i < m_cachedSpectra.size(); ++i)
|
||||
{
|
||||
// Compute average for each slice
|
||||
const double divider = 1.0 / m_cachedSpectra[i].size();
|
||||
m_encoder.getInputMatrix()->resetBuffer();
|
||||
|
||||
for (auto mat : m_cachedSpectra[i])
|
||||
{
|
||||
const double* inptr = mat->getBuffer();
|
||||
for (size_t p = 0; p < mat->getBufferElementCount(); ++p) { outptr[p] += divider * inptr[p]; }
|
||||
delete mat;
|
||||
}
|
||||
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, m_timestamps[i].start - m_timestamps[0].start, m_timestamps[i].end - m_timestamps[0].start);
|
||||
}
|
||||
|
||||
m_numTrials = 0;
|
||||
m_currentChunk = 0;
|
||||
m_cachedSpectra.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxAlgorithmERSPAverage final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ERSPAverage)
|
||||
|
||||
protected:
|
||||
|
||||
bool appendChunk(const CMatrix& chunk, uint64_t startTime, uint64_t endTime);
|
||||
bool computeAndSend();
|
||||
|
||||
Toolkit::TSpectrumDecoder<CBoxAlgorithmERSPAverage> m_decoderSpectrum;
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmERSPAverage> m_decoderStimulations;
|
||||
Toolkit::TSpectrumEncoder<CBoxAlgorithmERSPAverage> m_encoder;
|
||||
|
||||
struct STimestamp
|
||||
{
|
||||
uint64_t start, end;
|
||||
};
|
||||
|
||||
std::vector<std::vector<CMatrix*>> m_cachedSpectra;
|
||||
std::vector<STimestamp> m_timestamps;
|
||||
|
||||
size_t m_currentChunk = 0;
|
||||
size_t m_numTrials = 0;
|
||||
|
||||
uint64_t m_epochingStim = 0;
|
||||
uint64_t m_computeStim = 0;
|
||||
};
|
||||
|
||||
|
||||
class CBoxAlgorithmERSPAverageDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("ERSP Average"); }
|
||||
CString getAuthorName() const override { return CString("Jussi T. Lindgren"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
|
||||
CString getShortDescription() const override
|
||||
{
|
||||
return CString("Averages a sequence of spectra per trial across multiple trials. The result is a sequence starting from t=0.");
|
||||
}
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"Example: Given an input sequence [t,s1,s2,t,s3,s4] for two trials with s* the spectra and t a stimulation denoting the trial start, the box returns 1/2*(s1+s3), 1/2*(s2+s4).");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-sort-ascending"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ERSPAverage; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmERSPAverage; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input spectra", OV_TypeId_Spectrum);
|
||||
prototype.addInput("Control stream", OV_TypeId_Stimulations);
|
||||
prototype.addOutput("Output spectra", OV_TypeId_Spectrum);
|
||||
|
||||
prototype.addSetting("Trial start marker", OV_TypeId_Stimulation, "OVTK_GDF_Start_Of_Trial", false);
|
||||
prototype.addSetting("Computation trigger", OV_TypeId_Stimulation, "OVTK_StimulationId_ExperimentStop", false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ERSPAverageDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
#include "ovpCBoxAlgorithmEpochVariance.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CEpochVariance::initialize()
|
||||
{
|
||||
CIdentifier inputTypeID;
|
||||
getStaticBoxContext().getInputType(0, inputTypeID);
|
||||
if (inputTypeID == OV_TypeId_StreamedMatrix)
|
||||
{
|
||||
m_decoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixDecoder));
|
||||
m_encoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
m_encoderForVariance = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
m_encoderForConfidenceBound = &getAlgorithmManager().
|
||||
getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
}
|
||||
else if (inputTypeID == OV_TypeId_FeatureVector)
|
||||
{
|
||||
m_decoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorDecoder));
|
||||
m_encoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
m_encoderForVariance = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
m_encoderForConfidenceBound = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
}
|
||||
else if (inputTypeID == OV_TypeId_Signal)
|
||||
{
|
||||
m_decoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalDecoder));
|
||||
m_encoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_encoderForVariance = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_encoderForConfidenceBound = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
}
|
||||
else if (inputTypeID == OV_TypeId_Spectrum)
|
||||
{
|
||||
m_decoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumDecoder));
|
||||
m_encoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumEncoder));
|
||||
m_encoderForVariance = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumDecoder));
|
||||
m_encoderForConfidenceBound = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumDecoder));
|
||||
}
|
||||
else { return false; }
|
||||
m_decoder->initialize();
|
||||
m_encoder->initialize();
|
||||
m_encoderForVariance->initialize();
|
||||
m_encoderForConfidenceBound->initialize();
|
||||
|
||||
m_matrixVariance = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_ClassId_Algorithm_MatrixVariance));
|
||||
m_matrixVariance->initialize();
|
||||
|
||||
if (inputTypeID == OV_TypeId_StreamedMatrix) { }
|
||||
else if (inputTypeID == OV_TypeId_FeatureVector) { }
|
||||
else if (inputTypeID == OV_TypeId_Signal)
|
||||
{
|
||||
m_encoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_encoderForVariance->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_encoderForConfidenceBound->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
}
|
||||
else if (inputTypeID == OV_TypeId_Spectrum)
|
||||
{
|
||||
m_encoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_FrequencyAbscissa)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_FrequencyAbscissa));
|
||||
m_encoderForVariance->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_FrequencyAbscissa)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_FrequencyAbscissa));
|
||||
m_encoderForConfidenceBound->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_FrequencyAbscissa)->setReferenceTarget(
|
||||
m_decoder->
|
||||
getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_FrequencyAbscissa));
|
||||
|
||||
m_encoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_Sampling));
|
||||
m_encoderForVariance->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_Sampling));
|
||||
m_encoderForConfidenceBound->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_Sampling));
|
||||
}
|
||||
|
||||
ip_averagingMethod.initialize(m_matrixVariance->getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_AveragingMethod));
|
||||
ip_matrixCount.initialize(m_matrixVariance->getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_MatrixCount));
|
||||
ip_SignificanceLevel.initialize(m_matrixVariance->getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_SignificanceLevel));
|
||||
|
||||
ip_averagingMethod = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0));
|
||||
ip_matrixCount = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1));
|
||||
ip_SignificanceLevel = double(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2));
|
||||
|
||||
m_matrixVariance->getInputParameter(OVP_Algorithm_MatrixVariance_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputParameterId_Matrix));
|
||||
m_encoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixVariance->getOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_AveragedMatrix));
|
||||
m_encoderForVariance->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixVariance->getOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_Variance));
|
||||
m_encoderForConfidenceBound->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixVariance->getOutputParameter(OVP_Algorithm_MatrixVariance_OutputParameterId_ConfidenceBound));
|
||||
|
||||
|
||||
if (ip_matrixCount <= 0)
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_Error << "You should provide a positive number of epochs better than " << ip_matrixCount << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEpochVariance::uninitialize()
|
||||
{
|
||||
ip_averagingMethod.uninitialize();
|
||||
ip_matrixCount.uninitialize();
|
||||
|
||||
m_matrixVariance->uninitialize();
|
||||
m_encoder->uninitialize();
|
||||
m_encoderForVariance->uninitialize();
|
||||
m_encoderForConfidenceBound->uninitialize();
|
||||
m_decoder->uninitialize();
|
||||
|
||||
getAlgorithmManager().releaseAlgorithm(*m_matrixVariance);
|
||||
getAlgorithmManager().releaseAlgorithm(*m_encoder);
|
||||
getAlgorithmManager().releaseAlgorithm(*m_encoderForVariance);
|
||||
getAlgorithmManager().releaseAlgorithm(*m_encoderForConfidenceBound);
|
||||
getAlgorithmManager().releaseAlgorithm(*m_decoder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEpochVariance::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEpochVariance::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = getDynamicBoxContext();
|
||||
const size_t nInput = getStaticBoxContext().getInputCount();
|
||||
|
||||
for (size_t i = 0; i < nInput; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < boxContext.getInputChunkCount(i); ++j)
|
||||
{
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> bufferHandle(
|
||||
m_decoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> obufferHandle(
|
||||
m_encoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> obufferHandleForVariance(
|
||||
m_encoderForVariance->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> bufferHandleForConfidenceBound(
|
||||
m_encoderForConfidenceBound->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
bufferHandle = boxContext.getInputChunk(i, j);
|
||||
obufferHandle = boxContext.getOutputChunk(0);
|
||||
obufferHandleForVariance = boxContext.getOutputChunk(1);
|
||||
bufferHandleForConfidenceBound = boxContext.getOutputChunk(2);
|
||||
|
||||
m_decoder->process();
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedHeader))
|
||||
{
|
||||
m_matrixVariance->process(OVP_Algorithm_MatrixVariance_InputTriggerId_Reset);
|
||||
m_encoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
m_encoderForVariance->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
m_encoderForConfidenceBound->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
}
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
m_matrixVariance->process(OVP_Algorithm_MatrixVariance_InputTriggerId_FeedMatrix);
|
||||
if (m_matrixVariance->isOutputTriggerActive(OVP_Algorithm_MatrixVariance_OutputTriggerId_AveragePerformed))
|
||||
{
|
||||
m_encoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
m_encoderForVariance->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
m_encoderForConfidenceBound->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
}
|
||||
}
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedEnd))
|
||||
{
|
||||
m_encoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
m_encoderForVariance->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
m_encoderForConfidenceBound->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(i, j), boxContext.getInputChunkEndTime(i, j));
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CEpochVariance final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_EpochVariance)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::IAlgorithmProxy* m_decoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_encoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_encoderForVariance = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_encoderForConfidenceBound = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_matrixVariance = nullptr;
|
||||
|
||||
Kernel::TParameterHandler<uint64_t> ip_matrixCount;
|
||||
Kernel::TParameterHandler<uint64_t> ip_averagingMethod;
|
||||
Kernel::TParameterHandler<double> ip_SignificanceLevel;
|
||||
};
|
||||
|
||||
class CEpochVarianceListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onInputTypeChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
CIdentifier typeID = CIdentifier::undefined();
|
||||
box.getInputType(index, typeID);
|
||||
for (size_t i = 0; i < box.getOutputCount(); ++i) { box.setOutputType(i, typeID); }
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CEpochVarianceDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
CString getName() const override { return CString("Epoch variance"); }
|
||||
CString getAuthorName() const override { return CString("Dieter Devlaminck"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
CString getShortDescription() const override { return CString("Computes variance of each sample over several epochs"); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-missing-image"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_EpochVariance; }
|
||||
IPluginObject* create() override { return new CEpochVariance(); }
|
||||
IBoxListener* createBoxListener() const override { return new CEpochVarianceListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input epochs", OV_TypeId_StreamedMatrix);
|
||||
prototype.addOutput("Averaged epochs", OV_TypeId_StreamedMatrix);
|
||||
prototype.addOutput("Variance of epochs", OV_TypeId_StreamedMatrix);
|
||||
prototype.addOutput("Confidence bounds", OV_TypeId_StreamedMatrix);
|
||||
prototype.addSetting("Averaging type", OVP_TypeId_EpochAverageMethod, "Moving epoch average");
|
||||
prototype.addSetting("Epoch count", OV_TypeId_Integer, "4");
|
||||
prototype.addSetting("Significance level", OV_TypeId_Float, "0.01");
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
|
||||
|
||||
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
|
||||
prototype.addInputSupport(OV_TypeId_FeatureVector);
|
||||
prototype.addInputSupport(OV_TypeId_Signal);
|
||||
prototype.addInputSupport(OV_TypeId_Spectrum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_EpochVarianceDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
#include "ovpCBoxAlgorithmHilbert.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmHilbert::initialize()
|
||||
{
|
||||
// Signal stream decoder
|
||||
m_decoder.initialize(*this, 0);
|
||||
// Signal stream encoder
|
||||
m_algo1Encoder.initialize(*this, 0);
|
||||
m_algo2Encoder.initialize(*this, 1);
|
||||
m_algo3Encoder.initialize(*this, 2);
|
||||
|
||||
m_hilbertAlgo = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_ClassId_Algorithm_HilbertTransform));
|
||||
m_hilbertAlgo->initialize();
|
||||
|
||||
ip_signalMatrix.initialize(m_hilbertAlgo->getInputParameter(OVP_Algorithm_HilbertTransform_InputParameterId_Matrix));
|
||||
op_hilbertMatrix.initialize(m_hilbertAlgo->getOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_HilbertMatrix));
|
||||
op_envelopeMatrix.initialize(m_hilbertAlgo->getOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_EnvelopeMatrix));
|
||||
op_phaseMatrix.initialize(m_hilbertAlgo->getOutputParameter(OVP_Algorithm_HilbertTransform_OutputParameterId_PhaseMatrix));
|
||||
|
||||
ip_signalMatrix.setReferenceTarget(m_decoder.getOutputMatrix());
|
||||
|
||||
m_algo1Encoder.getInputSamplingRate().setReferenceTarget(m_decoder.getOutputSamplingRate());
|
||||
m_algo2Encoder.getInputSamplingRate().setReferenceTarget(m_decoder.getOutputSamplingRate());
|
||||
m_algo3Encoder.getInputSamplingRate().setReferenceTarget(m_decoder.getOutputSamplingRate());
|
||||
|
||||
m_algo1Encoder.getInputMatrix().setReferenceTarget(op_hilbertMatrix);
|
||||
m_algo2Encoder.getInputMatrix().setReferenceTarget(op_envelopeMatrix);
|
||||
m_algo3Encoder.getInputMatrix().setReferenceTarget(op_phaseMatrix);
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmHilbert::uninitialize()
|
||||
{
|
||||
m_decoder.uninitialize();
|
||||
m_algo1Encoder.uninitialize();
|
||||
m_algo2Encoder.uninitialize();
|
||||
m_algo3Encoder.uninitialize();
|
||||
|
||||
ip_signalMatrix.uninitialize();
|
||||
op_hilbertMatrix.uninitialize();
|
||||
op_envelopeMatrix.uninitialize();
|
||||
op_phaseMatrix.uninitialize();
|
||||
|
||||
m_hilbertAlgo->uninitialize();
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_hilbertAlgo);
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
bool CBoxAlgorithmHilbert::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmHilbert::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//iterate over all chunk on input 0
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
// decode the chunk i on input 0
|
||||
m_decoder.decode(i);
|
||||
// the decoder may have decoded 3 different parts : the header, a buffer or the end of stream.
|
||||
if (m_decoder.isHeaderReceived())
|
||||
{
|
||||
// Header received
|
||||
m_hilbertAlgo->process(OVP_Algorithm_HilbertTransform_InputTriggerId_Initialize);
|
||||
|
||||
// Pass the header to the next boxes, by encoding a header on the output 0:
|
||||
m_algo1Encoder.encodeHeader();
|
||||
m_algo2Encoder.encodeHeader();
|
||||
m_algo3Encoder.encodeHeader();
|
||||
|
||||
// send the output chunk containing the header. The dates are the same as the input chunk:
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
m_hilbertAlgo->process(OVP_Algorithm_HilbertTransform_InputTriggerId_Process);
|
||||
|
||||
// Encode the output buffer :
|
||||
m_algo1Encoder.encodeBuffer();
|
||||
m_algo2Encoder.encodeBuffer();
|
||||
m_algo3Encoder.encodeBuffer();
|
||||
|
||||
// and send it to the next boxes :
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if (m_decoder.isEndReceived())
|
||||
{
|
||||
// End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
|
||||
m_algo1Encoder.encodeEnd();
|
||||
m_algo2Encoder.encodeEnd();
|
||||
m_algo3Encoder.encodeEnd();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
// The current input chunk has been processed, and automaticcaly discarded.
|
||||
// you don't need to call "boxContext.markInputAsDeprecated(0, i);"
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmHilbert
|
||||
* \author Alison Cellard (Inria)
|
||||
* \date Thu Jun 6 13:47:53 2013
|
||||
* \brief The class CBoxAlgorithmHilbert describes the box Phase and Envelope.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmHilbert 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;
|
||||
|
||||
// As we do with any class in openvibe, we use the macro below
|
||||
// to associate this box to an unique identifier.
|
||||
// The inheritance information is also made available,
|
||||
// as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_Hilbert)
|
||||
|
||||
protected:
|
||||
|
||||
// Signal stream decoder
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmHilbert> m_decoder;
|
||||
// Signal stream encoder
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmHilbert> m_algo1Encoder;
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmHilbert> m_algo2Encoder;
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmHilbert> m_algo3Encoder;
|
||||
|
||||
Kernel::IAlgorithmProxy* m_hilbertAlgo = nullptr;
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> ip_signalMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_hilbertMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_envelopeMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_phaseMatrix;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmHilbertDesc
|
||||
* \author Alison Cellard (Inria)
|
||||
* \date Thu Jun 6 13:47:53 2013
|
||||
* \brief Descriptor of the box Phase and Envelope.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmHilbertDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Hilbert Transform"); }
|
||||
CString getAuthorName() const override { return CString("Alison Cellard"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Hilbert transform, Phase and Envelope from discrete-time analytic signal using Hilbert"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("Return Hilbert transform, phase and envelope of the input signal using analytic signal computation");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-new"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Hilbert; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmHilbert; }
|
||||
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input Signal",OV_TypeId_Signal);
|
||||
|
||||
prototype.addOutput("Hilbert Transform", OV_TypeId_Signal);
|
||||
prototype.addOutput("Envelope",OV_TypeId_Signal);
|
||||
prototype.addOutput("Phase",OV_TypeId_Signal);
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_HilbertDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
//#define __OpenViBEPlugins_BoxAlgorithm_IFFTbox_CPP__
|
||||
// to get ifft:
|
||||
#include <itpp/itsignal.h>
|
||||
#include "ovpCBoxAlgorithmIFFTbox.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
|
||||
bool CBoxAlgorithmIFFTbox::initialize()
|
||||
{
|
||||
m_decoder[0].initialize(*this, 0); // Spectrum stream real part decoder
|
||||
m_decoder[1].initialize(*this, 1); // Spectrum stream imaginary part decoder
|
||||
m_encoder.initialize(*this, 0); // Signal stream encoder
|
||||
|
||||
m_nSample = 0;
|
||||
m_headerSent = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmIFFTbox::uninitialize()
|
||||
{
|
||||
m_decoder[0].uninitialize();
|
||||
m_decoder[1].uninitialize();
|
||||
m_encoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmIFFTbox::processInput(const size_t /*index*/)
|
||||
{
|
||||
IDynamicBoxContext& boxContext = this->getDynamicBoxContext();
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
|
||||
if (boxContext.getInputChunkCount(0) == 0) { return true; }
|
||||
const uint64_t start = boxContext.getInputChunkStartTime(0, 0);
|
||||
const uint64_t end = boxContext.getInputChunkEndTime(0, 0);
|
||||
for (size_t i = 1; i < nInput; ++i)
|
||||
{
|
||||
if (boxContext.getInputChunkCount(i) == 0) { return true; }
|
||||
|
||||
if (start != boxContext.getInputChunkStartTime(i, 0) || end != boxContext.getInputChunkEndTime(i, 0))
|
||||
{
|
||||
OV_WARNING_K("Chunk dates mismatch, check stream structure or parameters");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
|
||||
return true;
|
||||
}
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmIFFTbox::process()
|
||||
{
|
||||
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
|
||||
size_t nHeader = 0;
|
||||
size_t nBuffer = 0;
|
||||
size_t nEnd = 0;
|
||||
|
||||
for (size_t i = 0; i < nInput; ++i)
|
||||
{
|
||||
m_decoder[i].decode(0);
|
||||
if (m_decoder[i].isHeaderReceived())
|
||||
{
|
||||
//detect if header of other input is already received
|
||||
if (0 == nHeader)
|
||||
{
|
||||
// Header received. This happens only once when pressing "play". For example with a StreamedMatrix input, you now know the dimension count, sizes, and labels of the matrix
|
||||
// ... maybe do some process ...
|
||||
m_channelsNumber = m_decoder[i].getOutputMatrix()->getDimensionSize(0);
|
||||
m_nSample = m_decoder[i].getOutputMatrix()->getDimensionSize(1);
|
||||
OV_ERROR_UNLESS_KRF(m_channelsNumber > 0 && m_nSample > 0, "Both dims of the input matrix must have positive size",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
|
||||
m_nSample = (m_nSample - 1) * 2;
|
||||
if (m_nSample == 0) { m_nSample = 1; }
|
||||
}
|
||||
else
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(
|
||||
m_decoder[0].getOutputMatrix()->isDescriptionEqual(*m_decoder[i].getOutputMatrix(), false),
|
||||
"The matrix components of the two streams have different properties, check stream structures or parameters",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
|
||||
OV_ERROR_UNLESS_KRF(
|
||||
m_decoder[0].getOutputFrequencyAbscissa()->isDescriptionEqual(*m_decoder[i].getOutputFrequencyAbscissa(), false),
|
||||
"The frequencies abscissas descriptors of the two streams have different properties, check stream structures or parameters",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
|
||||
OV_ERROR_UNLESS_KRF(
|
||||
m_decoder[0].getOutputMatrix()->getDimensionSize(1) == m_decoder[i].getOutputFrequencyAbscissa()->getDimensionSize(0),
|
||||
"Frequencies abscissas count " << m_decoder[i].getOutputFrequencyAbscissa()->getDimensionSize(0) <<
|
||||
" does not match the corresponding matrix chunk size " << m_decoder[0].getOutputMatrix()->getDimensionSize(1) <<
|
||||
", check stream structures or parameters", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
OV_ERROR_UNLESS_KRF(m_decoder[0].getOutputSamplingRate(), "Sampling rate must be positive, check stream structures or parameters",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
|
||||
OV_ERROR_UNLESS_KRF(m_decoder[0].getOutputSamplingRate() == m_decoder[i].getOutputSamplingRate(),
|
||||
"Sampling rates don't match (" << m_decoder[0].getOutputSamplingRate() << " != " << m_decoder[i].getOutputSamplingRate() <<
|
||||
"), please check stream structures or parameters", Kernel::ErrorType::BadProcessing);
|
||||
}
|
||||
|
||||
nHeader++;
|
||||
}
|
||||
if (m_decoder[i].isBufferReceived()) { nBuffer++; }
|
||||
if (m_decoder[i].isEndReceived()) { nEnd++; }
|
||||
}
|
||||
|
||||
if ((nHeader && nHeader != nInput) || (nBuffer && nBuffer != nInput) || (nEnd && nEnd != nInput))
|
||||
{
|
||||
OV_WARNING_K("Stream structure mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nBuffer)
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(m_nSample, "Received buffer before header, shouldn't happen\n", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
if (!m_headerSent)
|
||||
{
|
||||
m_signalBuffer.set_size(int(m_nSample));
|
||||
m_frequencyBuffer.set_size(int(m_nSample));
|
||||
|
||||
m_encoder.getInputSamplingRate() = m_decoder[0].getOutputSamplingRate();
|
||||
m_encoder.getInputMatrix()->resize(m_channelsNumber, m_nSample);
|
||||
for (size_t channel = 0; channel < m_channelsNumber; ++channel)
|
||||
{
|
||||
m_encoder.getInputMatrix()->setDimensionLabel(
|
||||
0, channel, m_decoder[0].getOutputMatrix()->getDimensionLabel(0, channel));
|
||||
}
|
||||
|
||||
// Pass the header to the next boxes, by encoding a header on the output 0:
|
||||
m_encoder.encodeHeader();
|
||||
// send the output chunk containing the header. The dates are the same as the input chunk:
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, 0), boxContext.getInputChunkEndTime(0, 0));
|
||||
|
||||
m_headerSent = true;
|
||||
}
|
||||
|
||||
const double* bufferInput0 = m_decoder[0].getOutputMatrix()->getBuffer();
|
||||
const double* bufferInput1 = m_decoder[1].getOutputMatrix()->getBuffer();
|
||||
|
||||
for (size_t channel = 0; channel < m_channelsNumber; ++channel)
|
||||
{
|
||||
for (size_t j = 0; j < m_nSample; ++j)
|
||||
{
|
||||
m_frequencyBuffer[j].real(bufferInput0[int(channel * m_nSample + j)]);
|
||||
m_frequencyBuffer[j].imag(bufferInput1[int(channel * m_nSample + j)]);
|
||||
}
|
||||
|
||||
m_signalBuffer = ifft_real(m_frequencyBuffer);
|
||||
|
||||
double* bufferOutput = m_encoder.getInputMatrix()->getBuffer();
|
||||
for (size_t j = 0; j < m_nSample; ++j) { bufferOutput[int(channel * m_nSample + j)] = m_signalBuffer[j]; }
|
||||
}
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, 0), boxContext.getInputChunkEndTime(0, 0));
|
||||
}
|
||||
if (nEnd)
|
||||
{
|
||||
// End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, 0), boxContext.getInputChunkEndTime(0, 0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif //TARGET_HAS_ThirdPartyITPP
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <complex>
|
||||
|
||||
#include <itpp/itbase.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmIFFTbox
|
||||
* \author Guillermo Andrade B. (INRIA)
|
||||
* \date Fri Jan 20 15:35:05 2012
|
||||
* \brief The class CBoxAlgorithmIFFTbox describes the box IFFT box.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmIFFTbox final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_IFFTbox)
|
||||
|
||||
protected:
|
||||
// Codec algorithms specified in the skeleton-generator:
|
||||
Toolkit::TSpectrumDecoder<CBoxAlgorithmIFFTbox> m_decoder[2];
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmIFFTbox> m_encoder;
|
||||
private:
|
||||
itpp::Vec<std::complex<double>> m_frequencyBuffer;
|
||||
itpp::Vec<double> m_signalBuffer;
|
||||
size_t m_nSample = 0;
|
||||
size_t m_channelsNumber = 0;
|
||||
bool m_headerSent = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmIFFTboxDesc
|
||||
* \author Guillermo Andrade B. (INRIA)
|
||||
* \date Fri Jan 20 15:35:05 2012
|
||||
* \brief Descriptor of the box IFFT box.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmIFFTboxDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("IFFT"); }
|
||||
CString getAuthorName() const override { return CString("Guillermo Andrade B."); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
CString getShortDescription() const override { return CString("Compute Inverse Fast Fourier Transformation"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString("Compute Inverse Fast Fourier Transformation (depends on ITPP/fftw)"); }
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Spectral Analysis"); }
|
||||
CString getVersion() const override { return CString("0.2"); }
|
||||
CString getStockItemName() const override { return CString("gtk-execute"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_IFFTbox; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmIFFTbox; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("real part",OV_TypeId_Spectrum);
|
||||
prototype.addInput("imaginary part",OV_TypeId_Spectrum);
|
||||
|
||||
prototype.addOutput("Signal output",OV_TypeId_Signal);
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_IFFTboxDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif //TARGET_HAS_ThirdPartyITPP
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#include "ovpCBoxAlgorithmMatrixTranspose.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmMatrixTranspose::initialize()
|
||||
{
|
||||
m_decoder.initialize(*this, 0);
|
||||
m_encoder.initialize(*this, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixTranspose::uninitialize()
|
||||
{
|
||||
m_encoder.uninitialize();
|
||||
m_decoder.uninitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixTranspose::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixTranspose::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_decoder.decode(i);
|
||||
|
||||
if (m_decoder.isHeaderReceived())
|
||||
{
|
||||
const size_t nDim = m_decoder.getOutputMatrix()->getDimensionCount();
|
||||
|
||||
const CMatrix* input = m_decoder.getOutputMatrix();
|
||||
CMatrix* output = m_encoder.getInputMatrix();
|
||||
|
||||
if (nDim == 1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Upgrading your 1 dimensional matrix to 2 dimensions, [" << input->getDimensionSize(0) <<
|
||||
"x 1]\n";
|
||||
|
||||
output->resize(input->getDimensionSize(0), 1);
|
||||
|
||||
for (size_t j = 0; j < input->getDimensionSize(0); ++j) { output->setDimensionLabel(0, j, input->getDimensionLabel(0, j)); }
|
||||
output->setDimensionLabel(1, 0, "Dimension 0");
|
||||
}
|
||||
else if (nDim == 2)
|
||||
{
|
||||
output->resize(input->getDimensionSize(1), input->getDimensionSize(0));
|
||||
|
||||
for (size_t j = 0; j < output->getDimensionSize(0); ++j) { output->setDimensionLabel(0, j, input->getDimensionLabel(1, j)); }
|
||||
for (size_t j = 0; j < output->getDimensionSize(1); ++j) { output->setDimensionLabel(1, j, input->getDimensionLabel(0, j)); }
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Only 1 and 2 dimensional matrices supported\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Output matrix will be [" << output->getDimensionSize(0) << "x" << output->getDimensionSize(1) << "]\n";
|
||||
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
const CMatrix* input = m_decoder.getOutputMatrix();
|
||||
CMatrix* output = m_encoder.getInputMatrix();
|
||||
|
||||
if (input->getDimensionCount() == 1)
|
||||
{
|
||||
const double* iBuffer = input->getBuffer();
|
||||
double* oBuffer = output->getBuffer();
|
||||
|
||||
for (size_t j = 0; j < input->getBufferElementCount(); ++j) { oBuffer[j] = iBuffer[j]; }
|
||||
}
|
||||
else
|
||||
{
|
||||
// 2 dim
|
||||
const size_t nRows = input->getDimensionSize(0);
|
||||
const size_t nCols = input->getDimensionSize(1);
|
||||
|
||||
const double* iBuffer = input->getBuffer();
|
||||
double* oBuffer = output->getBuffer();
|
||||
|
||||
for (size_t j = 0; j < nRows; ++j) { for (size_t k = 0; k < nCols; ++k) { oBuffer[k * nRows + j] = iBuffer[j * nCols + k]; } }
|
||||
}
|
||||
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
|
||||
if (m_decoder.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxAlgorithmMatrixTranspose final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_MatrixTranspose)
|
||||
|
||||
protected:
|
||||
|
||||
Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatrixTranspose> m_decoder;
|
||||
Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmMatrixTranspose> m_encoder;
|
||||
};
|
||||
|
||||
|
||||
class CBoxAlgorithmMatrixTransposeDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Matrix Transpose"); }
|
||||
CString getAuthorName() const override { return CString("Jussi T. Lindgren"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Transposes each matrix of the input stream"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("Only works for 1 and 2 dimensional matrices. One-dimensional matrixes will be upgraded to two dimensions: [N x 1]");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-sort-ascending"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_MatrixTranspose; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmMatrixTranspose; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input matrix", OV_TypeId_StreamedMatrix);
|
||||
prototype.addOutput("Output matrix", OV_TypeId_StreamedMatrix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_MatrixTransposeDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#include "ovpCBoxAlgorithmNull.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmNull::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmNull::process()
|
||||
{
|
||||
Kernel::IBoxIO* boxContext = getBoxAlgorithmContext()->getDynamicBoxContext();
|
||||
const size_t nInput = getBoxAlgorithmContext()->getStaticBoxContext()->getInputCount();
|
||||
for (size_t i = 0; i < nInput; ++i) { for (size_t j = 0; j < boxContext->getInputChunkCount(i); ++j) { boxContext->markInputAsDeprecated(i, j); } }
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#define OVP_ClassId_BoxAlgorithm_Null OpenViBE::CIdentifier(0x601118A8, 0x14BF700F)
|
||||
#define OVP_ClassId_BoxAlgorithm_NullDesc OpenViBE::CIdentifier(0x6BD21A21, 0x0A5E685A)
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxAlgorithmNull final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithm, OVP_ClassId_BoxAlgorithm_Null)
|
||||
};
|
||||
|
||||
class CBoxAlgorithmNullDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
CString getName() const override { return CString("Null"); }
|
||||
CString getAuthorName() const override { return CString("Jussi T. Lindgren"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Consumes input and produces nothing. It can be used to show scenario design intent."); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("Directing to Null instead of leaving a box output unconnected may add a tiny overhead.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getSoftwareComponent() const override { return CString("openvibe-extras"); }
|
||||
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Null; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmNull(); }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input stream", OV_TypeId_EBMLStream);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_NullDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file CBoxAlgorithmConnectivityMeasure.cpp
|
||||
/// \brief Implementation of the Box Connectivity Measure.
|
||||
/// \author Arthur DESBOIS (INRIA).
|
||||
/// \version 0.0.1.
|
||||
/// \date Fri Oct 30 16:18:49 2020.
|
||||
///
|
||||
/// \copyright (C) 2020 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "CBoxAlgorithmConnectivityMeasure.hpp"
|
||||
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmConnectivityMeasure::initialize()
|
||||
{
|
||||
m_signalDecoder.initialize(*this, 0);
|
||||
m_matrixEncoder.initialize(*this, 0);
|
||||
|
||||
m_iMatrix = m_signalDecoder.getOutputMatrix();
|
||||
m_oMatrix = m_matrixEncoder.getInputMatrix();
|
||||
|
||||
// Settings
|
||||
m_metric = EConnectMetric(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0)));
|
||||
m_windowMethod = EConnectWindowMethod(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1)));
|
||||
m_windowLengthSeconds = double(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2));
|
||||
m_windowOverlap = int(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3));
|
||||
m_connectLengthSeconds = double(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 4));
|
||||
m_connectOverlap = int(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 5));
|
||||
m_fftSize = int(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 6));
|
||||
m_dcRemoval = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 7);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmConnectivityMeasure::uninitialize()
|
||||
{
|
||||
m_signalDecoder.uninitialize();
|
||||
m_matrixEncoder.uninitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmConnectivityMeasure::processInput(const size_t)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
bool CBoxAlgorithmConnectivityMeasure::process()
|
||||
{
|
||||
|
||||
// the static box context describes the box inputs, outputs, settings structures
|
||||
const Kernel::IBox& staticBoxContext = this->getStaticBoxContext();
|
||||
|
||||
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//iterate over all chunk on input 0
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_signalDecoder.decode(i);
|
||||
|
||||
const uint64_t tStart = boxContext.getInputChunkStartTime(0, i); // Time Code Chunk Start
|
||||
const uint64_t tEnd = boxContext.getInputChunkEndTime(0, i); // Time Code Chunk End
|
||||
|
||||
if (m_signalDecoder.isHeaderReceived())
|
||||
{
|
||||
// Header received. This happens only once when pressing "play".
|
||||
|
||||
CMatrix* matrix = m_signalDecoder.getOutputMatrix(); // the StreamedMatrix of samples.
|
||||
uint64_t sampRate = m_signalDecoder.getOutputSamplingRate(); // the sampling rate of the signal
|
||||
|
||||
m_windowLength = std::floor((float) sampRate * m_windowLengthSeconds);
|
||||
m_connectLength = std::floor((float) sampRate * m_connectLengthSeconds);
|
||||
m_connectOverlapSamples = int(std::floor(double(m_connectLength) * double(m_connectOverlap) / 100.0));
|
||||
m_windowOverlapSamples = int(std::floor(double(m_windowLength) * double(m_windowOverlap) / 100.0));
|
||||
|
||||
m_nbChannels = matrix->getDimensionSize(0);
|
||||
const auto sampPerChan = matrix->getDimensionSize(1);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "HEADER : nChannels " << m_nbChannels << ", " << sampPerChan
|
||||
<< " samples, sampling rate " << sampRate << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Connectivity segments overlap percent/samples : "
|
||||
<< m_connectOverlap << " " << m_connectOverlapSamples << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Welch Windows overlap percent/samples : " << m_windowOverlap
|
||||
<< " " << m_windowOverlapSamples << "\n";
|
||||
|
||||
|
||||
// Vectors buffers init
|
||||
m_vectorXdBuffer.resize(m_nbChannels);
|
||||
m_signalChannelBuffers.resize(m_nbChannels);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "m_signalChannelBuffers.size() " << m_signalChannelBuffers.size()
|
||||
<< "\n";
|
||||
|
||||
// Connectivity algo class inits
|
||||
connectivityMeasure.initialize(m_metric, m_windowMethod, m_windowLength, m_windowOverlap, m_nbChannels,
|
||||
m_fftSize, m_dcRemoval);
|
||||
|
||||
matrix3DInit(*m_oMatrix, m_fftSize, m_nbChannels, m_nbChannels); // nbFreqs x nbChan x nbChan
|
||||
|
||||
m_matrixEncoder.encodeHeader(); // Pass the header to the next boxes
|
||||
|
||||
} else if (m_signalDecoder.isBufferReceived())
|
||||
{
|
||||
|
||||
CMatrix* matrix = m_signalDecoder.getOutputMatrix(); // the StreamedMatrix of samples.
|
||||
uint64_t sampRate = m_signalDecoder.getOutputSamplingRate(); // the sampling rate of the signal
|
||||
const auto sampPerChan = matrix->getDimensionSize(1);
|
||||
|
||||
// Accumulate buffers here and send a whole chunk to the connectivity algorithm
|
||||
const double* buffer = matrix->getBuffer();
|
||||
size_t idx = 0;
|
||||
std::vector<double> temp;
|
||||
for (size_t row = 0; row < m_nbChannels; ++row)
|
||||
{
|
||||
for (size_t col = 0; col < sampPerChan; ++col)
|
||||
{ // parse all columns in the buffer
|
||||
temp.push_back(buffer[idx++]);
|
||||
}
|
||||
m_signalChannelBuffers[row].insert(m_signalChannelBuffers[row].end(), temp.begin(), temp.end());
|
||||
temp.clear();
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "BUFFER : " << sampPerChan << " samples per " << m_nbChannels
|
||||
<< " channels, sampling rate " << sampRate << " // Signal buffers size : "
|
||||
<< m_signalChannelBuffers[0].size() << "\n";
|
||||
|
||||
// If enough data was accumulated, process it.
|
||||
if (m_signalChannelBuffers[0].size() >= m_connectLength)
|
||||
{
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Signal buffers : FULL (" << m_signalChannelBuffers[0].size()
|
||||
<< ")\n";
|
||||
|
||||
// Convert to Eigen container for easier use in algo
|
||||
for (size_t aa = 0; aa < m_nbChannels; ++aa)
|
||||
{
|
||||
m_vectorXdBuffer[aa] = Eigen::VectorXd::Map(m_signalChannelBuffers[aa].data(),
|
||||
m_signalChannelBuffers[aa].size());
|
||||
}
|
||||
|
||||
// Connectivity chunk overlap for next processing loop: Keep the overlapping part in the vectors, discard the rest
|
||||
for (size_t aa = 0; aa < m_nbChannels; ++aa)
|
||||
{
|
||||
m_signalChannelBuffers[aa].erase(m_signalChannelBuffers[aa].begin(),
|
||||
m_signalChannelBuffers[aa].begin() +
|
||||
(m_connectLength - m_connectOverlapSamples));
|
||||
}
|
||||
|
||||
// 3D Matrix init, vector of size (chan) of Matrices (chan x fftsize)
|
||||
std::vector <Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>> connectivityMatrix(m_nbChannels,
|
||||
Eigen::MatrixXd(
|
||||
m_nbChannels,
|
||||
m_fftSize));
|
||||
|
||||
OV_ERROR_UNLESS_KRF(connectivityMeasure.process(m_vectorXdBuffer, connectivityMatrix),
|
||||
"Connectivity measurement error", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Exited connectivityMeasure.process() : connect size "
|
||||
<< connectivityMatrix.size() << " x " << connectivityMatrix[0].rows() << " x "
|
||||
<< connectivityMatrix[0].cols() << "\n";
|
||||
|
||||
// Convert output matrix (nchan x nchan x fftsize) to matrix (fftsize x nchan x nchan)
|
||||
matrix3DConvert(connectivityMatrix, *m_oMatrix);
|
||||
|
||||
m_matrixEncoder.encodeBuffer();
|
||||
}
|
||||
|
||||
} else if (m_signalDecoder.isEndReceived())
|
||||
{
|
||||
m_matrixEncoder.encodeEnd();
|
||||
}
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, tStart, tEnd);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBoxAlgorithmConnectivityMeasure::matrix3DInit(CMatrix& m, const size_t dim0, const size_t dim1, const size_t dim2)
|
||||
{
|
||||
m.setDimensionCount(3);
|
||||
m.setDimensionSize(0, dim0);
|
||||
m.setDimensionSize(1, dim1);
|
||||
m.setDimensionSize(2, dim2);
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmConnectivityMeasure::matrix3DConvert(const std::vector <Eigen::MatrixXd>& in, CMatrix& out)
|
||||
{
|
||||
if (in.size() == 0 || in[0].rows() == 0 || in[0].cols() == 0)
|
||||
{ return false; }
|
||||
const size_t nChan0 = in.size(), nChan1 = in[0].rows(), fftSize = in[0].cols();
|
||||
|
||||
if (out.getDimensionCount() != 3
|
||||
|| out.getDimensionSize(0) != fftSize
|
||||
|| out.getDimensionSize(1) != nChan1
|
||||
|| out.getDimensionSize(2) != nChan0)
|
||||
{
|
||||
out.setDimensionCount(3);
|
||||
out.setDimensionSize(0, fftSize);
|
||||
out.setDimensionSize(1, nChan1);
|
||||
out.setDimensionSize(2, nChan0);
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
double* buffer = out.getBuffer();
|
||||
for (size_t fftIdx = 0; fftIdx < fftSize; ++fftIdx)
|
||||
{
|
||||
for (size_t chan1 = 0; chan1 < nChan1; ++chan1)
|
||||
{
|
||||
for (size_t chan0 = 0; chan0 < nChan0; ++chan0)
|
||||
{
|
||||
buffer[idx++] = in[chan0](chan1, fftIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file CBoxAlgorithmConnectivityMeasure.hpp
|
||||
/// \brief Classes of the Box Connectivity Measure.
|
||||
/// \author Arthur DESBOIS (INRIA).
|
||||
/// \version 0.0.1.
|
||||
/// \date Fri Oct 30 16:18:49 2020.
|
||||
///
|
||||
/// \copyright (C) 2020 INRIA
|
||||
///
|
||||
/// This program is free software: you can redistribute it and/or modify
|
||||
/// it under the terms of the GNU Affero General Public License as published
|
||||
/// by the Free Software Foundation, either version 3 of the License, or
|
||||
/// (at your option) any later version.
|
||||
///
|
||||
/// This program is distributed in the hope that it will be useful,
|
||||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/// GNU Affero General Public License for more details.
|
||||
///
|
||||
/// You should have received a copy of the GNU Affero General Public License
|
||||
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include "ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include "connectivityMeasure.hpp"
|
||||
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
///
|
||||
/// \brief The class CBoxAlgorithmConnectivityMeasure describes the box Connectivity Measure.
|
||||
///
|
||||
class CBoxAlgorithmConnectivityMeasure final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ConnectivityMeasure)
|
||||
|
||||
protected:
|
||||
|
||||
// Algo class instance
|
||||
ConnectivityMeasure connectivityMeasure;
|
||||
|
||||
// Codecs
|
||||
Toolkit::TSignalDecoder <CBoxAlgorithmConnectivityMeasure> m_signalDecoder;
|
||||
Toolkit::TStreamedMatrixEncoder <CBoxAlgorithmConnectivityMeasure> m_matrixEncoder; // Output Matrix Codec
|
||||
|
||||
// Matrices
|
||||
CMatrix* m_iMatrix = nullptr; // Input Matrix pointer
|
||||
CMatrix* m_oMatrix = nullptr; // Output Matrix pointer
|
||||
|
||||
std::vector <Eigen::VectorXd> m_vectorXdBuffer; // Vector buffer, for connectivity segments
|
||||
std::vector <std::vector<double>> m_signalChannelBuffers;
|
||||
|
||||
Eigen::VectorXd m_window;
|
||||
|
||||
// Settings
|
||||
EConnectMetric m_metric = EConnectMetric::Coherence;
|
||||
EConnectWindowMethod m_windowMethod = EConnectWindowMethod::Hann;
|
||||
|
||||
float m_windowLengthSeconds = 0.25f; // size of one windowing (sec)
|
||||
float m_connectLengthSeconds = 0.5f; // size of one full connectivity estimation occurrence (sec)
|
||||
|
||||
int m_windowLength = 128; // size of one windowing (samples)
|
||||
int m_windowOverlap = 50; // overlap btw windows (%)
|
||||
int m_connectLength = 256; // size of one full connectivity estimation occurrence (samples)
|
||||
int m_connectOverlap = 50; // overlap btw connectivity measurements (%)
|
||||
int m_fftSize = 128; // FFT size (and nb of freq taps at the output)
|
||||
|
||||
int m_nbChannels = 0;
|
||||
|
||||
int m_connectOverlapSamples = 128;
|
||||
int m_windowOverlapSamples = 64;
|
||||
|
||||
bool m_dcRemoval = false;
|
||||
|
||||
private:
|
||||
///
|
||||
/// \brief Conversion from a vector of 2D Matrices into OpenViBE's CMatrix of 3 dimensions
|
||||
/// \param in std:vector<Eigen::Matrix> (vect of nChan x Matrices (nChan x fftsize)
|
||||
/// \param out CMatrix of 3 dimensions (fftsize x nchan x nchan)
|
||||
/// \return True if the conversion was successful, false otherwise
|
||||
bool matrix3DConvert(const std::vector <Eigen::MatrixXd>& in, CMatrix& out);
|
||||
|
||||
///
|
||||
/// \brief Initialises 3 dimensions matrix with the provided dimensions
|
||||
/// \param m The matrix to initialise
|
||||
/// \param dim1 dimension 1
|
||||
/// \param dim2 dimension 2
|
||||
/// \param dim3 dimension 3
|
||||
void matrix3DInit(CMatrix& m, const size_t dim1, const size_t dim2, const size_t dim3);
|
||||
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Descriptor of the box Connectivity Measure.
|
||||
///
|
||||
class CBoxAlgorithmConnectivityMeasureDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override {}
|
||||
CString getName() const override { return CString("Connectivity Measure"); }
|
||||
CString getAuthorName() const override { return CString("Arthur DESBOIS"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
CString getShortDescription() const override { return CString("Connectivity Measure"); }
|
||||
CString getDetailedDescription() const override { return CString("Measure connectivity between pairs of channel"); }
|
||||
CString getCategory() const override { return CString("Signal processing/Connectivity"); }
|
||||
CString getVersion() const override { return CString("0.0.1"); }
|
||||
CString getStockItemName() const override { return CString(""); }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ConnectivityMeasure; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmConnectivityMeasure; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input signal", OV_TypeId_Signal);
|
||||
|
||||
prototype.addOutput("Connectivity Matrix", OV_TypeId_StreamedMatrix);
|
||||
|
||||
prototype.addSetting("Metric", OVP_TypeId_Connectivity_Metric,
|
||||
toString(EConnectMetric::Coherence).c_str());
|
||||
prototype.addSetting("Welch Window method", OV_TypeId_ConnectivityMeasure_WindowMethod, "Hann");
|
||||
prototype.addSetting("Welch Window Length (in sec)", OV_TypeId_Float, "0.25"); // s
|
||||
prototype.addSetting("Welch Window Overlap (in %)", OV_TypeId_Integer, "50"); // percent
|
||||
prototype.addSetting("Connectivity Measure Length (in sec)", OV_TypeId_Float, "0.5"); //s
|
||||
prototype.addSetting("Connectivity Measure Overlap (in %)", OV_TypeId_Integer, "50"); // percent
|
||||
prototype.addSetting("FFT size (frequency taps)", OV_TypeId_Integer, "128");
|
||||
prototype.addSetting("DC removal", OV_TypeId_Boolean, "false");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ConnectivityMeasureDesc)
|
||||
};
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "ovpCBoxAlgorithmXDAWNSpatialFilterTrainer.h"
|
||||
|
||||
#include <complex>
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <itpp/base/algebra/inv.h>
|
||||
#include <itpp/stat/misc_stat.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
// Taken from http://techlogbook.wordpress.com/2009/08/12/adding-generalized-eigenvalue-functions-to-it
|
||||
// http://techlogbook.wordpress.com/2009/08/12/calling-lapack-functions-from-c-codes
|
||||
// http://sourceforge.net/projects/itpp/forums/forum/115656/topic/3363490?message=7557038
|
||||
//
|
||||
// http://icl.cs.utk.edu/projectsfiles/f2j/javadoc/org/netlib/lapack/DSYGV.html
|
||||
// http://www.lassp.cornell.edu/sethna/GeneDynamics/NetworkCodeDocumentation/lapack_8h.html#a17
|
||||
|
||||
namespace {
|
||||
extern "C" {
|
||||
// This symbol comes from LAPACK
|
||||
/*
|
||||
void zggev_(char *jobvl, char *jobvr, int *n, std::complex<double> *a,
|
||||
int *lda, std::complex<double> *b, int *ldb, std::complex<double> *alpha,
|
||||
std::complex<double> *beta, std::complex<double> *vl,
|
||||
int *ldvl, std::complex<double> *vr, int *ldvr,
|
||||
std::complex<double> *work, int *lwork, double *rwork, int *info);
|
||||
*/
|
||||
int dsygv_(int* itype, char* jobz, char* uplo, int* n, double* a, int* lda, double* b, int* ldb, double* w, double* work, int* lwork, int* info);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace itppext {
|
||||
bool eig(const itpp::mat& A, const itpp::mat& B, itpp::vec& d, itpp::mat& V)
|
||||
{
|
||||
it_assert_debug(A.rows() == A.cols(), "eig: Matrix A is not square");
|
||||
it_assert_debug(B.rows() == B.cols(), "eig: Matrix B is not square");
|
||||
it_assert_debug(A.rows() == B.cols(), "eig: Matrix A and B don't have the same size");
|
||||
|
||||
const int worksize = 4 * A.rows(); // This may be chosen better!
|
||||
itpp::mat lA(A);
|
||||
itpp::mat lB(B);
|
||||
itpp::vec lW(A.rows());
|
||||
itpp::vec lWork(worksize);
|
||||
lW.zeros();
|
||||
lWork.zeros();
|
||||
|
||||
int itype = 1; // 1: Ax=lBx 2: ABx=lx 3: BAx=lx
|
||||
char jobz = 'V', uplo = 'U';
|
||||
int n = lA.rows();
|
||||
double* a = lA._data();
|
||||
int lda = n;
|
||||
double* b = lB._data();
|
||||
int ldb = n;
|
||||
double* w = lW._data();
|
||||
int lwork = worksize;
|
||||
double* work = lWork._data();
|
||||
int info = 0;
|
||||
|
||||
dsygv_(&itype, &jobz, &uplo, &n, a, &lda, b, &ldb, w, work, &lwork, &info);
|
||||
|
||||
d = lW;
|
||||
V = lA;
|
||||
|
||||
return (info == 0);
|
||||
}
|
||||
|
||||
itpp::mat convert(const CMatrix& matrix)
|
||||
{
|
||||
itpp::mat res(matrix.getDimensionSize(1), matrix.getDimensionSize(0));
|
||||
memcpy(res._data(), matrix.getBuffer(), matrix.getBufferElementCount() * sizeof(double));
|
||||
return res.transpose();
|
||||
}
|
||||
} // namespace itppext
|
||||
|
||||
bool CBoxAlgorithmXDAWNSpatialFilterTrainer::initialize()
|
||||
{
|
||||
m_stimulationDecoder.initialize(*this, 0);
|
||||
m_signalDecoder.initialize(*this, 1);
|
||||
m_evokedPotentialDecoder.initialize(*this, 2);
|
||||
|
||||
m_encoder.initialize(*this, 0);
|
||||
|
||||
m_stimID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_spatialFilterConfigurationFilename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
m_filterDim = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
m_saveAsBoxConfig = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmXDAWNSpatialFilterTrainer::uninitialize()
|
||||
{
|
||||
m_evokedPotentialDecoder.uninitialize();
|
||||
m_signalDecoder.uninitialize();
|
||||
m_stimulationDecoder.uninitialize();
|
||||
m_encoder.uninitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmXDAWNSpatialFilterTrainer::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
typedef struct
|
||||
{
|
||||
uint64_t startTime;
|
||||
uint64_t endTime;
|
||||
CMatrix* matrix;
|
||||
} chunk_t;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmXDAWNSpatialFilterTrainer::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
bool shouldTrain = false;
|
||||
uint64_t date = 0, chunkStartTime = 0, chunkEndTime = 0;
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_stimulationDecoder.decode(i);
|
||||
if (m_stimulationDecoder.isHeaderReceived())
|
||||
{
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if (m_stimulationDecoder.isBufferReceived())
|
||||
{
|
||||
IStimulationSet* stimSet = m_stimulationDecoder.getOutputStimulationSet();
|
||||
// See if there is a training stimulation. If several, accept the first one.
|
||||
for (size_t j = 0; j < stimSet->getStimulationCount(); ++j)
|
||||
{
|
||||
if (stimSet->getStimulationIdentifier(j) == m_stimID)
|
||||
{
|
||||
date = stimSet->getStimulationDate(j); // date of the last matching stimulus in the set
|
||||
chunkStartTime = boxContext.getInputChunkStartTime(0, i);
|
||||
chunkEndTime = boxContext.getInputChunkEndTime(0, i);
|
||||
shouldTrain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_stimulationDecoder.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
boxContext.markInputAsDeprecated(0, i);
|
||||
}
|
||||
|
||||
if (shouldTrain)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Received train stimulation - be patient\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Decoding signal chunks ...\n";
|
||||
|
||||
bool isContinuous = true;
|
||||
uint64_t end = 0;
|
||||
std::vector<chunk_t> chunks;
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_signalDecoder.decode(i);
|
||||
if (m_signalDecoder.isHeaderReceived())
|
||||
{
|
||||
// Don't care about the header
|
||||
}
|
||||
if (m_signalDecoder.isBufferReceived())
|
||||
{
|
||||
const CMatrix* matrix = m_signalDecoder.getOutputMatrix();
|
||||
chunk_t chunk;
|
||||
chunk.startTime = boxContext.getInputChunkStartTime(1, i);
|
||||
chunk.endTime = boxContext.getInputChunkEndTime(1, i);
|
||||
chunk.matrix = new CMatrix;
|
||||
chunk.matrix->copy(*matrix);
|
||||
chunks.push_back(chunk);
|
||||
|
||||
if (chunk.startTime != end)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Chunk " << i << " start time != last chunk end time [" << chunk.startTime << " vs "
|
||||
<< end << "]\n";
|
||||
isContinuous = false;
|
||||
break;
|
||||
}
|
||||
end = chunk.endTime;
|
||||
}
|
||||
if (m_signalDecoder.isEndReceived()) { }
|
||||
boxContext.markInputAsDeprecated(1, i);
|
||||
}
|
||||
|
||||
if (!isContinuous)
|
||||
{
|
||||
// @fixme mem leak
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Input signal is not continuous... Can't continue\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Decoding evoked response potential chunks ...\n";
|
||||
|
||||
std::vector<chunk_t> evokedPotential;
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(2); ++i)
|
||||
{
|
||||
m_evokedPotentialDecoder.decode(i);
|
||||
if (m_evokedPotentialDecoder.isHeaderReceived())
|
||||
{
|
||||
// Don't care about the header
|
||||
}
|
||||
if (m_evokedPotentialDecoder.isBufferReceived())
|
||||
{
|
||||
const CMatrix* matrix = m_evokedPotentialDecoder.getOutputMatrix();
|
||||
chunk_t chunk;
|
||||
chunk.startTime = boxContext.getInputChunkStartTime(2, i);
|
||||
chunk.endTime = boxContext.getInputChunkEndTime(2, i);
|
||||
chunk.matrix = new CMatrix;
|
||||
chunk.matrix->copy(*matrix);
|
||||
evokedPotential.push_back(chunk);
|
||||
}
|
||||
if (m_evokedPotentialDecoder.isEndReceived()) { }
|
||||
boxContext.markInputAsDeprecated(2, i);
|
||||
}
|
||||
|
||||
if (evokedPotential.empty())
|
||||
{
|
||||
// @fixme mem leak
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "No evoked potentials received... Can't continue\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Averaging evoked response potential...\n";
|
||||
|
||||
auto it = evokedPotential.begin();
|
||||
CMatrix averagedERPMatrixOV;
|
||||
averagedERPMatrixOV.copy(*it->matrix);
|
||||
for (++it; it != evokedPotential.end(); ++it)
|
||||
{
|
||||
const double* potentialBuffer = it->matrix->getBuffer();
|
||||
double* buffer = averagedERPMatrixOV.getBuffer();
|
||||
for (size_t j = 0; j < averagedERPMatrixOV.getBufferElementCount(); ++j) { *(buffer++) += *(potentialBuffer++); }
|
||||
}
|
||||
double* buffer = averagedERPMatrixOV.getBuffer();
|
||||
for (size_t j = 0; j < averagedERPMatrixOV.getBufferElementCount(); ++j) { (*buffer++) /= evokedPotential.size(); }
|
||||
|
||||
// WARNING - OpenViBE matrices are transposed ITPP matrices !
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Converting OpenViBE matrices to IT++ matrices...\n";
|
||||
|
||||
const size_t nChunk = chunks.size();
|
||||
const size_t nChannel = chunks.begin()->matrix->getDimensionSize(0);
|
||||
const size_t nSamplePerChunk = chunks.begin()->matrix->getDimensionSize(1);
|
||||
const size_t nSamplePerErp = averagedERPMatrixOV.getDimensionSize(1);
|
||||
|
||||
itpp::mat matrix(nChannel, nChunk * nSamplePerChunk);
|
||||
it = chunks.begin();
|
||||
for (size_t i = 0; it != chunks.end(); ++it, ++i)
|
||||
{
|
||||
const itpp::mat m = itppext::convert(*it->matrix);
|
||||
matrix.set_submatrix(0, int(i) * nSamplePerChunk, m);
|
||||
}
|
||||
|
||||
itpp::mat averagedERPMatrix(nChannel, nSamplePerErp);
|
||||
averagedERPMatrix = itppext::convert(averagedERPMatrixOV);
|
||||
|
||||
itpp::mat dMatrix(nChunk * nSamplePerChunk, nSamplePerErp);
|
||||
dMatrix.clear();
|
||||
for (it = evokedPotential.begin(); it != evokedPotential.end(); ++it)
|
||||
{
|
||||
// Compute index of the sample corresponding to the start of the ERP
|
||||
const uint64_t erpStartTime = it->startTime;
|
||||
const size_t erpStartIndex = size_t(CTime(erpStartTime).toSampleCount(m_signalDecoder.getOutputSamplingRate()));
|
||||
|
||||
for (size_t k = 0; k < nSamplePerErp; ++k) { dMatrix(erpStartIndex + k, k) = 1; }
|
||||
}
|
||||
|
||||
const itpp::mat A = (averagedERPMatrix * inv(dMatrix.transpose() * dMatrix) * averagedERPMatrix.transpose()) * double(evokedPotential.size())
|
||||
/ double(nSamplePerChunk * nChunk);
|
||||
std::stringstream s4;
|
||||
s4 << "A :\n" << A << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << s4.str() << "\n";
|
||||
|
||||
const itpp::mat B = (matrix * matrix.transpose()) / double(nSamplePerChunk * nChunk);
|
||||
|
||||
std::stringstream s5;
|
||||
s5 << "B :\n" << B << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << s5.str() << "\n";
|
||||
|
||||
// Free resources
|
||||
for (auto& c : chunks) { delete c.matrix; }
|
||||
chunks.clear();
|
||||
|
||||
for (auto& ep : evokedPotential) { delete ep.matrix; }
|
||||
evokedPotential.clear();
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Computing generalized eigen vector decomposition...\n";
|
||||
|
||||
itpp::mat eigenVector;
|
||||
itpp::vec eigenValue;
|
||||
|
||||
if (itppext::eig(A, B, eigenValue, eigenVector))
|
||||
{
|
||||
std::map<double, itpp::vec> eigenVectors;
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
itpp::vec v = eigenVector.get_col(i);
|
||||
eigenVectors[eigenValue[i]] = v / norm(v);
|
||||
}
|
||||
|
||||
size_t cnt = 0;
|
||||
//We need to compute the size of the first dimension before setting the matrix
|
||||
const size_t dimension1Size = eigenVectors.size() < m_filterDim ? eigenVectors.size() : m_filterDim;
|
||||
|
||||
CMatrix outputVectors;
|
||||
outputVectors.resize(dimension1Size, nChannel);
|
||||
|
||||
auto itR = eigenVectors.rbegin();
|
||||
for (size_t i = 0; i < dimension1Size; ++itR, i++) { for (size_t j = 0; j < nChannel; ++j) { outputVectors.getBuffer()[cnt++] = itR->second[j]; } }
|
||||
if (m_saveAsBoxConfig)
|
||||
{
|
||||
FILE* file = fopen(m_spatialFilterConfigurationFilename.toASCIIString(), "wb");
|
||||
if (!file)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The file [" << m_spatialFilterConfigurationFilename
|
||||
<< "] could not be opened for writing...";
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(file, "<OpenViBE-SettingsOverride>\n");
|
||||
fprintf(file, "\t<SettingValue>");
|
||||
for (size_t i = 0; i < outputVectors.getBufferElementCount(); ++i) { fprintf(file, "%e ", outputVectors.getBuffer()[i]); }
|
||||
fprintf(file, "</SettingValue>\n");
|
||||
fprintf(file, "\t<SettingValue>%zu</SettingValue>\n", m_filterDim);
|
||||
fprintf(file, "\t<SettingValue>%zu</SettingValue>\n", nChannel);
|
||||
fprintf(file, "\t<SettingValue></SettingValue>\n");
|
||||
fprintf(file, "</OpenViBE-SettingsOverride>\n");
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Toolkit::Matrix::saveToTextFile(outputVectors, m_spatialFilterConfigurationFilename))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unable to save to [" << m_spatialFilterConfigurationFilename << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Training finished... Eigen values are ";
|
||||
itR = eigenVectors.rbegin();
|
||||
for (size_t i = 0; itR != eigenVectors.rend() && i < m_filterDim; ++itR, i++) { this->getLogManager() << " | " << double(itR->first); }
|
||||
this->getLogManager() << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Generalized eigen vector decomposition failed...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "xDAWN Spatial filter trained successfully.\n";
|
||||
|
||||
m_encoder.getInputStimulationSet()->clear();
|
||||
m_encoder.getInputStimulationSet()->appendStimulation(OVTK_StimulationId_TrainCompleted, date, 0);
|
||||
m_encoder.encodeBuffer();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, chunkStartTime, chunkEndTime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxAlgorithmXDAWNSpatialFilterTrainer final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_XDAWNSpatialFilterTrainer)
|
||||
|
||||
protected:
|
||||
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmXDAWNSpatialFilterTrainer> m_stimulationDecoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmXDAWNSpatialFilterTrainer> m_signalDecoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmXDAWNSpatialFilterTrainer> m_evokedPotentialDecoder;
|
||||
|
||||
Toolkit::TStimulationEncoder<CBoxAlgorithmXDAWNSpatialFilterTrainer> m_encoder;
|
||||
|
||||
uint64_t m_stimID = 0;
|
||||
CString m_spatialFilterConfigurationFilename;
|
||||
size_t m_filterDim = 0;
|
||||
bool m_saveAsBoxConfig = false;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmXDAWNSpatialFilterTrainerDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("xDAWN Trainer (Deprecated)"); }
|
||||
CString getAuthorName() const override { return CString("Yann Renard"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
|
||||
CString getShortDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"Computes spatial filter coeffcients in order to get better evoked potential classification (typically used for P300 detection)");
|
||||
}
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Signal processing/Filtering"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-missing-image"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_XDAWNSpatialFilterTrainer; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmXDAWNSpatialFilterTrainer; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Stimulations", OV_TypeId_Stimulations);
|
||||
prototype.addInput("Session signal", OV_TypeId_Signal);
|
||||
prototype.addInput("Evoked potential epochs", OV_TypeId_Signal);
|
||||
prototype.addOutput("Train-completed Flag", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addSetting("Train stimulation", OV_TypeId_Stimulation, "OVTK_StimulationId_Train");
|
||||
prototype.addSetting("Spatial filter configuration", OV_TypeId_Filename, "");
|
||||
prototype.addSetting("Filter dimension", OV_TypeId_Integer, "4");
|
||||
prototype.addSetting("Save as box config", OV_TypeId_Boolean, "true");
|
||||
// prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
prototype.addFlag(Kernel::BoxFlag_IsDeprecated);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_XDAWNSpatialFilterTrainerDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyFFTW3) // fftw3 required by wavelet2s
|
||||
|
||||
#include "ovpCBoxAlgorithmDiscreteWaveletTransform.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <math.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "../../../contrib/packages/wavelet2d/wavelet2s.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmDiscreteWaveletTransform::initialize()
|
||||
{
|
||||
const size_t nOutput = this->getStaticBoxContext().getOutputCount();
|
||||
m_decoder.initialize(*this, 0); // Signal stream decoder
|
||||
m_encoder.initialize(*this, 0); // Signal stream encoder
|
||||
|
||||
m_waveletType = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_decompositionLevel = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
for (size_t o = 0; o < nOutput - 1; ++o) { m_encoders.push_back(new Toolkit::TSignalEncoder<CBoxAlgorithmDiscreteWaveletTransform>(*this, o + 1)); }
|
||||
|
||||
m_infolength = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmDiscreteWaveletTransform::uninitialize()
|
||||
{
|
||||
m_decoder.uninitialize();
|
||||
m_encoder.uninitialize();
|
||||
|
||||
for (auto& elem : m_encoders)
|
||||
{
|
||||
elem->uninitialize();
|
||||
delete elem;
|
||||
}
|
||||
m_encoders.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmDiscreteWaveletTransform::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmDiscreteWaveletTransform::process()
|
||||
{
|
||||
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
const int j = std::atoi(m_decompositionLevel);
|
||||
const std::string nm(m_waveletType.toASCIIString());
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
//Decode input signal
|
||||
m_decoder.decode(i);
|
||||
|
||||
// Construct header when we receive one
|
||||
if (m_decoder.isHeaderReceived())
|
||||
{
|
||||
const size_t nChannels0 = m_decoder.getOutputMatrix()->getDimensionSize(0);
|
||||
const size_t nSamples0 = m_decoder.getOutputMatrix()->getDimensionSize(1);
|
||||
|
||||
if (nSamples0 <= std::pow(2.0, j + 1))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Number of samples [" << nSamples0 << "] is smaller or equal than 2^{J+1} == ["
|
||||
<< std::pow(2.0, j + 1) << "]\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Verify quantity of samples and number of decomposition levels" << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Error <<
|
||||
"You can introduce a Time based epoching to have more samples per chunk or reduce the decomposition levels" << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//sig will be resized to the number of channels and the total number of samples (Channels x Samples)
|
||||
m_sig.resize(nChannels0);
|
||||
for (size_t c = 0; c < nChannels0; ++c) { m_sig[c].resize(nSamples0); }
|
||||
|
||||
//Do one dummy transform to get the m_flag and m_length filled. Since all channels & blocks have the same chunk size in OV, once is enough.
|
||||
std::vector<double> flag; //flag is an auxiliar vector (see wavelet2d library)
|
||||
std::vector<size_t> length; //length contains the length of each decomposition level. last entry is the length of the original signal.
|
||||
std::vector<double> dwtOutput; //dwt_output is the vector containing the decomposition levels
|
||||
|
||||
dwt(m_sig[0], j, nm, dwtOutput, flag, length);
|
||||
|
||||
// Set info stream dimension
|
||||
m_infolength = (length.size() + flag.size() + 2);
|
||||
m_encoder.getInputMatrix()->resize(nChannels0, m_infolength);
|
||||
|
||||
// Set decomposition stream dimensions
|
||||
for (size_t e = 0; e < m_encoders.size(); ++e) { m_encoders[e]->getInputMatrix()->resize(nChannels0, length[e]); }
|
||||
|
||||
// Set decomposition stream channel names
|
||||
for (size_t c = 0; c < nChannels0; c++)
|
||||
{
|
||||
for (auto& encoder : m_encoders) { encoder->getInputMatrix()->setDimensionLabel(0, c, m_decoder.getOutputMatrix()->getDimensionLabel(0, c)); }
|
||||
}
|
||||
|
||||
|
||||
// Info stream header
|
||||
m_encoder.getInputSamplingRate().setReferenceTarget(m_decoder.getOutputSamplingRate());
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
|
||||
// Decomposition stream headers
|
||||
for (size_t e = 0; e < m_encoders.size(); ++e)
|
||||
{
|
||||
const double sampling = double(m_decoder.getOutputSamplingRate()) / std::pow(2.0, int(e));
|
||||
m_encoders[e]->getInputSamplingRate() = uint64_t(std::floor(sampling));
|
||||
|
||||
m_encoders[e]->encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(e + 1, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
const CMatrix* matrix = m_decoder.getOutputMatrix();
|
||||
const double* buffer0 = matrix->getBuffer();
|
||||
|
||||
const size_t nChannels0 = matrix->getDimensionSize(0);
|
||||
const size_t nSamples0 = matrix->getDimensionSize(1);
|
||||
|
||||
//sig will store the samples of the different channels
|
||||
for (size_t c = 0; c < nChannels0; ++c) //Number of EEG channels
|
||||
{
|
||||
for (size_t s = 0; s < nSamples0; ++s) //Number of Samples per Chunk
|
||||
{
|
||||
m_sig[c][s] = (buffer0[s + c * nSamples0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Due to how wavelet2s works, we'll have to have the output variables empty before each call.
|
||||
std::vector<std::vector<double>> flag;
|
||||
std::vector<std::vector<size_t>> length;
|
||||
std::vector<std::vector<double>> dwtOutput;
|
||||
flag.resize(nChannels0);
|
||||
length.resize(nChannels0);
|
||||
dwtOutput.resize(nChannels0);
|
||||
|
||||
//Calculation of wavelets coefficients for each channel.
|
||||
for (size_t c = 0; c < nChannels0; ++c) { dwt(m_sig[c], j, nm, dwtOutput[c], flag[c], length[c]); }
|
||||
|
||||
//Transmission of some information (flag and legth) to the inverse dwt box
|
||||
//@fixme since the data dimensions do not change runtime, it should be sufficient to send this only once
|
||||
for (size_t c = 0; c < nChannels0; ++c)
|
||||
{
|
||||
size_t f = 0;
|
||||
m_encoder.getInputMatrix()->getBuffer()[f + c * m_infolength] = double(length[c].size());
|
||||
for (size_t l = 0; l < length[c].size(); ++l)
|
||||
{
|
||||
m_encoder.getInputMatrix()->getBuffer()[l + 1 + c * m_infolength] = double(length[c][l]);
|
||||
f = l;
|
||||
}
|
||||
m_encoder.getInputMatrix()->getBuffer()[f + 2 + c * m_infolength] = double(flag[c].size());
|
||||
for (size_t l = 0; l < flag[c].size(); ++l) { m_encoder.getInputMatrix()->getBuffer()[f + 3 + l + c * m_infolength] = flag[c][l]; }
|
||||
}
|
||||
|
||||
//Decode the dwt coefficients of each decomposition level to separate channels
|
||||
for (size_t c = 0; c < nChannels0; ++c)
|
||||
{
|
||||
for (size_t e = 0, vectorPos = 0; e < m_encoders.size(); ++e)
|
||||
{
|
||||
CMatrix* oMatrix = m_encoders[e]->getInputMatrix();
|
||||
double* oBuffer = oMatrix->getBuffer();
|
||||
|
||||
// loop levels
|
||||
for (size_t l = 0; l < size_t(length[c][e]); ++l) { oBuffer[l + c * length[c][e]] = dwtOutput[c][l + vectorPos]; }
|
||||
|
||||
vectorPos = vectorPos + length[c][e];
|
||||
}
|
||||
}
|
||||
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
|
||||
for (size_t e = 0; e < m_encoders.size(); ++e)
|
||||
{
|
||||
m_encoders[e]->encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(e + 1, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_decoder.isEndReceived())
|
||||
{
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
|
||||
for (size_t e = 0; e < m_encoders.size(); ++e)
|
||||
{
|
||||
m_encoders[e]->encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(e + 1, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyFFTW3) // required by wavelet2s
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmDiscreteWaveletTransform
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Wed Jul 16 15:05:16 2014
|
||||
* \brief The class CBoxAlgorithmDiscreteWaveletTransform describes the box DiscreteWaveletTransform.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmDiscreteWaveletTransform final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_DiscreteWaveletTransform)
|
||||
|
||||
protected:
|
||||
// Codec algorithms specified in the skeleton-generator:
|
||||
// Signal stream decoder
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmDiscreteWaveletTransform> m_decoder;
|
||||
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmDiscreteWaveletTransform> m_encoder;
|
||||
std::vector<Toolkit::TSignalEncoder<CBoxAlgorithmDiscreteWaveletTransform>*> m_encoders;
|
||||
|
||||
CString m_waveletType;
|
||||
CString m_decompositionLevel;
|
||||
|
||||
size_t m_infolength = 0;
|
||||
std::vector<std::vector<double>> m_sig;
|
||||
};
|
||||
|
||||
|
||||
// The box listener can be used to call specific callbacks whenever the box structure changes : input added, name changed, etc.
|
||||
// Please uncomment below the callbacks you want to use.
|
||||
class CBoxAlgorithmDiscreteWaveletTransformListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onSettingValueChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
if (index == 0) { return true; }
|
||||
|
||||
if (index == 1)
|
||||
{
|
||||
const size_t nOutputs = box.getOutputCount();
|
||||
CString str;
|
||||
box.getSettingValue(1, str);
|
||||
const size_t nDecompositionLevels = atoi(str);
|
||||
if (nOutputs != nDecompositionLevels + 2)
|
||||
{
|
||||
for (size_t i = 0; i < nOutputs; ++i) { box.removeOutput(nOutputs - i - 1); }
|
||||
|
||||
box.addOutput("Info",OV_TypeId_Signal);
|
||||
box.addOutput("A",OV_TypeId_Signal);
|
||||
for (size_t i = nDecompositionLevels; i > 0; i--) { box.addOutput(("D" + std::to_string(i)).c_str(),OV_TypeId_Signal); }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmDiscreteWaveletTransformDesc
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Wed Jul 16 15:05:16 2014
|
||||
* \brief Descriptor of the box DiscreteWaveletTransform.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmDiscreteWaveletTransformDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Discrete Wavelet Transform"); }
|
||||
CString getAuthorName() const override { return CString("Joao-Pedro Berti-Ligabo"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Calculate DiscreteWaveletTransform"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString("Calculate DiscreteWaveletTransform using different types of wavelets"); }
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Wavelets"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gnome-fs-regular.png"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_DiscreteWaveletTransform; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmDiscreteWaveletTransform; }
|
||||
|
||||
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmDiscreteWaveletTransformListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Signal",OV_TypeId_Signal);
|
||||
|
||||
prototype.addOutput("Info",OV_TypeId_Signal);
|
||||
prototype.addOutput("A",OV_TypeId_Signal);
|
||||
prototype.addOutput("D2",OV_TypeId_Signal);
|
||||
prototype.addOutput("D1",OV_TypeId_Signal);
|
||||
|
||||
prototype.addSetting("Wavelet type",OVP_TypeId_WaveletType, "");
|
||||
prototype.addSetting("Wavelet decomposition levels",OVP_TypeId_WaveletLevel, "");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_DiscreteWaveletTransformDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
|
||||
#endif
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "ovpCBoxAlgorithmEOG_Denoising.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising::initialize()
|
||||
{
|
||||
// Signal stream decoder
|
||||
m_algo0SignalDecoder.initialize(*this, 0);
|
||||
m_algo1SignalDecoder.initialize(*this, 1);
|
||||
|
||||
m_algo2SignalEncoder.getInputSamplingRate().setReferenceTarget(m_algo0SignalDecoder.getOutputSamplingRate());
|
||||
|
||||
m_filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
m_fBMatrixFile.open(m_filename.toASCIIString(), std::ios::in);
|
||||
if (m_fBMatrixFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Failed to open [" << m_filename << "] for reading\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fBMatrixFile >> m_nChannels0;
|
||||
m_fBMatrixFile >> m_nChannels1;
|
||||
m_fBMatrixFile >> m_nSamples0;
|
||||
|
||||
if (m_fBMatrixFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Not able to successfully read dims from [" << m_filename << "]\n";
|
||||
m_fBMatrixFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
m_nSamples1 = m_nSamples0;
|
||||
|
||||
m_noiseCoeff.resize(m_nChannels0, m_nChannels1); //Noise Coefficients Matrix (Dim: Channels EEG x Channels EOG)
|
||||
m_noiseCoeff.setZero(m_nChannels0, m_nChannels1);
|
||||
|
||||
for (size_t i = 0; i < m_nChannels0; ++i) //Number of channels
|
||||
{
|
||||
for (size_t j = 0; j < m_nChannels1; ++j) { m_fBMatrixFile >> m_noiseCoeff(i, j); } //Number of Samples per Chunk
|
||||
}
|
||||
|
||||
if (m_fBMatrixFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Not able to successfully read coefficients from [" << m_filename << "]\n";
|
||||
m_fBMatrixFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fBMatrixFile.close();
|
||||
|
||||
// Signal stream encoder
|
||||
m_algo2SignalEncoder.initialize(*this, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising::uninitialize()
|
||||
{
|
||||
m_algo0SignalDecoder.uninitialize();
|
||||
m_algo1SignalDecoder.uninitialize();
|
||||
m_algo2SignalEncoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
Eigen::MatrixXd data0(m_nChannels0, m_nSamples0); //EEG data
|
||||
Eigen::MatrixXd data1(m_nChannels1, m_nSamples1); //EOG data
|
||||
Eigen::MatrixXd eegC(m_nChannels0, m_nSamples0); //Corrected Matrix
|
||||
|
||||
if (boxContext.getInputChunkCount(0) != 0)
|
||||
{
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i) //Don't know why getinputchunkcount(1)
|
||||
{
|
||||
// Signal EEG
|
||||
|
||||
// decode the chunk ii on input 0
|
||||
m_algo0SignalDecoder.decode(i);
|
||||
|
||||
CMatrix* matrix0 = m_algo0SignalDecoder.getOutputMatrix();
|
||||
double* buffer0 = matrix0->getBuffer();
|
||||
|
||||
for (size_t c = 0; c < m_nChannels0; ++c) //Number of channels
|
||||
{
|
||||
for (size_t s = 0; s < m_nSamples0; ++s) { data0(c, s) = buffer0[s + c * m_nSamples0]; } //Number of Samples per Chunk
|
||||
}
|
||||
|
||||
//Signal EOG
|
||||
m_algo1SignalDecoder.decode(i);
|
||||
|
||||
CMatrix* matrix1 = m_algo1SignalDecoder.getOutputMatrix();
|
||||
double* buffer1 = matrix1->getBuffer();
|
||||
|
||||
for (size_t c = 0; c < m_nChannels1; ++c) //Number of channels
|
||||
{
|
||||
for (size_t s = 0; s < m_nSamples1; ++s) { data1(c, s) = buffer1[s + c * m_nSamples1]; } //Number of Samples per Chunk
|
||||
}
|
||||
|
||||
|
||||
//Set the output (corrected EEG) to the same structure as the EEG input
|
||||
m_algo2SignalEncoder.getInputMatrix()->resize(m_nChannels0, m_nSamples0);
|
||||
|
||||
|
||||
for (size_t c = 0; c < m_nChannels0; c++)
|
||||
{
|
||||
m_algo2SignalEncoder.getInputMatrix()->setDimensionLabel(0, c, m_algo0SignalDecoder.getOutputMatrix()->getDimensionLabel(0, c));
|
||||
}
|
||||
|
||||
|
||||
//Remove the noise
|
||||
eegC = data0 - (m_noiseCoeff * data1);
|
||||
|
||||
|
||||
for (size_t c = 0; c < m_nChannels0; ++c) //Number of EEG channels
|
||||
{
|
||||
for (size_t s = 0; s < m_nSamples0; ++s) //Number of Samples per Chunk
|
||||
{
|
||||
m_algo2SignalEncoder.getInputMatrix()->getBuffer()[s + c * m_nSamples0] = eegC(c, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_algo2SignalEncoder.getInputSamplingRate().setReferenceTarget(m_algo1SignalDecoder.getOutputSamplingRate());
|
||||
|
||||
|
||||
if (m_algo1SignalDecoder.isHeaderReceived())
|
||||
{
|
||||
m_algo2SignalEncoder.encodeHeader();
|
||||
// send the output chunk containing the header. The dates are the same as the input chunk:
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
|
||||
if (m_algo1SignalDecoder.isBufferReceived())
|
||||
{
|
||||
// Encode the output buffer :
|
||||
m_algo2SignalEncoder.encodeBuffer();
|
||||
// and send it to the next boxes :
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if (m_algo1SignalDecoder.isEndReceived())
|
||||
{
|
||||
// End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
|
||||
m_algo2SignalEncoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <fstream>
|
||||
|
||||
// Verify Eigen Path
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmEOG_Denoising
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Tue May 20 15:33:22 2014
|
||||
* \brief The class CBoxAlgorithmEOG_Denoising describes the box Test.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmEOG_Denoising final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
//Here is the different process callbacks possible
|
||||
// - On clock ticks :
|
||||
//virtual bool processClock(Kernel::CMessageClock& msg);
|
||||
// - On new input received (the most common behaviour for signal processing) :
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
// If you want to use processClock, you must provide the clock frequency.
|
||||
//virtual uint64_t getClockFrequency();
|
||||
|
||||
bool process() override;
|
||||
|
||||
// As we do with any class in openvibe, we use the macro below
|
||||
// to associate this box to an unique identifier.
|
||||
// The inheritance information is also made available,
|
||||
// as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_EOG_Denoising)
|
||||
|
||||
protected:
|
||||
// Codec algorithms specified in the skeleton-generator:
|
||||
// Signal stream decoder
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmEOG_Denoising> m_algo0SignalDecoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmEOG_Denoising> m_algo1SignalDecoder;
|
||||
|
||||
// Kernel::IAlgorithmProxy* m_matrixRegressionAlgorithm;
|
||||
// Kernel::TParameterHandler < CMatrix* > ip_pMatrixRegressionAlgorithm_Matrix0;
|
||||
// Kernel::TParameterHandler < CMatrix* > ip_pMatrixRegressionAlgorithm_Matrix1;
|
||||
|
||||
// Kernel::TParameterHandler < CMatrix* > op_pMatrixRegressionAlgorithm_Matrix;
|
||||
// Kernel::TParameterHandler < CString > par_Filename;
|
||||
|
||||
|
||||
// Signal stream encoder
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmEOG_Denoising> m_algo2SignalEncoder;
|
||||
|
||||
CString m_filename;
|
||||
std::ifstream m_fBMatrixFile;
|
||||
Eigen::MatrixXd m_noiseCoeff;
|
||||
|
||||
size_t m_nChannels0 = 0;
|
||||
size_t m_nChannels1 = 0;
|
||||
|
||||
size_t m_nSamples0 = 0;
|
||||
size_t m_nSamples1 = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmEOG_DenoisingDesc
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Tue May 20 15:33:22 2014
|
||||
* \brief Descriptor of the box Test.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmEOG_DenoisingDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("EOG Denoising"); }
|
||||
CString getAuthorName() const override { return CString("Joao-Pedro Berti-Ligabo"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("EOG Denoising using Regression Analysis"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString("Algorithm implementation as suggested in Schlogl's article of 2007"); }
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Denoising"); }
|
||||
CString getVersion() const override { return CString("023"); }
|
||||
CString getStockItemName() const override { return CString("gnome-fs-regular.png"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_EOG_Denoising; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmEOG_Denoising; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("EEG",OV_TypeId_Signal);
|
||||
prototype.addInput("EOG",OV_TypeId_Signal);
|
||||
|
||||
prototype.addOutput("EEG_Corrected",OV_TypeId_Signal);
|
||||
prototype.addSetting("Filename b Matrix", OV_TypeId_Filename, "b-Matrix-EEG.txt");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_EOG_DenoisingDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
#include "ovpCBoxAlgorithmEOG_Denoising_Calibration.h"
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising_Calibration::initialize()
|
||||
{
|
||||
m_algo0SignalDecoder.initialize(*this, 0);
|
||||
m_algo1SignalDecoder.initialize(*this, 1);
|
||||
m_algo2StimulationDecoder.initialize(*this, 2);
|
||||
m_stimulationEncoder.initialize(*this, 0);
|
||||
|
||||
m_calibrationFilename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_stimID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
m_startTime = 0;
|
||||
m_endTime = 0;
|
||||
m_nChunks = 0;
|
||||
m_chunksVerify = -1;
|
||||
m_endProcess = false;
|
||||
m_time = 0;
|
||||
m_startTimeChunks = 0;
|
||||
m_endTimeChunks = 0;
|
||||
|
||||
// Random id for tmp token, clash possible if multiple boxes run in parallel (but unlikely)
|
||||
const CString randomToken = CIdentifier::random().toString();
|
||||
m_eegTempFilename = this->getConfigurationManager().expand("${Path_Tmp}/denoising_") + randomToken + "_EEG_tmp.dat";
|
||||
m_eogTempFilename = this->getConfigurationManager().expand("${Path_Tmp}/denoising_") + randomToken + "_EOG_tmp.dat";
|
||||
|
||||
m_eegFile.open(m_eegTempFilename, std::ios::out | std::ios::in | std::ios::trunc);
|
||||
if (m_eegFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Opening [" << m_eegTempFilename << "] for r/w failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_eogFile.open(m_eogTempFilename, std::ios::out | std::ios::in | std::ios::trunc);
|
||||
if (m_eogFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Opening [" << m_eogTempFilename << "] for r/w failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_matrixFile.open(m_calibrationFilename.toASCIIString(), std::ios::out | std::ios::trunc);
|
||||
if (m_matrixFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Opening [" << m_calibrationFilename << "] for writing failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising_Calibration::uninitialize()
|
||||
{
|
||||
m_algo0SignalDecoder.uninitialize();
|
||||
m_algo1SignalDecoder.uninitialize();
|
||||
m_algo2StimulationDecoder.uninitialize();
|
||||
m_stimulationEncoder.uninitialize();
|
||||
|
||||
// Clean up temporary files
|
||||
if (m_eegFile.is_open()) { m_eegFile.close(); }
|
||||
if (m_eegTempFilename != CString("")) { std::remove(m_eegTempFilename); }
|
||||
|
||||
if (m_eogFile.is_open()) { m_eogFile.close(); }
|
||||
if (m_eogTempFilename != CString("")) { std::remove(m_eogTempFilename); }
|
||||
|
||||
if (m_matrixFile.is_open()) { m_matrixFile.close(); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising_Calibration::processClock(Kernel::CMessageClock& /*msg*/)
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
if (m_nChunks != m_chunksVerify && m_endProcess == false)
|
||||
{
|
||||
m_chunksVerify = m_nChunks;
|
||||
if (m_time == m_startTime) { m_startTimeChunks = m_nChunks; }
|
||||
|
||||
if (m_time == m_endTime) { m_endTimeChunks = m_nChunks; }
|
||||
}
|
||||
else if (m_nChunks == m_chunksVerify && m_endProcess == false)
|
||||
{
|
||||
if ((m_startTime >= m_endTime) || (m_endTime >= m_time))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "Verify time interval of sampling" << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "Total time of your sample: " << m_time << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "b Matrix was NOT successfully calculated" << "\n";
|
||||
|
||||
m_stimulationEncoder.getInputStimulationSet()->clear();
|
||||
m_stimulationEncoder.getInputStimulationSet()->appendStimulation(OVTK_StimulationId_TrainCompleted, 0, 0);
|
||||
m_stimulationEncoder.encodeBuffer();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, 0), boxContext.getInputChunkEndTime(0, 0));
|
||||
|
||||
//this->getLogManager() << Kernel::LogLevel_Warning << "You can stop this scenario " <<"\n";
|
||||
m_chunksVerify = -1;
|
||||
m_endProcess = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "End of data gathering...calculating b matrix" << "\n";
|
||||
|
||||
m_eegFile.close();
|
||||
m_eogFile.close();
|
||||
|
||||
m_eegFile.open(m_eegTempFilename, std::ios::in | std::ios::app);
|
||||
if (m_eegFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Opening [" << m_eegTempFilename << "] for reading failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_eogFile.open(m_eogTempFilename, std::ios::in | std::ios::app);
|
||||
if (m_eogFile.fail())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Opening [" << m_eogTempFilename << "] for reading failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//Process to extract the Matrix B
|
||||
double aux;
|
||||
|
||||
Eigen::MatrixXd data0(m_nChannels0, m_nSamples0 * m_nChunks); //EEG data
|
||||
Eigen::MatrixXd data1(m_nChannels1, m_nSamples1 * m_nChunks); //EOG data
|
||||
|
||||
for (size_t k = 0; k < m_nChunks; ++k)
|
||||
{
|
||||
for (size_t i = 0; i < m_nChannels0; ++i) //Number of channels
|
||||
{
|
||||
for (size_t j = 0; j < m_nSamples0; ++j) //Number of Samples per Chunk
|
||||
{
|
||||
m_eegFile >> aux;
|
||||
data0(i, j + k * m_nSamples0) = aux;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < m_nChunks; ++k)
|
||||
{
|
||||
for (size_t i = 0; i < m_nChannels1; ++i) //Number of channels
|
||||
{
|
||||
for (size_t j = 0; j < m_nSamples1; ++j) //Number of Samples per Chunk
|
||||
{
|
||||
m_eogFile >> aux;
|
||||
data1(i, j + k * m_nSamples1) = aux;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We will eliminate the firsts and lasts chunks of each channel
|
||||
|
||||
Eigen::MatrixXd data0N(m_nChannels0, m_nSamples0 * m_nChunks); //EEG data
|
||||
Eigen::MatrixXd data1N(m_nChannels1, m_nSamples1 * m_nChunks); //EOG data
|
||||
|
||||
size_t validChunks = m_endTimeChunks - m_startTimeChunks;
|
||||
|
||||
size_t iblockeeg = 0;
|
||||
size_t jblockeeg = m_startTimeChunks * m_nSamples0 - 1;
|
||||
size_t pblockeeg = m_nChannels0;
|
||||
size_t qblockeeg = m_nSamples0 * (validChunks);
|
||||
size_t iblockeog = 0;
|
||||
size_t jblockeog = m_startTimeChunks * m_nSamples1 - 1;
|
||||
size_t pblockeog = m_nChannels1;
|
||||
size_t qblockeog = m_nSamples1 * (validChunks);
|
||||
|
||||
|
||||
data0N = data0.block(iblockeeg, jblockeeg, pblockeeg, qblockeeg);
|
||||
data1N = data1.block(iblockeog, jblockeog, pblockeog, qblockeog);
|
||||
|
||||
|
||||
double nVal = 0;
|
||||
double min = 1e-6;
|
||||
double max = 1e6;
|
||||
|
||||
Eigen::VectorXd meanRowEEG(m_nChannels0);
|
||||
Eigen::VectorXd meanRowEog(m_nChannels1);
|
||||
|
||||
meanRowEEG.setZero(m_nChannels0, 1);
|
||||
meanRowEog.setZero(m_nChannels1, 1);
|
||||
|
||||
|
||||
for (size_t i = 0; i < m_nChannels0; ++i) //Number of channels
|
||||
{
|
||||
nVal = 0;
|
||||
for (size_t j = 0; j < m_nSamples0 * validChunks; ++j) //Number of Samples per Chunk
|
||||
{
|
||||
if ((data0N(i, j) > -max && data0N(i, j) < -min) || (data0N(i, j) > min && data0N(i, j) < max))
|
||||
{
|
||||
//Valid Interval
|
||||
meanRowEEG(i) = meanRowEEG(i) + data0N(i, j);
|
||||
nVal = nVal + 1;
|
||||
}
|
||||
}
|
||||
if (nVal != 0) { meanRowEEG(i) = meanRowEEG(i) / nVal; }
|
||||
else { meanRowEEG(i) = 0; }
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < m_nChannels1; ++i) //Number of channels
|
||||
{
|
||||
nVal = 0;
|
||||
for (size_t j = 0; j < m_nSamples1 * validChunks; ++j) //Number of Samples per Chunk
|
||||
{
|
||||
if ((data1N(i, j) > -max && data1N(i, j) < -min) || (data1N(i, j) > min && data1N(i, j) < max))
|
||||
{
|
||||
//Valid Interval
|
||||
meanRowEog(i) = meanRowEog(i) + data1N(i, j);
|
||||
nVal = nVal + 1;
|
||||
}
|
||||
}
|
||||
if (nVal != 0) { meanRowEog(i) = meanRowEog(i) / nVal; }
|
||||
else { meanRowEog(i) = 0; }
|
||||
}
|
||||
|
||||
|
||||
// The values which are not valid (very large or very small) will be set to the mean value
|
||||
// So these values will not influence to the covariance calcul because the covariance is centered (value - mean)
|
||||
|
||||
|
||||
for (size_t i = 0; i < m_nChannels0; ++i) //Number of channels
|
||||
{
|
||||
for (size_t j = 0; j < m_nSamples0 * validChunks; ++j) //Number of total samples
|
||||
{
|
||||
if ((data0N(i, j) > -max && data0N(i, j) < -min) || (data0N(i, j) > min && data0N(i, j) < max))
|
||||
{
|
||||
//Valid Interval
|
||||
data0N(i, j) = data0N(i, j) - meanRowEEG(i);
|
||||
}
|
||||
else { data0N(i, j) = 0; } //Invalid
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < m_nChannels1; ++i) //Number of channels
|
||||
{
|
||||
for (size_t j = 0; j < m_nSamples1 * validChunks; ++j) //Number of total samples
|
||||
{
|
||||
if ((data1N(i, j) > -max && data1N(i, j) < -min) || (data1N(i, j) > min && data1N(i, j) < max))
|
||||
{
|
||||
//Valid Interval
|
||||
data1N(i, j) = data1N(i, j) - meanRowEog(i);
|
||||
}
|
||||
else { data1N(i, j) = 0; } //Invalid
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Now we need to calculate the matrix b (which tells us the correct weights to be stored in b matrix)
|
||||
|
||||
|
||||
Eigen::MatrixXd noiseCoeff(m_nChannels0, m_nChannels1); //Noise Coefficients Matrix (Dim: Channels EEG x Channels EOG)
|
||||
Eigen::MatrixXd covEog(m_nChannels1, m_nChannels1);
|
||||
Eigen::MatrixXd covEogInv(m_nChannels1, m_nChannels1);
|
||||
Eigen::MatrixXd covEegAndEog(m_nChannels0, m_nChannels1);
|
||||
|
||||
covEog = (data1N * data1N.transpose());
|
||||
|
||||
covEogInv = covEog.inverse();
|
||||
|
||||
covEegAndEog = data0N * (data1N.transpose());
|
||||
|
||||
noiseCoeff = covEegAndEog * covEogInv;
|
||||
|
||||
|
||||
// Save Matrix b to the file specified in the parameters
|
||||
|
||||
|
||||
m_matrixFile << m_nChannels0 << " " << m_nChannels1 << " " << m_nSamples0 << "\n";
|
||||
|
||||
for (size_t i = 0; i < m_nChannels0; ++i) //Number of channels EEG
|
||||
{
|
||||
for (size_t j = 0; j < m_nChannels1; ++j) { m_matrixFile << noiseCoeff(i, j) << "\n"; } //Number of channels EOG
|
||||
}
|
||||
|
||||
|
||||
m_eegFile.close();
|
||||
m_eogFile.close();
|
||||
m_matrixFile.close();
|
||||
|
||||
m_chunksVerify = -1;
|
||||
m_endProcess = true;
|
||||
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "b Matrix was successfully calculated" << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Wrote the matrix to [" << m_calibrationFilename << "]\n";
|
||||
|
||||
//this->getLogManager() << Kernel::LogLevel_Warning << "You can stop this scenario " <<"\n";
|
||||
|
||||
m_stimulationEncoder.getInputStimulationSet()->clear();
|
||||
m_stimulationEncoder.getInputStimulationSet()->appendStimulation(OVTK_StimulationId_TrainCompleted, 0, 0);
|
||||
m_stimulationEncoder.encodeBuffer();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, 0), boxContext.getInputChunkEndTime(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
m_time++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising_Calibration::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmEOG_Denoising_Calibration::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
if (m_endProcess)
|
||||
{
|
||||
// We have done our stuff and have sent out a stimuli that we're done. However, if we're called again, we just do nothing,
|
||||
// but do not return false (==error) as this state is normal after training.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Signal EEG
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_algo0SignalDecoder.decode(i);
|
||||
|
||||
m_nChannels0 = m_algo0SignalDecoder.getOutputMatrix()->getDimensionSize(0);
|
||||
m_nSamples0 = m_algo0SignalDecoder.getOutputMatrix()->getDimensionSize(1);
|
||||
|
||||
CMatrix* matrix0 = m_algo0SignalDecoder.getOutputMatrix();
|
||||
double* buffer0 = matrix0->getBuffer();
|
||||
|
||||
for (size_t j = 0; j < matrix0->getBufferElementCount(); ++j) { m_eegFile << buffer0[j] << "\n"; }
|
||||
}
|
||||
//Signal EOG
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_algo1SignalDecoder.decode(i);
|
||||
|
||||
m_nChannels1 = m_algo1SignalDecoder.getOutputMatrix()->getDimensionSize(0);
|
||||
m_nSamples1 = m_algo1SignalDecoder.getOutputMatrix()->getDimensionSize(1);
|
||||
|
||||
CMatrix* matrix1 = m_algo1SignalDecoder.getOutputMatrix();
|
||||
double* buffer1 = matrix1->getBuffer();
|
||||
|
||||
for (size_t j = 0; j < matrix1->getBufferElementCount(); ++j) { m_eogFile << buffer1[j] << "\n"; }
|
||||
|
||||
m_nChunks++;
|
||||
}
|
||||
|
||||
|
||||
for (size_t chunk = 0; chunk < boxContext.getInputChunkCount(2); ++chunk)
|
||||
{
|
||||
m_algo2StimulationDecoder.decode(chunk);
|
||||
for (size_t j = 0; j < m_algo2StimulationDecoder.getOutputStimulationSet()->getStimulationCount(); ++j)
|
||||
{
|
||||
if (m_algo2StimulationDecoder.getOutputStimulationSet()->getStimulationIdentifier(j) == 33025)
|
||||
{
|
||||
m_startTime = m_time;
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Start time: " << m_startTime << "\n";
|
||||
}
|
||||
|
||||
if (m_algo2StimulationDecoder.getOutputStimulationSet()->getStimulationIdentifier(j) == 33031)
|
||||
{
|
||||
m_endTime = m_time;
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "End time: " << m_endTime << "\n";
|
||||
}
|
||||
|
||||
// m_trainDate = m_algo2StimulationDecoder.getOutputStimulationSet()->getStimulationDate(m_algo2StimulationDecoder.getOutputStimulationSet()->getStimulationCount());
|
||||
m_trainDate = m_algo2StimulationDecoder.getOutputStimulationSet()->getStimulationDate(j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmEOG_Denoising_Calibration
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Fri May 23 15:30:58 2014
|
||||
* \brief The class CBoxAlgorithmEOG_Denoising_Calibration describes the box EOG_Denoising_Calibration.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmEOG_Denoising_Calibration final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processClock(Kernel::CMessageClock& msg) override;
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
// If you want to use processClock, you must provide the clock frequency.
|
||||
uint64_t getClockFrequency() override { return 1LL << 32; } // the box clock frequency
|
||||
|
||||
bool process() override;
|
||||
|
||||
//virtual bool openfile();
|
||||
|
||||
// As we do with any class in openvibe, we use the macro below
|
||||
// to associate this box to an unique identifier.
|
||||
// The inheritance information is also made available,
|
||||
// as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_EOG_Denoising_Calibration)
|
||||
|
||||
protected:
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmEOG_Denoising_Calibration> m_algo0SignalDecoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmEOG_Denoising_Calibration> m_algo1SignalDecoder;
|
||||
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmEOG_Denoising_Calibration> m_algo2StimulationDecoder;
|
||||
Toolkit::TStimulationEncoder<CBoxAlgorithmEOG_Denoising_Calibration> m_stimulationEncoder;
|
||||
|
||||
CString m_calibrationFilename;
|
||||
|
||||
size_t m_chunksVerify = 0;
|
||||
size_t m_nChunks = 0;
|
||||
bool m_endProcess = false;
|
||||
std::fstream m_eegFile;
|
||||
std::fstream m_eogFile;
|
||||
std::ofstream m_matrixFile;
|
||||
|
||||
double m_startTime = 0;
|
||||
double m_endTime = 0;
|
||||
|
||||
size_t m_startTimeChunks = 0;
|
||||
size_t m_endTimeChunks = 0;
|
||||
|
||||
uint64_t m_trainDate = 0;
|
||||
uint64_t m_trainChunkStartTime = 0;
|
||||
uint64_t m_trainChunkEndTime = 0;
|
||||
|
||||
double m_time = 0;
|
||||
|
||||
size_t m_nChannels0 = 0;
|
||||
size_t m_nChannels1 = 0;
|
||||
|
||||
size_t m_nSamples0 = 0;
|
||||
size_t m_nSamples1 = 0;
|
||||
|
||||
uint64_t m_stimID = 0;
|
||||
|
||||
CString m_eegTempFilename;
|
||||
CString m_eogTempFilename;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmEOG_Denoising_CalibrationDesc
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Fri May 23 15:30:58 2014
|
||||
* \brief Descriptor of the box EOG_Denoising_Calibration.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmEOG_Denoising_CalibrationDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("EOG Denoising Calibration"); }
|
||||
CString getAuthorName() const override { return CString("Joao-Pedro Berti-Ligabo"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Algorithm implementation as suggested in Schlogl's article of 2007."); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("Press 'a' to set start point and 'u' to set end point, you can connect the Keyboard Stimulator for that");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Denoising"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gnome-fs-regular.png"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_EOG_Denoising_Calibration; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmEOG_Denoising_Calibration; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("EEG",OV_TypeId_Signal);
|
||||
prototype.addInput("EOG",OV_TypeId_Signal);
|
||||
prototype.addInput("Stimulations", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addSetting("Filename b Matrix", OV_TypeId_Filename, "b-Matrix-EEG.cfg");
|
||||
prototype.addSetting("End trigger", OV_TypeId_Stimulation, "OVTK_GDF_End_Of_Session");
|
||||
|
||||
prototype.addOutput("Train-completed Flag",OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_EOG_Denoising_CalibrationDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
|
||||
#endif
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyFFTW3) // required by wavelet2s
|
||||
|
||||
#include "ovpCBoxAlgorithmInverse_DWT.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "../../../contrib/packages/wavelet2d/wavelet2s.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmInverse_DWT::initialize()
|
||||
{
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
m_algoInfoDecoder.initialize(*this, 0);
|
||||
|
||||
m_waveletType = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_decompositionLevel = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
m_algoXDecoder = new Toolkit::TSignalDecoder<CBoxAlgorithmInverse_DWT> [nInput];
|
||||
|
||||
for (size_t o = 0; o < nInput - 1; ++o) { m_algoXDecoder[o].initialize(*this, o + 1); }
|
||||
|
||||
m_encoder.initialize(*this, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmInverse_DWT::uninitialize()
|
||||
{
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
|
||||
m_algoInfoDecoder.uninitialize();
|
||||
|
||||
for (size_t o = 0; o < nInput - 1; ++o) { m_algoXDecoder[o].uninitialize(); }
|
||||
|
||||
if (m_algoXDecoder)
|
||||
{
|
||||
delete[] m_algoXDecoder;
|
||||
m_algoXDecoder = nullptr;
|
||||
}
|
||||
|
||||
m_encoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmInverse_DWT::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmInverse_DWT::process()
|
||||
{
|
||||
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
// size_t J = std::atoi(m_decompositionLevel);
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
const std::string nm(m_waveletType.toASCIIString());
|
||||
std::vector<std::vector<double>> dwtop;
|
||||
std::vector<std::vector<double>> idwt_output;
|
||||
std::vector<std::vector<double>> flag;
|
||||
std::vector<std::vector<size_t>> length;
|
||||
|
||||
size_t flagReceveid = 0;
|
||||
|
||||
std::vector<size_t> nChannels(nInput);
|
||||
std::vector<size_t> nSamples(nInput);
|
||||
|
||||
//Check if all inputs have some information to decode
|
||||
if (boxContext.getInputChunkCount(nInput - 1) == 1)
|
||||
{
|
||||
//Decode the first input (Informations)
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_algoInfoDecoder.decode(i);
|
||||
|
||||
nChannels[0] = m_algoInfoDecoder.getOutputMatrix()->getDimensionSize(0);
|
||||
nSamples[0] = m_algoInfoDecoder.getOutputMatrix()->getDimensionSize(1);
|
||||
|
||||
|
||||
CMatrix* matrix = m_algoInfoDecoder.getOutputMatrix();
|
||||
double* buffer = matrix->getBuffer();
|
||||
|
||||
//this->getLogManager() << Kernel::LogLevel_Warning << "buffer 0 " << (size_t)buffer0[0] << "\n";
|
||||
|
||||
length.resize(nChannels[0]);
|
||||
flag.resize(nChannels[0]);
|
||||
|
||||
for (size_t j = 0; j < nChannels[0]; ++j)
|
||||
{
|
||||
size_t f = 0;
|
||||
for (size_t l = 0; l < size_t(buffer[0]); ++l)
|
||||
{
|
||||
length[j].push_back(size_t(buffer[l + 1]));
|
||||
f = l;
|
||||
}
|
||||
|
||||
for (size_t l = 0; l < size_t(buffer[f + 2]); ++l) { flag[j].push_back(size_t(buffer[f + 3 + l])); }
|
||||
}
|
||||
flagReceveid = 1;
|
||||
}
|
||||
|
||||
//If Informations is decoded
|
||||
if (flagReceveid == 1)
|
||||
{
|
||||
//Decode each decomposition level
|
||||
for (size_t o = 0; o < nInput - 1; ++o)
|
||||
{
|
||||
//Decode data of channels
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(o + 1); ++i)
|
||||
{
|
||||
m_algoXDecoder[o].decode(i);
|
||||
nChannels[o + 1] = m_algoXDecoder[o].getOutputMatrix()->getDimensionSize(0);
|
||||
nSamples[o + 1] = m_algoXDecoder[o].getOutputMatrix()->getDimensionSize(1);
|
||||
CMatrix* matrix = m_algoXDecoder[o].getOutputMatrix();
|
||||
double* buffer = matrix->getBuffer();
|
||||
|
||||
//dwtop is the dwt coefficients
|
||||
dwtop.resize(nChannels[0]);
|
||||
|
||||
//Store input data (dwt coefficients) in just one vector (dwtop)
|
||||
for (size_t j = 0; j < nChannels[0]; ++j)
|
||||
{
|
||||
for (size_t k = 0; k < nSamples[o + 1]; ++k) { dwtop[j].push_back(buffer[k + j * nSamples[o + 1]]); }
|
||||
}
|
||||
|
||||
//Check if received informations about dwt box are coherent with inverse dwt box settings
|
||||
if (!length[0].empty() && o == nInput - 2)
|
||||
{
|
||||
//Check if quantity of samples received are the same
|
||||
if (length[0].at(nInput - 1) == dwtop[0].size())
|
||||
{
|
||||
//Resize idwt vector
|
||||
idwt_output.resize(nChannels[0]);
|
||||
|
||||
//Calculate idwt for each channel
|
||||
for (size_t j = 0; j < nChannels[0]; ++j) { idwt(dwtop[j], flag[j], nm, idwt_output[j], length[j]); }
|
||||
|
||||
|
||||
m_encoder.getInputSamplingRate() = 2 * m_algoXDecoder[o].getOutputSamplingRate();
|
||||
|
||||
m_encoder.getInputMatrix()->resize(nChannels[0], length[0].at(nInput - 1));
|
||||
|
||||
|
||||
for (size_t j = 0; j < nChannels[0]; j++)
|
||||
{
|
||||
m_encoder.getInputMatrix()->setDimensionLabel(0, j, m_algoXDecoder[o].getOutputMatrix()->getDimensionLabel(0, j));
|
||||
}
|
||||
|
||||
|
||||
//Encode resultant signal to output
|
||||
for (size_t j = 0; j < nChannels[0]; ++j)
|
||||
{
|
||||
for (size_t k = 0; k < size_t(idwt_output[j].size()); ++k)
|
||||
{
|
||||
m_encoder.getInputMatrix()->getBuffer()[k + j * size_t(idwt_output[j].size())] = idwt_output[j][k];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
m_encoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
m_encoder.encodeEnd();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyFFTW3) // required by wavelet2s
|
||||
|
||||
//You may have to change this path to match your folder organisation
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
/**
|
||||
* \class CBoxAlgorithmInverse_DWT
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Thu Jul 24 10:57:05 2014
|
||||
* \brief The class CBoxAlgorithmInverse_DWT describes the box Inverse DWT.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmInverse_DWT final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
CBoxAlgorithmInverse_DWT() { }
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_Inverse_DWT)
|
||||
|
||||
protected:
|
||||
// Codec algorithms specified in the skeleton-generator:
|
||||
// Signal stream encoder
|
||||
Toolkit::TSignalEncoder<CBoxAlgorithmInverse_DWT> m_encoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmInverse_DWT> m_algoInfoDecoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmInverse_DWT>* m_algoXDecoder = nullptr;
|
||||
|
||||
CString m_waveletType;
|
||||
CString m_decompositionLevel;
|
||||
};
|
||||
|
||||
|
||||
class CBoxAlgorithmInverse_DWTListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onSettingValueChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
if (index == 0) { return true; }
|
||||
|
||||
if (index == 1)
|
||||
{
|
||||
const size_t nInput = box.getInputCount();
|
||||
|
||||
CString nDecompositionLevels;
|
||||
box.getSettingValue(1, nDecompositionLevels);
|
||||
|
||||
const size_t nDecompositionLevel = atoi(nDecompositionLevels);
|
||||
|
||||
if (nInput != nDecompositionLevel + 2)
|
||||
{
|
||||
for (size_t i = 0; i < nInput; ++i) { box.removeInput(nInput - i - 1); }
|
||||
|
||||
box.addInput("Info",OV_TypeId_Signal);
|
||||
box.addInput("A",OV_TypeId_Signal);
|
||||
for (size_t i = nDecompositionLevel; i > 0; i--) { box.addInput(("D" + std::to_string(i)).c_str(),OV_TypeId_Signal); }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmInverse_DWTDesc
|
||||
* \author Joao-Pedro Berti-Ligabo / Inria
|
||||
* \date Thu Jul 24 10:57:05 2014
|
||||
* \brief Descriptor of the box Inverse DWT.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmInverse_DWTDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Inverse DWT"); }
|
||||
CString getAuthorName() const override { return CString("Joao-Pedro Berti-Ligabo"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Calculate Inverse DiscreteWaveletTransform"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString("Calculate Inverse DiscreteWaveletTransform using different types of wavelets"); }
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Wavelets"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gnome-fs-regular.png"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Inverse_DWT; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmInverse_DWT; }
|
||||
|
||||
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmInverse_DWTListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Info",OV_TypeId_Signal);
|
||||
prototype.addInput("A",OV_TypeId_Signal);
|
||||
prototype.addInput("D2",OV_TypeId_Signal);
|
||||
prototype.addInput("D1",OV_TypeId_Signal);
|
||||
|
||||
prototype.addOutput("Signal",OV_TypeId_Signal);
|
||||
|
||||
prototype.addSetting("Wavelet type",OVP_TypeId_WaveletType, "");
|
||||
prototype.addSetting("Wavelet decomposition levels",OVP_TypeId_WaveletLevel, "");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_Inverse_DWTDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
|
||||
#endif
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
//include OpenViBE
|
||||
#include "ovpCBoxAlgorithmQuadraticForm.h"
|
||||
|
||||
//include C++ STL
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
//atoi
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmQuadraticForm::initialize()
|
||||
{
|
||||
//the algorithms that decode and encode the signals
|
||||
m_decoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalDecoder));
|
||||
m_encoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_decoder->initialize();
|
||||
m_encoder->initialize();
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling)->setReferenceTarget(
|
||||
m_encoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling));
|
||||
|
||||
//connecting the decoding and encoding the parameters
|
||||
m_iEBMLBufferHandle.initialize(m_decoder->getInputParameter(OVP_GD_Algorithm_SignalDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
m_iMatrixHandle.initialize(m_decoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputParameterId_Matrix));
|
||||
m_oMatrixHandle.initialize(m_encoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix));
|
||||
m_oEBMLBufferHandle.initialize(m_encoder->getOutputParameter(OVP_GD_Algorithm_SignalEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
|
||||
//end and start time
|
||||
m_startTime = 0;
|
||||
m_endTime = 0;
|
||||
|
||||
//reading the quadratic operator (matrix) values
|
||||
|
||||
//the number of rows/columns
|
||||
const size_t nRow = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
//setting the size of the matrix
|
||||
m_quadraticOperator.resize(nRow, nRow);
|
||||
|
||||
//the coefficients
|
||||
const CString coefs = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
const char* str = coefs.toASCIIString();
|
||||
double* buffer = m_quadraticOperator.getBuffer();
|
||||
|
||||
std::istringstream iss(str); //the stream for parsing the matrix coefficient
|
||||
double currentValue = 0.0; //the current coefficient being read
|
||||
|
||||
for (size_t i = 0; i < nRow; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < nRow; ++j)
|
||||
{
|
||||
//actual parsing, checking and storing value if everything is OK
|
||||
if (!(iss >> currentValue))
|
||||
{
|
||||
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error <<
|
||||
"Error reading quadratic operator coefficients\n The coefficients or the number of coefficient must be wrong\n";
|
||||
return false;
|
||||
}
|
||||
buffer[i * nRow + j] = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (iss >> currentValue)
|
||||
{
|
||||
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Warning <<
|
||||
"There may be more coefficients specified in the setting 'Matrix values' than the number of rows/columns can allow\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmQuadraticForm::uninitialize()
|
||||
{
|
||||
//uninitializing algorithms and parameters handlers
|
||||
m_decoder->uninitialize();
|
||||
m_encoder->uninitialize();
|
||||
m_iEBMLBufferHandle.uninitialize();
|
||||
m_oEBMLBufferHandle.uninitialize();
|
||||
m_iMatrixHandle.uninitialize();
|
||||
m_oMatrixHandle.uninitialize();
|
||||
|
||||
//releasing algorithms
|
||||
getAlgorithmManager().releaseAlgorithm(*m_decoder);
|
||||
getAlgorithmManager().releaseAlgorithm(*m_encoder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmQuadraticForm::processInput(const size_t /*index*/)
|
||||
{
|
||||
//if input is arrived, processing it, i.e., computing the corresponding quadratic forms
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmQuadraticForm::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//prcessing the input buffers
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
//decoding the input signal
|
||||
m_iEBMLBufferHandle = boxContext.getInputChunk(0, i);
|
||||
m_decoder->process();
|
||||
//storing the start and end time of the chunk
|
||||
m_startTime = boxContext.getInputChunkStartTime(0, i);
|
||||
m_endTime = boxContext.getInputChunkEndTime(0, i);
|
||||
|
||||
//deal with the header if needed (initializations)
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_SignalDecoder_OutputTriggerId_ReceivedHeader))
|
||||
{
|
||||
//getting some input matrix properties
|
||||
m_nChannels = m_iMatrixHandle->getDimensionSize(0);
|
||||
m_nSamplesPerBuffer = m_iMatrixHandle->getDimensionSize(1);
|
||||
|
||||
//checking that the number of channels is compatible with the quadratic operator size
|
||||
if (m_nChannels != m_quadraticOperator.getDimensionSize(0))
|
||||
{
|
||||
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error <<
|
||||
"The number of input channels is not compatible with the number of rows/columns of the quadratic operator matrix. This number of rows/columns must be equal to the number of input channels\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//setting the size of the output buffer
|
||||
m_oMatrixHandle->resize(1, m_nSamplesPerBuffer);
|
||||
|
||||
m_oEBMLBufferHandle = boxContext.getOutputChunk(0);
|
||||
//encoding the output
|
||||
m_encoder->process(OVP_GD_Algorithm_SignalEncoder_InputTriggerId_EncodeHeader);
|
||||
//sending the output
|
||||
boxContext.markOutputAsReadyToSend(0, m_startTime, m_endTime);
|
||||
}
|
||||
|
||||
//applying the quadratic operator
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_SignalDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
double* buffer = m_quadraticOperator.getBuffer();
|
||||
double* iBuffer = m_iMatrixHandle->getBuffer();
|
||||
double* oBuffer = m_oMatrixHandle->getBuffer();
|
||||
|
||||
//applying the quadratic operator for each sample: o = m^T * A * m
|
||||
for (size_t j = 0; j < m_nSamplesPerBuffer; ++j)
|
||||
{
|
||||
std::vector<double> prime(m_nChannels); //performing m' = A * m (intermediate step 1)
|
||||
for (size_t k = 0; k < prime.size(); ++k) { prime[k] = 0.0; } //initializing with zeros
|
||||
|
||||
//browsing the quadratic operator matrix (A) rows
|
||||
for (size_t k = 0; k < m_quadraticOperator.getDimensionSize(0); ++k)
|
||||
{
|
||||
//browsing the quadratic operator matrix (A) columns
|
||||
for (size_t l = 0; l < m_quadraticOperator.getDimensionSize(1); ++l)
|
||||
{
|
||||
prime[k] += buffer[k * m_quadraticOperator.getDimensionSize(0) + l] * iBuffer[l * m_nChannels + j];
|
||||
}
|
||||
}
|
||||
|
||||
//performing o = m^T * m' (intermediate step 2)
|
||||
double output = 0.0;
|
||||
for (size_t k = 0; k < prime.size(); ++k) { output += iBuffer[k * m_nChannels + j] * prime[k]; }
|
||||
|
||||
oBuffer[j] = output;
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(0, i);
|
||||
m_oEBMLBufferHandle = boxContext.getOutputChunk(0);
|
||||
m_encoder->process(OVP_GD_Algorithm_SignalEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(0, m_startTime, m_endTime);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxAlgorithmQuadraticForm final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
uint64_t getClockFrequency() override { return 0; } // the box clock frequency
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processClock(Kernel::CMessageClock& /*msg*/) override { return true; }
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_QuadraticForm)
|
||||
|
||||
protected:
|
||||
|
||||
//algorithms for encoding and decoding EBML stream
|
||||
Kernel::IAlgorithmProxy* m_encoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_decoder = nullptr;
|
||||
|
||||
//input and output buffers
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> m_iEBMLBufferHandle;
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> m_oEBMLBufferHandle;
|
||||
|
||||
//the signal matrices (input and output)
|
||||
Kernel::TParameterHandler<CMatrix*> m_iMatrixHandle;
|
||||
Kernel::TParameterHandler<CMatrix*> m_oMatrixHandle;
|
||||
|
||||
//start and end times
|
||||
uint64_t m_startTime = 0;
|
||||
uint64_t m_endTime = 0;
|
||||
|
||||
//The matrix used in the quadratic form: the quadratic operator
|
||||
CMatrix m_quadraticOperator;
|
||||
|
||||
//dimensions (number of input channels and number of samples) of the input buffer
|
||||
size_t m_nChannels = 0;
|
||||
size_t m_nSamplesPerBuffer = 0;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmQuadraticFormDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Quadratic Form"); }
|
||||
CString getAuthorName() const override { return CString("Fabien Lotte"); }
|
||||
CString getAuthorCompanyName() const override { return CString("IRISA-INSA Rennes"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Perform a quadratic matrix operation on the input signals m (result = m^T * A * m)"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"a square matrix A (which can be seen as a spatial filter) is applied to the input signals m (a vector). Then the transpose m^T of the input signals is multiplied to the resulting vector. In other words the output o is such as: o = m^T * A * m.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-missing-image"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_QuadraticForm; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmQuadraticForm; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("input signal", OV_TypeId_Signal);
|
||||
prototype.addOutput("output signal", OV_TypeId_Signal);
|
||||
prototype.addSetting("Matrix values", OV_TypeId_String, "1 0 0 1");
|
||||
prototype.addSetting("Number of rows/columns (square matrix)", OV_TypeId_Integer, "2");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_QuadraticFormDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_Identity OpenViBE::CIdentifier(0x5DFFE431, 0x35215C50)
|
||||
#define OVP_ClassId_IdentityDesc OpenViBE::CIdentifier(0x54743810, 0x6A1A88CC)
|
||||
#define OVP_ClassId_TimeBasedEpoching OpenViBE::CIdentifier(0x00777FA0, 0x5DC3F560)
|
||||
#define OVP_ClassId_TimeBasedEpochingDesc OpenViBE::CIdentifier(0x00ABDABE, 0x41381683)
|
||||
#define OVP_ClassId_BoxAlgorithm_EOG_Denoising OpenViBE::CIdentifier(0xC223FF12, 0x069A987E)
|
||||
#define OVP_ClassId_BoxAlgorithm_EOG_DenoisingDesc OpenViBE::CIdentifier(0x4F9BE623, 0xF2027046)
|
||||
#define OVP_ClassId_BoxAlgorithm_EOG_Denoising_Calibration OpenViBE::CIdentifier(0xE8DFE002, 0x70389932)
|
||||
#define OVP_ClassId_BoxAlgorithm_EOG_Denoising_CalibrationDesc OpenViBE::CIdentifier(0xF4D74831, 0x88B80DCF)
|
||||
#define OVP_ClassId_BoxAlgorithm_Inverse_DWT OpenViBE::CIdentifier(0x5B5B8468, 0x212CF963)
|
||||
#define OVP_ClassId_BoxAlgorithm_Inverse_DWTDesc OpenViBE::CIdentifier(0x01B9BC9A, 0x34766AE9)
|
||||
#define OVP_ClassId_BoxAlgorithm_DiscreteWaveletTransform OpenViBE::CIdentifier(0x824194C5, 0x46D7FDE9)
|
||||
#define OVP_ClassId_BoxAlgorithm_DiscreteWaveletTransformDesc OpenViBE::CIdentifier(0x6744711B, 0xF21B59EC)
|
||||
#define OVP_ClassId_BoxAlgorithm_EpochAverage OpenViBE::CIdentifier(0x21283D9F, 0xE76FF640)
|
||||
#define OVP_ClassId_BoxAlgorithm_EpochAverageDesc OpenViBE::CIdentifier(0x95F5F43E, 0xBE629D82)
|
||||
#define OVP_ClassId_Algorithm_MatrixAverage OpenViBE::CIdentifier(0x5E5A6C1C, 0x6F6BEB03)
|
||||
#define OVP_ClassId_Algorithm_MatrixAverageDesc OpenViBE::CIdentifier(0x1992881F, 0xC938C0F2)
|
||||
#define OVP_ClassId_BoxAlgorithm_Crop OpenViBE::CIdentifier(0x7F1A3002, 0x358117BA)
|
||||
#define OVP_ClassId_BoxAlgorithm_CropDesc OpenViBE::CIdentifier(0x64D619D7, 0x26CC42C9)
|
||||
#define OVP_ClassId_BoxAlgorithm_DifferentialIntegral OpenViBE::CIdentifier(0xCE490CBF, 0xDF7BA2E2)
|
||||
#define OVP_ClassId_BoxAlgorithm_DifferentialIntegralDesc OpenViBE::CIdentifier(0xCE490CBF, 0xDF7BA2E3)
|
||||
#define OVP_ClassId_BoxAlgorithm_MatrixTranspose OpenViBE::CIdentifier(0x5E0F04B5, 0x5B5005CF)
|
||||
#define OVP_ClassId_BoxAlgorithm_MatrixTransposeDesc OpenViBE::CIdentifier(0x119249F7, 0x556C7E0D)
|
||||
#define OVP_ClassId_BoxAlgorithm_ERSPAverage OpenViBE::CIdentifier(0x3CDB4B72, 0x295D51F7)
|
||||
#define OVP_ClassId_BoxAlgorithm_ERSPAverageDesc OpenViBE::CIdentifier(0x32C45B7E, 0x2F1B7D58)
|
||||
#define OVP_ClassId_BoxAlgorithm_ARCoefficients OpenViBE::CIdentifier(0xBAADC2F3, 0xB556A07B)
|
||||
#define OVP_ClassId_BoxAlgorithm_ARCoefficientsDesc OpenViBE::CIdentifier(0xBAADC2F3, 0xB556A07A)
|
||||
#define OVP_ClassId_BoxAlgorithm_ConnectivityMeasure OpenViBE::CIdentifier(0x994a9a45, 0x4181a048)
|
||||
#define OVP_ClassId_BoxAlgorithm_ConnectivityMeasureDesc OpenViBE::CIdentifier(0xaf5e56f9, 0xcbc54c18)
|
||||
#define OVP_ClassId_ReferenceChannel OpenViBE::CIdentifier(0xEFA8E95B, 0x4F22551B)
|
||||
#define OVP_ClassId_ReferenceChannelDesc OpenViBE::CIdentifier(0x1873B151, 0x969DD4E4)
|
||||
#define OVP_ClassId_ChannelSelector OpenViBE::CIdentifier(0x39484563, 0x46386889)
|
||||
#define OVP_ClassId_ChannelSelectorDesc OpenViBE::CIdentifier(0x34893489, 0x44934897)
|
||||
#define OVP_ClassId_SimpleDSP OpenViBE::CIdentifier(0x00E26FA1, 0x1DBAB1B2)
|
||||
#define OVP_ClassId_SimpleDSPDesc OpenViBE::CIdentifier(0x00C44BFE, 0x76C9269E)
|
||||
#define OVP_ClassId_SignalAverage OpenViBE::CIdentifier(0x00642C4D, 0x5DF7E50A)
|
||||
#define OVP_ClassId_SignalAverageDesc OpenViBE::CIdentifier(0x007CDCE9, 0x16034F77)
|
||||
#define OVP_ClassId_SignalConcatenation OpenViBE::CIdentifier(0x6568D29B, 0x0D753CCA)
|
||||
#define OVP_ClassId_SignalConcatenationDesc OpenViBE::CIdentifier(0x3921BACD, 0x1E9546FE)
|
||||
#define OVP_ClassId_BoxAlgorithm_QuadraticForm OpenViBE::CIdentifier(0x54E73B81, 0x1AD356C6)
|
||||
#define OVP_ClassId_BoxAlgorithm_QuadraticFormDesc OpenViBE::CIdentifier(0x31C11856, 0x3E4F7B67)
|
||||
#define OVP_ClassId_BoxAlgorithm_EpochVariance OpenViBE::CIdentifier(0x335384EA, 0x88C917D9)
|
||||
#define OVP_ClassId_BoxAlgorithm_EpochVarianceDesc OpenViBE::CIdentifier(0xA15EAEC5, 0xAB0CE73D)
|
||||
#define OVP_ClassId_Algorithm_MatrixVariance OpenViBE::CIdentifier(0x7FEFDCA9, 0x816ED903)
|
||||
#define OVP_ClassId_Algorithm_MatrixVarianceDesc OpenViBE::CIdentifier(0xE405260B, 0x59EEFAE4)
|
||||
#define OVP_ClassId_Algorithm_HilbertTransform OpenViBE::CIdentifier(0x344B79DE, 0x89EAAABB)
|
||||
#define OVP_ClassId_Algorithm_HilbertTransformDesc OpenViBE::CIdentifier(0x8CAB236A, 0xA789800D)
|
||||
#define OVP_ClassId_BoxAlgorithm_Hilbert OpenViBE::CIdentifier(0x7878A47F, 0x9A8FE349)
|
||||
#define OVP_ClassId_BoxAlgorithm_HilbertDesc OpenViBE::CIdentifier(0x2DB54E2F, 0x435675EF)
|
||||
#define OVP_ClassId_Algorithm_ARBurgMethod OpenViBE::CIdentifier(0x3EC6A165, 0x2823A034)
|
||||
#define OVP_ClassId_Algorithm_ARBurgMethodDesc OpenViBE::CIdentifier(0xD7234DFF, 0x55447A14)
|
||||
#define OVP_ClassId_BoxAlgorithm_XDAWNSpatialFilterTrainer OpenViBE::CIdentifier(0xAE241F9F, 0x599FAD88)
|
||||
#define OVP_ClassId_BoxAlgorithm_XDAWNSpatialFilterTrainerDesc OpenViBE::CIdentifier(0x46FFAD13, 0x5F5C68CE)
|
||||
#define OVP_ClassId_BoxAlgorithm_IFFTbox OpenViBE::CIdentifier(0xD533E997, 0x4AFD2423)
|
||||
#define OVP_ClassId_BoxAlgorithm_IFFTboxDesc OpenViBE::CIdentifier(0xD533E997, 0x4AFD2423)
|
||||
#define OVP_ClassId_BoxAlgorithm_Matrix3dTo2d OpenViBE::CIdentifier(0x3ab2b81e, 0x73ef01a5)
|
||||
#define OVP_ClassId_BoxAlgorithm_Matrix3dTo2dDesc OpenViBE::CIdentifier(0xb9099590, 0xae33d758)
|
||||
|
||||
// Type definitions
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
#define OVP_TypeId_EpochAverageMethod OpenViBE::CIdentifier(0x6530BDB1, 0xD057BBFE)
|
||||
#define OVP_TypeId_CropMethod OpenViBE::CIdentifier(0xD0643F9E, 0x8E35FE0A)
|
||||
#define OVP_TypeId_SelectionMethod OpenViBE::CIdentifier(0x3BCF9E67, 0x0C23994D)
|
||||
#define OVP_TypeId_MatchMethod OpenViBE::CIdentifier(0x666F25E9, 0x3E5738D6)
|
||||
#define OVP_TypeId_DifferentialIntegralOperation OpenViBE::CIdentifier(0x6E6AD85D, 0x14FD203A)
|
||||
#define OVP_TypeId_WindowType OpenViBE::CIdentifier(0x332BBB80, 0xC212810A)
|
||||
#define OVP_TypeId_WaveletType OpenViBE::CIdentifier(0x393EAC3E, 0x793C0F1D)
|
||||
#define OVP_TypeId_WaveletLevel OpenViBE::CIdentifier(0xF80A2144, 0x6E692C51)
|
||||
|
||||
enum class EEpochAverageMethod { Moving, MovingImmediate, Block, Cumulative };
|
||||
|
||||
enum class ECropMethod { Min, Max, MinMax };
|
||||
|
||||
enum class ESelectionMethod { Select, Reject, Select_EEG };
|
||||
|
||||
enum class EMatchMethod { Name, Index, Smart };
|
||||
|
||||
enum class EDifferentialIntegralOperation { Differential, Integral };
|
||||
|
||||
enum class EWaveletType
|
||||
{
|
||||
Haar,
|
||||
Db1, Db2, Db3, Db4, Db5, Db6, Db7, Db8, Db9, Db10, Db11, Db12, Db13, Db14, Db15,
|
||||
Bior11, Bior13, Bior15, Bior22, Bior24, Bior26, Bior28, Bior31, Bior33, Bior35, Bior37, Bior39, Bior44, Bior55, Bior68,
|
||||
Coif1, Coif2, Coif3, Coif4, Coif5,
|
||||
Sym1, Sym2, Sym3, Sym4, Sym5, Sym6, Sym7, Sym8, Sym9, Sym10
|
||||
};
|
||||
|
||||
enum class EWaveletLevel { L1, L2, L3, L4, L5 };
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OVP_Value_CoupledStringSeparator '-'
|
||||
//#define OVP_Value_AllSelection '*'
|
||||
|
||||
#define OVP_Algorithm_MatrixAverage_InputParameterId_Matrix OpenViBE::CIdentifier(0x913E9C3B, 0x8A62F5E3)
|
||||
#define OVP_Algorithm_MatrixAverage_InputParameterId_MatrixCount OpenViBE::CIdentifier(0x08563191, 0xE78BB265)
|
||||
#define OVP_Algorithm_MatrixAverage_InputParameterId_AveragingMethod OpenViBE::CIdentifier(0xE63CD759, 0xB6ECF6B7)
|
||||
#define OVP_Algorithm_MatrixAverage_OutputParameterId_AveragedMatrix OpenViBE::CIdentifier(0x03CE5AE5, 0xBD9031E0)
|
||||
#define OVP_Algorithm_MatrixAverage_InputTriggerId_Reset OpenViBE::CIdentifier(0x670EC053, 0xADFE3F5C)
|
||||
#define OVP_Algorithm_MatrixAverage_InputTriggerId_FeedMatrix OpenViBE::CIdentifier(0x50B6EE87, 0xDC42E660)
|
||||
#define OVP_Algorithm_MatrixAverage_InputTriggerId_ForceAverage OpenViBE::CIdentifier(0xBF597839, 0xCD6039F0)
|
||||
#define OVP_Algorithm_MatrixAverage_OutputTriggerId_AveragePerformed OpenViBE::CIdentifier(0x2BFF029B, 0xD932A613)
|
||||
|
||||
#define OVP_Algorithm_StimulationBasedEpoching_InputParameterId_InputSignal OpenViBE::CIdentifier(0x0ED5C92B, 0xE16BEF25)
|
||||
#define OVP_Algorithm_StimulationBasedEpoching_InputParameterId_OffsetSampleCount OpenViBE::CIdentifier(0x7646CE65, 0xE128FC4E)
|
||||
#define OVP_Algorithm_StimulationBasedEpoching_OutputParameterId_OutputSignal OpenViBE::CIdentifier(0x00D331A2, 0xC13DF043)
|
||||
#define OVP_Algorithm_StimulationBasedEpoching_InputTriggerId_Reset OpenViBE::CIdentifier(0x6BA44128, 0x418CF901)
|
||||
#define OVP_Algorithm_StimulationBasedEpoching_InputTriggerId_PerformEpoching OpenViBE::CIdentifier(0xD05579B5, 0x2649A4B2)
|
||||
#define OVP_Algorithm_StimulationBasedEpoching_OutputTriggerId_EpochingDone OpenViBE::CIdentifier(0x755BC3FE, 0x24F7B50F)
|
||||
#define OVP_Algorithm_StimulationBasedEpoching_InputParameterId_EndTimeChunkToProcess OpenViBE::CIdentifier(0x8B552604, 0x10CD1F94)
|
||||
|
||||
#define OVP_Algorithm_MatrixVariance_InputParameterId_Matrix OpenViBE::CIdentifier(0x781F51CA, 0xE6E3B0B8)
|
||||
#define OVP_Algorithm_MatrixVariance_InputParameterId_MatrixCount OpenViBE::CIdentifier(0xE5103C63, 0x08D825E0)
|
||||
#define OVP_Algorithm_MatrixVariance_InputParameterId_AveragingMethod OpenViBE::CIdentifier(0x043A1BC4, 0x925D3CD6)
|
||||
#define OVP_Algorithm_MatrixVariance_InputParameterId_SignificanceLevel OpenViBE::CIdentifier(0x1E1065B2, 0x2CA32013)
|
||||
#define OVP_Algorithm_MatrixVariance_OutputParameterId_AveragedMatrix OpenViBE::CIdentifier(0x5CF66A73, 0xF5BBF0BF)
|
||||
#define OVP_Algorithm_MatrixVariance_OutputParameterId_Variance OpenViBE::CIdentifier(0x1BD67420, 0x587600E6)
|
||||
#define OVP_Algorithm_MatrixVariance_OutputParameterId_ConfidenceBound OpenViBE::CIdentifier(0x1E1065B2, 0x2CA32013)
|
||||
#define OVP_Algorithm_MatrixVariance_InputTriggerId_Reset OpenViBE::CIdentifier(0xD5C5EF91, 0xE1B1C4F4)
|
||||
#define OVP_Algorithm_MatrixVariance_InputTriggerId_FeedMatrix OpenViBE::CIdentifier(0xEBAEB213, 0xDD4735A0)
|
||||
#define OVP_Algorithm_MatrixVariance_InputTriggerId_ForceAverage OpenViBE::CIdentifier(0x344A52F5, 0x489DB439)
|
||||
#define OVP_Algorithm_MatrixVariance_OutputTriggerId_AveragePerformed OpenViBE::CIdentifier(0x2F9ECA0B, 0x8D3CA7BD)
|
||||
|
||||
#define OVP_Algorithm_ARBurgMethod_InputParameterId_Matrix OpenViBE::CIdentifier(0x36A69669, 0x3651271D)
|
||||
#define OVP_Algorithm_ARBurgMethod_OutputParameterId_Matrix OpenViBE::CIdentifier(0x55EF8C81, 0x178A51B2)
|
||||
#define OVP_Algorithm_ARBurgMethod_InputParameterId_UInteger OpenViBE::CIdentifier(0x33139BC1, 0x03D30D3B)
|
||||
#define OVP_Algorithm_ARBurgMethod_InputTriggerId_Initialize OpenViBE::CIdentifier(0xC27B06C6, 0xB8EB5F8D)
|
||||
#define OVP_Algorithm_ARBurgMethod_InputTriggerId_Process OpenViBE::CIdentifier(0xBEEBBE84, 0x4F14F8F8)
|
||||
#define OVP_Algorithm_ARBurgMethod_OutputTriggerId_ProcessDone OpenViBE::CIdentifier(0xA5AAD435, 0x9EC3DB80)
|
||||
|
||||
#define OVP_Algorithm_MagnitudeSquaredCoherence_InputParameterId_SegLength OpenViBE::CIdentifier(0xA4826743, 0x0FA27C06)
|
||||
#define OVP_Algorithm_MagnitudeSquaredCoherence_InputParameterId_Overlap OpenViBE::CIdentifier(0x527F8AEC, 0xA25F2EAB)
|
||||
#define OVP_Algorithm_MagnitudeSquaredCoherence_InputParameterId_Window OpenViBE::CIdentifier(0x0EA349EE, 0xB9DC95D0)
|
||||
#define OVP_Algorithm_MagnitudeSquaredCoherence_InputParameterId_Nfft OpenViBE::CIdentifier(0x7726C677, 0xE266C5A2)
|
||||
#define OVP_Algorithm_MagnitudeSquaredCoherence_OutputParameterId_OutputMatrixSpectrum OpenViBE::CIdentifier(0x331326BA, 0xA94CFC8A)
|
||||
#define OVP_Algorithm_MagnitudeSquaredCoherence_OutputParameterId_FreqVector OpenViBE::CIdentifier(0xD9FAA21C, 0x67D7C451)
|
||||
|
||||
#define OVP_Algorithm_HilbertTransform_InputParameterId_Matrix OpenViBE::CIdentifier(0xC117CE9A, 0x3FFCB156)
|
||||
#define OVP_Algorithm_HilbertTransform_OutputParameterId_HilbertMatrix OpenViBE::CIdentifier(0xDAE13CB8, 0xEFF82E69)
|
||||
#define OVP_Algorithm_HilbertTransform_OutputParameterId_EnvelopeMatrix OpenViBE::CIdentifier(0x9D0A023A, 0x7690C48E)
|
||||
#define OVP_Algorithm_HilbertTransform_OutputParameterId_PhaseMatrix OpenViBE::CIdentifier(0x495B55E2, 0x8CAAC08E)
|
||||
#define OVP_Algorithm_HilbertTransform_InputTriggerId_Initialize OpenViBE::CIdentifier(0xE4B3CB4A, 0xF0121A20)
|
||||
#define OVP_Algorithm_HilbertTransform_InputTriggerId_Process OpenViBE::CIdentifier(0xC3DC087D, 0x4AAFC1F0)
|
||||
#define OVP_Algorithm_HilbertTransform_OutputTriggerId_ProcessDone OpenViBE::CIdentifier(0xB0B2A2DD, 0x73529B46)
|
||||
|
||||
#define OVP_TypeId_Connectivity_Metric OpenViBE::CIdentifier(0x9188339d, 0x5da83a84)
|
||||
#define OV_TypeId_ConnectivityMeasure_WindowMethod OpenViBE::CIdentifier(0x8815bfa7, 0x557b102f)
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
#include "box-algorithms/basic/ovpCBoxAlgorithmMatrixTranspose.h"
|
||||
#include "box-algorithms/basic/ovpCBoxAlgorithmDifferentialIntegral.h"
|
||||
|
||||
#include "box-algorithms/connectivity/CBoxAlgorithmConnectivityMeasure.hpp"
|
||||
|
||||
#include "box-algorithms/basic/ovpCBoxAlgorithmHilbert.h"
|
||||
#include "algorithms/basic/ovpCHilbertTransform.h"
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmQuadraticForm.h"
|
||||
|
||||
#include "box-algorithms/filters/ovpCBoxAlgorithmXDAWNSpatialFilterTrainer.h"
|
||||
|
||||
#include "box-algorithms/basic/ovpCBoxAlgorithmIFFTbox.h"
|
||||
|
||||
#include "algorithms/basic/ovpCAlgorithmARBurgMethod.h"
|
||||
#include "box-algorithms/basic/ovpCBoxAlgorithmARCoefficients.h"
|
||||
|
||||
#include "algorithms/basic/ovpCMatrixVariance.h"
|
||||
#include "box-algorithms/basic/ovpCBoxAlgorithmEpochVariance.h"
|
||||
#include "box-algorithms/basic/ovpCBoxAlgorithmERSPAverage.h"
|
||||
|
||||
//#include "box-algorithms/basic/ovpCBoxAlgorithmNull.h"
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmEOG_Denoising.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmEOG_Denoising_Calibration.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmDiscreteWaveletTransform.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmInverse_DWT.h"
|
||||
|
||||
#include "box-algorithms/CBoxAlgorithmMatrix3dTo2d.hpp"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
//*********** Boxes ***********
|
||||
OVP_Declare_New(CBoxAlgorithmDifferentialIntegralDesc)
|
||||
OVP_Declare_New(CBoxAlgorithmMatrixTransposeDesc)
|
||||
//OVP_Declare_New(CBoxAlgorithmNullDesc)
|
||||
OVP_Declare_New(CBoxAlgorithmERSPAverageDesc)
|
||||
OVP_Declare_New(CBoxAlgorithmQuadraticFormDesc)
|
||||
OVP_Declare_New(CEpochVarianceDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmMatrix3dTo2dDesc)
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyEIGEN
|
||||
OVP_Declare_New(CBoxAlgorithmARCoefficientsDesc);
|
||||
OVP_Declare_New(CAlgorithmARBurgMethodDesc);
|
||||
OVP_Declare_New(CAlgorithmHilbertTransformDesc)
|
||||
OVP_Declare_New(CBoxAlgorithmConnectivityMeasureDesc)
|
||||
OVP_Declare_New(CBoxAlgorithmEOG_DenoisingDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmEOG_Denoising_CalibrationDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmHilbertDesc)
|
||||
#endif
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
OVP_Declare_New(CBoxAlgorithmXDAWNSpatialFilterTrainerDesc)
|
||||
OVP_Declare_New(CMatrixVarianceDesc)
|
||||
OVP_Declare_New(CBoxAlgorithmIFFTboxDesc)
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyFFTW3)
|
||||
OVP_Declare_New(CBoxAlgorithmDiscreteWaveletTransformDesc)
|
||||
OVP_Declare_New(CBoxAlgorithmInverse_DWTDesc)
|
||||
|
||||
//*********** Enumeration ***********
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_WaveletType, "Wavelet type");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "haar", size_t(EWaveletType::Haar));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db1", size_t(EWaveletType::Db1));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db2", size_t(EWaveletType::Db2));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db3", size_t(EWaveletType::Db3));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db4", size_t(EWaveletType::Db4));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db5", size_t(EWaveletType::Db5));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db6", size_t(EWaveletType::Db6));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db7", size_t(EWaveletType::Db7));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db8", size_t(EWaveletType::Db8));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db9", size_t(EWaveletType::Db9));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db10", size_t(EWaveletType::Db10));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db11", size_t(EWaveletType::Db11));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db12", size_t(EWaveletType::Db12));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db13", size_t(EWaveletType::Db13));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db14", size_t(EWaveletType::Db14));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "db15", size_t(EWaveletType::Db15));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior1.1", size_t(EWaveletType::Bior11));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior1.3", size_t(EWaveletType::Bior13));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior1.5", size_t(EWaveletType::Bior15));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior2.2", size_t(EWaveletType::Bior22));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior2.4", size_t(EWaveletType::Bior24));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior2.6", size_t(EWaveletType::Bior26));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior2.8", size_t(EWaveletType::Bior28));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior3.1", size_t(EWaveletType::Bior31));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior3.3", size_t(EWaveletType::Bior33));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior3.5", size_t(EWaveletType::Bior35));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior3.7", size_t(EWaveletType::Bior37));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior3.9", size_t(EWaveletType::Bior39));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior4.4", size_t(EWaveletType::Bior44));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior5.5", size_t(EWaveletType::Bior55));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "bior6.8", size_t(EWaveletType::Bior68));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "coif1", size_t(EWaveletType::Coif1));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "coif2", size_t(EWaveletType::Coif2));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "coif3", size_t(EWaveletType::Coif3));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "coif4", size_t(EWaveletType::Coif4));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "coif5", size_t(EWaveletType::Coif5));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym1", size_t(EWaveletType::Sym1));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym2", size_t(EWaveletType::Sym2));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym3", size_t(EWaveletType::Sym3));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym4", size_t(EWaveletType::Sym4));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym5", size_t(EWaveletType::Sym5));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym6", size_t(EWaveletType::Sym6));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym7", size_t(EWaveletType::Sym7));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym8", size_t(EWaveletType::Sym8));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym9", size_t(EWaveletType::Sym9));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletType, "sym10", size_t(EWaveletType::Sym10));
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_WaveletLevel, "Wavelet decomposition levels");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletLevel, "1", size_t(EWaveletLevel::L1));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletLevel, "2", size_t(EWaveletLevel::L2));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletLevel, "3", size_t(EWaveletLevel::L3));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletLevel, "4", size_t(EWaveletLevel::L4));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_WaveletLevel, "5", size_t(EWaveletLevel::L5));
|
||||
#endif
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_BoxAlgorithmFlag, OV_AttributeId_Box_FlagIsUnstable.toString(),
|
||||
OV_AttributeId_Box_FlagIsUnstable.id());
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_EpochAverageMethod, "Epoch Average method");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_EpochAverageMethod, "Moving epoch average", size_t(EEpochAverageMethod::Moving));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_EpochAverageMethod, "Moving epoch average (Immediate)",
|
||||
size_t(EEpochAverageMethod::MovingImmediate));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_EpochAverageMethod, "Epoch block average", size_t(EEpochAverageMethod::Block));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_EpochAverageMethod, "Cumulative average", size_t(EEpochAverageMethod::Cumulative));
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_CropMethod, "Crop method");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_CropMethod, "Min", size_t(ECropMethod::Min));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_CropMethod, "Max", size_t(ECropMethod::Max));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_CropMethod, "Min/Max", size_t(ECropMethod::MinMax));
|
||||
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_SelectionMethod, "Selection method");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SelectionMethod, "Select", size_t(ESelectionMethod::Select));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SelectionMethod, "Reject", size_t(ESelectionMethod::Reject));
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_MatchMethod, "Match method");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_MatchMethod, "Name", size_t(EMatchMethod::Name));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_MatchMethod, "Index", size_t(EMatchMethod::Index));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_MatchMethod, "Smart", size_t(EMatchMethod::Smart));
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_DifferentialIntegralOperation, "Differential/Integral select");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_DifferentialIntegralOperation, "Differential",
|
||||
size_t(EDifferentialIntegralOperation::Differential));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_DifferentialIntegralOperation, "Integral", size_t(EDifferentialIntegralOperation::Integral));
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEIGEN)
|
||||
context.getTypeManager().registerEnumerationType(OV_TypeId_ConnectivityMeasure_WindowMethod, "Welch Window method");
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_ConnectivityMeasure_WindowMethod, "Hamming", size_t(EConnectWindowMethod::Hamming));
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_ConnectivityMeasure_WindowMethod, "Hann", size_t(EConnectWindowMethod::Hann));
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_ConnectivityMeasure_WindowMethod, "Welch", size_t(EConnectWindowMethod::Welch));
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_Connectivity_Metric, "Metric method");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Connectivity_Metric, toString(EConnectMetric::Coherence).c_str(), size_t(EConnectMetric::Coherence));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Connectivity_Metric, toString(EConnectMetric::MagnitudeSquaredCoherence).c_str(), size_t(EConnectMetric::MagnitudeSquaredCoherence));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Connectivity_Metric, toString(EConnectMetric::ImaginaryCoherence).c_str(), size_t(EConnectMetric::ImaginaryCoherence));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Connectivity_Metric, toString(EConnectMetric::AbsImaginaryCoherence).c_str(), size_t(EConnectMetric::AbsImaginaryCoherence));
|
||||
#endif
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Reference in New Issue
Block a user