Mean-shift clustering
Mean-shift clustering algorithm is used to find high density clusters of events (or point) within a set of events. The dv-processing library provides an implementation of such an algorithm which is efficiently implemented for incoming streams of events from event cameras.
The algorithm can be summarized as follows:
Given a set of coordinates, for each coordinate retrieve a neighborhood of events within a configured window.
Calculate the mean of coordinates for the retrieved neighborhood.
Shift the initial coordinates by a mode, which is a vector going from the initial point to the mean coordinate multiplied by a learning rate factor.
If the mode of a vector is lower than a configured threshold, the coordinate is considered to have converged into a cluster center, otherwise repeat from step one.
This algorithm is useful to find event clusters that could be used as point of interest in event processing algorithms.
Sample usage
The following code sample shows the use of mean-shift clustering implementation to find and track clusters of events on simulated events.
1#include <dv-processing/cluster/mean_shift/event_store_adaptor.hpp>
2#include <dv-processing/core/event.hpp>
3#include <dv-processing/data/generate.hpp>
4#include <dv-processing/visualization/events_visualizer.hpp>
5
6#include <opencv2/highgui.hpp>
7
8int main() {
9 // Use VGA resolution
10 const cv::Size resolution(640, 480);
11
12 // Initialize a list of clusters for synthetic data generation
13 const std::vector<dv::Point2f> clusters(
14 {dv::Point2f(100.f, 100.f), dv::Point2f(462.f, 25.f), dv::Point2f(105.f, 340.f), dv::Point2f(540.f, 420.f)});
15
16 // Generate some random events for a background
17 dv::EventStore events = dv::data::generate::uniformlyDistributedEvents(0, resolution, 10'000);
18
19 // Declare a region filter which we will use to filter out-of-bounds events in the next step
20 dv::EventRegionFilter filter(cv::Rect(0, 0, resolution.width, resolution.height));
21
22 for (const auto &cluster : clusters) {
23 // Generate a batch of normally distributed events around each of the cluster centers
24 filter.accept(dv::data::generate::normallyDistributedEvents(0, cluster, dv::Point2f(15.f, 15.f), 5'000));
25
26 // Apply region filter to the events to filter out events outside valid dimensions
27 events += filter.generateEvents();
28 }
29
30 // Initialize mean shift clustering algorithm, with initial parameters of:
31 // bandwidth = 100, this is pixel search radius around a point
32 // conv = 0.01, the search converges when the magnitude of mean-shift vector is below this value
33 // maxIter = 10000, maximum number of mean-shift update iterations
34 // numStartingPoints = 100, number of randomly selected starting points
35 dv::cluster::mean_shift::MeanShiftEventStoreAdaptor meanShift(events, 100.0f, 0.01f, 10000, 100);
36
37 // Perform the mean-shift, the algorithm returns a tuple of center coordinates, labels, count of event in
38 // the cluster, and variances of the cluster
39 auto [centers, labels, counts, variances] = meanShift.fit();
40
41 // Let's assign the cluster size to the response value of the center keypoint
42 for (int i = 0; i < centers.size(); i++) {
43 centers[i].response = static_cast<float>(counts[i]);
44 }
45
46 // Sort the estimated centers by the number of events in cluster, the values are sorted in descending order
47 std::sort(centers.begin(), centers.end(), [](const auto &a, const auto &b) {
48 return a.response > b.response;
49 });
50
51 // Choose top four center with most events; these centers should be close to initial hardcoded cluster centers
52 if (centers.size() > 4) {
53 centers.resize(4);
54 }
55
56 // Use event visualizer to generate a preview image
57 dv::visualization::EventVisualizer visualizer(resolution);
58 auto preview = visualizer.generateImage(events);
59
60 // Draw markers on each of the center coordinates
61 for (const auto ¢er : centers) {
62 cv::drawMarker(preview, cv::Point2f(center.pt.x(), center.pt.y()), dv::visualization::colors::red);
63 }
64
65 // Show the preview image with detected cluster centers
66 cv::namedWindow("Preview", cv::WINDOW_NORMAL);
67 cv::imshow("Preview", preview);
68 cv::waitKey();
69
70 return 0;
71}
1import dv_processing as dv
2import cv2 as cv
3
4# Use VGA resolution
5resolution = (640, 480)
6
7# Initialize a list of clusters for synthetic data generation
8clusters = [(100, 100), (462, 25), (105, 340), (540, 420)]
9
10# Generate some random events for a background
11events = dv.data.generate.uniformlyDistributedEvents(0, resolution, 10000)
12
13# Declare a region filter which we will use to filter out-of-bounds events in the next step
14filter = dv.EventRegionFilter((0, 0, resolution[0], resolution[1]))
15
16for cluster in clusters:
17 # Generate a batch of normally distributed events around each of the cluster centers
18 filter.accept(dv.data.generate.normallyDistributedEvents(0, cluster, (15, 15), 5000))
19
20 # Apply region filter to the events to filter out events outside valid dimensions
21 events.add(filter.generateEvents())
22
23# Initialize mean shift clustering algorithm, with initial parameters of:
24# bandwidth = 100, this is pixel search radius around a point
25# conv = 0.01, the search converges when the magnitude of mean-shift vector is below this value
26# maxIter = 10000, maximum number of mean-shift update iterations
27# numStartingPoints = 100, number of randomly selected starting points
28mean_shift = dv.cluster.mean_shift.MeanShiftEventStoreAdaptor(events, 100, 0.01, 10000, 100)
29
30# Perform the mean-shift, the algorithm returns a tuple of center coordinates, labels, count of event in the
31# cluster, and variances of the cluster
32centers, labels, counts, variances = mean_shift.fit()
33
34# Let's assign the cluster size to the response value of the center keypoint
35for i in range(len(centers)):
36 centers[i].response = counts[i]
37
38# Sort the estimated centers by the number of events in cluster, the values are sorted in descending order
39centers.sort(key=lambda a: a.response, reverse=True)
40
41# Choose top four center with most events; these centers should be close to initial hardcoded cluster centers
42if len(centers) > 4:
43 centers = centers[:4]
44
45# Use event visualizer to generate a preview image
46visualizer = dv.visualization.EventVisualizer(resolution)
47preview = visualizer.generateImage(events)
48
49# Draw markers on each of the center coordinates
50for center in centers:
51 cv.drawMarker(preview, (int(center.pt[0]), int(center.pt[1])), dv.visualization.colors.red())
52
53# Show the preview image with detected cluster centers
54cv.namedWindow("Preview", cv.WINDOW_NORMAL)
55cv.imshow("Preview", preview)
56cv.waitKey()