python 图像处理——图像平滑(图像滤波)之线性滤波(手写均值滤波、方框滤波、高斯滤波)

本文介绍了图像处理中的噪声滤波技术,包括线性滤波的均值滤波、方框滤波和高斯滤波。均值滤波通过邻域像素平均值来平滑图像,但可能导致图像模糊;方框滤波与均值滤波相似,非归一化处理可能产生溢出;高斯滤波利用高斯函数权重,能有效抑制高斯噪声,同时保留更多图像细节。这些滤波方法在图像去噪中各有优势和局限性。
摘要由CSDN通过智能技术生成

前言
在数字成像过程中,由于多种因素——包括传感器噪声、传输误差、量化误差等——图像往往会受到各种形式的干扰。这些不受欢迎的“访客”会以噪点或粗糙纹理的形式出现在我们的照片或视频中,影响图像的整体美感和后续处理的准确性。为了解决这些问题,图像平滑技术应运而生,它们的核心目标是在保留图像重要特征的同时抑制噪声。常用于消除噪声的图像平滑方法包括三种线性滤波(均值滤波、方框滤波、高斯滤波)和两种非线性滤波(中值滤波、双边滤波),本文将详细讲解三种线性滤波方法。

一、图像平滑

图像平滑是一项简单且使用频率很高的图像处理方法,可以用来压制、弱化或消除图像中的细节、突变、边缘和噪声,最常见的是用来减少图像上的噪声。何为图像噪声?噪声是妨碍人的感觉器官所接受信源信息理解的因素,在图像上常表现为一引起较强视觉效果的孤立像素点或像素块,是不可预测只能用概率统计方法认识的随机误差。一般,噪声信号与要研究的对象不相关,它以无用的信息形式出现,扰乱图像的可观测信息。通俗的说就是噪声让图像不清楚。

一幅图像不可避免地要受到各种噪声源的干扰,所以噪声滤除往往是图像处理中的第一步,滤波效果好坏将直接影响后续处理结果,噪声滤除在图像处理中占有相当重要的地位。噪声滤除算法多种多样,可以从设计方法上分为线性滤波算法和非线性滤波算法两大类

(1)线性滤波
在图像处理中,对邻域中的像素的计算为线性运算时,如利用窗口函数进行平滑加权求和的运算,或者某种卷积运算,都可以称为线性滤波。在数字信号处理和数字图像处理的早期研究中,线性滤波器是噪声抑制处理的主要手段,如均值滤波、方框滤波、高斯滤波等。

线性滤波算法对高斯型噪声有较好的滤波效果,而当信号频谱与噪声频谱混叠时或者当信号中含有非叠加性噪声时(例如由系统非线性引起的噪声或存在非高斯噪声等),线性滤波器的处理结果就很难令人满意。

(2)非线性滤波
非线性滤波利用原始图像跟模版之间的一种逻辑关系得到结果,如中值滤波、双边滤波等。非线性滤波技术从某种程度上弥补了线性滤波方法的不足,由于它能够在滤除噪声的同时较好地保持图像信号的高频细节,从而得到广泛的应用。著名学者 Tukey [3]于1971年首次提出了一种非线性滤波器——中值滤波器,从此揭开了非线性滤波方法研究的序幕。非线性滤波技术发展到现在,基于中值滤波的改进算法层出不穷,在非线性滤波算法中占有重要的地位。另外很多新的非线性滤波算法也相继涌现,如基于数学形态学的滤波方法、基于模糊理论的滤波方法、基于神经网络的滤波方法等,它们为图像滤波技术提供新的思路。

后文将详细介绍以下常用的一些滤波器,包括均值滤波、方框滤波、高斯滤波、中值滤波等,如表1-1所示。
在这里插入图片描述
图3为这五种滤波的效果对比,从滤波的结果可以看出各种滤波算法对图像的作用非常不同,有些变化非常大,有些甚至跟原图一样。在实际应用时,应根据噪声的特点、期望的图像和边缘特征等来选择合适的滤波器,这样才能发挥图像滤波的最大优点。
在这里插入图片描述
在图像产生、传输和复制过程中,常常会因为多方面原因而被噪声干扰或出现数据丢失,降低了图像的质量。这就需要对图像进行一定的增强处理以减小这些缺陷带来的影响。

二、均值滤波

均值滤波是最简单的一种线性滤波算法,它是指在原始图像上对目标像素给一个模板,该模板包括了其周围的临近像素(以目标像素为中心的周围8个像素,构成一个滤波模板,即去掉目标像素本身),再用模板中的全体像素的平均值来代替原来的像素值。换句话说,均值滤波输出图像的每一个像素值是其周围M×M个像素值的加权平均值。

图4表示均值滤波处理的过程,中心红色点的像素值为蓝色背景区域像素值求和的均值。5×5的矩阵称之为模糊内核,针对原始图像内的像素点,均值滤波采用核对其像素逐个进行均值处理,并得到最终的效果图。
在这里插入图片描述
其中红色区域的像素值均值滤波处理过程为:
在这里插入图片描述
均值滤波算法比较简单,计算速度较快,对周期性的干扰噪声有很好的抑制作用,但是它不能很好地保护图像的细节,在图像去噪的同时,也破坏了图像的细节部分,从而使图像变得模糊。

手搓代码如下:

def Mean_blur(img,kernel_size):
    if kernel_size%2 ==0:
        print(f'输入的kernel_size要为奇数哦')
        sys.exit()

    if len(img.shape) ==3:
        channel = 3
    else:
        channel = 1


    weights_matrix = np.ones((kernel_size,kernel_size))
    weights_matrix /=(kernel_size**2)

    #用于填充图像,否则输出的图像尺寸将小于输入的图像尺寸
    #计算方式如下 :(w - kernel_size + 2p)/s +1 = w 不用问公式怎么来的,学到CNN就知道了
    #在这里s=1 那么就是 2p = kernel_size -1
    padding = int((kernel_size-1)/2)
    h, w = img.shape[0], img.shape[1]
    new_h,new_w = h+2*padding,w+2*padding

    if channel ==1:

        image = np.zeros((new_h, new_w))
        result = np.zeros((h, w))

        for i in range(padding, h + padding):  #从第padding行 第padding列开始
            for j in range(padding, w + padding):
                image[i, j] = img[i - padding, j - padding]

        for i in range(new_h-kernel_size+1):
            for j in range(new_w-kernel_size+1):
                roi = image[i:i+kernel_size,j:j+kernel_size]
                # 对应元素相乘
                product_matrix = np.multiply(roi, weights_matrix)
                # 加权求和
                weighted_sum = np.sum(product_matrix)
                result[i,j] = int(weighted_sum)
    else:
        image = np.zeros((3,new_h, new_w))
        result = np.zeros((3,new_h, new_w))
        img = np.transpose(img,[2,0,1]) #调换通道 变成(channel,height,width)

        for k in range(3):
            for i in range(padding, h + padding):
                for j in range(padding, w + padding):
                        image[k][i,j] = img[k][i - padding, j - padding]

        for k in  range(3):
            for i in range(new_h-kernel_size+1):
                for j in range(new_w-kernel_size+1):

                    roi = image[k][i:i+kernel_size,j:j+kernel_size]
                    # 对应元素相乘
                    product_matrix = np.multiply(roi, weights_matrix)
                    # 加权求和
                    weighted_sum = np.sum(product_matrix)
                    result[k][i,j] = int(weighted_sum)
        result = np.transpose(result,[1,2,0])
    cv2.imwrite('/home/netted/img_process_ml/photo/test.jpg',result)
    return result

Python调用OpenCV中的cv2.blur()函数实现均值滤波处理,其函数原型如下所示,输出的dst图像与输入图像src具有相同的大小和类型。

dst = blur(src, ksize[, dst[, anchor[, borderType]]])

—src表示输入图像,它可以有任意数量的通道,但深度应为CV_8U、CV_16U、CV_16S、CV_32F或CV_64F
—ksize表示模糊内核大小,以(宽度,高度)的形式呈现
—anchor表示锚点,即被平滑的那个点,其默认值Point(-1,-1)表示位于内核的中央,可省略
—borderType表示边框模式,用于推断图像外部像素的某种边界模式,默认值为BORDER_DEFAULT,可省略

常见的模糊内核包括(3,3)和(5,5),如公式(2)和(3)所示:

在这里插入图片描述

图像均值滤波的Python实现代码如下所示,需要注意的是,代码中使用的是5×5的模板,plt.rcParams是用于设置中文汉字正常显示

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

# 读取图片
img = cv2.imread('C:/Users/Administrator/Desktop/picture/xiaoxin.png')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 均值滤波
result = cv2.blur(source, (5, 5))

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']

# 显示图形
titles = ['原始图像', '均值滤波']
images = [source, result]
for i in range(2):
    plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

“xiaoxin”图输出结果如图2-1所示,左边表示含有噪声的待处理原图,右边是均值滤波处理后的图像,图像中的椒盐噪声被去除大部分。
在这里插入图片描述
如果图像中的噪声仍然存在,可以增加模糊内核的大小,比如使用5×5、10×10,甚至20×20的模板。图6就是使用10×10的内核,但是处理后的图像会逐渐变得更模糊。

在这里插入图片描述
图像均值滤波是通过模糊内核对图像进行平滑处理,由于模糊内核中的每个权重值都相同,故称为均值。该方法在一定程度上消除了原始图像中的噪声,降低了原始图像的对比度,但也存在一定缺陷,它在降低噪声的同时使图像变得模糊,尤其是边缘和细节处,而且模糊内核越大,模糊程度越严重。

三、方框滤波

方框滤波又称为盒式滤波,它利用卷积运算对图像邻域的像素值进行平均处理,从而实现消除图像中的噪声。方框滤波和和均值滤波的模糊内核基本一样,区别为是否需要进行均一化处理。

Python调用OpenCV中的cv2.boxFilter()函数实现方框滤波处理,其函数原型如下所示:

dst = boxFilter(src, depth, ksize[, dst[, anchor[, normalize[, borderType]]]])

—src表示输入图像
—dst表示输出图像,其大小和类型与输入图像相同
—depth表示输出图像深度,通常设置为“-1”,表示与原图深度一致
—ksize表示模糊内核大小,以(宽度,高度)的形式呈现
—normalize表示是否对目标图像进行归一化处理,默认值为true
—anchor表示锚点,即被平滑的那个点,其默认值Point(-1,-1)表示位于内核的中央,可省略
—borderType表示边框模式,用于推断图像外部像素的某种边界模式,默认值为BORDER_DEFAULT,可省略

常见的模糊内核ksize包括(3,3)和(5,5),如下所示:
在这里插入图片描述
在这里插入图片描述

参数normalize表示是否对目标图像进行归一化处理。

(1)当normalize为true时,需要执行归一化处理,方框滤波就变成了均值滤波。其中,归一化就是把要处理的像素值都缩放到一个范围内,以便统一处理和直观量化。

(2)当normalize为false时,表示非归一化的方框滤波,不进行均值化处理,实际上就是求周围各像素的和。但此时很容易发生溢出,多个像素值相加后的像素值大于255,溢出后的像素值均设置为255,即白色。

参数normalize的定义如公式(6)所示。

在这里插入图片描述
这个就不用手搓了吧,要不要 只要删去weights_matrix /=(kernel_size**2) 这一行就好了

图像方框滤波的Python实现代码如下所示,代码中使用3×3的核,normalize=0表示不进行图像归一化处理。

# -*- coding: utf-8 -*-
# By:Eastmount
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取图片
img = cv2.imread('C:/Users/Administrator/Desktop/picture/xiaoxin.png')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 方框滤波
result = cv2.boxFilter(source, -1, (3, 3), normalize=0)

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']

# 显示图形
titles = ['原始图像', '方框滤波']
images = [source, result]
for i in range(2):
    plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

方框滤波非归一化处理的输出结果如图3-1所示,处理后的效果图中包含很多白色的像素点,这是因为图像像素求和结果发生溢出(超过255)。由此可见,进行非归一化的处理时,得到的图像包含白色过多,对源图像的毁坏太大。
在这里插入图片描述
下面代码是使用5×5内核,进行归一化方框滤波处理的代码,其输出结果与3×3内核均值滤波完全相同。

# -*- coding: utf-8 -*-
# By:Eastmount
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取图片
img = cv2.imread('C:/Users/Administrator/Desktop/picture/xiaoxin.png')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 方框滤波
result = cv2.boxFilter(source, -1, (5, 5), normalize=1)

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']

# 显示图形
titles = ['原始图像', '方框滤波']
images = [source, result]
for i in range(2):
    plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

输出结果如图3-3所示:
在这里插入图片描述

四、高斯滤波

为了克服局部平均法造成图像模糊的弊端,又提出了一些保持边缘细节的局部平滑算法,图像高斯滤波(高斯平滑)就是这样一种算法。它是应用邻域平均思想对图像进行平滑的一种线性平滑滤波,对于抑制服从正态分布的噪声非常有效,适用于消除高斯噪声,被广泛应用于图像处理的减噪过程。

图像高斯滤波为图像不同位置的像素值赋予了不同的权重,距离越近的点权重越大,距离越远的点权重越小。它与方框滤波和均值滤波不同,它对邻域内的像素进行平均时,为不同位置的像素赋予不同的权值。通俗地讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值(权重不同)经过加权平均后得到。

高斯滤波引入了数学中的高斯函数(正态分布函数),一个二维高斯函数如下公式(9-1)所示,其中σ为标准差。高斯加权平均中,最重要是σ的选取,标准差代表数据离散程度,如果σ较小,则高斯分布中心区域将更加聚集,平滑效果更差;反之,如果σ较大,高斯分布中心区域将更离散,平滑效果更明显。

公式9

当然,为了更好的理解,大家可以看下面这个公式就好懂很多,σ是自己选取的,(x,y)代表高斯核的任意一个格子距离自己设定的中心点(也可以理解成中心格子或者理解成坐标原点)的大小,例如3×3高斯核中我们常用的中心点在第二行第二列,然后第一行第一列格子的x和y 计算方式就是x=1-2 =-1,y=1-2=-1 然后带入函数h(x,y)便得到高斯核中第一行第一列的系数了
在这里插入图片描述
给大家看看正态分布σ的变化对值的影响:
在这里插入图片描述

高斯核的生成代码如下:

import math
def weights_matrix_init(kernel_size,sigmaX=0.0,sigmaY=0.0): #sigma:数学公式中的:σ
    matrix = np.zeros((kernel_size,kernel_size))
    center = int(kernel_size /2)

    sum = 0
    for i in range(kernel_size):
        for j in range(kernel_size):
            x,y = i -center ,j-center
            matrix[i,j] = np.exp(-(x**2+y**2)/2*sigmaX*sigmaY)
            matrix[i,j] = matrix[i,j] /2*math.pi*sigmaX*sigmaY
            sum+=matrix[i,j]

    matrix/=sum

    return matrix

高斯滤波的核心思想是对高斯函数进行离散化,以离散点上的高斯函数值为权值,对图像中的每个像素点做一定范围邻域内的加权平均,从而有效地消除高斯噪声。高斯滤波让临近中心的像素点具有更高的重要度,对周围像素计算加权平均值,举个例子,如图4-1所示,假设高斯核为第二个格子所示,其中心位置权重最高为0.4。

在这里插入图片描述

然后手搓实现的代码如下:

import math
def weights_matrix_init(kernel_size,sigmaX=0.0,sigmaY=0.0): #sigma:数学公式中的:σ
    matrix = np.zeros((kernel_size,kernel_size))
    center = int(kernel_size /2)

    sum = 0
    for i in range(kernel_size):
        for j in range(kernel_size):
            x,y = i -center ,j-center
            matrix[i,j] = np.exp(-(x**2+y**2)/2*sigmaX*sigmaY)
            matrix[i,j] = matrix[i,j] /2*math.pi*sigmaX*sigmaY
            sum+=matrix[i,j]

    matrix/=sum

    return matrix


def Gauusasion(img,kernel_size,sigmaX=0.0,sigmaY=0.0):
    if kernel_size%2 ==0:
        print(f'输入的kernel_size要为奇数哦')
        sys.exit()

    if len(img.shape) ==3:
        channel = 3
    else:
        channel = 1

    if sigmaX ==0.0:
        sigmaX = ((kernel_size-1)/2)-1
        sigmaX = 0.3*sigmaX+0.8
        sigmaY = sigmaX

    weights_matrix = weights_matrix_init(kernel_size,sigmaX,sigmaY)


    #padding  用于填充图像,否则输出的图像尺寸将小于输入的图像尺寸
    #计算方式如下 :(w - kernel_size + 2p)/s +1 = w 不用问公式怎么来的,学到CNN就知道了
    #在这里s=1 那么就是 2p = kernel_size -1
    padding = int((kernel_size-1)/2)
    h, w = img.shape[0], img.shape[1]
    new_h,new_w = h+2*padding,w+2*padding

    if channel ==1:

        image = np.zeros((new_h, new_w))
        result = np.zeros((h, w))

        for i in range(padding, h + padding):  #从第padding行 第padding列开始
            for j in range(padding, w + padding):
                image[i, j] = img[i - padding, j - padding]

        for i in range(new_h-kernel_size+1):
            for j in range(new_w-kernel_size+1):
                roi = image[i:i+kernel_size,j:j+kernel_size]
                # 对应元素相乘
                product_matrix = np.multiply(roi, weights_matrix)
                # 加权求和
                weighted_sum = np.sum(product_matrix)
                result[i,j] = int(weighted_sum)
    else:
        image = np.zeros((3,new_h, new_w))
        result = np.zeros((3,new_h, new_w))
        img = np.transpose(img,[2,0,1]) #调换通道 变成(channel,height,width)

        for k in range(3):
            for i in range(padding, h + padding):
                for j in range(padding, w + padding):
                        image[k][i,j] = img[k][i - padding, j - padding]

        for k in  range(3):
            for i in range(new_h-kernel_size+1):
                for j in range(new_w-kernel_size+1):

                    roi = image[k][i:i+kernel_size,j:j+kernel_size]
                    # 对应元素相乘
                    product_matrix = np.multiply(roi, weights_matrix)
                    # 加权求和
                    weighted_sum = np.sum(product_matrix)
                    result[k][i,j] = int(weighted_sum)
        result = np.transpose(result,[1,2,0])
    return result

:这个代码是作者自己理解的基础上编写的,存在挺多优化空间,希望大家能优化优化,哈哈,尤其是运行速度方面

Python中OpenCV主要调用GaussianBlur()函数实现高斯平滑处理,函数原型如下所示:

dst = GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])

—src表示待处理的输入图像
—dst表示输出图像,其大小和类型与输入图像相同
—ksize表示高斯滤波器模板大小,ksize.width和ksize.height可以不同,但它们都必须是正数和奇数,它们也可以是零,即(0, 0)
—sigmaX表示高斯核函数在X方向的高斯内核标准差
—sigmaY表示高斯核函数在Y方向的高斯内核标准差。如果sigmaY为零,则设置为等于sigmaX,如果两个sigma均为零,则分别从ksize.width和ksize.height计算得到
—borderType表示边框模式,用于推断图像外部像素的某种边界模式,默认值为BORDER_DEFAULT,可省略

下面代码是使用7×7核模板进行高斯滤波处理。

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

# 读取图片
img = cv2.imread('C:/Users/Administrator/Desktop/picture/xiaoxin.png')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 高斯滤波
result = cv2.GaussianBlur(source, (7, 7), 0)

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']

# 显示图形
titles = ['原始图像', '高斯滤波']
images = [source, result]
for i in range(2):
    plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

输出结果如图4-2所示,左边为待处理图像,右边为高斯滤波处理后图像。
在这里插入图片描述
图4-3是使用15×15高斯核模板进行高斯滤波处理的效果图,由图可知,图像在去除噪声的同时也变得更加模糊。
在这里插入图片描述

总之,高斯滤波作为最有效的滤波器之一,它对于抑制服从正态分布的噪声非常有效。

五、总结

本文主要讲解了常用于消除噪声的图像平滑方法,常见方法包括三种线性滤波(均值滤波、方框滤波、高斯滤波)和两种非线性滤波(中值滤波、双边滤波)。这篇文章介绍了均值滤波、方框滤波和高斯滤波,通过原理和代码进行对比,分别讲述了各种滤波方法的优缺点,有效地消除了图像的噪声,并保留图像的边缘轮廓。

希望大家能多指正指正~
点赞或收藏能鼓励作者更快更新哟~

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值