Event Output

This sample shows how to export event-list output from DELTA raw data. It uses a two-phase workflow: first save live sensor data as a .dvs file, then reopen the saved file with playback and convert the event output to CSV.

Back to Code Samples.

Example Overview

Step SDK API Purpose
1 scanDevices() / openDevice() Find and open a connected DELTA Series camera.
2 applySensorSetting() Apply the recommended sensor setting file.
3 startStream() Start live USB streaming.
4 startRawSaving() / saveRawBy*() Save raw sensor data as a .dvs file.
5 openFile() / startPlayback() Reopen the saved .dvs file for offline conversion.
6 setFrameGeneration() Choose how events are grouped into generated frames.
7 setOutputType() Select ClassicEvents or CompactEvents.
8 getClassicEvents() / getCompactEvents() Read event-list output into caller-owned buffers.
9 setReadOverCallback() Detect when playback reaches the end of the file.

Expected Result

When the sample runs successfully, an event_output folder is created under the save directory. The folder contains the raw data captured during Phase 1 and the CSV file generated during Phase 2.

event_output/
├─ raw_data/
│  └─ year-month-day-hour-minute-second-DELTA.dvs
└─ Classic.csv or Compact.csv

The raw_data folder stores the .dvs raw data file recorded from the camera. The CSV file stores only the positions where events occurred.

If Classic is selected, the output file is Classic.csv.

timestamp_us,x,y,polarity

Each row contains one event with its timestamp, x position, y position, and polarity.

If Compact is selected, the output file is Compact.csv.

frame_timestamp_us,x,y,polarity

Each row contains one event with the timestamp of the generated frame, x position, y position, and polarity.

Note

Classic and Compact may look similar in CSV form, but the timestamp meaning is different. Classic stores a timestamp for each event, while Compact uses one frame timestamp generated from the sensor frame-end packet. Because DELTA sensors do not generate an independent timestamp for every event, Compact is usually the more efficient format for saving and transferring event data.

Code Walkthrough

Edit the User Control Panel

The USER CONTROL PANEL section is the only part you normally need to edit. Each option group has one active line and one or more commented alternatives. To use a different mode, comment out the current active line and uncomment the mode you want to use.

Select a Camera

const std::int32_t selectDeviceIndex = 0;

selectDeviceIndex = 0 selects the first detected camera.

Select an Event Format

Only one selected_eventformat line should be active at a time.

(a) Classic

const std::string selected_eventformat = "Classic";

Classic exports one timestamp per event. The CSV header is:

timestamp_us,x,y,polarity

(b) Compact

// const std::string selected_eventformat = "Compact";

Compact exports x, y, and polarity per event, with one frame timestamp shared by all events in the generated frame. The CSV header is:

frame_timestamp_us,x,y,polarity

Note

ClassicEvent contains x, y, polarity, and per-event timestamp. CompactEvent contains only x, y, and polarity; the timestamp is stored once in CompactEventList::frameTimestamp.

Select a Frame Generation Mode

Frame generation controls how raw event data is grouped before event lists are read during conversion. Only one selected_framemode line should be active.

const std::string selected_framemode = "FrameEndBased";
const std::int32_t frameEndCount = 1;

// const std::string selected_framemode = "TimeBased";
const std::int32_t framePeriodMs = 16;

// const std::string selected_framemode = "EventCountBased";
const std::int32_t eventsPerFrame = 50000;

FrameEndBased follows the recorded frame-end markers. TimeBased groups events by fixed time intervals, and EventCountBased groups events by event count.

Select a Raw Saving Mode

The raw saving mode controls how much live data is recorded in Phase 1.

const std::string selected_savemode = "Continuous";

// const std::string selected_savemode = "Time";
const std::int32_t durationMs = 2000;

// const std::string selected_savemode = "FrameCount";
const std::uint64_t targetFrames = 100;

// const std::string selected_savemode = "EventCount";
const std::uint64_t targetEvents = 1000000;

Continuous starts saving when you press s and stops when you press q. Other modes call saveRawByTime(), saveRawByFrameCount(), or saveRawByEventCount().

Select Output Directories

const std::filesystem::path rawSaveDirectory =
    std::filesystem::path(DELTA_SAMPLE_ROOT) / "saved_data" / "event_output" / "raw_data";
const std::filesystem::path csvSaveDirectory =
    std::filesystem::path(DELTA_SAMPLE_ROOT) / "saved_data" / "event_output";

Raw .dvs files are written to rawSaveDirectory. CSV files are written to csvSaveDirectory.

Phase 1: Capture Raw Data

The example starts live streaming and saves raw data according to the selected save mode.

status = delta::startStream(cameraHandle);
if (selected_savemode == "Continuous") {
    status = delta::startRawSaving(cameraHandle, rawSaveDirectoryString.c_str());

    if (status == delta::Status::Success) {
        // Wait for the user to press 'q'.
        status = delta::stopRawSaving(cameraHandle);
    }
}
else if (selected_savemode == "Time") {
    status = delta::saveRawByTime(cameraHandle, rawSaveDirectoryString.c_str(), durationMs);
}

After saving completes, the example locates the newly created .dvs file and uses it as the input for Phase 2.

Phase 2: Select Event Output

The saved raw file is reopened through the playback API. The selected frame generation setting is applied before reading events.

status = delta::openFile(rawFileString.c_str(), &fileHandle);
status = delta::setPlaybackMode(fileHandle, delta::PlaybackMode::Once);
status = delta::setFrameGeneration(fileHandle, frameGeneration);

The selected event format controls the processing output type.

if (selected_eventformat == "Classic") {
    status = delta::setOutputType(fileHandle, delta::OutputType::ClassicEvents);
}
else if (selected_eventformat == "Compact") {
    status = delta::setOutputType(fileHandle, delta::OutputType::CompactEvents);
}

Prepare Event Buffers

Event outputs use caller-owned buffers. Allocate the event array and attach it to the corresponding output structure before polling.

std::vector<delta::ClassicEvent> classicEventBuffer(DELTA_DEFAULT_EVENT_CAPACITY);
delta::ClassicEventList classicEventList{};
classicEventList.events        = classicEventBuffer.data();
classicEventList.eventCapacity = DELTA_DEFAULT_EVENT_CAPACITY;
std::vector<delta::CompactEvent> compactEventBuffer(DELTA_DEFAULT_EVENT_CAPACITY);
delta::CompactEventList compactEventList{};
compactEventList.events        = compactEventBuffer.data();
compactEventList.eventCapacity = DELTA_DEFAULT_EVENT_CAPACITY;

Detect End of Playback

PlaybackMode::Once fires this callback once the file has been fully read. A few more frames can still be in flight in the processing pipeline at that point, so the sample keeps polling for a short grace period afterward instead of stopping immediately.

std::atomic<bool> playbackDone{false};

status = delta::setReadOverCallback(
    fileHandle,
    [](void* userData) { static_cast<std::atomic<bool>*>(userData)->store(true); },
    &playbackDone
);

Once playbackDone is set, the polling loop allows up to 100 consecutive empty polls to drain any remaining frames before it stops.

constexpr int kDrainRetriesAfterEnd = 100;
int drainRetries = 0;

while (true) {
    const auto result = pollAndWrite(fileHandle, csvFile);

    if (result.frameReady) {
        drainRetries = 0;
    }
    else if (playbackDone.load()) {
        if (++drainRetries >= kDrainRetriesAfterEnd) { break; }
    }
}

Poll Events and Write CSV

For Classic output, each event row contains per-event timestamp, x, y, and polarity.

delta::getClassicEvents(handle, &classicEventList, 0);

for (std::int32_t i = 0; i < classicEventList.eventCount; ++i) {
    const auto& e = classicEventList.events[i];
    csvFile << e.timestamp << ',' << e.x << ',' << e.y << ','
            << static_cast<int>(e.polarity) << '\n';
}

For Compact output, each event row uses the frame timestamp from CompactEventList.

delta::getCompactEvents(handle, &compactEventList, 0);

for (std::int32_t i = 0; i < compactEventList.eventCount; ++i) {
    const auto& e = compactEventList.events[i];
    csvFile << compactEventList.frameTimestamp << ',' << e.x << ',' << e.y << ','
            << static_cast<int>(e.polarity) << '\n';
}

polarity uses the sensor convention:

Value Meaning
0 ON event
1 OFF event

Stop Playback

delta::stopPlayback(fileHandle);
delta::closeFile(fileHandle);