Python+OpenCV实现用MediaPipe手势数字识别一键打开下载夹里的图片

 更新时间:2026年06月15日 08:18:04   作者:winfredzhang  
本文介绍了一个基于MediaPipe Tasks的手势识别工具,通过摄像头识别手势数字(1-5)自动打开电脑下载文件夹中对应的最新PNG图片,感兴趣的小伙伴可以了解下

一、想要做的事

我电脑屏幕上经常一边写代码一边盯刚下载的截图。每次都要打开"下载"文件夹、按时间排序、双击 ——这个动作太小却很烦。

那干脆让摄像头来干:

  • 比划"1" → 自动打开下载夹里最新的那张 PNG;
  • 比划"2" → 打开第二新的那张;
  • ……一直到 5;
  • 握拳(0 根手指)= 解除武装,准备下一次触发。

最终效果是一个 200 行不到的 Python 脚本,靠 MediaPipe 的 HandLandmarker 模型识别 21 个关键点,再用一条非常朴素的几何规则数手指。

本文从 0 到 1 拆开它,讲清四件事:

  1. MediaPipe Tasks 在 Python 里到底怎么用?LIVE_STREAM 模式和回调长什么样?
  2. 给定 21 个关键点,怎么数"伸出几根手指"?
  3. 怎样防止识别抖动一下就疯狂连发?
  4. Windows 下用 os.startfile 调起默认看图程序的小坑。

二、环境准备

MediaPipe 当前对 Python 3.13 还没有发布的 wheel,必须用 3.10(3.11/3.12 也行,但 3.10 是最稳的)。我直接装在虚拟环境里:

py -3.10 -m venv venv
venv\Scripts\activate
pip install mediapipe opencv-python pillow numpy

模型文件自己下一次到本地

https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/latest/hand_landmarker.task

放到项目根目录的 models/hand_landmarker.task。整体目录长这样:

MediaPipe/
├── models/hand_landmarker.task
├── demo_hand_landmarker.py
└── venv/

三、MediaPipe Tasks 的最小可用骨架

新版 MediaPipe(0.10.x)已经全面切到 Tasks API,老博客里的 mp.solutions.hands 在 0.10.35 之后不存在了。所以请直接用:

import mediapipe as mp
from mediapipe.tasks import python as mp_python
from mediapipe.tasks.python import vision

options = vision.HandLandmarkerOptions(
    base_options=mp_python.BaseOptions(model_asset_path="models/hand_landmarker.task"),
    running_mode=vision.RunningMode.LIVE_STREAM,   # 摄像头流式推理
    num_hands=1,
    min_hand_detection_confidence=0.5,
    min_hand_presence_confidence=0.5,
    min_tracking_confidence=0.5,
    result_callback=on_result,                     # 异步结果回调
)

LIVE_STREAM 模式下,detect_async() 立刻返回,结果通过你注册的 result_callback 异步给你。这意味着主循环千万不要在调用之后阻塞等结果——直接读"上一次最新结果"就好:

class LatestResult:
    def __init__(self):
        self.result = None

latest = LatestResult()

def on_result(result, output_image, timestamp_ms):
    latest.result = result

主循环里每帧把 BGR→RGB 包成 mp.Image,再带一个单调递增的时间戳调 detect_async

start_ns = time.perf_counter_ns()
# ...
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb)
ts_ms = (time.perf_counter_ns() - start_ns) // 1_000_000
landmarker.detect_async(mp_image, ts_ms)

时间戳必须递增,否则 MediaPipe 会直接抛错把帧丢掉。这是新手最容易踩的坑之一。

四、HandLandmarker 的输出长什么样

模型给你的是每只手 21 个 3D 归一化坐标(x,y 在 [0,1],z 是相对于手腕的深度)。索引按官方文档:

0   WRIST
1-4 THUMB
5-8 INDEX
9-12 MIDDLE
13-16 RING
17-20 PINKY

每根手指有 4 个点:MCP(指根)、PIP、DIP、TIP(指尖)。这次我们只关心 TIP 和 PIP。

绘制的连线表(之前从 mp.solutions.hands.HAND_CONNECTIONS 拿,现在自己写死):

HAND_CONNECTIONS = (
    (0,1),(1,2),(2,3),(3,4),
    (0,5),(5,6),(6,7),(7,8),
    (5,9),(9,10),(10,11),(11,12),
    (9,13),(13,14),(14,15),(15,16),
    (13,17),(0,17),(17,18),(18,19),(19,20),
)

五、最朴素的"数手指"算法

很多教程会比较 TIP 和 PIP 的 y 坐标,但这只有在手"竖着伸"的时候才对。一旦手歪了或者反过来就崩。

更鲁棒的判定是:TIP 离 WRIST 的距离 > PIP 离 WRIST 的距离 × 1.1,那这根手指就是伸直的。它跟方向无关,因为屈指的时候 TIP 会被卷向手腕,距离一定变小。

FINGER_TIPS = (4, 8, 12, 16, 20)
FINGER_PIPS = (3, 6, 10, 14, 18)

def count_extended_fingers(landmarks) -> int:
    wrist = landmarks[0]
    def d(a, b):
        return math.hypot(a.x - b.x, a.y - b.y)
    n = 0
    for tip, pip in zip(FINGER_TIPS, FINGER_PIPS):
        if d(landmarks[tip], wrist) > d(landmarks[pip], wrist) * 1.1:
            n += 1
    return n

20 行不到,对所有方向都鲁棒。1.1 是经验阈值,太小会把弯指误判为伸直,太大会漏掉刚伸出的手指。

六、抖动处理:稳定窗口 + armed 状态机

如果直接拿 count_extended_fingers 的瞬时值去开图,识别抖一下立刻连开五张图,体验崩溃。两步处理:

第一步:连续 N 帧都是同一个值才算"稳定"。我用 deque(maxlen=10),10 帧 ≈ 0.3-0.5 秒:

history = deque(maxlen=10)
history.append(cur_n)
stable = history[0] if len(history) == history.maxlen and all(v == history[0] for v in history) else None

第二步:触发后必须先回到 0 才能再触发——一个标志位 armed

if stable == 0:
    armed = True
elif stable is not None and 1 <= stable <= 5 and armed:
    open_nth_png(stable)
    armed = False
    history.clear()  # 清空缓冲,防止同一稳定段被重复识别

这就是 Game UI 里常说的"边沿触发",不是"电平触发"。代价只有一句 armed = False,效果极好。

七、按时间倒序定位"第 N 新"的 PNG

Python 标准库就够:

DOWNLOADS_DIR = Path.home() / "Downloads"

def open_nth_png(n: int):
    files = sorted(
        DOWNLOADS_DIR.glob("*.png"),
        key=lambda p: p.stat().st_mtime,
        reverse=True,
    )
    if len(files) < n:
        return False, f"Only {len(files)} PNG(s), need #{n}"
    target = files[n - 1]
    os.startfile(str(target))    # Windows 专属:相当于双击
    return True, f"Opened #{n}: {target.name}"

os.startfile 在 Linux/macOS 没有,要写跨平台的话用 subprocess.run(["open", path])(macOS)/xdg-open(Linux),不过我这是个人 Windows 工具就懒得抽象了。

八、画面镜像与左右手反转

cv2.flip(frame, 1) 让画面像照镜子一样自然,但 MediaPipe 拿到的是镜像后的图,它返回的 Left/Right 也跟实际相反。一句话补回来:

raw = result.handedness[hand_idx][0].category_name
label = "Right" if raw == "Left" else "Left"

handedness 这个字段名我每次都拼错,提醒自己。

九、完整主循环

把上面零件拼起来:

with vision.HandLandmarker.create_from_options(options) as landmarker:
    while True:
        ok, frame = cap.read()
        if not ok:
            break
        frame = cv2.flip(frame, 1)
        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb)
        ts_ms = (time.perf_counter_ns() - start_ns) // 1_000_000
        landmarker.detect_async(mp_image, ts_ms)

        res = latest.result
        cur_n = count_extended_fingers(res.hand_landmarks[0]) if (res and res.hand_landmarks) else 0
        history.append(cur_n)
        stable = history[0] if len(history) == history.maxlen \
                 and all(v == history[0] for v in history) else None

        if stable == 0:
            armed = True
        elif stable is not None and 1 <= stable <= 5 and armed:
            ok2, msg = open_nth_png(stable)
            armed = False
            history.clear()

        draw_landmarks(frame, res)
        cv2.imshow("Gesture -> Open PNG", frame)
        if cv2.waitKey(1) & 0xFF in (ord("q"), 27):
            break

十、几个一定会遇到的坑

  1. No module named 'mediapipe':99% 是没激活虚拟环境,或者用的是系统 Python 3.13。先 where python 确认。
  2. module 'mediapipe' has no attribute 'solutions':你装的是 0.10.x,老 API 已经下线,请用 mediapipe.tasks.python.vision 那一套。
  3. 时间戳报错detect_async 必须传单调递增的 timestamp_ms,重复值会被拒。
  4. 回调线程on_result 是另一个线程跑的,不要在里面调 OpenCV GUI 函数;只更新共享变量给主循环读。

十一、还能往哪儿玩

  • 把"打开 PNG"换成"启动指定程序"、“切歌”、“切桌面”——一切快捷键能干的事都行;
  • 双手(num_hands=2):左 0 + 右 N,组合表达式更多;
  • 配合 OBS 摄像头,做不需要触屏的演讲翻页器;
  • 21 个关键点也足以训练自定义手势分类器,喂给一个 MLP 就能识别"OK"、"❤"等更花哨的姿势。

代码不到 200 行,跑起来流畅 25-30 fps(CPU 推理,i5 笔记本)。MediaPipe 这一套真正的价值是把"摄像头到关键点"的脏活全干了,剩下的判断都只是几何题。

小结:HandLandmarker + 一个稳定窗口 + 一个 armed 标志 + os.startfile,就是 Windows 上一个能用的手势触发器。下一篇我们会用同一套骨架,把 HandLandmarker 换成 FaceDetector,做一个"按 ‘s’ 一键存所有人脸"的小工具。

到此这篇关于Python+OpenCV实现用MediaPipe手势数字识别一键打开下载夹里的图片的文章就介绍到这了,更多相关Python OpenCV数字识别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python自动化高效实现Word文档的动态创建与管理

    Python自动化高效实现Word文档的动态创建与管理

    在日常工作中,Word文档处理占据了我们大量时间,ord文档的动态创建与管理,让文档自动化成为您的得力助手,本文将深入探讨如何利用一个高效的Python库,实现Word文档的动态创建与管理,感兴趣的小伙伴可以了解下
    2025-09-09
  • Python使用Spire.PDF轻松实现压缩PDF文件的大小

    Python使用Spire.PDF轻松实现压缩PDF文件的大小

    PDF 文件因其优秀的兼容性和格式保持能力而被广泛使用,但随着内容的增加,特别是包含大量高清图片时,文件体积往往会变得非常庞大,下面我们就来看看如何使用 Python 和 Spire.PDF 库来压缩 PDF 文件吧
    2026-04-04
  • Conda命令教程小结

    Conda命令教程小结

    Conda 是一个开源的包管理器和环境管理器,主要用于安装和管理 Python 包及依赖项,本文就来介绍一下Conda命令,具有一定的参考价值,感兴趣的可以了解一下
    2025-01-01
  • 在python中,使用scatter绘制散点图的实例

    在python中,使用scatter绘制散点图的实例

    今天小编就为大家分享一篇在python中,使用scatter绘制散点图的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • python自动化运维之Telnetlib的具体使用

    python自动化运维之Telnetlib的具体使用

    本文将结合实例代码,介绍python自动化运维之Telnetlib的具体使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • python脚本实现xls(xlsx)转成csv

    python脚本实现xls(xlsx)转成csv

    这篇文章主要介绍了python脚本实现xls(xlsx)转成csv的相关资料,需要的朋友可以参考下
    2016-04-04
  • python如何获取Prometheus监控数据

    python如何获取Prometheus监控数据

    这篇文章主要介绍了python如何获取Prometheus监控数据,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • Python pyasn库解析和生成ASN.1数据结构

    Python pyasn库解析和生成ASN.1数据结构

    这篇文章主要介绍了Python pyasn库实现ASN.1数据结构的解析和生成实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • 详谈Python基础之内置函数和递归

    详谈Python基础之内置函数和递归

    下面小编就为大家带来一篇Python基础之内置函数和递归。小编觉得挺不错的。现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • Python 调用GPT-3 API实现过程详解

    Python 调用GPT-3 API实现过程详解

    这篇文章主要为大家介绍了Python 调用GPT-3 API实现过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02

最新评论