python中使用opencv LED屏数字识别(可用做车牌识别,一样的原理)

应项目要求需要基于cpu的LED数字识别,为了满足需求,使用传统方法进行实验。识别传感器中显示的数字。因此使用opencv的函数做一些处理,实现功能需求。

在这里插入图片描述

首先读取图像,因为我没想大致得到LED屏幕的区域,因此将RGB转换为HSV空间,并分别设置H、S和V的阈值,让该区域显现出来。可以看到代码中进行了resize操作,这个操作不是必须的,具体H、S和V的数值根据具体的图像自行设置。

img = cv2.imread('pic.jpg')
#start = time.time()
new_size = (640, 400)
img = cv2.resize(img, new_size)
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = np.zeros(hsv_img.shape[:2], dtype=np.uint8)
mask[(hsv_img[:,:,0] >= 130) & (hsv_img[:,:,0] <= 255) & (hsv_img[:,:,1] >= 0) &
(hsv_img[:,:,1] <= 255) & (hsv_img[:,:,2] >= 0) & (hsv_img[:,:,2] <= 90)] = 1
result = cv2.bitwise_and(img, img, mask=mask)

原图处理后结果:

在这里插入图片描述

接下来需要检测轮廓信息,因此进行灰度化处理。

gray = cv2.cvtColor(result,cv2.COLOR_BGR2GRAY)

处理后结果:

在这里插入图片描述

使用Canny做轮廓检测,并保存所有轮廓信息,将所有轮廓信息框选出来,因为框选区域矩形,所以采用面积策略删掉小块区域面积只显示大块的面积。

#转灰度,做单通道计算比较节省时间
edges = cv2.Canny(gray,50,150,apertureSize = 3)

#所有轮廓的列表contours和分层信息
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# print("轮廓数量:", len(contours))

img_copy = img.copy() # 复制原图,避免在原图上直接画框

for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt) # 获取轮廓的边界矩形
    area = w * h
    if area > 10000:
        x, y, w, h = cv2.boundingRect(cnt)
        area = w * h
        # print("xywh", x, y, w, h)
        # print("矩形面积:", area)
        roi = img_copy[y:y + h + 26, x:x + w+2]
        # cv2.imshow("ROI", roi)
        # cv2.waitKey(0)
        # print("矩形面积:", area)

该图为框选的所有轮廓

在这里插入图片描述

做面积阈值处理后得到液晶屏幕区域的矩形框:

在这里插入图片描述

后边拿出框选的区域做处理就行了,直接从原图中拿出框选区域做灰度处理,并作边缘检测,边缘检测前做高斯处理,去除一下噪声。

gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# 高斯滤波
blur = cv2.GaussianBlur(gray, (3, 3), 0)

# 边缘检测
edges = cv2.Canny(gray, 50, 255)

得到框选区域并进行边缘检测结果:

在这里插入图片描述

在这里插入图片描述

到这里已经检测到LED屏的轮廓了,可以看出我们的拍摄得到的框是斜的,因此需要进行透视校正,而透视校正需要得到四个顶点的坐标值,这里我尝试了两种方法:一、绘制最小外接矩形;二、获取白色像素点坐标(这里自己思考的一种策略,分别按照x轴和y轴找,分别找x轴白色边缘最小值和最大值坐标,y轴白色边缘最小值和最大值坐标)

一、使用最小外接矩形做透视校正

# 获得轮廓信息
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 绘制最小外接矩形
for cnt in contours:
    rect = cv2.minAreaRect(cnt)
    box = cv2.boxPoints(rect)
    box = np.int0(box)

    cv2.drawContours(roi, [box], 0, (0, 255, 0), 1)

    # 获取矩形的四个顶点坐标
    rect_points = np.int0(cv2.boxPoints(rect))
    # print("矩形的四个顶点坐标:", rect_points)

# cv2.imshow('Contours', roi)
# cv2.waitKey(0)

# 定义原图像的四个角点坐标
src = np.float32([[-8,61],[186 , -3],[ 23, 156], [218 , 91]])

# 定义输出图像的四个角点坐标
dst = np.float32([[0, 0],[200, 0],[0, 100],[200, 100]])

# 计算变换矩阵
M = cv2.getPerspectiveTransform(src, dst)

# 应用变换矩阵
result = cv2.warpPerspective(roi, M, (200, 100))


显示图像
cv2.imshow('Contours', result)
cv2.waitKey(0)

得到最小外接矩形:

在这里插入图片描述

透视校正结果:

在这里插入图片描述

从最小外接矩形可以看出框选的区域除了LED屏幕还有一些其他区域影响,这边后边的实验中发现,这些多余地方会影响后序的实验准确性。

二、分别获取x轴和y轴白色像素点坐标

# 获取白色像素点坐标
coordinates = np.column_stack(np.where(edges == 255))

# # 打印白色像素点坐标
# print(coordinates)

# 在x轴方向上找到最小和最大的两个坐标
x_min = coordinates[np.argmin(coordinates[:,0])].copy()
x_min[0], x_min[1] = x_min[1], x_min[0]
x_max = coordinates[np.argmax(coordinates[:,0])].copy()
x_max[0], x_max[1] = x_max[1], x_max[0]

# 在y轴方向上找到最小和最大的两个坐标
y_min = coordinates[np.argmin(coordinates[:,1])].copy()
y_min[0], y_min[1] = y_min[1], y_min[0]
y_max = coordinates[np.argmax(coordinates[:,1])].copy()
y_max[0], y_max[1] = y_max[1], y_max[0]

# # # 打印最小和最大的两个坐标
# print('x_min:', x_min)
# print('x_max:', x_max)
# print('y_min:', y_min)
# print('y_max:', y_max)


# 定义原图像的四个角点坐标
src = np.float32([y_min,x_min,x_max, y_max])

# 定义输出图像的四个角点坐标
dst = np.float32([[0, 0],[200, 0],[0, 100],[200, 100]])

# 计算变换矩阵
M = cv2.getPerspectiveTransform(src, dst)

# 应用变换矩阵
result = cv2.warpPerspective(roi, M, (200, 100))
#显示图像
# cv2.imshow('result', result)
# cv2.waitKey(0)

按照思路找x轴和y轴最大最小值对于的白色像素点坐标并绘制框:

在这里插入图片描述

透视校正后:

在这里插入图片描述

将透视校正后的图像区域继续进行RGB转换HSV,并设置阈值使数字区域更好的显示出来

hsv_img = cv2.cvtColor(result, cv2.COLOR_BGR2HSV)
# cv2.imshow('hsv_img', hsv_img)
# cv2.waitKey(0)
mask = np.zeros(hsv_img.shape[:2], dtype=np.uint8)
mask[(hsv_img[:,:,0] >= 0) & (hsv_img[:,:,0] <= 255) & (hsv_img[:,:,1] >= 0) &
(hsv_img[:,:,1] <= 255) & (hsv_img[:,:,2] >= 0) & (hsv_img[:,:,2] <= 100)] = 1
result1 = cv2.bitwise_and(hsv_img, hsv_img, mask=mask)

# cv2.imshow("Contours", result1)
# cv2.waitKey(0)

RGB转换HSV:

在这里插入图片描述

H、S和V设置阈值使数字区域显示:

在这里插入图片描述

将得到的图像进行二值化处理

# 转换为灰度图像
gray = cv2.cvtColor(result1, cv2.COLOR_BGR2GRAY)
# cv2.imshow('gray', gray)
# cv2.waitKey(0)
#
#二值化处理
_, binary = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY)

# 
# cv2.imshow("Contours", binary)
# cv2.waitKey(0)

灰度处理和二值化后的结果:

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

可以看到图像中含有多余的部分,继续使用形态学操作(腐蚀)处理。这里分别进行x方向和y方向的腐蚀操作。

# 自定义 1x3 的核进行 x 方向的膨胀腐蚀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 3))
xtx_img = cv2.dilate(binary, kernel, iterations=3)
xtx_img = cv2.erode(xtx_img, kernel, iterations=3)#y 腐蚀去除碎片

# cv2.imshow("Contours", xtx_img)
# cv2.waitKey(0)

# 自定义 3x1 的核进行 y 方向的膨胀腐蚀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 1))
xtx_img = cv2.dilate(xtx_img, kernel, iterations=3)#x 膨胀回复形态
xtx_img = cv2.erode(xtx_img, kernel, iterations=3)#x 腐蚀去除碎片
#
# cv2.imshow("Contours", xtx_img)
# cv2.waitKey(0)

x方向腐蚀结果:

在这里插入图片描述

y方向腐蚀结果

在这里插入图片描述

然后需要找到所有的轮廓,因为opencv中默认函数找到的是白色轮廓,因此需要将得到的二值化结果取反操作。再使用函数获取所有的白色轮廓。运行出来的结果符合预期,确实是4。

# 将二值化图像取反
binary_inv = cv2.bitwise_not(xtx_img)
# cv2.imshow("Contours", binary_inv)
# cv2.waitKey(0)
#所有轮廓的列表contours和分层信息
contours, hierarchy = cv2.findContours(binary_inv, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 打印轮廓数量

print("轮廓数量:", len(contours))

在这里插入图片描述

得到轮廓后绘制在原图上看一下

img_contour = cv2.drawContours(result, contours, -1, (0, 255, 0), 1)

# cv2.imshow("img_contour", img_contour)
# cv2.waitKey(0)

感觉像那么回事了:

在这里插入图片描述

得到轮廓后按照轮廓的x轴坐标进行排序,以免后序识别中顺序乱了,从这每个数字就被我们单独拿出来了。

# 存储所有轮廓的图像
plate_imgs = []

# 获取所有轮廓的x坐标
x_coordinates = []
for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    x_coordinates.append(x)

# 将轮廓按照x坐标排序
sorted_contours = [contours[i] for i in np.argsort(x_coordinates)]

有了这些数字后,就需要分别识别这些数字都是数字几了,这里可以使用分类任务,训练一个自己的模型识别这些数字,这里我们采用一种自己研究生方向学到的结构相似性来做识别,只要能识别出来就行,结构相似性的值在0-1之间,越大说明两个图像越像(大概可以这么理解),因此我们提前保存好数字的图片,并且图片的名字分别对应数字的编号,这样在识别出来后,就直接打印图片的命名就知道这个数字是几了,因为这个LED屏幕中只有2、7、1三个数字因此我们只保存了这三张图片,用来识别,至于前边的第一个框区域不识别,是因为除过这些数字外还有CO甲烷等东西,这里没有做识别。我们分别将分割出来的每个数字和提前保存到文件夹中的数字对比结构相似性,保留结构相似性最高的数字以及文件名最终得到识别结果。

# 初始化最高相似度和对应文件名
max_sim = -1
max_sim_filename = ''

# 按x轴逐个绘制
for i, contour in enumerate(sorted_contours):
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(xtx_img, (x, y), (x + w, y + h), (0, 255, 0), 1)
    plate_img1 = binary_inv[y:y + h, x:x + w].copy()
    # 生成文件名,加上时间戳
    # filename = f"saved_image_{i}_{int(time.time())}.jpg"
    # cv2.imwrite(filename, plate_img1)
    # cv2.imshow("img_contour", plate_img1)
    # cv2.waitKey(0)
    plate_img = cv2.resize(plate_img1, (22, 60))
    # cv2.imshow("img_contour", plate_img)
    # cv2.waitKey(0)

    max_sim = 0
    # 遍历result文件夹下的所有文件
    for filename in os.listdir('Desktop\\count\\result2'):
        if filename.endswith('.png'):
            # 读取图像并resize//C:\\Users\\12561\\Desktop\\count\\result\\
            img2 = cv2.imread(os.path.join('Desktop\\count\\result2', filename), cv2.IMREAD_GRAYSCALE)
            img2 = cv2.resize(img2, (22, 60))

            # 计算相似度
            similarity = ssim(plate_img, img2)
            print(similarity, end=' ')
            # 如果相似度更高,则更新最高相似度和对应文件名
            if similarity > max_sim:
                max_sim = similarity
                max_sim_filename = os.path.splitext(filename)[0]  # 去掉文件后缀
    #if max_sim_filename == '_':
        #max_sim_filename = '.'
    # print(max_sim_filename, end='')
    print(f"{max_sim_filename} ({max_sim:.5f})", end=' ')
    print()

最终运行结果打印了每种图像的结构相似性值,得到最大的,并且处理速度很快,完全可以在CPU下实现快速处理。

在这里插入图片描述

项目完整代码:

import cv2
import numpy as np
import time
import os
from skimage.metrics import structural_similarity as ssim


img = cv2.imread('304.jpg')
start = time.time()
new_size = (640, 400)
img = cv2.resize(img, new_size)
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = np.zeros(hsv_img.shape[:2], dtype=np.uint8)
mask[(hsv_img[:,:,0] >= 130) & (hsv_img[:,:,0] <= 255) & (hsv_img[:,:,1] >= 0) &
(hsv_img[:,:,1] <= 255) & (hsv_img[:,:,2] >= 0) & (hsv_img[:,:,2] <= 90)] = 1
result = cv2.bitwise_and(img, img, mask=mask)

# cv2.imshow("Contours", result)
# cv2.waitKey(0)

gray = cv2.cvtColor(result,cv2.COLOR_BGR2GRAY)

# cv2.imshow("Contours", gray)
# cv2.waitKey(0)


#边缘检测
#转灰度,做单通道计算比较节省时间
edges = cv2.Canny(gray,50,150,apertureSize = 3)

#所有轮廓的列表contours和分层信息
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# print("轮廓数量:", len(contours))

img_copy = img.copy() # 复制原图,避免在原图上直接画框

for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt) # 获取轮廓的边界矩形
    area = w * h
    if area > 10000:
        x, y, w, h = cv2.boundingRect(cnt)
        area = w * h
        # print("xywh", x, y, w, h)
        # print("矩形面积:", area)
        roi = img_copy[y:y + h + 26, x:x + w+2]
        # cv2.imshow("ROI", roi)
        # cv2.waitKey(0)
        # print("矩形面积:", area)
        # cv2.rectangle(img_copy, (x, y), (x + w, y + h + 25), (0, 255, 0), 1)  # 画矩形框,绿色,宽度为2

# cv2.imshow("ROI", roi)
# cv2.waitKey(0)

# 转换为灰度图像
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# 高斯滤波
blur = cv2.GaussianBlur(gray, (3, 3), 0)

# 边缘检测
edges = cv2.Canny(gray, 50, 255)
# cv2.imshow("ROI", edges)
# cv2.waitKey(0)

# # 获得轮廓信息
# contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#
# # 绘制最小外接矩形
# for cnt in contours:
#     rect = cv2.minAreaRect(cnt)
#     box = cv2.boxPoints(rect)
#     box = np.int0(box)
#
#     cv2.drawContours(roi, [box], 0, (0, 255, 0), 1)
#
#     # 获取矩形的四个顶点坐标
#     rect_points = np.int0(cv2.boxPoints(rect))
#     # print("矩形的四个顶点坐标:", rect_points)
#
# # cv2.imshow('Contours', roi)
# # cv2.waitKey(0)
#
# # 定义原图像的四个角点坐标
# src = np.float32([[-8,61],[186 , -3],[ 23, 156], [218 , 91]])
#
# # 定义输出图像的四个角点坐标
# dst = np.float32([[0, 0],[200, 0],[0, 100],[200, 100]])
#
# # 计算变换矩阵
# M = cv2.getPerspectiveTransform(src, dst)
#
# # 应用变换矩阵
# result = cv2.warpPerspective(roi, M, (200, 100))
#
#
# 显示图像
# cv2.imshow('Contours', result)
# cv2.waitKey(0)

#
# 获取白色像素点坐标
coordinates = np.column_stack(np.where(edges == 255))

# # 打印白色像素点坐标
# print(coordinates)

# 在x轴方向上找到最小和最大的两个坐标
x_min = coordinates[np.argmin(coordinates[:,0])].copy()
x_min[0], x_min[1] = x_min[1], x_min[0]
x_max = coordinates[np.argmax(coordinates[:,0])].copy()
x_max[0], x_max[1] = x_max[1], x_max[0]

# 在y轴方向上找到最小和最大的两个坐标
y_min = coordinates[np.argmin(coordinates[:,1])].copy()
y_min[0], y_min[1] = y_min[1], y_min[0]
y_max = coordinates[np.argmax(coordinates[:,1])].copy()
y_max[0], y_max[1] = y_max[1], y_max[0]

# # # 打印最小和最大的两个坐标
# print('x_min:', x_min)
# print('x_max:', x_max)
# print('y_min:', y_min)
# print('y_max:', y_max)


# 定义原图像的四个角点坐标
src = np.float32([y_min,x_min,x_max, y_max])

# 定义输出图像的四个角点坐标
dst = np.float32([[0, 0],[200, 0],[0, 100],[200, 75]])

# 计算变换矩阵
M = cv2.getPerspectiveTransform(src, dst)

# 应用变换矩阵
result = cv2.warpPerspective(roi, M, (200, 75))
#显示图像
# cv2.imshow('result', result)
# cv2.waitKey(0)

# gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
# cv2.imshow('hsv_img', gray)
# cv2.waitKey(0)

hsv_img = cv2.cvtColor(result, cv2.COLOR_BGR2HSV)
# cv2.imshow('hsv_img', hsv_img)
# cv2.waitKey(0)
mask = np.zeros(hsv_img.shape[:2], dtype=np.uint8)
mask[(hsv_img[:,:,0] >= 0) & (hsv_img[:,:,0] <= 255) & (hsv_img[:,:,1] >= 0) &
(hsv_img[:,:,1] <= 255) & (hsv_img[:,:,2] >= 0) & (hsv_img[:,:,2] <= 100)] = 1
result1 = cv2.bitwise_and(hsv_img, hsv_img, mask=mask)

# cv2.imshow("Contours", result1)
# cv2.waitKey(0)

# 转换为灰度图像
gray = cv2.cvtColor(result1, cv2.COLOR_BGR2GRAY)
# cv2.imshow('gray', gray)
# cv2.waitKey(0)
#
#二值化处理
_, binary = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY)

#
# cv2.imshow("Contours", binary)
# cv2.waitKey(0)

# 自定义 1x3 的核进行 x 方向的膨胀腐蚀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 3))
xtx_img = cv2.dilate(binary, kernel, iterations=3)
xtx_img = cv2.erode(xtx_img, kernel, iterations=3)#y 腐蚀去除碎片

# cv2.imshow("Contours", xtx_img)
# cv2.waitKey(0)

# 自定义 3x1 的核进行 y 方向的膨胀腐蚀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 1))
xtx_img = cv2.dilate(xtx_img, kernel, iterations=3)#x 膨胀回复形态
xtx_img = cv2.erode(xtx_img, kernel, iterations=3)#x 腐蚀去除碎片
#
# cv2.imshow("Contours", xtx_img)
# cv2.waitKey(0)

# 将二值化图像取反
binary_inv = cv2.bitwise_not(xtx_img)
# cv2.imshow("Contours", binary_inv)
# cv2.waitKey(0)
#所有轮廓的列表contours和分层信息
contours, hierarchy = cv2.findContours(binary_inv, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 打印轮廓数量

print("轮廓数量:", len(contours))

# # 创建一个空图像,与原图像大小和通道数相同
# contours_img = np.zeros_like(result)

# 绘制轮廓
img_contour = cv2.drawContours(result, contours, -1, (0, 255, 0), 1)

# cv2.imshow("img_contour", img_contour)
# cv2.waitKey(0)

# 存储所有轮廓的图像
plate_imgs = []

# 获取所有轮廓的x坐标
x_coordinates = []
for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    x_coordinates.append(x)

# 将轮廓按照x坐标排序
sorted_contours = [contours[i] for i in np.argsort(x_coordinates)]

# 初始化最高相似度和对应文件名
max_sim = -1
max_sim_filename = ''

# 按x轴逐个绘制
for i, contour in enumerate(sorted_contours):
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(xtx_img, (x, y), (x + w, y + h), (0, 255, 0), 1)
    plate_img1 = binary_inv[y:y + h, x:x + w].copy()
    # 生成文件名,加上时间戳
    # filename = f"saved_image_{i}_{int(time.time())}.jpg"
    # cv2.imwrite(filename, plate_img1)
    # cv2.imshow("img_contour", plate_img1)
    # cv2.waitKey(0)
    plate_img = cv2.resize(plate_img1, (22, 60))
    # cv2.imshow("img_contour", plate_img)
    # cv2.waitKey(0)

    max_sim = 0
    # 遍历result文件夹下的所有文件
    for filename in os.listdir('Desktop\\count\\result2'):
        if filename.endswith('.png'):
            # 读取图像并resize//C:\\Users\\12561\\Desktop\\count\\result\\
            img2 = cv2.imread(os.path.join('Desktop\\count\\result2', filename), cv2.IMREAD_GRAYSCALE)
            img2 = cv2.resize(img2, (22, 60))

            # 计算相似度
            similarity = ssim(plate_img, img2)
            print(similarity, end=' ')
            # 如果相似度更高,则更新最高相似度和对应文件名
            if similarity > max_sim:
                max_sim = similarity
                max_sim_filename = os.path.splitext(filename)[0]  # 去掉文件后缀
    if max_sim_filename == '_':
        max_sim_filename = '.'
    # print(max_sim_filename, end='')
    print(f"{max_sim_filename} ({max_sim:.5f})", end=' ')
    print()
    # print(max_sim_filename)
    # cv2.imwrite("C:\\Users\\12561\\Desktop\\count\\result\\plate_img1.png", dilation)  # 保存结果为 PNG 文件
    # cv2.imshow("Contours", plate_img1)
    # cv2.waitKey(0)

print()
end = time.time();
print("time =",end-start)

项目中很多阈值都需要按照特殊图片特殊设置。

总体步骤:

  • 轮廓检测
  • 透视校正
  • 字符分割
  • 字符识别

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

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

相关文章

postman处理各种请求数据

1、后台request接收postman参数 2、后台单个参数接收postman 3、后台RequestParam参数接收postman 注意事项&#xff1a;情况一&#xff1a;全部都是单个字符串的 情况二&#xff1a;有可能是一个json对象序列化成字符串过来的&#xff0c;那么需要在form-data中设置 …

鸿蒙Hi3861学习十-Huawei LiteOS-M(消息队列)

一、简介 消息队列&#xff0c;是一种常用于任务间通信的数据结构&#xff0c;实现了接收来自任务或中断的不固定长度的消息&#xff0c;并根据不同的接口选择传递消息是否存放在自己空间。任务能够从队列里面读取消息&#xff0c;当队列中的消息是空时&#xff0c;挂起读取任务…

国内免费cdn汇总2023最新

内容分发网络简称CDN&#xff0c;其原理大概是将网站内容分发至加速节点&#xff0c;让用户从就近的服务器节点上获取内容&#xff0c;从而提高网站的访问加载速度。大部分服务商&#xff08;如阿里云&#xff0c;腾讯云&#xff0c;京东云等&#xff09;的CDN服务是按使用量收…

【iOS】-- GET和POST(NSURLSession)

文章目录 NSURLSessionGET和POST区别 GET方法GET请求步骤 POSTPOST请求步骤 NSURLSessionDataDelegate代理方法AFNetWorking添加头文件GETPOST第一种第二种 NSURLSession 使用NSURLSession&#xff0c;一般有两步操作&#xff1a;通过NSURLSession的实例创建task&#xff1b;执…

STL配接器(容器适配器)—— stack 的介绍使用以及模拟实现。

注意 &#xff1a; 以下所有文档都来源此网站 &#xff1a; http://cplusplus.com/ 一、stack 的介绍和使用 stack 文档的介绍&#xff1a;https://cplusplus.com/reference/stack/stack/ 1. stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&…

预训练模型之BERT、Transformer-XL、XL-Net等

文章目录 预训练模型&#xff08;Pre-trained Models, PTMs&#xff09;前置知识BERTTransformer-XLXLNetTransformer-XL类似工作&#xff08;Scalable Transformer&#xff09;1. 《Scaling Transformer to 1M tokens and beyond with RMT》2. 《》 预训练模型&#xff08;Pre…

【Linux常见指令以及权限理解】基本指令(3)

写在前面 上一篇文章&#xff0c;我们学习了Linux的一些常用指令&#xff0c; 学习了如何理解Linux系统&#xff0c;介绍了对Linux系统的理解&#xff1a;Linux下一切皆文件 介绍了重定向还有管道相关的知识。这里是上一篇博客的链接&#xff1a;http://t.csdn.cn/2d6fc 接…

Vue组件化编程

2.1. 模块与组件、模块化与组件化 模块&#xff1a; 理解&#xff1a;向外提供特定功能的 js 程序&#xff0c;一般就是一个 js 文件为什么&#xff1a;js 文件很多很复杂作用&#xff1a;复用、简化 js 的编写&#xff0c;提高 js 运行效率 组件&#xff1a; 定义&#xff…

QT界面开发杂记(五)

QString转char* QString("name").toStdString().c_str() c_str()没有‘\0’结尾可能导致一些错误可以使用以下方法解决&#xff1a; QString xmlPath "path"; const char cXmlName[1024] {0}&#xff1b; memcpy((void*)cXmlName,xmlPath.toStdStri…

Prompt learning 教学[案例篇]:文生文案例设定汇总,你可以扮演任意角色进行专业分析

Prompt learning 教学[案例篇]&#xff1a;文生文案例设定汇总&#xff0c;你可以扮演任意角色进行专业分析 1.角色扮演 行为Prompt写法“牙医”““我想让你扮演一名牙医。我会向你提供有关寻找牙科服务&#xff08;例如 X 光、清洁和其他治疗&#xff09;的个人的详细信息。…

《编程思维与实践》1072.下一位妙数

《编程思维与实践》1072.下一位妙数 题目 思路 思路与最小不重复数基本一致,从最高位开始找到第一个出现9的位置,让其加1,后面全变为0即可. 只需要再加一个判定条件:不能被9整除. 由数学知识,一个数不能被9整除当且仅当各位数之和不能被9整除. 这里给出简单的证明: 不妨以三位…

工程化:vite4和vue3里面的命令式loading的封装及使用

用习惯了vue的组件使用方式,转到vue3里面发现没有了vue的原型,不能全局挂载方法了,我们要使用命令式调用组件该怎么做, 效果展示 代码演练 1.组件结构 2.基础的组件模板loading.vue <template><sectionclass="full-loading":class

多台电脑共享鼠标键盘软件

背景 最近接手了2个不同base的项目&#xff0c;由于2个base的不同代码加密管理&#xff0c;必须要用两台电脑进行分别开发。于是&#xff0c;我不大的办公桌上要摆上2个键盘和2个鼠标&#xff0c;一下子就显得桌面特别杂乱&#xff0c;办公心情都不舒畅了。 我跟朋友吐槽了这件…

华硕ROG|玩家国度魔霸新锐2023 Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原

华硕ROG|玩家国度魔霸新锐2023 Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原 文件地址&#xff1a;https://pan.baidu.com/s/1snKOsH3OMl3GZLqeAf-GLA?pwd8888 华硕工厂恢复系统 &#xff0c;安装结束后带隐藏分区以及机器所有驱动软件 需准备一个16G左右…

《小钊记》项目启动前期工作相关记录:VUE、powerdesigner建模、虚拟机密码重置、代码生成

目录 VUE镜像基本命令vue 不是内部或外部命令路径配置路由 powerdesigner 建模栏位添加注释id设置自增导出sql 虚拟机root密码重置&#xff08;centos7&#xff09;生成代码工具安装EasyCode插件连接数据库生成代码可以自定义模板复制现有的模板&#xff0c;在其基础上进行改造…

神秘的IP地址8.8.8.8地址到底是什么?为什么会被用作DNS服务器地址呢?

当我们在配置网络连接或者路由器时&#xff0c;经常会遇到需要填写DNS服务器地址的情况。而在这些情况下&#xff0c;很多人都会听到一个神秘的数字地址&#xff1a;8.8.8.8。那么&#xff0c;这个地址到底是什么&#xff0c;为什么会被用作DNS服务器地址呢&#xff1f;本文将详…

【HA】HomeAssistant 添加 小米温湿度计2代

本方法只是被动的获取小米温湿度计广播出的温度和湿度数据&#xff0c;并没有其他更多功能。 0. 本人配置 树莓派3B Debian 11 (Bullseye) 64 位 Supervisor 2023.05.dev0901 HomeAssistant 已安装 HACS 1. 安装 打开侧边栏中的HACS&#xff0c;点击“集成” 右下角找到“…

如何把软件从C盘移到D盘?

​为什么要把软件从C盘移到D盘&#xff1f; C盘是安装操作系统的系统分区。虽然很多用户在安装系统的时候会给C盘分配了大量的磁盘空间&#xff0c;但是大多数用户会发现C盘很快就会无缘无故的被占满。这是为什么呢&#xff1f;这主要是由于大多数三方程序默认安装在C盘造成…

ios打包ipa的四种实用方法(.app转.ipa)

总结一下&#xff0c;目前.app包转为.ipa包的方法有以下几种&#xff1a; 1、Apple推荐的方式&#xff0c;即实用xcode的archive功能 Xcode菜单栏->Product->Archive->三选一&#xff0c;一般选后两个。 局限性&#xff1a;个人开发一般采用这种方法&#xff0c;但…

【C++】-const对象及成员函数之类和对象中篇完结(中)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树 ❤️‍&#x1fa79;作者宣言&#xff1a;认真写好每一篇博客 &#x1f4a8;作者gitee:gitee &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 文章目录 前言一、案例的引入二、const对象和成员函数三、取地…