我在Vscode学OpenCV 图像处理三(图像梯度--边缘检测【图像梯度、Sobel 算子、 Scharr 算子、 Laplacian 算子、Canny 边缘检测】)

文章目录

  • 一、图像梯度
    • 1.1 介绍
    • 1.2 涉及函数
  • 二、高频强调滤波器
    • 2.1 Sobel 算子
      • 2.1.1 Sobel 理论基础
      • 2.1.2 Sobel 算子及函数使用
        • (1)对参数取绝对值
        • (2)控制dx,dy方向的求导阶数
          • 1. **计算 x 方向边缘(梯度):**
          • 2. **计算 y 方向边缘(梯度):**
          • 3. **参数 `dx` 和参数 `dy` 的值均为 1**
          • 4. **计算 x 方向和 y 方向的边缘叠加:**
      • 2.1.3通过实际例子表示
        • (1)简单图像
        • (2)复杂的,实际的相片
      • 2.1.4 近似值
    • 2.2 Scharr 算子
      • 2.2.1 等价的函数。
      • 2.2.2需要满足的条件:dx >= 0 && dy >= 0 && dx+dy = 1
      • 2.2.3 Sobel 算子和 Scharr 算子的比较
    • 2.3 拉普拉斯 Laplacian 算子
      • 2.3.1 函数
  • 三、Canny 边缘检测
    • 3.1 Canny 原理
    • 3.2 Canny 函数及使用

这里需要区分开边缘检测和轮廓检测
边缘检测并非万能,边缘检测虽然能够检测出边缘,但边缘是不连续的,检测到的边缘并不是一个整体。图像轮廓是指将边缘连接起来形成的一个整体,用于后续的计算。

OpenCV 提供了查找图像轮廓的函数 cv2.findContours(),该函数能够查找图像内的轮廓信息,而函数cv2.drawContours()能够将轮廓绘制出来。

一、图像梯度

图像梯度是指图像中灰度强度变化的方向和幅度。梯度计算对于许多计算机视觉和图像处理任务非常重要,如边缘检测、特征提取等。
图像梯度通常使用Sobel、Scharr或其他卷积核进行计算。这些卷积核可以在图像上进行卷积操作,以便检测图像中的水平和垂直边缘。梯度的方向表示灰度变化最快的方向,而梯度的幅度表示这个变化的强度。
·
图像梯度是指图像中像素值变化的快慢和方向。在图像处理中,常用于检测图像中的边缘和轮廓。图像梯度的计算可以通过一阶或二阶导数的方法来实现。

1.1 介绍

  1. 一阶导数(梯度):

    • 水平方向梯度(Gx): 表示图像在水平方向上的变化率。
    • 垂直方向梯度(Gy): 表示图像在垂直方向上的变化率。
    • 梯度幅值: 通过Gx和Gy的组合计算,通常使用欧几里得范数(平方和的平方根)表示:
      在这里插入图片描述
  2. 梯度方向:

    • 梯度方向角度(θ): 通过arctan(Gy/Gx)计算,表示梯度的方向。
  3. 边缘检测:

    • 基于图像梯度的边缘检测算法,如Sobel、Prewitt、Roberts等,通过卷积运算来获取图像梯度。
  4. Canny边缘检测:

    • Canny边缘检测是一种常用的边缘检测方法,它利用图像梯度的幅值和方向来检测边缘。
  5. 图像增强:

    • 图像梯度也可以用于图像增强,通过调整图像梯度的幅值和方向来改善图像的视觉效果。

在实际图像处理中,图像梯度是一个重要的特征,它不仅用于边缘检测,还可以应用于目标检测、图像分割和其他计算机视觉任务。通过分析图像梯度,可以获取关于图像结构和内容的有用信息。

1.2 涉及函数

  1. Sobel算子:

    • cv2.Sobel(src, ddepth, dx, dy, ksize): 对图像进行Sobel算子的卷积操作,其中dxdy表示求导的阶数,ksize是卷积核的大小。
  2. Scharr算子:

    • cv2.Scharr(src, ddepth, dx, dy): 类似于Sobel,用于对图像进行Scharr算子的卷积操作。
  3. Laplacian算子:

    • cv2.Laplacian(src, ddepth): 对图像进行Laplacian算子的卷积操作,用于增强图像中的高频信息。
  4. Canny边缘检测:

    • cv2.Canny(image, threshold1, threshold2): 使用Canny边缘检测算法,threshold1threshold2是梯度阈值,用于定义边缘的强弱。
  5. 角度和幅值计算:

    • cv2.phase(x, y): 计算输入图像梯度的相位角度。
    • cv2.magnitude(x, y): 计算输入图像梯度的幅值。
  6. 图像梯度计算:

    • cv2.Sobel(), cv2.Scharr(), 和 cv2.Laplacian() 的输出可以通过 cv2.magnitude()cv2.phase() 计算幅值和相位。
  7. 图像增强:

    • 借助梯度信息可以进行图像增强,例如通过调整梯度的幅值来实现锐化。
  8. 图像显示:

    • cv2.imshow(window_name, image): 用于显示图像,可以用来显示梯度图等中间结果。

二、高频强调滤波器

在OpenCV中,梯度滤波器或高通滤波器通常用于突出图像中的边缘和细节。这些滤波器是一种高频强调滤波器,可以通过卷积操作来应用于图像。、

  1. Sobel滤波器:

    • Sobel滤波器是一种常用的梯度滤波器,用于在图像中检测边缘。它分为水平和垂直两个方向。
    sobelx = cv2.Sobel(src, cv2.CV_64F, 1, 0, ksize=3)
    sobely = cv2.Sobel(src, cv2.CV_64F, 0, 1, ksize=3)
    

    这里,10分别表示水平和垂直方向,ksize是卷积核的大小。

  2. Scharr滤波器:

    • Scharr滤波器类似于Sobel,但对边缘的响应更强。你可以使用cv2.Scharr()函数来应用Scharr滤波器。
    scharrx = cv2.Scharr(src, cv2.CV_64F, 1, 0)
    scharry = cv2.Scharr(src, cv2.CV_64F, 0, 1)
    
  3. Laplacian滤波器:

    • Laplacian滤波器用于增强图像的高频部分,通常用于边缘检测。
    laplacian = cv2.Laplacian(src, cv2.CV_64F)
    
  4. Canny边缘检测:

    • Canny边缘检测是一种综合了梯度信息的方法,包括非极大值抑制和双阈值处理。
    edges = cv2.Canny(src, threshold1, threshold2)
    

    其中,threshold1threshold2是梯度阈值,用于定义边缘的强弱。

这些滤波器可以帮助突出图像中的边缘信息,对于图像处理中的特定任务,你可以根据需要选择合适的滤波器。

2.1 Sobel 算子

2.1.1 Sobel 理论基础

Sobel算子是一种常用的边缘检测算法,用于计算图像中像素点的梯度强度。在Sobel算子中,中心点相连的系数为2,是为了更好地捕捉图像中的边缘信息。

Sobel算子包含两个3x3的卷积核(一个用于检测水平边缘,另一个用于检测垂直边缘)

  1. 水平方向Sobe检测核:
-1  -2  -1
 0   0   0
 1   2   1

水平方向Sobel算子的卷积核主要关注图像中水平方向的变化。它通过将权重分配给中间列和左右列的像素,来检测图像中的水平边缘。

  1. 垂直方向Sobel检测核:
-1   0   1
-2   0   2
-1   0   1

垂直方向Sobel算子的卷积核主要关注图像中垂直方向的变化。它通过将权重分配给中间行和上下行的像素,来检测图像中的垂直边缘。

  1. 梯度计算:

假设要计算图像中某个像素位置的值,例如该位置为图像中的中心点(i,j)位置。卷积操作的计算步骤如下:

  1. 将Sobel算子的中心(1,1)位置与图像中以该点为中心的3x3区域进行对应元素相乘,并将结果相加。

  2. (1)对于水平边缘检测核:

    (-1 * I(i-1, j-1)) + (-2 * I(i, j-1)) + (-1 * I(i+1, j-1)) +
    (0 * I(i-1, j))   + (0 * I(i, j))    + (0 * I(i+1, j))    +
    (1 * I(i-1, j+1)) + (2 * I(i, j+1))  + (1 * I(i+1, j+1))
    

    其中,I(i, j) 表示图像中像素位置为 (i, j) 的值。

简化:
P5x = (P3-P1) + 2·(P6-P4) + (P9-P7)在这里插入图片描述

  1. (2) 垂直边缘检测核的计算公式:
(-1 * I(i-1, j-1)) + (0 * I(i, j-1)) + (1 * I(i+1, j-1)) +
(-2 * I(i-1, j))   + (0 * I(i, j))   + (2 * I(i+1, j))   +
(-1 * I(i-1, j+1)) + (0 * I(i, j+1)) + (1 * I(i+1, j+1))

简化:
P5y = (P7-P1) + 2·(P8-P2) + (P9-P3)在这里插入图片描述

Sobel算子的应用可以帮助检测图像中的边缘,因为在边缘处,图像的强度发生明显的变化,导致梯度的幅值较大。Sobel算子在实际图像处理中广泛应用,尤其是在计算机视觉和图像分析领域,为后续的边缘检测和图像特征提取提供了基础。

2.1.2 Sobel 算子及函数使用

dst = cv2.Sobel(src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]])

其中参数的含义如下:

  • src: 输入图像,通常是灰度图像。
  • ddepth: 输出图像的深度(数据类型)。通常使用 cv2.CV_64F-1(让处理结果与原始图像保持一致)。

当 ddepth 设置为 -1 时,如果 Sobel 滤波器的计算导致了负数,这些负数将被截断为 0。这可能导致信息的丢失,因为负数通常在图像处理中是有意义的。

为了避免信息丢失,推荐使用更高的数据类型来进行计算,例如 cv2.CV_64F,即64位浮点型。这样,计算结果将以浮点数的形式保存,包括负数。然后,可以通过取绝对值或其他操作将其映射为需要的数据类型,例如 cv2.CV_8U,即8位无符号整数。

  • dxdy: 分别表示在 x 和 y 方向上的导数的阶数,通常是 0 或 1。
  • ksize: 可选参数,表示 Sobel 滤波器的大小,通常是 1、3、5 或 7。默认值是 3。
    当该值为-1 时,则会使用 Scharr 算子进行运算。
  • scale: 可选参数,用于缩放导数的比例因子。默认值是 1,是没有缩放的。
  • delta: 可选参数,表示可选的增加到输出图像的值,用于调整图像的亮度。默认值是 0。
  • borderType: 可选参数,表示图像边界的处理方式。默认值是 cv2.BORDER_DEFAULT

这个函数的作用是应用 Sobel 滤波器,计算图像中每个像素点的梯度。dxdy 参数决定了计算的梯度方向,常见的取值为 0、1,表示水平和垂直方向的梯度。ksize 参数用于指定 Sobel 滤波器的大小。

绝对值的原因:为了让偏导数正确地显示出来,需要将值为负数的近似偏导数转换为正数。即,要将偏导数取绝对值,以保证偏导数总能正确地显示出来。
`
如果不取绝对值,梯度的正负将分别表示变化的方向。但在实际应用中,我们更关心图像中是否存在边缘以及边缘的强度,而不太在意边缘的具体方向。因此,为了简化计算并准确表示边缘的变化强度,常常使用绝对值来忽略方向信息。
在这里插入图片描述

设想在二维图像中有两个线条 A 和 B,A 线条是一条黑到白的线,B 线条是一条白到黑的线。
若针对 A 线条所在列,右侧像素值减去左侧像素值所得近似偏导数的值为-1。  针对 B 线条所在列,右侧像素值减去左侧像素值所得近似偏导数的值为 1。

针对 A 线条所在行,下方像素值减去上方像素值所得近似偏导数为-1。  针对 B 线条所在行,下方像素值减去上方像素值所得近似偏导数为 1。

示例:

import cv2
import numpy as np

# 读取图像
image = cv2.imread('input_image.jpg', cv2.IMREAD_GRAYSCALE)

# 应用 Sobel 滤波器计算水平方向的梯度
sobelx = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)

# 应用 Sobel 滤波器计算垂直方向的梯度
sobely = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)

这将分别计算图像中每个像素点的水平和垂直方向的梯度。你可以根据需要进一步处理这些梯度图像,例如计算梯度幅值和方向,用于边缘检测或其他图像处理任务。

(1)对参数取绝对值

cv2.convertScaleAbs()是 OpenCV 中用于线性缩放和截断的函数。它的目的是将输入数组进行线性缩放并进行截断,最后将结果转换为无符号8位整数(cv2.CV_8U)类型。

该函数的基本语法如下:

#该函数的作用是将原始图像 src 转换为 256 色位图
import cv2
dst = cv2.convertScaleAbs(src [, alpha[, beta]])

其中:

src: 输入数组,即待处理的图像或数据。
alpha: 缩放因子,用于乘以输入数组的每个元素。默认值为 1。
beta: 偏移量,用于在缩放后对每个元素进行加法运算。默认值为 0。

import cv2
import numpy as np

# 读取图像
image = cv2.imread('input_image.jpg', cv2.IMREAD_GRAYSCALE)

# 缩放和平移图像,然后取绝对值
alpha = 1.5
beta = 20
result = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)

# 显示原图和处理后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
(2)控制dx,dy方向的求导阶数

在这里插入图片描述

需要注意的是,dx和dy的值不能同时为0,因为至少要在一个方向上进行梯度计算,否则Sobel算子无法发挥作用。

1. 计算 x 方向边缘(梯度):
  • dx=1, dy=0,表示只计算图像中每个像素点在 x 方向上的梯度。这有助于检测图像中水平方向的边缘。

注意里面的ddepth的不同和求导的数字1和2造成的差别:
*
ddepth=-1加粗样式
ddepth=cv2.CV_64F
在这里插入图片描述

2. 计算 y 方向边缘(梯度):
  • dx=0, dy=1,表示只计算图像中每个像素点在 y 方向上的梯度。这有助于检测图像中垂直方向的边缘。
    在这里插入图片描述
3. 参数 dx 和参数 dy 的值均为 1
  • dx=1, dy=1,表示同时计算图像中每个像素点在 x 和 y 方向上的梯度。这将产生一个包含综合梯度信息的结果,用于检测边缘的强度和方向。

出现零星的微小白点,每个点的大小为一个像素。
在这里插入图片描述

4. 计算 x 方向和 y 方向的边缘叠加:
  • 通过组合不同的参数,如 dx=1, dy=0dx=0, dy=1,可以实现计算 x 方向和 y 方向的边缘的叠加,得到更全面的边缘信息。
dx= cv2.Sobel( src , ddepth , 1 , 0 )
dy= cv2.Sobel( src , ddepth , 0 , 1 )
dst=cv2.addWeighted( src1 , alpha , src2 , beta , gamma )

在这里插入图片描述

2.1.3通过实际例子表示

(1)简单图像

为了保留更多的有效信息,利用“cv2.CV_64F”,参数 dx
和 dy 的值设置为“dx=1, dy=0”后执行该函数,再对该函数的结果计算绝对值并进行对比

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

img = cv.imread('img8/testSobelimg.jpg', cv.IMREAD_GRAYSCALE)
sobelx = cv.Sobel(img, cv.CV_64F, 1, 0)
testsoblex = cv.Sobel(img, -1, 1, 0)
print(sobelx)
sobelxbeside = cv.convertScaleAbs(sobelx)

#%%

sobely = cv.Sobel(img, cv.CV_64F, 0, 1)
testsobley = cv.Sobel(img, -1, 0, 1)
print(sobely)
sobelybeside = cv.convertScaleAbs(sobely)

#%%

sobelxy = cv.Sobel(img, -1, 1, 1)
print(sobelxy)
Sobelxyy = cv.Sobel(img, -1, 1, 1)
plt.imshow(sobelxy, cmap='gray')

#%%

sobelxy = cv.addWeighted(sobelx, 1, sobely, 1, 0)
print(sobelxy)
plt.imshow(sobelxy, cmap='gray')
Sobelxybeside = cv.convertScaleAbs(sobelxy)

#%%

Sobelx = cv.convertScaleAbs(sobelxy)
print(Sobelx)
plt.imshow(Sobelx, cmap='gray')

#%% md



#%%

plt.figure(figsize=(12, 8))

plt.subplot(3, 3, 1), plt.imshow(img, cmap='gray'), plt.title('原始图像')

plt.subplot(3, 3, 2), plt.imshow(sobelx, cmap='gray'), plt.title('sobelx')
plt.subplot(3, 3, 3), plt.imshow(sobely, cmap='gray'), plt.title('sobely')

plt.subplot(3, 3, 4), plt.imshow(Sobelxyy, cmap='gray')

plt.subplot(3, 3, 5), plt.imshow(sobelxbeside, cmap='gray'), plt.title('sobelxbeside')
plt.subplot(3, 3, 6), plt.imshow(sobelybeside, cmap='gray'), plt.title('sobelybeside')

plt.subplot(3, 3, 7), plt.imshow(Sobelxybeside, cmap='gray')
plt.subplot(3, 3, 8), plt.imshow(testsoblex, cmap='gray'), plt.title('testsoblex')
plt.subplot(3, 3, 9), plt.imshow(testsobley, cmap='gray'), plt.title('testsobley')

plt.show()

在这里插入图片描述

(2)复杂的,实际的相片

在这里插入图片描述
我们可以看到ddepth是-1和cv_64F的区别,在人物和这个细微
在这里插入图片描述

2.1.4 近似值

在这里插入图片描述

在这里插入图片描述

2.2 Scharr 算子

为了弥补Sobel不太精准的情况,我们可以用 Scharr 算子并看作对 Sobel 算子的改进:
在这里插入图片描述

dst = cv2.Scharr( src, ddepth, dx, dy[, scale[, delta[, borderType]]] )
# 最好ddepth-cv2.cv_64F
dst= cv2.convertScaleAbs(dst)

2.2.1 等价的函数。

dst=cv2.Scharr(src, ddepth, dx, dy)

和 cv2.Sobel()中,如果 ksize=-1,则会使用 Scharr 滤波器。

dst=cv2.Sobel(src, ddepth, dx, dy, -1)

在这里插入图片描述
在这里插入图片描述

2.2.2需要满足的条件:dx >= 0 && dy >= 0 && dx+dy = 1

参数 ddepth 的值应该设置为“cv2.CV_64F”,并对函数
cv2.Scharr()的计算结果取绝对值,才能保证得到正确的处理结果。

2.2.3 Sobel 算子和 Scharr 算子的比较

在这里插入图片描述
Sobel算子和Scharr算子都是用于图像边缘检测的滤波器,它们在一些方面有相似之处,但也存在一些差异。以下是Sobel算子和Scharr算子的比较:

  1. 灵敏度:

    • Sobel算子:
      Sobel算子对边缘的响应相对较强,但相对来说,对噪声的敏感性也较高。

    • Scharr算子:
      Scharr算子在对边缘的响应上更加平滑,对噪声的敏感性相对较低。

  2. 计算复杂度:

    • Sobel算子:
      Sobel算子的计算较为简单,适用于一些对计算资源要求较低的场合。

    • Scharr算子:
      Scharr算子的计算复杂度相对较高,但在一些对精确性要求较高的场景中可能更为合适。

  3. 性能:

    • Sobel算子:
      Sobel算子广泛应用于一般的边缘检测任务,性能较好。

    • Scharr算子:
      Scharr算子在一些特定的图像分析任务中可能表现得更好,尤其是对于强边缘的检测。

在选择使用Sobel算子或Scharr算子时,通常会根据具体的应用场景和任务需求来权衡它们之间的差异。

2.3 拉普拉斯 Laplacian 算子

在这里插入图片描述

它是通过对图像进行二阶微分操作来实现的,能够突出图像中像素值变化较大的区域。拉普拉斯算子通常应用于灰度图像。,需要计算两个方向的梯度值。

前两个算子,都是m连通型的(即:对角),现在介绍一个4连通的。
计算像素点 P5 的近似导数值,
P5lap = (P2 + P4 + P6 + P8) - 4·P5
在这里插入图片描述
计算结果的值可能为正数,也可能为负数。所以,需要对计算
结果取绝对值,以保证后续运算和显示都是正确的。
在这里插入图片描述

2.3.1 函数

laplacian = cv2.Laplacian(src, ddepth[, ksize[, scale[, delta[, borderType]]]])


laplacian = cv2.Laplacian(o,cv2.CV_64F)laplacian = cv2.convertScaleAbs(laplacian) # 回uint8
  • src:输入图像,通常是灰度图像。
  • ddepth:输出图像的深度(数据类型),通常使用cv2.CV_64F
  • ksize:拉普拉斯算子的卷积核大小,通常为1。
  • scale:拉普拉斯算子的缩放因子,用于调整结果的幅值。
  • delta:可选的增加到输出图像的值,用于调整图像的亮度。
  • borderType:图像边界处理类型,默认为cv2.BORDER_DEFAULT

不用像Sobel算子一样,需要算x和y方向的再加起来。
在这里插入图片描述
#
在这里插入图片描述

三、Canny 边缘检测

3.1 Canny 原理

【去噪–计算梯度的幅度与方向–非极大值抑制–确定边缘】
Canny 边缘检测是一种经典的图像处理技术,通常包括以下几个步骤:

  1. 高斯滤波(Gaussian Blur):

    • 首先,图像会经过高斯滤波以平滑噪声和细节。这有助于减少后续步骤中的误差和不必要的细节。

    让临近的像素具有更高的重要度。对周围像素计算加权平均值,较近的像素具有较大的权重值。

  2. 灰度转换(Grayscale Conversion):

    • 将图像转换为灰度,将彩色图像转换为单通道灰度图像。这样可以简化处理,并减少计算复杂性。
  3. 计算梯度(Gradient Calculation):

    • 使用卷积操作,如Sobel算子,计算图像的梯度。梯度的方向和强度有助于找到图像中的边缘。
      在这里插入图片描述

梯度:在这里插入图片描述

方向:
在这里插入图片描述

  1. 非极大值抑制(Non-maximum Suppression):
    • 对梯度图进行非极大值抑制,以保留局部梯度最大的点,从而使边缘更细化。

在这里插入图片描述
在这里插入图片描述

  1. 滞后阈值处理(Hysteresis Thresholding):
    • 使用两个阈值进行滞后阈值处理。通常,有一个较低的阈值(弱边缘)和一个较高的阈值(强边缘)。根据梯度强度,将像素标记为强边缘、弱边缘或非边缘。强边缘会被保留,而与强边缘连接的弱边缘也会被保留。
  1. 设定阈值:

    • 选择两个阈值,一个是高阈值(maxVal),另一个是低阈值(minVal)。这两个阈值的选择通常依赖于具体的应用和图像特性。
  2. 判断边缘属性:

    • 对图像中的每个像素进行检查,根据其梯度值与设定的阈值之间的关系,判断其边缘的属性:
      • 如果梯度值大于或等于高阈值(maxVal),则将该像素标记为强边缘。
      • 如果梯度值介于高阈值和低阈值之间(minVal到maxVal之间),则将该像素标记为虚边缘(需要保留)。
      • 如果梯度值小于或等于低阈值(minVal),则抑制该像素,认为它不是边缘。
  3. 处理虚边缘:

    • 对于被标记为虚边缘的像素,进一步判断其是否与强边缘相连。如果与强边缘相连,则将其标记为强边缘,否则抑制它。 这个步骤的目标是最终确定哪些边缘是真实的、强的,哪些是需要保留的虚边缘,以及哪些应该被抑制。这样的处理可以有效减少噪声和虚假边缘,提高Canny边缘检测的准确性。

3.2 Canny 函数及使用

edges = cv.Canny( image, threshold1, threshold2[, apertureSize[, L2gradient]])

 edges 为计算得到的边缘图像。
 image 为 8 位输入图像。
如果为了细节,就设置的小一些
 threshold1 表示处理过程中的第一个阈值。
 threshold2 表示处理过程中的第二个阈值。
在这里插入图片描述

import cv2
import matplotlib.pyplot as plt

# 读取图像
image_path = './'
original_image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# 高斯滤波,去除噪声
blurred_image = cv2.GaussianBlur(original_image, (5, 5), 0)

# 使用Canny函数进行边缘检测
edges = cv2.Canny(blurred_image, 50, 150)  # 50是低阈值,150是高阈值

# 显示原始图像、模糊图像和边缘图像
plt.figure(figsize=(10, 6))

plt.subplot(131)
plt.imshow(original_image, cmap='gray')
plt.title('Original Image')

plt.subplot(132)
plt.imshow(blurred_image, cmap='gray')
plt.title('Blurred Image')

plt.subplot(133)
plt.imshow(edges, cmap='gray')
plt.title('Canny Edges')

plt.show()

猜你喜欢

转载自blog.csdn.net/m0_74154295/article/details/135120323