CV06_Canny边缘检测算法和python实现

1.1简介

Canny边缘检测算法是计算机视觉和图像处理领域中一种广泛应用的边缘检测技术,由约翰·F·坎尼(John F. Canny)于1986年提出。它是基于多级处理的边缘检测方法,旨在实现以下三个优化目标:

  1. 好的检测:尽可能多地检测出真正的边缘,同时尽量减少假阳性(误报)。
  2. 好的定位:检测到的边缘应该尽可能接近真实边缘的实际位置。
  3. 最小响应:对于单个边缘,只希望有一个响应,避免重复检测同一边缘。

Canny算法的步骤可以概括为以下几个阶段:

1. 高斯滤波(降噪)

算法的第一步是对原始图像进行高斯滤波,以去除图像中的噪声。高斯滤波是一种平滑处理,通过与高斯核进行卷积来实现,可以有效减少图像中的随机波动,而不模糊边缘太多。

2. 计算梯度幅度和方向

接着,算法会计算图像中每个像素点的梯度幅度和方向。梯度代表了像素值变化的强度,是边缘检测的关键。这通常通过应用Sobel算子或类似的微分算子在水平和垂直方向上进行,然后组合这些结果来得到梯度的大小和方向。梯度方向用于后续的非极大值抑制步骤。

3. 非极大值抑制(Edge thinning)

在这一步,算法沿着每个像素的梯度方向检查周围的像素,仅保留局部梯度最大的像素,即真正的边缘候选点。这样做是为了细化边缘,确保每个边缘只有单一的一行像素宽度,同时进一步减少噪声。

4. 双阈值检测和边缘连接

Canny算法采用了滞后阈值策略来区分强边缘和弱边缘。首先设定两个阈值(高阈值和低阈值),梯度幅度大于高阈值的像素被标记为强边缘,小于低阈值的像素被视为背景。介于两者之间的像素,如果与已标记的强边缘像素相连,则被视为弱边缘并最终加入到边缘集合中。这样可以有效连接断裂的边缘,同时排除孤立的噪声点。

5. 边缘跟踪与抑制

最后,通过边缘跟踪和抑制进一步优化边缘,确保边缘连续且光滑,同时减少不必要的边缘响应。

Canny算法之所以被广泛认为是最优的边缘检测算法之一,是因为它在准确性、效率和鲁棒性之间取得了很好的平衡。尽管如此,它也并非没有缺点,例如计算成本相对较高,且对阈值的选择敏感。但通过适当的参数调整和优化,Canny算法在许多应用场景中仍能提供高质量的边缘检测效果。

1.2 差分和梯度的关系

离散值的梯度计算通常涉及到差分,特别是在数字图像处理、数值分析以及某些类型的机器学习算法中。梯度本质上是一个向量,表示函数在某一点上的方向导数,即函数增长最快的方向。在连续空间中,梯度是偏导数的集合。但在离散空间(如像素网格上的图像)中,我们不能直接计算导数,而是通过有限差分近似来估计梯度。

一维情况

对于一维离散函数 𝑓(𝑥),在点 𝑥𝑖处的梯度(即导数的近似)可以通过向前差分或向后差分来估算:

这里,ℎ 是两点之间的间隔,通常代表采样间隔。

二维情况

在二维图像处理中,一个像素点 (𝑥𝑖,𝑦𝑗) 处的梯度是一个二维向量 (∂𝑓∂𝑥,∂𝑓∂𝑦),分别表示函数在 𝑥 和 𝑦 方向上变化的速率。同样使用差分近似:

最终,梯度的模(即梯度的大小)和方向常被用于边缘检测、图像增强、特征提取等应用中。在某些情况下,为了得到连续且更平滑的梯度估计,可以使用更复杂的方法,如高斯差分或其他平滑先验的梯度计算方法。

为什么离散函数的梯度用差分来表示

离散函数的梯度计算之所以使用差分来表示,是因为在离散空间中(比如数字图像中的像素点),我们无法直接应用连续函数的微分概念。在连续空间中,导数定义为函数值的变化率,即无限小的区间内的变化量比上这个区间的长度趋向于零的极限。然而,在离散空间里,函数值只在离散的点上有定义,没有“无限小”的概念,因此无法直接求极限。

差分则是离散空间中导数概念的自然近似。它通过计算相邻两点间函数值之差来量化函数值的变化率。这与导数的思想相似,但适用于离散数据点。例如,在一维离散函数中,两点之间的差分可以看作是在这两个点之间函数斜率的近似。在二维图像处理中,通过计算像素间灰度值的差异,我们可以近似得到图像中亮度变化的速率和方向,从而识别边缘、纹理等特征。

因此,差分不仅提供了计算离散梯度的有效方法,而且在数值分析和图像处理等领域的实际应用中非常实用,能够以一种直观且计算上可行的方式处理和分析离散数据的局部变化特性。

1.3 三种邻域像素距离定义

Canny算法中使用欧氏距离和城市距离都可以,欧氏距离含根号计算比较慢但是比较精准,城市距离都是整数计算比较快。

1.4 Sobel(索贝尔)算子

索贝尔算子(Sobel Operator)是一种广泛应用于图像处理领域的边缘检测技术,特别适用于需要确定图像中物体边界的位置和方向的情况。它是基于离散微分的一种方法,通过对图像进行局部微分操作,来估计图像中每个像素点的梯度强度及其方向,进而发现边缘。

原理与特点

  1. 离散差分算子:索贝尔算子是一种一阶离散差分算子,能够提供图像亮度函数梯度的近似值。它通过计算像素点在水平和垂直方向上的梯度分量来工作,这两个分量分别是图像在x轴和y轴方向上的变化率。

  2. 权重分配:与简单的梯度估计方法相比,索贝尔算子在计算像素差分时考虑了邻域像素的加权平均,即距离当前像素越近的邻像素被赋予的权重越大。这种权重分配机制使得索贝尔算子对噪声具有一定的抑制作用,因为它不仅仅依赖于直接相邻的像素差异,而是综合考虑了一个小邻域内的变化。

  3. 模板结构:索贝尔算子通常使用两个3x3的卷积核(掩模)来实现,一个用于计算水平梯度(Sobel x方向),另一个用于计算垂直梯度(Sobel y方向)。这些模板的系数经过精心设计,既能捕捉到方向上的变化,又能保持对噪声的一定鲁棒性。

  4. 边缘检测与方向:通过计算每个像素点的梯度幅度(通常是x和y方向梯度平方和的平方根),可以得到边缘强度。同时,梯度的方向可以指示边缘的方向。

  5. 性能与局限:索贝尔算子因其易于实现、计算效率高且能提供较好的边缘定位而被广泛采用。但它也存在一些局限性,比如检测到的边缘可能会比较粗,并且在某些情况下可能产生伪边缘,特别是在图像噪声较大或者边缘不清晰的情况下。

1.3 算法思路 

降噪(滤波)

在对图像进行边缘检测之前我们需要对图像进行滤波,常用的一般是高斯滤波,这样能够过滤点一些噪声点,否则在后期边缘检测会出现一些斑点。

滤波也有缺点,它会使图像变得模糊,从而丢失掉一些细节信息,也会导致边缘线一定程度的变粗。

灰度化图像并计算梯度

首先,我们的RGB图需要转成灰度图才能进行边缘检测,灰度图每个点有一个灰度值,最亮是255,最暗是0。边缘和里外两侧灰度值变化比较陡峭,但边缘连线上的灰度值变化比较平滑,所以边缘都是一些突变点。

我们用亮度值来衡量边缘,把变化越剧烈的点变得越亮,变化越平缓的点越暗,最终就能得到边缘检测的效果。

对于一维的离散函数,我们用梯度差分来表示亮度值,即 Dx = Ax+1 - Ax。比如我们有一列离散值1,2,10,100,100,这些是灰度值。对它们求差分,得到1,8,90,0,这些是亮度值。显然亮度值为90的点是边缘。

对于二维的离散函数,就比如图像的像素值,我们可以用前向水平和垂直差分表示:

\overrightarrow{D}=\begin{bmatrix}D^{\prime}x\\D^{\prime}y\end{bmatrix}=\begin{bmatrix}a_{x+1,y}-a_{x,y}\\a_{x,y+1}-a_{x,y}\end{bmatrix}

D的模值就当作亮度值,写作\overrightarrow{D}=\sqrt{D_{x}^{2}+D_{y}^{2}},D的方向角θ,记作\theta=\arctan(\frac{D'y}{D'x})

但是我们在Canny边缘检测时,梯度用的不是前向差分而是中心差分

\overrightarrow{D}_{xy}=\begin{bmatrix}D_{'x}\\D_{'y}\end{bmatrix}=\begin{bmatrix}\frac{a_{x+1,y}-a_{x-1,y}}{2}\\\\\frac{a_{x,y+1}-a_{x,y-1}}{2}\end{bmatrix}

a1a2a3
a4a5a6
a7a8a9

但是我们可以发现,用这种方法算中心点a5的梯度,a1,a3,a7,a9是没有作用的,但实际上它们对边缘检测是有影响的。这就引入了Sobel算子。

对于a5使用索贝尔算子后,采用城市距离,的中心差分梯度为:

\left.Da_{5}=\left[\begin{matrix}D_{5}^{\prime}x\\D_{5}^{\prime}y\end{matrix}\right.\right]=\left[\begin{matrix}\frac{a_{6}-a_{4}}{2\times1}+\frac{a_{9}-a_{1}}{2\times2}&-\frac{a_{7}-a_{3}}{2\times2}\\\frac{a_{8}-a_{2}}{2\times1}&+\frac{a_{9}-a_{1}}{2\times2}&+\frac{a_{7}-a_{3}}{2\times2}\end{matrix}\right],计算求模再向下取整。

公式如下:

这里第一行最后一个是减法的原因是根据sobel算子和XY轴正方向,a7到a3的方向在X轴的分量是负的。

得到θ角(范围-180°,180°)以后,我们规定X轴正方向为0°。假如θ角是45度,那么说明从a5到a9这个方向亮度值变化最剧烈。

计算图像中每一个像素点的亮度值(梯度),比较像素点与梯度方向相邻点的梯度值,最大的那个即为边缘。

非极大值抑制

如果只求梯度会导致一个问题,就是导致求出的图像边缘比较模糊而且线条比较暗且线条比较粗。

我们放大原图可以看到,边缘有几行像素只有最中间是最黑的,我们假设灰度值为0,旁边的像素灰度值可能是10,15,20,也就是说,边缘到非边缘其实是一个渐进的变化,不是突然从0到200的,这就导致了边缘的线条比较粗。

如何解决这个问题?就是用NMS。我们再看这个图,我们已经找到了最大梯度值的边缘,但是其他的值得到了保留,例如第三列的7,上面的5和下面的4还是保留着的,这时候我们就用NMS,将他们置为零,这样真正的边缘就出现了。

可以明显看到线条变细了。

双阈值检测

比如上面的图,如果某些像素点灰度值大于180,那么标记为强边缘,直接置为255,相对的,比如某些像素点的灰度值小于70,标记为背景,那么直接置为0.

孤立弱边缘抑制

什么是弱边缘,就是灰度值介于背景和强边缘之间的那些像素点的集合。弱边缘分为有用的和无用的,有用的大多与强边缘相近,无用的大多是孤立的。所以,我们要抑制这种孤立的弱边缘。

那么怎么才算孤立呢?假如一个弱边缘像素点,它周围一圈八个点不含强边缘的像素点,那么这个像素点就是孤立的,我们就置为0.

思考

在计算sobel算子下的离散函数梯度时,图像最边缘的像素点缺少相邻像素,该怎么办?

在使用Sobel算子计算图像边缘的离散函数梯度时,确实会遇到边界问题,因为图像的边缘像素缺少完整的邻域来应用3x3的卷积核。处理这种情况有几种常见方法:

  1. 边界扩展(Padding)

    • 镜像扩展(Mirror Padding):缺失的像素值由其对称位置的像素值镜像复制而来。
    • 常数扩展(Constant Padding):缺失的像素值填充为一个常数值,如0或图像边缘的像素值。
    • 重复最后一个像素值(Replicate Padding):边界像素值直接复制作为扩展。
    • 反射扩展(Reflect Padding):类似于镜像,但更像是物理反射,比如最后一个像素值的“前面”像素就是它自己。
  2. 忽略边界像素

    • 直接不计算图像边缘像素的梯度,这样会导致最终处理后的图像比原图小2个像素宽和高。
  3. 循环移位(Cyclic Padding)

    • 将图像视为周期性的,即图像的右侧与左侧相连,上边与下边相连,从而形成一个循环的边界条件。
  4. 使用专门针对边缘的算法变体

    • 有些算法对边界进行了特殊处理,设计了特殊的边界滤波器或者调整了边界处的卷积操作,以减少边缘效应。

在实际应用中,选择哪种方法取决于具体需求和对边缘效应的容忍度。镜像扩展是常用且效果较好的方法之一,因为它能够保持图像边缘的连续性,减少因边界处理引入的人工痕迹。在一些图像处理库中,如OpenCV,提供了对边界处理的选择项,允许用户根据具体情况选择合适的边界处理方式。

Python实现

1,rgb转灰度图

2,滤波(要注意滤波强度,滤波会使图像变模糊,图像太模糊会失去细节信息。 但是滤波可以去除噪声点,也可以用边缘强化滤波强化边缘)

3,求梯度(sobel算子)

4,非极大值抑制(使边缘更清晰)

5,双阈值(消去噪点,强化边缘)

6,抑制孤立弱边缘

from PIL import Image
from matplotlib import pyplot as plt
import math
from PIL import ImageFilter

im = Image.open("pic.jpg")
im_gray = im.convert('L')   #rgb转灰度图
# im_gray.show()  #显示灰度图

# im_gray = im_gray.filter(ImageFilter.SMOOTH)

#Image转为list类型
im_array = im_gray.load()
im_list = [[0 for i in range(im.size[0])] for j in range(im.size[1])]
for i in range(im.size[1]):
    for j in range(im.size[0]):
        im_list[i][j] = im_array[j, i]
plt.figure(1)
plt.imshow(im_list, cmap='gray')
# plt.show()


#求梯度 (梯度模值和方向)
#方向:  0:0度(x)    1: 45度   2: 90度(y) 3: 135度
grad = [[[0, 0] for i in range(im.size[0])] for j in range(im.size[1])]  #170 * 200 * 2 每个像素点的梯度大小和方向
for j in range(1, im.size[1] - 1):
    for i in range(1, im.size[0] - 1):
        #x方向梯度
        grad_x = im_list[j + 1][i + 1] + im_list[j - 1][i + 1] + 2 * im_list[j][i + 1] - \
                 im_list[j - 1][i - 1] - im_list[j + 1][i - 1] - 2 * im_list[j][i - 1]
        #y方向梯度
        grad_y = im_list[j + 1][i - 1] + im_list[j + 1][i + 1] + 2 * im_list[j + 1][i] - \
                 im_list[j - 1][i - 1] - im_list[j - 1][i + 1] - 2 * im_list[j - 1][i]
        grad_x = math.floor(grad_x / 4)
        grad_y = math.floor(grad_y / 4)
        #合梯度
        grad[j][i][0] = math.floor(math.sqrt(grad_x * grad_x + grad_y * grad_y))
        if(grad[j][i][0] > 255):
            grad[j][i][0] = 255
        # if(grad[j][i][0] < 50):
        #     grad[j][i][0] = 0
        #梯度方向
        if(grad_x == 0):
            grad[j][i][1] = 2  #y方向
        else:
            theta = math.atan2(grad_y, grad_x)
            if(math.fabs(theta) < math.pi / 8):
                grad[j][i][1] = 0 #x方向
            elif(theta > 0):
                if(math.fabs(theta) <math.pi * 3 / 8):
                    grad[j][i][1] = 1  #45度方向
                else:
                    grad[j][i][1] = 2#y方向
            else:
                if (math.fabs(theta) < math.pi * 3 / 8):
                    grad[j][i][1] = 3  # 135度方向
                else:
                    grad[j][i][1] = 2  # y方向

#显示梯度图
img = [[0 for i in range(im.size[0])] for j in range(im.size[1])]
for i in range(im.size[1]):
    for j in range(im.size[0]):
        img[i][j] = grad[i][j][0]
plt.figure(2)
plt.imshow(img, cmap='gray')
# plt.show()


#非极大值抑制,使边缘更清晰和细
img2 = img
for j in range(1, im.size[1] - 1):
    for i in range(1, im.size[0] - 1):
        dir = grad[j][i][1]
        grad_now = grad[j][i][0]
        if (dir == 0):  #梯度方向为x
            if(grad_now < grad[j][i + 1][0] or grad_now < grad[j][i - 1][0]):
                # grad[j][i][0] == 0
                img2[j][i] = 0
                print('0')
        elif(dir == 1):#45度方向
            if(grad_now < grad[j + 1][i + 1][0] or grad_now < grad[j - 1][i - 1][0]):
                # grad[j][i][0] == 0
                img2[j][i] = 0
                print('1')
        elif(dir == 2): #y方向
            if(grad_now < grad[j + 1][i][0] or grad_now < grad[j - 1][i][0]):
                # grad[j][i][0] == 0
                img2[j][i] = 0
                print('2')
        else:  #145度
            if(grad_now < grad[j + 1][i - 1][0] or grad_now < grad[j - 1][i + 1][0]):
                # grad[j][i][0] == 0
                img2[j][i] = 0
                print('3')

#显示非极大值抑制后的图像
plt.figure(3)
plt.imshow(img2, cmap='gray')
# plt.show()




#双阈值检测
low = 30
high = 60

for i in range(im.size[1]):
    for j in range(im.size[0]):
        if(img2[i][j] < low):
            img2[i][j] = 0
        elif(img2[i][j] > high):
            img2[i][j] = 255

#双阈值检测后的图像
plt.figure(4)
plt.imshow(img2, cmap='gray')
# plt.show()



#抑制孤立的弱边缘
listx = [-1, 0, 1]
listy = [-1, 0, 1]
img3 = img2
for j in range(1, im.size[1] - 1):
    for i in range(1, im.size[0] - 1):
        flag = 1
        for dx in range(len(listx)):
            for dy in range(len(listy)):
                j = j + listy[dy]
                i = i + listx[dx]
                if(img2[j][i] == 255):
                    flag = 0
                    break
            if(not flag):
                break
        if(flag):
            img3[j][i] = 0

#抑制孤立的弱边缘后的图像
# print('ssssssss')
plt.figure(5)
plt.imshow(img3, cmap='gray')
plt.show() 

Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:

(1)最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;

(2)最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;

(3)检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。 

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

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

相关文章

机器学习第四十七周周报 CF-LT

文章目录 week47 CF-LT摘要Abstract1. 题目2. Abstract3. 网络结构3.1 CEEMDAN&#xff08;完全自适应噪声集合经验模态分解&#xff09;3.2 CF-LT模型结构3.3 SHAP 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程 5. 结论6.代码复现小结参考文献 week47 CF-LT 摘要 本周…

以太网连接、本地连接、宽带连接和无线WLAN连接有什么不同?

电脑上的以太网连接、本地连接、宽带连接和无线WLAN连接在功能和实现方式上存在一定的区别。以下是对这四种连接方式的详细解析&#xff1a; 一、以太网连接与本地连接 1. 定义与关系 以太网连接&#xff1a;以太网是一种广泛应用的局域网&#xff08;LAN&#xff09;技术&a…

【YOLOv8】 用YOLOv8实现数字式工业仪表智能读数(二)

上一篇圆形表盘指针式仪表的项目受到很多人的关注&#xff0c;咱们一鼓作气&#xff0c;把数字式工业仪表的智能读数也研究一下。本篇主要讲如何用YOLOV8实现数字式工业仪表的自动读数&#xff0c;并将读数结果进行输出&#xff0c;若需要完整数据集和源代码可以私信。 目录 &…

排序(二)——快速排序(QuickSort)

欢迎来到繁星的CSDN&#xff0c;本期内容包括快速排序(QuickSort)的递归版本和非递归版本以及优化。 一、快速排序的来历 快速排序又称Hoare排序&#xff0c;由霍尔 (Sir Charles Antony Richard Hoare) &#xff0c;一位英国计算机科学家发明。霍尔本人是在发现冒泡排序不够快…

MQTT协议网关解决方案及实施简述-天拓四方

MQTT协议网关是一个中间件&#xff0c;负责接收来自不同MQTT客户端的消息&#xff0c;并将这些消息转发到MQTT服务器&#xff1b;同时&#xff0c;也能接收来自MQTT服务器的消息&#xff0c;并将其转发给相应的MQTT客户端。MQTT协议网关的主要功能包括协议转换、消息过滤、安全…

ImportError: DLL load failed while importing cv2解决方案

系统是 server 2012 r2 datacenter 背景&#xff1a;在window10系统上采用PyInstaller打包python310版本程序后&#xff0c;在server 2012 r2 datacenter运行报错ImportError: DLL load failed while importing cv2&#xff0c;最后解决方案参考了一篇文章下评论修改成功。 原…

Qt http网络编程

学习目标&#xff1a;Qt HTTP网络编程 学习内容 1、Http就是超文本传输协议(Hypertext Transfer Protocol)的缩写,它定义了浏览器和网页服务器之间的通信规范。是一个简单的请求一响应协议&#xff0c;它通常运行在 TCP 之上。 作用:规定 WWW 服务器与浏览器之间信息传递规范…

软件测试常见面试题汇总(2024版)

一、常见的面试题汇总 1、你做了几年的测试、自动化测试&#xff0c;说一下 selenium 的原理是什么&#xff1f; 我做了五年的测试&#xff0c;1年的自动化测试&#xff1b; selenium 它是用 http 协议来连接 webdriver &#xff0c;客户端可以使用 Java 或者 Python 各种编…

软件测试想转职有适合的岗位吗?

软件测试被有些人看做技术含量低&#xff0c;但是软件测试实际上是万金油行业&#xff0c;如果你不是在很大的公司做的软件测试&#xff0c;相比你做的工作是很杂的&#xff0c;比如软件测试找bug&#xff0c;你的主业&#xff0c;帮着产品经理整理需求&#xff0c;帮着项目经理…

微软开源项目GraphRAG——基于知识图谱的RAG简介

前言 在大型语言模型&#xff08;LLM&#xff09;的前沿研究中&#xff0c;一个核心挑战与机遇并存的领域是扩展它们的能力&#xff0c;以解决超出其训练数据范畴的问题。这不仅要求模型在面对全新数据时仍能保持卓越表现&#xff0c;还意味着开辟了全新的数据分析可能性&…

【C++】C++ 汽车租赁管理系统(源码+论文)【500+行代码】【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

CAN总线实战项目:使用STM32和PCAN-View实现数据采集与监控系统(附完整代码)

摘要: 本文深入浅出地介绍CAN&#xff08;Controller Area Network&#xff0c;控制器局域网络&#xff09;总线协议&#xff0c;涵盖其基础概念、报文帧格式、仲裁机制、错误处理等关键知识。同时&#xff0c;文章结合STM32平台&#xff0c;从硬件设计、软件开发到实战案例&am…

【益起童行】为“来自星星的孩子”点亮希望之光

在未来的日子里&#xff0c; 我期望每一个孩子都能得到优质的干预治疗&#xff0c;让他们在未来能够过上正常、快乐的生活。 我也期望每一个家庭都能战胜困境&#xff0c;迎来美好。 作为社会的一份子&#xff0c;我愿意为这繁华人世贡献出自己微不足道但却真挚的力量&#xff…

24暑假计划

暑假计划&#xff1a; 1.从明天起开始将C语言的部分补充完整&#xff0c;这部分的预计在7月24日前完成 2.由于之前的文章内容冗余&#xff0c;接下来进行C语言数据结构的重新编写和后面内容的补充预计8月10号前完成 3.后续开始C的初级学习

新加坡很火的slots游戏代投Facebook广告新流量趋势

新加坡很火的slots游戏代投Facebook广告新流量趋势 在新加坡这片充满活力的土地上&#xff0c;Slots游戏以其独特的魅力和吸引力&#xff0c;迅速成为了许多玩家的心头好。而Facebook&#xff0c;作为全球最大的社交媒体平台之一&#xff0c;为Slots游戏的推广提供了得天独厚的…

element-plus 按需导入问题 404等问题

场景 新开一个项目&#xff0c;需要用element-plus这个ui库&#xff0c;使用按需引入。 这是我项目的一些版本号 "element-plus": "^2.7.6","vue": "^3.2.13","vue-router": "^4.0.3",过程&#xff08;看解决方法…

【MySQL】常见的MySQL日志都有什么用?

MySQL日志的内容非常重要&#xff0c;面试中经常会被问到。同时&#xff0c;掌握日志相关的知识也有利于我们理解MySQL 底层原理&#xff0c;必要时帮助我们排查解决问题。 MySQL中常见的日志类型主要有下面几类(针对的是InnoDB 存储引擎): 错误日志(error log):对 MySQL 的启…

利用Python与uiautomator2实现【手机群控】

利用Python与uiautomator2实现多设备自动化测试 引言 在移动应用测试中&#xff0c;自动化测试是一种提高测试效率和覆盖率的有效手段。本文将介绍如何使用Python语言结合uiautomator2库来实现对多个设备的并行自动化测试。 老规矩先放实现的效果 环境准备 Python环境安装u…

评价妙笔生词智能写歌词软件:助力与局限并存

在音乐创作的领域&#xff0c;科技的发展催生了各种创新工具&#xff0c;妙笔生词智能写歌词软件便是其中引人注目的一项。对于这款软件&#xff0c;我们需要以客观和全面的视角来进行评估&#xff0c;因为它既带来了显著的助力&#xff0c;同时也存在不可忽视的局限。 妙笔生…

Iridient Developer:解锁Mac RAW图像处理的极致潜力,打造专业级色彩与细节

Iridient Developer for Mac是一款专为Mac用户设计的RAW图像调整软件&#xff0c;它以其卓越的性能和丰富的功能&#xff0c;赢得了众多摄影师的青睐。以下是对这款软件的详细介绍&#xff1a; 一、强大的RAW图像处理能力 Iridient Developer专为处理RAW图像而设计&#xff0…