CUDA 导向滤波复现:从公式到 Kernel 管线

2023 年 4 月 9 日 星期日(已编辑)
/ , , , ,
7
摘要
导向滤波(He et al. TPAMI 2013)的 CUDA 完整实现。核心在 BoxFilter 转前缀和查询,让窗口半径与计算量脱钩,输出边缘自然、无块状感的透射图。

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

CUDA 导向滤波复现:从公式到 Kernel 管线

编写时间:2023-04

代码仓库:Plumess/Guided-Filter-Using-CUDA

导向滤波(Guided Image Filtering, He et al. TPAMI 2013)的价值在于:它可以让透射图、深度图、mask 这类粗糙估计结果变平滑,同时保留原图边缘。去雾里常用原图灰度作为引导图 I,待滤波的透射图作为 p,输出细化后的 q

局部线性假设

导向滤波的核心假设很朴素:在局部窗口 ω_k 内,输出 q 与引导图 I 呈线性关系。

qi=akIi+bk,iωk q_i=a_k I_i+b_k, \quad i\in \omega_k

系数由局部统计量求得:

ak=covωk(I,p)varωk(I)+ϵ a_k=\frac{cov_{\omega_k}(I,p)}{var_{\omega_k}(I)+\epsilon}
bk=meanωk(p)akmeanωk(I) b_k=mean_{\omega_k}(p)-a_k mean_{\omega_k}(I)

最后对重叠窗口得到的 ab 再做平均:

q=aˉI+bˉ q=\bar a I+\bar b

CUDA 复现的关键

这部分复现的关键不在公式,而在 BoxFilter 的实现。导向滤波里需要多次局部均值,如果每个像素都扫一个 r x r 窗口,半径一大就会很慢。CUDA 实现里把 BoxFilter 转成前缀和/积分图查询,让窗口求和与半径基本脱钩。

计算项用途
mean(I)引导图局部均值
mean(p)输入图局部均值
corr(I)计算 var(I)
corr(I,p)计算 cov(I,p)
mean(a), mean(b)平均重叠窗口系数

Kernel 管线

这个版本把所有函数都从 CPU 端改写为 CUDA GPU 实现。核心经验是:不要只把最显眼的公式搬到 kernel 里,更要把隐藏的局部统计、数组访问和中间缓存一起想清楚。

和 OpenCV CUDA 的差异

OpenCV 的 CUDA BoxFilter 能用,但为了去雾链路里的数据布局和中间缓存复用,自己写 kernel 反而更容易控制:

  1. 自写 kernel 可以减少输入输出格式转换,不用像调用通用库那样被数据布局牵着走。
  2. 可以把多次 box filter 共享的缓冲区复用起来,减少中间拷贝。
  3. 可以和去雾主流程的 stream / memory pool 对齐,更容易做多 Stream 并行。

工程结果

在 RTX 4090 上测试,1080P 导向滤波单帧约 9.8ms(含 malloc 和 memcpy,排掉 GPU 预热首帧)。核心优化来自 BoxFilter 转前缀和查询,让窗口半径与计算量基本脱钩。导向滤波不是去雾链路里唯一的环节,但它决定了透射图边缘是否自然——这一步没做好,输出去雾图会有明显的块状感。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...