Event store
The dv-processing library provides an easy to use and an efficient data structure to store and manage incoming event
data from the camera - the dv::EventStore
class. It is implemented as a shallow, shared ownership data
structure which only holds pointers to actual memory locations containing event data. Similarly to how OpenCV handles
image data, the dv::EventStore
does not copy the data, instead it only holds pointers to received packets of
events and uses metadata of these packets to efficiently slice the data given time-intervals.
Create and store events
The following sample code shows how to create an empty event store and fill it with software generated events:
1#include <dv-processing/core/core.hpp>
2#include <dv-processing/core/utils.hpp>
3
4int main() {
5 // Initialize an empty store
6 dv::EventStore store;
7
8 // Get the current timestamp
9 const int64_t timestamp = dv::now();
10
11 // Add some events into the event store
12 // This allocates and inserts events at the back, the function arguments are:
13 // timestamp, x, y, polarity
14 store.emplace_back(timestamp, 0, 0, true);
15 store.emplace_back(timestamp + 1000, 1, 1, false);
16 store.emplace_back(timestamp + 2000, 2, 2, false);
17 store.emplace_back(timestamp + 3000, 3, 3, true);
18
19 // Perform time-based slicing of event store, the output event store "sliced" will contain
20 // the second and third events from above. The end timestamp (second argument) is 2001, since start
21 // timestamp (first argument) is inclusive and timestamp is exclusive, so 1 is added.
22 const dv::EventStore sliced = store.sliceTime(timestamp + 1000, timestamp + 2001);
23
24 // This should print two events
25 for (const dv::Event &ev : sliced) {
26 std::cout << fmt::format("Sliced event [{}, {}, {}, {}]", ev.timestamp(), ev.x(), ev.y(), ev.polarity())
27 << std::endl;
28 }
29
30 return 0;
31}
1import dv_processing as dv
2
3# Initialize an empty store
4store = dv.EventStore()
5
6# Get the current timestamp
7timestamp = dv.now()
8
9# Add some events into the event store
10# This allocates and inserts events at the back, the function arguments are:
11# timestamp, x, y, polarity
12store.push_back(timestamp, 0, 0, True)
13store.push_back(timestamp + 1000, 1, 1, False)
14store.push_back(timestamp + 2000, 2, 2, False)
15store.push_back(timestamp + 3000, 3, 3, True)
16
17# Perform time-based slicing of event store, the output event store "sliced" will contain
18# the second and third events from above. The end timestamp (second argument) is 2001, since start
19# timestamp (first argument) is inclusive and timestamp is exclusive, so 1 is added.
20sliced = store.sliceTime(timestamp + 1000, timestamp + 2001)
21
22# This should print two events
23for ev in store:
24 print(f"Sliced event [{ev.timestamp()}, {ev.x()}, {ev.y()}, {ev.polarity()}]")
Slicing Event Stores
dv::EventStore
can be sliced by time or by the number of events. Slicing is a shallow operation, it does not
copy any data. Slicing returns a new dv::EventStore
that only references the data requested. The original
store is unaffected.
Slicing by time
dv::EventStore
implements event data slicing by time in an efficient way. By reusing the underlying packet
structure, the slicing is performed with O(log n)
time complexity. The following sample shows the usage of time based
slicing functions:
1#include <dv-processing/core/core.hpp>
2#include <dv-processing/data/generate.hpp>
3
4int main() {
5 using namespace std::chrono_literals;
6
7 // Generate 10 events with time range [10000; 20000]
8 const auto store = dv::data::generate::uniformEventsWithinTimeRange(10000, 10ms, cv::Size(100, 100), 10);
9
10 // Get all events with timestamp above 12500, it will be 13000 and up
11 dv::EventStore eventsAfterTimestamp = store.sliceTime(12'500);
12
13 // Print the timestamp ranges
14 std::cout << "1. " << eventsAfterTimestamp << std::endl;
15
16 // Slice event within time range [12000; 16000); the end time is exclusive
17 const dv::EventStore eventsInRange = store.sliceTime(12'000, 16'000);
18
19 // Print the timestamp ranges; It will print that range is [12000; 15000] since end time is exclusive and
20 // event at timestamp 16000 is not going to be included.
21 std::cout << "2. " << eventsInRange << std::endl;
22
23 return 0;
24}
1import dv_processing as dv
2from datetime import timedelta
3
4# Generate 10 events with time range [10000; 20000]
5store = dv.data.generate.uniformEventsWithinTimeRange(10000, timedelta(milliseconds=10), (100, 100), 10)
6
7# Get all events with timestamp above 12500, it will be 13000 and up
8eventsAfterTimestamp = store.sliceTime(12500)
9
10# Print the timestamp ranges
11print(f"1. {eventsAfterTimestamp}")
12
13# Slice event within time range [12000; 16000); the end time is exclusive
14eventsInRange = store.sliceTime(12000, 16000)
15
16# Print the timestamp ranges; It will print that range is [12000; 15000] since end time is exclusive and
17# event at timestamp 16000 is not going to be included.
18print(f"2. {eventsInRange}")
Slicing by number of events
dv::EventStore
provides slicing capabilities by an index and number of events. The first argument to the
dv::EventStore::slice()
method is a starting index from which the output slice starts, the second optional
argument is the number of events to be sliced. If the number argument is not provided, the slice will contain all events
from given index.
Following sample shows how to slice an event store within given indices of the underlying events:
1#include <dv-processing/core/core.hpp>
2#include <dv-processing/data/generate.hpp>
3
4int main() {
5 using namespace std::chrono_literals;
6
7 // Add 10 event with timestamps in range [10000; 20000]
8 const auto store = dv::data::generate::uniformEventsWithinTimeRange(10000, 10ms, cv::Size(100, 100), 10);
9
10 // Get all events beyond and including index 5
11 dv::EventStore eventsAfterIndex = store.slice(5);
12 std::cout << "1. " << eventsAfterIndex << std::endl;
13
14 // Get 3 events starting with index 2
15 dv::EventStore eventsInRange = store.slice(2, 3);
16 std::cout << "2. " << eventsInRange << std::endl;
17
18 // Use sliceBack to retrieve event from the end; this call will retrieve last 3 events
19 dv::EventStore lastEvents = store.sliceBack(3);
20 std::cout << "3. " << lastEvents << std::endl;
21
22 return 0;
23}
1import dv_processing as dv
2from datetime import timedelta
3
4# Generate 10 events with time range [10000; 20000]
5store = dv.data.generate.uniformEventsWithinTimeRange(10000, timedelta(milliseconds=10), (100, 100), 10)
6
7# Get all events beyond and including index 5
8events_after_index = store.slice(5)
9print(f"1. {events_after_index}")
10
11# Get 3 events starting with index 2
12events_in_range = store.slice(2, 3)
13print(f"2. {events_in_range}")
14
15# Use sliceBack to retrieve event from the end; this call will retrieve last 3 events
16last_events = store.sliceBack(3)
17print(f"3. {last_events}")
Combining multiple EventStores
Multiple instances of dv::EventStore
can be added together to have a single object to access and manage it.
Since dv::EventStore
uses pointers to underlying event packet data, combining does not involve any deep data
copies, the implementation takes over shared memory ownership instead. Following sample show how to efficiently combine
multiple event stores into one:
1#include <dv-processing/core/core.hpp>
2#include <dv-processing/data/generate.hpp>
3
4int main() {
5 using namespace std::chrono_literals;
6
7 // Generate 10 events with timestamps in range [10000; 20000]
8 const auto store1 = dv::data::generate::uniformEventsWithinTimeRange(10000, 10ms, cv::Size(100, 100), 10);
9
10 // Generate second event store with 10 events with timestamps in range [20000; 29000] to the second store
11 const auto store2 = dv::data::generate::uniformEventsWithinTimeRange(20000, 10ms, cv::Size(100, 100), 10);
12
13 // Final event store which will contain all events
14 dv::EventStore finalStore;
15
16 // Add the events into the final store; this operation is shallow, so no data copies
17 // are performed, but the underlying data has shared ownership between all stores
18 finalStore.add(store1);
19 finalStore.add(store2);
20
21 // Print specific information on what we contain in the event store
22 std::cout << finalStore << std::endl;
23
24 return 0;
25}
1import dv_processing as dv
2from datetime import timedelta
3
4# Generate 10 events with timestamps in range [10000; 20000]
5store1 = dv.data.generate.uniformEventsWithinTimeRange(10000, timedelta(milliseconds=10), (100, 100), 10)
6
7# Generate second event store with 10 events with timestamps in range [20000; 29000] to the second store
8store2 = dv.data.generate.uniformEventsWithinTimeRange(20000, timedelta(milliseconds=10), (100, 100), 10)
9
10# Final event store which will contain all events
11final_store = dv.EventStore()
12
13# Add the events into the final store; this operation is shallow, so no data copies
14# are performed, but the underlying data has shared ownership between all stores
15final_store.add(store1)
16final_store.add(store2)
17
18# Print specific information on what we contain in the final event store
19print(f"{final_store}")