CUDA 导向滤波复现:从公式到 Kernel 管线
编写时间:2023-04
代码仓库:Plumess/Guided-Filter-Using-CUDA
导向滤波(Guided Image Filtering, He et al. TPAMI 2013)的价值在于:它可以让透射图、深度图、mask 这类粗糙估计结果变平滑,同时保留原图边缘。去雾里常用原图灰度作为引导图 I,待滤波的透射图作为 p,输出细化后的 q。
局部线性假设
导向滤波的核心假设很朴素:在局部窗口 ω_k 内,输出 q 与引导图 I 呈线性关系。
系数由局部统计量求得:
最后对重叠窗口得到的 a、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 反而更容易控制:
- 自写 kernel 可以减少输入输出格式转换,不用像调用通用库那样被数据布局牵着走。
- 可以把多次 box filter 共享的缓冲区复用起来,减少中间拷贝。
- 可以和去雾主流程的 stream / memory pool 对齐,更容易做多 Stream 并行。
工程结果
在 RTX 4090 上测试,1080P 导向滤波单帧约 9.8ms(含 malloc 和 memcpy,排掉 GPU 预热首帧)。核心优化来自 BoxFilter 转前缀和查询,让窗口半径与计算量基本脱钩。导向滤波不是去雾链路里唯一的环节,但它决定了透射图边缘是否自然——这一步没做好,输出去雾图会有明显的块状感。