图像处理方法——导向滤波的原理

这篇文章上次修改于 1 年前,可能部分内容已经不适用,如有疑问可询问作者。

图像处理方法——导向滤波的原理

参考链接:详解——导向滤波(Guided Filter)和快速导向滤波


前言


1. 保边滤波的核心目标

核心矛盾:传统滤波(如高斯滤波)在平滑噪声时会模糊图像边缘
解决思路:在平滑区域内进行强滤波,在边缘处保持弱滤波
数学本质:设计权重函数使滤波器对边缘区域敏感


2. 经典保边滤波算法分类

(1) 基于局部统计的方法

  • 双边滤波 (Bilateral Filter)
    • 核心公式:
    • 特点:空间+颜色相似性双重约束
    • 缺点:O(N²)计算复杂度

(2) 基于偏微分方程的方法

  • 各向异性扩散 (Anisotropic Diffusion)
    • 原理:沿边缘切线方向扩散,法线方向抑制扩散
    • 控制方程:

(3) 全局优化方法

  • 加权最小二乘 (WLS Filter)
    • 能量函数:
    • 特点:显式控制平滑强度

(4) 基于引导图像的方法 → 导向滤波 (Guided Filter)

  • 核心创新:利用引导图像生成线性系数
  • 关键公式:
  • 优势:O(N)线性复杂度,边缘保持性强

3. 导向滤波技术详解 算法流程

  1. 输入:引导图像,输入图像,窗口半径,正则化参数

  2. 计算局部均值: ,

  3. 计算方差与协方差: ,

  4. 线性系数计算: ,

  5. 输出滤波结果:

特性对比

滤波器类型时间复杂度边缘保持梯度反转硬件友好性
双边滤波O(N²)优秀存在差
WLSO(N)极好无一般
导向滤波O(N)优秀无极佳

导向滤波实现

输入: 输入图像p,经过引导图像I, 滤波得到输出图像q

公式推导见前文参考博客

最终结果如上, 为引导图I在窗口内的均值, 下面的 为引导图的方差。

过程来说,我们需要实现的是窗口内的I均值,p均值和Ip乘积的均值。至于方差换算,很容易推导得到为I的平方减去I均值的平方。

实际上可以直接用平均值卷积核来实现上述的所有操作。

python代码如下

PYTHON
def Guidedfilter(im, p, r, eps):
    mean_I = cv2.boxFilter(im, cv2.CV_64F, (r, r))
    mean_p = cv2.boxFilter(p, cv2.CV_64F, (r, r))
    mean_Ip = cv2.boxFilter(im * p, cv2.CV_64F, (r, r))
    cov_Ip = mean_Ip - mean_I * mean_p

    mean_II = cv2.boxFilter(im * im, cv2.CV_64F, (r, r))
    var_I = mean_II - mean_I * mean_I

    a = cov_Ip / (var_I + eps)
    b = mean_p - a * mean_I

    mean_a = cv2.boxFilter(a, cv2.CV_64F, (r, r))
    mean_b = cv2.boxFilter(b, cv2.CV_64F, (r, r))

    return mean_a * im + mean_b
Copy

该工作运用在诸如去雾等上面。用灰度图当引导图——

加一个新问题,如果输入“图像”p 是一个离散的数据又该如何处理。

答案是公式不变,但是不再使用简单的平均值卷积核。

下面给出相关修改,其中的mask作为一个判断是否为合法离散值的掩码,其余加入的一些极小值操作是为了防止出现0相除的情况,其余大部分同正常算法。

PYTHON
def lidar_guided_filter(I, p, r, eps=1e-3):
        # 定义mask
        mask = (p > 0) & (p < 1)
        p = p * mask

        # 定义窗口图f_k
        f_k = cv2.boxFilter(mask.astype(np.float32), -1, (r, r), normalize=False)
        # 计算m_p
        m_p = cv2.boxFilter(p + 1e-6, -1, (r, r), normalize=False) / (f_k + 1e-6)
        # 计算m_Ip
        m_Ip = cv2.boxFilter(I * p + 1e-6, -1, (r, r), normalize=False) / (f_k + 1e-6)
 
        # 计算m_II
        I_mask = I * mask
        m_II = cv2.boxFilter(I_mask * I_mask + 1e-6, -1, (r, r), normalize=False) / (f_k + 1e-6)

        # 其余步骤不变
        cov_Ip = m_Ip - m_p * m_p
        var_I = m_II - m_p * m_p

        a = cov_Ip / (var_I + eps)
        b = m_p - a * m_p

        m_a = cv2.boxFilter(a, -1, (r, r))
        m_b = cv2.boxFilter(b, -1, (r, r))
        cv2.imshow('m_a', m_a)
        cv2.waitKey(0)
        cv2.imshow('m_b', m_b)
        cv2.waitKey(0)
        return m_a * I + m_b
Copy