【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

时间:2022-11-22 12:55:23

OpenCV Python 平滑去噪

【目标】

  • 利用不同的低通滤波器对图像进行平滑去噪(平均、高斯、中值、双边)
  • 使用自己定制的滤波器

  • 5x5 平均滤波器

K = 1 25 [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ] K= \frac{1}{25} \begin{bmatrix} 1 & 1 & 1 & 1 & 1\\ 1 & 1 & 1 & 1 & 1\\ 1 & 1 & 1 & 1 & 1\\ 1 & 1 & 1 & 1 & 1\\ 1 & 1 & 1 & 1 & 1 \end{bmatrix} K=2511111111111111111111111111

  • 3x3 平均滤波器

K = 1 9 [ 1 1 1 1 1 1 1 1 1 ] K= \frac{1}{9} \begin{bmatrix} 1 & 1 & 1\\ 1 & 1 & 1\\ 1 & 1 & 1 \end{bmatrix} K=91111111111

【代码】

  • 自定义平滑滤波器

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

和一维信号一样,图像也可以通过低通滤波器和高通滤波器进行滤波;低通滤波器可以模糊图像和去除噪声,高通滤波器可以用于找图像边缘。

import numpy as np
import cv2

img = cv2.imread('opencv-logo-white.png')
kernel = np.ones((5, 5), np.float32)/25
dst = cv2.filter2D(img, -1, kernel)

cv2.imshow("src", img)
cv2.imshow("filter2D", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 模糊

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

以一个像素为中心,取周边的像素平均值作为该像素的像素值
如果不想用归一化的滤波器,那就用 cv2.boxFilter, 并且传入 normalize=False

import cv2
import numpy as np

img = cv2.imread('opencv-logo-white.png')
blur = cv2.blur(img,(5,5))

cv2.imshow("src", img)
cv2.imshow("blur", blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 高斯模糊

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

import cv2
import numpy as np

img = cv2.imread('opencv-logo-white.png')
blur = cv2.GaussianBlur(img, (5, 5), 0)

cv2.imshow("src", img)
cv2.imshow("GaussianBlur", blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 中值滤波

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

中值滤波和其他滤波器不一样,中值滤波取的像素值为图像内部的像素值,其他滤波是通过计算得到的。中值滤波器的核宽为正奇数。非常适合处理椒盐噪声。

import numpy as np
import cv2

def addPepperSalt(src: cv2.Mat, snr: float = 0.99) -> cv2.Mat:
    """
    addPepperSalt 给彩色图像添加黑白椒盐噪声

    _extended_summary_

    Args:
        src (cv2.Mat): 输入图像
        snr (float): 信噪比
    """
    if src is None:
        return None

    dst = src.copy()

    h, w, _ = src.shape
    imgsz = h * w
    pepperSaltNum = int(imgsz * (1.0 - snr))

    for i in range(pepperSaltNum):
        randx = np.random.randint(1, w-1)
        randy = np.random.randint(1, h-1)

        if np.random.random() <= 0.5:
            dst[randy, randx] = 0
        else:
            dst[randy, randx] = 255
    return dst


img = cv2.imread('sheets.png')
cv2.imshow("src", img)

pepperSaltIm = addPepperSalt(img, 0.99)
cv2.imshow("peppersalt", pepperSaltIm)

median = cv2.medianBlur(pepperSaltIm, 5)
blur = cv2.bilateralFilter(pepperSaltIm, 15, 180, 180)

cv2.imshow("median", median)
cv2.imshow("blur", blur)

cv2.waitKey(0)
cv2.destroyAllWindows()
  • 双边滤波

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

可以非常有效的去除噪声的同时保留边缘信息,但是非常耗时。
NOTE: 双边滤波对椒盐噪声效果非常不友好,具体原理请看 参考2

import cv2
import numpy as np

img = cv2.imread('bilateral.png')
blur = cv2.bilateralFilter(img,9,75,75)

cv2.imshow("src", img)
cv2.imshow("bilateralFilter", blur)
cv2.waitKey(0)
cv2.destroyAllWindows()

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

【接口】

  • cv2.filter2D
void cv::filter2D	(	InputArray 	src,
OutputArray 	dst,
int 	ddepth,
InputArray 	kernel,
Point 	anchor = Point(-1,-1),
double 	delta = 0,
int 	borderType = BORDER_DEFAULT 
);
cv2.filter2D(	src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]]	) ->	dst

通过卷积核对图像进行卷积

  • src: 输入图像
  • dst: 输出图像
  • ddepth: 输出图像的深度
  • kernel: 卷积核
  • anchor: 默认为(-1, -1) 是在卷积核的中心
  • delta: 在存储前加到滤波后的像素值上
  • borderType: 填充类型,不支持 BORDER_WRAP

ddepth:
【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

borderType:

【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波

  • cv2.blur
void cv::blur	(	InputArray 	src,
OutputArray 	dst,
Size 	ksize,
Point 	anchor = Point(-1,-1),
int 	borderType = BORDER_DEFAULT 
);
cv2.blur(	src, ksize[, dst[, anchor[, borderType]]]	) ->	dst

利用 boxFilter 对图像进行模糊处理

  • src: 输入图像
  • dst: 输出图像
  • ksize: 模糊核的大小
  • anchor: 默认为(-1, -1) 是在卷积核的中心
  • borderType: 填充类型,不支持 BORDER_WRAP
  • cv2.boxFilter
void cv::boxFilter	(	InputArray 	src,
OutputArray 	dst,
int 	ddepth,
Size 	ksize,
Point 	anchor = Point(-1,-1),
bool 	normalize = true,
int 	borderType = BORDER_DEFAULT 
);
cv2.boxFilter(	src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]	) ->	dst
  • src: 输入图像
  • dst: 输出图像
  • ddepth: 输出图像的深度
  • ksize: 模糊核的大小
  • anchor: 默认为(-1, -1) 是在卷积核的中心
  • normalize: 归一化标志
  • borderType: 填充类型,不支持 BORDER_WRAP
  • cv2.GaussianBlur
void cv::GaussianBlur	(	InputArray 	src,
OutputArray 	dst,
Size 	ksize,
double 	sigmaX,
double 	sigmaY = 0,
int 	borderType = BORDER_DEFAULT 
);
cv2.GaussianBlur(	src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]	) ->	dst

高斯模糊

  • src: 输入图像
  • dst: 输出图像
  • ksize: 模糊核的大小
  • sigmaX: 高斯核X方向的方差
  • sigmaY: 高斯核Y方向的方差,如果设置为0,则被设置为 sigmaX, 如果都设置为0,则通过窗口计算。最好是都设置。
  • borderType: 填充类型,不支持 BORDER_WRAP
  • cv2.bilateralFilter
void cv::bilateralFilter	(	InputArray 	src,
OutputArray 	dst,
int 	d,
double 	sigmaColor,
double 	sigmaSpace,
int 	borderType = BORDER_DEFAULT 
);
cv2.bilateralFilter(	src, d, sigmaColor, sigmaSpace[, dst[, borderType]]	) ->	dst

对图像应用双边滤波

  • src: 输入图像
  • dst: 输出图像
  • d: 滤波使用的像素邻域直径,如果是负数,则通过 sigmaSpace 计算。
  • sigmaColor: 颜色空间的方差,如果设置很大,则认定像素差很大的像素为邻域像素;
  • sigmaSpace: 坐标空间的方差,如果设置很大,意味着很远的像素为邻域像素,如果 d > 0, 由坐标决定是否为邻域。
  • borderType: 填充类型,不支持 BORDER_WRAP

【参考】

  1. OpenCV 官方文档
  2. 双边滤波的介绍与应用