人工智能 — 边缘提取

目录

  • 一、边缘提取
    • 1、边缘
    • 2、边缘提取
    • 3、高频信号和低频信号
    • 4、步骤
    • 5、原理
  • 二、图像锐化和图像平滑
    • 1、图像锐化
    • 2、图像平滑
  • 三、Prewitt 算子
  • 四、Sobel 算子
  • 五、Canny 边缘检测算法
    • 1、步骤
    • 2、高斯平滑
    • 3、非极大值抑制
    • 4、用双阈值算法检测(滞后阈值)
    • 六、代码实现
    • 1、手动实现 canny 算法
    • 2、OpenCV 库实现 canny 算法
    • 3、OpenCV 库实现 canny 算法(查看不同阈值下的边缘检测效果)
    • 4、sobel、laplace、canny 对比

一、边缘提取

1、边缘

图像的边缘是指图像局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个阶跃,即从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。

边缘有正负之分,就像导数有正值也有负值一样:由暗到亮为正,由亮到暗为负。

求边缘幅度的算法:
sobel、prewitt、laplace、Canny 算子

Canny 算子效果比其它的都要好,但是实现起来有点麻烦。

2、边缘提取

边缘检测主要是图像的灰度变化的度量、检测和定位

在这里插入图片描述

3、高频信号和低频信号

图像中的低频信号和高频信号也叫做低频分量和高频分量。

高频分量:指的是图像强度(亮度/灰度)变化剧烈的地方,也就是边缘(轮廓)

低频分量:指的是图像强度(亮度/灰度)变换平缓的地方,也就是大片色块的地方。

人眼对图像中的高频信号更为敏感。

4、步骤

1、滤波(优化项):边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波方法主要有高斯滤波。

2、增强(优化项):增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。

3、检测(必须项):经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。

5、原理

关于边缘检测的基础来自于一个事实,即在边缘部分,像素值出现”跳跃“或者较大的变化。

如果在此边缘部分求取一阶导数,就会看到极值的出现。

而在一阶导数为极值的地方,二阶导数为0,基于这个原理,就可以进行边缘检测。

在这里插入图片描述

二、图像锐化和图像平滑

1、图像锐化

图像锐化是补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰

图像锐化是为了突出图像上的物的边缘、轮廓,或某些线性目标要素的特征。这种滤波方法提高了地物边缘与周围像元之间的反差,因此也被称为边缘增强。

图像锐化常使用的是拉普拉斯变换核函数:
− 1 − 1 − 1 − 1 9 − 1 − 1 − 1 − 1 \begin{array}{|c|c|c|} \hline -1 & -1 & -1 \\ \hline -1 & 9 & -1 \\ \hline -1 & -1 & -1 \\ \hline \end{array} 111191111

0 − 1 0 − 1 5 − 1 0 − 1 0 \begin{array}{|c|c|c|} \hline 0 & -1 & 0 \\ \hline -1 & 5 & -1 \\ \hline 0 & -1 & 0 \\ \hline \end{array} 010151010

2、图像平滑

图像平滑是指用于突出图像的宽大区域、低频成分、主干部分或抑制图像噪声和干扰高频成分的图像处理方法。

目的是使图像亮度平缓渐变,减小突变梯度,改善图像质量。
水平梯度卷积核 1 0 − 1 1 0 − 1 1 0 − 1 水平梯度卷积核\\ \begin{array}{|c|c|c|} \hline 1 & 0 & -1 \\ \hline 1 & 0 & -1 \\ \hline 1 & 0 & -1 \\ \hline \end{array} 水平梯度卷积核111000111
垂直梯度卷积核 1 1 1 0 0 0 − 1 − 1 − 1 垂直梯度卷积核\\ \begin{array}{|c|c|c|} \hline 1 & 1 & 1 \\ \hline 0 & 0 & 0 \\ \hline -1 & -1 & -1 \\ \hline \end{array} 垂直梯度卷积核101101101

三、Prewitt 算子

Prewitt 算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用。

原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。
S x = [ − 1 0 1 − 1 0 1 − 1 0 1 ] , S y = [ 1 1 1 0 0 0 − 1 − 1 − 1 ] S_x = \begin{bmatrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{bmatrix},S_y = \begin{bmatrix} 1 & 1 & 1 \\ 0 & 0 & 0 \\ -1 & -1 & -1 \end{bmatrix} Sx= 111000111 Sy= 101101101

四、Sobel 算子

Sobel 算子是典型的基于一阶导数的边缘检测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声的影响。

Sobel 算子对于像素的位置的影响做了加权,因此与 Prewitt 算子相比效果更好。

Sobel 算子包含两组 3x3 的矩阵,分别为横向及纵向模板,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。

实际使用中,常用如下两个模板来检测图像边缘:

S x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] , S y = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] S_x = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix},S_y = \begin{bmatrix} 1 & 2 & 1 \\ 0 & 0 & 0 \\ -1 & -2 & -1 \end{bmatrix} Sx= 121000121 Sy= 101202101

缺点是 Sobel 算子并没有将图像的主题与背景严格地区分开来,换言之就是 Sobel 算子并没有基于图像灰度进行处理,由于 Sobel 算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。

Soble边缘检测

在这里插入图片描述

五、Canny 边缘检测算法

Canny 是目前最优秀的边缘检测算法之一,其目标为找到一个最优的边缘,其最优边缘的定义为:

1、好的检测:算法能够尽可能的标出图像中的实际边缘。

2、好的定位:标识出的边缘要与实际图像中的边缘尽可能接近。

3、最小响应:图像中的边缘只能标记一次。

1、步骤

1、对图像进行灰度化。

2、对图像进行高斯滤波:

根据待滤波的像素点及其邻域点的灰度值按照一定的参数规则进行加权平均。这样可以有效滤去理想图像中叠加的高频噪声。

3、检测图像中的水平、垂直和对角边缘(如 Prewitt,Sobel 算子等)。

4、对梯度幅值进行非极大值抑制:

通俗意义上是指寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉一大部分非边缘的点。

5、用双阈值算法检测和连接边缘。

2、高斯平滑

高斯平滑水平和垂直方向呈现高斯分布,更突出了中心点在像素平滑后的权重,相比于均值滤波而言,有着更好的平滑效果。
一维: G ( x ) = 1 2 π σ e − x 2 2 σ 2 二维: G ( x , y ) = 1 2 π σ 2 e − x 2 + y 2 2 σ 2 一维:G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{x^2}{2\sigma^2}}\\ 二维:G(x, y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2 + y^2}{2\sigma^2}} 一维:G(x)=2π σ1e2σ2x2二维:G(x,y)=2πσ21e2σ2x2+y2

重要的是需要理解,高斯卷积核大小的选择将影响 Canny 检测器的性能:

尺寸越大,检测器对噪声的敏感度越低,但是边缘检测的定位误差也将略有增加。一般 5x5 是一个比较不错的选择。

在这里插入图片描述

3、非极大值抑制

非极大值抑制,简称为 NMS 算法,英文为 Non-Maximum Suppression。

其思想是搜素局部最大值,抑制非极大值。NMS 算法在不同应用中的具体实现不太一样,但思想是一样的。

使用原因

以目标检测为例:目标检测的过程中在同一目标的位置上会产生大量的候选框,这些候选框相互之间可能会有重叠,此时我们需要利用非极大值抑制找到最佳的目标边界框,消除冗余的边界框。

在这里插入图片描述

对于重叠的候选框,计算它们的重叠部分,若大于规定阈值,则删除,低于阈值则保留。

对于无重叠的候选框,都保留。

原理

通俗意义上是指寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉一大部分非边缘的点。

  • 将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
  • 如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制(灰度值置为0)。

在这里插入图片描述

4、用双阈值算法检测(滞后阈值)

完成非极大值抑制后,会得到一个二值图像,非边缘的点灰度值均为0,可能为边缘的局部灰度极大值点可设置其灰度为128(或其它)。

这样一个检测结果还是包含了很多由噪声及其它原因造成的假边缘。因此还需要进一步的处理。

双阈值检测

  • 如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素。
  • 如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素。
  • 如果边缘像素的梯度值小于低阈值,则会被抑制。

大于高阈值为强边缘,小于低阈值不是边缘。介于中间是弱边缘。

阈值的选择取决于给定输入图像的内容。

抑制孤立低阈值点

到目前为止,被划分为强边缘的像素点已经被确定为边缘,因为它们是从图像中的真实边缘中提取出来的。

然而,对于弱边缘像素,将会有一些争论,因为这些像素可以从真实边缘提取也可以是因噪声或颜色变化引起的。

为了获得准确的结果,应该抑制由后者引起的弱边缘:

  • 通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。
  • 为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。

六、代码实现

1、手动实现 canny 算法

import numpy as np  # 导入 NumPy 库,用于数组操作
import matplotlib.pyplot as plt  # 导入 Matplotlib 库,用于绘制图表
import math  # 导入 math 库,用于数学计算

if __name__ == '__main__':
    pic_path = 'img/lenna.png'
    img = plt.imread(pic_path)
    if pic_path[-4:] == '.png':  # .png图片在这里的存储格式是0到1的浮点数,所以要扩展到255再计算
        img = img * 255  # 还是浮点数类型
    img = img.mean(axis=-1)  # 取均值就是灰度化了

    # 1、高斯平滑
    # sigma = 1.52  # 高斯平滑时的高斯核参数,标准差,可调
    sigma = 0.5  # 高斯平滑时的高斯核参数,标准差,可调
    dim = int(np.round(6 * sigma + 1))  # round是四舍五入函数,根据标准差求高斯核是几乘几的,也就是维度
    if dim % 2 == 0:  # 最好是奇数,不是的话加一
        dim += 1
    Gaussian_filter = np.zeros([dim, dim])  # 存储高斯核,这是数组不是列表了
    tmp = [i - dim // 2 for i in range(dim)]  # 生成一个序列
    n1 = 1 / (2 * math.pi * sigma ** 2)  # 计算高斯核
    n2 = -1 / (2 * sigma ** 2)
    for i in range(dim):
        for j in range(dim):
            Gaussian_filter[i, j] = n1 * math.exp(n2 * (tmp[i] ** 2 + tmp[j] ** 2))
    Gaussian_filter = Gaussian_filter / Gaussian_filter.sum()
    dx, dy = img.shape
    img_new = np.zeros(img.shape)  # 存储平滑之后的图像,zeros函数得到的是浮点型数据
    tmp = dim // 2
    img_pad = np.pad(img, ((tmp, tmp), (tmp, tmp)), 'constant')  # 边缘填补
    for i in range(dx):
        for j in range(dy):
            img_new[i, j] = np.sum(img_pad[i:i + dim, j:j + dim] * Gaussian_filter)
    plt.figure(1)
    plt.imshow(img_new.astype(np.uint8), cmap='gray')  # 此时的img_new是255的浮点型数据,强制类型转换才可以,gray灰阶
    plt.axis('off')

    # 2、求梯度。以下两个是滤波求梯度用的sobel矩阵(检测图像中的水平、垂直和对角边缘)
    sobel_kernel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
    sobel_kernel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
    img_tidu_x = np.zeros(img_new.shape)  # 存储梯度图像
    img_tidu_y = np.zeros([dx, dy])
    img_tidu = np.zeros(img_new.shape)
    img_pad = np.pad(img_new, ((1, 1), (1, 1)), 'constant')  # 边缘填补,根据上面矩阵结构所以写1
    for i in range(dx):
        for j in range(dy):
            img_tidu_x[i, j] = np.sum(img_pad[i:i + 3, j:j + 3] * sobel_kernel_x)  # x方向
            img_tidu_y[i, j] = np.sum(img_pad[i:i + 3, j:j + 3] * sobel_kernel_y)  # y方向
            img_tidu[i, j] = np.sqrt(img_tidu_x[i, j] ** 2 + img_tidu_y[i, j] ** 2)
    img_tidu_x[img_tidu_x == 0] = 0.00000001
    angle = img_tidu_y / img_tidu_x
    plt.figure(2)
    plt.imshow(img_tidu.astype(np.uint8), cmap='gray')
    plt.axis('off')

    # 3、非极大值抑制
    img_yizhi = np.zeros(img_tidu.shape)
    for i in range(1, dx - 1):
        for j in range(1, dy - 1):
            flag = True  # 在8邻域内是否要抹去做个标记
            temp = img_tidu[i - 1:i + 2, j - 1:j + 2]  # 梯度幅值的8邻域矩阵
            if angle[i, j] <= -1:  # 使用线性插值法判断抑制与否
                num_1 = (temp[0, 1] - temp[0, 0]) / angle[i, j] + temp[0, 1]
                num_2 = (temp[2, 1] - temp[2, 2]) / angle[i, j] + temp[2, 1]
                if not (img_tidu[i, j] > num_1 and img_tidu[i, j] > num_2):
                    flag = False
            elif angle[i, j] >= 1:
                num_1 = (temp[0, 2] - temp[0, 1]) / angle[i, j] + temp[0, 1]
                num_2 = (temp[2, 0] - temp[2, 1]) / angle[i, j] + temp[2, 1]
                if not (img_tidu[i, j] > num_1 and img_tidu[i, j] > num_2):
                    flag = False
            elif angle[i, j] > 0:
                num_1 = (temp[0, 2] - temp[1, 2]) * angle[i, j] + temp[1, 2]
                num_2 = (temp[2, 0] - temp[1, 0]) * angle[i, j] + temp[1, 0]
                if not (img_tidu[i, j] > num_1 and img_tidu[i, j] > num_2):
                    flag = False
            elif angle[i, j] < 0:
                num_1 = (temp[1, 0] - temp[0, 0]) * angle[i, j] + temp[1, 0]
                num_2 = (temp[1, 2] - temp[2, 2]) * angle[i, j] + temp[1, 2]
                if not (img_tidu[i, j] > num_1 and img_tidu[i, j] > num_2):
                    flag = False
            if flag:
                img_yizhi[i, j] = img_tidu[i, j]
    plt.figure(3)
    plt.imshow(img_yizhi.astype(np.uint8), cmap='gray')
    plt.axis('off')

    # 4、双阈值检测,连接边缘。遍历所有一定是边的点,查看8邻域是否存在有可能是边的点,进栈
    lower_boundary = img_tidu.mean() * 0.5
    high_boundary = lower_boundary * 3  # 这里设置高阈值是低阈值的三倍
    zhan = []
    for i in range(1, img_yizhi.shape[0] - 1):  # 外圈不考虑了
        for j in range(1, img_yizhi.shape[1] - 1):
            if img_yizhi[i, j] >= high_boundary:  # 取,一定是边的点
                img_yizhi[i, j] = 255
                zhan.append([i, j])
            elif img_yizhi[i, j] <= lower_boundary:  # 舍
                img_yizhi[i, j] = 0

    while not len(zhan) == 0:
        temp_1, temp_2 = zhan.pop()  # 出栈
        a = img_yizhi[temp_1 - 1:temp_1 + 2, temp_2 - 1:temp_2 + 2]
        if (a[0, 0] < high_boundary) and (a[0, 0] > lower_boundary):
            img_yizhi[temp_1 - 1, temp_2 - 1] = 255  # 这个像素点标记为边缘
            zhan.append([temp_1 - 1, temp_2 - 1])  # 进栈
        if (a[0, 1] < high_boundary) and (a[0, 1] > lower_boundary):
            img_yizhi[temp_1 - 1, temp_2] = 255
            zhan.append([temp_1 - 1, temp_2])
        if (a[0, 2] < high_boundary) and (a[0, 2] > lower_boundary):
            img_yizhi[temp_1 - 1, temp_2 + 1] = 255
            zhan.append([temp_1 - 1, temp_2 + 1])
        if (a[1, 0] < high_boundary) and (a[1, 0] > lower_boundary):
            img_yizhi[temp_1, temp_2 - 1] = 255
            zhan.append([temp_1, temp_2 - 1])
        if (a[1, 2] < high_boundary) and (a[1, 2] > lower_boundary):
            img_yizhi[temp_1, temp_2 + 1] = 255
            zhan.append([temp_1, temp_2 + 1])
        if (a[2, 0] < high_boundary) and (a[2, 0] > lower_boundary):
            img_yizhi[temp_1 + 1, temp_2 - 1] = 255
            zhan.append([temp_1 + 1, temp_2 - 1])
        if (a[2, 1] < high_boundary) and (a[2, 1] > lower_boundary):
            img_yizhi[temp_1 + 1, temp_2] = 255
            zhan.append([temp_1 + 1, temp_2])
        if (a[2, 2] < high_boundary) and (a[2, 2] > lower_boundary):
            img_yizhi[temp_1 + 1, temp_2 + 1] = 255
            zhan.append([temp_1 + 1, temp_2 + 1])

    for i in range(img_yizhi.shape[0]):
        for j in range(img_yizhi.shape[1]):
            if img_yizhi[i, j] != 0 and img_yizhi[i, j] != 255:
                img_yizhi[i, j] = 0

    # 绘图
    plt.figure(4)
    plt.imshow(img_yizhi.astype(np.uint8), cmap='gray')
    plt.axis('off')  # 关闭坐标刻度值
    plt.show()

2、OpenCV 库实现 canny 算法

import cv2  # 导入OpenCV库

'''
cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])   
必要参数:
第一个参数是需要处理的原图像,该图像必须为单通道的灰度图;
第二个参数是滞后阈值1;
第三个参数是滞后阈值2。
'''

# 读取图像
img = cv2.imread("img/lenna.png", 1)
# 将彩色图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 对灰度图像进行Canny边缘检测,并显示结果
cv2.imshow("canny", cv2.Canny(gray, 200, 300))
# 等待用户按下任意键
cv2.waitKey()
# 关闭所有窗口
cv2.destroyAllWindows()

3、OpenCV 库实现 canny 算法(查看不同阈值下的边缘检测效果)

import cv2  # 导入OpenCV库

def CannyThreshold(lowThreshold):
    # 使用3x3的高斯滤波对灰度图进行模糊处理
    detected_edges = cv2.GaussianBlur(gray, (3, 3), 0)
    # 使用Canny边缘检测,lowThreshold为下阈值,ratio为高低阈值比例,kernel_size为Sobel算子的大小
    detected_edges = cv2.Canny(detected_edges, lowThreshold, lowThreshold * ratio, apertureSize=kernel_size)
    # 使用位运算将边缘检测结果叠加到原图上
    dst = cv2.bitwise_and(img, img, mask=detected_edges)
    # 在窗口中显示Canny边缘检测的结果
    cv2.imshow('canny demo', dst)

# 初始化变量
lowThreshold = 0
max_lowThreshold = 100
ratio = 3
kernel_size = 3

# 读取图像并将其转换为灰度图像
img = cv2.imread('img/lenna.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 创建一个窗口
cv2.namedWindow('canny demo')

'''
下面是第二个函数,cv2.createTrackbar()
共有5个参数,其实这五个参数看变量名就大概能知道是什么意思了
第一个参数,是这个trackbar对象的名字
第二个参数,是这个trackbar对象所在面板的名字
第三个参数,是这个trackbar的默认值,也是调节的对象
第四个参数,是这个trackbar上调节的范围(0~count)
第五个参数,是调节trackbar时调用的回调函数名
'''
# 创建一个trackbar,用于调节Canny边缘检测的阈值
cv2.createTrackbar('Min threshold', 'canny demo', lowThreshold, max_lowThreshold, CannyThreshold)
# 初始时调用一次CannyThreshold函数,显示初始阈值下的Canny边缘检测结果
CannyThreshold(0)
# 等待用户按键操作,按下ESC键则关闭窗口
if cv2.waitKey(0) == 27:
    cv2.destroyAllWindows()

4、sobel、laplace、canny 对比

import cv2  # 导入OpenCV库
from matplotlib import pyplot as plt  # 导入 Matplotlib 库,用于绘制图表

# 读取彩色图像
img = cv2.imread("img/lenna.png", 1)
# 将彩色图像转换为灰度图像
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

'''
Sobel算子
Sobel算子函数原型如下:
dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]]) 
前四个是必须的参数:
第一个参数是需要处理的图像;
第二个参数是图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;
dx和dy表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。
其后是可选的参数:
dst是目标图像;
ksize是Sobel算子的大小,必须为1、3、5、7。
scale是缩放导数的比例常数,默认情况下没有伸缩系数;
delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
'''

# 使用Sobel算子进行水平方向的边缘检测
img_sobel_x = cv2.Sobel(img_gray, cv2.CV_64F, 1, 0, ksize=3)
# 使用Sobel算子进行垂直方向的边缘检测
img_sobel_y = cv2.Sobel(img_gray, cv2.CV_64F, 0, 1, ksize=3)

# 使用Laplacian算子进行边缘检测
img_laplace = cv2.Laplacian(img_gray, cv2.CV_64F, ksize=3)

# 使用Canny算子进行边缘检测
img_canny = cv2.Canny(img_gray, 100, 150)

# 使用matplotlib库将图像显示在多个子图中
plt.subplot(231), plt.imshow(img_gray, "gray"), plt.title("Original")
plt.subplot(232), plt.imshow(img_sobel_x, "gray"), plt.title("Sobel_x")
plt.subplot(233), plt.imshow(img_sobel_y, "gray"), plt.title("Sobel_y")
plt.subplot(234), plt.imshow(img_laplace, "gray"), plt.title("Laplace")
plt.subplot(235), plt.imshow(img_canny, "gray"), plt.title("Canny")

# 显示图像
plt.show()

在这里插入图片描述

记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/407412.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

共基课程学习

序言 教育教师 政治基础知识 马克思主义哲学 西方哲学史 三个阶段 西方哲学的起源 圈1 圈2 圈3 第一个哲学高峰 希腊三贤 圈4 圈5 是故格拉底的学生 圈6 是柏拉图的学生 圈7、圈8 这是一个政教合一的社会 圈7 圈8 圈9 圈10 圈11 圈12 文艺复兴、启蒙运动共…

2.22日学习打卡----正则表达式

2.22日学习打卡 目录&#xff1a; 2.22日学习打卡正则表达式什么是正则表达式&#xff1f;正则表达式的作用正则表达式特点基础语法表格元字符Java 中正则表达式的使用正则表达式语法规则内容限定单个字符限定范围字符限定取反限定 长度限定长度限定符号预定义字符正则表达式的…

【Linux Kernel】虚拟文件系统初探

学无止境~ 看LKD进行的粗浅整理&#xff0c;目标是能够做到设计上面的理解~ Linux操作系统上支持多种文件系统&#xff0c;如本地文件系统EXT4、XFS、EXT3 等&#xff0c;同时还支持NFS、CIFS以及一些特殊的文件系统&#xff0c;同时在上层调用文件管理时又不感知不同文件系…

高并发Server的基石:reactor反应堆模式

业务开发同学只关心业务处理流程。但是我们开发的程序都是运行服务端server上&#xff0c;服务端server接收到IO请求后&#xff0c;是如何处理请求并最终进入业务流程的呢&#xff1f;这里不得不提到reactor反应堆模型。nginx tomcat redis nodejs dubbo等软件的网络处理模型都…

如何食用Kaggle的Course中的exercise?

前言 读完本文只需要几分钟&#xff0c;读完后你将知道&#xff1a; 如何连接kaggle的反馈系统如何检查代码正确性如何查看提示和答案 读者可以拿kaggle的 pandas入门课来练手。 关于Setup 通常最上面的会有一块代码&#xff0c;它的功能是连接kaggle的反馈系统&#xff0…

Python 在Word中创建表格并填入数据、图片

在Word中&#xff0c;表格是一个强大的工具&#xff0c;它可以帮助你更好地组织、呈现和分析信息。本文将介绍如何使用Python在Word中创建表格并填入数据、图片&#xff0c;以及设置表格样式等。 Python Word库&#xff1a; 要使用Python在Word中创建或操作表格&#xff0c;需…

HDFS源码解析---写数据流程

太长不看版 1、写入&#xff08;create&#xff09;创建DFSOutputStream&#xff0c;启动DataStreamer线程run &#xff08;主线程&#xff09; 2、setPipeline -> nextBlockOutputStream -> locateFollowingBlock&#xff08;addBlock&#xff09; 2、createBlockOut…

【前端素材】推荐优质后台管理系统Qovex平台模板(附源码)

一、需求分析 1、定义 后台管理系统是一种用于管理和监控网站、应用程序或系统的在线工具。它通常是通过网页界面进行访问和操作&#xff0c;用于管理网站内容、用户权限、数据分析等。后台管理系统是网站或应用程序的控制中心&#xff0c;管理员可以通过后台系统进行各种管理…

关于字符集(彻底搞清楚一个中文占几个字节?)

目录 一、字符集二、ASCII码(字符编码)三、ISO-8859-1(字符集)四、GBxxx(字符集)五、Unicode码(字符集)六、UTF-8(字符编码)总结 一、字符集 编码与解码 计算机中储存的信息都是用二进制数表示的而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结…

Ubuntu20.04和Windows11下配置StarCraft II环境

1.Ubuntu20.04 根据下面这篇博客就可以顺利安装&#xff1a; 强化学习实战(九) Linux下配置星际争霸Ⅱ环境https://blog.csdn.net/weixin_39059031/article/details/117247635?spm1001.2014.3001.5506 Ubuntu下显示游戏界面目前还没有解决掉。 大家可以根据以下链接看看能…

腾讯云ICP备案服务器多少钱?

腾讯云备案服务器多少钱&#xff1f;备案服务器只要62元一年&#xff0c;可以备案5个网站。备案服务器申请页面 https://curl.qcloud.com/oRMoSucP 链接打开如下图&#xff1a; 腾讯云备案服务器价格 腾讯云支持ICP备案的服务器有以下三点限制条件&#xff1a; 1、地域必须是中…

PMP认证有什么用?含金量高吗?如何备考?

PMP备考多久能参加PMP考试&#xff0c;培训机构是关键点 依我这几年的持证体验来看&#xff0c;PMP认证的用处还是比较多的&#xff0c;也有一定的含金量&#xff0c;这两个方面基本都是随便一百度就能得到结果的&#xff0c;在考PMP的人群中唯一不同的可能就是备考方面的问题…

设计一个 shell 命令行程序

目录 实现 shell 主要思路 代码&#xff08;Linux&#xff09;系统 实现 shell 主要思路 1、要知道一个 shell 进程在运行起来都会在命令行呈现什么&#xff0c;如图是Xshell 登录成功后的界面&#xff1a;所以第一步要做的就是打印命令行提示符。 Xshell 命令行提示符的组…

SQL-Labs46关order by注入姿势

君衍. 四十六关 ORDER BY数字型注入1、源码分析2、rand()盲注3、if语句盲注4、时间盲注5、报错注入6、Limit注入7、盲注脚本 四十六关 ORDER BY数字型注入 请求方式注入类型拼接方式GET报错、布尔盲注、延时盲注ORDER BY $id 我们直接可以从界面中得知传参的参数为SORT&#x…

力扣 169. 多数元素

思路&#xff1a; 因为题目说一定存在多数元素&#xff0c;就说明一定有一个数的个数多于n/2 将数组采用冒泡从小到大排序&#xff0c;最中间的那个元素一定是多数元素&#xff08;因为在大小排好序后&#xff0c;中位数也一定是众数&#xff09; 答案&#xff1a; int maj…

【鸿蒙 HarmonyOS 4.0】UIAbility、页面及组件的生命周期

一、背景 主要梳理下鸿蒙系统开发中常用的生命周期 二、UIAbility组件 UIAbility组件是一种包含UI界面的应用组件&#xff0c;主要用于和用户交互。 UIAbility组件是系统调度的基本单元&#xff0c;为应用提供绘制界面的窗口&#xff1b;一个UIAbility组件中可以通过多个页…

数据价值在线化丨TiDB 在企查查数据中台的应用及 v7.1 版本升级体验

本文介绍了企查查在数据中台建设中使用 TiDB 的经验和应用。通过从 MySQL 到 TiDB 的迁移&#xff0c;企查查构建了基于 TiDB Flink 的实时数仓框架 &#xff0c;充分利用了 TiDB 的分布式架构、MySQL 兼容性和完善的周边工具等特性&#xff0c;实现了数据的在线化处理。2023 年…

svn客户端下载、安装、使用

下载、使用 打开360软件管家&#xff0c;选怎宝库&#xff0c;搜索svn&#xff0c;点击安装 可以修改安装路径 使用 在桌面右键弹出菜单&#xff0c;点击 输入地址&#xff0c;点击ok 输入用户名、密码 &#xff0c;等待检出完成

Java智慧工地管理云平台源码 带AI识别、桌面管理+大屏指挥+手机APP

目录 智慧工地云系统技术说明 1.什么是 AI 危险源识别 2.什么是标养室监测 3.什么是塔机监测 4.什么是塔机黑匣子 5.什么是升降机监测 6.什么是升降机黑匣子 7.什么是标养箱 8.什么是吊钩可视化 9.什么是吊钩追踪控制设备 10.什么是扬尘监测 11.什么是扬尘监测设备…

基于SpringBoot的停车场管理系统

基于SpringBootVue的停车场管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台首页 停车位 个人中心 管理员界面 摘要 摘要&#xff1a;随着城市化进程的…