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}
1import dv_processing as dv
2
3capture = dv.io.CameraCapture()
4
5writer = dv.io.MonoCameraWriter("mono_writer_sample.aedat4", capture)
6
7print(f"Is event stream available? {str(writer.isEventStreamConfigured())}")
8print(f"Is frame stream available? {str(writer.isFrameStreamConfigured())}")
9print(f"Is imu stream available? {str(writer.isImuStreamConfigured())}")
10print(f"Is trigger stream available? {str(writer.isTriggerStreamConfigured())}")
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}
1import dv_processing as dv
2
3config = dv.io.MonoCameraWriter.Config("DVXplorer_sample")
4
5# Define VGA resolution for this camera
6resolution = (640, 480)
7
8# Add an event stream with a resolution
9config.addEventStream(resolution)
10
11# Add frame stream with a resolution
12config.addFrameStream(resolution)
13
14# Add IMU stream
15config.addImuStream()
16
17# Add trigger stream
18config.addTriggerStream()
19
20# Create the writer instance with the configuration structure
21writer = dv.io.MonoCameraWriter("mono_writer_sample.aedat4", config)
22
23print(f"Is event stream available? {str(writer.isEventStreamConfigured())}")
24print(f"Is frame stream available? {str(writer.isFrameStreamConfigured())}")
25print(f"Is imu stream available? {str(writer.isImuStreamConfigured())}")
26print(f"Is trigger stream available? {str(writer.isTriggerStreamConfigured())}")
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}
1import dv_processing as dv
2
3# Create a DVS config - events, imu, and triggers are going to be enabled
4config = dv.io.MonoCameraWriter.DVSConfig("DVXplorer_sample", (640, 480))
5
6# Create the writer instance with the configuration structure
7writer = dv.io.MonoCameraWriter("mono_writer_sample.aedat4", config)
8
9# Print which streams were configured
10print(f"Is event stream available? {str(writer.isEventStreamConfigured())}")
11print(f"Is frame stream available? {str(writer.isFrameStreamConfigured())}")
12print(f"Is imu stream available? {str(writer.isImuStreamConfigured())}")
13print(f"Is trigger stream available? {str(writer.isTriggerStreamConfigured())}")
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}
1import dv_processing as dv
2
3# Sample VGA resolution, same as the DVXplorer camera
4resolution = (640, 480)
5
6# Event only configuration
7config = dv.io.MonoCameraWriter.EventOnlyConfig("DVXplorer_sample", resolution)
8
9# Create the writer instance, it will only have a single event output stream.
10writer = dv.io.MonoCameraWriter("mono_writer_sample.aedat4", config)
11
12# Write 100 packet of event data
13for i in range(100):
14 # EventStore requires strictly monotonically increasing data, generate
15 # a timestamp from the iteration counter value
16 timestamp = i * 1000
17
18 # Empty event store
19 events = dv.data.generate.dvLogoAsEvents(timestamp, resolution)
20
21 # Write the packet using the writer, the data is not going be written at the exact
22 # time of the call to this function, it is only guaranteed to be written after
23 # the writer instance is destroyed (destructor has completed)
24 writer.writeEvents(events)
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}
1import dv_processing as dv
2import numpy as np
3
4# Frame only configuration
5config = dv.io.MonoCameraWriter.FrameOnlyConfig("DVXplorer_sample", (640, 480))
6
7# Create the writer instance, it will only have a single frame output stream
8writer = dv.io.MonoCameraWriter("mono_writer_sample.aedat4", config)
9
10# Write 10 image frames
11for i in range(10):
12 # Initialize a white image
13 image = np.full((480, 640, 3), fill_value=255, dtype=np.uint8)
14
15 # Generate some monotonically increasing timestamp
16 timestamp = i * 1000
17
18 # Encapsulate the image in a frame that has a timestamp, this does not copy the pixel data
19 frame = dv.Frame(timestamp, image)
20
21 # Write the frame
22 writer.writeFrame(frame)
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}
1import dv_processing as dv
2
3# IMU data only configuration
4config = dv.io.MonoCameraWriter.Config("DVXplorer_sample")
5config.addImuStream()
6
7# Create the writer instance, it will only have a single IMU data output stream
8writer = dv.io.MonoCameraWriter("mono_writer_sample.aedat4", config)
9
10# Write 100 IMU measurements
11for i in range(100):
12 # Generate some monotonically increasing timestamp
13 timestamp = i * 1000
14
15 # Single IMU measurement instance
16 measurement = dv.data.generate.levelImuWithNoise(timestamp)
17
18 # Write the measurement
19 writer.writeImu(measurement)
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}
1import dv_processing as dv
2
3# Trigger data only configuration
4config = dv.io.MonoCameraWriter.Config("DVXplorer_sample")
5config.addTriggerStream()
6
7# Create the writer instance, it will only have a single trigger output stream
8writer = dv.io.MonoCameraWriter("mono_writer_sample.aedat4", config)
9
10# Write 100 triggers
11for i in range(100):
12 # Generate some monotonically increasing timestamp
13 timestamp = i * 1000
14
15 # Single trigger instance, let's say this is some signal from external source
16 trigger = dv.Trigger(timestamp, dv.TriggerType.EXTERNAL_GENERATOR_RISING_EDGE)
17
18 # Write the trigger value
19 writer.writeTrigger(trigger)
[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: