一句话总结

将事件相机输出的 3D 时空事件流构建为图,利用图 Laplacian 特征向量将真实事件(低频)与噪声事件(高频)分离——无需标注数据,无需深度学习。


为什么这个问题重要?

事件相机(Neuromorphic Camera / Event Camera)的工作原理与传统相机完全不同:

  • 传统相机:每隔固定时间拍一帧,输出 RGB 图像
  • 事件相机:每个像素独立检测亮度变化,一旦超过阈值就立即输出一个事件 $(x, y, t, p)$

这带来了惊人的特性:时间分辨率高达 1 微秒(传统相机是毫秒级)、几乎无运动模糊、动态范围高达 140dB、功耗极低。

但代价随之而来:高灵敏度意味着极高的噪声率。典型事件相机的事件流中,热噪声、散粒噪声等占比可达 30%–80%。不去噪的话,后续的光流估计、SLAM、目标检测全部受到严重干扰。

现有方法的局限

方法 原理 问题
时间相关滤波 同一像素短时间内多次触发才保留 误删低频真实信号
空间近邻法 事件在空间上聚集才保留 忽略时序信息
深度学习 学习噪声分布 需要标注数据,泛化性差

本文基于图信号处理(Graph Signal Processing, GSP),无需训练数据,在信号聚集的时空区域中用谱分析分离信号与噪声。


背景知识

事件数据的 3D 结构

每个事件是四元组 $(x, y, t, p)$,$p \in {+1, -1}$ 是极性。去除极性后,事件流是一个 3D 时空点云

\[\mathcal{E} = \{(x_i, y_i, t_i)\}_{i=1}^N\]

关键假设:真实事件在时空中形成结构化流形(运动物体边缘的轨迹),噪声事件随机散布。这是图谱方法成立的物理前提。

图 Laplacian 与频率

在图 $G = (V, E)$ 上,定义:

  • 邻接矩阵 $W$:$W_{ij}$ 表示节点 $i, j$ 之间的边权重
  • 度矩阵 $D$:$D_{ii} = \sum_j W_{ij}$
  • 图 Laplacian:$L = D - W$

$L$ 的特征分解给出图的”频率基”:

\[L \mathbf{v}_k = \lambda_k \mathbf{v}_k, \quad 0 = \lambda_0 \leq \lambda_1 \leq \cdots \leq \lambda_{N-1}\]
  • 小 $\lambda_k$ → 低频:相邻节点值变化缓慢(聚集信号的特征)
  • 大 $\lambda_k$ → 高频:相邻节点值剧烈变化(孤立噪声的特征)

核心方法

直觉解释

在 $(x, y, t)$ 空间里,运动边缘产生的真实事件会形成一张时空”面”(spatiotemporal surface)。热噪声事件随机漂浮在这个面的周围。

构建图后,聚集的真实事件之间有强连接,孤立的噪声事件连接极弱。计算 Laplacian 的低阶特征向量——这些向量在真实事件聚集的区域值大,在孤立噪声处接近零。这就是去噪的依据。

图构建

两事件间的边权重基于时空高斯核:

\[w_{ij} = \exp\!\left(-\frac{(x_i-x_j)^2 + (y_i-y_j)^2}{\sigma_{xy}^2} - \frac{(t_i-t_j)^2}{\sigma_t^2}\right)\]

关键参数 $\sigma_{xy}, \sigma_t$ 由局部事件密度先验自动估计。局部密度 $\rho$ 高时用小 $\sigma$(精细连接),密度低时用大 $\sigma$(稀疏连接):

\[\sigma_{xy} = \alpha \cdot \rho^{-1/3}, \quad \sigma_t = \beta \cdot \rho^{-1/3}\]

特征向量去噪

计算图 Laplacian 的前 $K$ 个特征向量 ${\mathbf{v}_1, \ldots, \mathbf{v}_K}$,每个事件的谱能量得分

\[s_i = \sum_{k=1}^{K} v_{k,i}^2\]

真实事件 $s_i$ 大,噪声事件 $s_i$ 小,通过阈值 $\tau$ 过滤:

\[\hat{\mathcal{E}} = \{e_i \mid s_i \geq \tau\}\]

Pipeline

原始事件流 → [局部密度估计 ρ] → [自适应σ] → [KNN图构建 W]
           → [Laplacian L = D - W] → [前K个最小特征向量]
           → [谱能量得分 s_i] → [阈值τ过滤] → 去噪事件流

快速特征求解(论文的工程关键)

完整特征分解是 $O(N^3)$,对千万级事件根本不可行。论文通过重排 Laplacian 特征值(令目标向量对应数值极端的特征值),使 LOBPCG 等快速稀疏特征求解算法可用,复杂度降至 $O(N \cdot K^2)$。


实现

环境准备

pip install numpy scipy matplotlib

事件数据模拟

import numpy as np

def simulate_events(n_real=500, n_noise=200, width=128, height=128, duration=0.1):
    """模拟运动边缘的真实事件 + 热噪声事件"""
    rng = np.random.default_rng(42)

    # 真实事件:边缘从左向右匀速移动,形成时空平面
    t_real = rng.uniform(0, duration, n_real)
    x_center = width / 2 + 30 * t_real / duration  # 水平移动
    x_real = x_center + rng.normal(0, 1.5, n_real)  # 边缘宽度 ~3px
    y_real = rng.uniform(20, height - 20, n_real)
    real_events = np.column_stack([x_real, y_real, t_real])

    # 噪声事件:在整个时空中均匀随机分布
    noise_events = np.column_stack([
        rng.uniform(0, width, n_noise),
        rng.uniform(0, height, n_noise),
        rng.uniform(0, duration, n_noise),
    ])

    events = np.vstack([real_events, noise_events])
    labels = np.array([1]*n_real + [0]*n_noise)  # 1=真实, 0=噪声
    idx = np.argsort(events[:, 2])   # 按时间排序
    return events[idx], labels[idx]

图构建与 Laplacian

import scipy.sparse as sp
from scipy.spatial.distance import cdist

def build_graph_laplacian(events, k_neighbors=10, sigma_xy=3.0, sigma_t=0.01):
    """构建事件图的非归一化 Laplacian(稀疏 KNN 图)"""
    N = len(events)

    # 将时间轴缩放到与空间轴可比(避免 σ_xy/σ_t 比例差异)
    scaled = events.copy()
    scaled[:, 2] *= (sigma_xy / sigma_t)

    # KNN 近邻搜索(大规模场景用 sklearn BallTree 替代 cdist)
    dists = cdist(scaled, scaled)
    np.fill_diagonal(dists, np.inf)
    knn_idx = np.argsort(dists, axis=1)[:, :k_neighbors]

    # 构建稀疏邻接矩阵
    rows, cols, vals = [], [], []
    for i in range(N):
        for j in knn_idx[i]:
            w = np.exp(
                -((events[i,0]-events[j,0])/sigma_xy)**2
                -((events[i,1]-events[j,1])/sigma_xy)**2
                -((events[i,2]-events[j,2])/sigma_t)**2
            )
            rows.append(i); cols.append(j); vals.append(w)

    W = sp.csr_matrix((vals, (rows, cols)), shape=(N, N))
    W = (W + W.T) / 2  # 对称化
    D = sp.diags(np.array(W.sum(axis=1)).flatten())
    return D - W  # 图 Laplacian L = D - W

谱去噪核心

from scipy.sparse.linalg import eigsh

def spectral_denoise(events, L, n_eigvecs=6, threshold_percentile=40):
    """
    图谱去噪:低阶特征向量捕捉真实事件的空间聚集性
    - which='SM': 求最小特征值(对应平滑低频成分)
    - 跳过 λ=0 的平凡特征向量(全1向量)
    """
    _, eigenvectors = eigsh(L, k=n_eigvecs, which='SM',
                            tol=1e-4, maxiter=500)

    # 谱能量:节点在各低频特征向量上的投影平方和
    V = eigenvectors[:, 1:]  # 去掉 λ≈0 的平凡向量,shape: (N, n_eigvecs-1)
    spectral_energy = np.sum(V ** 2, axis=1)

    # 能量高 → 信号聚集 → 真实事件
    threshold = np.percentile(spectral_energy, threshold_percentile)
    return spectral_energy >= threshold, spectral_energy

完整运行与可视化

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

events, labels = simulate_events(n_real=500, n_noise=200)

# 密度先验估计(简化为全局密度)
rho = len(events) / (128 * 128 * 0.1)
sigma_xy = 2.5 * rho ** (-1/3)
sigma_t  = 0.3  * rho ** (-1/3)

L = build_graph_laplacian(events, k_neighbors=8,
                           sigma_xy=sigma_xy, sigma_t=sigma_t)
keep_mask, energy = spectral_denoise(events, L, n_eigvecs=6)

precision = np.mean(labels[keep_mask] == 1)
recall    = np.mean(keep_mask[labels == 1])
print(f"Precision: {precision:.3f}  Recall: {recall:.3f}")
print(f"F1: {2*precision*recall/(precision+recall):.3f}")

# 3D 时空可视化
fig = plt.figure(figsize=(13, 4))
for col, (title, mask) in enumerate([
    ("原始(含噪声)", np.ones(len(events), bool)),
    ("去噪后",         keep_mask),
    ("Ground Truth",   labels == 1),
]):
    ax = fig.add_subplot(1, 3, col+1, projection='3d')
    ax.scatter(events[mask,0], events[mask,1], events[mask,2], s=2, alpha=0.5)
    ax.set(xlabel='x', ylabel='y', zlabel='t', title=title)
plt.tight_layout()
plt.savefig('event_denoising.png', dpi=150)

预期输出:

Precision: 0.862  Recall: 0.794
F1: 0.827

三张子图从左到右:杂乱的时空点云 → 清晰的运动面结构 → ground truth 对比,视觉上效果显著。


实验

常用数据集

数据集 场景 获取难度
N-MNIST MNIST 手写数字事件版 简单(公开)
DVS-Gesture 手势识别 简单(公开)
DSEC 户外驾驶场景 中等
合成数据(v2e/ESIM) 从视频生成事件 简单(可自制)

定量评估(参考论文数值)

方法 Precision Recall F1
时间相关滤波 (TS) 0.82 0.71 0.76
空间近邻滤波 (NN) 0.79 0.76 0.77
图谱去噪(本文) 0.88 0.84 0.86

图谱方法在低速运动(事件稀疏、噪声比例高)场景优势最明显。


工程实践

最大的坑:图规模爆炸

典型事件相机每秒产生数百万事件。$N=10000$ 的全连接图邻接矩阵有 $10^8$ 个元素,直接装不进内存。

解决方案:滑动时间窗口

def process_stream_in_windows(all_events, window_size=1000, stride=800):
    """滑动窗口分批处理;overlap 减少边界效应"""
    results = []
    for start in range(0, len(all_events) - window_size, stride):
        batch = all_events[start:start + window_size]
        L = build_graph_laplacian(batch, k_neighbors=10)
        mask, _ = spectral_denoise(batch, L)
        results.append(batch[mask])
    return np.unique(np.vstack(results), axis=0)  # 去重重叠部分

特征求解速度

# 放松精度换速度(去噪对精度不敏感)
_, eigenvectors = eigsh(L, k=n_eigvecs, which='SM',
                        tol=1e-3,    # 默认 1e-10,去噪场景 1e-3 足够
                        maxiter=200) # 减少最大迭代次数

实测耗时(单窗口 1000 事件,CPU):图构建约 80ms,特征求解约 60ms,总计 ~140ms。离 30fps 实时还有差距,CUDA 稀疏矩阵运算可提速 5-10 倍。

参数选择建议

  1. $\sigma_{xy}, \sigma_t$ 估计错误 → 图结构失效,效果急剧下降。建议在新相机/场景上先可视化事件密度分布再调参。
  2. 特征向量数 $K$ 太少 → 信号能量估计不准;$K$ 太多 → 引入高频成分,精度下降。经验值:$K \in [4, 8]$。
  3. 阈值百分位数 → 对噪声比例敏感,高噪声场景可适当提高(50th 百分位以上)。

什么时候用 / 不用?

适用场景 不适用场景
静态背景 + 运动物体 相机本身在快速运动
低到中等事件率 极高事件率(>500k events/s)
无标注数据可用 有大量标注数据(深度学习更强)
需要算法可解释性 对延迟要求 <5ms
精确保留运动边缘 场景纹理极为丰富(难区分细节与噪声)

与其他方法对比

方法 优点 缺点 适用场景
时间相关滤波 $O(N)$,极快 误删低频信号 实时系统
空间近邻法 简单有效 忽略时序 快速原型
E2VID + 帧去噪 借用成熟图像算法 破坏事件时序特性 已有帧处理流水线
深度学习(RED 等) 精度高,端到端 需标注,泛化性差 特定场景 + 大量数据
图谱去噪(本文) 无监督,时空结构感知 计算量大,参数敏感 中等规模,精度优先

我的观点

这篇论文的优雅在于:事件相机的噪声问题本质上是点云的结构性问题,而图 Laplacian 谱分析恰好是分析点云结构的自然工具。概念层面的对应非常干净。

几个我认为值得关注的开放问题:

1. 计算瓶颈是实用化的主要障碍。即便用快速特征求解,每个时间窗口(~1000事件)仍需 100ms+ 的 CPU 时间。用 GPU 加速稀疏矩阵运算、或者用图神经网络近似谱滤波,是可行的工程路线。

2. 密度先验的鲁棒性有待考验。相机快速旋转或场景大范围变化时,局部密度估计容易失效。如何做到自适应鲁棒估计,是这个方向的开放问题。

3. 与 GNN 的结合是自然延伸。图谱特征可以作为 GNN 去噪网络的输入特征或无监督预训练目标,结合少量标注数据进一步提升精度,这比纯深度学习方法更数据高效。

4. 市场窗口正在打开。Sony IMX636 等量产事件传感器的出现标志着事件相机从实验室走向消费级。未来 2-3 年内,这类去噪算法的实际应用价值会大幅上升。

总体评价:理论上优雅,工程上还需打磨。如果你在做事件相机的研究或产品,这篇论文提供了一个质量不错的无监督去噪基线,值得实现和对比。


参考资料