OpenCV学习笔记 | 边缘检测Canny算法复现 | Python

news2024/11/15 9:30:15

摘要

        OpenCV中的边缘检测是指在图像中检测出明显的边缘轮廓线,可以通过计算图像中每个像素的梯度来实现。Canny算法是一种常用的边缘检测算法,它主要通过连续的操作来寻找边缘,包括对图像去噪计算图像梯度非极大值抑制双阈值处理等步骤。


一、图片加载及添加椒盐噪声

二、中值滤波和高斯滤波去噪

三、计算每个像素点的梯度强度和方向

四、非极大值抑制算法减少非边缘

五、双阈值法确定强边缘

六 、完整代码及结果展示

初始图像

噪声图像

中值滤波去噪后的图像

梯度强度边缘检测图像

非极大值抑制算法边缘检测图像

双阈值法边缘检测图像

双阈值法离散点曲线拟合边缘检测图像


一、图片加载及添加椒盐噪声

        为方便算法实现,本文仅对灰度图像进行测试。首先导入必要的库后对图片进行加载,转化成灰度图像后重置图片大小,以便最后图像输出。

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import splprep, splev
from scipy import signal

image = cv.imread('D:\pythonProject2\canny_image.jpg')
image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
image = cv.resize(image, (928, 723))

        为对比不同滤波算法对图片去噪处理的能力,首先添加像素比例为0.03的椒盐噪声。椒盐噪声是数字图像处理中常见的一种噪声类型,它通常是由于传感器故障、传输过程中的干扰以及存储介质的损坏等因素引起的。它的特点是将图像中的某些像素点替换为黑色或白色,从而形成亮点或暗点,看起来就像粒子的分布一样,因此也称为“椒盐粒子噪声”。原始图片和添加了椒盐噪声的图片如图1和图2所示。

图1 原始图片

        添加椒盐噪声的函数如下:

def add_salt_and_pepper_noise(image, ratio): # 椒盐噪声
    noisy = np.copy(image)

    num_salt = int(ratio * np.size(image)) # 计算需要添加的椒盐像素个数:
    coords_row = np.random.randint(0, image.shape[0] - 1, size=num_salt)
    coords_col = np.random.randint(0, image.shape[0] - 1, size=num_salt)
    # 首先生成0到image.shape[0] - 1的随机整数,一共生成num_salt个随机数,即num_salt个椒盐噪声位置
    # 设置最大值为image.shape[0] - 1是因为索引从0开始,不能包含image.shape[0]
    coords = np.vstack((coords_row,coords_col))
    # 用vstack将椒盐噪声的行坐标列坐标组合成一个二维数组
    noisy[tuple(coords)] = 0 # 在原始图像上将coords的位置像素点变为黑色
    # 用tuple进行操作,是因为tuple是一种不可改变的数据类型,能保证添加椒盐噪声后原始图像不会改变

    return noisy
图2 添加了椒盐噪声的图片 

二、中值滤波和高斯滤波去噪

        中值滤波是一种常见的数字图像处理技术,用于去除数字图像中的椒盐噪声等,中值滤波不仅能去除椒盐噪声,而且能够保留图像的边缘信息

        首先设置一个3×3的核,我们将遍历整个图片的像素,将核中心的像素值替换为周围领域八个像素的中值,需要注意的是得注意核的半径,避免出现遍历到图片大小以外的位置。再一个是像素的数值都是处于0到255之间,因此在初始化时定义了np.uint8的数据类型,可以有效地降低图像数据的存储空间,节省内存,并提高计算速度。中值滤波的实现代码如下,效果如图3所示。

def median_filter(image, kernel_size): # 中值滤波
    row, col = image.shape # 图像的行高和列宽
    kernel_radius = kernel_size // 2 # 计算 kernel 的半径

    median_image = np.zeros((row, col), np.uint8) # 初始化输出图像

    # 遍历每个像素点,进行中值滤波
    for y in range(kernel_radius, row - kernel_radius): # range左闭右开
        for x in range(kernel_radius, col - kernel_radius):
            kernel = image[y - kernel_radius:y + kernel_radius + 1, x - kernel_radius:x + kernel_radius + 1]
            # 获取 kernel 区域内的像素值
            median_image[y, x] = np.median(kernel) # 计算中位数并更新输出图像,注意坐标y,x

    return median_image
图3 中值滤波去噪后的图片 

 三、计算每个像素点的梯度强度和方向

        计算图像梯度强度通常使用Sobel算子,其算法基于微积分中的梯度概念,通过计算像素点周围像素值变化的差异来确定图像的边缘。假设Gx和Gy分别表示原始图像在 x 和 y 方向上的梯度,则可以使用以下Sobel算子计算梯度强度:

梯度强度

         当G数值较大时,表示该点有较大的梯度强度,可以认为是图像的边缘。其中Gx和Gy的矩阵大小如下所示:

Gx
Gx
Gy

   

   而对于梯度方向的计算,如果梯度方向在y轴上,即夹角为90°,其他情况只需代入公式计算即可,公式如下:

梯度方向

        实现代码如下,利用Sobel算子计算出梯度强度输出的边缘检测图片如图4所示。

def Sobel(image): # 利用Sobel算子计算每个像素点的梯度和梯度方向
    # 默认窗口大小为3*3
    Gx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
    Gy = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])

    row, col = image.shape
    gradients = np.zeros([row - 2, col - 2])
    direction = np.zeros([row - 2, col - 2])

    for i in range(row - 2): # range左闭右开,从0到row-3
        for j in range(col - 2): # range左闭右开,从0到col-3
            dx = np.sum(image[i:i+3, j:j+3] * Gx) # 进行卷积运算
            dy = np.sum(image[i:i+3, j:j+3] * Gy)
            gradients[i, j] = np.sqrt(dx ** 2 + dy ** 2) # 计算每一点的梯度
            if dx == 0:
                direction[i, j] = np.pi / 2 # 若梯度在y轴上,那么方向夹角为90°,由于dx在分母上,因此需要单独讨论
            else:
                direction[i, j] = np.arctan(dy / dx) # 其余情况方向角的计算代入公式即可

    gradients = np.uint8(gradients) # 由于像素的值都在0到255之间,因此需要将数值存储在8位的整型数组中
    return gradients, direction
 图4 梯度强度输出的边缘检测图片

 四、非极大值抑制算法减少非边缘

        非极大值抑制算法(Non-Maximum Suppression,NMS)是一种常见的边缘检测后处理方法,它的目的是减少由离散化边缘检测算法产生的假阳性结果(即对图像中非边缘位置赋值为边缘),从而得到更加准确的图像边缘。

        首先先将Sobel算子所计算出来的梯度方向离散成水平方向(0°、180°)和对角线方向(45°、135°)以及垂直方向(90°、270°)三种类别。

  • 对于水平方向,我们检查当前像素点的左右两个像素点是否比当前像素点的梯度幅值小,如果是,说明当前像素点不是一条边缘上的点之一,舍弃这个点;否则,将该像素点保留下来。

  • 对于垂直方向,我们检查当前像素点的上下两个像素点是否比当前像素点的梯度幅值小,如果是,说明当前像素点不是一条边缘上的点之一,舍弃这个点;否则,将该像素点保留下来。

  • 对于对角线方向,我们检查当前像素点的两个对角线方向的像素点是否比当前像素点的梯度幅值小,如果是,说明当前像素点不是一条边缘上的点之一,舍弃这个点;否则,将该像素点保留下来。

        对于离散类别的角度范围如下表所示,为方便讨论,所有负数角都将加\pi,转化成正数角考虑。实现代码如下,使用非极大值抑制算法检测的边缘图像如图5所示。

def non_maximum_suppression(magnitude, orientation): # 非极大值抑制算法
    # magnitude:梯度强度矩阵 orientation:梯度方向矩阵
    row, col = magnitude.shape  # 获取尺寸信息
    out_edges = np.zeros_like(magnitude)  # 初始化输出矩阵

    # 每个像素和周围像素点进行比较
    for i in range(1, row - 1): # 该算法是要比较中间像素点和周围八个像素点的数值大小,因此需要去除首行和尾行
        for j in range(1, col - 1): # 去除首列和尾列
            angle = orientation[i, j] # 对每个像素点的梯度方向进行比较
            if angle < 0: # 将负值角度翻转到x轴上方讨论
                angle += np.pi
            if (angle <= np.pi/8 or angle >= 7*np.pi/8) :  # 离散成0度和180度的梯度方向
                if magnitude[i, j] > magnitude[i, j - 1] and magnitude[i, j] > magnitude[i, j + 1]: # 只检查左右两个像素点的数值
                    out_edges[i, j] = magnitude[i, j]
            elif (angle > np.pi/8 and angle <= 3*np.pi/8) or (angle >= 5*np.pi/8 and angle < 7*np.pi/8): # 离散成45度和135的梯度方向
                if magnitude[i, j] > magnitude[i - 1, j - 1] and magnitude[i, j] > magnitude[i + 1, j + 1]: # 只检测对角像素点的数值
                    out_edges[i, j] = magnitude[i, j]
            elif (angle > 3*np.pi/8 and angle < 5*np.pi/8) : # 离散成90度或270度的梯度方向
                if magnitude[i, j] > magnitude[i - 1, j] and magnitude[i, j] > magnitude[i + 1, j]: # 只检测上下两个像素点的数值
                    out_edges[i, j] = magnitude[i, j]

    return out_edges
图5 非极大值边缘检测图像

 五、双阈值法确定强边缘

        双阈值法是Canny边缘检测算法中的一种方法,用于确定哪些边缘是真正的边缘(强边缘),哪些边缘是无关紧要的噪声(弱边缘)。

        在这个算法中,我们可以通过设定低于低阈值(threshold_low)的梯度幅值的像素被认为是非边缘像素,低阈值(threshold_low)和高阈值(threshold_high)之间的像素被认为是弱边缘像素,而高于高阈值(threshold_high)的梯度幅值被认为是强边缘像素。因此该函数只需检查弱边缘像素是否与强边缘像素相连接。

        对于一般的图像,可以以该阈值设置为参考去对比调整:

  • threshold_low 取图像梯度幅值的平均值减一倍标准差
  • threshold_high 取图像梯度幅值的平均值加一倍标准差

       通常情况下,较小的最小阈值会产生更多的弱边缘,因此它可能会导致更多的总边缘数量,但同时也可能会增加边缘的错误检测率。较高的最大阈值可以消除由于噪声引起的边缘,从而可以提高边缘检测的精度,并减少误判率。但是,较高的最大阈值也可能会将实际的边缘误判为非边缘,从而导致错过一些边缘。

        threshold_low 取图像梯度幅值的平均值减一倍标准差,threshold_high 取图像梯度幅值的平均值加一倍标准差,即threshold_low取-19.59,threshold_high取62.92的效果,如图6所示。

图6 双阈值法边缘检测效果1

        threshold_low取-5,threshold_high取75的效果,如图7所示。

图7 双阈值法边缘检测效果2

         

        threshold_low取-5,threshold_high取100的效果,如图8所示。

图8 双阈值法边缘检测效果3

         

        threshold_low取-5,threshold_high取85的效果,如图9所示。

图9 双阈值法边缘检测效果4

        需要注意的是,在实现算法过程中应该对图像进行拷贝,否则将会修改本地图片代码如下。

def double_threshold_discrete(out_edges,threshold_low,threshold_high): # 双阈值法进行边缘连接
    out_pic = out_edges.copy()
    row, col = np.shape(out_pic)
    thresholded_mag = np.zeros((row, col))

    # 将满足高阈值的像素点设为强边缘像素点
    strong_i, strong_j = np.where(out_pic >= threshold_high)
    thresholded_mag[strong_i, strong_j] = 1

    # 将满足低阈值的像素点设为弱边缘像素点
    weak_i, weak_j = np.where((out_pic <= threshold_high) & (out_edges >= threshold_low))

    # 使用连通域方法连接弱边缘像素点,得到最终的边缘图像
    edge_map = np.zeros((row, col))
    for k in range(len(weak_i)):
        # 如果与确定为边缘的像素点邻接,则判定为边缘;否则为非边缘
        i = weak_i[k]
        j = weak_j[k]
        if np.any(thresholded_mag[i-1:i+2, j-1:j+2]):
            edge_map[i, j] = 1
    return edge_map.astype(np.uint8)

         最小阈值设置为负数的时候,输出的边缘是连续性较强,而设置为正数的时候,基本上都是离散的点了,此时我们可以考虑使用splev函数进行曲线拟合。其中最小阈值设置为10,最大阈值设置为85的效果如图10所示。

图10 双阈值法边缘检测效果5

         曲线拟合实现函数如下,拟合后的效果如图11所示。由于是拟合的结果,会避免不了地出现重影模糊的情况,不推荐使用。

def double_threshold_spline(out_edges, threshold_low, threshold_high, s=500):
    # 使用双阈值法进行边缘连接,得到离散的边缘点
    edge_map = double_threshold_discrete(out_edges, threshold_low, threshold_high)
    edge_points = np.argwhere(edge_map == 1)

    # 使用样条插值进行边缘曲线拟合
    tck, u = splprep(edge_points.T, s=s, per=1)

    # 确定边缘曲线上的样本点
    x, y = splev(u, tck)

    # 将曲线上的像素点在图像上标出
    out_pic = out_edges.copy()
    for i in range(len(x)):
        out_pic[int(x[i]), int(y[i])] = 255

    return out_pic
图11 双阈值法边缘检测效果6

六 、完整代码及结果展示

import cv2 as cv
import numpy as np
from scipy.interpolate import splprep, splev

def cv_show(name,image): # 图像显示
    cv.imshow('name',image)
    cv.waitKey(0)

def add_salt_and_pepper_noise(image, ratio): # 椒盐噪声
    noisy = np.copy(image)

    num_salt = int(ratio * np.size(image)) # 计算需要添加的椒盐像素个数:0.05*291600
    coords_row = np.random.randint(0, image.shape[0] - 1, size=num_salt)
    coords_col = np.random.randint(0, image.shape[0] - 1, size=num_salt)
    # 一共生成num_salt个随机数,即num_salt个椒盐噪声位置
    coords = np.vstack((coords_row,coords_col))
    # 用vstack将椒盐噪声的行坐标列坐标组合成一个二维数组
    noisy[tuple(coords)] = 0 # 在原始图像上将coords的位置像素点变为黑色
    # 用tuple进行操作,是因为tuple是一种不可改变的数据类型,能保证添加椒盐噪声后原始图像不会改变

    return noisy

def median_filter(image, kernel_size): # 中值滤波
    row, col = image.shape # 图像的行高和列宽
    kernel_radius = kernel_size // 2 # 计算 kernel 的半径

    median_image = np.zeros((row, col), np.uint8) # 初始化输出图像

    # 遍历每个像素点,进行中值滤波
    for y in range(kernel_radius, row - kernel_radius): # range左闭右开
        for x in range(kernel_radius, col - kernel_radius):
            kernel = image[y - kernel_radius:y + kernel_radius + 1, x - kernel_radius:x + kernel_radius + 1]
            # 获取 kernel 区域内的像素值
            median_image[y, x] = np.median(kernel) # 计算中位数并更新输出图像,注意坐标y,x

    return median_image

def Sobel(image): # 利用Sobel算子计算每个像素点的梯度和梯度方向
    # 默认窗口大小为3*3
    Gx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
    Gy = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])

    row, col = image.shape
    gradients = np.zeros([row - 2, col - 2])
    direction = np.zeros([row - 2, col - 2])

    for i in range(row - 2): # range左闭右开,从0到row-3
        for j in range(col - 2): # range左闭右开,从0到col-3
            dx = np.sum(image[i:i+3, j:j+3] * Gx) # 进行卷积运算
            dy = np.sum(image[i:i+3, j:j+3] * Gy)
            gradients[i, j] = np.sqrt(dx ** 2 + dy ** 2) # 计算每一点的梯度
            if dx == 0:
                direction[i, j] = np.pi / 2 # 若梯度在y轴上,那么方向夹角为90°,由于dx在分母上,因此需要单独讨论
            else:
                direction[i, j] = np.arctan(dy / dx) # 其余情况方向角的计算代入公式即可

    gradients = np.uint8(gradients) # 由于像素的值都在0到255之间,因此需要将数值存储在8位的整型数组中
    return gradients, direction

def non_maximum_suppression(magnitude, orientation): # 非极大值抑制算法
    # magnitude:梯度强度矩阵 orientation:梯度方向矩阵
    row, col = magnitude.shape  # 获取尺寸信息
    out_edges = np.zeros_like(magnitude)  # 初始化输出矩阵

    # 每个像素和周围像素点进行比较
    for i in range(1, row - 1): # 该算法是要比较中间像素点和周围八个像素点的数值大小,因此需要去除首行和尾行
        for j in range(1, col - 1): # 去除首列和尾列
            angle = orientation[i, j] # 对每个像素点的梯度方向进行比较
            if angle < 0: # 将负值角度翻转到x轴上方讨论
                angle += np.pi
            if (angle <= np.pi/8 or angle >= 7*np.pi/8) :  # 离散成0度和180度的梯度方向
                if magnitude[i, j] > magnitude[i, j - 1] and magnitude[i, j] > magnitude[i, j + 1]: # 只检查左右两个像素点的数值
                    out_edges[i, j] = magnitude[i, j]
            elif (angle > np.pi/8 and angle <= 3*np.pi/8) or (angle >= 5*np.pi/8 and angle < 7*np.pi/8): # 离散成45度和135的梯度方向
                if magnitude[i, j] > magnitude[i - 1, j - 1] and magnitude[i, j] > magnitude[i + 1, j + 1]: # 只检测对角像素点的数值
                    out_edges[i, j] = magnitude[i, j]
            elif (angle > 3*np.pi/8 and angle < 5*np.pi/8) : # 离散成90度或270度的梯度方向
                if magnitude[i, j] > magnitude[i - 1, j] and magnitude[i, j] > magnitude[i + 1, j]: # 只检测上下两个像素点的数值
                    out_edges[i, j] = magnitude[i, j]

    return out_edges

def double_threshold_discrete(out_edges,threshold_low,threshold_high): # 双阈值法进行边缘连接
    out_pic = out_edges.copy()
    row, col = np.shape(out_pic)
    thresholded_mag = np.zeros((row, col))

    # 将满足高阈值的像素点设为强边缘像素点
    strong_i, strong_j = np.where(out_pic >= threshold_high)
    thresholded_mag[strong_i, strong_j] = 1

    # 将满足低阈值的像素点设为弱边缘像素点
    weak_i, weak_j = np.where((out_pic <= threshold_high) & (out_edges >= threshold_low))

    # 使用连通域方法连接弱边缘像素点,得到最终的边缘图像
    edge_map = np.zeros((row, col))
    for k in range(len(weak_i)):
        # 如果与确定为边缘的像素点邻接,则判定为边缘;否则为非边缘
        i = weak_i[k]
        j = weak_j[k]
        if np.any(thresholded_mag[i-1:i+2, j-1:j+2]):
            edge_map[i, j] = 1
    return edge_map.astype(np.uint8)


def double_threshold_spline(out_edges, threshold_low, threshold_high, s=500):
    # 使用双阈值法进行边缘连接,得到离散的边缘点
    edge_map = double_threshold_discrete(out_edges, threshold_low, threshold_high)
    edge_points = np.argwhere(edge_map == 1)

    # 使用样条插值进行边缘曲线拟合
    tck, u = splprep(edge_points.T, s=s, per=1)

    # 确定边缘曲线上的样本点
    x, y = splev(u, tck)

    # 将曲线上的像素点在图像上标出
    out_pic = out_edges.copy()
    for i in range(len(x)):
        out_pic[int(x[i]), int(y[i])] = 255

    return out_pic

image = cv.imread('D:\pythonProject2\canny_image.jpg')
image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
image = cv.resize(image, (928, 723))

# 输入图片以及需要添加噪声点的比例
noisy_image = add_salt_and_pepper_noise(image,0.03)
# 中值滤波去噪
median_image = median_filter(noisy_image,3)
# 梯度强度和梯度方向
gradients,  direction = Sobel(median_image)
# 输出初始边缘检测的图像
# 统一化梯度幅值到 [0,255] 范围内
gradients_image = cv.normalize(gradients, dst=None, alpha=0, beta=255, norm_type=cv.NORM_MINMAX, dtype=cv.CV_8UC1)
# 非极大值抑制算法
nms = non_maximum_suppression(gradients,direction)
# 离散边缘检测图像
threshold_low = np.mean(gradients) - np.std(gradients)
threshold_high = np.mean(gradients) + np.std(gradients)
# print(threshold_low)
# print(threshold_high)
Output_Image_Discrete = double_threshold_discrete(nms,-5,85)
Output_Image_Discrete = Output_Image_Discrete * 255
# 使用样条插值拟合出边缘曲线
Output_Image_continuous = double_threshold_spline(nms,10,85)

cv.imshow('initial_image',image)
cv.imshow('noisy_image',noisy_image)
cv.imshow('median_image',median_image)
cv.imshow('gradients_image',gradients_image)
cv.imshow('nms_image',nms)
cv.imshow('Output_Image_Discrete',Output_Image_Discrete)
cv.imshow('Output_Image_continuous',Output_Image_continuous)
cv.waitKey(0)
cv.destroyAllWindows()

初始图像

噪声图像

中值滤波去噪后的图像

梯度强度边缘检测图像

非极大值抑制算法边缘检测图像

双阈值法边缘检测图像

双阈值法离散点曲线拟合边缘检测图像

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第36节:cesium 下雨效果(含源码+视频)

结果示例: 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false"><!

[ JVM ] 常用参数 优化参考

基础概念回顾 JDK、JRE、JVM的关系&#xff08;JDK>JRE>JVM&#xff09; JDK JRE 开发工具 、JRE JVM 类库&#xff0c;具体关系如下图&#xff1a; JDK&#xff08;Java Development Kit&#xff09; 用于开发 Java 应用程序的软件开发工具集合&#xff0c;包括 了 …

【接口mock工具】推荐一个好用的api接口关联,接口mock,文档管理的工具-yapi

【接口mock工具】推荐一个好用的api接口关联&#xff0c;接口mock&#xff0c;文档管理的工具-yapi 能满足的功能 &#xff1a; 1. 接口mock 2. 接口文档生成 3. 接口自动化测试 4. 接口自动化测试结果可以发送到你的企业微信上面 5. 接口管理可以按照分组&#xff0c;分项…

python数据分析之利用多种机器学习方法实现文本分类、情感预测

大家好&#xff0c;我是带我去滑雪&#xff01; 文本分类是一种机器学习和自然语言处理&#xff08;NLP&#xff09;任务&#xff0c;旨在将给定的文本数据分配到预定义的类别或标签中。其目标是为文本数据提供自动分类和标注&#xff0c;使得可以根据其内容或主题进行组织、排…

初见RNN(第七次组会)

初见RNN&#xff08;第七次组会&#xff09; 序列模型、马尔可夫假设循环神经网络 序列模型、马尔可夫假设 循环神经网络

[rocketmq] 浅谈结构

rocketmq 结构 NameServer &#xff1a;几乎是无状态节点&#xff0c;可横向扩展&#xff0c;节点之间无消息同步&#xff0c;主要负责对源数据的管理&#xff0c;包括对于Topic和路由信息的管理。 每个 Broker 在启动的时候会到 NameServer 注册&#xff0c;Producer 在发送消…

【UEFI实战】UEFI图形显示(从像素到字符)

GraphicsConsoleDxe 在【UEFI实战】UEFI图形显示&#xff08;显示驱动&#xff09;中已经介绍了如何使用显卡驱动安装的GOP来进行像素级别的显示&#xff0c;本文介绍的内容是对像素的包装&#xff0c;最终变成普通字符的输出。 模块简述 本模块将原本的GOP包装成了字符输出…

MySQL数据库——主从复制优化及读写分离

目录 主从复制优化主服务器配置优化redo log&#xff08;事务日志&#xff09;的刷盘策略从服务器配置优化 搭建MySQL读写分离 主从复制优化 主服务器配置优化 vim /etc/my.cnf expire_logs_days7 #设置二进制日志文件过期时间&#xff0c;默认值为0&#xff0c;表示logs不…

Vue2生命周期

Vue2生命周期 1、概念2、生命周期过程2.1 流程图示2.2 三阶段 3、钩子函数详解3.1 beforeCreate() 创建前3.2 created()创建后3.3 beforeMount() 挂载前3.4 mounted()挂载完成3.5 beforeUpdate() 更新前3.6 updated() 更新后3.7 beforeDestroy() 销毁前3.8 destroyed() 销毁完成…

在群晖上安装运行Airflow

本文是应网友 &#xff1a; 要求折腾的&#xff1b; 什么是 Airflow &#xff1f; Apache Airflow 是一个开源平台&#xff0c;用于开发、调度和监控面向批处理的工作流。Airflow 的可扩展 Python 框架使您能够构建与几乎任何技术连接的工作流。Web 界面有助于管理工作流程的状…

RabbitMQ灵活运用,怎么理解五种消息模型

RabbitMQ灵活运用&#xff0c;怎么理解五种消息模型 简介一、AMQP协议二、交换机类型与默认交换机1. 交换机的四种类型2. 默认交换机 三、五种模式速览1. 一对一简单模式2. work模式&#xff08;轮询&#xff09;3. 发布/订阅模式4. 路由模式&#xff08;自称direct模式&#x…

Android 应用自动开启辅助(无障碍)功能并使用辅助(无障碍)功能

一.背景 由于最近的项目需要开启无障碍功能然后实现对应的功能需求,但是由于需求是需要安装后就开启辅助功能,不要在繁琐的在设置中开启辅助功能,所以需要如何在应用中开启辅助功能。 二.前提条件 将普通应用转换成系统应用,然后将系统的framework.jar包放到应用中并且可以…

vscode配置task.json和launch.json启动调试

首先说一下参考博文&#xff1a; 文章标题“VScode 调试教程 tasks.json和launch.json的设置&#xff08;超详细&#xff09;” 地址&#xff1a;https://blog.csdn.net/qq_59084325/article/details/125662393 官方文档太官方&#xff0c;其他人的文档也看过&#xff0c;单独…

微信小程序浏览docx,pdf等文件在线预览使用wx.openDocument

wx.downloadFile({ url: fileUrl,//pdf链接success(res) {wx.openDocument({ //打开文档filePath: res.tempFilePath,fileType: "pdf",//文档类型showMenu: true,success: function (res) {wx.showToast({title: 打开文档成功,})},fail: function (res) {wx.showToas…

Stable Diffusion使用“面部修复”时报TypeError: ‘NoneType‘ object is not subscriptable错

问题 Stable Diffusion使用“面部修复”时报TypeError: ‘NoneType’ object is not subscriptable错 解决方案 下载【detection_Resnet50_Final.pth】和【parsing_parsenet.pth】到【repositories\CodeFormer\weights\facelib】目录下&#xff0c;并重新运行项目即可。 ht…

Unity 基础之 URP 项目创建\项目转URP Pipline

Unity 基础之 URP 项目创建\项目转URP Pipline 目录 Unity 基础之 URP 项目创建\项目转URP Pipline 一、简单介绍 二、创建 URP 项目 三、工程项目转 URP 一、简单介绍 Unity中的一些基础知识点&#xff0c;方便日后查阅。 Unity游戏开发中&#xff0c;这里简单介绍如何创…

Methodot低代码开发教程——玩转表格增删改查分页

目录 1、背景介绍 2、连接数据源 2.1 新增数据源 2.2 填写数据源信息 3、表格数据的展示 3.1 新增查询&#xff0c;编写查询语句 3.2 使用表格组件 3.3 同步数据源与表格列名 4、表格的数据新增 4.1 新增查询&#xff0c;编写新增语句 4.2 表格配置新增一行&#xff0…

华为云专家出品《从零到一•Python图像处理入门》电子书

《华为云云享.书库》系列电子书来啦&#xff01; 本系列电子书旨在帮助开发者成长&#xff0c;汇聚华为云内外部专家技术精华制作而成。 本书《从零到一•Python图像处理》是该系列电子书第3部。 我们在华为开发者即将到来之际&#xff0c;开放电子书免费下载。 点击下方链接…

JVM探究

JVM探究 请谈谈你对JVM的理解&#xff1f;java8虚拟机和之前的变化、更新&#xff1f;什么是OOM&#xff0c;栈溢出StackOverFlowError&#xff1f;怎么分析&#xff1f;JVM的常用调优参数有哪些&#xff1f;内存快照如何 抓取&#xff0c;怎么分析Dump文件&#xff1f;知道吗…

Unity VR开发教程 OpenXR+XR Interaction Toolkit(八)手指触控 Poke Interaction

文章目录 &#x1f4d5;教程说明&#x1f4d5;XR Poke Interactor&#x1f4d5;与 UI 进行触控交互⭐添加 Tracked Device Graphic Raycaster 和 XR UI Input Module 让 UI 可被交互 &#x1f4d5;与物体进行交互⭐XR Simple Interactable⭐XR Poke Filter 往期回顾&#xff1a…