Commit bc741429 authored by Serrière Guillaume's avatar Serrière Guillaume

Documentation + code review.

Signed-off-by: default avatarSerrière Guillaume <guillaume.serriere@inria.fr>
parent 3bce1510
......@@ -140,4 +140,6 @@ namespace OpenViBEDesigner
#include "ovp_global_defines.h"
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
#endif // __OpenViBEDesigner_defines_H__
......@@ -6,16 +6,44 @@
namespace XML
{
/**
* @class IXMLHandler
* @author Serrière Guillaume (INRIA/Loria)
* @brief This class is design to help about XML manipulation.
* @sa XML
*/
class OV_API IXMLHandler
{
public:
/**
* @brief Release the handler.
*/
virtual void release(void)=0;
//Parsing
/**
* @brief Parse file points by sPath and return the root name of the document.
* @param sPath [in] : Path to the File
* @return The root node of the document, or NULL if there is an error.
*/
virtual XML::IXMLNode* parseFile(const char* sPath)=0;
/**
* @brief Parse the string sString on uiSize caracters and return the root name of the document.
* @param sString [in] : String which contains the XML
* @param uiSize [in] : Size of the part to analyze
* @return The root node of the parse part, or NULL if there is an error.
*/
virtual XML::IXMLNode* parseString(const char* sString, const uint32& uiSize)=0;
//XML extraction
/**
* @brief Write the XML corresponding to the node rNode in the file points by sPath. If the file exists
* it will be erase.
* @param rNode [in] : The node to write.
* @param sPath [in] : The path to the file.
* @return True on success, false otherwise.
*/
virtual XML::boolean writeXMLInFile(const IXMLNode &rNode, const char* sPath) const =0;
protected:
......
......@@ -6,6 +6,12 @@
namespace XML
{
/**
* @class IXMLNode
* @author Serrière Guillaume (INRIA/Loria)
* @brief Symbolize a node in a XML tree structure.
* @sa XML
*/
class OV_API IXMLNode
{
public:
......@@ -14,27 +20,87 @@ namespace XML
virtual const char* getName() const =0;
//Attribute
/**
* @brief Add the attribute sAttributeName with value
* sAttributeValue to the node.
* @param sAttributeName [in] : Name of the attribute
* @param sAttributeValue [in] : Value of the attribute
* @return true in success, false otherwise
*/
virtual XML::boolean addAttribute(const char* sAttributeName, const char* sAttributeValue)=0;
/**
* @brief Indicate if an attribute exists or not.
* @param sAttributeName [in] : Name of the attribute
* @return true if attribute exists, false otherwise
*/
virtual XML::boolean hasAttribute(const char* sAttributeName) const =0;
/**
* @brief Return the value of an attribute.
* @param sAttributeName [in] : Name of the attribute
* @return Value of the attribute
*/
virtual const char* getAttribute(const char* sAttributeName) const =0;
//PCDATA
/**
* @brief Set the PCDATA of the node.
* @param childData [in] : Value of the PCDATA
*/
virtual void setPCData(const char* childData)=0;
/**
* @brief Return the PCDATA of the node.
* @return Value of PCDATA
*/
virtual const char* getPCData(void) const =0;
//Child
/**
* @brief Add a node child of the
* @param ChildNode [in] : The Node that will became the new child
*/
virtual void addChild(XML::IXMLNode* ChildNode)=0;
/**
* @brief Return the ith child of the node.
* @param iChildIndex [in] : index of the child.
* @return The ith child of the node.
*/
virtual XML::IXMLNode* getChild(const XML::uint32 iChildIndex) const =0;
/**
* @brief Return the first child with the name sName.
* @param sName [in]] : Name of th child
* @return The first child of the node which name is sName.
*/
virtual XML::IXMLNode* getChildByName(const char* sName) const =0;
/**
* @brief Return the amount of child the node has.
* @return Amount of child.
*/
virtual XML::uint32 getChildCount(void) const =0;
//XML generation
/**
* @brief Return a string which contains the XML of the node. The string is dynamically instantiate so
* it requires to be free.
* @param depth [in] : Amount of indentation
* @return XML string describing the node and its childs.
*/
virtual char* getXML(const XML::uint32 depth=0) const =0;
protected:
virtual ~IXMLNode(void) {}
};
/**
* @brief Create a new node with the name sName. The node is created dynamically and requires to be free.
* @param sName [in] : Name of the node
* @return New node
*/
extern OV_API XML::IXMLNode* createNode(const char* sName);
}
......
......@@ -96,6 +96,7 @@ IXMLNode *IXMLHandlerImpl::parseFile(const char *sPath)
delete[] l_sBuffer;
return l_pRes;
}
std::cout << "Error : unable to open the file" << sPath << "." << std::endl;
return NULL;
}
......@@ -107,6 +108,7 @@ IXMLNode *IXMLHandlerImpl::parseString(const char *sString, const uint32& uiSize
//We delete what is still in the stack
while(!m_oNodeStack.empty())
{
std::cout << "Warning : the file has been parsed but some tags are not closed. The file is probably not well-formed." << std::endl;
IXMLNode * l_pNode = m_oNodeStack.top();
l_pNode->release();
m_oNodeStack.pop();
......@@ -131,6 +133,7 @@ boolean IXMLHandlerImpl::writeXMLInFile(const IXMLNode &rNode, const char *sPath
free(l_sXML);
return true;
}
std::cout << "Error : unable to open the file " << sPath << "." << std::endl;
return false;
}
......@@ -170,7 +173,7 @@ void IXMLHandlerImpl::closeChild()
static void XMLCALL XML::expat_xml_start(void* pData, const char* pElement, const char** ppAttribute)
{
uint64 i, l_ui64AttributeCount=0;
uint64 l_ui64AttributeCount=0;
while(ppAttribute[l_ui64AttributeCount++]);
l_ui64AttributeCount>>=1;
......@@ -178,7 +181,7 @@ static void XMLCALL XML::expat_xml_start(void* pData, const char* pElement, cons
const char** l_pAttributeName=new const char*[static_cast<size_t>(l_ui64AttributeCount)];
const char** l_pAttributeValue=new const char*[static_cast<size_t>(l_ui64AttributeCount)];
for(i=0; i<l_ui64AttributeCount; i++)
for(uint64 i=0; i<l_ui64AttributeCount; i++)
{
l_pAttributeName[i]=ppAttribute[(i<<1)];
l_pAttributeValue[i]=ppAttribute[(i<<1)+1];
......
......@@ -133,9 +133,9 @@ IXMLNode *IXMLNodeImpl::getChild(const XML::uint32 iChildIndex) const
IXMLNode *IXMLNodeImpl::getChildByName(const char *sName) const
{
for (vector<XML::IXMLNode*>::const_iterator it=m_oNodeVector.begin(); it!=m_oNodeVector.end(); ++it)
for (vector<IXMLNode*>::const_iterator it=m_oNodeVector.begin(); it!=m_oNodeVector.end(); ++it)
{
IXMLNode *l_sTempNode = (IXMLNode *)(*it);
IXMLNode *l_sTempNode = static_cast<IXMLNode*>(*it);
if(::strcmp(l_sTempNode->getName(), sName) == 0)
return l_sTempNode;
}
......@@ -205,7 +205,7 @@ char* IXMLNodeImpl::getXML(const XML::uint32 depth) const
for (vector<XML::IXMLNode*>::const_iterator it=m_oNodeVector.begin(); it!=m_oNodeVector.end(); ++it)
{
IXMLNode *l_sTempNode = (IXMLNode *)(*it);
IXMLNode *l_sTempNode = static_cast<IXMLNode *>(*it);
l_sRes = l_sRes + string("\n") + l_sTempNode->getXML(depth+1);
}
......
......@@ -9,8 +9,9 @@ __________________________________________________________________
The <em>Classifier Processor</em> box is a generic box for classification purpose. It works
in conjunction with the \ref Doc_BoxAlgorithm_ClassifierTrainer box.
This box' role is to expose a generic interface to the rest of the BCI pipelines. The tasks
specific to a given classifier are forwarded to one of the registered \c OVTK_TypeId_ClassifierAlgorithm
algorithms. The behavior is simple, at initialization phase, the classification algorithm is initialized
specific to a given classifier are forwarded to an algorithm or a structur of algorithms depending if what is
describe in the configuration file. The behavior is simple, at initialization phase, the classification structure
is initialized
and its configuration is loaded from the configuration file. Then each time this box receives a new
feature vector, it is forwarded to the classification algorithm that classifies it. The box gets the algorithm
status and the actual class value and translates this information to its output. The class is sent in the form of
......@@ -61,26 +62,10 @@ __________________________________________________________________
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessor_Settings|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessor_Setting1|
The first setting of this box is the classifier to use. You have to choose the same classifier as the one you used
during the training phase with the \ref Doc_BoxAlgorithm_ClassifierTrainer box.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessor_Setting1|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessor_Setting2|
This setting points to the configuration file of the algorithm. This configuration file is generally generated from the
This setting points to the configuration file of the box generated by the
\ref Doc_BoxAlgorithm_ClassifierTrainer box. Its syntax depends on the selected algorithm.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessor_Setting2|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessor_Setting3|
For classification algorithms that support rejection, you can choose a stimulation that reflects the feature vector
could not be classified.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessor_Setting3|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessor_Setting4|
This is the stimulation to send when the classifier algorithm detects a class-1 feature vector
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessor_Setting4|
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessor_Setting1|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessor_Setting5|
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessor_Setting5|
__________________________________________________________________
Examples description
......
/**
* \page BoxAlgorithm_ClassifierProcessor_Deprecated Classifier processor
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Description|
The <em>Classifier Processor</em> box is a generic box for classification purpose. It works
in conjunction with the \ref Doc_BoxAlgorithm_ClassifierTrainer box.
This box' role is to expose a generic interface to the rest of the BCI pipelines. The tasks
specific to a given classifier are forwarded to one of the registered \c OVTK_TypeId_ClassifierAlgorithm
algorithms. The behavior is simple, at initialization phase, the classification algorithm is initialized
and its configuration is loaded from the configuration file. Then each time this box receives a new
feature vector, it is forwarded to the classification algorithm that classifies it. The box gets the algorithm
status and the actual class value and translates this information to its output. The class is sent in the form of
a stimulation and the algorithm status is sent in the form a streamed matrix. The stimulation can be generically
interpreted by the rest of the pipeline but it is important to understand that each classification algorithm is
free to report whatever it wants in its "status matrix". Consequently, the use of this output stream will be
dependent on the chosen classification algorithm. For example, the LDA classifier sends the hyperplane distance
value as its status.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Inputs|
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Inputs|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Input1|
This input should be connected to the feature vectors to classify. Each time a new feature vector arrives,
a classification process will be triggered. Consequently, a classification stimulation will be sent on the
first output of this box.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Input1|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Outputs|
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Outputs|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Output1|
This output will contain the classification stimulations. Each time a new feature vector arrives to this box,
a new classification process is triggered, resulting in the generation of the corresponding class stimulation.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Output1|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Output2|
This output reflects the classification algorithm status in the form of a matrix of value. The content of this
matrix is dependent of the chosen classification algorithm. For example, the LDA classifier sends the hyperplane
distance as its status. Given that this value is dependent of the chosen algorithm, you should be very carefull
with the use of this output stream. Unexepected behavior may (will) occur when changing the classifier.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Output2|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Settings|
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Settings|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Setting1|
The first setting of this box is the classifier to use. You have to choose the same classifier as the one you used
during the training phase with the \ref Doc_BoxAlgorithm_ClassifierTrainer box.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Setting1|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Setting2|
This setting points to the configuration file of the algorithm. This configuration file is generally generated from the
\ref Doc_BoxAlgorithm_ClassifierTrainer box. Its syntax depends on the selected algorithm.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Setting2|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Setting3|
For classification algorithms that support rejection, you can choose a stimulation that reflects the feature vector
could not be classified.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Setting3|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Setting4|
This is the stimulation to send when the classifier algorithm detects a class-1 feature vector
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Setting4|
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Setting5|
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Setting5|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Examples|
This box is used in BCI pipelines in order to classify cerebral activity states. For a detailed scenario using this
box and its associated \ref Doc_BoxAlgorithm_ClassifierTrainer, please see the <b>motor imagary</b>
BCI scenario in the sample scenarios.
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierProcessorDeprecated_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_ClassifierProcessorDeprecated_Miscellaneous|
*/
......@@ -8,14 +8,16 @@ __________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Description|
The <em>Classifier Trainer</em> box is a generic box for classification training purpose. It works
in conjunction with the \ref Doc_BoxAlgorithm_ClassifierProcessor box.
This box' role is to expose a generic interface to the rest of the BCI pipelines. The tasks
specific to a given classifier are forwarded to one of the registered \c OVTK_TypeId_ClassifierAlgorithm
algorithms. The behavior is simple, the box collects a number of feature vectors. Those feature vectors
This box' role is to expose a generic interface to the rest of the BCI pipelines. The box
generate will generate an internal structure according to the startegy and the algorithm selected.
The behavior is simple, the box collects a number of feature vectors. Those feature vectors
are labelled depending on the input they arrive on. When a specific stimulation arrives, a training
process is triggered. This process can take some time so this box should be used offline. Depending on the
settings you enter, you will be able to perform a k-fold test in order to train a better classifier. When
this training stimulation is received, the box requests the selected classification algorithm to generate
a configuration file that will be usable online by the \ref Doc_BoxAlgorithm_ClassifierProcessor box. Finally, the box releases a particular stimulation (OVTK_StimulationId_TrainCompleted) on its ouput, that can be used to trigger further treatments in the scenario.
this training stimulation is received, the box genrate a configuration file that will be usable online by the
\ref Doc_BoxAlgorithm_ClassifierProcessor box.
Finally, the box releases a particular stimulation (OVTK_StimulationId_TrainCompleted)
on its ouput, that can be used to trigger further treatments in the scenario.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Description|
__________________________________________________________________
......@@ -67,27 +69,47 @@ parameters can not be done in this page because it is impossible to know at this
parameters you will have available. This will depend on the classification algorihtms that are be implemented in OpenViBE.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Settings|
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting1|
The first setting of this box is the classifier to use. You can choose any registered \c OVTK_TypeId_ClassifierAlgorithm
algorithm you want.
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting1|
The first setting of this box is the strategy to use. You can choose any registered \c OVTK_TypeId_ClassificationStrategy
strategy you want.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting1|
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting2|
This setting points to the configuration file where to save the result of the training for later online use. This
configuration file is used by the \ref Doc_BoxAlgorithm_ClassifierProcessor box. Its syntax
depends on the selected algorithm.
The second setting is the classifier to use. You can choose any registered \c OVTK_TypeId_ClassifierAlgorithm
algorithm you want.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting2|
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting3|
This is the stimulation to consider to trigger the training process.
This setting points to the configuration file where to save the result of the training for later online use. This
configuration file is used by the \ref Doc_BoxAlgorithm_ClassifierProcessor box. Its syntax
depends on the selected algorithm.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting3|
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting4|
This is the stimulation to consider to trigger the training process.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting4|
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting5|
If you want to perform a k-fold test, you should enter something else than 0 or 1 here. A k-fold test generally allows
better classification rates. The idea is to divide the set of feature vectors in a number of partitions. The classification
algorithm is trained on some of the partitions and its accuracy is tested on the others. The classifier with the best results
is selected as the trained classifier. See the miscellaneous section for details on how the k-fold test is done in this box.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting4|
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting5|
*
*
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting6|
For classification algorithms that support rejection, you can choose a stimulation that reflects the feature vector
could not be classified.
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting6|
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting7|
This is the stimulation to send when the classifier algorithm detects a class-1 feature vector
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting7|
*
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Setting8|
This is the stimulation to send when the classifier algorithm detects a class-1 feature vector
* |OVP_DocEnd_BoxAlgorithm_ClassifierTrainer_Setting8|
......@@ -108,7 +130,25 @@ __________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClassifierTrainer_Miscellaneous|
Available classifiers:
\par Available startegy:
\par Native
Use directly the algorithm without pairwise strategy.
\par One Vs All
Use a One vs All pairwise strategy which consits in training one classifiers against the others for each class.
\par One vs One
Use One vs One pairwise strategy which consists in training one classifier for each pair of classifier. Then we use a decision startegy to extract the most relevant
results. There is three differents decision strategy:
\li Voting: method based on a simple voting process,
\li HT: method describes in the following article Hastie, Trevor; Tibshirani, Robert. Classification by pairwise coupling. The Annals of Statistics 26 (1998), no. 2, 451--471
\li PKPD: method describe in the article Price, S. Knerr, L. Personnaz, and G. Dreyfus. Pairwise neural network classifiers with probabilistic outputs. In G. Tesauro, D. Touretzky, and T. Leen (eds.)
Advances in Neural Information Processing Systems 7 (NIPS-94), pp. 1109-1116. MIT Press, 1995.
You cannot use every algorithm with every decision stratagy, but the interface will restain the choice according to your selection.
\par Available classifiers:
\par Support Vector Machine (SVM)
A well-known classifier supporting non-linear classification via kernels. The implementation is based on LIBSVM 2.91, which is included in the OpenViBE source tree. The parameters exposed in the GUI correspond to LIBSVM parameters. For more information on LIBSVM, see <a href="http://www.csie.ntu.edu.tw/~cjlin/libsvm/">here</a>.
......@@ -116,6 +156,9 @@ A well-known classifier supporting non-linear classification via kernels. The im
\par Linear Discriminant Analysis (LDA)
A simple and fast linear classifier. For description, see any major textbook on Machine Learning or Statistics (e.g. Duda, Hart & Stork, or Hastie, Tibshirani & Friedman).
\par Probabilistic LDA
This is the same algorithm as Linear Discriminant Analysis (LDA) but generate confidence score between 0 and 1.
\par Shrinkage LDA (sLDA)
A variant of Linear Discriminant Analysis (LDA) with regularization. The regularization is performed by shrinking the empiric covariance matrix towards a prior covariance matrix according to a method proposed by Ledoit & Wolf: "A Well-Conditioned Estimator for Large-Dimensional Covariance Matrices", 2004. The code follows the original Matlab implementation of the authors.
\par
......@@ -126,6 +169,9 @@ The shrinkage LDA classifier has the following options.
\par
Note that setting shrinkage to 0 should get you the regular LDA behavior. If you additionally force the covariance to be diagonal, you should get a model resembling the Naive Bayes classifier.
\par Probabilistic Shrinkage LDA
This is the same algorithm as Shrinkage LDA (sLDA) but generate confidence score between 0 and 1.
Cross Validation
In this section, we will detail how the k-fold test is implemented in this box. For the k-fold test to be performed, you
......
......@@ -15,12 +15,14 @@ static const char* const c_sCoeffNodeName = "Coefficients";
extern const char* const c_sClassifierRoot;
OpenViBE::int32 OpenViBEPlugins::Classification::getLDABestClassification(OpenViBE::IMatrix& rFirstClassificationValue, OpenViBE::IMatrix& rSecondClassificationValue)
{
if(::fabs(rFirstClassificationValue[0]) < ::fabs(rSecondClassificationValue[0]) )
return -1;
else if(::fabs(rFirstClassificationValue[0]) == ::fabs(rSecondClassificationValue[0]))
if(ov_float_equal(rFirstClassificationValue[0], ::fabs(rSecondClassificationValue[0])))
return 0;
else if(::fabs(rFirstClassificationValue[0]) < ::fabs(rSecondClassificationValue[0]))
return -1;
else
return 1;
}
......@@ -45,16 +47,15 @@ boolean CAlgorithmClassifierLDA::initialize(void)
boolean CAlgorithmClassifierLDA::train(const IFeatureVectorSet& rFeatureVectorSet)
{
uint32 i;
std::map < float64, uint64 > l_vClassLabels;
for(i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
for(size_t i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
{
l_vClassLabels[rFeatureVectorSet[i].getLabel()]++;
}
if(l_vClassLabels.size() != 2)
{
this->getLogManager() << LogLevel_Error << "A LDA classifier can only be trained with 2 classes, not more, not less - got " << (uint32)l_vClassLabels.size() << "\n";
this->getLogManager() << LogLevel_Error << "A LDA classifier can only be trained with 2 classes, not more, not less - got " << l_vClassLabels.size() << "\n";
return false;
}
......@@ -70,11 +71,10 @@ boolean CAlgorithmClassifierLDA::train(const IFeatureVectorSet& rFeatureVectorSe
l_oMeanFeatureVector1.zeros();
l_oMeanFeatureVector2.zeros();
for(i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
for(size_t i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
{
const IFeatureVector& l_rFeatureVector=rFeatureVectorSet[i];
float64 l_f64Label=l_rFeatureVector.getLabel();
const float64 l_f64Label=l_rFeatureVector.getLabel();
if(l_f64Label==m_f64Class1)
{
......@@ -87,14 +87,14 @@ boolean CAlgorithmClassifierLDA::train(const IFeatureVectorSet& rFeatureVectorSe
}
}
l_oMeanFeatureVector1/=(double)l_vClassLabels[m_f64Class1];
l_oMeanFeatureVector2/=(double)l_vClassLabels[m_f64Class2];
l_oMeanFeatureVector1/=static_cast<float64>(l_vClassLabels[m_f64Class1]);
l_oMeanFeatureVector2/=static_cast<float64>(l_vClassLabels[m_f64Class2]);
itpp::vec l_oDiff;
itpp::mat l_oSigma(l_ui32NumberOfFeatures, l_ui32NumberOfFeatures);
l_oSigma.zeros();
for(i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
for(size_t i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
{
const IFeatureVector& l_rFeatureVector=rFeatureVectorSet[i];
......@@ -133,7 +133,7 @@ boolean CAlgorithmClassifierLDA::classify(const IFeatureVector& rFeatureVector,
l_oFeatures.ins(0, 1);
float64 l_f64Result=-l_oFeatures*m_oCoefficients;
const float64 l_f64Result=-l_oFeatures*m_oCoefficients;
rClassificationValues.setSize(1);
rClassificationValues[0]=l_f64Result;
......
......@@ -35,7 +35,7 @@ namespace OpenViBEPlugins
virtual XML::IXMLNode* saveConfiguration(void);
virtual OpenViBE::boolean loadConfiguration(XML::IXMLNode* pConfiguratioNode);
_IsDerivedFromClass_Final_(OpenViBEToolkit::CAlgorithmClassifier, OVP_ClassId_Algorithm_ClassifierLDA);
_IsDerivedFromClass_Final_(OpenViBEToolkit::CAlgorithmClassifier, OVP_ClassId_Algorithm_ClassifierLDA)
protected:
......
......@@ -8,13 +8,14 @@
#include <iostream>
#include <system/Memory.h>
static const char* const c_sTypeNodeName = "OneVsAll";
static const char* const c_sSubClassifierIdentifierNodeName = "SubClassifierIdentifier";
static const char* const c_sAlgorithmIdAttribute = "algorithm-id";
static const char* const c_sSubClassifierCountNodeName = "SubClassifierCount";
static const char* const c_sSubClassifiersNodeName = "SubClassifiers";
static const char* const c_sSubClassifierNodeName = "SubClassifier";
namespace{
const char* const c_sTypeNodeName = "OneVsAll";
const char* const c_sSubClassifierIdentifierNodeName = "SubClassifierIdentifier";
const char* const c_sAlgorithmIdAttribute = "algorithm-id";
const char* const c_sSubClassifierCountNodeName = "SubClassifierCount";
const char* const c_sSubClassifiersNodeName = "SubClassifiers";
const char* const c_sSubClassifierNodeName = "SubClassifier";
}
extern const char* const c_sClassifierRoot;
......@@ -50,7 +51,8 @@ boolean CAlgorithmClassifierOneVsAll::train(const IFeatureVectorSet& rFeatureVec
{
const uint32 l_ui32AmountClass = m_oSubClassifierList.size();
std::map < float64, size_t > l_vClassLabels;
for(uint32 i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
for(size_t i=0; i<rFeatureVectorSet.getFeatureVectorCount(); i++)
{
if(!l_vClassLabels.count(rFeatureVectorSet[i].getLabel()))
{
......@@ -61,31 +63,40 @@ boolean CAlgorithmClassifierOneVsAll::train(const IFeatureVectorSet& rFeatureVec
if(l_vClassLabels.size() != l_ui32AmountClass)
{
this->getLogManager() << LogLevel_Error << "There is sample for " << (uint32)l_vClassLabels.size() << " classes but expected for " << l_ui32AmountClass << ".\n";
this->getLogManager() << LogLevel_Error << "There is sample for " << l_vClassLabels.size() << " classes but expected for " << l_ui32AmountClass << ".\n";
return false;
}
//We send new set of data to each classifer. They will all use two different classes 1 and 2. 1 is for the class it should recognize, 2 for the others
for(uint32 l_iClassifierCounter = 1 ; l_iClassifierCounter <= m_oSubClassifierList.size() ; ++l_iClassifierCounter )
//We set the IMatrix fo the first classifier
const uint32 l_ui32FeatureVectorSize=rFeatureVectorSet[0].getSize();
TParameterHandler < IMatrix* > ip_pFeatureVectorSetReference(m_oSubClassifierList[0]->getInputParameter(OVTK_Algorithm_Classifier_InputParameterId_FeatureVectorSet));
ip_pFeatureVectorSetReference->setDimensionCount(2);
ip_pFeatureVectorSetReference->setDimensionSize(0, rFeatureVectorSet.getFeatureVectorCount());
ip_pFeatureVectorSetReference->setDimensionSize(1, l_ui32FeatureVectorSize+1);
float64* l_pFeatureVectorSetBuffer=ip_pFeatureVectorSetReference->getBuffer();
for(size_t j=0; j<rFeatureVectorSet.getFeatureVectorCount(); j++)
{
uint32 l_ui32FeatureVectorSize=rFeatureVectorSet[0].getSize();
System::Memory::copy(
l_pFeatureVectorSetBuffer,
rFeatureVectorSet[j].getBuffer(),
l_ui32FeatureVectorSize*sizeof(float64));
//We let the space for the label
l_pFeatureVectorSetBuffer+=(l_ui32FeatureVectorSize+1);
}
//And then we just change adapt the label for each feature vector but we don't copy them anymore
for(size_t l_iClassifierCounter = 1 ; l_iClassifierCounter <= m_oSubClassifierList.size() ; ++l_iClassifierCounter )
{
TParameterHandler < IMatrix* > ip_pFeatureVectorSet(m_oSubClassifierList[l_iClassifierCounter-1]->getInputParameter(OVTK_Algorithm_Classifier_InputParameterId_FeatureVectorSet));
ip_pFeatureVectorSet->setDimensionCount(2);
ip_pFeatureVectorSet->setDimensionSize(0, rFeatureVectorSet.getFeatureVectorCount());
ip_pFeatureVectorSet->setDimensionSize(1, l_ui32FeatureVectorSize+1);
ip_pFeatureVectorSet = (IMatrix*)ip_pFeatureVectorSetReference;
float64* l_pFeatureVectorSetBuffer=ip_pFeatureVectorSet->getBuffer();
for(size_t j=0; j<rFeatureVectorSet.getFeatureVectorCount(); j++)
{
System::Memory::copy(
l_pFeatureVectorSetBuffer,
rFeatureVectorSet[j].getBuffer(),
l_ui32FeatureVectorSize*sizeof(float64));
//Modify the class of each featureVector
float64 l_f64Class = rFeatureVectorSet[j].getLabel();
if((uint32)l_f64Class == l_iClassifierCounter)
const float64 l_f64Class = rFeatureVectorSet[j].getLabel();
if(static_cast<uint32>(l_f64Class) == l_iClassifierCounter)
{
l_pFeatureVectorSetBuffer[l_ui32FeatureVectorSize]=1;
}
......@@ -105,9 +116,9 @@ boolean CAlgorithmClassifierOneVsAll::classify(const IFeatureVector& rFeatureVec
{
std::vector< std::pair < float64, IMatrix*> > l_oClassificationVector;
uint32 l_ui32FeatureVectorSize=rFeatureVector.getSize();
const uint32 l_ui32FeatureVectorSize=rFeatureVector.getSize();
for(uint32 l_iClassifierCounter = 0 ; l_iClassifierCounter < m_oSubClassifierList.size() ; ++l_iClassifierCounter )
for(size_t l_iClassifierCounter = 0 ; l_iClassifierCounter < m_oSubClassifierList.size() ; ++l_iClassifierCounter )
{
IAlgorithmProxy* l_pSubClassifier = this->m_oSubClassifierList[l_iClassifierCounter];
TParameterHandler < IMatrix* > ip_pFeatureVector(l_pSubClassifier->getInputParameter(OVTK_Algorithm_Classifier_InputParameterId_FeatureVector));
......@@ -123,63 +134,64 @@ boolean CAlgorithmClassifierOneVsAll::classify(const IFeatureVector& rFeatureVec
l_ui32FeatureVectorSize*sizeof(float64));
l_pSubClassifier->process(OVTK_Algorithm_Classifier_InputTriggerId_Classify);
//this->getLogManager() << LogLevel_Info << l_iClassifierCounter << " " << (float64)op_f64ClassificationStateClass << " " << (*op_pClassificationValues)[0] << "\n";
l_oClassificationVector.push_back(std::pair< float64, IMatrix*>((float64)op_f64ClassificationStateClass, (IMatrix*)op_pClassificationValues));
l_oClassificationVector.push_back(std::pair< float64, IMatrix*>(static_cast<float64>(op_f64ClassificationStateClass),
static_cast<IMatrix*>(op_pClassificationValues)));
}