OpenCV实战:控制手势实现无触摸拖拽功能

前言: Hello大家好,我是Dream。 今天来学习一下如何使用OpenCV来控制手势,瞬间提升操作体验!跨越界限,OpenCV手势控制拖拽功能现身。

在这里插入图片描述

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

1.主要步骤

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

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

二、导入所需要的模块

# 导入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()

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis缓存问题解决方案

Redis缓存问题解决方案 为什么使用Redis缓存&#xff1a; 1.在高并发的情况下&#xff0c;大量查询进入数据库&#xff0c;会大量占用数据库的连接&#xff08;默认数据库连接数151&#xff09;,数据库压力过大就会出现connection refuse(数据库连接拒绝)问题&#xff0c; 2.…

Codeforces Round 895 (Div. 3)补题

Two Vessels&#xff08;Problem - A - Codeforces&#xff09; 题目大意&#xff1a;有两个无限容器&#xff0c;目前一个容器中有a克水&#xff0c;另一个容器中有b克水&#xff0c;现有一个大小为cg的容器&#xff0c;我们每次可以从一个无限容器中取任意不大于c克的水&…

android中.9图如何制作

在工作中我们需要做界面往往需要设计师给我们提供图标&#xff0c;我们才能把界面友好的显示出来&#xff0c;普通的图标用png、jpg等都可以显示出来&#xff0c;为了让界面更与众不同&#xff0c;设计师们通常会设计一些弯角、弯钩如果我们要作为背景使用的话就不能设置高度&a…

【 Qt 快速上手】-①- Qt 背景介绍与发展前景

文章目录 1.1 什么是 Qt1.2 Qt 的发展史1.3 Qt 支持的平台1.4 Qt 版本1.5 Qt 的优点1.6 Qt的应用场景1.7 Qt的成功案例1.8 Qt的发展前景及就业分析行业发展方向就业方面的发展前景 1.1 什么是 Qt Qt 是一个跨平台的 C 图形用户界面应用程序框架。它为应用程序开发者提供了建立…

349. 两个数组的交集(力扣)(OJ题)

题目链接&#xff1a;349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 个人博客主页&#xff1a;https://blog.csdn.net/2301_79293429?typeblog 专栏&#xff1a;https://blog.csdn.net/2301_79293429/category_12545690.html 给定两个数组 nums1 和 nums2 &a…

协议网关BL110轻松实现多种协议转MQTT、OPC UA,支持8种主流工业协议转换

随着工业4.0的迅猛发展&#xff0c;人们深刻认识到在工业生产和生活中&#xff0c;实时、可靠、安全的数据传输至关重要。而在工厂自动化领域&#xff0c;工业设备之间的联动控制离不开各种工业通信协议。在此背景下&#xff0c;高性能的工业自动化数据传输解决方案——协议转换…

mac查看maven版本报错:The JAVA_HOME environment variable is not defined correctly

终端输入mvn -version报错: The JAVA_HOME environment variable is not defined correctly, this environment variable is needed to run this program. Java环境变量的问题,打开bash_profile查看 open ~/.bash_profile export JAVA_8_HOME/Library/Java/JavaVirtualMachine…

心跳检测与服务剔除

社保中心的忧桑 今天社保中心来了一位钉子户&#xff0c;90多岁的王大爷又兴高采烈的来给自己快120岁的老父亲领社保了! 工作人员这一-想&#xff0c;好像哪里不对啊&#xff0c;这老父亲120岁的年纪都可以上吉尼斯世界纪录了&#xff0c;要不咱帮老爷子去申请一下?王大爷一听…

vue3-侦听器

侦听器 计算属性允许我们声明性地计算衍生值。 需求在状态变化时进行一些操作&#xff0c;比如更改 Dom,根据异步操作结果去修改另外的数据状态。 watch 监听异步请求结果 <script lang"ts" setup> import { ref, watch } from "vue"const ques…

开发实践8_REST

一、Django REST Framework, Django View & APIView MTV模式实现前后端分离。Representational State Transfer 表现层状态转化。Representation 资源&#xff08;Resource a specific info. on net.&#xff09;具体呈现形式。ST 修改服务端的数据。修改数据 POST请求。…

用VSCode玩STM32的烧录工具 CooCox Cortex Flash Programmer

一、下载软件 经热心兄弟推荐的版本&#xff0c;不知道有没有版权&#xff0c;如有版权问题&#xff0c;请通知删除。 CSDN - 0积分下载&#xff1a;https://download.csdn.net/download/qq_49053936/88744187 二、生成bin文件 插件不同&#xff0c;方法有所不同&#xff0c;各…

云轴科技ZStack位列IDC云系统软件市场教育行业TOP2

近日&#xff0c;全球IT市场研究和咨询公司IDC发布 《中国云系统软件市场跟踪报告2023H1》 ZStack作为产品化的云基础软件提供商 位居云系统软件市场第一梯队 市场份额位列独立云厂商*第一 增速最快 教育行业TOP2 在教育行业&#xff0c;云计算已成为教育行业信息化的重要基础…