Writing camera data

The dv-processing library provides a convenient way of recording camera data into AEDAT4 files. These files can record multiple data-types and can be read using existing tools in library. DV GUI software also provides ways to playback these recordings.

Writing a file

Files can be written using the dv::io::MonoCameraWriter class. Since the writer supports multiple data type configuration, it needs to know apriori what data streams are needed for writing. They are declared using a dv::io::MonoCameraWriter::Config structure and is passed to the constructor of the writer class to correctly initialise the required streams in the file header.

Defining output streams from an existing camera reader handle

As mentioned in the previous paragraph, number of output streams and their data-types needs to be known for the writer. Output streams definition can be generated by an input camera capture. In this case, the writer class will inspect what streams the capture can output and will create output streams for them. Following is an example on how to initialize a writer with a camera capture class:

 1#include <dv-processing/io/camera_capture.hpp>
 2#include <dv-processing/io/mono_camera_writer.hpp>
 3
 4int main() {
 5    // Open any camera
 6    dv::io::CameraCapture capture;
 7
 8    // Create the writer instance, writer will inspect the capture capabilities and create output
 9    // streams for all available data streams from the capture instance.
10    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", capture);
11
12    // Print which streams were configured
13    std::cout << std::boolalpha;
14    std::cout << "Is event stream available? " << writer.isEventStreamConfigured() << std::endl;
15    std::cout << "Is frame stream available? " << writer.isFrameStreamConfigured() << std::endl;
16    std::cout << "Is imu stream available? " << writer.isImuStreamConfigured() << std::endl;
17    std::cout << "Is trigger stream available? " << writer.isTriggerStreamConfigured() << std::endl;
18
19    return 0;
20}

Defining output streams manually

Output streams definition can be manually defined in the config structure, following is a sample that manually defines event, frame, IMU, and trigger data-type streams and creates a writer instance:

 1#include <dv-processing/io/mono_camera_writer.hpp>
 2
 3int main() {
 4    dv::io::MonoCameraWriter::Config config("DVXplorer_sample");
 5
 6    // Sample VGA resolution, same as the DVXplorer camera
 7    const cv::Size resolution(640, 480);
 8
 9    // Add an event stream with a resolution
10    config.addEventStream(resolution);
11
12    // Add frame stream with a resolution
13    config.addFrameStream(resolution);
14
15    // Add IMU stream
16    config.addImuStream();
17
18    // Add trigger stream
19    config.addTriggerStream();
20
21    // Create the writer instance with the configuration structure
22    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", config);
23
24    // Print which streams were configured
25    std::cout << std::boolalpha;
26    std::cout << "Is event stream available? " << writer.isEventStreamConfigured() << std::endl;
27    std::cout << "Is frame stream available? " << writer.isFrameStreamConfigured() << std::endl;
28    std::cout << "Is imu stream available? " << writer.isImuStreamConfigured() << std::endl;
29    std::cout << "Is trigger stream available? " << writer.isTriggerStreamConfigured() << std::endl;
30
31    return 0;
32}

Defining output streams using predefined templates

The dv::io::MonoCameraWriter class provides some templates for configuration to reduce the code lines. Following is a sample how to use a named method that generates a config for writing data:

 1#include <dv-processing/io/mono_camera_writer.hpp>
 2
 3int main() {
 4    // Create a DVS config - events, imu, and triggers are going to be enabled
 5    const auto config = dv::io::MonoCameraWriter::DVSConfig("DVXplorer_sample", cv::Size(640, 480));
 6
 7    // Create the writer instance with the configuration structure
 8    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", config);
 9
10    // Print which streams were configured
11    std::cout << std::boolalpha;
12    std::cout << "Is event stream available? " << writer.isEventStreamConfigured() << std::endl;
13    std::cout << "Is frame stream available? " << writer.isFrameStreamConfigured() << std::endl;
14    std::cout << "Is imu stream available? " << writer.isImuStreamConfigured() << std::endl;
15    std::cout << "Is trigger stream available? " << writer.isTriggerStreamConfigured() << std::endl;
16
17    return 0;
18}

A list of available output stream configurations available under the dv::io::MonoCameraWriter class:

  • EventOnlyConfig - a single output stream “events”

  • FrameOnlyConfig - a single output stream “frames”

  • DVSConfig - three output streams: “events”, “imu”, and “trigger”

  • DAVISConfig - four output streams: “events”, “frames”, “imu”, and “trigger”

Writing data to the file

The writer instance can be used to write data with the output streams configured. Following sections provide sample code on how to write individual data types using the writer class.

Writing events

The following sample shows an example on how to write event data using the dv::io::MonoCameraWriter class:

 1#include <dv-processing/data/generate.hpp>
 2#include <dv-processing/io/mono_camera_writer.hpp>
 3
 4int main() {
 5    // Sample VGA resolution, same as the DVXplorer camera
 6    const cv::Size resolution(640, 480);
 7
 8    // Event only configuration
 9    const auto config = dv::io::MonoCameraWriter::EventOnlyConfig("DVXplorer_sample", resolution);
10
11    // Create the writer instance, it will only have a single event output stream.
12    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", config);
13
14    // Write 100 packet of event data
15    for (int i = 0; i < 100; i++) {
16        // EventStore requires strictly monotonically increasing data, generate
17        // a timestamp from the iteration counter value
18        const int64_t timestamp = i * 10000;
19
20        // Generate sample event batch
21        dv::EventStore events = dv::data::generate::dvLogoAsEvents(timestamp, resolution);
22
23        // Write the packet using the writer, the data is not going be written at the exact
24        // time of the call to this function, it is only guaranteed to be written after
25        // the writer instance is destroyed (destructor has completed)
26        writer.writeEvents(events);
27    }
28
29    return 0;
30}

Writing frames

The following sample shows an example on how to write image frames using the dv::io::MonoCameraWriter class:

 1#include <dv-processing/io/mono_camera_writer.hpp>
 2
 3int main() {
 4    // Sample VGA resolution, same as the DVXplorer camera
 5    const cv::Size resolution(640, 480);
 6
 7    // Frame only configuration
 8    const auto config = dv::io::MonoCameraWriter::FrameOnlyConfig("DVXplorer_sample", resolution);
 9
10    // Create the writer instance, it will only have a single frame output stream.
11    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", config);
12
13    // Write 10 image frames
14    for (int i = 0; i < 10; i++) {
15        // Initialize a white image
16        cv::Mat image(resolution, CV_8UC3, cv::Scalar(255, 255, 255));
17
18        // Generate some monotonically increasing timestamp
19        const int64_t timestamp = i * 1000;
20
21        // Encapsulate the image in a frame that has a timestamp, this does not copy the pixel data
22        dv::Frame frame(timestamp, image);
23
24        // Write the frame
25        writer.writeFrame(frame);
26    }
27
28    return 0;
29}

Writing IMU data

The following sample shows an example on how to write IMU measurement data using the dv::io::MonoCameraWriter class:

 1#include <dv-processing/data/generate.hpp>
 2#include <dv-processing/io/mono_camera_writer.hpp>
 3
 4int main() {
 5    // IMU data only configuration
 6    auto config = dv::io::MonoCameraWriter::Config("DVXplorer_sample");
 7    config.addImuStream();
 8
 9    // Create the writer instance, it will only have a single IMU data output stream
10    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", config);
11
12    // Write 100 IMU measurements
13    for (int i = 0; i < 100; i++) {
14        // Generate some monotonically increasing timestamp
15        const int64_t timestamp = i * 1000;
16
17        // Some sample measurements - no rotation on gyro and a one-G gravity on Y axis of accelerometer
18        const dv::IMU measurement = dv::data::generate::levelImuWithNoise(timestamp);
19
20        // Write the measurement
21        writer.writeImu(measurement);
22    }
23
24    return 0;
25}

Writing trigger data

The following sample shows an example on how to write triggers using the dv::io::MonoCameraWriter class:

 1#include <dv-processing/io/mono_camera_writer.hpp>
 2
 3int main() {
 4    // Trigger data only configuration
 5    auto config = dv::io::MonoCameraWriter::Config("DVXplorer_sample");
 6    config.addTriggerStream();
 7
 8    // Create the writer instance, it will only have a single trigger output stream
 9    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", config);
10
11    // Write 100 triggers
12    for (int i = 0; i < 100; i++) {
13        // Generate some monotonically increasing timestamp
14        const int64_t timestamp = i * 1000;
15
16        // Single trigger instance, let's say this is some signal from external source
17        dv::Trigger trigger(timestamp, dv::TriggerType::EXTERNAL_GENERATOR_RISING_EDGE);
18
19        // Write the trigger value
20        writer.writeTrigger(trigger);
21    }
22
23    return 0;
24}

[Advanced] Writing custom data types

The previous samples show how to use named functions for writing different data types into a file. C++ API provides templated methods to write any type of data. Below is sample that shows how to write data using the generic templated API:

Note

Since templated methods are only available in C++, the generic writing methods are only available in the C++ API.

 1#include <dv-processing/io/mono_camera_writer.hpp>
 2
 3int main() {
 4    // Create a stream named "keypoints" for a data type of TimedKeyPoint packet
 5    auto config = dv::io::MonoCameraWriter::Config("DVXplorer_sample");
 6
 7    // Define and contain a stream name in a variable
 8    const std::string streamName = "keypoints";
 9
10    // Add an output stream with a packet type and a defined stream name
11    config.addStream<dv::TimedKeyPointPacket>(streamName);
12
13    // Initialize the writer
14    dv::io::MonoCameraWriter writer("mono_writer_sample.aedat4", config);
15
16    // Let's create 10 packets of key-points
17    for (int i = 0; i < 10; i++) {
18        // Create a packet for writing
19        dv::TimedKeyPointPacket packet;
20
21        // Generate some monotonically increasing timestamp
22        const int64_t timestamp = i * 1000;
23
24        // 10 Let's generate 10 key-points for the packet
25        for (int y = 0; y < 10; y++) {
26            // Using emplace_back to directly allocate and insert the keypoint at the end of the vector
27            packet.elements.emplace_back(dv::Point2f(5.f, 5.f), 10.f, 3.f, 1.f, 0, -1, timestamp);
28        }
29
30        // Write the packet using, requires a stream name that is the same with the
31        writer.writePacket(packet, streamName);
32    }
33
34    return 0;
35}

Sample application - recording data from live camera

An application writing multiple types of data from a live camera can be found among the code samples in the source code repository of the dv-processing library: