Skip to content

OpenCV中的滤波器

图像滤波

  • 图像滤波:一副图像通过滤波器得到另一幅图像,其中滤波器又被称为卷积核,滤波的过程被称为卷积
  • 卷积核的大小:3x3 5x5 9x9 11x11 ...
  • 锚点:
  • 边界扩充: 对于一张图片在卷积之后,他的卷积结果会比原始图像小,有时候我们需要输出结果与原始图像一样大,此时需要做边界扩充。
  • 步长: 我们在卷积的过程中,每次扫描的范围长度是多少,需要跨几步。

Image title

图像卷积的效果图

Image title

卷积的过程

卷积相关概念

  • 卷积核的大小:卷积就是滤波器,卷积是有大小的

    • 卷积核一般为奇数,如3x3 5x5 7x7等
    • 为什么是卷积核大小是奇数呢
      1. 一方面是增加padding的原因,扩充我们图像的尺寸,主要是为了使我们输出的图像大小和我们原始的图像大小保持一致。
      2. 另一方面是保证锚点在卷积核的正中间,放置位置发生偏移的原因。
  • 卷积核大小的影响

    • 在深度学习中,卷积核越大,看到的信息(感受野)越多,提取的特征越好,同时计算量也就越大。
  • 锚点

    • 如下图,中心的41就是锚点,如果是3x3的那么16就是锚点。主要是为了防止信息的偏差。
      Image title
  • 边界扩充

    • 当卷积核大于1且不进行边界扩充,输出尺寸将相应缩小。
    • 当卷积核以标准方式进行边界扩充,则输出数据的空间尺寸将于输入相等。
      Image title
    • 计算公式
      • N = ( W - F + 2P ) / S + 1
        • N 输出图像大小
        • W 原图大小;F卷积核大小;P扩充尺寸
        • S 步长大小
  • 步长

    • 举例:有个5x5的输入,用3x3的滤波器,如果是2步的步长,第一次就是左边的图示 第二次是两步之后右边的图示。最后就产生了2x2的图像。如果步长变为1,那么最后就会产生3x3的图像。
      Image title

图像卷积

  • filter2D(src, ddepth, kernel, anchor, delta, borderType)
    1. src: 图片源
    2. ddepth: 图像的位深,可以输出图像为8位/16位/32位
    3. kernel: 卷积核
    4. anchor: 锚点 默认是-1
    5. delta: 滤波之后卷积之后得到的元素加一个delta值,默认为0
    6. borderType: 边界的类型,映射、黑边...
      import cv2
      from cv2 import imshow
      import numpy as np
      
      img = cv2.imread('./dog.jpeg');
      # 创建卷积核 为5x5 float32位的阵列,给每个元素除以50,也就是每个元素值为50分之1
      kernel = np.ones((5, 5), np.float32) / 50
      dst = cv2.filter2D(img, -1, kernel)
      
      cv2.imshow('img', img)
      cv2.imshow('filter', dst)
      cv2.waitKey(0)
      
      Image title

低通滤波与高通滤波

  • 低通滤波:低于某个阈值是可以通过的,低通滤波是可以去除噪音平滑图像
  • 高通滤波:高于某个阈值是可以通过的,高通滤波可以帮助查找图像的边缘

低通滤波

方盒滤波与均值滤波

  • 方盒滤波API:boxFilter(src, ddepth, ksize, anchor, normalize, borderType)

    • normalize = true, a = 1 / W x H
    • 当normalize为true时,假设是3x3的滤波,那么a的值就是9分之1。5x5的滤波就是25分之1。
    • normalize = false, a = 1
    • 当normalize为false时,a等于1,不进行均值化。最终我们输出的结果就是每个元素之和。
    • 当normalize == true时,方盒滤波 == 均值滤波
    • 参数释义:
      1. src: 图像源
      2. ddepth: 输出图像的位深,一般情况下输出图像的位深与原图像的位深是一致的。
      3. ksize: kernel size,卷积核的大小3x3 5x5 7x7 ...
      4. anchor: 锚点,默认值是-1,取卷积核的中心点。
      5. normalize: true与false,简介参照以上,默认值为true
      6. borderType: 边界的类型,映射、黑边...
  • 均值滤波API:blur(src, ksize, anchor, borderType)

    1. src: 图像源
    2. ksize: kernel size,卷积核的大小3x3 5x5 7x7 ...
    3. anchor: 锚点,默认值是-1,取卷积核的中心点。
    4. borderType: 边界的类型,映射、黑边...
      import cv2
      from cv2 import imshow
      import numpy as np
      
      img = cv2.imread('./dog.jpeg');
      # 均值滤波
      dst = cv2.blur(img, (9, 9))
      
      cv2.imshow('img', img)
      cv2.imshow('filter', dst)
      cv2.waitKey(0)
      
      Image title

高斯滤波

  • 什么是高斯滤波:如下图,每一个滤波都是一个钟形(两边低中间高),所以高斯滤波也叫钟形滤波。高斯滤波主要解决的是高斯噪点。

    Image title

  • 高斯滤波在卷积核中的体现如下图,,中间的值不是最大的,但是它的比重的最重的。

    Image title
    卷积原理图
    Image title
    高斯权重

  • 高斯滤波API:GaussianBlur(src, kernel, sigmaX, sigmaY, ...)

    1. src: 图像源
    2. kernel: kernel size,卷积核的大小3x3 5x5 7x7 ...
    3. sigmaX: 钟形滤波的延展宽度,x与中心点最大的误差。
    4. sigmaY: 钟形滤波的延展宽度,y与中心点最大的误差。
      import cv2
      from cv2 import imshow
      import numpy as np
      
      img = imread('./test.png')
      dst = cv2.GaussianBlur(img, (5,5), sigmaX=100)
      
      cv2.imshow('img', img)
      cv2.imshow('filter', dst)
      cv2.waitKey(0)
      

    Image title
    执行结果
    Image title
    sigma的作用
    Image title
    没有sigma时,以kernel size为基准。

中值滤波

  • 什么是中值滤波,假设有一个数组[1556789],取其中的中间值作为卷积后的结果值6,每一个元素都输出这样一个元素,取其中的中间值,这就是中值滤波
  • 中值滤波的优点:对胡椒噪音效果明显
    Image title
    胡椒噪音图
  • 中值滤波API:medianBlur(img, ksize)
    1. src: 图像源
    2. kernel: kernel size,卷积核的大小3x3 5x5 7x7 ...
      import cv2
      from cv2 import imshow
      import numpy as np
      
      # 中值滤波
      img = imread('./papper.png')
      dst = cv2.medianBlur(img, 5)
      
      cv2.imshow('img', img)
      cv2.imshow('filter', dst)
      cv2.waitKey(0)
      
      Image title

双边滤波

  • 双边滤波的优点
    1. 可以保留边缘
    2. 同时可以对边缘内的区域进行平滑处理
    3. 双边滤波的主要作用是美颜
      Image title
      a图像有3个边缘面,分别是上部凸起 下部凸起 中间高度,中间高度的颜色和落差特别大,对于双边滤波来讲他不会对边缘进行处理,他会把上下凸起部分抹平。最后就输出了d图像。d图像中只有高度的保留的其余地方都已抹平模糊处理了。
  • 双边滤波API:bilateralFilter(img, d, sigmaColor, sigmaSpace, ...)

    1. img:
    2. d: 直径,filter的大小
    3. sigmaColor: 颜色的sigma
    4. sigmaSpace: 空间的sigma

    import cv2
    from cv2 import imshow
    import numpy as np
    
    # 双边滤波
    img = imread('./fat.png')
    dst = cv2.bilateralFilter(img, 30, 155, 8000)
    
    cv2.imshow('img', img)
    cv2.imshow('filter', dst)
    cv2.waitKey(0)
    
    Image title

高通滤波

索贝尔算子

  • Sobel(索贝尔)(高斯)
    • 优点:在内部实现的时候首先使用的高斯滤波,对噪音进行了过滤。之后通过一阶导数求得图像的边缘,对抗噪音性比较强。
    • 缺点:在检测边缘的时候只能求一个方向的 要码横轴 要码纵轴。最终的检测结果需要将横轴检测边缘与纵轴检测边缘加起来,才能得出最终的结果。
  • 流程
    1. 先向x方向求导
    2. 再向y方向求导
    3. 最终结果累加:|G| = |Gx| + |Gy|
  • API: Sobel(src, ddepth, dx, dy, ksize=3, scale=1, delta=0, borderType=BORDER_DEFAULT)

    1. src: 源
    2. ddepth: 位深
    3. dx: 对x求导得出y
    4. dy: 对y求导得出x
    5. ksize: kernel size,如果等于-1就变成沙尔算法
    6. scale = 1: 缩放
    7. delta = 0: 结果加delta值
    8. borderType=BORDER_DEFAULT: 边框类型

    import cv2
    import numpy as np
    
    img = cv2.imread('./chess.png')
    #索贝尔算子y方向边缘
    d1 = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
    #索贝尔算子x方向边缘
    d2 = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
    dst = d1 + d2
    
    cv2.imshow('img', img)
    cv2.imshow('d1', d1)
    cv2.imshow('d2', d2)
    cv2.imshow('dst', dst)
    cv2.waitKey(0)
    
    Image title

沙尔算子

  • Scharr(沙尔):只支持3x3的卷积核
    • 优点:卷积核大小不会改变的3x3的卷积核。当我们给其他卷积核设定为-1,默认就会使用沙尔算子。该卷积核显示图像细腻。
    • 缺点:在检测边缘的时候只能求一个方向的 要码横轴 要码纵轴。最终的检测结果需要将横轴检测边缘与纵轴检测边缘加起来,才能得出最终的结果
  • 与Soble类似,只不过使用的kernel值不同
  • Scharr只能求x方向或y方向的边缘
  • API: Scharr(src, ddepth, dx, dy, scale = 1, delta=0, borderType=BORDER_DEFAULT)
    import cv2
    import numpy as np
    
    img = cv2.imread('./chess.png')
    #沙尔
    d1 = cv2.Scharr(img, cv2.CV_64F, 1, 0)
    #沙尔
    d2 = cv2.Scharr(img, cv2.CV_64F, 0, 1)
    dst = cv2.add(d1, d2)
    
    cv2.imshow('img', img)
    cv2.imshow('d1', d1)
    cv2.imshow('d2', d2)
    cv2.imshow('dst', dst)
    cv2.waitKey(0)
    
    Image title

拉普拉斯算子

  • Laplacian(拉普拉斯)
    • 优点:不用单独求 x y的边缘,直接可以一次检测横轴与纵轴的边缘。
    • 缺点:对于噪音比较敏感,我们在使用拉普拉斯时需要先对图像进行降噪处理。
  • API: Laplacian(img, ddepth, ksize=1, scale=1, borderType=BORDER_DEFAULT)
    1. img: 源
    2. ddepth: 位深
    3. ksize: kernel size
    4. scale: 缩放比
    5. borderType=BORDER_DEFAULT: 边界类型
      import cv2
      import numpy as np
      
      #拉普拉斯
      ldst = cv2.Laplacian(img, cv2.CV_64F, ksize=5)
      
      cv2.imshow('img', img)
      cv2.imshow('ldst', ldst)
      cv2.waitKey(0)
      
      Image title

边缘检测 Canny

  • 为了降低噪音的影响,canny会使用5x5的高斯滤波来消除噪声
  • 之后调用索贝尔计算图像梯度的方向(0度、45度、90度、135度)
  • 之后会取局部极大值,确定该边缘是最好的边缘
  • 阈值计算:
    Image title
    如果超过maxVal肯定就是边缘,如果低于minVal就不是边缘。如果在maxVal与minVal之间有没有联系,a是边缘c和a在连线上那么c也就是边缘,由于b不在maxVal边缘相关的连线上,所以b就不是边缘
  • API: Canny(img, minVal, maxVal, ...)
    import cv2
    from cv2 import imread
    
    img = imread('./lena.png')
    dst = cv2.Canny(img, 50, 80)
    
    cv2.imshow('img', img)
    cv2.imshow('canny', dst)
    cv2.waitKey(0)
    
    Image title