python实现电脑手势识别截图

徐德智联合国展示手势截屏

请看视频!灵感来源。

一、核心功能

实时手势识别:通过 USB 摄像头捕获手部画面,识别手掌的张合状态(从握拳到张开的动作)。

全屏截图触发:当检测到 "从握拳到完全张开" 的手势时,自动截取电脑全屏并保存。

中文界面提示:在摄像头预览窗口中显示中文操作提示(如 "全屏截图已保存"),解决 OpenCV 默认不支持中文的问题。

截图管理:所有截图自动保存在程序目录下的screenshots文件夹中,文件名包含时间戳,避免重复。

二、技术原理

整个程序的工作流程可分为 5 个核心步骤,涉及计算机视觉、手势识别和系统交互技术:

  1. 摄像头画面捕获

使用OpenCV库的cv2.VideoCapture(0)调用电脑默认 USB 摄像头,实时获取视频帧(每帧为一张图像)。

视频帧格式为 BGR(OpenCV 默认格式),后续需要转换为 RGB 格式供手势识别模型使用。

  1. 手势识别与关键点提取

依赖库:使用 Google 的MediaPipe库实现手部检测,该库提供了预训练的手势识别模型,能高效识别手掌 21 个关键点(如指尖、关节、手腕等)。

关键点检测:

模型处理 RGB 格式的图像,输出手部关键点的坐标(归一化到 0-1 范围,与图像尺寸无关)。

程序中绘制了关键点及连接关系(如mp_drawing.draw_landmarks),方便用户在预览窗口中看到识别效果。

  1. 手的张合程度计算

核心逻辑:通过计算指尖到手腕的距离变化,判断手的张合状态。

选取 5 个指尖(拇指、食指、中指、无名指、小指)和手腕的关键点。

计算每个指尖到手腕的欧氏距离(calculate_distance函数),取平均值作为参考。

归一化处理:

将平均距离映射到 0-1 范围(detect_hand_openness函数):

0 表示握拳(指尖到手腕距离最小)。

1 表示完全张开(指尖到手腕距离最大)。

经验值max_distance(0.3)和min_distance(0.1)可根据用户手型调整,优化识别精度。

  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" 手势截取窗口、挥手翻页等)。

优化张合程度的阈值参数,适应不同光线和手型。

增加截图预览或编辑功能。

相关推荐
奇树谦2 小时前
Qt 自定义菜单栏 / 工具栏按钮 QToolButton + InstantPopup 详细解析
开发语言·数据库·qt
草莓熊Lotso2 小时前
Git 本地操作入门:版本控制基础、跨平台部署与仓库核心流程
开发语言·人工智能·经验分享·git·后端·架构·gitee
CS_浮鱼2 小时前
【C++进阶】异常
开发语言·c++
百锦再2 小时前
大话Rust的前生今世
开发语言·后端·rust·go·内存·时间·抽象
QT 小鲜肉2 小时前
【C++基础与提高】第十一章:面向对象编程进阶——继承与多态
java·linux·开发语言·c++·笔记·qt
shixian10304112 小时前
conda安装Django+pg运行环境
python·django·conda
jiaoxingk2 小时前
Django 接口文档生成:Swagger 与 ReDoc 全面说明
python·django
点云SLAM3 小时前
Boost库中Boost.PropertyTree使用和实战示例
开发语言·c++·josn·boost库·参数读取