Note

This C++ sample has a corresponding Python sample.

Synced Camera Streams Slicer using C++

The Synced Camera Streams Slicer sample demonstrates how to utilize the SyncedCameraStreamsSlicer class from the Metavision SDK to divide and process event streams generated by a master and slave Camera instances. This can be done based on either a fixed number of events or a specified time duration.

Expected Output

Synced Camera Streams Slicer sample output

How to start

You can directly execute pre-compiled binary installed with Metavision SDK or compile the source code as described in this tutorial.

To start the sample based on recorded data, provide the full path to synchronized RAW files (here, we use files from our Sample Recordings):

Linux

./metavision_synced_camera_streams_slicer -i courtyard_walk_stereo.master.hdf5 courtyard_walk_stereo.slave.hdf5

Windows

metavision_synced_camera_streams_slicer.exe -i courtyard_walk_stereo.master.hdf5 courtyard_walk_stereo.slave.hdf5

This sample cannot be run with the first available cameras as it needs to know which camera is the master and which are the slaves. To start the sample based on the live stream from your cameras, run:

Linux

./metavision_synced_camera_streams_slicer -s SN_MASTER SN_SLAVE_1 ... SN_SLAVE_N

Windows

metavision_synced_camera_streams_slicer.exe -s SN_MASTER SN_SLAVE_1 ... SN_SLAVE_N

To check for additional options:

Linux

./metavision_synced_camera_streams_slicer -h

Windows

metavision_synced_camera_streams_slicer.exe -h

Code Overview

First we start by building the camera system meaning the master and slaves cameras. To do so, we rely on the SyncedCameraSystemBuilder helper class. We pass all the provided command line arguments to it and then call the build() method to create the camera system. Internally, the builder will determine if a live or offline camera system should be created based on the provided arguments and build and configure all the camera instances accordingly:

Metavision::SyncedCameraSystemBuilder builder;

const auto get_settings_file_path = [&config](const std::string &serial) -> std::optional<std::filesystem::path> {
    namespace fs = std::filesystem;

    const auto settings_file_path = fs::path(config.settings_dir) / (serial + ".json");
    if (!fs::exists(settings_file_path)) {
        return std::nullopt;
    }

    return settings_file_path;
};

for (const auto &serial_number : config.serial_numbers) {
    builder.add_live_camera_parameters({serial_number, {}, get_settings_file_path(serial_number)});
}

builder.set_record(config.record);
builder.set_record_dir(config.record_dir);

for (const auto &path : config.record_paths) {
    builder.add_record_path(path);
}

builder.set_file_config_hints(Metavision::FileConfigHints{}.real_time_playback(config.real_time_playback));

auto &&[master, slaves] = builder.build();

Next, we create a SyncedCameraStreamsSlicer instance by transferring the ownership of the camera system to it and setting the desired slicing mode:

Metavision::SyncedCameraStreamsSlicer slicer(std::move(master), std::move(slaves), config.slicing_condition);

Finally, we start slicing the synchronized event streams by iterating over the slicer. The information about the slices is printed to the console and the slice of each camera is displayed in a separate window:

for (const auto &slice : slicer) {
    for (auto &frame : slice_frames) {
        frame.setTo(0);
    }

    MV_LOG_INFO() << "MASTER ts: " << slice.t << " " << slice.n_events << " [" << slice.master_events->front().t
                  << ", " << slice.master_events->back().t << "]";

    Metavision::BaseFrameGenerationAlgorithm::generate_frame_from_events(
        slice.master_events->cbegin(), slice.master_events->cend(), slice_frames[0]);

    cv::imshow("Master slice", slice_frames[0]);

    for (size_t i = 0; i < slice.slave_events.size(); ++i) {
        const auto &slave_slice = slice.slave_events[i];

        MV_LOG_INFO() << "SLAVE " << i + 1 << " ts: " << slice.t << " " << slave_slice->size() << " ["
                      << slave_slice->front().t << ", " << slave_slice->back().t << "]";

        Metavision::BaseFrameGenerationAlgorithm::generate_frame_from_events(
            slave_slice->cbegin(), slave_slice->cend(), slice_frames[i + 1]);

        cv::imshow("Slave slice " + std::to_string(i + 1), slice_frames[i + 1]);
    }

    const auto key = cv::waitKey(1);
    if (key == 'q')
        break;
}