【OpenCV4】滤波器、图片卷积、步长、padding、卷积核的大小、方盒滤波与均值滤波、高斯滤波、中值滤波、双边滤波、边缘检测Canny(重要)、索贝尔算子、沙尔算子、 拉普拉斯算子

1 卷积
2 方盒滤波与均值滤波
3 高斯滤波
4 中值滤波
5 双边滤波
6 索贝尔(sobel)算子
7 沙尔(Scharr)算子
8 拉普拉斯算子
9 边缘检测Canny(重要)

1 卷积

1.1 什么是图片卷积

图像卷积就是卷积核在图像上按行滑动遍历像素时不断的相乘求和的过程
请添加图片描述

1.2 步长

**步长就是卷积核在图像上移动的步幅.**上面例子中卷积核每次移动一个像素步长的结果, 如果将这个步长修改为2, 结果会如何?

为了充分扫描图片, 步长一般设为1.

请添加图片描述

1.3 padding

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

请添加图片描述

我们可以通过公式计算出需要填充的0的圈数.
请添加图片描述

如果要保持卷积之后图片大小不变, 可以得出等式: ( N + 2 P − F + 1 ) = N (N + 2P - F + 1) = N (N+2PF+1)=N从而可以推导出 P = F − 1 2 P = \frac{F -1}{2} P=2F1

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 边界类型.一般不设.
import cv2
import numpy as np

img = cv2.imread('./dog.jpeg')

# 将彩色图像转换为灰度图像
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)

# kernel是卷积核大小, 用元组或者ndarray表示, 要求数据类型必须是float型.
# 相当于原始图片中的每个点都被平均了一下, 所以图像变模糊了.
# kernel = np.ones((5, 5), np.float32) / 25

# 尝试其他的卷积操作,突出轮廓
# kernel = np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]])

# 浮雕效果
# kernel = np.array([[-2, 1, 0], [-1, 1, -1], [0, 1, 2]])

# 锐化
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])

# 卷积操作
# 使用自定义卷积核对灰度图像进行二维滤波
# gray_img 是输入的灰度图像
# -1 表示输出图像的深度与输入图像相同
# kernel 是自定义的卷积核,用于对图像进行滤波
# 该函数通过卷积操作应用滤波器,输出图像存储在 dst 变量中
dst = cv2.filter2D(gray_img, -1, kernel)


# 显示一幅并排展示的图像
# np.hstack((gray_img, dst)) 将 gray_img 和 dst 水平堆叠成一幅图像
# gray_img 是原始灰度图像,dst 是经过滤波处理后的图像
# 'img' 是窗口的名称
# cv2.imshow 用于显示图像,窗口中将并排显示原图和滤波后的图像
cv2.imshow('img', np.hstack((gray_img, dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

请添加图片描述

2 方盒滤波与均值滤波

  • 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]]]) 均值滤波.

方盒滤波

# 方盒滤波
import cv2
import numpy as np

# 读取输入图像 'dog.jpeg'
# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./dog.jpeg')

# 使用方盒滤波对图像进行平滑处理
# cv2.boxFilter() 函数进行方盒滤波,不需要手动创建卷积核
# -1 表示输出图像的深度与输入图像相同
# (5, 5) 是方盒滤波的卷积核大小(5x5 的核)
# normalize=True 表示进行归一化处理(卷积核的每个元素都除以核的元素总数,使得滤波的结果平均化)
dst = cv2.boxFilter(img, -1, (5, 5), normalize=True)

# 使用 np.hstack() 将原始图像和经过方盒滤波处理的图像在水平方向上并排拼接
# 并通过 cv2.imshow() 显示拼接后的图像
# 'img' 是显示窗口的名称
cv2.imshow('img', np.hstack((img, dst)))

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

均值滤波

# 均值滤波
import cv2
import numpy as np

# 读取输入图像 'dog.jpeg'
# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./dog.jpeg')

# 使用平均滤波器对图像进行平滑处理
# img 是输入的原始图像
# (5, 5) 是滤波器的卷积核大小(5x5 的矩形区域)
# cv2.blur() 函数通过对该区域内像素进行平均值计算,从而减小噪声,实现图像平滑
# 结果存储在 dst 中,dst 是滤波后的图像
dst = cv2.blur(img, (5, 5))

# 使用 np.hstack() 将原始图像和经过方盒滤波处理的图像在水平方向上并排拼接
# 并通过 cv2.imshow() 显示拼接后的图像
# 'img' 是显示窗口的名称
cv2.imshow('img', np.hstack((img, dst)))

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

3 高斯滤波

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

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

请添加图片描述

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

高斯函数的一般形式为:

请添加图片描述

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

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

请添加图片描述

  • G ( x , y ) = 1 2 π σ 2 e − x 2 + y 2 2 σ 2 G(x, y) = \frac{1}{2 \pi \sigma^2}e^{-\frac{x^2 + y^2}{2\sigma^2}} G(x,y)=2πσ21e2σ2x2+y2

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

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

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

请添加图片描述

有了卷积核, 计算高斯滤波就简单了.假设现有9个像素点,灰度值(0-255)的高斯滤波计算如下:

请添加图片描述

将这9个值加起来,就是中心点的高斯滤波的值。对所有点重复这个过程,就得到了高斯模糊后的图像。

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

    • kernel 高斯核的大小.
    • sigmaX, X轴的标准差
    • sigmaY, Y轴的标准差, 默认为0, 这时sigmaY = sigmaX
    • 如果没有指定sigma值, 会分别从ksize的宽度和高度中计算sigma.
  • 选择不同的sigma值会得到不同的平滑效果, sigma越大, 平滑效果越明显.
    请添加图片描述

# 选择不同的sigma值会得到不同的平滑效果, sigma越大, 平滑效果越明显.
import cv2
import numpy as np

# 读取输入图像 'lena.png'
# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./lena.png')

# 使用高斯滤波对图像进行平滑处理
# cv2.GaussianBlur() 函数用于对图像进行高斯模糊处理
# (5, 5) 是高斯滤波的卷积核大小(5x5 的矩形核),核越大,模糊效果越强
# sigmaX=1 是高斯滤波器在 X 方向上的标准差,sigma 越大,平滑效果越明显
dst = cv2.GaussianBlur(img, (5, 5), sigmaX=1)

# 使用 np.hstack() 将原始图像和经过高斯滤波处理的图像在水平方向上并排拼接
# 并通过 cv2.imshow() 显示拼接后的图像
# 'img' 是显示窗口的名称
cv2.imshow('img', np.hstack((img, dst)))

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

  • 没有指定sigma时, ksize越大, 平滑效果越明显
    请添加图片描述
# 没有指定sigma时, ksize越大, 平滑效果越明显
import cv2
import numpy as np

# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./gaussian.png')
dst = cv2.GaussianBlur(img, (5, 5), sigmaX=0)
cv2.imshow('img', np.hstack((img, dst)))

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

请添加图片描述

4 中值滤波

  • cv2.medianBlur(src, ksize)

  • src:输入图像,可以是彩色图像(BGR 格式)或灰度图像。该图像必须是 numpy 数组形式。

  • ksize:滤波器的核大小,必须为正奇数(例如 3、5、7 等)。该值定义了核的大小,即用于计算中值的邻域范围。

    • 如果 ksize=3,意味着核的大小为 3x3,系统会选择该区域的中值作为输出像素值。
    • ksize 越大,平滑效果越强,但图像会变得更加模糊。
      中值滤波原理非常简单, 假设有一个数组[1556789], 取其中的中间值(即中位数)作为卷积后的结果值即可.中值滤波对胡椒噪音(也叫椒盐噪音)效果明显.
# 导入 OpenCV 和 NumPy 库
import cv2
import numpy as np

# 读取输入图像 'papper.png'
# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./papper.png')

# 使用中值滤波器对图像进行平滑处理
# cv2.medianBlur() 函数应用中值滤波,用于去除图像中的椒盐噪声
# 中值滤波会用卷积核区域内的中值来替换中心像素的值
# 5 是中值滤波的核大小,必须是奇数,且越大,滤波效果越强
dst = cv2.medianBlur(img, 5)

# 使用 np.hstack() 将原始图像和经过中值滤波处理的图像在水平方向上并排拼接
# 并通过 cv2.imshow() 显示拼接后的图像
# 'img' 是显示窗口的名称
cv2.imshow('img', np.hstack((img, dst)))

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

请添加图片描述

5 双边滤波

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

请添加图片描述

双边滤波本质上是高斯滤波, 双边滤波和高斯滤波不同的就是:双边滤波既利用了位置信息又利用了像素信息来定义滤波窗口的权重。而高斯滤波只用了位置信息.

对于高斯滤波,仅用空间距离的权值系数核与图像卷积后,确定中心点的灰度值。即认为离中心点越近的点,其权重系数越大。

双边滤波中加入了对灰度信息的权重,即在邻域内,灰度值越接近中心点灰度值的点的权重更大,灰度值相差大的点权重越小。此权重大小,则由值域高斯函数确定。

两者权重系数相乘,得到最终的卷积模板。由于双边滤波需要每个中心点邻域的灰度信息来确定其系数,所以其速度与比一般的滤波慢很多,而且计算量增长速度为核大小的平方。

请添加图片描述

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

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

  • bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])
    • sigmaColor是计算像素信息使用的sigma
    • sigmaSpace是计算空间信息使用的sigma
  • 参数解释:
    • src:
      输入图像,可以是彩色图像(BGR 格式)或灰度图像。该图像必须是 numpy 数组形式。

    • d:
      邻域直径,即用于计算像素的邻域的直径。可以是一个正整数。这个值决定了滤波器考虑的邻域大小。较大的 d 值会考虑更大的邻域,但计算量也会增加。

    • sigmaColor:
      色彩空间的标准差,用于控制颜色相似性。较大的 sigmaColor 值会使得更多颜色相似的像素被平滑处理,导致颜色变化更加平滑。

    • sigmaSpace:
      空间坐标的标准差,用于控制距离相似性。较大的 sigmaSpace 值会使得距离较远的像素也被考虑在内,导致更大范围的平滑。

    • dst(可选):
      输出图像,与输入图像具有相同的大小和类型。如果未提供,函数会创建一个与输入图像相同类型和大小的输出图像。

    • borderType(可选):
      边界类型,用于处理图像边界的像素。常用的边界类型包括:

    • cv2.BORDER_CONSTANT:使用常数值填充边界外的区域。

    • cv2.BORDER_REFLECT:边界像素对称地反射。

    • cv2.BORDER_REPLICATE:边界像素的复制。

    • cv2.BORDER_WRAP:边界像素的循环包裹。

# 导入 OpenCV 和 NumPy 库
import cv2
import numpy as np

# 读取输入图像 'papper.png'
# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./lena.png')

# 使用双边滤波器对图像进行平滑处理
# cv2.bilateralFilter() 函数用于应用双边滤波,保持边缘的同时去除噪声

# img 是输入图像,必须是 numpy 数组形式,支持彩色图像或灰度图像
# 7 是邻域直径,表示滤波器在计算每个像素值时考虑的邻域大小
# sigmaColor=20 是颜色空间的标准差,用于控制颜色相似性的平滑程度
# sigmaSpace=50 是空间坐标的标准差,用于控制距离相似性的平滑程度
# 较小的 sigmaColor 值表示只有颜色差异较小的像素才会被平滑,较大的 sigmaSpace 值表示距离较远的像素也会被考虑在内

# dst 是输出图像,与输入图像具有相同的大小和类型,存储平滑处理后的结果
dst = cv2.bilateralFilter(img, 7, sigmaColor=20, sigmaSpace=50)

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

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

请添加图片描述

6 索贝尔(sobel)算子

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

人眼如何识别图像边缘?

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

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

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

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

  • 水平方向

    请添加图片描述

  • 垂直方向
    请添加图片描述

这样的话,我们就得到了两个新的矩阵,分别反映了每一点像素在水平方向上的亮度变化情况和在垂直方向上的亮度变换情况.

综合考虑这两个方向的变化,我们使用以下公式反映某个像素的梯度变化情况.

G = G x 2 + G y 2 G= \sqrt{G^2_x+G^2_y} G=Gx2+Gy2

有时候为了简单起见,也直接用绝对值相加替代. G = ∣ G X ∣ + ∣ G Y ∣ G = |G_X| + |G_Y| G=GX+GY

# 索贝尔算子.
# 导入 OpenCV 和 NumPy 库
import cv2
import numpy as np

# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./chess.png')

# 注意:Sobel 算子要分别计算 x 轴和 y 轴的梯度
# Sobel 算子用于边缘检测,通过计算图像的梯度来检测图像中的边缘

# 计算 x 轴方向的梯度,只检测垂直方向的边缘
# cv2.Sobel() 函数计算图像在 x 和 y 方向的导数
# img 是输入图像
# cv2.CV_64F 指定输出图像的像素深度为 64 位浮点数,以便处理负值
# dx=1 表示计算 x 方向的导数(梯度),dy=0 表示 y 方向的导数不计算
# ksize=3 是 Sobel 算子的核大小(3x3 的卷积核),可以选择 1、3、5、7 等奇数值
dx = cv2.Sobel(img, cv2.CV_64F, dx=1, dy=0, ksize=3)

# 计算 y 轴方向的梯度,只检测水平方向的边缘
# dx=0 表示不计算 x 方向的导数,dy=1 表示计算 y 方向的导数
# 这样就只会检测出水平方向的边缘
dy = cv2.Sobel(img, cv2.CV_64F, dx=0, dy=1, ksize=3)

# 合并 x 轴和 y 轴方向的梯度
# cv2.add() 函数将两个图像逐元素相加
# dst 是合并后的梯度图像,既包含垂直方向的边缘,也包含水平方向的边缘
dst = cv2.add(dx, dy)

# 使用 addWeighted 函数也可以合并 x 和 y 方向的梯度,得到相似效果
# cv2.addWeighted() 按权重合并两个图像
# 0.5 是 x 方向梯度的权重,0.5 是 y 方向梯度的权重
# gamma=0 表示不添加额外的偏移量
wight = cv2.addWeighted(dx, 0.5, dy, 0.5, gamma=0)

# cv2.imshow('chess', img)
# cv2.imshow('img', dx)
# cv2.imshow('dy', dy)

# 把x轴y轴的梯度合并到一起
cv2.imshow('chess', np.hstack((dx, dy)))

cv2.imshow('dst', dst)

cv2.imshow('wight', wight)

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

请添加图片描述

7 沙尔(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擅长寻找细小的边缘, 一般用的较少.

# 索贝尔算子.
# 导入 OpenCV 和 NumPy 库
import cv2
import numpy as np

# img 是读取的彩色图像(默认使用 BGR 色彩格式)
img = cv2.imread('./lena.png')

# 使用 Scharr 算子计算图像的 x 和 y 方向的梯度
# Scharr 是 Sobel 算子的改进版本,特别是在计算 3x3 核时更精确

# 计算 x 轴方向的梯度,主要用于检测垂直方向的边缘
# cv2.Scharr() 是计算图像梯度的函数,类似于 cv2.Sobel(),但精度更高
# img 是输入图像
# cv2.CV_64F 指定输出图像的像素深度为 64 位浮点数,以便处理负值
# dx=1 表示计算 x 方向的导数(梯度),dy=0 表示不计算 y 方向的导数
dx = cv2.Scharr(img, cv2.CV_64F, dx=1, dy=0)

# 计算 y 轴方向的梯度,主要用于检测水平方向的边缘
# dx=0 表示不计算 x 方向的导数,dy=1 表示计算 y 方向的导数
dy = cv2.Scharr(img, cv2.CV_64F, dx=0, dy=1)

# 按权重将 x 和 y 方向的梯度图像合并
# cv2.addWeighted() 函数用于加权合并两个图像
# dx 和 dy 分别是 x 方向和 y 方向的梯度图像
# 0.5 是 x 方向梯度的权重,0.5 是 y 方向梯度的权重,gamma=0 表示不添加额外的偏移量
# 合并后的结果 dst 包含了水平和垂直方向的边缘信息
dst = cv2.addWeighted(dx, 0.5, dy, 0.5, gamma=0)

# 将 x 方向梯度、y 方向梯度和合并后的图像水平拼接,并在窗口中显示
# np.hstack() 将图像在水平方向上并排拼接
cv2.imshow('img', np.hstack((dx, dy, dst)))

# 等待用户按任意键继续,0 表示无限等待
cv2.waitKey(0)

# 销毁所有窗口,释放资源
cv2.destroyAllWindows()

请添加图片描述

8 拉普拉斯算子

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

请添加图片描述

那如果继续对f’(t)求导呢?

请添加图片描述

可以发现"边缘处"的二阶导数=0, 我们可以利用这一特性去寻找图像的边缘. 注意有一个问题,二阶求导为0的位置也可能是无意义的位置.

  • 拉普拉斯算子推导过程

    • 以x方向求解为例:
      一阶差分: f ′ ( x ) = f ( x ) − f ( x − 1 ) f'(x) = f(x) - f(x - 1) f(x)=f(x)f(x1)
      二阶差分: f ′ ′ ( x ) = f ′ ( x + 1 ) − f ′ ( x ) = ( f ( x + 1 ) − f ( x ) ) − ( f ( x ) − f ( x − 1 ) ) f''(x) = f'(x+1) - f'(x) = (f(x + 1) - f(x)) - (f(x) - f(x - 1)) f′′(x)=f(x+1)f(x)=(f(x+1)f(x))(f(x)f(x1))
      化简后: f ′ ′ ( x ) = f ( x − 1 ) − 2 f ( x ) ) + f ( x + 1 ) f''(x) = f(x - 1) - 2 f(x)) + f(x + 1) f′′(x)=f(x1)2f(x))+f(x+1)

      同理可得: f ′ ′ ( y ) = f ( y − 1 ) − 2 f ( y ) ) + f ( y + 1 ) f''(y) = f(y - 1) - 2 f(y)) + f(y + 1) f′′(y)=f(y1)2f(y))+f(y+1)

      把x,y方向的梯度叠加在一起.

      f ′ ′ ( x , y ) = f x ′ ( x , y ) + f y ′ ( x , y ) f''(x,y) = f'_x(x,y) + f'_y(x,y) f′′(x,y)=fx(x,y)+fy(x,y)

      f ′ ′ ( x , y ) = f ( x − 1 , y ) − 2 f ( x , y ) ) + f ( x + 1 , y ) + f ( x , y − 1 ) − 2 f ( x , y ) ) + f ( x , y + 1 ) f''(x,y) = f(x - 1, y) - 2 f(x,y)) + f(x + 1, y) + f(x, y - 1) - 2 f(x,y)) + f(x,y + 1) f′′(x,y)=f(x1,y)2f(x,y))+f(x+1,y)+f(x,y1)2f(x,y))+f(x,y+1)

      $f’'(x,y) = f(x - 1, y) + f(x + 1, y) + f(x, y - 1) + f(x,y + 1) - 4 f(x,y)) $

      这个等式可以用矩阵写成:

      f ′ ′ ( x , y ) = [ 0 1 0 1 − 4 1 0 1 0 ] ⨀ [ f ( x − 1 , y − 1 ) f ( x , y − 1 ) f ( x + 1 , y − 1 ) f ( x − 1 , y ) f ( x , y ) f ( x + 1 , y ) f ( x − 1 , y + 1 ) f ( x , y + 1 ) f ( x + 1 , y + 1 ) ] f''(x,y) = \left[\begin{matrix}0 & 1 & 0\\1 & -4 & 1\\0 & 1 & 0\end{matrix}\right] \bigodot \left[\begin{matrix}f(x-1, y-1) & f(x, y-1) & f(x+1,y-1)\\f(x-1,y) & f(x,y) & f(x+1,y)\\f(x-1,y+1) & f(x,y+1) & f(x+1,y+1)\end{matrix}\right] f′′(x,y)= 010141010 f(x1,y1)f(x1,y)f(x1,y+1)f(x,y1)f(x,y)f(x,y+1)f(x+1,y1)f(x+1,y)f(x+1,y+1)

      这样就得到了拉普拉斯算子的卷积核即卷积模板.++

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

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

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

# 拉普拉斯
import cv2
import numpy as np


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

# 使用拉普拉斯算子进行图像边缘检测
# cv2.Laplacian() 函数用于计算图像的二阶导数,常用于检测图像中的边缘
# img 是输入图像
# -1 表示输出图像的深度与输入图像相同,保持原始图像的数据类型
# ksize=3 是卷积核的大小,拉普拉斯算子通常使用 3x3 的核进行计算
# 较大的 ksize 会使边缘检测更平滑,但同时会导致边缘模糊
dst = cv2.Laplacian(img, -1, ksize=3)


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

请添加图片描述

9 边缘检测Canny

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

  • 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
  • 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
  • 最小响应: 图像中的边缘只能标识一次。
  • Canny边缘检测的一般步骤

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

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

      • G = G x 2 + G y 2 G = \sqrt{G_x^2+G_y^2} G=Gx2+Gy2 为了方便一般可以改用绝对值

      • θ = a r c t a n ( G y G x ) \theta = arctan(\frac{G_y}{G_x}) θ=arctan(GxGy)

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

      • 计算出来的梯度和方向大概如下图:

    • 非极大值抑制

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

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

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

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

        请添加图片描述

      • 更形象的例子:

    • 滞后阈值

      请添加图片描述

  • Canny(img, minVal, maxVal, …)

# Canny
# Canny
import cv2
import numpy as np


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

# 使用 Canny 边缘检测算法对图像进行处理
# Canny 算法是一种常用的边缘检测方法,通过计算图像的梯度和非极大值抑制来检测边缘

# img 是输入图像
# 100 是低阈值,用于检测图像中较弱的边缘
# 200 是高阈值,用于检测图像中较强的边缘
# Canny 算子会在较强的边缘和较弱的边缘之间进行连接,生成二值化的边缘图像
lena1 = cv2.Canny(img, 100, 200)

# 另一组不同的阈值,64 是低阈值,128 是高阈值
# 通过使用较低的阈值范围,可以检测出更多的边缘,但可能会包含更多的噪声
lena2 = cv2.Canny(img, 64, 128)


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

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值