From 3e42a9074c29ef77ed245b70e007e147ebbd1487 Mon Sep 17 00:00:00 2001 From: Arthur Desbois <arthur.desbois@gmail.com> Date: Mon, 19 Apr 2021 15:33:28 +0200 Subject: [PATCH] Added Graph Laplacian box Graph Laplacian + Denoising computation from Connectivity Matrices --- .../connectivity/graphLaplacian.cpp | 253 ++++++++++++++++++ .../algorithms/connectivity/graphLaplacian.h | 77 ++++++ .../CBoxAlgorithmGraphLaplacian.cpp | 110 ++++++++ .../CBoxAlgorithmGraphLaplacian.h | 109 ++++++++ .../signal-processing/src/ovp_defines.h | 5 + .../signal-processing/src/ovp_main.cpp | 16 +- 6 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.cpp create mode 100644 plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.h create mode 100644 plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.cpp create mode 100644 plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.h diff --git a/plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.cpp b/plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.cpp new file mode 100644 index 000000000..8926f7c49 --- /dev/null +++ b/plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.cpp @@ -0,0 +1,253 @@ +#include "graphLaplacian.h" +#include <openvibe/ov_all.h> +#include <cmath> +#include <Eigen/Dense> +#include <Eigen/Eigenvalues> +#include <iostream> + +bool GraphLaplacian::initialize(EGraphLaplacianDenoisingType denoisingType, size_t denoisingSize, bool denoising, bool debugLogs, EGraphLaplacianOutputType outputType) +{ + m_denoisingSize = denoisingSize; + m_denoising = denoising; + m_debugLogs = debugLogs; + + m_outputType = outputType; + m_denoisingType = denoisingType; + + return true; +} + + +bool GraphLaplacian::process(const OpenViBE::IMatrix& in, OpenViBE::IMatrix& out) +{ + // in matrix : A (freq x N x N) + // GraphLaplacian computation (for each frequency) : Lf = Deg(Af) - Af + // with Deg(Af) = diag(sum_rows(Af)) + + // Conversion from OpenViBE's IMatrix of 3 dimensions (fftsize x nchan x nchan) + // to std:vector<Eigen::Matrix> (vect of fftsize x (nChan x nChan) ) + std::vector< Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > laplacianMatrices(in.getDimensionSize(0), Eigen::MatrixXd(in.getDimensionSize(1),in.getDimensionSize(2))); + std::vector< Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > adjacencyMatrices(in.getDimensionSize(0), Eigen::MatrixXd(in.getDimensionSize(1),in.getDimensionSize(2))); + + size_t fftSize = in.getDimensionSize(0); + size_t nbChan = in.getDimensionSize(1); + + const double* inbuffer = in.getBuffer(); + for (size_t fftIdx = 0; fftIdx < fftSize; ++fftIdx) { + for (size_t chan0 = 0; chan0 < nbChan; ++chan0) { + for (size_t chan1 = 0; chan1 < nbChan; ++chan1) { + // inverted sign to put (-A) in (L = D - A) + laplacianMatrices[fftIdx](chan0, chan1) = -inbuffer[fftIdx*nbChan*nbChan + chan0*nbChan + chan1]; + } + } + } + + if(m_debugLogs) { + std::cout << "GraphLaplacian : process() this matrix (freq0) : " << std::endl; + for (size_t chan0 = 0; chan0 < nbChan; ++chan0) { + std::cout << -laplacianMatrices[0].row(chan0) << std::endl; + } + std::cout << std::endl; + } + + // Degree matrix and laplacian computation + for(size_t fftIdx = 0; fftIdx < fftSize; ++fftIdx) { + for (size_t rowIdx = 0; rowIdx < nbChan; ++rowIdx) { + double deg = 0.0; + // sum of the row values + for(size_t colIdx = 0; colIdx < nbChan; ++colIdx) { + deg += laplacianMatrices[fftIdx](rowIdx, colIdx); + } + // degree value in the diagonal (inverted sign since we already inverted before) + laplacianMatrices[fftIdx](rowIdx, rowIdx) = -deg; + } + } + + if(m_debugLogs) { + std::cout << "GraphLaplacian matrix for freq0 (pre denoising) :" << std::endl; + for (size_t chan0 = 0; chan0 < nbChan; ++chan0) { + std::cout << laplacianMatrices[0].row(chan0) << std::endl; + } + std::cout << std::endl; + } + + // At this point, the GraphLaplacian matrix L=D-A is fully computed. + // Now, depending on the parameters, we can perform "denoising" by manipulating the eigenvectors & eigenvalues + + if(m_denoising) { + // /!\ we assume the connectivity/adjacency matrix is symmetric and self adjoint. + // Its eigenvalues/vectors are real valued. + + Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esright(laplacianMatrices[0].rows()); + Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esleft(laplacianMatrices[0].cols()); + + for(size_t fftIdx = 0; fftIdx < fftSize; ++fftIdx) { + + // compute the eigenValues and RIGHT eigenVectors of GraphLaplacian Matrix for freq fftIdx + // right eigenvectors are V as A.V = V.D (with D = diag(eigenvalues)) (=> A = V.D.V(-1)) + esright.compute(laplacianMatrices[fftIdx]); + + // compute the eigenValues and LEFT eigenVectors of GraphLaplacian Matrix for freq fftIdx + // left eigenvectors are W as W'.A = D.W' (with D = diag(eigenvalues)) (=> A = W'(-1).D.W') + esleft.compute(laplacianMatrices[fftIdx].transpose()); + + Eigen::MatrixXd D = esright.eigenvalues().asDiagonal(); + Eigen::MatrixXd V = esright.eigenvectors(); + Eigen::MatrixXd W = esleft.eigenvectors(); + + size_t nbEigen = V.rows(); + + // When using SelfAdjointEigenSolver, the eigenvalues (and associated vectors) are + // already sorted from smallest to largest value. + + if(m_debugLogs && fftIdx == 0) { + + std::cout << "GraphLaplacian : eigenvalues of the GraphLaplacian (freq0) : " << std::endl; + for (size_t idx = 0; idx < nbEigen; ++idx) { + std::cout << D.row(idx) << std::endl; + } + std::cout << std::endl; + + std::cout << "GraphLaplacian : eigenvectors of the GraphLaplacian (freq0) : " << std::endl; + for (size_t idx = 0; idx < nbEigen; ++idx) { + std::cout << V.row(idx) << std::endl; + } + std::cout << std::endl; + } + + // "Filter" the eigenvectors & values + Eigen::MatrixXd Dfilt; + Eigen::MatrixXd Vfilt; + Eigen::MatrixXd Wfilt; + + switch(m_denoisingType) { + + case EGraphLaplacianDenoisingType::LowPass : + // We keep the m_L first columns of V, W, and L first eigenvalues in D + {size_t L = std::min(m_denoisingSize, nbEigen); + Dfilt = D.block(0, 0, L, L); + Vfilt = V.block(0, 0, V.rows(), L); + Wfilt = W.block(0, 0, W.rows(), L);} + break; + + case EGraphLaplacianDenoisingType::MidCut : + // We CUT the m_M "mid" columns of V, W, and m_M "mid" eigenvalues in D + if(m_denoisingSize < nbEigen) { + size_t nbCols = nbEigen - m_denoisingSize; + size_t nbColsLeft = (size_t)(nbCols/2); + size_t nbColsRight = nbCols - nbColsRight; + Dfilt = Eigen::MatrixXd::Zero(nbCols,nbCols); + Vfilt.resize(V.rows(), nbCols); + Wfilt.resize(W.rows(), nbCols); + size_t idxColNew = 0; + for(size_t idxCol = 0; idxCol < nbColsLeft; idxCol++) { + Dfilt(idxColNew,idxColNew) = D(idxCol,idxCol); + Vfilt.col(idxColNew) = V.col(idxCol); + Wfilt.col(idxColNew++) = W.col(idxCol); + } + + for(size_t idxCol = V.cols()-nbColsRight; idxCol < V.cols(); idxCol++) { + Dfilt(idxColNew,idxColNew) = D(idxCol,idxCol); + Vfilt.col(idxColNew) = V.col(idxCol); + Wfilt.col(idxColNew++) = W.col(idxCol); + } + } else { + // Everything is cut...! + // TODO : error message ? + Dfilt = Eigen::MatrixXd(0,0); + Vfilt = Eigen::MatrixXd(0,0); + Wfilt = Eigen::MatrixXd(0,0); + } + break; + + case EGraphLaplacianDenoisingType::HighPass : + // We keep the m_H last columns of V, W, and m_H last eigenvalues in D + {size_t H = std::min(m_denoisingSize, nbEigen); + size_t idxStart = nbEigen-H; + Dfilt = D.block(idxStart, idxStart, H, H); + Vfilt = V.block(0, idxStart, V.rows(), H); + Wfilt = W.block(0, idxStart, W.rows(), H);} + break; + + default: + // Do something if parameter values (L/M/H) conflict with eigen sizes? + break; + } + + // Reconstruct the laplacian matrix L = W.D.V' + laplacianMatrices[fftIdx] = Wfilt * Dfilt * Vfilt.transpose(); + + // Reconstruct the adjacency matrix A = -(L-D) + if(m_outputType == EGraphLaplacianOutputType::AdjacencyMatrix) { + for(size_t idxRow = 0; idxRow < laplacianMatrices[fftIdx].rows(); idxRow++) { + for(size_t idxCol = 0; idxCol < laplacianMatrices[fftIdx].cols(); idxCol++) { + adjacencyMatrices[fftIdx](idxRow,idxCol) = - (laplacianMatrices[fftIdx](idxRow,idxCol)); + } + } + for(size_t idxRow = 0; idxRow < adjacencyMatrices[fftIdx].rows(); idxRow++) { + adjacencyMatrices[fftIdx](idxRow,idxRow) = 0.0; + } + } + + } + + if(m_debugLogs) { + std::cout << "GraphLaplacian matrix for freq0 (reconstructed post denoising) :" << std::endl; + for (size_t idx = 0; idx < laplacianMatrices[0].rows(); ++idx) { + std::cout << laplacianMatrices[0].row(idx) << std::endl; + } + std::cout << std::endl; + } + + } + + // Output : GraphLaplacian or Adjacency + // Conversion from eigen format to IMatrix format + // freq x N x N x 2 (real/imag) + + if (out.getDimensionCount() != 3 + || out.getDimensionSize(0) != fftSize + || out.getDimensionSize(1) != nbChan + || out.getDimensionSize(2) != nbChan) + { + out.setDimensionCount(3); + out.setDimensionSize(0, fftSize); + out.setDimensionSize(1, nbChan); + out.setDimensionSize(2, nbChan); + } + + double* outbuffer = out.getBuffer(); + size_t outidx = 0; + + if(m_outputType == EGraphLaplacianOutputType::GraphLaplacianMatrix) { + + for (size_t fftIdx = 0; fftIdx < fftSize; ++fftIdx) { + for (size_t chan0 = 0; chan0 < nbChan; ++chan0) { + for (size_t chan1 = 0; chan1 < nbChan; ++chan1) { + outbuffer[outidx++] = laplacianMatrices[fftIdx](chan0, chan1); + } + } + } + + } else { + if(m_denoising) { + for (size_t fftIdx = 0; fftIdx < fftSize; ++fftIdx) { + for (size_t chan0 = 0; chan0 < nbChan; ++chan0) { + for (size_t chan1 = 0; chan1 < nbChan; ++chan1) { + outbuffer[outidx++] = adjacencyMatrices[fftIdx](chan0, chan1); + } + } + } + } else { + // output = input but you computed lots of things that will not be used. + // why would you do this ? + // TODO : warning ? + for (size_t inIdx = 0; inIdx < in.getBufferElementCount(); ++inIdx) { + outbuffer[outidx++] = inbuffer[inIdx]; + } + } + + } + + return true; +} diff --git a/plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.h b/plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.h new file mode 100644 index 000000000..454f7d984 --- /dev/null +++ b/plugins/processing/signal-processing/src/algorithms/connectivity/graphLaplacian.h @@ -0,0 +1,77 @@ +///------------------------------------------------------------------------------------------------- +/// +/// \file graphLaplacian.hpp +/// \brief Graph Laplacian & denoising for BCI connectivity. +/// \author Arthur Desbois (Inria). +/// \version 1.0 +/// \date 28 Jan 2021 +/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>. +/// \remarks +///------------------------------------------------------------------------------------------------- + +#pragma once +#include <string> +#include <openvibe/ov_all.h> +#include <Eigen/Dense> + +enum class EGraphLaplacianOutputType +{ + GraphLaplacianMatrix, + AdjacencyMatrix +}; + +/// <summary> Convert GraphLaplacian output to string. </summary> +/// <param name="outputType"> Output type. </param> +/// <returns> std::string </returns> +inline std::string toString(const EGraphLaplacianOutputType type) +{ + switch (type) + { + case EGraphLaplacianOutputType::GraphLaplacianMatrix: return "GraphLaplacian Matrix"; + case EGraphLaplacianOutputType::AdjacencyMatrix: return "Adjacency Matrix"; + } +} + +enum class EGraphLaplacianDenoisingType +{ + LowPass, + MidCut, + HighPass +}; + +/// <summary> Convert GraphLaplacian denoising type to string. </summary> +/// <param name="outputType"> Denoising type. </param> +/// <returns> std::string </returns> +inline std::string toString(const EGraphLaplacianDenoisingType type) +{ + switch (type) + { + case EGraphLaplacianDenoisingType::LowPass: return "Low Pass (lowest eigenvalues)"; + case EGraphLaplacianDenoisingType::MidCut: return "Mid cut (lowest & highest eigenvalues)"; + case EGraphLaplacianDenoisingType::HighPass: return "High Pass (highest eigenvalues)"; + } +} + +class GraphLaplacian +{ +public: + GraphLaplacian() {}; + ~GraphLaplacian() {}; + + bool initialize(EGraphLaplacianDenoisingType denoisingType, size_t denoisingSize, bool denoising, bool debugLogs, EGraphLaplacianOutputType outputType); + + bool process(const OpenViBE::IMatrix& in, OpenViBE::IMatrix& out); + +private: + + size_t m_denoisingSize = 0; + bool m_denoising = false; + + EGraphLaplacianOutputType m_outputType = EGraphLaplacianOutputType::GraphLaplacianMatrix; + EGraphLaplacianDenoisingType m_denoisingType = EGraphLaplacianDenoisingType::LowPass; + + bool m_debugLogs = false; + +}; + + diff --git a/plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.cpp b/plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.cpp new file mode 100644 index 000000000..1f5faa522 --- /dev/null +++ b/plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.cpp @@ -0,0 +1,110 @@ +#include "CBoxAlgorithmGraphLaplacian.h" + +using namespace OpenViBE; +using namespace /*OpenViBE::*/Kernel; +using namespace /*OpenViBE::*/Plugins; +using namespace /*OpenViBE::Plugins::*/SignalProcessing; + +bool CBoxAlgorithmGraphLaplacian::initialize() +{ + m_i0MatrixCodec.initialize(*this, 0); + m_o0MatrixCodec.initialize(*this, 0); + + m_iMatrix = m_i0MatrixCodec.getOutputMatrix(); + m_oMatrix = m_o0MatrixCodec.getInputMatrix(); + + // Settings + m_outputType = EGraphLaplacianOutputType(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0))); + m_denoisingType = EGraphLaplacianDenoisingType(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1))); + m_denoisingSize = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2); + m_denoising = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3); + m_debugLogs = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 4); + + laplacian.initialize(m_denoisingType, m_denoisingSize, m_denoising, m_debugLogs, m_outputType); + + if(m_denoising) { + if(m_denoisingSize <= 0) OV_ERROR_KRF("L, M, H must be positive", Kernel::ErrorType::BadInput); + if( m_denoisingType == EGraphLaplacianDenoisingType::MidCut ) OV_ERROR_KRF("Mid-cut not yet available", Kernel::ErrorType::BadInput); + } + + return true; +} +/*******************************************************************************/ + +bool CBoxAlgorithmGraphLaplacian::uninitialize() +{ + m_i0MatrixCodec.uninitialize(); + m_o0MatrixCodec.uninitialize(); + return true; +} + +bool CBoxAlgorithmGraphLaplacian::processInput(const size_t ) +{ + getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess(); + return true; +} + +/*******************************************************************************/ + + +bool CBoxAlgorithmGraphLaplacian::process() +{ + const IBox& staticBoxContext=this->getStaticBoxContext(); + IBoxIO& boxContext = this->getDynamicBoxContext(); + + for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i) + { + m_i0MatrixCodec.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_i0MatrixCodec.isHeaderReceived()) {// Header received. This happens only once when pressing "play". + + IMatrix* matrix = m_i0MatrixCodec.getOutputMatrix(); // the StreamedMatrix of samples. + + // OUTPUT MATRIX INIT + m_oMatrix->setDimensionCount(matrix->getDimensionCount()); + m_oMatrix->setDimensionSize(0, matrix->getDimensionSize(0)); // frequencies + m_oMatrix->setDimensionSize(1, matrix->getDimensionSize(1)); // channels + m_oMatrix->setDimensionSize(2, matrix->getDimensionSize(2)); // channels + Toolkit::Matrix::clearContent(*m_oMatrix); + + m_o0MatrixCodec.encodeHeader(); // Pass the header to the next boxes + + if(m_debugLogs) { + this->getLogManager() << LogLevel_Info << "Header buffer processed\n"; + } + + + } + else if(m_i0MatrixCodec.isBufferReceived()) { + + IMatrix* matrix = m_i0MatrixCodec.getOutputMatrix(); // the StreamedMatrix of samples. + + if(m_debugLogs) { + this->getLogManager() << LogLevel_Info << "Matrix received : " << matrix->getDimensionSize(0) << "x" << matrix->getDimensionSize(1) << "x" << matrix->getDimensionSize(2) << "\n"; + } + + OV_ERROR_UNLESS_KRF(laplacian.process(*matrix, *m_oMatrix), "GraphLaplacian error", ErrorType::BadProcessing); + + if(m_debugLogs) { + this->getLogManager() << LogLevel_Info << "GraphLaplacian computed : " << m_oMatrix->getDimensionSize(0) << "x" << m_oMatrix->getDimensionSize(1) << "x" << m_oMatrix->getDimensionSize(2) << "\n"; + } + + m_o0MatrixCodec.encodeBuffer(); + + + } + else if(m_i0MatrixCodec.isEndReceived()) { + m_o0MatrixCodec.encodeEnd(); + } + + // send the output chunk. The dates are the same as the input chunk + boxContext.markOutputAsReadyToSend(0, tStart, tEnd); + + } + + return true; +} + diff --git a/plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.h b/plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.h new file mode 100644 index 000000000..4de579960 --- /dev/null +++ b/plugins/processing/signal-processing/src/box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.h @@ -0,0 +1,109 @@ +///------------------------------------------------------------------------------------------------- +/// +/// \file CBoxAlgorithmGraphLaplacian.h +/// \brief Classes of the Box GraphLaplacian +/// \author Arthur DESBOIS (INRIA). +/// \version 0.0.1. +/// \date Thu Jan 28 16:18:49 2021. +/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>. +/// +///------------------------------------------------------------------------------------------------- +#pragma once + +#include "ovp_defines.h" + +#include <openvibe/ov_all.h> +#include <toolkit/ovtk_all.h> +#include <Eigen/Dense> +#include "graphLaplacian.h" + + +#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF) + +namespace OpenViBE +{ + namespace Plugins + { + namespace SignalProcessing + { + /// <summary> The class CBoxAlgorithmGraphLaplacian describes the box. </summary> + class CBoxAlgorithmGraphLaplacian 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_GraphLaplacian) + + protected: + + // Algo class instance + GraphLaplacian laplacian; + + // Codecs + Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmGraphLaplacian> m_i0MatrixCodec; + Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmGraphLaplacian> m_o0MatrixCodec; + + // Matrices + IMatrix* m_iMatrix = nullptr; // Input Matrix pointer + IMatrix* m_oMatrix = nullptr; // Output Matrix pointer + + // Settings + EGraphLaplacianOutputType m_outputType = EGraphLaplacianOutputType::GraphLaplacianMatrix; + EGraphLaplacianDenoisingType m_denoisingType = EGraphLaplacianDenoisingType::LowPass; + size_t m_denoisingSize = 0; + + bool m_denoising = false; // enabling/disabling denoising TODO : Keep ? + + bool m_debugLogs = false; + + Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; + }; + + /// <summary> Descriptor of the box GraphLaplacian. </summary> + class CBoxAlgorithmGraphLaplacianDesc final : virtual public IBoxAlgorithmDesc + { + public: + + void release() override { } + + CString getName() const override { return CString("GraphLaplacian"); } + CString getAuthorName() const override { return CString("Arthur DESBOIS"); } + CString getAuthorCompanyName() const override { return CString("INRIA"); } + CString getShortDescription() const override { return CString("GraphLaplacian / denoising for BCI connectivity"); } + CString getDetailedDescription() const override { return CString("Computes laplacian & denoising process"); } + CString getCategory() const override { return CString("Signal processing/Connectivity"); } + CString getVersion() const override { return CString("0.0.1"); } + CString getStockItemName() const override { return CString("gtk-zoom-out"); } + + CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_GraphLaplacian; } + IPluginObject* create() override { return new CBoxAlgorithmGraphLaplacian; } + + bool getBoxPrototype(Kernel::IBoxProto& prototype) const override + { + prototype.addInput("Input matrix",OV_TypeId_StreamedMatrix); + prototype.addOutput("Output matrix",OV_TypeId_StreamedMatrix); + + prototype.addSetting("Output Type", OV_TypeId_GraphLaplacian_OutputType, toString(EGraphLaplacianOutputType::GraphLaplacianMatrix).c_str()); + prototype.addSetting("Denoising Type", OV_TypeId_GraphLaplacian_DenoisingType, toString(EGraphLaplacianDenoisingType::LowPass).c_str()); + prototype.addSetting("Denoising Size",OV_TypeId_Integer, "0"); + prototype.addSetting("Denoising activated",OV_TypeId_Boolean,"false"); + + prototype.addSetting("DEBUG LOGS",OV_TypeId_Boolean,"false"); + + prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable); + + return true; + } + _IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_GraphLaplacianDesc) + }; + } // namespace SignalProcessing + } // namespace Plugins +} // namespace OpenViBE diff --git a/plugins/processing/signal-processing/src/ovp_defines.h b/plugins/processing/signal-processing/src/ovp_defines.h index e2e45757f..4d0281196 100755 --- a/plugins/processing/signal-processing/src/ovp_defines.h +++ b/plugins/processing/signal-processing/src/ovp_defines.h @@ -58,6 +58,8 @@ #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) +#define OVP_ClassId_BoxAlgorithm_GraphLaplacian OpenViBE::CIdentifier(0x35202ffc, 0xa6a312cc) +#define OVP_ClassId_BoxAlgorithm_GraphLaplacianDesc OpenViBE::CIdentifier(0x58b723d7, 0xfb32fdf7) // Type definitions //--------------------------------------------------------------------------------------------------- @@ -155,4 +157,7 @@ enum class EWaveletLevel { L1, L2, L3, L4, L5 }; #define OVP_TypeId_Connectivity_Metric OpenViBE::CIdentifier(0x9188339d, 0x5da83a84) #define OV_TypeId_ConnectivityMeasure_WindowMethod OpenViBE::CIdentifier(0x8815bfa7, 0x557b102f) +#define OV_TypeId_GraphLaplacian_OutputType OpenViBE::CIdentifier(0x8f5633a5, 0x6cc7f6e9) +#define OV_TypeId_GraphLaplacian_DenoisingType OpenViBE::CIdentifier(0xbe346f0b, 0xe0ee4117) + #define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF) diff --git a/plugins/processing/signal-processing/src/ovp_main.cpp b/plugins/processing/signal-processing/src/ovp_main.cpp index 12d8bd202..8ad813570 100755 --- a/plugins/processing/signal-processing/src/ovp_main.cpp +++ b/plugins/processing/signal-processing/src/ovp_main.cpp @@ -28,6 +28,8 @@ #include "box-algorithms/CBoxAlgorithmMatrix3dTo2d.hpp" +#include "box-algorithms/connectivity/CBoxAlgorithmGraphLaplacian.h" + namespace OpenViBE { namespace Plugins { namespace SignalProcessing { @@ -39,8 +41,9 @@ OVP_Declare_Begin() //OVP_Declare_New(CBoxAlgorithmNullDesc) OVP_Declare_New(CBoxAlgorithmERSPAverageDesc) OVP_Declare_New(CBoxAlgorithmQuadraticFormDesc) - OVP_Declare_New(CEpochVarianceDesc); + OVP_Declare_New(CEpochVarianceDesc) OVP_Declare_New(CBoxAlgorithmMatrix3dTo2dDesc) + OVP_Declare_New(CBoxAlgorithmGraphLaplacianDesc) #if defined TARGET_HAS_ThirdPartyEIGEN OVP_Declare_New(CBoxAlgorithmARCoefficientsDesc); @@ -159,6 +162,17 @@ OVP_Declare_Begin() 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)); + + context.getTypeManager().registerEnumerationType(OV_TypeId_GraphLaplacian_OutputType, "Output Type"); + context.getTypeManager().registerEnumerationEntry(OV_TypeId_GraphLaplacian_OutputType, toString(EGraphLaplacianOutputType::GraphLaplacianMatrix).c_str(), size_t(EGraphLaplacianOutputType::GraphLaplacianMatrix)); + context.getTypeManager().registerEnumerationEntry(OV_TypeId_GraphLaplacian_OutputType, toString(EGraphLaplacianOutputType::AdjacencyMatrix).c_str(), size_t(EGraphLaplacianOutputType::AdjacencyMatrix)); + + context.getTypeManager().registerEnumerationType(OV_TypeId_GraphLaplacian_DenoisingType, "Denoising Type"); + context.getTypeManager().registerEnumerationEntry(OV_TypeId_GraphLaplacian_DenoisingType, toString(EGraphLaplacianDenoisingType::LowPass).c_str(), size_t(EGraphLaplacianDenoisingType::LowPass)); + context.getTypeManager().registerEnumerationEntry(OV_TypeId_GraphLaplacian_DenoisingType, toString(EGraphLaplacianDenoisingType::MidCut).c_str(), size_t(EGraphLaplacianDenoisingType::MidCut)); + context.getTypeManager().registerEnumerationEntry(OV_TypeId_GraphLaplacian_DenoisingType, toString(EGraphLaplacianDenoisingType::HighPass).c_str(), size_t(EGraphLaplacianDenoisingType::HighPass)); + + #endif OVP_Declare_End() -- GitLab