• 机器学习基础篇(4)滤波器


    目录

    1.卷积

    1.1.何为卷积

     1.2.卷积步长

    1.3卷积填充 padding

    1.4卷积大小

    1.6卷积的API

    1.7卷积的意义

     2.滤波

    2.1.方盒滤波与均值滤波

    2.2.高斯滤波

        2.3中值滤波

    2.4.双边滤波

    3.算子

    3.1索贝尔(sobel)算子

    3.2沙尔(Scharr)算子

    3.3拉普拉斯算子

     .3.4.边缘检测Canny


    1.卷积

    1.1.何为卷积

    图像处理中, 输入一幅图像f(x,y),经过特定设计的卷积核g(x,y)进行卷积处理以后,输出图像将会得到模糊,边缘强化等各种效果。图像卷积就是卷积核在图像上按行滑动遍历像素时不断的相乘求和的过程。

     1.2.卷积步长

    步长就是卷积核在图像上移动的步幅. 默认是1,步长为1,可以让更加多像素点被处理,可以充分扫描图片

    1.3卷积填充 padding

    卷积之后图片的长宽会变小. 如果要保持图片大小不变, 我们需要在图片周围填充0. padding指的就是填充的0的圈数.

     如果步长是1,则,P=(F-1)/2 F是卷积大小

    1.4卷积大小

    图片卷积中, 卷积核一般为奇数, 比如 3 * 3, 5 * 5, 7 * 7.为什么一般是奇数呢, 出于以下两个方面的考虑:

    1. 根据上面padding的计算公式, 如果要保持图片大小不变, 采用偶数卷积核的话, 比如4 * 4, 将会出现填充1.5圈零的情况.

    2. 奇数维度的过滤器有中心,便于指出过滤器的位置, 即OpenCV卷积中的锚点.

    1.6卷积的API

    filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])

    • ddepth是卷积之后图片的位深, 即卷积之后图片的数据类型, 一般设为-1, 表示和原图类型一致.

    • kernel是卷积核大小, 用元组或者ndarray表示, 要求数据类型必须是float型.

    • anchor 锚点, 即卷积核的中心点, 是可选参数, 默认是(-1,-1)

    • delta 可选参数, 表示卷积之后额外加的一个值, 相当于线性方程中的偏差, 默认是0.

    • borderType 边界类型.一般不设.

    • 返回值是处理之后的图片数据

     示例代码:

    1. # OpenCV图像卷积操作
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./dog.jpeg')
    6. # 相当于原始图片中的每个点都被平均了一下, 所以图像变模糊了.
    7. kernel = np.ones((5, 5), np.float32) / 25
    8. # ddepth = -1 表示图片的数据类型不变
    9. dst = cv2.filter2D(img, -1, kernel)
    10. # 很明显卷积之后的图片模糊了.
    11. cv2.imshow('img', np.hstack((img, dst)))
    12. cv2.waitKey(0)
    13. cv2.destroyAllWindows()

    1.7卷积的意义

    不同的卷积核,可以到达不同的处理效果。卷积操作的目的是提取图像的特征。

    可以看看下面这个博客

    卷积操作的基础知识_王小鹏鹏的博客-CSDN博客_卷积操作

     

     2.滤波

    不管什么滤波,都是卷积操作,使用卷积核与图像数据进行卷积得到相应的数据。图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。

    2.1.方盒滤波与均值滤波

    • boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]) 方盒滤波.

      • 方盒滤波的卷积核的形式如下:

      • normalize = True时, a = 1 / (W * H) 滤波器的宽高

      • normalize = False是. a = 1

      • 一般情况我们都使用normalize = True的情况. 这时 方盒滤波 等价于 均值滤波

    • blur(src, ksize[, dst[, anchor[, borderType]]]) 均值滤波.

    案列代码:

    1. import cv2
    2. import numpy as np
    3. #导入图片
    4. img = cv2.imread('./dog.jpeg')
    5. #方盒滤波
    6. dst=cv2.boxFilter(img,-1,(5,5),normalize=True)
    7. # kernel = np.ones((5, 5), np.float32) / 25
    8. # ddepth = -1 表示图片的数据类型不变
    9. dst = cv2.blur(img, (5, 5))
    10. # 很明显卷积之后的图片模糊了.
    11. cv2.imshow('img', img)
    12. cv2.imshow('dst', dst)
    13. cv2.waitKey(0)
    14. cv2.destroyAllWindows()

    2.2.高斯滤波

    高斯滤波就是使用符合高斯分布的卷积核对图片进行卷积操作. 所以高斯滤波的重点就是如何计算符合高斯分布的卷积核, 即高斯模板.

    • GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])

      • kernel 高斯核的大小.

      • sigmaX, X轴的标准差

      • sigmaY, Y轴的标准差, 默认为0, 这时sigmaY = sigmaX

      • 如果没有指定sigma值, 会分别从ksize的宽度和高度中计算sigma.

    • 选择不同的sigma值会得到不同的平滑效果, sigma越大, 平滑效果越明显.

    代码案列:

    1. # 高斯滤波
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./gaussian.png')
    6. dst = cv2.GaussianBlur(img, (5, 5), sigmaX=1)
    7. cv2.imshow('img', np.hstack((img, dst)))
    8. cv2.waitKey(0)
    9. cv2.destroyAllWindows()

        2.3中值滤波

    中值滤波原理非常简单, 假设有一个数组[1556789], 取其中的中间值(即中位数)作为卷积后的结果值即可.中值滤波对胡椒噪音(也叫椒盐噪音)效果明显.

    注意,中值滤波的api,ksize使用的是整数,不是元组。

    1. # 中值滤波
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./papper.png')
    6. # 注意这里的ksize就是一个数字
    7. dst = cv2.medianBlur(img, 5)
    8. cv2.imshow('img', np.hstack((img, dst)))
    9. cv2.waitKey(0)
    10. cv2.destroyAllWindows()

    2.4.双边滤波

    双边滤波本质上是高斯滤波, 双边滤波和高斯滤波不同的就是:双边滤波既利用了位置信息又利用了像素信息来定义滤波窗口的权重。而高斯滤波只用了位置信息..双边滤波器可以很好的保存图像边缘细节而滤除掉低频分量的噪音,但是双边滤波器的效率不是太高,花费的时间相较于其他滤波器而言也比较长。

    双边滤波可以保留边缘, 同时可以对边缘内的区域进行平滑处理.

    双边滤波的作用就相当于做了美颜.

    • dst=bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])

      • d:卷积核大小

      • sigmaColor是计算像素信息使用的sigma

      • sigmaSpace是计算空间信息使用的sigma

      • 滤波 N越大越平越模糊(2*N+1) sigmas空间越大越模糊sigmar相似因子

     . int d: 表示在过滤过程中每个像素邻域的直径范围。如果这个值是非正数,则函数会从第五个参数sigmaSpace计算该值。 
    . double sigmaColor: 颜色空间过滤器的sigma值,这个参数的值越大,表明该像素邻域内有越宽广的颜色会被混合到一起,产生较大的半相等颜色区域。 
    . double sigmaSpace: 坐标空间中滤波器的sigma值,如果该值较大,则意味着颜色相近的较远的像素将相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色。当d>0时,d指定了邻域大小且与sigmaSpace五官,否则d正比于sigmaSpace. 
     

    1. # 双边滤波
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./lena.png')
    6. dst = cv2.bilateralFilter(img, 7, 20, 50)
    7. cv2.imshow('img', np.hstack((img, dst)))
    8. cv2.waitKey(0)
    9. cv2.destroyAllWindows()

    3.算子

    算子是高通滤波里面的,主要用于边缘提取一般转降噪,灰度图再处理比较好,而前面的滤波主要是降噪,美颜的。

    3.1索贝尔(sobel)算子

    边缘是像素值发生跃迁的位置,是图像的显著特征之一,在图像特征提取,对象检测,模式识别等方面都有重要的作用。

    人眼如何识别图像边缘?

    比如有一幅图,图里面有一条线,左边很亮,右边很暗,那人眼就很容易识别这条线作为边缘.也就是像素的灰度值快速变化的地方.

    sobel算子对图像求一阶导数。一阶导数越大,说明像素在该方向的变化越大,边缘信号越强

    因为图像的灰度值都是离散的数字, sobel算子采用离散差分算子计算图像像素点亮度值的近似梯度.

    图像是二维的,即沿着宽度/高度两个方向. 我们使用两个卷积核对原图像进行处理:

     # x轴方向, 获取的是垂直边缘
    dx_Img = cv2.Sobel(img, cv2.CV_64F, dx=1, dy=0, ksize=5)
    dy_Img = cv2.Sobel(img, cv2.CV_64F, dx=0, dy=1, ksize=5)

    cv2.CV_64F位深写-1也可以  dx dy ksize卷积核大小

    代码案列: 

    1. # 索贝尔算子.
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./chess.png')#
    6. # x轴方向, 获取的是垂直边缘
    7. dx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
    8. dy = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
    9. # 可利用numpy的加法, 直接整合两张图片
    10. # dst = dx + dy
    11. # 也可利用opencv的加法
    12. dst = cv2.add(dx, dy)
    13. cv2.imshow('dx', np.hstack((dx, dy, dst)))
    14. cv2.waitKey(0)
    15. cv2.destroyAllWindows()

    3.2沙尔(Scharr)算子

    • Scharr(src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]])

    • 当内核大小为 3 时, 以上Sobel内核可能产生比较明显的误差(毕竟,Sobel算子只是求取了导数的近似值)。 为解决这一问题,OpenCV提供了 Scharr函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确.

    • Scharr算子和Sobel很类似, 只不过使用不同的kernel值, 放大了像素变换的情况:

    • Scharr算子只支持3 * 3 的kernel所以没有kernel参数了.

    • Scharr算子只能求x方向或y方向的边缘.

    • Sobel算子的ksize设为-1就是Scharr算子.

    • Scharr擅长寻找细小的边缘, 一般用的较少.

    代码案列: 

    1. # 索贝尔算子.
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./lena.png')#
    6. # x轴方向, 获取的是垂直边缘
    7. dx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
    8. # y轴方向, 获取的是水平边缘
    9. dy = cv2.Scharr(img, cv2.CV_64F, 0, 1)
    10. # 可利用numpy的加法, 直接整合两张图片
    11. # dst = dx + dy
    12. # 也可利用opencv的加法
    13. dst = cv2.add(dx, dy)
    14. cv2.imshow('dx', np.hstack((dx, dy, dst)))
    15. cv2.waitKey(0)
    16. cv2.destroyAllWindows()

    3.3拉普拉斯算子

    索贝尔算子是模拟一阶求导,导数越大的地方说明变换越剧烈,越有可能是边缘.拉普拉斯算子可以发现"边缘处"的二阶导数=0, 我们可以利用这一特性去寻找图像的边缘.注意有一个问题,二阶求导为0的位置也可能是无意义的位置.我们需要去降噪处理

    • Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])

    • 可以同时求两个方向的边缘

    • 对噪音敏感, 一般需要先进行去噪再调用拉普拉斯

     代码案列:

    1. # 拉普拉斯
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./chess.png')#
    6. dst = cv2.Laplacian(img, -1, ksize=3)
    7. cv2.imshow('dx', np.hstack((img, dst)))
    8. cv2.waitKey(0)
    9. cv2.destroyAllWindows()

     .3.4.边缘检测Canny

    Canny 边缘检测算法 是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的 最优算法, 最优边缘检测的三个主要评价标准是:

    • 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。

    • 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。

    • 最小响应: 图像中的边缘只能标识一次。

    Canny边缘检测的一般步骤 :

    • 去噪. 边缘检测容易受到噪声影响, 在进行边缘检测前通常需要先进行去噪, 一般用高斯滤波去除噪声.

    • 计算梯度: 对平滑后的图像采用sobel算子计算梯度和方向.

    • 非极大值抑制:

    • 在获取了梯度和方向后, 遍历图像, 去除所有不是边界的点.

    • 实现方法: 逐个遍历像素点, 判断当前像素点是否是周围像素点中具有相同方向梯度的最大值.

    • 下图中, 点A,B,C具有相同的方向, 梯度方向垂直于边缘.

    • 判断点A是否为A,B,C中的局部最大值, 如果是, 保留该点;否则,它被抑制(归零)

    Canny(img, minVal, maxVal, ...)

    边界设置是难点: 阈值越小, 细节越丰富,网上有资源,可以设置这个值

    代码案例:

    1. # Canny
    2. import cv2
    3. import numpy as np
    4. #导入图片
    5. img = cv2.imread('./lena.png')#
    6. # 阈值越小, 细节越丰富
    7. lena1 = cv2.Canny(img, 100, 200)
    8. lena2 = cv2.Canny(img, 64, 128)
    9. cv2.imshow('lena', np.hstack((lena1, lena2)))
    10. cv2.waitKey(0)
    11. cv2.destroyAllWindows()

  • 相关阅读:
    【ssh_config】SSH中配置多个private key
    【活着活着就老了-冯唐】阅后
    Show Me the Code之MXNet网络模型(三)
    【附代码】使用Shapely计算多边形外扩与收缩
    QT MV\MVC结构
    常用的网络攻击手段
    OAuth2授权服务器Id Server一键生成配置原理
    Java程序设计——集元数据(JDBC编程)
    回归啦!!!
    java 数据脱敏
  • 原文地址:https://blog.csdn.net/m0_46392035/article/details/125885986