
# HoloLens 2 传感器数据流

Juan Carlos Dibene

史蒂文斯理工学院

Enrique Dunn

史蒂文斯理工学院

## 摘要

我们开发了一款用于HoloLens 2的服务器应用程序，可通过TCP协议实时流式传输设备数据。该服务器能够从四台灰度摄像头、深度传感器、惯性测量单元（IMU）、前置RGB摄像头、麦克风、头部追踪模块、眼部追踪模块以及手部追踪模块中采集数据。每个发送的数据帧都包含时间戳，还可选包含设备在三维空间中的瞬时姿态信息。该服务器支持下载设备校准数据，例如摄像头内参；它还能作为插件集成到Unity项目中，并具备基本的上行通信功能。为了实现全帧率下的实时视频流传输，我们利用了HoloLens 2的视频编码能力。最后，我们还开发了一款用于接收和解码数据的Python库，其中包含了便于将数据传递给其他库的工具。源代码、Python演示程序以及预编译二进制文件可在<https://github.com/jdibenes/hl2ss>获取。

### 1. 引言

在本报告中，我们介绍了一种通过TCP协议流式传输HoloLens 2传感器数据的实时系统。该系统由运行在HoloLens 2上的服务器应用程序，以及用于接收和解码数据的Python客户端库组成。该客户端库可在Windows、Linux和OS X系统上运行。该服务器还能作为插件集成到Unity项目中，并支持接收来自客户端的消息。这样一来，就能充分利用客户端的计算能力以及Unity引擎在HoloLens 2上的强大功能。

该系统的目的在于实现在其他系统上对HoloLens 2数据进行实时在线实验，这些系统往往拥有更强的计算能力和第三方库支持。Windows设备门户[^19]可以流式传输HoloLens 2前置RGB摄像头和麦克风的数据，但无法访问四台侧视灰度摄像头、深度传感器以及IMU。研究模式API[^26]虽然能访问这些传感器，但现有工具仅允许将数据记录到文件中，之后再从HoloLens 2下载。

![Image](images/95651af589f00765a66582a32c71cf18d7d3be0be410525df5973f19e3939af3.jpg)

<div style="text-align: center;">Figure 1. Our HoloLens 2 Sensor Streaming system allows to stream HoloLens 2 sensor data over Wi-Fi, enabling real time, online experiments on Windows, Linux, and OS X systems.</div>

---

我们的系统填补了这一空白：它可以通过Wi-Fi访问所有这些传感器，以及头部姿态、眼部追踪和手部追踪数据。图1展示了使用我们的系统同时采集HoloLens 2所有摄像头数据的情况。数据能以全帧率实时传输，且延迟极低。在我们的测试环境中，RGB摄像头输出的1080p 30 FPS视频的延迟约为270毫秒。

本报告其余部分的结构如下：第2节详细介绍了我们的HoloLens 2服务器应用程序；第3节说明了Unity插件的功能；第4节则介绍了我们用于接收数据的Python库。

### 2. HoloLens 2服务器应用程序

我们的HoloLens 2服务器是一款基于通用Windows平台（UWP）的C++应用程序，如表1所示，它在特定的TCP端口上提供以下数据流：

• RM可见光摄像头（VLC）——四台灰度摄像头，分辨率为640×480，帧率为30 FPS，采用H264或HEVC编码。

• RM长距离深度传感器——输出16位深度图像（单位：毫米）和16位有效亮度（AB）图像，分辨率为320×288，帧率为1-5 FPS，编码为单张32位PNG格式。不支持RM Depth AHaT格式。

• RM IMU——加速度计 $(m/s^{2})$、陀螺仪 $(deg/s)$ 以及磁力计。

• 照片/视频RGB摄像头（PV）——分辨率为1920x1080，帧率为30 FPS，分辨率、帧率、曝光、对焦和白平衡均可配置。采用H264或HEVC编码。

• 麦克风——具备2个声道，采样率为48000 Hz。采用AAC ADTS编码。

• 空间输入——头部姿态、视线方向（起点与方向）以及手部追踪（两只手各含26个关节）。

灰度摄像头、深度传感器和IMU的数据通过研究模式（RM）API获取 [^26]。PV摄像头的视频则通过MediaCapture类获取 [^10]。麦克风音频通过WASAPI捕获 [^1]。头部姿态和眼部追踪数据通过Spatial-PointerPose类获取 [^18]。手部追踪数据通过SpatialInteractionManager类获取 [^16]。

#### 2.1. 数据压缩

为降低网络带宽需求并实现实时数据流传输，音频和视频采用Microsoft Media Foundation SDK进行压缩 [^11]。对于视频，客户端可从四种视频编码配置中选择一种并直接设置比特率；对于音频，客户端可从四种比特率预设中选择一种。所有可用的编码配置如表2所示，且均为有损压缩。

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Stream</td><td style='text-align: center; word-wrap: break-word;'>TCP Port</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM VLC Left-front</td><td style='text-align: center; word-wrap: break-word;'>3800</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM VLC Left-left</td><td style='text-align: center; word-wrap: break-word;'>3801</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM VLC Right-front</td><td style='text-align: center; word-wrap: break-word;'>3802</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM VLC Right-right</td><td style='text-align: center; word-wrap: break-word;'>3803</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM Depth Long-throw</td><td style='text-align: center; word-wrap: break-word;'>3805</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Accelerometer</td><td style='text-align: center; word-wrap: break-word;'>3806</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Gyroscope</td><td style='text-align: center; word-wrap: break-word;'>3807</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Magnetometer</td><td style='text-align: center; word-wrap: break-word;'>3808</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>PV</td><td style='text-align: center; word-wrap: break-word;'>3810</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Microphone</td><td style='text-align: center; word-wrap: break-word;'>3811</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Spatial Input</td><td style='text-align: center; word-wrap: break-word;'>3812</td></tr></table>

<div style="text-align: center;">Table 1. TCP Ports for each data stream exposed by the HoloLens 2 server application.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>ID</td><td style='text-align: center; word-wrap: break-word;'>Video encoding profiles</td><td style='text-align: center; word-wrap: break-word;'>Audio encoding presets</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>0</td><td style='text-align: center; word-wrap: break-word;'>H264 Baseline</td><td style='text-align: center; word-wrap: break-word;'>AAC, 12000 bytes/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>1</td><td style='text-align: center; word-wrap: break-word;'>H264 Main</td><td style='text-align: center; word-wrap: break-word;'>AAC, 16000 bytes/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>2</td><td style='text-align: center; word-wrap: break-word;'>H264 High</td><td style='text-align: center; word-wrap: break-word;'>AAC, 20000 bytes/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>3</td><td style='text-align: center; word-wrap: break-word;'>H265 Main (HEVC)</td><td style='text-align: center; word-wrap: break-word;'>AAC, 24000 bytes/s</td></tr></table>

<div style="text-align: center;">Table 2. Available audio and video encoding profiles. All of these encoding formats are lossy.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Stream</td><td style='text-align: center; word-wrap: break-word;'>Bandwidth</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Single RM VLC</td><td style='text-align: center; word-wrap: break-word;'>1 Mbit/s (default)</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM Depth Long-throw</td><td style='text-align: center; word-wrap: break-word;'>6.5 Mbit/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Accelerometer</td><td style='text-align: center; word-wrap: break-word;'>250 Kbit/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Gyroscope</td><td style='text-align: center; word-wrap: break-word;'>1.5 Mbit/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Magnetometer</td><td style='text-align: center; word-wrap: break-word;'>12 Kbit/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>PV</td><td style='text-align: center; word-wrap: break-word;'>5 Mbit/s (default)</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Microphone</td><td style='text-align: center; word-wrap: break-word;'>192 Kbit/s (default)</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Spatial Input</td><td style='text-align: center; word-wrap: break-word;'>1 Mbit/s</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Total</td><td style='text-align: center; word-wrap: break-word;'>19 Mbit/s</td></tr></table>

<div style="text-align: center;">Table 3. Average bandwidth for each stream. The bitrate of the audio and video streams is configurable.</div>

对于深度数据，深度图像中无效的像素会被置零，这些无效像素由8位320x288的sigma通道标识。随后，深度图像与AB图像会通过NEON技术交错处理 [^7]，再使用BitmapEncoder类以PNG格式（无损）编码 [^3]。这样做一是为了无需传输sigma通道，二是因为图像交错后的压缩效果优于拼接方式，不过这一效果会因场景而异。各数据流的平均带宽如表3所示。

---

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Timestamp</td><td style='text-align: center; word-wrap: break-word;'>u64</td><td style='text-align: center; word-wrap: break-word;'>8</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Size of payload</td><td style='text-align: center; word-wrap: break-word;'>u32</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Payload</td><td style='text-align: center; word-wrap: break-word;'>bytes</td><td style='text-align: center; word-wrap: break-word;'>Size of payload</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Pose (optional)</td><td style='text-align: center; word-wrap: break-word;'>4x4 float</td><td style='text-align: center; word-wrap: break-word;'>64</td></tr></table>

<div style="text-align: center;">Table 4. Common data frame structure. The pose field is optional, and if not included, its length is zero. Data frames are unpacked from the stream by a simple FSM.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Mode</td><td style='text-align: center; word-wrap: break-word;'>Description</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>0</td><td style='text-align: center; word-wrap: break-word;'>Continuous transfer of device data</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>1</td><td style='text-align: center; word-wrap: break-word;'>Continuous transfer of device data and pose</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>2</td><td style='text-align: center; word-wrap: break-word;'>Single transfer of calibration data</td></tr></table>

<div style="text-align: center;">Table 5. Stream operating modes. The client configures the operating mode when opening the stream.</div>

#### 2.2. 数据传输

数据通过Windows Sockets经TCP传输 [^24]。所有数据均以小端格式传输，矩阵采用行优先顺序存储。数据结构如图4所示，适用于所有数据流。时间戳以百纳秒为单位，且属于同一时间域（QPC [^2]），因此可用于关联不同数据流的数据。设备姿态字段为可选字段，其是否出现由客户端控制。姿态以4x4浮点矩阵形式表示：若姿态有效，矩阵的最后一个元素为1；否则，若该帧中HoloLens 2的追踪丢失，最后一个元素则为0。

每个数据流在独立线程中运行，因此可同时激活多个数据流。但除了数据流启动时外，并未提供用于数据帧解包的数据流-客户端同步机制，故每个数据流仅允许一个客户端连接。数据流支持多达三种不同的运行模式，如表5所示。客户端在打开数据流时即可设置运行模式，该设置会一直生效直至数据流关闭。模式0仅传输设备数据；模式1会在每帧中包含对应的设备姿态；模式2供客户端下载依赖设备的校准数据，传输结束后数据流会自动关闭。各数据流支持的运行模式如表6所示。

#### 2.3. 模式0与模式1的数据传输

除了空间输入流之外，服务器会等待客户端发送配置数据，在收到数据后才开始流式传输。配置数据会以小端格式通过流的TCP端口发送。各流的配置参数及其顺序如下：

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Stream</td><td style='text-align: center; word-wrap: break-word;'>Supported Modes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM VLC</td><td style='text-align: center; word-wrap: break-word;'>0, 1, 2</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM Depth</td><td style='text-align: center; word-wrap: break-word;'>0, 1, 2</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Accelerometer</td><td style='text-align: center; word-wrap: break-word;'>0, 1, 2</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Gyroscope</td><td style='text-align: center; word-wrap: break-word;'>0, 1, 2</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>RM IMU Magnetometer</td><td style='text-align: center; word-wrap: break-word;'>0, 1</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>PV</td><td style='text-align: center; word-wrap: break-word;'>0, 1, 2</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Microphone</td><td style='text-align: center; word-wrap: break-word;'>0</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Spatial Input</td><td style='text-align: center; word-wrap: break-word;'>0</td></tr></table>

<div style="text-align: center;">Table 6. Stream operating modes supported by each stream. Not all streams support all operating modes.</div>

• RM VLC：工作模式（u8）、宽度（u16）、高度（u16）、帧率（u8）、视频编码配置文件（u8）、比特率（u32）。其中宽度、高度和帧率必须分别为640、480和30。

• RM Depth：工作模式（u8）。

• RM IMU：工作模式（u8）。

• PV：工作模式（u8）、宽度（u16）、高度（u16）、帧率（u8）、视频编码配置文件（u8）、比特率（u32）。其中宽度、高度和帧率必须是可定位摄像头概览中所示的视频会议配置文件所包含的配置之一 [^9]。

• 麦克风：音频编码预设（u8）。

服务器处理完配置数据后会立即开始数据流传输。对于RM VLC和PV流，负载为编码后的视频帧；对于RM Depth流，负载是包含深度图像（前2个通道）和AB图像（后2个通道）的PNG文件；对于RM IMU流，负载包含多批采样数据。每个采样数据的结构如表7所示。其中RM IMU加速度计、陀螺仪和磁力计的帧分别包含93、315和11个采样点。对于麦克风流，负载为编码后的音频采样数据。

空间输入流的负载结构如表8所示。valid字段中置位的位用于指示头部姿态（第0位）、视线方向（第1位）、左手（第2位）和右手（第3位）字段的数据是否有效。头部姿态和视线方向字段的结构分别如表9和表10所示。对于头部姿态而言，向上对应+Y轴，向前对应-Z轴。左手和右手字段包含每只手26个关节的姿态信息。有关手部关节的列表，请参阅HandJointKind枚举文档 [^6]。每个手部关节姿态的结构如表11所示，其方向以四元数形式表示。更多详情请参阅JointPose结构体的文档 [^8]。

---

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Sensor timestamp in ns</td><td style='text-align: center; word-wrap: break-word;'>u64</td><td style='text-align: center; word-wrap: break-word;'>8</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Data frame timestamp</td><td style='text-align: center; word-wrap: break-word;'>u64</td><td style='text-align: center; word-wrap: break-word;'>8</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>X-axis measurement</td><td style='text-align: center; word-wrap: break-word;'>float</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Y-axis measurement</td><td style='text-align: center; word-wrap: break-word;'>float</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Z-axis measurement</td><td style='text-align: center; word-wrap: break-word;'>float</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr></table>

<div style="text-align: center;">Table 7. RM IMU sample structure. The data frame timestamp is the same for all of the samples in the batch.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Valid</td><td style='text-align: center; word-wrap: break-word;'>u8</td><td style='text-align: center; word-wrap: break-word;'>1</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Head Pose</td><td style='text-align: center; word-wrap: break-word;'>bytes</td><td style='text-align: center; word-wrap: break-word;'>36</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Eye Ray</td><td style='text-align: center; word-wrap: break-word;'>bytes</td><td style='text-align: center; word-wrap: break-word;'>24</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Left Hand</td><td style='text-align: center; word-wrap: break-word;'>bytes</td><td style='text-align: center; word-wrap: break-word;'>26\times36</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Right Hand</td><td style='text-align: center; word-wrap: break-word;'>bytes</td><td style='text-align: center; word-wrap: break-word;'>26\times36</td></tr></table>

<div style="text-align: center;">Table 8. Spatial Input payload structure. The first 4 bits of the valid field indicate the validity of the Head Pose, Eye Ray, Left Hand, and Right Hand fields.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Position</td><td style='text-align: center; word-wrap: break-word;'>1x3 float</td><td style='text-align: center; word-wrap: break-word;'>12</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Forward direction</td><td style='text-align: center; word-wrap: break-word;'>1x3 float</td><td style='text-align: center; word-wrap: break-word;'>12</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Up direction</td><td style='text-align: center; word-wrap: break-word;'>1x3 float</td><td style='text-align: center; word-wrap: break-word;'>12</td></tr></table>

<div style="text-align: center;">Table 9. Head Pose structure. Vector element order is X, Y, and Z. Right direction can be obtained as cross(up, -forward).</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Position</td><td style='text-align: center; word-wrap: break-word;'>1x3 float</td><td style='text-align: center; word-wrap: break-word;'>12</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Direction</td><td style='text-align: center; word-wrap: break-word;'>1x3 float</td><td style='text-align: center; word-wrap: break-word;'>12</td></tr></table>

<div style="text-align: center;">Table 10. Eye Ray structure. Vector element order is X, Y, and Z. Ray length is not provided.</div>

对于模式1流，每个数据帧中都包含设备的姿态信息。对于RM流而言，该姿态对应的是设备定义的坐标系，即rigNode。详情请参阅HoloLens 2研究模式报告 [^26]。表示各传感器到rigNode变换的外参可通过模式2传输获取。这些姿态是通过SpatialLocator类获取的 [^17]。对于PV流来说，姿态与摄像头相对应，会随采集到的视频帧一同传输。

#### 2.4. 模式2传输

每个传感器的校准数据可通过一次模式2传输获取。与模式0和模式1传输类似，客户端必须先发送配置数据。

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Orientation</td><td style='text-align: center; word-wrap: break-word;'>1x4 float</td><td style='text-align: center; word-wrap: break-word;'>16</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Position</td><td style='text-align: center; word-wrap: break-word;'>1x3 float</td><td style='text-align: center; word-wrap: break-word;'>12</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Radius</td><td style='text-align: center; word-wrap: break-word;'>float</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Accuracy</td><td style='text-align: center; word-wrap: break-word;'>u32</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr></table>

<div style="text-align: center;">Table 11. Hand Joint structure. Quaternion element order is X, Y, Z, and W. Vector element order is X, Y, and Z.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>LUT uv2xy</td><td style='text-align: center; word-wrap: break-word;'>480x640x2 float</td><td style='text-align: center; word-wrap: break-word;'>2457600</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Extrinsics</td><td style='text-align: center; word-wrap: break-word;'>4x4 float</td><td style='text-align: center; word-wrap: break-word;'>64</td></tr></table>

<div style="text-align: center;">Table 12. RM VLC calibration data.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>LUT uv2xy</td><td style='text-align: center; word-wrap: break-word;'>288x320x2 float</td><td style='text-align: center; word-wrap: break-word;'>737280</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Extrinsics</td><td style='text-align: center; word-wrap: break-word;'>4x4 float</td><td style='text-align: center; word-wrap: break-word;'>64</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Scale</td><td style='text-align: center; word-wrap: break-word;'>float</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr></table>

<div style="text-align: center;">Table 13. RM Depth Long-throw calibration data.</div>

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Extrinsics</td><td style='text-align: center; word-wrap: break-word;'>4x4 float</td><td style='text-align: center; word-wrap: break-word;'>64</td></tr></table>

<div style="text-align: center;">Table 14. RM IMU calibration data. Not available for RM IMU Magnetometer.</div>

对于RM流，只需发送工作模式字节即可；对于PV流，则需发送第2.3小节中定义的完整配置字符串。所有校准数据传输完成后，服务器会自动关闭连接。

RM VLC、RM Depth Long-throw和RM IMU流校准数据的结构分别如表12、表13和表14所示。uv2xy LUT将图像坐标转换为相机单元平面上的归一化坐标：第一个通道对应x，第二个通道对应y。传感器的外部矩阵即其到rigNode的变换矩阵。缩放值用于将深度单位转换为米。这些数据是通过Research Mode API [^26]获取的，更多细节可参考相关文档。PV流校准数据的结构则如表15所示，该数据被嵌入到每一帧视频中。不过由于服务器仅通过模式2传输方式提供此类数据，因此必须禁用PV摄像头的自动对焦功能，才能确保下载的校准数据有效。

---

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Focal length</td><td style='text-align: center; word-wrap: break-word;'>1x2 float</td><td style='text-align: center; word-wrap: break-word;'>8</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Principal point</td><td style='text-align: center; word-wrap: break-word;'>1x2 float</td><td style='text-align: center; word-wrap: break-word;'>8</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Radial distortion</td><td style='text-align: center; word-wrap: break-word;'>1x3 float</td><td style='text-align: center; word-wrap: break-word;'>12</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Tangential distortion</td><td style='text-align: center; word-wrap: break-word;'>1x2 float</td><td style='text-align: center; word-wrap: break-word;'>8</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Projection</td><td style='text-align: center; word-wrap: break-word;'>4x4 float</td><td style='text-align: center; word-wrap: break-word;'>64</td></tr></table>

<div style="text-align: center;">Table 15. PV calibration data.</div>

#### 2.5. 远程配置端口

服务器在TCP端口3809上开放了一个接口，客户端可通过该接口发送辅助配置数据并查询相关信息。所有数据均采用小端格式存储。可用的命令及其参数如下：

- 设置PV显示标记状态：0 (u8)，启用标志 (u8)。该标记用于向用户指示PV摄像头视野的下边界，旨在帮助用户将感兴趣的对象始终保持在PV摄像头的视频帧内。

- 设置PV摄像头视频时域降噪参数：2 (u8)，模式 (u32)。详情请参阅VideoTemporalDenoisingControl类 [^20]。

- 设置PV摄像头对焦参数：1 (u8)，对焦模式 (u32)、自动对焦范围 (u32)、手动对焦距离 (u32)、对焦值 (u32)、驱动程序回退选项 (u32)。详情请参阅FocusControl类 [^5]。

- 设置PV摄像头白平衡预设：3 (u8)，预设值 (u32)。详情请参阅WhiteBalanceControl类 [^23]。

- 设置PV摄像头白平衡数值：4 (u8)，数值 (u32)。详情请参阅WhiteBalanceControl类 [^23]。

• 获取服务器版本：6 (u8)。返回的服务器版本为一个包含4个u16元素的向量。

- 设置PV摄像头曝光参数：5 (u8)，模式 (u32)、数值 (32)。详情请参阅ExposureControl类 [^4]。

即便PV流正在向客户端传输数据，也可对PV摄像头进行配置。对于白平衡数值和曝光参数命令，对应的数值需分别除以25和10，如此才能使步长等于1。服务器会还原这一转换，以确保数值处于预期范围内。与数据流类似，同一时间仅允许一个连接。

### 3. Unity集成

我们的HoloLens 2服务器可作为插件集成到Unity项目中，所有流均受支持。不过若要使用空间输入功能，需先对插件进行初始化——

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Field</td><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Length in bytes</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Command ID</td><td style='text-align: center; word-wrap: break-word;'>u32</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Size of parameters</td><td style='text-align: center; word-wrap: break-word;'>u32</td><td style='text-align: center; word-wrap: break-word;'>4</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>Parameters</td><td style='text-align: center; word-wrap: break-word;'>bytes</td><td style='text-align: center; word-wrap: break-word;'>Size of parameters</td></tr></table>

<div style="text-align: center;">Table 16. Unity plugin IPC message structure. Command ID $FFFFFFFF is reserved and should not be used by the client.</div>

该功能由UI线程专门处理，且不支持PV显示标记功能。与独立服务器不同，该插件具备基本的上行通信能力。

#### 3.1. Unity IPC端口

该插件在TCP端口3816上提供了一个额外的接口，允许客户端向Unity应用程序发送消息。消息结构如表16所示。该接口与消息类型无关，因此命令及其参数的含义由用户自行定义。这使得用户无需修改插件即可为Unity应用程序添加对新命令的支持。不过，命令$FFFFFFF是预留的，不应用于新命令，因为它用于通知Unity应用程序客户端已断开与IPC端口的连接。该插件还支持以4字节整数的形式向客户端发送响应。所有数据均采用小端格式。同时只允许一个连接。

#### 3.2. 远程Unity场景

举个例子，我们创建了一个使用混合现实工具包（MRTK）[^22]的Unity项目，并实现了一组命令，以便客户端能够创建基本的Unity对象，这些对象可在增强现实（AR）中看到。这些命令及其参数如下：

- 创建基本几何体（0）：type（u32）。在Unity场景中创建一个Unity基本几何体。详见表17。返回一个key（u32），可用于修改该几何体的属性。

- 设置激活状态（1）：key（u32），state（u32）。激活或停用与key关联的对象。

- 设置世界变换（2）：key（u32），position（1x3浮点数），rotation（1x4浮点数），scale（1x3浮点数）。设置与key关联的对象的世界变换。旋转部分采用四元数表示。

- 设置颜色（4）：key（u32），rgba（1x4浮点数）。设置与key关联的几何体的颜色。rgba各元素的取值范围为[^0] [^1]，且支持半透明效果。

- 设置纹理（5）：key（u32），texture（JPG或PNG文件）。设置与key关联的几何体的纹理。

---

<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>Type</td><td style='text-align: center; word-wrap: break-word;'>Primitive</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>0</td><td style='text-align: center; word-wrap: break-word;'>Sphere</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>1</td><td style='text-align: center; word-wrap: break-word;'>Capsule</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>2</td><td style='text-align: center; word-wrap: break-word;'>Cylinder</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>3</td><td style='text-align: center; word-wrap: break-word;'>Cube</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>4</td><td style='text-align: center; word-wrap: break-word;'>Plane</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>5</td><td style='text-align: center; word-wrap: break-word;'>Quad</td></tr></table>

表17. 可用的Unity基本几何体。

- 创建文本（6）：创建一个TextMeshPro对象。返回一个key（u32），可用于修改该对象的属性。

- 设置文本（7）：key（u32），font size（浮点数），rgba（1x4浮点数），string（UTF-8编码）。设置与key关联的TextMeshPro对象的文本、字体大小和颜色。

- 删除（16）：key（u32）。销毁与key关联的对象。

- 删除所有（17）：销毁通过插件创建的所有对象。

- 开始显示列表（18）：提示Unity应用程序在同一次处理中执行后续命令。

• 结束显示列表（19）。

- 设置目标模式（20）：mode（u32）。改变用于修改对象属性的命令的行为。若mode为0，属性更改将应用于与key关联的对象；若mode为1，则忽略key参数，属性更改将应用于最后创建的对象。这样客户端无需等待服务器返回key，就能立即创建对象并设置其属性。

所有命令都会返回一个4字节的值。对于不返回key的命令，值0表示失败，1表示成功。

### 4. Python库

我们提供一款 Python 库，可帮助连接 HoloLens 2 服务器、发送配置数据和命令、接收数据并解码，还能对数据进行预处理以便与其他库配合使用。对于 H264 和 AAC 解码，我们使用 PyAV [^13] 库；PNG 解码则通过 OpenCV [^12] 实现。

该库封装了大部分通信细节，为用户提供了简单的接口。以下是一个从 PV 摄像头获取 1080p 30 FPS 视频并实时在窗口中显示的示例。

import cv2
import h12ss
import h12ss_utilities

## HoloLens 2 的 IP 地址

host = '192.168.1.15'

## 流设置

port = h12ss.StreamPort.PERSONAL_VIDEO
chunk = h12ss.ChunkSize.PERSONAL_VIDEO
mode = h12ss.StreamMode.MODE_1
width = 1920
height = 1080
framerate = 30

## 视频编码设置

profile = h12ss.VideoProfile.H265_MAIN
bitrate = 5*1024*1024

## 视频解码设置

output_format = 'bgr24'

client = h12ss_utilities.rx_decoded_pv(
    host,
    port,
    chunk,
    mode,
    width,
    height,
    framerate,
    profile,
    bitrate,
    output_format)

client.open()

while (enable):

    # Get next video frame, blocks

    # until the next frame is received

    data = client.get_next_packet()

    # data fields are:
    # data.timestamp
    # data.payload
    # data.pose (None for Mode 0)

    # Display frame using OpenCV
    # Decoded payload is a 1080x1920x3
    # NumPy array of u8
    cv2.imshow('Video', data.payload)
    cv2.waitKey(1)

client.close()

---

从其他传感器获取数据的过程非常相似：1）根据所需配置创建 h12ss rx 或 h12ss_utilities rx_decoded（针对编码流）对象；2）调用 open() 方法连接到 HoloLens 2 服务器；3）持续调用 get_next_packet() 方法以接收数据帧；4）调用 close() 方法关闭连接。必须尽快且频繁地调用 get_next_packet() 方法，以免丢失帧。需要注意的是，当使用带有编码流的 h12ss rx 对象时，负载中包含的是编码后的帧。

一个重要的限制是：get_next_packet() 方法会一直阻塞，直到接收到下一帧，这给多流处理带来了麻烦。为解决这一问题，我们提供了基于 Python multiprocessing 库的库扩展。

我们利用 Python 的多进程功能扩展了该库，以方便处理多流。这一扩展将 HoloLens 2 数据采集以及进程间数据传输抽象为四个组件：

### 4.1. 多进程扩展

- 数据源（进程）：通过调用 get_next_packet() 持续从单个 HoloLens 2 流中接收数据，并将每个数据帧发送到互连组件。

- 互连组件（进程）：从单个数据源接收数据帧并将其存入环形缓冲区。多个接收端可以从互连组件中请求数据。

- 接收端（接口）：从互连组件中获取数据帧。一个进程可以管理多个接收端，也可以让每个接收端位于独立的进程中，或者两者结合使用。

- 控制组件（进程）：负责创建、附加和终止数据源、互连组件及接收端。它通常是主进程。

互连组件会以轮询方式处理来自数据源、控制组件和接收端的消息。各组件间的通信通过信号量来触发，从而避免了轮询机制，也省去了忙等待所浪费的 CPU 时间。

互连组件向接收端对象提供以下接口：

- get_nearest(timestamp)：返回时间上最接近输入时间戳的数据帧。

- get_frame_stamp()：返回最近数据帧的帧戳。

- get_most_recent_frame()：返回最新的数据帧。

get_buffered_frame(frame_stamp)：返回与输入帧戳对应的数据帧。

帧戳对应数据帧在流中的全局位置（0对应流起始处的第一个帧）。由于环形缓冲区的容量有限，较旧的帧将不再可用。

该扩展封装了大部分多进程细节，为用户提供了简单的接口。以下是一个从PV和RM深度长焦流中获取数据的示例。

import multiprocessing as mp
import h12ss
import h12ss_mp
import h12ss_utilities

## HoloLens 2的IP地址

host = '192.168.1.15'
.....

## 生产者即源互连

## pair与h12ss_utilities生产者

## 用于管理生产者的集合

## 针对HoloLens 2流

producer = h12ss_utilities.producer()

## 初始化PV

producer.initialize_decoded_pv(
    30 * 5, # Space for 5 seconds of data
    host,
    h12ss.StreamPort.PERSONAL_VIDEO,
    h12ss.ChunkSize.PERSONAL_VIDEO,
    h12ss.StreamMode.MODE_0,
    640,
    360,
    30,
    h12ss.VideoProfile.H265_MAIN,
    1*1024*1024,
    'rgb24')

## 初始化RM深度长焦

producer.initialize_decoded_rm_depth(
    5 * 5, # Space for 5 seconds of data
    host,
    h12ss.StreamPort.RM_DEPTH_LONGTHROW,
    h12ss.ChunkSize.RM_DEPTH_LONGTHROW,
    h12ss.StreamMode.MODE_1)

## 各数据源开始采集数据，

## 互连设备将其添加到自身的

## 环形缓冲区中

producer.start()

---

## 我们使用该管理器来创建一个

## 信号量，用于在新数据接收时发出通知

## 新数据已接收

manager = mp.Manager()

## 该消费者对象用于管理

## 一组接收端

consumer = hl2ss_utilities.consumer()

## 创建一个接收端，用于从PV读取数据

## 来自生产者，但不创建

## 信号量

sink_pv = consumer.create_sink(
    producer,
    hl2ss.StreamPort.PERSONAL_VIDEO,
    manager,
    None) # No semaphore

## 创建一个接收端，用于从深度数据流读取数据

## 来自生产者，并创建信号量

## 因为我们想知道何时有新

## 深度帧到达

sink_depth = consumer.create_sink(
    producer,
    hl2ss.StreamPort.RM_DEPTH_LONGTHROW,
    manager,
    ...) # Create new semaphore

## 同一消费者的多个接收端可以共享信号量，

## 但此时需要用户自行判断

## 哪个生产者接收到了新数据

## 接收到了新数据

## 获取接收端连接到生产者时的帧戳

## 该帧戳用于同步生产者和接收端，实现1对1对应

## （本示例中未展示）。调用此方法必须在其他任何接收端方法之前

## 调用此方法必须在其他任何接收端方法之前

## 调用此方法必须在其他任何接收端方法之前

sink_pv.get_attach_response()
sink_depth.get_attach_response()

while (enable):

    # Wait for new data

    sink_depth.acquire()

    # Get depth data and check that the
    # pose is valid
    data_depth =
        sink_depth.get_most_recent_frame()
    if (not data_depth.is_valid_pose():
        continue

## 获取最近的RGB帧

_, data_pv =
    sink_pv.get_nearest(
        data_depth.timestamp)
    if (data_pv is None):
        continue

## 此时我们已经得到了RGB深度图像

## 这种图像对包含可用于三维重建的位姿信息

## 实现三维重建

.....

## 将接收端从生产者处断开

sink_pv.detach()
sink_depth.detach()

## 终止生产者进程

producer.stop()

我们采用了该示例的一种变体，在客户端机器上利用Open3D进行实时TSDF积分[^27]。

### 4.2. 互操作性

所有解码后的数据均以NumPy数组的形式返回，可直接与其他库配合使用：

• RM VLC：480×640分辨率的u8格式数据。

• 麦克风：2×1024分辨率的float格式数据。

• PV：高×宽×3分辨率的u8格式数据。

此外，我们的库还提供了用于数据预处理和存储的实用函数：

- 针对RM VLC和RM Depth流的图像畸变校正（PV图像不存在畸变）。

• RGBD图像对齐：RM VLC + Depth或PV + Depth（AB与Depth始终对齐）。详见图2。

• 关联不同流中的数据。

• 参考帧之间的坐标变换。

- 将时间戳、数据和位姿写入文件，并将编码后的音频/视频保存为MP4格式。

畸变校正和对齐操作通过OpenCV实现[^12]，而MP4写入功能则借助PyAV完成[^13]。由于解码、畸变校正和对齐操作均可实时完成，我们的框架能够集成到实时在线系统中。

举个例子，图3展示的是利用HoloLens 2的rgbaligned数据，通过Open3D [^27]在客户端机器上生成的点云；图4展示的是利用MMDetection [^25]对未失真的RM VLC图像进行全景分割的结果，该结果随后被用于分割RM深度数据。

---

![Image](images/7dfce8313016c4c5e8039c62b29d1bdbf68c8245daefd46bee6677d17b7e2e0e.jpg)

![Image](images/a62e3a812da304d4feb0c889af41c3fc3ad1dca424e78f469ab6df1c5347579b.jpg)

<div style="text-align: center;">Figure 3. Point cloud generated from HoloLens 2 data using Open3D [^27].</div>

<div style="text-align: center;">Figure 2. RGBD image alignment. Color can be from either the RM VLC (top) or the PV (bottom) stream.</div>

![Image](images/c7a6167fa82f2edd88739c22f6041735a28f6fe4483eee6aeac9ee0b1e17e8ca.jpg)

<div style="text-align: center;">Figure 4. Panoptic segmentation on RM VLC data using MMD-detection [^25] and used to 3D segment RM Depth data.</div>

#### 6. 致谢

#### 5. 结论

本研究由国防高级研究计划局资助，文中内容并不必然反映政府的立场或政策，也不应视为官方认可。

我们提出了一种系统，能够通过Wi-Fi以全帧率实时传输HoloLens 2的数据，从而支持在Windows、Linux和OS X系统上开展实时在线实验。在未来的工作中，我们计划加入Spatial Mapping数据流[^15]、场景理解数据流[^14]，并让客户端能够获取语音输入功能[^21]。

### 参考文献

[^1]: About WASAPI. <https://learn.microsoft.com/en-us/windows/win32/coreaudio/wasapi>. Accessed: 2010-09-30. 2

[^2]: Acquiring high-resolution time stamps. <https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps>. Accessed: 2010-09-30. 3

[^3]: BitmapEncoder Class. <https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.imaging.bitmapencoder?view=windowsdesktop-6.0>. Accessed: 2010-09-30.2

[^4]: ExposureControl Class. <https://learn.microsoft.com/en-us/uwp/api/windows.media.devices.exposurecontrol?view=winrt-22621>. Accessed: 2010-09-30. 5

[^5]: FocusControl Class. <https://learn.microsoft.com/en-us/uwp/api/windows.media.devices.focuscontrol?view=winrt-22621>. Accessed: 2010-09-30. 5

[^6]: HandJointKind Enum. <https://learn.microsoft.com/en-us/uwp/api/windows.perception.people.handjointkind?view=winrt-22621.Accessed>: 2010-09-30.3

[^7]: Intrinsic. <https://developer.arm.com/architectures/instruction-sets/intrinsics/>. Accessed: 2010-09-30. 2

[^8]: JointPose Struct. <https://learn.microsoft.com/en-us/uwp/api/windows.perception.people>. jointpose?view=winrt-22621. Accessed: 2010-09-30. 3

[^9]: Locatable camera overview. <https://learn.microsoft.com/en-us/windows/mixed-reality/develop/advanced-concepts/locatable-camera-overview>. Accessed: 2010-09-30.3

[^10]: MediaCapture Class. <https://learn.microsoft.com/en-us/uwp/api/windows.media.capture.mediacapture?view=winrt-22621>. Accessed: 2010-09-30.2

[^11]: Microsoft Media Foundation. <https://learn.microsoft.com/en-us/windows/win32/medfound/microsoft-media-foundation-sdk.Accessed>: 2010-09-30.2

[^12]: opencv-python. <https://github.com/opencv/opencv-python>. Accessed: 2010-09-30. 6.8

## [^13]: PyAV. <https://github.com/PyAV-Org/PyAV>. Accessed: 2010-09-30. 6.8

---

[^14]: Scene understanding. <https://learn.microsoft.com/en-us/windows/mixed-reality/design/scene-understanding>. Accessed: 2010-09-30. 9

[^15]: Spatial mapping. <https://learn.microsoft.com/en-us/windows/mixed-reality/design/spatial-mapping>. Accessed: 2010-09-30. 9

[^16]: SpatialInteractionManager Class. <https://learn.microsoft.com/en-us/uwp/api/windows.ui.input.spatial.spatialinteractionmanager?view=winrt-22621>. Accessed: 2010-09-30. 2

[^17]: SpatialLocator Class. <https://learn.microsoft.com/en-us/uwp/api/windows.perception.spatial.spatiallocator?view=winrt-22621.Accessed>: 2010-09-30.4

[^18]: SpatialPointerPose Class. <https://learn.microsoft.com/en-us/uwp/api/windows.ui.input.spatial.spatialpointerpose?view=winrt-22621.Accessed:2010-09-30.2>

[^19]: Using the Windows Device Portal. <https://learn.microsoft.com/en-us/windows/mixed-reality/develop/advanced-concepts/using-the-windows-device-portal>. Accessed: 2010-09-30.1

[^20]: VideoTemporalDenoisingControl Class. <https://learn.microsoft.com/en-us/uwp/api/windows.media.devices.videotemporaldenoisingcontrol?view=winrt-22621.Accessed:2010-09-30.5>

[^21]: Voice input. <https://learn.microsoft.com/en-us/windows/mixed-reality/design/voice-input>. Accessed: 2010-09-30. 9

[^22]: What is Mixed Reality Toolkit 2? <https://learn.microsoft.com/en-us/windows/mixed-reality/mrtk-unity/mrtk2/?view=mrtkunity-2022-05>. Accessed: 2010-09-30. 5

[^23]: WhiteBalanceControl Class. <https://learn.microsoft.com/en-us/uwp/api/windows.media.devices.whitebalancecontrol?view=winrt-22621>. Accessed: 2010-09-30. 5

[^24]: Windows Sockets 2. <https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-start-page-2>. Accessed: 2010-09-30. 3

[^25]: Kai Chen, Jiaqi Wang, Jiangmiao Pang, Yuhang Cao, Yu Xiong, Xiaoxiao Li, Shuyang Sun, Wansen Feng, Ziwei Liu, Jiarui Xu, Zheng Zhang, Dazhi Cheng, Chenchen Zhu, Tianheng Cheng, Qijie Zhao, Buyu Li, Xin Lu, Rui Zhu, Yue Wu, Jifeng Dai, Jingdong Wang, Jianping Shi, Wanli Ouyang, Chen Change Loy, and Dahua Lin. MMDetection: Open mmlab detection toolbox and benchmark. arXiv preprint arXiv:1906.07155, 2019. 8, 9

[^26]: Dorin Ungureanu, Federica Bogo, Silvano Galliani, Pooja Sama, Xin Duan, Casey Meekhof, Jan Stühmer, Thomas J. Cashman, Bugra Tekin, Johannes L. Schönberger, Bugra Tekin, Pawel Olszta, and Marc Pollefeys. HoloLens 2 Research Mode as a Tool for Computer Vision Research. arXiv:2008.11239, 2020. 1, 2, 4

[^27]: Qian-Yi Zhou, Jaesik Park, and Vladlen Koltun. Open3D: A modern library for 3D data processing. arXiv:1801.09847, 2018. 8, 9
