双边滤波python实现


前言

双边滤波的实验原理和在python上的具体代码实现

一、去噪算法

图像去噪是用于解决图像由于噪声干扰而导致其质量下降的问题,通过去噪技术可以有效地提高图像质量,增大信噪比,更好的体现原来图像所携带的信息。在我们的图像中常见的噪声主要有以下4种:加性噪声、乘性噪声、量化噪声、椒盐噪声。根据不同的噪声特点,我们可以采用不同的去噪算法,按照数学运算主要分为两大类,一类是通过滤波(相当于积分的过程),又可以在空域(和频率域(傅立叶变换和小波变换)中分别采用此操作,比如说空域中值滤波对于椒盐噪声有很好的处理效果,另一类是通过偏微分方程,具有各向异性的特点,具有平滑图像和将边缘尖锐化的能力,在低噪声密度的图像处理中取得了较好的效果,但是在处理高噪声密度图像时去噪效果不好。

二、双边滤波算法背景介绍

双边滤波不同于以往的平滑滤波,是一种常用于图像边缘保持的空间域非线性滤波方法,主要是利用邻域内像素点的空间邻近度和像素值相似度来构建高斯权重滤波器。由Tomasi等人提出该算法,在图像处理趋于有着广泛的应用,比如去噪,去马赛克,流光估计等等。

三、双边滤波算法原理

双边滤波之所以能够既作平滑处理又保留边界,是因为它综合了高斯滤波器和α-截尾均值滤波器的特点,同时考虑了空间域与值域,即其核是由空间域核和值域核相乘得到。

  1. 空间域核:由像素位置欧氏距离决定模板权值w_g
    w_g (i,j,k,l)=1/(√2π σ_α ) exp⁡(-((i-k)2+(j-l)2)/(2σ_d^2 ))                式 (1)
    此公式基于高斯函数,其中σ_α 是标准差,(k,l)是模板中心像素坐标,周围像素坐标(i,j)到中心的距离越远,其权重系数越低。
  2. 值域核:由像素值插值决定的模板权值w_p
    在这里插入图片描述
    此公式基于高斯函数,其中σ_r 是标准差,f(k,l)是模板中心像素h灰度值,周围像素灰度值f(i,j)域与中心像素灰度值的差异越大其权重系数越低。
  3. 双边滤波模板:将上述两个模板相乘。
    在这里插入图片描述
    之后用这个模板对图像进行卷积,就得到双边滤波之后的图像。

四、开发环境

① opencv库,是专门解决计算机视觉的库函数,在本次实验中用于打开、存储图片,RGB图片转灰度图,调用其双边滤波函数等图像操作
② math库,提供了许多数学运算函数,本次实验中用到了其中的幂次函数来计算两个滤波模板中涉及到的系数。
③ numpy库,主要用于处理任意维度数组和矩阵。本次实验用此库了构造n*n大小的模板矩阵。
④ matplotlib库,一个常用的画图库,本次实验中用于将多幅图放在一起作对比分析。

五、实验内容

  1. 总设计流程
    . 在这里插入图片描述

  2. 各步骤详细介绍
    . 在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 模板不同参数对不同图像处理效果的分析和探究
    共有3个参数可以设置,对于一幅256*256像素大小的RGB图像,作3组实验,比较其运行时间及图像模糊程度。
    ① 模板大小kernel_size改变,两个标准差为10时的运行时间如下表1所示。
    在这里插入图片描述

  4. 双边滤波与其他去噪算法的图像处理效果对比与分析
    ① 为图像人工加入随机噪声,接着用高斯滤波,均值滤波,中值
    滤波,双边滤波进行去噪处理,比较其去噪效果。
    ② 对于一幅真实人像,接着用高斯滤波,均值滤波,中值滤波,
    双边滤波进行去噪处理,比较其去噪效果。

六、实验代码

python 3.9

import cv2
import math
import numpy as np
import matplotlib.pyplot as plt
import random

#构造距离高斯模板,即模板的中心权重系数高,离中心距离越远,权重系数越小
def kernel_distance_gaussian(kernel_size, gsigma):
    #定中心,找模板边界[-r,r]
    row = kernel_size // 2
    column = kernel_size // 2
    #方差
    gsigma2 = gsigma * gsigma
    # 高斯系数 1 / 根号2π * σ^2
    coefficient = 1 / (math.sqrt(2 * math.pi) * gsigma2)
    #构造二维矩阵0
    gkernel = np.zeros((kernel_size, kernel_size))
    #(i,j)为模板中心像素坐标,按照高斯公式
    for i in range(-row, row + 1):
        for j in range(-column, column + 1):
            gkernel[i + row][j + column] = coefficient * np.exp(-(i * i + j * j) / (2 * gsigma2))

    return gkernel

#构造像素值高斯模板,即模板的中心权重系数高,与中心像素值差异越大的像素权重系数越小
def kernel_pixel_gaussian(image, kernel_size, psigma, centroid_x, centroid_y, channel):
    #定中心,找模板边界[-r,r]
    row = kernel_size // 2
    column = kernel_size // 2
    #对于边界上的像素点需要打补丁,便于后面用模板进行卷积
    # image1 = np.pad(image, ((row, column), (row, column)), constant_values=0)
    # image = image1
    #方差
    psigma2 = 2 * psigma * psigma
    pkernel = np.zeros((kernel_size, kernel_size))
    # image[centroid_x][centroid_y]为模板中心像素值,
    for m in range(-row, row + 1):
        for n in range(-column, column + 1):
            for k in range(0, channel):
                pkernel[m + row][n + column] = np.exp(
                    -pow((image[centroid_x][centroid_y][k] - image[centroid_x + m][centroid_y + n][k]), 2) / (2 * psigma2))
    return pkernel

#构造双边滤波模板,为上述两个模板相乘
def kernel_bilateral(image, kernel_size, psigma, gsigma, centroid_x, centroid_y, channel):
    kernel = np.zeros((kernel_size, kernel_size))
    #得到距离高斯模板
    gkernel = kernel_distance_gaussian(kernel_size, gsigma)
    pkernel = kernel_pixel_gaussian(image, kernel_size, psigma, centroid_x, centroid_y, channel)
    for i in range(0, kernel_size):
        for j in range(0, kernel_size):
            kernel[i][j] = gkernel[i][j] * pkernel[i][j]
    sum_kernel = sum(sum(kernel))
    kernel = kernel / sum_kernel

    return kernel

#开始双边滤波
def start_bilateral(image, kernel_size, psigma, gsigma):
    r = kernel_size // 2
    c = kernel_size // 2
    # # 对于边界上的像素点需要打补丁,便于后面用模板进行卷积
    # image1 = np.pad(image, ((r, c), (r, c)), constant_values=0)
    # image = image1
    #读取图像的行数和列数
    row = image.shape[0]
    col = image.shape[1]
    #RGB为3通道图或灰度图
    if len(image.shape) == 2:
        channel = 1
    else:
        channel = image.shape[2]
    #bilater_image = np.zeros((row, col))

    image = image.reshape(row, col, channel)
    #用0打补丁,方便卷积
    image1 = np.pad(image, ((r, c), (r, c), (0,0)), constant_values=0)
    bilater_image = image.copy()
    for i in range(r, row-r):
        for j in range(c, col-c):

            for k in range(channel):
                kernel_all = kernel_bilateral(image1, kernel_size, psigma, gsigma, i, j, k)
                # img_padding = np.pad(img_1channel, ((r, c), (r, c)), constant_values=0)
                # bilater_image[i][j][k] = np.sum(np.multiply(image[i-r:i+r+1,j-c:j+c+1],kernel_all))
                # 用每一个小模板与下面的像素值相乘求和做卷积
                weight =0
                for m in range(-r, r + 1):
                    for n in range(-c, c + 1):
                        weight += image[i + m][j + n][k] * kernel_all[m + r][n + c]

            bilater_image[i][j][k] = weight
    return bilater_image

def pepper_and_salt(img, percentage):
    num = int(percentage * img.shape[0] * img.shape[1])  # 椒盐噪声点数量
    random.randint(0, img.shape[0])
    img2 = img.copy()
    for i in range(num):
        X = random.randint(0, img2.shape[0] - 1)  # 从0到图像长度之间的一个随机整数,因为是闭区间所以-1
        Y = random.randint(0, img2.shape[1] - 1)
        if random.randint(0, 1) == 0:  # 黑白色概率55开
            img2[X, Y] = (255, 255, 255)  # 白色
        else:
            img2[X, Y] = (0, 0, 0)  # 黑色
    return img2

if __name__ == '__main__':
    path = 'face2.png'
    #image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
    image = cv2.imread(path, 1)
    #cans

    # if kernel_size % 2 == 0:
    #     print("ERROR: the value of kernel_size should be odd!")
    gsigma = 5
    psigma = 5

    #对于边界上的像素点需要打补丁,便于后面用模板进行卷积

    # image_bilateral = start_bilateral(image, kernel_size=1 , psigma , gsigma)
    # image_bilateral = start_bilateral(image, kernel_size=3, psigma, gsigma)
    image_bilateral = start_bilateral(image, kernel_size=55, psigma = 5, gsigma = 5)
    cv2.imwrite("face2_kernel5psigma20gsigma20.png",image_bilateral)
    plt.figure()
    plt.subplot(121)

    plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)), plt.axis('off')
    plt.subplot(122)
    plt.imshow(cv2.cvtColor(image_bilateral, cv2.COLOR_BGR2RGB)), plt.axis('off')

    plt.show()

七、实验结果

  1. 双边滤波基本实验效果(以调用库函数为参照)
    以直接调用库函数双边滤波得到的图片(中间)为基准,查验自己手写的双边滤波去噪效果。前两幅灰度图是图像处理中常用的图像,Lena和CameraMan,可以看到本次实验较为成功,双边滤波确实有平滑但保持边缘的特性,特别的从最后一幅图像可以看到,双边滤波相当于人像磨皮的功能,去掉了脸上很多的小雀斑。
    在这里插入图片描述
    在这里插入图片描述

  2. 不同模板参数下的实验效果对比与分析
    ①在模板大小为 30 时,让高斯距离模板和高斯灰度值模板的标准差为 10,20,40,60,100 时,比较其双边滤波的效果。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 不同去噪算法的实验效果对比与分析

    在这里插入图片描述
    在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐