AirSim 提供了一个基于 Python 的事件相机模拟器,旨在实现高性能并能够与模拟器实时运行。

事件相机#

事件相机是一种特殊的视觉传感器,它测量对数亮度变化,并且只报告“事件”。每次对数亮度的绝对变化超过某个阈值时,都会生成一个事件。每个事件包含四个值:测量的时间戳、像素位置(x 和 y 坐标)以及极性:根据对数亮度是增加还是减少,极性为 +1 或 -1。大多数事件相机具有微秒量级的时间分辨率,使其比 RGB 传感器快得多,并且还表现出高动态范围和低运动模糊。有关事件相机的更多详细信息,请参阅RPG-UZH 的这份教程

AirSim 事件模拟器#

AirSim 事件模拟器使用两个连续的 RGB 图像(转换为灰度),并根据图像之间对数亮度的变化计算在过渡期间可能发生的“过去事件”。这些事件以字节流的形式报告,遵循以下格式:

<x> <y> <timestamp> <pol>

x 和 y 是事件发生的像素位置,timestamp 是以微秒为单位的全局时间戳,pol 根据亮度是增加还是减少而为 +1 或 -1。除了这个字节流之外,还会构建一个事件在 2D 帧上的累积,称为“事件图像”,它将 +1 事件可视化为红色像素,-1 事件可视化为蓝色像素。事件图像示例如下所示:

image

用法#

一个与 AirSim 一起运行事件模拟器的示例脚本位于 https://github.com/microsoft/AirSim/blob/main/PythonClient/eventcamera_sim/test_event_sim.py。以下可选的命令行参数可以传递给此脚本。

args.width, args.height (float): Simulated event camera resolution
args.save (bool): Whether or not to save the event data to a file, args.debug (bool): Whether or not to display the simulated events as an image

实际事件模拟的实现,用 Python 和 numba 编写,位于 https://github.com/microsoft/AirSim/blob/main/PythonClient/eventcamera_sim/event_simulator.py。事件模拟器按如下方式初始化,参数控制相机的分辨率。

from event_simulator import *
ev_sim = EventSimulator(W, H)

事件的实际计算通过一个 `image_callback` 函数触发,该函数在每次获取新的 RGB 图像时执行。由于缺少“先前”图像,第一次调用此函数时,它充当事件模拟器的初始化。

event_img, events = ev_sim.image_callback(img, ts_delta)
这个函数,其行为类似于回调(每当接收到新图像时被调用),返回一个一维的 +1/-1 值数组作为事件图像,因此只指示在每个像素处是否看到了事件,而不是事件的时间/数量。这个一维数组可以转换为红/蓝事件图像,如函数 `convert_event_img_rgb` 所示。`events` 是一个事件的 numpy 数组,每个事件的格式为 `<x> <y> <timestamp> <pol>`。

通过此函数,事件模拟器计算过去图像和当前图像之间的差异,并计算一个事件流,然后将其作为 numpy 数组返回。然后可以将其附加到文件中。

有相当多的参数可以调整,以实现事件模拟的视觉保真度/性能水平。主要的调整因素如下:

  1. 相机的分辨率。
  2. 对数亮度阈值 `TOL`,它决定了检测到的变化是否算作事件。

注意:目前每对图像生成的事件数量也有一个最大限制,也可以进行调整。

算法#

事件模拟器的工作原理大致遵循以下操作:1. 计算当前帧和前一帧的对数强度差。
2. 遍历所有像素,根据对数强度变化的阈值计算每个像素的极性。
3. 根据强度变化超过阈值的程度,确定每个像素触发的事件数量。设 \(N_{max}\) 为单个像素可能发生的最大事件数量,则在像素位置 \(u\) 处模拟的总触发次数为 \(N_e(u) = min(N_{max}, \frac{\Delta L(u)}{TOL})\)
4. 通过插值前一图像和当前图像捕获之间经过的时间量来确定每个插值事件的时间戳。
\(t = t_{prev} + \frac{\Delta T}{N_e(u)}\)
5. 通过在每个像素模拟事件并按时间戳排序来生成输出字节流。