图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
图像滤波按图像域可分为两种类型:
邻域滤波(Spatial Domain Filter),其本质是数字窗口上的数学运算。一般用于图像平滑、图像锐化、特征提取(如纹理测量、边缘检测)等,邻域滤波使用邻域算子——利用给定像素周围像素值以决定此像素最终输出的一种算子。邻域滤波方式又分为线性滤波和非线性滤波,其中线性滤波包括均值滤波、方框滤波和高斯滤波等,非线性滤波包括中值滤波和双边滤波等。
频域滤波(Frequency Domain Filter),其本质是对像素频率的修改。一般用于降噪、重采样、图像压缩等。按图像频率滤除效果主要分为两种类型:低通滤波(滤除原图像的高频成分,即模糊图像边缘与细节)和高通滤波(滤除原图像的低频成分,即图像锐化)。
均值滤波采用多次测量取平均值的思想,用每一个像素周围的像素的平均值代替自身。均值滤波是方框滤波归一化后的特殊情况。
取卷积核(Kernel)区域下所有像素的平均值并替换中心元素,如下公式:
优点:能够将受到噪声影响的像素使用该噪声周围的像素值进行修复,对椒盐噪声的滤除比较好。
缺点:不能很好地保护图像细节,在图像去噪的同时也破坏了图像的细节部分,从而使图像变得模糊。
与均值滤波不同的是,方框滤波不会计算像素的均值。在均值滤波中,滤波结果的像素值是任意一个点的邻域平均值,等于各邻域像素值之和除以邻域面积。而在方框滤波中,可以自由选择是否对均值滤波的结果进行归一化,即可以自由选择滤波结果是邻域像素值之和的平均值,还是邻域像素值之和。
当normalize=True时,与均值滤波结果相同;
当normalize=False时,表示对加和后的结果不进行平均操作,大于255的使用255表示。
高斯滤波(Gauss Filter)基于二维高斯核函数。用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。高斯滤波主要用来去除高斯噪声。
高斯滤波具有在保持细节的条件下进行噪声滤波的能力,因此广泛应用于图像降噪中,但其效率比均值滤波低。
中值滤波将待处理的像素周围像素从小到大排序,取中值代替该像素。
优点:去除脉冲噪声、椒盐噪声的同时又能保留图像边缘细节。
缺点:当卷积核较大时,仍然使得图像变得模糊,而且计算量很大。
因为高斯滤波把距离设为权重,设计滤波模板作为滤波系数,并且只考虑像素之间的空间位置关系,所以滤波结果丢失了边缘信息。
双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。
规则为低频信息能正常通过,而超过设定临界值的高频信息则被阻隔、减弱。但是阻隔、减弱的幅度则会依据不同的频率以及不同的滤波程序(目的)而改变。
低通滤波,通过了低频信息,保留了图像背景和基本内容,图像边缘被阻挡,图像变模糊。
规则为高频信息能正常通过,而低于设定临界值的低频信息则被阻隔、减弱。但是阻隔、减弱的幅度则会依据不同的频率以及不同的滤波程序(目的)而改变。
高通滤波,通过了高频信息,提取了图像边缘和噪声。
测试代码如下:
from PIL import Image
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
img = cv.imread('lenna.jpg')
# 均值滤波
img_blur = cv.blur(img, (3,3)) # (3,3)代表卷积核尺寸,随着尺寸变大,图像会越来越模糊
img_blur = cv.cvtColor(img_blur, cv.COLOR_BGR2RGB) # BGR转化为RGB格式
# 方框滤波
img_boxFilter1 = cv.boxFilter(img, -1, (3,3), normalize=True) # 当 normalize=True 时,与均值滤波结果相同
img_boxFilter1 = cv.cvtColor(img_boxFilter1, cv.COLOR_BGR2RGB) # BGR转化为RGB格式
img_boxFilter2 = cv.boxFilter(img, -1, (3,3), normalize=False)
img_boxFilter2 = cv.cvtColor(img_boxFilter2, cv.COLOR_BGR2RGB) # BGR转化为RGB格式
# 高斯滤波
img_GaussianBlur= cv.GaussianBlur(img, (3,3), 0, 0) # 参数说明:(源图像,核大小,x方向的标准差,y方向的标准差)
img_GaussianBlur = cv.cvtColor(img_GaussianBlur, cv.COLOR_BGR2RGB) # BGR转化为RGB格式
# 中值滤波
img_medianBlur = cv.medianBlur(img, 3)
img_medianBlur = cv.cvtColor(img_medianBlur, cv.COLOR_BGR2RGB) # BGR转化为RGB格式
# 双边滤波
# 参数说明:(源图像,核大小,sigmaColor,sigmaSpace)
img_bilateralFilter=cv.bilateralFilter(img, 50, 100, 100)
img_bilateralFilter = cv.cvtColor(img_bilateralFilter, cv.COLOR_BGR2RGB) # BGR转化为RGB格式
titles = ['img_blur', 'img_boxFilter1', 'img_boxFilter2',
'img_GaussianBlur', 'img_medianBlur', 'img_bilateralFilter']
images = [img_blur, img_boxFilter1, img_boxFilter2, img_GaussianBlur, img_medianBlur, img_bilateralFilter]
for i in range(6):
plt.subplot(3,3,i+1), plt.imshow(images[i]), plt.title(titles[i])
plt.axis('off')
plt.show()
# 低通滤波
def Low_Pass_Filter(srcImg_path):
#img = cv.imread('srcImg_path', 0)
img = np.array(Image.open(srcImg_path))
img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 傅里叶变换
dft = cv.dft(np.float32(img), flags = cv.DFT_COMPLEX_OUTPUT)
fshift = np.fft.fftshift(dft)
# 设置低通滤波器
rows, cols = img.shape
crow, ccol = int(rows/2), int(cols/2) # 中心位置
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# 掩膜图像和频谱图像乘积
f = fshift * mask
# 傅里叶逆变换
ishift = np.fft.ifftshift(f)
iimg = cv.idft(ishift)
res = cv.magnitude(iimg[:,:,0], iimg[:,:,1])
return res
# 高通滤波
def High_Pass_Filter(srcImg_path):
#img = cv.imread(srcImg_path, 0)
img = np.array(Image.open(srcImg_path))
img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 傅里叶变换
dft = cv.dft(np.float32(img), flags = cv.DFT_COMPLEX_OUTPUT)
fshift = np.fft.fftshift(dft)
# 设置高通滤波器
rows, cols = img.shape
crow, ccol = int(rows/2), int(cols/2) # 中心位置
mask = np.ones((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 0
# 掩膜图像和频谱图像乘积
f = fshift * mask
# 傅里叶逆变换
ishift = np.fft.ifftshift(f)
iimg = cv.idft(ishift)
res = cv.magnitude(iimg[:,:,0], iimg[:,:,1])
return res
img_Low_Pass_Filter = Low_Pass_Filter('lenna.jpg')
plt.subplot(121), plt.imshow(img_Low_Pass_Filter, 'gray'), plt.title('img_Low_Pass_Filter')
plt.axis('off')
img_High_Pass_Filter = High_Pass_Filter('lenna.jpg')
plt.subplot(122), plt.imshow(img_High_Pass_Filter, 'gray'), plt.title('img_High_Pass_Filter')
plt.axis('off')
plt.show()