Filtering events

Event stream coming from a camera can contain noise, which is caused by analog electrical circuitry used to compare brightness on each pixel. The dv-processing library provides algorithms that filter noise efficiently on an event stream. Additionally, filtering can be used to subsample events in region of interest, filter by polarity, apply masks to events. This tutorial covers available filter implementations and how to use them efficiently.

Implementation of filters

The library provides two main types of filters - noise and subsampling. Subsampling filters include polarity filters, region of interest, mask filters. The library provides two algorithms for filtering noise that can be found under namespace dv::noise. Below is a class hierarchy diagram for available noise filters:

_images/filter_class_diagram.png

Available filters and their hierarchy in the library.

All filter have a common programming pattern:

  • Events are added to the filter instance using overloaded dv::EventFilterBase::accept() method.

  • Input events are filtered and returned by calling overloaded dv::EventFilterBase::generateEvents() method.

  • Internally events are filtered by calling a filter retain() method on each event and discarding events if a false is returned.

Noise filtering

This chapter describes the available event noise filters in the library.

Background activity noise filter

dv::noise::BackgroundActivityNoiseFilter - events are filtered based on short-term local neighborhood activity. If an event is “supported” by another event registered at local pixel neighborhood, that event is not considered noise and is added to the output events.

Following sample code shows the usage of the dv::noise::BackgroundActivityNoiseFilter to filter noise:

 1#include <dv-processing/data/generate.hpp>
 2#include <dv-processing/noise/background_activity_noise_filter.hpp>
 3#include <dv-processing/visualization/event_visualizer.hpp>
 4
 5#include <opencv2/highgui.hpp>
 6
 7int main() {
 8    using namespace std::chrono_literals;
 9
10    // Hardcoded VGA resolution
11    const cv::Size resolution(640, 480);
12
13    // Initializing input events with uniformly distributed events which represent noise
14    dv::EventStore events = dv::data::generate::uniformEventsWithinTimeRange(0, 10ms, resolution, 1000);
15
16    // Adding additional data for drawing, this will give an idea whether the filter removes actual signal events
17    events.add(dv::data::generate::dvLogoAsEvents(10000, resolution));
18
19    // Initialize a background activity noise filter with 1-millisecond activity period
20    dv::noise::BackgroundActivityNoiseFilter filter(resolution, 1ms);
21
22    // Pass events to the filter
23    filter.accept(events);
24
25    // Call generate events to apply the noise filter
26    const dv::EventStore filtered = filter.generateEvents();
27
28    // Print out the reduction factor, which indicates the percentage of discarded events
29    std::cout << "Filter reduced number of events by a factor of " << filter.getReductionFactor() << std::endl;
30
31    // Use a visualizer instance to preview the events
32    dv::visualization::EventVisualizer visualizer(resolution);
33
34    // Generate preview images of data input and output
35    const cv::Mat input  = visualizer.generateImage(events);
36    const cv::Mat output = visualizer.generateImage(filtered);
37
38    // Concatenate the images into a single image for preview
39    cv::Mat preview;
40    cv::hconcat(input, output, preview);
41
42    // Display the input and output images
43    cv::namedWindow("preview", cv::WINDOW_NORMAL);
44    cv::imshow("preview", preview);
45    cv::waitKey();
46
47    return 0;
48}

The sample code outputs such images:

_images/bg_activity_filter.jpg

Output of the sample use of background activity filter. Left is a preview of input events, right is a preview of filtered events.

Note

The image above that the filter reduces the amount of speckles on white area, but maintains the logo preview, those events are not filtered out.

Fast decay noise filter

dv::noise::FastDecayNoiseFilter - events are filtered based on lower resolution fast-decaying representation of events. Events contribute to a low-resolution accumulated image with a fast decay, which also represents local activity. Unlike the BackgroundActivityNoiseFilter, this filter uses decay instead of a hard time threshold, although the approach is very similar - an event needs to be supported by another event in a local pixel neighborhood. This filter has a lower memory footprint since the neighborhood is represented in a low resolution accumulated image.

Following sample code shows the usage of the dv::noise::FastDecayNoiseFilter to filter noise:

 1#include <dv-processing/data/generate.hpp>
 2#include <dv-processing/noise/fast_decay_noise_filter.hpp>
 3#include <dv-processing/visualization/event_visualizer.hpp>
 4
 5#include <opencv2/highgui.hpp>
 6
 7int main() {
 8    using namespace std::chrono_literals;
 9
10    // Hardcoded VGA resolution
11    const cv::Size resolution(640, 480);
12
13    // Initializing input events with uniformly distributed events which represent noise
14    dv::EventStore events = dv::data::generate::uniformEventsWithinTimeRange(0, 10ms, resolution, 1000);
15
16    // Adding additional data for drawing, this will give an idea whether the filter removes actual signal events
17    events.add(dv::data::generate::dvLogoAsEvents(10000, resolution));
18
19    // Initialize a background activity noise filter with 10-millisecond half life decay, resolution subdivision
20    // factor of 4 and noise threshold of 1. Half life decay and noise threshold values controls the quality of
21    // filtering, while subdivision factor is used for resolution downsizing for internal event representation.
22    dv::noise::FastDecayNoiseFilter filter(resolution, 10ms, 4, 1.f);
23
24    // Pass events to the filter
25    filter.accept(events);
26
27    // Call generate events to apply the noise filter
28    const dv::EventStore filtered = filter.generateEvents();
29
30    // Print out the reduction factor, which indicates the percentage of discarded events
31    std::cout << "Filter reduced number of events by a factor of " << filter.getReductionFactor() << std::endl;
32
33    // Use a visualizer instance to preview the events
34    dv::visualization::EventVisualizer visualizer(resolution);
35
36    // Generate preview images of data input and output
37    const cv::Mat input  = visualizer.generateImage(events);
38    const cv::Mat output = visualizer.generateImage(filtered);
39
40    // Concatenate the images into a single image for preview
41    cv::Mat preview;
42    cv::hconcat(input, output, preview);
43
44    // Display the input and output images
45    cv::namedWindow("preview", cv::WINDOW_NORMAL);
46    cv::imshow("preview", preview);
47    cv::waitKey();
48
49    return 0;
50}

The sample code outputs such images:

_images/fast_decay_filter.png

Output of the sample use of fast decay noise filter. Left is a preview of input events, right is a preview of filtered events.

Note

The image above that the filter drastically the amount of speckles on white area, although the logo image is also affected and some true-signal events contributing to the logo are also filtered out.

Event subsampling

The same filtering approach is used to subsampling events based on their pixel location, polarity or other properties. This chapter describes the available event subsampling filters in the library.

Mask filter

dv::EventMaskFilter - filters events based on a pixel mask. Events are discarded in pixel locations where mask has zero values.

Following sample code shows the usage of the dv::EventMaskFilter to filter out selected regions of events:

 1#include <dv-processing/core/filters.hpp>
 2#include <dv-processing/data/generate.hpp>
 3#include <dv-processing/visualization/event_visualizer.hpp>
 4
 5#include <opencv2/highgui.hpp>
 6
 7int main() {
 8    using namespace std::chrono_literals;
 9
10    // Smaller resolution for previews
11    const cv::Size resolution(200, 200);
12
13    // Initializing input events with events that represent a logo
14    const dv::EventStore events = dv::data::generate::dvLogoAsEvents(0, resolution);
15
16    // Initialize a mask with all zero values
17    cv::Mat mask(resolution, CV_8UC1, cv::Scalar(0));
18
19    // Draw two rectangles to generate a similar to checkerboard mask pattern
20    cv::rectangle(
21        mask, cv::Point(0, 0), cv::Point(resolution.width / 2, resolution.height / 2), cv::Scalar(255), cv::FILLED);
22    cv::rectangle(mask, cv::Point(resolution.width / 2, resolution.height / 2),
23        cv::Point(resolution.width, resolution.height), cv::Scalar(255), cv::FILLED);
24
25    // Initialize the mask filter with the generated mask
26    dv::EventMaskFilter filter(mask);
27
28    // Pass events to the filter
29    filter.accept(events);
30
31    // Call generate events to apply the filter
32    const dv::EventStore filtered = filter.generateEvents();
33
34    // Print out the reduction factor, which indicates the percentage of discarded events
35    std::cout << "Filter reduced number of events by a factor of " << filter.getReductionFactor() << std::endl;
36
37    // Use a visualizer instance to preview the events
38    dv::visualization::EventVisualizer visualizer(resolution);
39
40    // Generate preview images of data input and output
41    const cv::Mat input  = visualizer.generateImage(events);
42    const cv::Mat output = visualizer.generateImage(filtered);
43
44    // Concatenate the images into a single image for preview
45    cv::Mat preview, maskColored;
46    cv::cvtColor(mask, maskColored, cv::COLOR_GRAY2BGR);
47    cv::hconcat(std::vector<cv::Mat>({input, maskColored, output}), preview);
48
49    // Display the input and output images
50    cv::namedWindow("preview", cv::WINDOW_NORMAL);
51    cv::imshow("preview", preview);
52    cv::waitKey();
53
54    return 0;
55}

The sample code outputs such images:

_images/mask_filter.png

Output of the sample use of event mask filter. Left is a preview of input events, middle is the mask used, and right is a preview of filtered events.

Refractory period filter

dv::RefractoryPeriodFilter - refractory period filter discards bursts of events at repeating pixel locations. Each event timestamp is compared against most recent event timestamp on the same pixel location, if the timestamp difference is less than the refractory period, the event is discarded.

Following sample code shows the usage of the dv::RefractoryPeriodFilter to filter out events that are within close time period on the same coordinate location:

 1#include <dv-processing/core/filters.hpp>
 2#include <dv-processing/data/generate.hpp>
 3
 4#include <opencv2/highgui.hpp>
 5
 6int main() {
 7    using namespace std::chrono_literals;
 8
 9    const cv::Size resolution(200, 200);
10
11    // Initializing 10000 events that are uniformly spaced in pixel area and time
12    dv::EventStore events = dv::data::generate::uniformEventsWithinTimeRange(0, 10ms, resolution, 10000);
13
14    // Initialize refractory period filter with 1-millisecond period
15    dv::RefractoryPeriodFilter filter(resolution, 1ms);
16
17    // Pass events to the filter
18    filter.accept(events);
19
20    // Call generate events to apply the filter
21    const dv::EventStore filtered = filter.generateEvents();
22
23    // Print out the number of events after filtering
24    std::cout << "Filtered [" << filtered.size() << "] events out of [" << events.size() << "]" << std::endl;
25
26    return 0;
27}

Polarity filter

dv::EventPolarityFilter - filter events based on polarity.

Following sample code shows the usage of the dv::EventPolarityFilter to filter out events based on polarity:

 1#include <dv-processing/core/filters.hpp>
 2#include <dv-processing/data/generate.hpp>
 3#include <dv-processing/visualization/event_visualizer.hpp>
 4
 5#include <opencv2/highgui.hpp>
 6
 7int main() {
 8    using namespace std::chrono_literals;
 9
10    const cv::Size resolution(200, 200);
11
12    // Initializing input events with events that represent a logo
13    dv::EventStore events = dv::data::generate::dvLogoAsEvents(0, resolution);
14
15    // Filter positive polarity events only
16    dv::EventPolarityFilter filter(true);
17
18    // Pass events to the filter
19    filter.accept(events);
20
21    // Call generate events to apply the filter
22    const dv::EventStore filtered = filter.generateEvents();
23
24    // Use a visualizer instance to preview the events
25    dv::visualization::EventVisualizer visualizer(resolution);
26
27    // Generate preview images of data input and output
28    const cv::Mat input  = visualizer.generateImage(events);
29    const cv::Mat output = visualizer.generateImage(filtered);
30
31    // Concatenate the images into a single image for preview
32    cv::Mat preview;
33    cv::hconcat(input, output, preview);
34
35    // Display the input and output images
36    cv::namedWindow("preview", cv::WINDOW_NORMAL);
37    cv::imshow("preview", preview);
38    cv::waitKey();
39
40    return 0;
41}

The sample code outputs such images:

_images/polarity_filter.png

Output of the sample use of polarity filter. Left is a preview of input events and right is a preview of filtered events.

Note

The generated DV logo image comes from event representation, white area means no events are there, dark grey area are negative events and pixels are blue on coordinates where positive polarity events are provided. The filtered image only contains the letters “DV” since the background circle is represented by negative polarity events.

Event region filter

dv::EventRegionFilter - filter events based on given region of interest.

Following sample code shows the usage of the dv::EventRegionFilter to filter out specific area of events:

 1#include <dv-processing/core/filters.hpp>
 2#include <dv-processing/data/generate.hpp>
 3#include <dv-processing/visualization/event_visualizer.hpp>
 4
 5#include <opencv2/highgui.hpp>
 6
 7int main() {
 8    using namespace std::chrono_literals;
 9
10    const cv::Size resolution(200, 200);
11
12    // Initializing input events with events that represent a logo
13    dv::EventStore events = dv::data::generate::dvLogoAsEvents(0, resolution);
14
15    // Initialize region filter using hardcoded coordinates
16    dv::EventRegionFilter filter(cv::Rect(50, 50, 100, 100));
17
18    // Pass events to the filter
19    filter.accept(events);
20
21    // Call generate events to apply the filter
22    const dv::EventStore filtered = filter.generateEvents();
23
24    // Use a visualizer instance to preview the events
25    dv::visualization::EventVisualizer visualizer(resolution);
26
27    // Generate preview images of data input and output
28    const cv::Mat input  = visualizer.generateImage(events);
29    const cv::Mat output = visualizer.generateImage(filtered);
30
31    // Concatenate the images into a single image for preview
32    cv::Mat preview;
33    cv::hconcat(input, output, preview);
34
35    // Display the input and output images
36    cv::namedWindow("preview", cv::WINDOW_NORMAL);
37    cv::imshow("preview", preview);
38    cv::waitKey();
39
40    return 0;
41}

The sample code outputs such images:

_images/roi_filter.png

Output of the sample use of region filter. Left is a preview of input events and right is a preview of filtered events.

Filter chain

Multiple filters can be combined into a single filter chain, which optimizes memory operations to increase the performance of applying multiple filters. This is achieved by using the dv::EventFilterChain class. Multiple filters can be added using dv::EventFilterChain::addFilter() method, it accepts filter wrapped in std::shared_pointer, the shared pointer is used to be able to modify the parameters of filters after they are added to the filter chain.

Following sample code shows the usage of the dv::EventFilterChain to apply multiple types of filters in a single chain:

 1#include <dv-processing/core/filters.hpp>
 2#include <dv-processing/data/generate.hpp>
 3#include <dv-processing/noise/background_activity_noise_filter.hpp>
 4#include <dv-processing/visualization/event_visualizer.hpp>
 5
 6#include <opencv2/highgui.hpp>
 7
 8int main() {
 9    using namespace std::chrono_literals;
10
11    const cv::Size resolution(200, 200);
12
13    // Initializing input events with events that represent a logo
14    dv::EventStore events = dv::data::generate::dvLogoAsEvents(0, resolution);
15
16    // Initialize event filter chain, it contains no filters
17    dv::EventFilterChain filter;
18
19    // Now let's add filters
20    // First, add a region filter with hardcoded coordinates
21    filter.addFilter(std::make_shared<dv::EventRegionFilter<>>(cv::Rect(50, 50, 100, 100)));
22
23    // Second, add a positive polarity filter
24    filter.addFilter(std::make_shared<dv::EventPolarityFilter<>>(true));
25
26    // Third, add a background activity noise filter
27    filter.addFilter(std::make_shared<dv::noise::BackgroundActivityNoiseFilter<>>(resolution));
28
29    // Pass events to the filter
30    filter.accept(events);
31
32    // Call generate events to apply the filter chain, it will apply all three filters
33    const dv::EventStore filtered = filter.generateEvents();
34
35    // Use a visualizer instance to preview the events
36    dv::visualization::EventVisualizer visualizer(resolution);
37
38    // Generate preview images of data input and output
39    const cv::Mat input  = visualizer.generateImage(events);
40    const cv::Mat output = visualizer.generateImage(filtered);
41
42    // Concatenate the images into a single image for preview
43    cv::Mat preview;
44    cv::hconcat(input, output, preview);
45
46    // Display the input and output images
47    cv::namedWindow("preview", cv::WINDOW_NORMAL);
48    cv::imshow("preview", preview);
49    cv::waitKey();
50
51    return 0;
52}

The sample code outputs such images:

_images/filter_chain.png

Output of the sample use of multiple filters in a filter chain. Left is a preview of input events and right is a preview of filtered events.

Filtering performance

The provided event filters performance is measured using benchmarks, the benchmarks for filters can be found under directory benchmarks/noise in the project repository. These are sample benchmarking results, performance of filters is measured in throughput of mega-events per second. These measurements were capture on an AMD Ryzen 5 3600 6-Core processor.

Filter

Event count per iteration

Throughput, MegaEvents / second

FastDecayNoiseFilter

1000

82.3

FastDecayNoiseFilter

4096

82.9

FastDecayNoiseFilter

32768

80.1

FastDecayNoiseFilter

262144

67.5

FastDecayNoiseFilter

1000000

67.2

BackgroundActivityNoiseFilter

1000

141.5

BackgroundActivityNoiseFilter

4096

139.5

BackgroundActivityNoiseFilter

32768

105.4

BackgroundActivityNoiseFilter

262144

134.6

BackgroundActivityNoiseFilter

1000000

135.0

RefractoryPeriodFilter

1000

268.7

RefractoryPeriodFilter

4096

278.9

RefractoryPeriodFilter

32768

254.9

RefractoryPeriodFilter

262144

255.1

RefractoryPeriodFilter

1000000

167.5

PolarityFilter

1000

503.7

PolarityFilter

4096

345.0

PolarityFilter

32768

165.4

PolarityFilter

262144

157.7

PolarityFilter

1000000

156.6

RegionFilter

1000

446.9

RegionFilter

4096

371.7

RegionFilter

32768

166.8

RegionFilter

262144

153.6

RegionFilter

1000000

151.0

MaskFilter

1000

421.5

MaskFilter

4096

264.8

MaskFilter

32768

123.6

MaskFilter

262144

118.0

MaskFilter

1000000

117.1

ThreeFiltersNoChain

1000

89.3

ThreeFiltersNoChain

4096

88.0

ThreeFiltersNoChain

32768

76.2

ThreeFiltersNoChain

262144

75.0

ThreeFiltersNoChain

1000000

72.6

ThreeFiltersWithChain

1000

170.4

ThreeFiltersWithChain

4096

99.1

ThreeFiltersWithChain

32768

69.4

ThreeFiltersWithChain

262144

70.3

ThreeFiltersWithChain

1000000

70.1