Custom Visualizations

Forum for software developers to discuss BCI2000 software development
Locked
jdwander
Posts: 6
Joined: 04 Apr 2012, 15:55

Custom Visualizations

Post by jdwander » 06 Aug 2013, 16:42

All,

A few of my colleagues and I are trying to add some custom visualization functionality to a number of self-paced SMR BCI paradigms. Specifically we would like to add a window, displayed in addition to standard visual feedback for the task, that displays the control signal power for the current sample block (imagine a vertical power meter). We could not find this functionality within the BCI framework so started working on a custom implementation but ran in to a few questions:

First, since this is going to be used with a variety of different application modules, we wanted to make the incorporation of the visualizer as simple as possible. The main approach we're considering is implementing a custom filter (effectively a copy of DummyFilter) that would be put at the end of the SignalProcessing filter chain and handles the visualization on its own. Since all of our BCI paradigms use the same SignalProcessing module, this wouldn't require addition of code to each Application module. However, it makes the assumption that the SignalProcessing and Application modules are being run on the same machine (always the case for us), which didn't seem to fit within the BCI2000 model. Alternatively, we considered building an Extension [1] (a la EyeTrackerLogger), but these appear to only have access to states, not signals. Does anyone have suggestions for the cleanest / easiest way to get the functionality we want?

Second, we went down the path of implementing a custom filter [2], and could tinker with the GenericVisualization class as is demonstrated in the programming tutorial, however it wasn't readily apparent to us how we could replace GenericVisualization with a custom class that has the desired visualization behavior (i.e. what functionality to override when subclassing GenericVisualization). If anyone has ideas/experience on this front that would be very helpful.

It may end up being that the simplest path to success is to build our own QT objects in a custom filter, but before going that route, we wanted to float our questions out to the Crowd to see if anyone had suggestions.

Thanks!
Jeremiah

[1] http://www.bci2000.org/wiki/index.php/C ... Extensions
[2] http://www.bci2000.org/wiki/index.php/P ... ing_Filter

mellinger
Posts: 1064
Joined: 12 Feb 2003, 11:06

Re: Custom Visualizations

Post by mellinger » 07 Aug 2013, 06:00

Hi,

unlike GenericFilter, GenericVisualization is not meant to be subclassed by filter programmers.
Rather, every instance of GenericVisualization acts like a direct output channel to a single Operator visualization window, which is identified by its visualization ID.

Most of the time, objects of type GenericSignal are sent to Operator visualization windows, resulting in the display of signals. However, it is also possible to send bitmap data, by creating an object of type BitmapImage, and sending it through a GenericVisualization object. This is how the small copy of the Application window is sent to the Operator module.

For efficiency reasons, BitmapImages use 4 bits for each RGB value, plus a 1-bit alpha channel, and allow for negative values, which makes it possible to send difference frames rather than a full frame each time. Due to RLE compression in transmission, this is quite efficient for computer-generated images, such as the Application window. There is a specialized kind of GenericVisualization, called BitmapVisualization, that handles differencing images behind the scene, so you don't need to care about that.

Thus, what you might do is to draw your desired visualization into an offscreen bitmap, and then send it through a BitmapVisualization object, like this:

Code: Select all

// filter declaration:
...
BitmapVisualization mPowerVis;

// filter's constructor:
MyFilter::MyFilter()
: mPowerVis( "PWRVIS" ),
...
{ ... }

// filter's Initialize():
mPowerVis.Send( CfgID::WindowTitle, "My Power Window" );
mPowerVis.SendReferenceFrame( BitmapImage( width, height ) );

// filter's StartRun():
mPowerVis.SendReferenceFrame( BitmapImage( width, height ) );

// filter's Process():
// Draw your image, e.g. using Qt's SVG renderer, and get/convert the result as/into a QImage:
QImage img = ...
BitmapImage bimg( width, height );
for( x = 0; x < width; ++x )
  for( y = 0; y < height; ++y )
  {
    QColor c = QColor::fromRgba( img.pixel( x, y ) );
    if( c.alpha() == 0 )
      bimg( x, y ) = RGBColor::NullColor;
    else
      bimg( x, y ) = RGBColor( c.red(), c.green(), c.blue() );
  }
mPowerVis.SendDifferenceFrame( bimg );
(edited)

Best regards,
Juergen

jdwander
Posts: 6
Joined: 04 Apr 2012, 15:55

Re: Custom Visualizations

Post by jdwander » 07 Aug 2013, 14:42

Juergen,

Thanks for the quick and helpful response. We will give that a shot. Quick question for clarification though: should we use a BitmapVisualization as opposed to a GenericVisualization to be able to send the reference and difference frames? In the version of the source that we're working from GenericVisualization doesn't have SendDifferenceFrame(...) and SendReferenceFrame(...). Or has this been changed in more recent versions?

I believe our version of the source is 3.0.5. (checked from operator -> help -> about)

Thanks!
Miah

mellinger
Posts: 1064
Joined: 12 Feb 2003, 11:06

Re: Custom Visualizations

Post by mellinger » 08 Aug 2013, 10:02

Hi,

sorry, I was a bit confused when I wrote the previous post. You are right, you should use BitmapVisualization rather than GenericVisualization.

I edited my previous post such that it should now be correct.

Best regards,
Juergen

Locked

Who is online

Users browsing this forum: No registered users and 1 guest