目录
流程说明
导入所需的库
初始化Mediapipe的手部检测模块
键盘控制器
打开摄像头
定义虚拟键盘的按键布局
记录每个按键的上次按压时间
显示虚拟键盘的函数
检测手指是否按压按键的函数
主循环
释放资源
总结
代码实现
效果展示
这段代码展示了如何使用摄像头、Mediapipe和OpenCV创建一个虚拟键盘,用户可以通过手部动作来“按压”虚拟键盘上的按键。代码通过检测手部位置和动作来确定哪个按键被按压,然后模拟实际的键盘输入。
流程说明
导入所需的库
import cv2
import mediapipe as mp
import time
from pynput.keyboard import Controller
cv2
:OpenCV库,用于图像处理和计算机视觉。mediapipe
:Mediapipe库,用于手部检测和跟踪。time
:Python内置的时间库,用于记录和计算时间。pynput.keyboard.Controller
:用于模拟键盘输入。
初始化Mediapipe的手部检测模块
# 初始化Mediapipe的手部检测模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.7)
mp_drawing = mp.solutions.drawing_utils
mp_hands
:初始化Mediapipe的手部检测模块。hands
:创建一个手部检测对象,设置最小检测和跟踪置信度为0.7。mp_drawing
:用于在图像上绘制手部关键点和连接线。
键盘控制器
keyboard = Controller()
keyboard
:创建一个键盘控制器对象,用于模拟键盘输入。
打开摄像头
cap = cv2.VideoCapture(0)
cap
:打开默认摄像头。
定义虚拟键盘的按键布局
# 定义虚拟键盘的按键布局
keys = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],['Z', 'X', 'C', 'V', 'B', 'N', 'M'],[' ', 'BACKSPACE']
]
keys
:定义虚拟键盘的按键布局,每个元素表示一行按键。
记录每个按键的上次按压时间
key_press_times = {key: 0 for row in keys for key in row}
key_press_times
:记录每个按键的上次按压时间,以防止在短时间内重复按压。
显示虚拟键盘的函数
def draw_keyboard(image):"""在图像上绘制虚拟键盘"""for i, row in enumerate(keys):for j, key in enumerate(row):# 计算按键的x坐标x = j * 60 + 50# 计算按键的y坐标y = i * 60 + 150# 绘制按键矩形cv2.rectangle(image, (x, y), (x + 50, y + 50), (255, 255, 255), -1)# 绘制按键文本cv2.putText(image, key, (x + 15, y + 35), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
draw_keyboard(image)
:在图像上绘制虚拟键盘。- 遍历每一行的按键,计算每个按键的位置并绘制按键矩形和文字。
检测手指是否按压按键的函数
def detect_key_press(landmarks, image):"""检测手指是否按压按键"""# 食指指尖index_finger_tip = landmarks[8]# 其他手指指尖other_fingers_tips = [landmarks[i] for i in [4, 12, 16, 20]]# 食指指尖的z坐标index_z = index_finger_tip.z# 其他手指指尖的z坐标other_z = [tip.z for tip in other_fingers_tips]# 检测食指是否显著低于其他手指(z轴坐标显著不同)if all(index_z < z - 0.004 for z in other_z):# 转换食指坐标到图像坐标x, y = int(index_finger_tip.x * image.shape[1]), int(index_finger_tip.y * image.shape[0])# 获取当前时间current_time = time.time()for i, row in enumerate(keys):for j, key in enumerate(row):# 计算按键的x坐标key_x = j * 60 + 50# 计算按键的y坐标key_y = i * 60 + 150# 检查食指坐标是否在按键范围内if key_x < x < key_x + 50 and key_y < y < key_y + 50:# 检查按键是否在1秒内被按压过if current_time - key_press_times[key] > 1:# 更新按键的上次按压时间key_press_times[key] = current_time# 绘制按压效果cv2.rectangle(image, (key_x, key_y), (key_x + 50, key_y + 50), (0, 255, 0), -1)cv2.putText(image, key, (key_x + 15, key_y + 35), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)print(f'Key {key} pressed')# 模拟键盘输入if key == ' ':keyboard.press(' ')keyboard.release(' ')elif key == 'BACKSPACE':keyboard.press('\b')keyboard.release('\b')else:keyboard.press(key)keyboard.release(key)
detect_key_press(landmarks, image)
:检测手指是否按压按键。index_finger_tip
:获取食指指尖的位置。other_fingers_tips
:获取其他手指指尖的位置。- 通过比较食指和其他手指的z轴坐标,检测食指是否显著低于其他手指(表示按压动作)。
- 计算食指在图像上的位置,遍历按键,检查食指是否在某个按键的范围内。
- 如果按键在1秒内未被按压过,绘制按压效果并模拟键盘输入。
主循环
while True:# 读取摄像头图像success, image = cap.read()if not success:break# 翻转图像image = cv2.flip(image, 1)# 转换为RGB格式image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 处理图像,检测手部results = hands.process(image_rgb)if results.multi_hand_landmarks:for hand_landmarks in results.multi_hand_landmarks:# 绘制手部连接线mp_drawing.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS)landmarks = hand_landmarks.landmark# 检测按压detect_key_press(landmarks, image)# 绘制虚拟键盘draw_keyboard(image)# 显示图像cv2.imshow('Virtual Keyboard', image)# 按下ESC键退出if cv2.waitKey(1) & 0xFF == 27:break
- 在主循环中,代码不断读取摄像头图像并翻转图像以获得镜像效果。
- 将图像从BGR格式转换为RGB格式,以便Mediapipe处理。
- 使用Mediapipe检测手部位置,并绘制手部连接线。
- 检测手指是否按压按键,并绘制虚拟键盘。
- 显示图像,并检查是否按下ESC键以退出循环。
释放资源
cap.release() # 释放摄像头 cv2.destroyAllWindows() # 销毁所有窗口
- 释放摄像头资源并销毁所有OpenCV窗口。
总结
这段代码展示了如何使用摄像头捕捉手部动作,通过手部检测和位置跟踪来模拟按压虚拟键盘上的按键。代码通过OpenCV绘制虚拟键盘,并通过pynput模拟实际的键盘输入。这种技术可以用于各种交互式应用,如虚拟输入设备和手势控制系统。
还是挺简单的嘛。
代码实现
下面的是完整的全部代码,下载完对应的Python包即可运行。
import cv2
import mediapipe as mp
import time
from pynput.keyboard import Controller# 初始化Mediapipe的手部检测模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.7)
mp_drawing = mp.solutions.drawing_utils# 键盘控制器,用于模拟键盘输入
keyboard = Controller()# 打开摄像头
cap = cv2.VideoCapture(0)# 定义虚拟键盘的按键布局
keys = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],['Z', 'X', 'C', 'V', 'B', 'N', 'M'],[' ', 'BACKSPACE']
]# 记录每个按键的上次按压时间,以防止在1秒内重复按压
key_press_times = {key: 0 for row in keys for key in row}# 显示虚拟键盘的函数
def draw_keyboard(image):"""在图像上绘制虚拟键盘"""for i, row in enumerate(keys):for j, key in enumerate(row):# 计算按键的x坐标x = j * 60 + 50 # 计算按键的y坐标y = i * 60 + 150 # 绘制按键矩形cv2.rectangle(image, (x, y), (x + 50, y + 50), (255, 255, 255), -1) # 绘制按键文本cv2.putText(image, key, (x + 15, y + 35), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2) # 检测手指是否按压按键的函数
def detect_key_press(landmarks, image):"""检测手指是否按压按键"""# 食指指尖index_finger_tip = landmarks[8] # 其他手指指尖other_fingers_tips = [landmarks[i] for i in [4, 12, 16, 20]] # 食指指尖的z坐标index_z = index_finger_tip.z # 其他手指指尖的z坐标other_z = [tip.z for tip in other_fingers_tips] # 检测食指是否显著低于其他手指(z轴坐标显著不同)if all(index_z < z - 0.004 for z in other_z):# 转换食指坐标到图像坐标x, y = int(index_finger_tip.x * image.shape[1]), int(index_finger_tip.y * image.shape[0]) # 获取当前时间current_time = time.time() for i, row in enumerate(keys):for j, key in enumerate(row):# 计算按键的x坐标key_x = j * 60 + 50 # 计算按键的y坐标key_y = i * 60 + 150 # 检查食指坐标是否在按键范围内if key_x < x < key_x + 50 and key_y < y < key_y + 50:# 检查按键是否在1秒内被按压过if current_time - key_press_times[key] > 1:# 更新按键的上次按压时间key_press_times[key] = current_time # 绘制按压效果cv2.rectangle(image, (key_x, key_y), (key_x + 50, key_y + 50), (0, 255, 0), -1)cv2.putText(image, key, (key_x + 15, key_y + 35), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)print(f'Key {key} pressed')# 模拟键盘输入if key == ' ':keyboard.press(' ')keyboard.release(' ')elif key == 'BACKSPACE':keyboard.press('\b')keyboard.release('\b')else:keyboard.press(key)keyboard.release(key)while True:# 读取摄像头图像success, image = cap.read() if not success:break# 翻转图像image = cv2.flip(image, 1) # 转换为RGB格式image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 处理图像,检测手部results = hands.process(image_rgb) if results.multi_hand_landmarks:for hand_landmarks in results.multi_hand_landmarks:# 绘制手部连接线mp_drawing.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS) landmarks = hand_landmarks.landmark# 检测按压detect_key_press(landmarks, image) # 绘制虚拟键盘draw_keyboard(image) # 显示图像cv2.imshow('Virtual Keyboard', image) # 按下ESC键退出if cv2.waitKey(1) & 0xFF == 27: break# 释放摄像头
cap.release()
# 销毁所有窗口
cv2.destroyAllWindows()