CARLA
Loading...
Searching...
No Matches
DvsEventEncoding.h
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7#pragma once
8
9#include <cstdint>
10#include <cstring>
11#include <vector>
12
13namespace carla {
14namespace ros2 {
15
16// DVS event tuple consumed by the encoder. Mirrors sensor::data::DVSEvent's
17// public fields exactly, including its `#pragma pack(push, 1)` so the
18// in-memory layout matches the wire bytes published to ROS 2 (13 B per
19// event). The seam header stays FastDDS-free so a GTest can build the
20// payload directly.
21#pragma pack(push, 1)
22struct DvsEvent {
23 std::uint16_t x;
24 std::uint16_t y;
25 std::int64_t t;
26 bool pol;
27};
28#pragma pack(pop)
29static_assert(sizeof(DvsEvent) == 13u, "DvsEvent must be packed to 13 bytes");
30
31// Encodes a stream of DVS events into a height*width*3 BGR uint8 buffer:
32// positive-polarity events light the Red channel at (x, y), negative-polarity
33// events light the Blue channel. The encoding deliberately matches the wire
34// format ROS 2 subscribers already consume on ue5-dev today:
35// index = (y * width + x) * 3 + (pol * 2)
36// buffer[index] = 255
37// The 3-channel BGR layout (sensor_msgs/Image encoding "bgr8") is what the
38// CarlaDVSCameraPublisher exposes to subscribers; restructuring the publisher
39// in PR-4 must not change that wire format.
40[[nodiscard]] inline std::vector<std::uint8_t> EncodeDvsEventsToBgr(
41 std::uint32_t height,
42 std::uint32_t width,
43 const DvsEvent *events,
44 std::size_t event_count) {
45 std::vector<std::uint8_t> buffer(
46 static_cast<std::size_t>(height) * static_cast<std::size_t>(width) * 3u, 0u);
47 for (std::size_t i = 0; i < event_count; ++i) {
48 const auto &event = events[i];
49 if (event.x >= width || event.y >= height) {
50 continue;
51 }
52 const std::size_t pixel_index =
53 (static_cast<std::size_t>(event.y) * width + event.x) * 3u
54 + (event.pol ? 2u : 0u);
55 buffer[pixel_index] = 255u;
56 }
57 return buffer;
58}
59
60// Same encoding but starting from a raw byte buffer of packed
61// sensor::data::DVSEvent records. Used by CarlaDVSCameraPublisher to bridge
62// the void* boundary into the seam without pulling DVSEvent into the
63// FastDDS-free header.
64[[nodiscard]] inline std::vector<std::uint8_t> EncodeDvsEventsToBgr(
65 std::uint32_t height,
66 std::uint32_t width,
67 const std::uint8_t *raw_events,
68 std::size_t event_count,
69 std::size_t event_stride) {
70 std::vector<std::uint8_t> buffer(
71 static_cast<std::size_t>(height) * static_cast<std::size_t>(width) * 3u, 0u);
72 for (std::size_t i = 0; i < event_count; ++i) {
73 DvsEvent event{};
74 std::memcpy(&event, raw_events + i * event_stride, sizeof(DvsEvent));
75 if (event.x >= width || event.y >= height) {
76 continue;
77 }
78 const std::size_t pixel_index =
79 (static_cast<std::size_t>(event.y) * width + event.x) * 3u
80 + (event.pol ? 2u : 0u);
81 buffer[pixel_index] = 255u;
82 }
83 return buffer;
84}
85
86// Packs a stream of DVS events into the sensor_msgs/PointCloud2 wire layout
87// declared by kDvsFields: x (UINT16 @0), y (UINT16 @2), t (FLOAT64 @4),
88// pol (INT8 @12), 13 B stride. The int64 event timestamp is converted to a
89// real double in the t slot because sensor_msgs PointField has no 8-byte
90// integer datatype, so a raw int64 byte copy would be misdecoded as a double.
91[[nodiscard]] inline std::vector<std::uint8_t> EncodeDvsEventsToPointCloud(
92 const DvsEvent *events,
93 std::size_t event_count) {
94 std::vector<std::uint8_t> bytes(event_count * sizeof(DvsEvent));
95 for (std::size_t i = 0; i < event_count; ++i) {
96 const DvsEvent &event = events[i];
97 std::uint8_t *out = bytes.data() + i * sizeof(DvsEvent);
98 const double t = static_cast<double>(event.t);
99 const std::int8_t pol = event.pol ? 1 : 0;
100 std::memcpy(out + 0u, &event.x, sizeof(event.x));
101 std::memcpy(out + 2u, &event.y, sizeof(event.y));
102 std::memcpy(out + 4u, &t, sizeof(t));
103 std::memcpy(out + 12u, &pol, sizeof(pol));
104 }
105 return bytes;
106}
107
108// Same packing but starting from a raw byte buffer of packed
109// sensor::data::DVSEvent records. Used by CarlaDVSPointCloudPublisher to bridge
110// the void* boundary into the seam without pulling DVSEvent into the
111// FastDDS-free header.
112[[nodiscard]] inline std::vector<std::uint8_t> EncodeDvsEventsToPointCloud(
113 const std::uint8_t *raw_events,
114 std::size_t event_count,
115 std::size_t event_stride) {
116 std::vector<std::uint8_t> bytes(event_count * sizeof(DvsEvent));
117 for (std::size_t i = 0; i < event_count; ++i) {
118 DvsEvent event{};
119 std::memcpy(&event, raw_events + i * event_stride, sizeof(DvsEvent));
120 std::uint8_t *out = bytes.data() + i * sizeof(DvsEvent);
121 const double t = static_cast<double>(event.t);
122 const std::int8_t pol = event.pol ? 1 : 0;
123 std::memcpy(out + 0u, &event.x, sizeof(event.x));
124 std::memcpy(out + 2u, &event.y, sizeof(event.y));
125 std::memcpy(out + 4u, &t, sizeof(t));
126 std::memcpy(out + 12u, &pol, sizeof(pol));
127 }
128 return bytes;
129}
130
131} // namespace ros2
132} // namespace carla
std::vector< std::uint8_t > EncodeDvsEventsToBgr(std::uint32_t height, std::uint32_t width, const DvsEvent *events, std::size_t event_count)
std::vector< std::uint8_t > EncodeDvsEventsToPointCloud(const DvsEvent *events, std::size_t event_count)
This file contains definitions of common data structures used in traffic manager.