项目简介
本项目利用OpenCV和MediaPipe库实现了一个简单的虚拟拖拽演示。通过摄像头捕获手部动作,用户可以通过手指在空中的移动来控制一个方块的位置,从而实现虚拟拖拽的效果。
项目特点
- 实时性:
使用OpenCV读取摄像头视频流,实时捕获手部动作,方便用户实时交互。 - 简单易用: 通过简单的手势,即可实现方块的拖拽,无需额外设备或复杂的操作。
- 灵活性:
用户可以根据需要对项目进行功能扩展,如添加更多手势识别、改变方块的形状或颜色等。
实现步骤:
(1)使用OpenCV读取摄像头视频流。
(2)在视频图像上绘制一个方块。
(3)使用MediaPipe库获取手指关节坐标。
(4)判断手指是否在方块上,若是,则方块跟随手指移动。
(5)通过食指和中指指尖距离确定是否激活移动。
(6)在画面显示FPS等信息,提升用户体验。
使用技术:
- OpenCV: 用于读取摄像头视频流、图像处理和显示。
- MediaPipe: 提供手部关键点检测功能,用于捕获手部动作。
适用场景:
教学演示: 可用于展示手部姿势识别和实时交互技术。
产品展示: 可用于展示虚拟拖拽功能,吸引用户关注。
科研探索: 可用于研究手部动作识别和交互技术的实验验证。
项目用途:
教学示例: 可作为深度学习和计算机视觉教学的案例之一,帮助学生理解手部动作识别和交互技术。
科研实验: 可用于验证手部动作识别算法的准确性和实时性,为科研工作者提供实验数据。
娱乐应用: 可作为娱乐应用的基础功能,如虚拟现实游戏中的手部交互等。
使用说明:
运行项目代码,启动摄像头。
根据屏幕中的方块位置,通过手部动作控制方块的移动。
可根据需要进行功能扩展和修改,实现更多交互效果。
项目演示链接:AI虚拟拖拽演示
案例代码:
"""
演示一个简单的虚拟拖拽
步骤:
1、opencv 读取视频流
2、在视频图像上画一个方块
3、通过mediapipe库获取手指关节坐标
4、判断手指是否在方块上
5、是,方块跟着移动
6、完善:通过食指和中指指尖距离确定是否激活移动
7、完善:画面显示FPS等信息
"""
# 导入opencv
import cv2
import numpy as np
import math
# 导入mediapipe:https://google.github.io/mediapipe/solutions/hands
import mediapipe as mp
# 初始化mediapipe的手部检测模块
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands
# 创建手部检测对象
hands = mp_hands.Hands(
model_complexity=0,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
# 读取视频流
cap = cv2.VideoCapture(0)
# 获取画面宽度、高度
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 方块初始数组
x = 100
y = 100
w = 200
h = 200
# 初始化手指距离变量
L1 = 0
L2 = 0
# 标记方块是否被按住
on_square = False
# 初始化方块颜色,为绿色
square_color = (0, 255, 0)
# 主循环
while True:
# 读取一帧
ret,frame = cap.read()
# 镜像处理
frame = cv2.flip(frame,1)
# 预处理帧,准备进行手部检测
frame.flags.writeable = False
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # BGR-> RGB
# 进行手部检测,需要RGB格式的图像
results = hands.process(frame)
# 还原帧为可写状态,并进行颜色转换为BGR格式
frame.flags.writeable = True
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
# 如果检测到手部
if results.multi_hand_landmarks:
# 遍历每一只手
for hand_landmarks in results.multi_hand_landmarks:
# 绘制手部关键点和连接线
mp_drawing.draw_landmarks(
frame,
hand_landmarks,
mp_hands.HAND_CONNECTIONS,
mp_drawing_styles.get_default_hand_landmarks_style(),
mp_drawing_styles.get_default_hand_connections_style())
# 使用这两句看一下里面到底是什么?
# print(type(hand_landmarks))
# print(hand_landmarks)
# exit()
# 21 个关键点的x,y坐标列表
x_list = []
y_list = []
for landmark in hand_landmarks.landmark:
x_list.append(landmark.x)
y_list.append(landmark.y)
# 输出一下长度
# print(len(x_list))
# 获取食指指尖坐标,坐标位置查看:https://google.github.io/mediapipe/solutions/hands
index_finger_x = int(x_list[8] * width)
index_finger_y = int(y_list[8] * height)
# 获取中指坐标
middle_finger_x = int(x_list[12] * width)
middle_finger_y = int(y_list[12] * height)
# 计算两指间的距离
# finger_distance =math.sqrt( (middle_finger_x - index_finger_x)**2 + (middle_finger_y-index_finger_y)**2)
finger_distance = math.hypot((middle_finger_x - index_finger_x),(middle_finger_y - index_finger_y))
# 看一下距离
# print(finger_distance)
# 把食指指尖画出来
cv2.circle(frame,(index_finger_x,index_finger_y), 10, (0,0,255),-1)
# 判断食指指尖在不在方块上
if finger_distance < 60:
# 判断食指指尖坐标是否在方块的X坐标范围 Y坐标范围内
if (index_finger_x > x and index_finger_x < (x+w)) and (index_finger_y > y and index_finger_y < (y+h)):
print('~食指指尖在方块区域内~')
# 方块原始颜色未改变,则激活
if on_square == False:
L1 = index_finger_x - x # > 0 < L1 < 200
L2 = index_finger_y - y # > 0 < L2 < 200
square_color = (255, 0, 255)
on_square = True
else:
print('~食指指尖不在方块区域内~')
# 食指与中指夹距离大于60,则方块颜色不改变
else:
# 解除
on_square = False
square_color = (0,255,0)
# 更新坐标
if on_square:
x = index_finger_x - L1 # 方块的左上角坐标随着食指手指移动
y = index_finger_y - L2 # 方块的左上角坐标随着食指手指移动
# 画一个正方形,需要实心
# cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),-1)
# 半透明处理
overlay = frame.copy()
cv2.rectangle(frame,(x,y),(x+w,y+h),square_color,-1)
frame = cv2.addWeighted(overlay, 0.5, frame, 1 - 0.5, 0)
# 显示画面
cv2.imshow('demo',frame)
if cv2.waitKey(10) & 0xFF == 27 : # 按esc 退出
break
cap.release()
cv2.destroyAllWindows()
注意事项:
本项目仅为演示用途,未经过完整测试和优化,可能存在一定的局限性和不足之处。
如需商业应用或其他用途,请谨慎使用并自行承担风险。