徐德智联合国展示手势截屏
请看视频!灵感来源。
一、核心功能
实时手势识别:通过 USB 摄像头捕获手部画面,识别手掌的张合状态(从握拳到张开的动作)。
全屏截图触发:当检测到 "从握拳到完全张开" 的手势时,自动截取电脑全屏并保存。
中文界面提示:在摄像头预览窗口中显示中文操作提示(如 "全屏截图已保存"),解决 OpenCV 默认不支持中文的问题。
截图管理:所有截图自动保存在程序目录下的screenshots文件夹中,文件名包含时间戳,避免重复。
二、技术原理
整个程序的工作流程可分为 5 个核心步骤,涉及计算机视觉、手势识别和系统交互技术:
- 摄像头画面捕获
使用OpenCV库的cv2.VideoCapture(0)调用电脑默认 USB 摄像头,实时获取视频帧(每帧为一张图像)。
视频帧格式为 BGR(OpenCV 默认格式),后续需要转换为 RGB 格式供手势识别模型使用。
- 手势识别与关键点提取
依赖库:使用 Google 的MediaPipe库实现手部检测,该库提供了预训练的手势识别模型,能高效识别手掌 21 个关键点(如指尖、关节、手腕等)。
关键点检测:
模型处理 RGB 格式的图像,输出手部关键点的坐标(归一化到 0-1 范围,与图像尺寸无关)。
程序中绘制了关键点及连接关系(如mp_drawing.draw_landmarks),方便用户在预览窗口中看到识别效果。
- 手的张合程度计算
核心逻辑:通过计算指尖到手腕的距离变化,判断手的张合状态。
选取 5 个指尖(拇指、食指、中指、无名指、小指)和手腕的关键点。
计算每个指尖到手腕的欧氏距离(calculate_distance函数),取平均值作为参考。
归一化处理:
将平均距离映射到 0-1 范围(detect_hand_openness函数):
0 表示握拳(指尖到手腕距离最小)。
1 表示完全张开(指尖到手腕距离最大)。
经验值max_distance(0.3)和min_distance(0.1)可根据用户手型调整,优化识别精度。
- 截图触发条件
检测手势状态的 "突变":当张合程度从小于 0.3(握拳)突然变为大于 0.7(完全张开)时,触发截图(避免误操作)。
使用pyautogui.screenshot()实现全屏截图,该函数与操作系统无关,可在 Windows/macOS/Linux 上直接获取整个屏幕画面。
解决方法:
自定义put_chinese_text函数,借助matplotlib库绘制中文(matplotlib支持中文字体配置)。
将 OpenCV 的 BGR 图像转换为matplotlib支持的 RGB 格式,绘制中文后再转回 BGR 格式,确保与 OpenCV 窗口兼容。
配置支持中文的字体库(如 SimHei、WenQuanYi Micro Hei),保证不同系统下的兼容性。
三、程序结构说明
初始化部分:
配置 MediaPipe 手势识别模型参数(检测置信度、跟踪置信度等)。
创建screenshots文件夹用于保存截图。
核心函数:
calculate_distance:计算两点间欧氏距离。
detect_hand_openness:计算手的张合程度(0-1)。
capture_full_screen:调用pyautogui实现全屏截图。
put_chinese_text:解决中文显示乱码问题。
主循环(main函数):
实时读取摄像头画面,处理并识别手势。
检测张合状态变化,触发截图并保存。
在预览窗口显示中文提示和识别结果,按q键退出程序。
四、实现代码
import cv2
import mediapipe as mp
import numpy as np
import os
import pyautogui
from datetime import datetime
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
# 初始化MediaPipe手势识别
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
# 手势关键点索引
THUMB_TIP = 4 # 拇指指尖
INDEX_FINGER_TIP = 8 # 食指指尖
MIDDLE_FINGER_TIP = 12 # 中指指尖
RING_FINGER_TIP = 16 # 无名指指尖
PINKY_TIP = 20 # 小指指尖
WRIST = 0 # 手腕
# 创建截图保存目录
if not os.path.exists('screenshots'):
os.makedirs('screenshots')
def calculate_distance(point1, point2):
"""计算两个点之间的欧氏距离"""
return np.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2)
def detect_hand_openness(hand_landmarks):
"""检测手的张合程度,返回0-1之间的值,1表示完全张开,0表示握拳"""
# 获取关键点
wrist = hand_landmarks.landmark[WRIST]
thumb_tip = hand_landmarks.landmark[THUMB_TIP]
index_tip = hand_landmarks.landmark[INDEX_FINGER_TIP]
middle_tip = hand_landmarks.landmark[MIDDLE_FINGER_TIP]
ring_tip = hand_landmarks.landmark[RING_FINGER_TIP]
pinky_tip = hand_landmarks.landmark[PINKY_TIP]
# 计算各指尖到手腕的距离
wrist_thumb_dist = calculate_distance(wrist, thumb_tip)
wrist_index_dist = calculate_distance(wrist, index_tip)
wrist_middle_dist = calculate_distance(wrist, middle_tip)
wrist_ring_dist = calculate_distance(wrist, ring_tip)
wrist_pinky_dist = calculate_distance(wrist, pinky_tip)
# 平均距离作为张开程度的度量
avg_distance = (wrist_thumb_dist + wrist_index_dist +
wrist_middle_dist + wrist_ring_dist + wrist_pinky_dist) / 5
# 归一化到0-1范围(可根据实际情况调整)
max_distance = 0.3 # 完全张开时的最大距离(经验值)
min_distance = 0.1 # 握拳时的最小距离(经验值)
openness = (avg_distance - min_distance) / (max_distance - min_distance)
return max(0, min(1, openness)) # 确保值在0-1范围内
def capture_full_screen():
"""捕获电脑全屏并返回截图对象"""
screenshot = pyautogui.screenshot()
return screenshot
def put_chinese_text(img, text, position, font_size=1, color=(0, 255, 0)):
# 创建一个与原图大小相同的matplotlib图形
fig = plt.figure(figsize=(img.shape[1] / 100, img.shape[0] / 100), dpi=100)
canvas = FigureCanvasAgg(fig)
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')
# 转换为RGB格式(matplotlib使用RGB)
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
ax.imshow(rgb_img)
# 设置中文字体
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
ax.text(position[0], position[1], text, fontsize=font_size * 10,
color=(color[2] / 255, color[1] / 255, color[0] / 255), # 转换为RGB
bbox=dict(facecolor='none', edgecolor='none'))
# 绘制并转换回OpenCV格式
canvas.draw()
buf = canvas.tostring_rgb()
result_img = np.frombuffer(buf, dtype=np.uint8).reshape(img.shape[0], img.shape[1], 3)
result_img = cv2.cvtColor(result_img, cv2.COLOR_RGB2BGR)
plt.close(fig) # 关闭图形释放资源
return result_img
def main():
# 初始化摄像头
cap = cv2.VideoCapture(0) # 0表示默认摄像头
# 手势状态变量
prev_openness = 0
screenshot_requested = False
screenshot_count = 0
with mp_hands.Hands(
model_complexity=1,
min_detection_confidence=0.7,
min_tracking_confidence=0.7) as hands:
while cap.isOpened():
success, image = cap.read()
if not success:
print("无法获取摄像头画面。")
continue
# 转换为RGB并处理图像
image.flags.writeable = False
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = hands.process(image_rgb)
# 绘制手势标记并检测张合状态
image.flags.writeable = True
image = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR)
openness = 0
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 绘制手部关键点
mp_drawing.draw_landmarks(
image,
hand_landmarks,
mp_hands.HAND_CONNECTIONS,
mp_drawing_styles.get_default_hand_landmarks_style(),
mp_drawing_styles.get_default_hand_connections_style())
# 计算张开程度
openness = detect_hand_openness(hand_landmarks)
# 检测手势张合变化(从闭合到张开)
if openness > 0.7 and prev_openness < 0.3:
screenshot_requested = True
screenshot_count += 1
prev_openness = openness
# 如果请求截图,则捕获全屏并保存
if screenshot_requested:
# 捕获全屏
full_screen_img = capture_full_screen()
# 保存截图
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"screenshots/fullscreen_{timestamp}_{screenshot_count}.png"
full_screen_img.save(filename)
print(f"全屏截图已保存: {filename}")
# 显示中文提示(使用自定义函数)
image = put_chinese_text(image, "全屏截图已保存!", (50, 50), 1, (0, 255, 0))
screenshot_requested = False
# 显示张开程度和操作提示(使用自定义中文绘制函数)
image = put_chinese_text(image, f"张开程度: {openness:.2f}", (10, 30), 1, (0, 255, 0))
image = put_chinese_text(image, "将手从握拳状态张开以截取全屏", (10, image.shape[0] - 20), 0.7, (255, 0, 0))
image = put_chinese_text(image, "按 'q' 退出", (image.shape[1] - 150, image.shape[0] - 20), 0.7,
(0, 0, 255))
# 显示摄像头画面(用于手势识别预览)
cv2.imshow('手势控制全屏截图', image)
# 按q退出
if cv2.waitKey(5) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
五、效果展示

手势识别效果展示
六、使用场景与扩展
适用场景:演示、教学、游戏等需要快速截图的场景,无需手动按快捷键(如PrtSc)。
扩展方向:
不同手势下设备间蓝牙互联进行手势滑动传输。
增加更多手势(如比 "OK" 手势截取窗口、挥手翻页等)。
优化张合程度的阈值参数,适应不同光线和手型。
增加截图预览或编辑功能。