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

news2024/10/7 16:22:12

应项目要求需要基于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)

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

总体步骤:

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

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

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

相关文章

Java 观察者模式 详解

观察者模式是一种常见的设计模式&#xff0c;也称作发布-订阅模式。它主要解决了对象之间的通知依赖关系问题。在这种模式中&#xff0c;一个对象&#xff08;称作Subject&#xff09;维护着一个对象列表&#xff0c;这些对象&#xff08;称作Observers&#xff09;都需要被通知…

STANet代码复现出现的问题

1 IndexError: boolean index did not match indexed array along dimension 0; dimension is 4194304 but corresponding boolean dimension is 65536定位到导致错误的代码&#xff0c;是metric.py&#xff0c;Collect values for Confusion Matrix 收集混淆矩阵的值时出错 …

Java框架学习02(SpringSpringBoot常用注解总结)

1. SpringBootApplication 这里先单独拎出SpringBootApplication 注解说一下&#xff0c;虽然我们一般不会主动去使用它。 Guide&#xff1a;这个注解是 Spring Boot 项目的基石&#xff0c;创建 SpringBoot 项目之后会默认在主类加上。 SpringBootApplication public class…

ROM、RAM、FLASH区别

文章目录 一、ROM二、RAM三、FLASH四、单片机程序大小计算1、Keil/MDK 一、ROM ROM&#xff08;Read Only Memory&#xff09;只读存储器。是一种半导体内存&#xff0c;其特性是一旦储存资料就无法再将之改变或删除。通常用在不需经常变更资料的电子或电脑系统中&#xff0c;资…

性能测试的核心原理

性能测试的核心原理 1 基于协议&#xff0c;前后端交互机制&#xff0c;性能核心。基于界面决定和前端用户交互&#xff0c;基于代码决定了后端。 1 网络分布式架构。 2 单机应用&#xff0c;比如安安兔&#xff0c;鲁大师。主要判断io读写&#xff0c;以及对资源的消耗。 2 多…

git常见操作命令

1.Git 配置 在安装完成 Git 后&#xff0c;开始正式使用前&#xff0c;是需要有一些全局设置的&#xff0c;如用户名、邮箱。 git config --global user.name "your name" // 设置全局用户名 git config --global user.email "your email" // 设…

Linux诞生与分支

a) 什么是操作系统操作系统是计算机系统中必不可少的基础系统软件&#xff0c;它的作用是管理和控制计算机系统中的硬件和软件资源&#xff0c;合理地组织计算机系统的工作流程&#xff0c;以便有效地利用这些资源为使用者提供一个功能强大、使用方便的操作环境。它在计算机系…

汽车轮胎充电宝打气泵方案

我们知道新能源车是没有配置充气泵的&#xff0c;所以在平时日常使用中我们还需要配置一个充气泵。充气泵方案便是在这个用户需求上面开发出来的。它体积小、外观精美、带有多模式充气并车胎检测等功能&#xff0c;是现在有车一族的出行必备物品。 充气泵方案其功能设计集成于一…

【语义分割】标注工具ISAT with segment anything介绍

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 极速分割标注工具 1. 正文 1.1 安装 创建虚拟环境 conda create -n ISAT_with_segment_anything python3.8 conda activate ISAT_with_segment_anyt…

宝塔面板搭建Discuz论坛并发布互联网访问【无需云服务器】

✨个人主页&#xff1a;bit me&#x1f447; 目 录 ⏳前言⏰1.安装基础环境⌚️2.一键部署Discuz&#x1f4fb;3.安装cpolar工具&#x1f4e1;4.配置域名访问Discuz&#x1f50d;5.固定域名公网地址&#x1f4f2;6.配置Discuz论坛 转载自cpolar极点云的文章&#xff1a;Linux宝…

关于startActivityForResult()方法被弃用的解决方案

关于startActivityForResult()方法被弃用的解决方案 背景&#xff1a;使用startActivityForResult()方法时&#xff0c;代码出现划线&#xff0c;显示该方法已被弃用 startActivityForResult() 方法是一种用于在 Android 应用程序中启动另一个活动&#xff08;Activity&#x…

Charles抓包

mac 1、下载安装包 官网下载&#xff1a;Download a Free Trial of Charles • Charles Web Debugging Proxy 2、有效期&#xff1a; 打开Charles > Help > Register Charles > 输入 Registered Name &#xff1a; https://zhile.io License Key&#xff1a;48891…

“正大杯”第十三届市场调查与分析大赛[省一]经验总结+复盘

目录 1 前期组队 2 队员组成 队长-成员1 应用统计学专业 成员2 化学实验专业 成员3-本人 物联网工程专业 成员4 金融ACCA专业 成员5 应用物理学 总结 3 比赛进度 3月中旬 部分图表的制作 问卷设计与制作 稍微改动主题 问卷相关总结 前期调查部分论文框架 3月…

手拉手教您Linux搭建web

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 转载自cpolar文章&#xff1a;Linux CentOS本地搭建…

【2023 · CANN训练营第一季】应用开发深入讲解——第四章 DVPP初级

学习资源 1.JPEG图片解码 文档 2.图片缩放 文档 媒体数据处理 数据预处理的典型使用场景 数据预处理的多种方式 分为两种&#xff0c;AIPP和DVPP DVPP数据预处理功能 了解两个重要概念 宽stride 和 高stride 理解&#xff1a;这里假设一张图片的宽为500&#xff0c;高为3…

SpringBoot Properties YML文件的优先级

SpringBoot启动文件的优先顺序测试 运行时指定加载文件时&#xff0c;最高Java jar spring.config.location优先于spring.profiles.activeJava -jar 三种语法 运行时不指定加载文件时&#xff0c;其次1.项目路径下的config目录优先级最高---12.项目路径的优先级其次---23.Eclip…

linux:查找相关命令find与grep

find 语法 在指定目录下查找文件和目录。 find [path] [expression]path&#xff1a;是要查找的目录路径&#xff0c;可以是一个目录或文件名&#xff0c;也可以是多个路径&#xff0c;多个路径之间用空格分隔&#xff0c;如果未指定路径&#xff0c;则默认为当前目录。 exp…

Kotlin高级协程

Kotlin高级协程 一.前言二.先从线程说起三.协程的设计思想四.协程特点&#xff1a;优雅的实现移步任务五.协程基本使用六.协程和线程相比有什么特点&#xff0c;如何优雅的实现异步任务 一.前言 在文章正式上干货之前&#xff0c;先说一点背景吧&#xff1b;我是 Kotlin 协程官…

C语言初阶之扫雷代码详解(含递归展开)

扫雷代码详解 扫雷代码思路头文件解析主函数解析函数文件解析①初始化函数&#xff08;InitBoard&#xff09;②打印函数③难易选项函数&#xff08;Select&#xff09;④布雷&#xff08;SetMine&#xff09;⑤获取周边雷数&#xff08;GetMine&#xff09;⑥展开函数&#xf…

从零开始,轻松打造Web自动化测试框架:Python+Selenium实战指南

在程序员的世界中&#xff0c;一切重复性的工作&#xff0c;都应该通过程序自动执行。「自动化测试」就是一个最好的例子。 随着互联网应用开发周期越来越短&#xff0c;迭代速度越来越快&#xff0c;只会点点点&#xff0c;不懂开发的手工测试&#xff0c;已经无法满足如今的…