Adding new classifiers to OpenViBE

Summer is tiiiiiiiime to coooooode :)

I know that several people have been interested in implementing new classifiers in OpenViBE. Indeed, the current release only proposes a basic LDA… This is a widely used classifier in BCI but one must admit that a lot of research and comparison has been done on EEG data classification and that it could be interesting to test/use different classifiers in some circumstances…

Well, implementing classifiers has been made really easy in OpenViBE. Indeed, it is not necessary to implement a complete box, or even a complete algorithm. All the basic work has been made in the OpenViBE Toolkit so that the overhead is minimal. The OpenViBE LDA implementation is a good example to look at if you want a reference… Basically, an OpenViBE classifier has to derive the OpenViBEToolkit::CAlgorithmClassifier class and implement 4 functions :

  • train : given a set of feature vectors, this function computes each parameter of the classifier. This is used offline, during the training of the classifier ; the Classifier Trainer box uses this function
  • saveConfiguration : after the train call(s), this function should store all the computed parameters in a memory buffer ; the Classifier Trainer box uses this function
  • loadConfiguration : before the classify call(s), this function should get computed parameters back from a memory buffer ; the Classifier Processor box uses this function
  • classify : given a feature vector, this function computes the most suitable class it belongs to. It may also ouptput some status values. For example, the LDA gives the distance to the hyperplane separating the two classes. This is used online ; the Classifier Processor box uses this function

The actual format of the configuration memory buffer does not really matter as soon as you know how to get the parameters back from it. I would personally recommend XML format as it is human readable usually making debugging phase easier. But still, the format does not matter.

The following code shows a very basic classifier which chooses a random class and classifies any feature vector to this class (useless classifier but – hopefully – useful example):

class CAlgorithmRandomClassifier
    : public OpenViBEToolkit::CAlgorithmClassifier
{
public:

virtual boolean train(
    const OpenViBEToolkit::IFeatureVectorSet& rFeatureVectorSet)
{
    // Put your training code here…
    // For this example, we choose a class between 1 and 2
    m_f64Class = 1 + (rand()%2);
    return true;
}

virtual boolean saveConfiguration(
    IMemoryBuffer& rMemoryBuffer)
{
    // Saving the classifier configuration simply consists
    // in putting the trained information into the rMemoryBuffer
    rMemoryBuffer.setSize(sizeof(float64));
    System::Memory::copy(
        rMemoryBuffer.getDirectPointer(),
        &m_f64Class,
        rMemoryBuffer.getSize());

    // Note that I didn’t use XML so to keep the example
    // as short as possible ^_^
    return true;
}

virtual boolean loadConfiguration(
    const IMemoryBuffer& rMemoryBuffer)
{
    // Loading the classifier configuration simply consists
    // in getting the trained information back from the rMemoryBuffer
    if(rMemoryBuffer.getSize()!=sizeof(float64))
    {
        return false;
    }

    System::Memory::copy(
        &m_f64Class,
        rMemoryBuffer.getDirectPointer(),
        rMemoryBuffer.getSize());

    // Note that I didn’t use XML so to keep the example
    // as short as possible ^_^
    return true;
}

virtual boolean classify(
    const OpenViBEToolkit::IFeatureVector& rFeatureVector,
    float64& rf64Class,
    OpenViBEToolkit::IVector& rClassificationValues)
{
    // Put your classifying code here…
    // For this example, we always return the same class ^_^
    rf64Class = m_f64Class;

    // The status vector may be used to reflect the
    // classifier state… For this example, we return the
    // confidence in the attributed class which is 0% ^_^
    rClassificationValues.setSize(1);
    rClassificationValues[0] = 0;
    return true;
}

_IsDerivedFromClass_Final_(
   CAlgorithmClassifier,
   OVP_ClassId_Algorithm_RandomClassifier);

float64 m_f64Class;
};

And you’re almost done :) ! You then just have to write a simple class descriptor as every plugin does :

class CAlgorithmRandomClassifierDesc
   : public OpenViBEToolkit::CAlgorithmClassifierDesc
{
public:

virtual CString getName(void) const { return “Random Classifier”; }
virtual CString getAuthorName(void) const { return “blah”; }
virtual CString getAuthorCompanyName(void) const { return “blah”; }
virtual CString getShortDescription(void) const { return “blah”; }
virtual CString getDetailedDescription(void) const { return “blah”; }
virtual CString getCategory(void) const { return “blah”; }
virtual CString getVersion(void) const { return “1.0”; }

virtual CIdentifier getCreatedClass(void) const
{
    return OVP_ClassId_Algorithm_RandomClassifier;
}

virtual Plugins::IPluginObject* create(void)
{
    return new CAlgorithmRandomClassifier;
}

_IsDerivedFromClass_Final_(
   CAlgorithmClassifierDesc,
   OVP_ClassId_Algorithm_RandomClassifierDesc);
};

and register this new classifier in your ovp_main.cpp file :

OVP_Declare_Begin()

    // …

    rPluginModuleContext.getTypeManager().registerEnumerationEntry(
        OVTK_TypeId_ClassificationAlgorithm,
        “Random Classifier”,
        OVP_ClassId_Algorithm_RandomClassifier.toUInteger());
    OVP_Declare_New(CAlgorithmRandomClassifierDesc);

    // …

OVP_Declare_End()

Once this is all completed, just run your designer and drag & drop a Classifier Trainer box. You should now have the Random Classifier listed in the combo box. Congratulations, you are now ready to implement new classifiers… and ultimately, you can compare your classifiers performance thanks to the Classifier Accuracy Measure box !

Comments are closed.