当前位置: 首页 > news >正文

OpenCV实现手势虚拟拖拽

前言: Hello大家好,我是Dream。 今天来学习一下如何使用OpenCV实现手势虚拟拖拽,欢迎大家一起前来探讨学习~

一、主要步骤及库的功能介绍

1.主要步骤

要实现本次实验,主要步骤如下:

  1. 导入OpenCV库。
  2. 通过OpenCV读取摄像头的视频流。
  3. 使用肤色检测算法(如色彩空间转换和阈值分割)来识别手部区域。
  4. 对手部区域进行轮廓检测,找到手的轮廓。
  5. 根据手的轮廓,获取手指关键点的像素坐标。对于拖拽手势,可以关注食指和中指的位置。
  6. 计算食指和中指指尖之间的距离并判断是否满足条件触发拖拽动作。
  7. 如果满足条件,可以使用勾股定理计算距离,并将矩形区域变色以示触发拖拽。
  8. 根据手指的位置更新矩形的坐标,使矩形跟随手指运动。
  9. 当手指放开时停止矩形的移动。

2.需要的库介绍

导入的库在实现手势虚拟拖拽的代码中起着重要的作用,下面是对每个库的简要介绍:

  1. OpenCV (cv2): OpenCV是一个开源的计算机视觉库,提供了丰富的图像和视频处理功能,使用OpenCV来读取摄像头视频流、进行图像处理和计算

  2. mediapipe (mp): mediapipe提供一系列预训练的机器学习模型和工具,用于实现计算机视觉和机器学习任务。我们使用mediapipe来进行手部关键点检测和姿势估计

  3. time: 我们使用time库来计时和进行时间相关的操作。

  4. math: 在本次实验我们使用math库来计算距离和角度

二、导入所需要的模块

# 导入OpenCV
import cv2
# 导入mediapipe
import mediapipe as mp# 导入其他依赖包
import time
import math

三、方块管理类

(SquareManager)是一个方块管理器,用于创建、显示、更新和处理方块的相关操作。

1.初始化方块管理器

初始化方块管理器,传入方块的长度(rect_width)作为参数,并初始化方块列表、距离、激活状态和激活的方块ID等属性。

class SquareManager:def __init__(self, rect_width):# 方框长度self.rect_width = rect_width# 方块列表self.square_count = 0self.rect_left_x_list = []self.rect_left_y_list = []self.alpha_list = []# 中指与矩形左上角点的距离self.L1 = 0self.L2 = 0# 激活移动模式self.drag_active = False# 激活的方块IDself.active_index = -1

2.创建一个方块

创建一个方块,将方块的左上角坐标和透明度添加到相应的列表中。

# 创建一个方块,但是没有显示def create(self, rect_left_x, rect_left_y, alpha=0.4):# 将方块的左上角坐标和透明度添加到相应的列表中self.rect_left_x_list.append(rect_left_x)self.rect_left_y_list.append(rect_left_y)self.alpha_list.append(alpha)self.square_count += 1

3.更新显示方块的位置

根据方块的状态,在图像上绘制方块,并使用透明度将叠加图像叠加到原始图像上。

 # 更新显示方块的位置def display(self, class_obj):# 遍历方块列表for i in range(0, self.square_count):x = self.rect_left_x_list[i]y = self.rect_left_y_list[i]alpha = self.alpha_list[i]overlay = class_obj.image.copy()# 如果方块处于激活状态,绘制紫色方块;否则绘制蓝色方块if (i == self.active_index):cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 255), -1)else:cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 0), -1)# 使用透明度将叠加图像叠加到原始图像上class_obj.image = cv2.addWeighted(overlay, alpha, class_obj.image, 1 - alpha, 0)

4.判断落点方块

判断给定的坐标是否在方块内,并返回方块的ID。

    # 判断落在哪个方块上,返回方块的IDdef checkOverlay(self, check_x, check_y):# 遍历方块列表for i in range(0, self.square_count):x = self.rect_left_x_list[i]y = self.rect_left_y_list[i]# 检查指定点是否在方块内if (x < check_x < (x + self.rect_width)) and (y < check_y < (y + self.rect_width)):# 保存被激活的方块IDself.active_index = ireturn ireturn -1

5.计算距离、更新位置

​​setLen​ 方法:计算激活方块与指尖的距离。
​​updateSquare​ 方法:根据给定的新坐标更新激活方块的位置。

    # 计算与指尖的距离def setLen(self, check_x, check_y):# 计算距离self.L1 = check_x - self.rect_left_x_list[self.active_index]self.L2 = check_y - self.rect_left_y_list[self.active_index]# 更新方块位置def updateSquare(self, new_x, new_y):self.rect_left_x_list[self.active_index] = new_x - self.L1self.rect_left_y_list[self.active_index] = new_y - self.L2

三、识别控制类

1.初始化识别控制类

class HandControlVolume:def __init__(self):# 初始化mediapipeself.mp_drawing = mp.solutions.drawing_utilsself.mp_drawing_styles = mp.solutions.drawing_stylesself.mp_hands = mp.solutions.hands# 中指与矩形左上角点的距离self.L1 = 0self.L2 = 0# image实例,以便另一个类调用self.image = None

HandControlVolume用于初始化mediapipe以及存储中指与矩形左上角点的距离和image实例。

  • __init__ 方法:在初始化对象时,初始化mediapipe,包括drawing_utils、drawing_styles和hands。此外,还初始化了中指与矩形左上角点的距离和image实例。

通过mediapipe,可以进行手部关键点检测和姿势估计,进而进行手势识别和处理。为了使其他类能够调用image实例,将其作为该类的属性进行存储,方便地处理手势识别和控制操作。

2.主函数

这部分代码主要用于初始化和准备处理视频流以进行手势识别和交互。

    def recognize(self):# 计算刷新率fpsTime = time.time()# OpenCV读取视频流cap = cv2.VideoCapture(0)# 视频分辨率resize_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))resize_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))# 画面显示初始化参数rect_percent_text = 0# 初始化方块管理器squareManager = SquareManager(150)# 创建多个方块for i in range(0, 5):squareManager.create(200 * i + 20, 200, 0.6)with self.mp_hands.Hands(min_detection_confidence=0.7,min_tracking_confidence=0.5,max_num_hands=2) as hands:while cap.isOpened():# 初始化矩形success, self.image = cap.read()self.image = cv2.resize(self.image, (resize_w, resize_h))if not success:print("空帧.")continue
  • resize_wresize_h:根据摄像头分辨率获取的视频帧的宽度和高度,并作为后续处理的图像尺寸进行缩放。

  • rect_percent_text:画面显示初始化参数,可能被用于屏幕上的文本显示。

  • squareManager:初始化了方块管理器类的实例,并设置方块的长度为150。

使用一个循环,创建了五个方块,并通过create方法将其添加到方块管理器中。进入循环,从视频流中读取帧图像,并将其调整为指定的尺寸。如果成功读取帧图像,则会进一步处理,否则将输出错误消息。

3.提高性能和处理图像

                self.image.flags.writeable = False# 转为RGBself.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)# 镜像self.image = cv2.flip(self.image, 1)# mediapipe模型处理results = hands.process(self.image)self.image.flags.writeable = Trueself.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR)
  • self.image.flags.writeable = False:设置图像为不可写,以提高性能并避免数据拷贝。

  • self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB):将BGR格式的图像转换为RGB格式,因为mediapipe模型处理的输入图像需要是RGB格式。

  • self.image = cv2.flip(self.image, 1):将图像进行镜像翻转,以与mediapipe模型期望的手部位置对应。

  • results = hands.process(self.image):将处理后的图像传递给mediapipe的hands模型,进行手势识别和处理。

  • self.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR):将图像从RGB格式转换回BGR格式,以便后续的显示和处理。

4.检测手掌,标记关键点和连接关系

                if results.multi_hand_landmarks:# 遍历每个手掌for hand_landmarks in results.multi_hand_landmarks:# 在画面标注手指self.mp_drawing.draw_landmarks(self.image,hand_landmarks,self.mp_hands.HAND_CONNECTIONS,self.mp_drawing_styles.get_default_hand_landmarks_style(),self.mp_drawing_styles.get_default_hand_connections_style())
  • if results.multi_hand_landmarks::检查是否检测到手掌。如果有检测到手掌,则进入下一步处理;否则跳过。

  • for hand_landmarks in results.multi_hand_landmarks::遍历检测到的每个手掌。

  • self.mp_drawing.draw_landmarks:使用mediapipe的draw_landmarks方法,在图像上标记手指的关键点和连接关系。

self.image:输入的图像。hand_landmarks:手掌的关键点。

self.mp_hands.HAND_CONNECTIONS:手指之间的连接关系。

5.解析检测到的手掌并提取手指的关键点

检测到的手掌并提取手指的关键点,然后将手指的坐标存储起来。

                        landmark_list = []# 用来存储手掌范围的矩形坐标paw_x_list = []paw_y_list = []for landmark_id, finger_axis in enumerate(hand_landmarks.landmark):landmark_list.append([landmark_id, finger_axis.x, finger_axis.y,finger_axis.z])paw_x_list.append(finger_axis.x)paw_y_list.append(finger_axis.y)if landmark_list:# 比例缩放到像素ratio_x_to_pixel = lambda x: math.ceil(x * resize_w)ratio_y_to_pixel = lambda y: math.ceil(y * resize_h)# 设计手掌左上角、右下角坐标paw_left_top_x, paw_right_bottom_x = map(ratio_x_to_pixel,[min(paw_x_list), max(paw_x_list)])paw_left_top_y, paw_right_bottom_y = map(ratio_y_to_pixel,[min(paw_y_list), max(paw_y_list)])# 给手掌画框框cv2.rectangle(self.image, (paw_left_top_x - 30, paw_left_top_y - 30),(paw_right_bottom_x + 30, paw_right_bottom_y + 30), (0, 255, 0), 2)# 获取中指指尖坐标middle_finger_tip = landmark_list[12]middle_finger_tip_x = ratio_x_to_pixel(middle_finger_tip[1])middle_finger_tip_y = ratio_y_to_pixel(middle_finger_tip[2])# 获取食指指尖坐标index_finger_tip = landmark_list[8]index_finger_tip_x = ratio_x_to_pixel(index_finger_tip[1])index_finger_tip_y = ratio_y_to_pixel(index_finger_tip[2])# 中间点between_finger_tip = (middle_finger_tip_x + index_finger_tip_x) // 2, (middle_finger_tip_y + index_finger_tip_y) // 2thumb_finger_point = (middle_finger_tip_x, middle_finger_tip_y)index_finger_point = (index_finger_tip_x, index_finger_tip_y)
  • landmark_list:一个列表,用于存储手指的关键点信息。

  • paw_x_listpaw_y_list:用于存储手掌范围的矩形框的横纵坐标。

  • 在循环中,将每个手指的关键点的索引、x坐标、y坐标和z坐标存储在landmark_list中,同时将手掌范围的横纵坐标存储在paw_x_listpaw_y_list中。

如果landmark_list不为空,即有手指的关键点被检测到ratio_x_to_pixelratio_y_to_pixel:两个lambda函数,用于将相对比例转换为像素坐标的函数。根据手掌范围的矩形坐标,计算手掌区域的左上角和右下角坐标,并画出方框。使用landmark_list中的信息获取中指指尖坐标和食指指尖坐标,并将它们转换为像素坐标。计算中指指尖坐标和食指指尖坐标的中间点。将中指指尖的坐标和食指指尖的坐标存储在thumb_finger_pointindex_finger_point中。

解析检测到的手掌信息,并提取手指的关键点坐标,将手指坐标转换为像素坐标,并将中指指尖和食指指尖的位置标记在图像上。

6.绘制指尖圆圈和连接线,计算距离

                            circle_func = lambda point: cv2.circle(self.image, point, 10, (255, 0, 255), -1)self.image = circle_func(thumb_finger_point)self.image = circle_func(index_finger_point)self.image = circle_func(between_finger_tip)# 画2点连线self.image = cv2.line(self.image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)# 勾股定理计算长度line_len = math.hypot((index_finger_tip_x - middle_finger_tip_x),(index_finger_tip_y - middle_finger_tip_y))# 将指尖距离映射到文字rect_percent_text = math.ceil(line_len)
  • cv2.line函数,在图像上绘制中指指尖和食指指尖之间的连接线。

  • math.hypot函数计算直角三角形斜边的长度。

  • 将指尖之间的距离映射到rect_percent_text变量中,用作后续文本显示的参数。

7.跟踪手指之间的距离

                            if squareManager.drag_active:# 更新方块squareManager.updateSquare(between_finger_tip[0], between_finger_tip[1])if (line_len > 100):# 取消激活squareManager.drag_active = FalsesquareManager.active_index = -1elif (line_len < 100) and (squareManager.checkOverlay(between_finger_tip[0],between_finger_tip[1]) != -1) and (squareManager.drag_active == False):# 激活squareManager.drag_active = True# 计算距离squareManager.setLen(between_finger_tip[0], between_finger_tip[1])

如果squareManagerdrag_active属性为True,即矩形的移动模式已经激活,使用squareManager.updateSquare方法更新矩形的位置。如果两个手指之间的距离大于100,即手指之间的距离超过了阈值,取消矩形的激活模式,将drag_active设置为False,将active_index设置为-1。

否则,如果两个手指之间的距离小于100,且手指之间存在重叠的矩形,并且矩形的移动模式未激活。激活矩形的移动模式,将drag_active设置为True。根据手指之间的距离,计算并设置矩形的长度,使用squareManager.setLen方法。

8.显示图像

                squareManager.display(self)# 显示距离cv2.putText(self.image, "Distance:" + str(rect_percent_text), (10, 120), cv2.FONT_HERSHEY_PLAIN, 3,(255, 0, 0), 3)# 显示当前激活cv2.putText(self.image, "Active:" + ("None" if squareManager.active_index == -1 else str(squareManager.active_index)), (10, 170),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)# 显示刷新率FPScTime = time.time()fps_text = 1 / (cTime - fpsTime)fpsTime = cTimecv2.putText(self.image, "FPS: " + str(int(fps_text)), (10, 70),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)# 显示画面cv2.imshow('virtual drag and drop', self.image)if cv2.waitKey(5) & 0xFF == 27:breakcap.release()control = HandControlVolume()
control.recognize()

主函数(recognize)的结尾部分,用于显示图像、矩形的状态和刷新率,并等待按键响应。使用squareManager.display方法显示矩形。cv2.waitKey函数等待按键输入,如果按下的键是ESC键(对应的ASCII码为27),则退出循环。

在屏幕上显示处理后的图像、矩形的状态和刷新率,并等待按键响应。这样可以实现交互式的虚拟拖放功能。接下来我们看一下实际的操作效果。

四、实战演示

在这里插入图片描述

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

通过演示我们可以实现通过手部对方块进行拖拽,效果可以达到良好的状态。

五、源码分享

import cv2
import mediapipe as mp
import time
import math
class SquareManager:def __init__(self, rect_width):# 方框长度self.rect_width = rect_width# 方块listself.square_count = 0self.rect_left_x_list = []self.rect_left_y_list = []self.alpha_list = []# 中指与矩形左上角点的距离self.L1 = 0self.L2 = 0# 激活移动模式self.drag_active = False# 激活的方块IDself.active_index = -1# 创建一个方块,但是没有显示def create(self, rect_left_x, rect_left_y, alpha=0.4):self.rect_left_x_list.append(rect_left_x)self.rect_left_y_list.append(rect_left_y)self.alpha_list.append(alpha)self.square_count += 1# 更新位置def display(self, class_obj):for i in range(0, self.square_count):x = self.rect_left_x_list[i]y = self.rect_left_y_list[i]alpha = self.alpha_list[i]overlay = class_obj.image.copy()if (i == self.active_index):cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 255), -1)else:cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 0), -1)# Following line overlays transparent rectangle over the self.imageclass_obj.image = cv2.addWeighted(overlay, alpha, class_obj.image, 1 - alpha, 0)# 判断落在哪个方块上,返回方块的IDdef checkOverlay(self, check_x, check_y):for i in range(0, self.square_count):x = self.rect_left_x_list[i]y = self.rect_left_y_list[i]if (x < check_x < (x + self.rect_width)) and (y < check_y < (y + self.rect_width)):# 保存被激活的方块IDself.active_index = ireturn ireturn -1# 计算与指尖的距离def setLen(self, check_x, check_y):# 计算距离self.L1 = check_x - self.rect_left_x_list[self.active_index]self.L2 = check_y - self.rect_left_y_list[self.active_index]# 更新方块    def updateSquare(self, new_x, new_y):# print(self.rect_left_x_list[self.active_index])self.rect_left_x_list[self.active_index] = new_x - self.L1self.rect_left_y_list[self.active_index] = new_y - self.L2# 识别控制类
class HandControlVolume:def __init__(self):# 初始化medialpipeself.mp_drawing = mp.solutions.drawing_utilsself.mp_drawing_styles = mp.solutions.drawing_stylesself.mp_hands = mp.solutions.hands# 中指与矩形左上角点的距离self.L1 = 0self.L2 = 0# image实例,以便另一个类调用self.image = None# 主函数def recognize(self):# 计算刷新率fpsTime = time.time()# OpenCV读取视频流cap = cv2.VideoCapture(0)# 视频分辨率resize_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))resize_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))# 画面显示初始化参数rect_percent_text = 0# 初始化方块管理器squareManager = SquareManager(150)# 创建多个方块for i in range(0, 5):squareManager.create(200 * i + 20, 200, 0.6)with self.mp_hands.Hands(min_detection_confidence=0.7,min_tracking_confidence=0.5,max_num_hands=2) as hands:while cap.isOpened():# 初始化矩形success, self.image = cap.read()self.image = cv2.resize(self.image, (resize_w, resize_h))if not success:print("空帧.")continue# 提高性能self.image.flags.writeable = False# 转为RGBself.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)# 镜像self.image = cv2.flip(self.image, 1)# mediapipe模型处理results = hands.process(self.image)self.image.flags.writeable = Trueself.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR)# 判断是否有手掌if results.multi_hand_landmarks:# 遍历每个手掌for hand_landmarks in results.multi_hand_landmarks:# 在画面标注手指self.mp_drawing.draw_landmarks(self.image,hand_landmarks,self.mp_hands.HAND_CONNECTIONS,self.mp_drawing_styles.get_default_hand_landmarks_style(),self.mp_drawing_styles.get_default_hand_connections_style())# 解析手指,存入各个手指坐标landmark_list = []# 用来存储手掌范围的矩形坐标paw_x_list = []paw_y_list = []for landmark_id, finger_axis in enumerate(hand_landmarks.landmark):landmark_list.append([landmark_id, finger_axis.x, finger_axis.y,finger_axis.z])paw_x_list.append(finger_axis.x)paw_y_list.append(finger_axis.y)if landmark_list:# 比例缩放到像素ratio_x_to_pixel = lambda x: math.ceil(x * resize_w)ratio_y_to_pixel = lambda y: math.ceil(y * resize_h)# 设计手掌左上角、右下角坐标paw_left_top_x, paw_right_bottom_x = map(ratio_x_to_pixel,[min(paw_x_list), max(paw_x_list)])paw_left_top_y, paw_right_bottom_y = map(ratio_y_to_pixel,[min(paw_y_list), max(paw_y_list)])# 给手掌画框框cv2.rectangle(self.image, (paw_left_top_x - 30, paw_left_top_y - 30),(paw_right_bottom_x + 30, paw_right_bottom_y + 30), (0, 255, 0), 2)# 获取中指指尖坐标middle_finger_tip = landmark_list[12]middle_finger_tip_x = ratio_x_to_pixel(middle_finger_tip[1])middle_finger_tip_y = ratio_y_to_pixel(middle_finger_tip[2])# 获取食指指尖坐标index_finger_tip = landmark_list[8]index_finger_tip_x = ratio_x_to_pixel(index_finger_tip[1])index_finger_tip_y = ratio_y_to_pixel(index_finger_tip[2])# 中间点between_finger_tip = (middle_finger_tip_x + index_finger_tip_x) // 2, (middle_finger_tip_y + index_finger_tip_y) // 2# print(middle_finger_tip_x)thumb_finger_point = (middle_finger_tip_x, middle_finger_tip_y)index_finger_point = (index_finger_tip_x, index_finger_tip_y)# 画指尖2点circle_func = lambda point: cv2.circle(self.image, point, 10, (255, 0, 255), -1)self.image = circle_func(thumb_finger_point)self.image = circle_func(index_finger_point)self.image = circle_func(between_finger_tip)# 画2点连线self.image = cv2.line(self.image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)# 勾股定理计算长度line_len = math.hypot((index_finger_tip_x - middle_finger_tip_x),(index_finger_tip_y - middle_finger_tip_y))# 将指尖距离映射到文字rect_percent_text = math.ceil(line_len)# 激活模式,需要让矩形跟随移动if squareManager.drag_active:# 更新方块squareManager.updateSquare(between_finger_tip[0], between_finger_tip[1])if (line_len > 100):# 取消激活squareManager.drag_active = FalsesquareManager.active_index = -1elif (line_len < 100) and (squareManager.checkOverlay(between_finger_tip[0],between_finger_tip[1]) != -1) and (squareManager.drag_active == False):# 激活squareManager.drag_active = True# 计算距离squareManager.setLen(between_finger_tip[0], between_finger_tip[1])# 显示方块,传入本实例,主要为了半透明的处理squareManager.display(self)# 显示距离cv2.putText(self.image, "Distance:" + str(rect_percent_text), (10, 120), cv2.FONT_HERSHEY_PLAIN, 3,(255, 0, 0), 3)# 显示当前激活cv2.putText(self.image, "Active:" + ("None" if squareManager.active_index == -1 else str(squareManager.active_index)), (10, 170),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)# 显示刷新率FPScTime = time.time()fps_text = 1 / (cTime - fpsTime)fpsTime = cTimecv2.putText(self.image, "FPS: " + str(int(fps_text)), (10, 70),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)# 显示画面cv2.imshow('virtual drag and drop', self.image)if cv2.waitKey(5) & 0xFF == 27:breakcap.release()control = HandControlVolume()
control.recognize()

相关文章:

OpenCV实现手势虚拟拖拽

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 今天来学习一下如何使用OpenCV实现手势虚拟拖拽&#xff0c;欢迎大家一起前来探讨学习~ 一、主要步骤及库的功能介绍 1.主要步骤 要实现本次实验&#xff0c;主要步骤如下&#xff1a; 导入OpenCV库。通过OpenCV读取摄…...

深圳市宝安区委常委、宣传部部长周学良一行莅临联诚发考察调研

11月9日&#xff0c;深圳市宝安区组织开展主题教育“大走访、大座谈、大起底”行动和调查研究、“基层调研服务日”活动。当日上午&#xff0c;区委常委、宣传部部长周学良率调研组莅临联诚发LCF总部考察调研。区委宣传部副部长孙箫韵&#xff0c;区文化广电旅游体育局党组成员…...

Presentation Prompter 5.4.2(mac屏幕提词器)

Presentation Prompter是一款演讲辅助屏幕提词器软件&#xff0c;旨在帮助演讲者在公共演讲、主持活动或录制视频时更加流畅地进行演讲。以下是Presentation Prompter的一些特色功能&#xff1a; 提供滚动或分页显示&#xff1a;可以将演讲稿以滚动或分页的形式显示在屏幕上&a…...

9 网关的作用

1、总结&#xff1a; 1.如果离开本局域网&#xff0c;就需要经过网关&#xff0c;网关是路由器的一个网口。 2.路由器是一个三层设备&#xff0c;里面有如何寻找下一跳的规则 3.经过路由器之后 MAC 头要变&#xff0c;如果 IP 不变&#xff0c;相当于不换护照的欧洲旅游&#…...

计算机网络实验

计算机网络实验 使用软件PT7.0按照上面的拓扑结构建立网络&#xff0c;进行合理配置&#xff0c;使得所有计算机之间能够互相通信。并且修改各交换机的系统名称为&#xff1a;学号_编号&#xff0c;如你的学号为123&#xff0c;交换机Switch0的编号为0&#xff0c;则系统名称为…...

九凌网络分享外贸快车实现迅速出口的目标

随着全球化进程的不断加速&#xff0c;外贸行业也越来越繁荣。但如何在这个竞争激烈的市场中迅速出口&#xff0c;成为了很多外贸企业家们面临的难题。为此&#xff0c;很多企业开始寻求新的帮助&#xff0c;其中一种办法就是通过专业的外贸快车来提高自己的出口速度。九凌网络…...

分享66个Python管理系统源代码总有一个是你想要的

分享66个Python管理系统源代码总有一个是你想要的 源码下载链接&#xff1a;https://pan.baidu.com/s/1FGmE9Q_NE1-cjjoxU540BQ?pwd8888 提取码&#xff1a;8888 项目名称 automobile-sales-management-system汽车销售管理系统 Python Vue BNUZ教务系统认证爬虫Python语言…...

python 删除特定字符所在行

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 查询文件中含有特殊字符串的行 #!/usr/bin/python # -*- coding:utf-8 -*- import re file1 open(test.txt,r) istxt re.compile(r.*if.*,re.I) for line in file1.readlines():line line.strip()ifstr re.findall(istxt…...

邮箱哪家强?哪个牌子邮箱好用

邮箱在国内外使用情况不太一样&#xff0c;国内一般都是工作中需要用邮箱&#xff0c;直接使用公司发的企业邮箱就可以了&#xff0c;个人一般自己需要使用邮箱频率比较少&#xff0c;大多是用来注册其他平台信息&#xff0c;接受验证码、电子发票等等&#xff0c;使用不频繁。…...

关于DDD的贫血模型和充血模型到底是什么区别?

贫血模型和充血模型是两种不同的设计模式&#xff0c;用于处理复杂的业务逻辑和数据操作。 贫血模型是指将业务逻辑和数据操作分离&#xff0c;业务逻辑在服务层处理&#xff0c;数据操作在数据访问层处理。这种设计模式的优点是易于维护和测试&#xff0c;但是在处理复杂的业…...

让BI自动生成零售数据分析报表?用模板

不知道BI零售数据分析怎么做&#xff1f;用模板。 没时间去整理数据、计算零售数据分析指标&#xff1f;用模板。 不知道怎么做出炫酷直观的零售数据分析报表&#xff1f;用模板。 …… 奥威BI零售数据分析模板全新上线&#xff0c;数据分析模型、数据可视图表、关键指标以…...

以吉祥物宣传片实力出圈!吉祥物三维动画宣传片怎么制作?

首届学青会吉祥物“壮壮”、“美美”在宣传片中展示了举重、打羽毛球、游泳等运动姿态&#xff0c;靠着可爱的虚拟形象萌出圈&#xff01; *图片源于网络 在数字化时代&#xff0c;吉祥物三维动画宣传片已成为众多大型活动、品牌宣发、文旅城市宣传的一大途径&#xff0c;如学…...

TensorFlow(1):深度学习的介绍

1 深度学习与机器学习的区别 学习目标&#xff1a;知道深度学习与机器学习的区别 区别&#xff1a;深度学习没有特征提取 1.1 特征提取方面 机器学习的特征工程步骤是要靠手动完成的&#xff0c;而且需要大量领域专业知识深度学习通常由多个层组成&#xff0c;它们通常将更简…...

C# 如何优雅的写代码[进阶篇]

文章目录 前言相关文章如何让代码优雅知识点补充enum枚举类型?null判定 前言 我之前发布过一些篇章&#xff0c;是专门关于代码优化的&#xff0c;距离我上一次[如何优雅的写C#]已经过去半年时间了&#xff0c;最近我又研究出了一些新东西。 相关文章 如何优雅的写C#&#x…...

【JavaEESpring】Spring, Spring Boot 和Spring MVC的关系以及区别

Spring, Spring Boot 和Spring MVC的关系以及区别 Spring&#xff1a;简单来说&#xff0c;Spring 是一个开发应用框架&#xff0c;什么样的框架呢&#xff1f;轻量级、一站式、模块化&#xff0c;其目的是用于简化企业级应用程序开发 Spring的主要功能: 管理对象, 以及对象之…...

【网络编程】传输层——TCP协议

文章目录 TCP协议TCP协议格式窗口大小六个标志位确认应答机制超时重传机制连接管理机制三次握手四次挥手 流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP的应用层协议TCP与UDP的对比 TCP相关实验CLOSE_WAIT状态实验TIME_WAIT状态实验TI…...

【数据结构与算法】如何衡量一个算法的好坏?

目录 1. 算法复杂度1.1 时间复杂度1.2 大O渐进表示法1.2.1 例子引入推导大O法1.2.2 举例 1.3 空间复杂度1.3.1 细分1.3.2 举例 2.什么是好的算法&#xff1f;3. 影响程序运行时间的因数 1. 算法复杂度 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和内存资源 。…...

在PostgreSQL中创建和管理数据库

PostgreSQL是一个强大、开源的关系型数据库管理系统&#xff0c;它提供了丰富的功能和灵活的配置选项&#xff0c;使得它成为许多开发者和组织的首选数据库之一&#xff0c;接下来我会介绍如何在PostgreSQL中创建和管理数据库。 一、安装和配置PostgreSQL 第一步&#xff0c;…...

从哪些方面做好电商系统的网站建设?

电子商务的迅猛发展&#xff0c;建设一款成功的电商系统网站成为企业取得竞争优势的重要一环。下面将从用户体验、网站设计、安全性和性能优化等方面&#xff0c;介绍如何打造一款优秀的电商系统网站。 一、用户体验 一款成功的电商系统网站必须注重用户体验&#xff0c;确保用…...

C++的Odyssey之旅——STL

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; &#x1f354;前言&#xff1a;我们已经将基本语法了解的差不多了&#xff0c;现在我们就该进入C中最重要也是最富有特点的一部分——STL。在学习C语言中我们想要使用顺序表、链表等一些数据结构进行做题时都需要进行这…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...