Skip to content

1. DVS event stream printer

Alexandre Marcireau edited this page Oct 7, 2021 · 21 revisions

goal: develop a simple application to print the contents of an Event Stream file

duration: 30 minutes

prerequisites:

  • basic notions in bash
  • basic notions in C++

result files: basics/1_dvs_event_stream_printer


steps

  1. prerequisites
  2. philosophy
  3. the code

prerequisites

Debian / Ubuntu

Open a terminal and run:

sudo apt install build-essential # C++ compiler
sudo apt install git # version management
sudo apt install premake4 # cross-platform build configuration

macOS

Open a terminal and run:

brew install premake # cross-platform build configuration

If you do not have Homebrew yet, run first:

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Windows

Download and install:

  • Visual Studio Community. Select at least Desktop development with C++ when asked.
  • git
  • premake 4.x. In order to use it from the command line, the premake4.exe executable must be copied to a directory in your path. After downloading and decompressing premake-4.4-beta5-windows.zip, run in the command prompt:
copy "%userprofile%\Downloads\premake-4.4-beta5-windows\premake4.exe" "%userprofile%\AppData\Local\Microsoft\WindowsApps"

philosophy

The tutorials provided by this repository rely on three libraries:

These libraries are header-only: their headers contain the entire source code, hence no linking to pre-compiled binaries is required. The steps presented in the tutorials result in local installations: each code project contains its own copy of the libraries. Hence, different code projects can easily use different libraries versions on the same machine. Moreover, a project can be shared with other machines simply by zipping and sending the directory.

The Sepia, Tarsier and Chameleon libraries start with the idea that events are best represented as function calls. Whenever something happens, a user-provided function is called with the event's parameters. This mechanism is known as callback. This approach conveys the idea that event-based algorithm are passive, and event-triggered.

the code

First, let us create the directory structure for the code project. Create an empty directory wherever you want, called whatever you like. It will be referred to as dvs_event_stream_printer in this tutorial. Then, create empty files and directories to match the following tree:

dvs_event_stream_printer
├── source
|   └── dvs_event_stream_printer.cpp
├── third_party
└── premake4.lua

third_party is a directory, whereas dvs_event_stream_printer.cpp and premake4.lua are files.

The first tutorial uses only the Sepia library. To install its latest version as a local dependency, open a terminal, go to the dvs_event_stream_printer directory and run:

git init
cd third_party
git submodule add https://github.com/neuromorphic-paris/sepia.git
git submodule update --init --recursive

Write the following content to dvs_event_stream_printer/premake4.lua:

solution 'dvs_event_stream_printer'
    configurations {'release', 'debug'}
    location 'build'
    project 'dvs_event_stream_printer'
        kind 'ConsoleApp'
        language 'C++'
        location 'build'
        files {'source/*.cpp'}
        buildoptions {'-std=c++11'}
        linkoptions {'-std=c++11'}
        defines {'SEPIA_COMPILER_WORKING_DIRECTORY="' .. project().location .. '"'}
        configuration 'release'
            targetdir 'build/release'
            defines {'NDEBUG'}
            flags {'OptimizeSpeed'}
        configuration 'debug'
            targetdir 'build/debug'
            defines {'DEBUG'}
            flags {'Symbols'}
        configuration 'linux'
            links {'pthread'}

You can find details on premake's role in premake workflows.

The first tutorial's goal is to write an application which reads an Event Stream file, prints + for light increase (ON) events and - for light decrease (OFF) events. Create the event handling function, which will be responsible for printing. Write the following content to dvs_event_stream_printer/source/dvs_event_stream_printer.cpp:

#include "../third_party/sepia/source/sepia.hpp" // include the Sepia library
#include <iostream> // required to use std::cout for printing 

/// handle_event is an event callback for an Event Stream observable.
void handle_event(sepia::dvs_event dvs_event) {
    if (dvs_event.is_increase) {
        std::cout << "+";
    } else {
        std::cout << "-";
    }
    std::cout.flush();
}

handle_event takes a single parameter - a sepia::dvs_event object - and returns nothing. This is the signature expected by Sepia. sepia::dvs_event has the following definition:

namespace sepia {
    struct dvs_event {
        /// t represents the event's timestamp. 
        /// By convention, timestamps are in microseconds and encoded on 64 bits unsigned integers.
        uint64_t t;

        /// x represents the coordinate of the event on the sensor grid alongside the horizontal axis.
        /// x is 0 on the left, and increases from left to right.
        /// By convention, spatial coordinates are in pixels, encoded on 16 bits unsigned integers.
        uint16_t x;

        /// y represents the coordinate of the event on the sensor grid alongside the vertical axis.
        /// y is 0 on the bottom, and increases from bottom to top.
        uint16_t y;

        /// is_increase is false if the light is decreasing.
        bool is_increase;
    }
}

The Sepia library provides the sepia::join_observable<sepia::type::dvs> which reads an Event Stream file and calls the provided callback function for each event. The name observable comes from the observer pattern. The function takes two arguments: an input stream and a callback function. You can use the example file dvs_event_stream_printer/third_party/sepia/third_party/event_stream/examples/dvs.es, and sepia::filename_to_ifstream to create a stream from the filename. Write the following content to dvs_event_stream_printer/source/dvs_event_stream_printer.cpp:

#include "../third_party/sepia/source/sepia.hpp"
#include <iostream>

/// handle_event is an event callback for a DVS Event Stream observable.
void handle_event(sepia::dvs_event dvs_event) {
    if (dvs_event.is_increase) {
        std::cout << "+";
    } else {
        std::cout << "-";
    }
    std::cout.flush();
}

int main() {
    sepia::join_observable<sepia::type::dvs>(
        sepia::filename_to_ifstream("../../third_party/sepia/third_party/event_stream/examples/dvs.es"), 
        handle_event);
    return 0;
}

To compile the code, run from the dvs_event_stream_printer directory:

premake4 gmake
cd build
make

To execute the program, run:

cd release
./dvs_event_stream_printer

Windows users must run premake4 vs2010 instead, and open the generated solution with Visual Studio.

The filename given to sepia::join_observable, if not absolute, is resolved relatively to the current working directory. If you plan on calling the executable from various locations, it can quickly become tedious. Sepia provides utilities to resolve filenames relatively to source files. Write the following content to dvs_event_stream_printer/source/dvs_event_stream_printer.cpp:

#include "../third_party/sepia/source/sepia.hpp"
#include <iostream>

/// filename points to the Event Stream file to read.
/// The use of the '__FILE__' macro and the 'sepia::dirname' and 'sepia::join' functions
/// allows to resolve the path relatively to the source file, rather than to the compiled executable.
const auto filename = sepia::join(
    {sepia::dirname(SEPIA_DIRNAME), "third_party", "sepia", "third_party", "event_stream", "examples", "dvs.es"});

/// handle_event is an event callback for a DVS Event Stream observable.
void handle_event(sepia::dvs_event dvs_event) {
    if (dvs_event.is_increase) {
        std::cout << "+";
    } else {
        std::cout << "-";
    }
    std::cout.flush();
}

int main() {
    sepia::join_observable<sepia::type::dvs>(sepia::filename_to_ifstream(filename), handle_event);
    return 0;
}

To compile the code, run from the dvs_event_stream_printer/build directory:

make

Since no files were added to the project, running premake4 gmake again is not required. To execute the program, run:

release/dvs_event_stream_printer

Windows users must recompile within Visual Studio instead.

Congratulations! You completed the first tutorial, and are ready for the DVS event stream counter tutorial.