图像处理:均值滤波算法详解
图像处理:均值滤波算法详解
均值滤波是一种常用的图像处理技术,主要用于去除图像噪声、平滑图像。其基本原理是用像素点周围的邻域像素的平均值来代替该像素的值。本文将从概念、基本原理到具体实现(包括使用OpenCV库和纯Python实现)详细讲解均值滤波算法,并通过代码示例和图像处理效果展示不同滤波核大小对图像的影响。
概念介绍
均值滤波是一种简单的图像平滑处理方法,其基本思想是用像素点周围的邻域像素的平均值来代替该像素的值。在图像处理中,均值滤波可以用于去除图像中的噪声,使图像变得更加平滑。它的计算简单易懂,但在滤波过程中可能会导致图像细节的损失。因此,在实际应用中,需要根据具体的情况选择适合的滤波算法。
基本原理
我们以5x5大小为例,均值滤波的原理只需要理解到,它其实是将这个范围内的25个值进行求和的平均值,以这个新值来代替这个区域的中心值。
配合这里的图进行理解:
运行之后,获得新值126,覆盖掉中心值得像素226。
对于边缘像素,只仅仅计算在这个范围内得数值。假如,左上角为中心值,而其左边和上边都没有值,我们只需要计算在这5x5区域内有的值就可以了。
计算如下:
print((23+0+25+158+140+238+67+199+197)/9)
得出新值为116,替换中心点23的值。
Opencv实现均值滤波
在OpenCV中,我们可以使用cv2.blur()
函数来实现均值滤波。在使用该函数时,我们需要输入原始图像、滤波核的大小以及边界样式等参数。一般情况下,我们可以直接采用函数默认值即可。
下面是使用OpenCV实现均值滤波的代码示例:
import cv2
path = 'Images/Colnoiselena.jpg'
img = cv2.imread(path)
imgAverage_1 = cv2.blur(img, (1, 1))
imgAverage_3 = cv2.blur(img, (3, 3))
imgAverage_5 = cv2.blur(img, (5, 5))
imgAverage_7 = cv2.blur(img, (7, 7))
imgStack = cv2.hconcat([cv2.vconcat([imgAverage_1, imgAverage_3]), cv2.vconcat([imgAverage_5, imgAverage_7])])
cv2.imshow("images", imgStack)
cv2.waitKey(0)
实现效果:
经典的lena的图片,可以看到,随着滤波核的大小逐渐增加,去噪效果越好,但相应的图片会变的模糊,计算时间会增长。所以,还是应了我开头就说过的话,在实际处理中,选择合适的滤波核大小,让模糊与去噪效果之间取得平衡。
Python手写实现均值滤波
下面是使用纯Python实现均值滤波的代码示例:
import cv2
import numpy as np
path = 'Images/Colnoiselena.jpg'
img = cv2.imread(path)
def Arerage_Filtering(img, k_size=3):
if k_size % 2 == 0:
k_size += 1
rows, cols = img.shape[:2]
pad_width = (k_size - 1) // 2
img_pad = cv2.copyMakeBorder(img, pad_width, pad_width, pad_width, pad_width, cv2.BORDER_REPLICATE)
img_filter = np.zeros_like(img)
for i in range(rows):
for j in range(cols):
pixel_values = img_pad[i:i+k_size, j:j+k_size].flatten()
img_filter[i, j] = np.mean(pixel_values)
return img_filter
imgAverage_1 = Arerage_Filtering(img, k_size=1)
imgAverage_3 = Arerage_Filtering(img, k_size=3)
imgAverage_5 = Arerage_Filtering(img, k_size=5)
imgAverage_7 = Arerage_Filtering(img, k_size=7)
imgStack = cv2.hconcat([cv2.vconcat([imgAverage_1, imgAverage_3]), cv2.vconcat([imgAverage_5, imgAverage_7])])
cv2.imshow("images", imgStack)
cv2.waitKey(0)
cv2.destroyAllWindows()
这个算法相对来说比较容易实现,但是相比调用OpenCV的函数,它的计算时间要长很多,而且我这里还只考虑了图像的两个通道,最终输出的结果是灰度图的情况下。
下面是这个函数的具体实现过程:
- 首先,判断卷积核的大小是否为奇数,如果为偶数,则将其加1,确保其大小为奇数。
- 获取图像的行数和列数。
- 计算填充的宽度,即卷积核宽度的一半,用于处理图像边缘。
- 使用
cv2.copyMakeBorder
函数进行边缘填充,将图像的边缘复制并填充到周围,以防止边缘像素点无法进行卷积。 - 初始化一个和原始图像大小一样的零矩阵。
- 遍历图像中的每一个像素点,计算该像素点周围邻域内的像素值,并求取其平均值,然后将其赋值给零矩阵中的对应像素点。
- 返回处理后的图像。
最后,函数通过stackImages
函数将处理后的四张图像以2x2的网格形式拼接成一张图像,并展示结果。