2.Opencv滤波器和边缘检测

今天下午来复习滤波器和边缘检测,广义上我将它的概念认知到也就是卷积操作,通过对原始图片数据进行加权求和获得新的矩阵。

1.卷积

1.1 :图片卷积

图像卷积就是卷积核在图像上按行滑动遍历像素时不断的相乘求和的过程。

1.2:步长

步长就是卷积核在原始图片上滑动的步幅,一般为了充分对图片进行扫描,将步长设为1。

1.3:填充(padding)

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

1.4:卷积核的大小

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

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

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

1.5:卷积案例

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

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

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

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

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

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

# OpenCV图像卷积操作
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
#导入图片
img = cv2.imread('img/111.png')
# img = Image.open('img/111.png')
# 相当于原始图片中的每个点都被平均了一下, 所以图像变模糊了.
kernel = np.ones((5, 5), np.float32) / 25

# 边缘提取核
kernel1 = np.float32([(-1,-1,-1),(-1,8,-1),(-1,-1,-1)])
# 浮雕效果
kernel2 = np.float32([(-2,1,0),(-1,1,1),(0,1,2)])
# 锐化效果
kernel3 = np.float32([(0,-1,0),(-1,5,-1),(0,-1,0)])

# ddepth = -1 表示图片的数据类型不变
dst = cv2.filter2D(img, -1, kernel3)

plt.figure("Dog!") 
plt.imshow(dst)
plt.show()
# 很明显卷积之后的图片模糊了.
# cv2.imshow('img', np.hstack((img, dst)))

# cv2.waitKey(0)
# cv2.destroyAllWindows()

2.方盒滤波和均值滤波

# encoding:gbk
# OpenCV图像卷积操作:方盒滤波,均值滤波
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
#导入图片
img = cv2.imread('img/111.png')

# 方盒滤波
img = cv2.boxFilter(img,-1,(5,5),normalize=True)

# 均值滤波
img = cv2.blur(img,(5,5))

plt.imshow(img)
plt.show()

3.高斯滤波

高斯滤波的核心思想是让临近的像素具有更高的重要度. 对周围像素计算加权平均值, 较近的像素具有较大的权重值.

要理解高斯滤波首先要知道什么是高斯函数.高斯函数在是符合高斯分布(也叫正态分布)的数据的概率密度函数.画出来长这样子:

高斯函数的特点是以x轴某一点(这一点称为均值)为对称轴, 越靠近中心数据发生的概率越高, 最终形成一个两边平缓, 中间陡峭的钟型(有的地方也叫帽子)图形.

高斯函数的一般形式为:

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

假定中心点的坐标是(0,0),那么取距离它最近的8个点坐标,为了计算,需要设定σ的值。假定σ=1.5,则模糊半径为1的高斯模板就算如下:

可以观察到越靠近中心, 数值越大, 越边缘的数值越小.符合高斯分布的特点.

通过高斯函数计算出来的是概率密度函数, 所以我们还要确保这九个点加起来为1,这9个点的权重总和等于0.4787147,因此上面9个值还要分别除以0.4787147,得到最终的高斯模板。

注: 有些整数高斯模板是在归一化后的高斯模板的基础上每个数除上左上角的值, 然后取整.

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

    • kernel 高斯核的大小.

    • sigmaX, X轴的标准差

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

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

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

  • 没有指定sigma时, ksize越大, 平滑效果越明显

# 高斯滤波
import cv2
import numpy as np

#导入图片
img = cv2.imread('./gaussian.png')

dst = cv2.GaussianBlur(img, (5, 5), sigmaX=1)

cv2.imshow('img', np.hstack((img, dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

4.中值滤波

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

dst = cv2.medianBlur(img, 5)

5.双边滤波

双边滤波对于图像的边缘信息能过更好的保存。其原理为一个与空间距离相关的高斯函数与一个灰度距离相关的高斯函数相乘。

  • 双边滤波本质上是高斯滤波, 双边滤波和高斯滤波不同的就是:双边滤波既利用了位置信息又利用了像素信息来定义滤波窗口的权重,而高斯滤波只用了位置信息。
  • 对于高斯滤波,仅用空间距离的权值系数核与图像卷积后,确定中心点的灰度值。即认为离中心点越近的点,其权重系数越大。
  • 双边滤波中加入了对灰度信息的权重,即在邻域内,灰度值越接近中心点灰度值的点的权重更大,灰度值相差大的点权重越小。此权重大小,则由值域高斯函数确定。两者权重系数相乘,得到最终的卷积模板。

由于双边滤波需要每个中心点邻域的灰度信息来确定其系数,所以其速度与比一般的滤波慢很多,而且计算量增长速度为核大小的平方。双边滤波可以保留边缘, 同时可以对边缘内的区域进行平滑处理。

bilateralFilter(src, d, sigmaColor, sigmaSpace, dst, borderType)

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

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

# 双边滤波
import cv2
import numpy as np

#导入图片
img = cv2.imread('./lena.png')

dst = cv2.bilateralFilter(img, 7, 20, 50)

cv2.imshow('img', np.hstack((img, dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

6. 边缘检测

6.1 索贝尔(sobel)算子

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

人眼如何识别图像边缘?

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

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

因为图像的灰度值都是离散的数字, sobel算子采用离散差分算子计算图像像素点亮度值的近似梯度.每个像素在水平和垂直方向上都有着梯度的变化,将其相加。

# 索贝尔算子.
import cv2
import numpy as np

#导入图片
img = cv2.imread('./chess.png')#
# x轴方向, 获取的是垂直边缘
dx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
dy = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)

# 可利用numpy的加法, 直接整合两张图片
# dst = dx + dy
# 也可利用opencv的加法
dst = cv2.add(dx, dy)
cv2.imshow('dx', np.hstack((dx, dy, dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()

6.2 拉普拉斯算子

索贝尔算子是模拟一阶求导,导数越大的地方说明变换越剧烈,越有可能是边缘.

那如果继续对f'(t)求导呢? 可以发现"边缘处"的二阶导数=0, 可以利用这一特性去寻找图像的边缘. 注意有一个问题,二阶求导为0的位置也可能是无意义的位置。

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

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

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

# 拉普拉斯
import cv2
import numpy as np

#导入图片
img = cv2.imread('./chess.png')#
dst = cv2.Laplacian(img, -1, ksize=3)

cv2.imshow('dx', np.hstack((img, dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()

6.3 边缘检测Canny算子

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

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

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

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

Canny边缘检测的一般步骤:

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

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

    • 为了方便一般可以改用绝对值。

    • 梯度的方向被归为四类: 垂直, 水平和两个对角线。

  • 非极大值抑制

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

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

# Canny
import cv2
import numpy as np

#导入图片
img = cv2.imread('./lena.png')#
# 阈值越小, 细节越丰富
lena1 = cv2.Canny(img, 100, 200)
lena2 = cv2.Canny(img, 64, 128)

cv2.imshow('lena', np.hstack((lena1, lena2)))
cv2.waitKey(0)
cv2.destroyAllWindows()

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值