中值滤波器原理:
统计排序滤波器是典型的非线性平滑滤波器。首先对模板覆盖的灰度值进行排序,选择有代表性的灰度值,作为统计排序滤波器的响应。典型的统计排序滤波器包括最大值滤波器、中值滤波器、最小值滤波器。最大值滤波器用像素邻域内的最大值代替该像素的灰度值,主要用于寻找亮点。中值滤波器用像素邻域内的中值代替该像素的灰度值,主要用于降噪。最小值滤波器用像素邻域内的最小值代替该像素的灰度值,主要用于寻找最暗点。对于一定类型的随机噪声,中值滤波器的降噪效果较好,比相同尺寸的均值滤波器模糊程度明显要低。中值滤波器对处理脉冲噪声非常有效,因为中值滤波器取中值作为滤波结果,可以很好的去除滤波器覆盖的邻域中的一些黑点和白点。中值滤波器使图像中突出的亮点或暗点更像周围的值,以消除孤立的亮点或暗点,从而实现对图像的平滑。
编写代码,输出如下图所示的结果:
注意:
结果显示了添加胡椒和盐粒概率分别为0.1的椒盐噪声后的图像及用3×3的中值滤波器分别滤波一次、两次、三次的结果。
python代码实现
import cv2
import numpy as np
import skimage
import math
import copy
from matplotlib import pyplot as plt
image = cv2.imread('D://Fig0507.tif')
image = skimage.util.random_noise(image, mode='s&p')
def spilt(a):
if a % 2 == 0:
x1 = x2 = a / 2
else:
x1 = math.floor(a / 2)
x2 = a - x1
return -x1, x2
def original(i, j, k, a, b, img):
x1, x2 = spilt(a)
y1, y2 = spilt(b)
temp = np.zeros(a * b)
count = 0
for m in range(x1, x2):
for n in range(y1, y2):
if i + m < 0 or i + m > img.shape[0] - 1 or j + n < 0 or j + n > img.shape[1] - 1:
temp[count] = img[i, j, k]
else:
temp[count] = img[i + m, j + n, k]
count += 1
return temp
def mid_function(a, b, img):
img0 = copy.copy(img)
for i in range(0, img.shape[0]):
for j in range(2, img.shape[1]):
for k in range(img.shape[2]):
temp = original(i, j, k, a, b, img0)
img[i, j, k] = np.median(temp)
return img
plt.subplot(2,2,1)
plt.title('s&p')
plt.imshow(image,cmap = 'gray')
plt.subplot(2,2,2)
plt.title('one_pass')
img1 = mid_function(3,3,image)
plt.imshow(img1,cmap = 'gray')
plt.subplot(2,2,3)
plt.title('two pass')
img2 = mid_function(3,3,img1)
plt.imshow(img2,cmap = 'gray')
plt.subplot(2,2,4)
plt.title('three pass')
img3 = mid_function(3,3,img2)
plt.imshow(img3,cmap = 'gray')
plt.show()
运行结果
总结:
中值滤波器的想法很简单,如果一个信号是平缓变化的,那么某一点的输出值可以用这点的某个大小的邻域内的所有值的统计中值来代替。这个邻域在信号处理领域称之为窗(window)。窗开的越大,输出的结果就越平滑,但也可能会把有用的信号特征给抹掉。所以窗的大小要根据实际的信号和噪声特性来确定。
通常会选择窗的大小使得窗内的数据个数为奇数个,之所以这么选是因为奇数个数据才有唯一的中间值。
中值滤波不仅是图像变得平滑,同时去除了椒盐噪声。中值操作的时候,白色(255)和黑色(0)因为是最大最小值, 除非周围的颜色都是黑色或者白色,不然一般都会被剔除掉, 这就是和均值最大的不同。 所以在效果上要好很多。一般来说中值滤波是去除椒盐噪声的非常理想的选择。