一、双边滤波算法(Bilateral Filter)

1、原理

高斯滤波是以距离为权重,设计滤波模板作为滤波系数,只考虑了像素间的空间位置上的关系,因此滤波的结果会丢失边缘的信息。

高斯滤波的缺陷如下图所示:平坦区域正常滤波,图像细节没有变化,而在突变的边缘上,因为只使用了距离来确定滤波权重,导致边缘被模糊。

python cv双边滤波 传统双边滤波参数取值_python cv双边滤波


在高斯基础上,进一步优化,叠加了像素值的考虑,因此也就引出了双边滤波,一种非线性滤波,滤波效果对保留边缘更有效。

python cv双边滤波 传统双边滤波参数取值_权重_02


空间距离:当前点距离滤波模板中心点的欧式距离。

灰度距离:当前点距离滤波模板中心点的灰度的差值的绝对值。

双边滤波的核函数是空间域核与像素范围域核的综合结果:

1)在图像的平坦区域,像素值变化很小,那么像素差值接近于0,对应的像素范围域权重接近于1,此时空间域权重起主要作用,相当于进行高斯模糊;

2)在图像的边缘区域,像素值变化很大,那么像素差值大,对应的像素范围域权重变大,即使距离远空间域权重小,加上像素域权重总的系数也较大,从而保护了边缘的信息。

双边滤波的效果如下图,在突变的边缘上,使用了像素差权重,所以很好的保留了边缘。

python cv双边滤波 传统双边滤波参数取值_灰度_03


python cv双边滤波 传统双边滤波参数取值_权重_04

2、opence-python 实现

import cv2
import numpy as np

image = cv2.imread('./image/cat.jpeg')
image_blur = cv2.blur(image, (3, 3))
image_bilater = np.hstack([  # 结果图像的水平拼接
    image_blur,
    cv2.bilateralFilter(image_blur, 5, 21, 21),
    cv2.bilateralFilter(image_blur, 7, 31, 31),
    cv2.bilateralFilter(image_blur, 9, 41, 41)
])
cv2.imshow('image_bilater', image_bilater)
cv2.waitKey(0)

python cv双边滤波 传统双边滤波参数取值_权重_05

二、导向滤波算法(Guided Filter)

1、原理

引导滤波的思想用一张引导图像产生权重,从而对输入图像进行处理,这个过程可以表示为下面公式:


python cv双边滤波 传统双边滤波参数取值_python cv双边滤波_06


公式中 q、I、p分表表示输出图像、引导图像和输入图像 ,i、j分别表示图像中像素点的索引。可以看到上方公式中权重 W 仅与引导图像 I 有关,而在双边滤波中权重 W 由输入图像自身决定。

python cv双边滤波 传统双边滤波参数取值_权重_07


python cv双边滤波 传统双边滤波参数取值_权重_08

2、python实现

"""
    uv通道滤波,取出颜色噪声
"""
import cv2
import numpy as np

image_path = './image/wait_uv_filter.png'
image = cv2.imread(image_path)
cv2.namedWindow('image', 0)
cv2.resizeWindow('image', 600, 500)
cv2.imshow('image', image)
cv2.waitKey(0)

def my_guidedFilter_oneChannel(srcImg, guidedImg, rad=9, eps=0.01):
    srcImg = srcImg / 255.0
    guidedImg = guidedImg / 255.0

    P_mean = cv2.boxFilter(srcImg, -1, (rad, rad), normalize=True)
    I_mean = cv2.boxFilter(guidedImg, -1, (rad, rad), normalize=True)

    I_square_mean = cv2.boxFilter(np.multiply(guidedImg, guidedImg), -1, (rad, rad), normalize=True)
    I_mul_P_mean = cv2.boxFilter(np.multiply(srcImg, guidedImg), -1, (rad, rad), normalize=True)

    var_I = I_square_mean - np.multiply(I_mean, I_mean)
    cov_I_P = I_mul_P_mean - np.multiply(I_mean, P_mean)

    a = cov_I_P / (var_I + eps)
    b = P_mean - np.multiply(a, I_mean)

    a_mean = cv2.boxFilter(a, -1, (rad, rad), normalize=True)
    b_mean = cv2.boxFilter(b, -1, (rad, rad), normalize=True)

    dstImg = np.multiply(a_mean, guidedImg) + b_mean

    return dstImg * 255.0


def my_guidedFilter_threeChannel(srcImg, guidedImg, rad=9, eps=0.01):
    img_shape = np.shape(srcImg)
    dstImg = np.zeros(img_shape, dtype=float)
    for ind in range(0, img_shape[2]):
        dstImg[:, :, ind] = my_guidedFilter_oneChannel(srcImg[:, :, ind],guidedImg[:, :, ind], rad, eps)
    dstImg = dstImg.astype(np.uint8)
    return dstImg


def main():
    img = cv2.imread(input_fn)
    print(np.shape(img))

    img_y = cv2.split(img)[0]
    img_u = cv2.split(img)[1]
    img_v = cv2.split(img)[2]

    image_guidedFilter = my_guidedFilter_threeChannel(img, img, 15, 0.0001)
    print(np.shape(image_guidedFilter))
    cv2.namedWindow('image_guidedFilter', 0)
    cv2.resizeWindow('image_guidedFilter', 600, 500)
    cv2.imshow('image_guidedFilter', image_guidedFilter)
    cv2.waitKey(0)

    dst_u = my_guidedFilter_oneChannel(img_u, img_u, 9, 0.0001).astype(np.uint8)
    dst_v = my_guidedFilter_oneChannel(img_v, img_v, 9, 0.0001).astype(np.uint8)
    dstimg_uv = cv2.merge([img_y, dst_u, dst_v])
    cv2.namedWindow('dstimg_uv', 0)
    cv2.resizeWindow('dstimg_uv', 600, 500)
    cv2.imshow('dstimg_uv', dstimg_uv)
    cv2.waitKey(0)


if __name__ == '__main__':
    main()