基本原理

如下图,希望对位于第5行第5列的像素点进行均值滤波:

image.png

在进行均值滤波时, 首先要考虑对周围多少个像素点取平均值 。通常情况下,我们会 以当前像素点为中心 ,对行数和列数相等的一块区域内的所有像素点的像素值求平均。

如上图,当前像素点的位置为第五行第五列,我们对其周围 5x5 区域内的像素取平均,计算方法为:

(197+25+106+156+159+149+40+107+5+71+163+198+226+223+156+222+37+68+193+157+42+72+250+41+75)/25 ,计算结果为 126 ,然后均值滤波将此均值作为当前像素点滤波后的像素值。以此类推,我们针对每一个像素点计算其周围5×5区域内的像素值均值,并将其作为当前像素点的新值,即可得到当前图像的均值滤波结果。

当然不可忽略的地方是图像的边界点,这些地方并不存在5×5邻域区域。例如,左上角第1行第1列上的像素点,其像素值为23,如果以其为中心点取周围5×5邻域,则5×5邻域中的部分区域位于图像外部。图像外部是没有像素点和像素值的,显然是无法计算该点的5×5邻域均值的。针对边缘像素点,可以只取图像内存在的周围邻域点的像素值均值,如下图所示,计算左上角的均值滤波结果时,仅取图中灰色背景的3×3邻域内的像素值的平均值。

image.png

对于上图所示第一行第一类的像素点,取第1~3列与第1~3行交汇处所包含的3×3邻域内的像素点的像素值均值。因此,当前像素点的均值滤波计算方法为:

(23+158+140+238+0+67+199+197+25)/9 ,其计算结果是 116 ,然后将该值代替原像素值即可完成均值滤波。

除此以外,还可以使用扩展当前图像的周围像素点的方法。例如,将当前9×7大小的图像扩展为13×11大小的图像,如下图所示:

image.png

完成图像边缘扩展后,可以在新增的行列内填充不同的像素值。在此基础上,再针对9×7的原始图像计算其5×5邻域内像素点的像素值均值。OpenCV提供了多种边界处理方式,我们可以根据实际需要选用不同的边界处理模式。

均值滤波函数

在openCV中,实现均值滤波的函数是 cv2.blur() ,语法格式如下:

dst = cv2.blur(src, ksize[, dst[, anchor[, borderType]]])
  • dst :均值滤波操作后的结果图像

  • src: 输入图像,即需要处理的图像

  • ksize :滤波核的大小

  • dst,anchor,borderType:可选参数。

  • 程序演示

    Author: Will Wang Email: [email protected] import cv2 import numpy as np ksize = (3, 3) noise = cv2.imread('noise.jpg', cv2.IMREAD_GRAYSCALE) noise = cv2.resize(noise, (512, 512)) mean = cv2.blur(noise, ksize) image_stack = np.hstack((noise, mean)) cv2.imshow('noise, mean', image_stack) cv2.waitKey(0) cv2.destroyAllWindows()

    本例中采取的kernel size为 3x3,平滑效果如下:

    image.png

    如果修改kernel size 为7x7

    image.png

    卷积核越大,参与到均值运算中的像素就会越多,即当前点计算的是更多点的像素值的平均值。因此,卷积核越大,去噪效果越好,当然花费的计算时间也会越长,同时图像失真也会越严重。在实际处理中,要在失真和去噪效果之间取得平衡,选取合适大小的卷积核。

    2. 高斯滤波

    在进行均值滤波时,其邻域内每个像素的权重是相等的。在高斯滤波中其邻域每个像素点的权重是不同的,高斯滤波会给中心点更大的权重,而减小远离中心点的权重,在此基础上计算邻域内各个像素值不同权重的和。

    基本原理

    在高斯滤波中,卷积核中的值不再都是1。例如,一个3×3的卷积核可能如下图所示:

    image.png

    如下图,针对最左侧的图像内第4行第3列位置上像素值为226的像素点进行高斯卷积,其运算规则为将该点邻域内的像素点按照不同的权重计算和:

    image.png

    在实际计算时,使用的卷积核如下图卷积核所示:

    image.png

    其计算方式为:

    (40x0.05+107x0.1+5x0.05+198x0.1+226x0.4+223x0.1+37x0.05+68x0.1+193x0.05),然后将该计算结果代替原像素值,以此类推,我们针对每一个像素点进行滤波计算,即可得到整幅图像的高斯滤波结果。

    在实际计算中,卷积核是归一化处理的,其形式可以为小数或者分数形式。有时候,给出的卷积核并没有进行归一化,这样的卷积核是为了说明问题用的,实际使用时往往需要进行归一化。严格来讲,使用没有进行归一化处理的卷积核进行滤波,得到的结果往往是错误的。

    高斯滤波函数

    在openCV中,实现均值滤波的函数是cv2.blur(),语法格式如下:

    dst = cv2.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])
    
  • dst :高斯滤波操作后的结果图像

  • src: 输入图像,即需要处理的图像

  • ksize :滤波核的大小。滤波核大小是指在滤波处理过程中其邻域图像的高度和宽度。滤波核的值必须是奇数

  • sigmaX:卷积核在水平方向上(X轴方向)的标准差,其控制的是权重比例。例如,下图是不同的sigmaX决定的卷积核,它们在水平方向上的标准差不同

  • sigmaY:卷积核在垂直方向上(Y轴方向)的标准差。如果将该值设置为0,则只采用sigmaX的值;如果sigmaXsigmaY都是0,则通过ksize.widthksize.height计算得到:

  • sigmaX=0.3×[(ksize.width-1)×0.5-1] + 0.8

  • sigmaY=0.3×[(ksize.height-1)×0.5-1] + 0.8

  • dst,borderType:默认即可

  • 在该函数中,sigmaYborderType是可选参数。sigmaX是必选参数,但是可以将该参数设置为0,让函数自己去计算sigmaX的具体值。

    官方文档建议显式地指定ksizesigmaXsigmaY三个参数的值,以避免将来函数修改后可能造成的语法错误。当然,在实际处理中,可以显式指定sigmaXsigmaY为默认值0。因此,函数cv2.GaussianBlur()的常用形式为:

    dst = cv2.GaussianBlur(src, ksize, 0, 0)
    noise = cv2.imread('noise.jpg', cv2.IMREAD_GRAYSCALE)
    noise = cv2.resize(noise, (512, 512))
    gaussian_blur = cv2.GaussianBlur(noise, ksize, 0, 0)
    image_stack = np.hstack((noise, gaussian_blur))
    cv2.imshow('noise, gaussian_blur', image_stack)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    本例中采取的kernel size为 3x3

    image.png

    如果修改kernel size 为7x7

    基本原理

    中值滤波与前面介绍的滤波方式不同,不再采用加权求均值的方式计算滤波结果。它用邻域内所有像素值的中间值来替代当前像素点的像素值。

    例如,针对下图中第4行第4列的像素点,计算它的中值滤波值。

    image.png

    将其邻域设置为3×3大小,对其3×3邻域内像素点的像素值进行排序,按升序排序后得到序列值为:[66,78,90,91,93,94,95,97,101]。在该序列中,处于中心位置(也叫中心点或中值点)的值是93,因此用该值替换原来的像素值78,作为当前点的新像素值,处理结果如下图所示:

    中值滤波函数

    在openCV中,实现中值滤波的函数是cv2.medianBlur(),其语法格式如下:

    dst = medianBlur(src, ksize[, dst])
    
  • dst :高斯滤波操作后的结果图像

  • src: 输入图像,即需要处理的图像

  • ksize :滤波核的大小

  • 其中需要注意的是ksize核的大小必须是比1大的奇数(注意是数字),比如3,5,7等。

    程序演示

    Author: Will Wang Email: [email protected] import cv2 import numpy as np noise = cv2.imread('noise.jpg', cv2.IMREAD_GRAYSCALE) noise = cv2.resize(noise, (512, 512)) median_blur = cv2.medianBlur(noise, 3) image_stack = np.hstack((noise, median_blur)) cv2.imshow('noise, median_blur', image_stack) cv2.waitKey(0) cv2.destroyAllWindows()

    中值滤波对含有椒盐噪声的图像特别有效,其结果如下图所示:

    image.png

    在中值滤波处理中,噪声成分很难被选中,所以可以在几乎不影响原有图像的情况下去除所有的噪声,但是由于需要进行排序等操作,中值滤波需要的运算较大,但是中值滤波可以很好地去除椒盐噪声。