This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,174 @@
#include "CBoxAlgorithmFeaturesSelection.hpp"
#include <fstream>
namespace OpenViBE {
namespace Plugins {
namespace FeaturesSelection {
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelection::initialize()
{
// Stimulations
m_stimDecoder.initialize(*this, 0);
m_iStim = m_stimDecoder.getOutputStimulationSet();
m_stimEncoder.initialize(*this, 0);
m_oStim = m_stimEncoder.getInputStimulationSet();
// Classes
//m_featureDecoders.initialize(*this, 1);
const Kernel::IBox& boxCtx = this->getStaticBoxContext();
m_nbClass = boxCtx.getInputCount() - 1;
m_featuresDecoders.resize(m_nbClass);
m_iFeatures.resize(m_nbClass);
for (size_t k = 0; k < m_nbClass; ++k)
{
m_featuresDecoders[k].initialize(*this, k + 1);
m_iFeatures[k] = m_featuresDecoders[k].getOutputMatrix();
}
auto& ctx = *this->getBoxAlgorithmContext();
size_t idx = 0;
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(ctx, idx++)));
m_stimName = FSettingValueAutoCast(ctx, idx++);
m_filename = CString(FSettingValueAutoCast(ctx, idx++)).toASCIIString();
m_method = EFeatureSelection(uint64_t(FSettingValueAutoCast(ctx, idx++)));
m_nFinalFeatures = uint64_t(FSettingValueAutoCast(ctx, idx++));
m_doDiscretization = FSettingValueAutoCast(ctx, idx++);
m_threshold = FSettingValueAutoCast(ctx, idx++);
m_mRMRMethod = EMRMRMethod(uint64_t(FSettingValueAutoCast(ctx, idx)));
m_selector.reset();
if (m_logLevel != Kernel::LogLevel_None)
{
getLogManager() << m_logLevel << "Trainer Initialized : \n\t" << m_nbClass << " Classes, Features Selection method is "
<< toString(m_method) << " with " << m_nFinalFeatures << " Features to select,";
if (m_doDiscretization) { getLogManager() << " with discretization (threshold = " << m_threshold << ")."; }
else { getLogManager() << " without discretization."; }
getLogManager() << "\n\tMethod used for mRMR is " << toString(m_mRMRMethod) << ".\n\t";
if (m_filename.empty()) { getLogManager() << "Config not saved.\n"; }
else { getLogManager() << "Config saved in file\'" << m_filename << "\'.\n"; }
}
return true;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelection::uninitialize()
{
m_stimDecoder.uninitialize();
m_stimEncoder.uninitialize();
for (auto& codec : m_featuresDecoders) { codec.uninitialize(); }
m_featuresDecoders.clear();
m_iFeatures.clear();
if (m_logLevel != Kernel::LogLevel_None) { getLogManager() << m_logLevel << "Trainer Uninitialized, selector infos : \n" << m_selector.print(); }
m_selector.reset();
return true;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelection::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelection::process()
{
if (m_isTrain) { return true; } // If train is made don't do process
Kernel::IBoxIO& boxCtx = this->getDynamicBoxContext();
//***** Stimulations (input 0) *****
for (size_t i = 0; i < boxCtx.getInputChunkCount(0); ++i)
{
m_stimDecoder.decode(i); // Decode the chunk
const uint64_t start = boxCtx.getInputChunkStartTime(0, i), end = boxCtx.getInputChunkEndTime(0, i); // Time Code
if (m_stimDecoder.isHeaderReceived()) // Header received
{
m_stimEncoder.encodeHeader();
boxCtx.markOutputAsReadyToSend(0, 0, 0);
}
if (m_stimDecoder.isBufferReceived()) // Buffer received
{
for (size_t j = 0; j < m_iStim->getStimulationCount(); ++j)
{
if (m_iStim->getStimulationIdentifier(j) == m_stimName)
{
// Process
getLogManager() << m_logLevel << "Train Flag Received, selector infos : \n" << m_selector.print();
m_result = m_selector.process((m_doDiscretization ? m_threshold : std::numeric_limits<double>::infinity()), m_nFinalFeatures, m_mRMRMethod);
getLogManager() << m_logLevel << "Features selected :";
for (const auto& r : m_result) { getLogManager() << " " << r; }
getLogManager() << "\n";
// Save File
if (!m_filename.empty()) { OV_ERROR_UNLESS_KRF(writeConfig(), "Error During File writing.", Kernel::ErrorType::BadFileWrite); }
// Send Stimulation
const uint64_t stim = this->getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_Stimulation, "OVTK_StimulationId_TrainCompleted");
m_oStim->appendStimulation(stim, m_iStim->getStimulationDate(j), 0);
m_isTrain = true;
}
}
m_stimEncoder.encodeBuffer();
boxCtx.markOutputAsReadyToSend(0, start, end);
}
if (m_stimDecoder.isEndReceived()) // End received
{
m_stimEncoder.encodeEnd();
boxCtx.markOutputAsReadyToSend(0, start, end);
}
}
//***** Features (Input 1 to N) *****
for (size_t k = 0; k < m_nbClass; ++k)
{
for (size_t i = 0; i < boxCtx.getInputChunkCount(k + 1); ++i)
{
m_featuresDecoders[k].decode(i); // Decode the chunk
OV_ERROR_UNLESS_KRF(m_iFeatures[k]->getDimensionCount() == 1, "Invalid Input Signal.", Kernel::ErrorType::BadInput);
if (m_featuresDecoders[k].isBufferReceived()) // Buffer received
{
const size_t n = m_iFeatures[k]->getBufferElementCount();
double* buffer = m_iFeatures[k]->getBuffer();
//const std::vector<double> sample(buffer, buffer + n);
m_selector.addSample(std::vector<double>(buffer, buffer + n), int(k));
//getLogManager() << m_logLevel << " Sample received for class " << k << "\n";
}
}
}
return true;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelection::writeConfig()
{
std::ofstream file;
file.open(m_filename);
OV_ERROR_UNLESS_KRF(file.is_open(), "File can't be opened.", Kernel::ErrorType::BadFileWrite);
std::string sep;
file << "<OpenViBE-SettingsOverride>\n\t<SettingValue>";
for (const auto& r : m_result)
{
file << sep << r;
sep = ";";
}
file << "</SettingValue>\n</OpenViBE-SettingsOverride>";
file.close();
return true;
}
///-------------------------------------------------------------------------------------------------
} // namespace FeaturesSelection
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,133 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmFeaturesSelection.hpp
/// \brief Classes of the Box Features Selection Trainer.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 12/02/2020.
/// \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 "algorithm/CMRMR.hpp"
#include <vector>
#define OV_AttributeId_Box_FlagIsUnstable CIdentifier(0x666FFFFF, 0x666FFFFF)
namespace OpenViBE {
namespace Plugins {
namespace FeaturesSelection {
/// <summary> The class CBoxAlgorithmFeaturesSelection describes the box Features Selection Trainer. </summary>
class CBoxAlgorithmFeaturesSelection 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_FeaturesSelection)
protected:
//***** Codecs *****
Toolkit::TStimulationDecoder<CBoxAlgorithmFeaturesSelection> m_stimDecoder;
Toolkit::TStimulationEncoder<CBoxAlgorithmFeaturesSelection> m_stimEncoder;
std::vector<Toolkit::TFeatureVectorDecoder<CBoxAlgorithmFeaturesSelection>> m_featuresDecoders;
std::vector<CMatrix*> m_iFeatures; // Input Matrix pointer
IStimulationSet *m_iStim = nullptr, *m_oStim = nullptr; // Stimulation receiver/sender
//***** Settings *****
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_Info; // Log Level
uint64_t m_stimName = OVTK_StimulationId_Train; // Name of stimulation to check for train lunch
std::string m_filename;
EFeatureSelection m_method = EFeatureSelection::MRMR;
size_t m_nbClass = 2; // Number of input classes
bool m_isTrain = false;
// mRMR Settings
CMRMR m_selector;
bool m_doDiscretization = true; // Check if we make Discretization
double m_threshold = 0.0; // Threshold for Discretisation
size_t m_nFinalFeatures = size_t(-1); // Number of Features in output
EMRMRMethod m_mRMRMethod = EMRMRMethod::MID; // mRMR Method
std::vector<size_t> m_result; // mRMR Result
bool writeConfig();
};
/// <summary> Listener of the box Features Selection Trainer. </summary>
class CBoxAlgorithmFeaturesSelectionListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
box.setInputType(index, OV_TypeId_FeatureVector);
box.setInputName(index, ("Class " + std::to_string(index)).c_str());
return true;
}
bool onInputRemoved(Kernel::IBox& box, const size_t index) override { return true; }
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
/// <summary> Descriptor of the box Features Selection Trainer. </summary>
class CBoxAlgorithmFeaturesSelectionDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Features Selection Trainer"); }
CString getAuthorName() const override { return CString("Thibaut Monseigne"); }
CString getAuthorCompanyName() const override { return CString("Inria"); }
CString getShortDescription() const override { return CString("Apply a Features Selection Algorithm"); }
CString getDetailedDescription() const override { return CString(""); }
CString getCategory() const override { return CString("Features Selection"); }
CString getVersion() const override { return CString("1.0"); }
CString getStockItemName() const override { return CString("gtk-execute"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_FeaturesSelection; }
IPluginObject* create() override { return new CBoxAlgorithmFeaturesSelection; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmFeaturesSelectionListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Train-Start Flag",OV_TypeId_Stimulations);
prototype.addInput("Class 1",OV_TypeId_FeatureVector);
prototype.addInput("Class 2",OV_TypeId_FeatureVector);
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addOutput("Train-Completed Flag",OV_TypeId_Stimulations);
prototype.addSetting("Log Level", OV_TypeId_LogLevel, "Information");
prototype.addSetting("Train trigger", OV_TypeId_Stimulation, "OVTK_StimulationId_Train");
prototype.addSetting("Filename to save Feature Selection", OV_TypeId_Filename, "${Player_ScenarioDirectory}/features-selected.xml");
prototype.addSetting("Method", OVP_TypeId_Features_Selection_Method, toString(EFeatureSelection::MRMR).c_str());
prototype.addSetting("Number of features to select", OV_TypeId_Integer, "2");
prototype.addSetting("Discretisation", OV_TypeId_Boolean, "true");
prototype.addSetting("Threshold", OV_TypeId_Float, "0.0");
prototype.addSetting("mRMR Method", OVP_TypeId_mRMR_Method, "MID");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_FeaturesSelectionDesc)
};
} // namespace FeaturesSelection
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,116 @@
#include "CBoxAlgorithmFeaturesSelector.hpp"
namespace OpenViBE {
namespace Plugins {
namespace FeaturesSelection {
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelector::initialize()
{
m_decoder.initialize(*this, 0);
m_iMatrix = m_decoder.getOutputMatrix();
m_encoder.initialize(*this, 0);
m_oMatrix = m_encoder.getInputMatrix();
m_lookup.clear();
return true;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelector::uninitialize()
{
m_decoder.uninitialize();
m_encoder.uninitialize();
m_lookup.clear();
return true;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelector::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
bool CBoxAlgorithmFeaturesSelector::process()
{
Kernel::IBoxIO& boxCtx = this->getDynamicBoxContext();
for (size_t i = 0; i < boxCtx.getInputChunkCount(0); ++i)
{
m_decoder.decode(i); // Decode the chunk
if (m_decoder.isHeaderReceived()) // Header Received
{
// Parse Setting
const std::string setting = CString(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0)).toASCIIString();
OV_ERROR_UNLESS_KRF(parseSetting(setting), "Parsing has failed", Kernel::ErrorType::BadSetting);
OV_ERROR_UNLESS_KRF(!m_lookup.empty(), "No channel selected", Kernel::ErrorType::BadConfig);
getLogManager() << Kernel::LogLevel_Debug << "Features selected :";
for (const auto& r : m_lookup) { getLogManager() << " " << r; }
getLogManager() << "\n";
// Initialize Output Matrix
m_oMatrix->resize(m_lookup.size());
m_oMatrix->resetBuffer();
for (size_t j = 0; j < m_lookup.size(); ++j) { m_oMatrix->setDimensionLabel(0, j, m_iMatrix->getDimensionLabel(0, m_lookup[j])); }
m_encoder.encodeHeader();
}
if (m_decoder.isBufferReceived()) // Buffer Received
{
const double* iBuffer = m_iMatrix->getBuffer();
double* oBuffer = m_oMatrix->getBuffer();
for (size_t j = 0; j < m_lookup.size(); ++j) { memcpy(oBuffer + j, iBuffer + m_lookup[j], sizeof(double)); }
m_encoder.encodeBuffer();
}
if (m_decoder.isEndReceived()) { m_encoder.encodeEnd(); } // End Received
boxCtx.markOutputAsReadyToSend(0, boxCtx.getInputChunkStartTime(0, i), boxCtx.getInputChunkEndTime(0, i));
}
return true;
}
bool CBoxAlgorithmFeaturesSelector::parseSetting(const std::string& setting)
{
OV_ERROR_UNLESS_KRF(!setting.empty(), "Empty Setting is forbidden", Kernel::ErrorType::BadSetting);
std::stringstream ss(setting);
std::string token;
std::vector<std::string> list;
while (std::getline(ss, token, OV_Value_EnumeratedStringSeparator)) { list.push_back(token); }
// Define Min Max
const size_t startIdx = 0, endIdx = m_iMatrix->getDimensionSize(0); // never update in case of duplication or changing order
size_t idx1, idx2;
char sep;
// For each token
for (const auto& tmp : list)
{
ss.clear();
ss.str(tmp);
// Check if it's range
const std::size_t pos = tmp.find(OV_Value_RangeStringSeparator);
if (pos != std::string::npos)
{
if (tmp[0] == OV_Value_RangeStringSeparator) { idx1 = startIdx; }
else { ss >> idx1; }
ss >> sep;
if (tmp[tmp.length() - 1] == OV_Value_RangeStringSeparator) { idx2 = endIdx; }
else { ss >> idx2; }
OV_ERROR_UNLESS_KRF((idx1 < idx2 && idx2 <= endIdx), "Invalid Range String.", Kernel::ErrorType::BadSetting);
for (size_t i = idx1; i < idx2; ++i) { m_lookup.push_back(i); }
}
else
{
ss >> idx1;
OV_ERROR_UNLESS_KRF(idx1 <= endIdx, "Invalid Index.", Kernel::ErrorType::BadSetting);
m_lookup.push_back(idx1);
}
}
return true;
}
///-------------------------------------------------------------------------------------------------
} // namespace FeaturesSelection
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,84 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmFeaturesSelector.hpp
/// \brief Classes of the Box Features Selector.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 12/02/2020.
/// \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 "algorithm/CMRMR.hpp"
#include <vector>
namespace OpenViBE {
namespace Plugins {
namespace FeaturesSelection {
/// <summary> The class CBoxAlgorithmFeaturesSelector describes the box Features Selector. </summary>
class CBoxAlgorithmFeaturesSelector 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_FeaturesSelector)
protected:
//***** Codecs *****
Toolkit::TFeatureVectorDecoder<CBoxAlgorithmFeaturesSelector> m_decoder;
Toolkit::TFeatureVectorEncoder<CBoxAlgorithmFeaturesSelector> m_encoder;
CMatrix *m_iMatrix = nullptr, *m_oMatrix = nullptr;
//***** Settings *****
std::vector<size_t> m_lookup;
/// <summary> Parse the setting. </summary>
/// <param name="setting"> Setting to parse. </param>
/// <returns> True if Setting is correctly parsed.</returns>
bool parseSetting(const std::string& setting);
};
/// <summary> Descriptor of the box Features Selector. </summary>
class CBoxAlgorithmFeaturesSelectorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Features Selector"); }
CString getAuthorName() const override { return CString("Thibaut Monseigne"); }
CString getAuthorCompanyName() const override { return CString("Inria"); }
CString getShortDescription() const override { return CString("Select a subset of features vector."); }
CString getDetailedDescription() const override { return CString("Select Features with index starting from 0."); }
CString getCategory() const override { return CString("Features Selection"); }
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_FeaturesSelector; }
IPluginObject* create() override { return new CBoxAlgorithmFeaturesSelector; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input", OV_TypeId_FeatureVector);
prototype.addOutput("Output", OV_TypeId_FeatureVector);
prototype.addSetting("Features List", OV_TypeId_String, ":");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_FeaturesSelectorDesc)
};
} // namespace FeaturesSelection
} // namespace Plugins
} // namespace OpenViBE