🍓 第十二章 AI 实验范例(二):进阶计算机视觉+AI 识别-TensorFlow+MediaPipe+CVZone
📄 一句话概括

MediaPipe 是 Google 的机器学习「流水线」框架——它把【检测→追踪→渲染】这些步骤预先搭好成图,你只需要喂进一帧画面,它就还你一堆关键点坐标;上一章 YOLO 是【万物通用】的检测器,MediaPipe 是【为特定目标精调】的专家(手、脸、姿态)。

📝 本节和前面几节的关系

11-2 装好了 OpenCV、11-1 的 venv 在本章从【推荐】升级为【必需】(MediaPipe 不在 apt 仓库里,只能 pip 装);11-3 的【图就是数组】是所有视觉库共同的地基;11-5 的 YOLO 教会我们【模型+预训练】的思路——本章的 MediaPipe 是同一思路的另一个流派:Google 替你把特定任务(手部、人脸、姿态)训练到开箱即用。下一节 CVZone 则是站在 MediaPipe 肩膀上的【积木层】。

一、MediaPipe 是什么、解决什么问题

上一章用 YOLO 认出了 80 类物体,但你注意过吗——YOLO 只会画框,不会告诉你【这只手哪根手指伸直了】。

MediaPipe 的本质:把【模型推理】与【前后处理】(缩放、坐标归一化、跨帧追踪、骨架连线)打包成一条现成的流水线,开发者只管喂帧、收结果。代价是任务固定——它只做 Google 精调过的那些任务,想认自家产品的零件,还是得回 YOLO 那条路(自己训练)。

二、两代 API:老 solutions 与新 Tasks(2026 现状)

MediaPipe 的 Python 接口经历过一次换代,网上的老教程大多还停在上一代:

老一代(legacy solutions) 新一代(Tasks API)
导入写法 mp.solutions.hands mediapipe.tasks.python.vision.HandLandmarker
模型来源 内置打包在库里 单独下载 .task 文件
现状 官方已标弃用(deprecation 警告),但仍能运行 当下正路,新功能只加在这边
配置方式 函数参数 Options 对象(更啰嗦但更清晰)

本课的取态:12-2 的 CVZone 内部用的还是老一代接口(它封装好了,你无感知);本节 Demo 直接用新一代 Tasks API,让你两头都见过。跑老教程代码时看到 UserWarning: ... is deprecated 别慌,那是提醒、不是报错。

三、在树莓派上安装(venv 是硬前提)

MediaPipe 不在 apt 仓库里(apt 搜不到 python3-mediapipe),只能 pip 装——所以 11-1 建的 venv 从这一节起是硬前提:

cd ~/ai            # 11-1 建的实验区
source venv/bin/activate
pip install mediapipe

装完立刻做版本体检——这里埋着本章第一大坑:

import mediapipe, numpy
print("mediapipe", mediapipe.__version__)
print("numpy    ", numpy.__version__)
⚠️ 老教程哪几处已失效(本章防坑段 ①)
  1. NumPy 2 冲突:MediaPipe 的 ARM 版轮子还在按 NumPy 1.x 编译,系统里若装了 NumPy 2.x,import mediapipe 会抛 _ARRAY_API / 版本不匹配错误——解法:在 venv 里 pip install "numpy<2"(如 1.26.4)。这是 ARM 平台特有且 2026 年仍高频出现的坑。
  2. 老教程手工编译 MediaPipe:2020 年前后的教程教你装 Bazel 源码编译几小时——现在官方直接提供树莓派 64 位(aarch64)的预编译轮子,pip install 几分钟搞定。
  3. 32 位系统装不上:官方轮子只发 64 位,还在跑 32 位老系统的要先换系统。

四、动手 Demo:用 Tasks API 找出一只手的 21 个关键点

不接摄像头也能跑:先下载官方 hand_landmarker.task 模型(约 7MB),再喂一张手部照片。

# demo12_1_hand.py —— MediaPipe Tasks API 版手部关键点检测
import mediapipe as mp
from mediapipe.tasks import python as mp_python
from mediapipe.tasks.python import vision

# 1. 创建检测器(模型文件首跑前先下载,命令见下方)
base = mp_python.BaseOptions(model_asset_path="hand_landmarker.task")
opts = vision.HandLandmarkerOptions(base_options=base, num_hands=2)
detector = vision.HandLandmarker.create_from_options(opts)

# 2. 用 OpenCV 读图(11-3:图就是 BGR 数组)
import cv2
img = cv2.imread("hand.jpg")          # 换成任意一张有手的照片
h, w = img.shape[:2]

# 3. 喂给检测器
mp_img = mp.Image(image_format=mp.ImageFormat.SRGB,
                  data=cv2.cvtColor(img, cv2.COLOR_BGR2RGB))   # MediaPipe 吃 RGB
result = detector.detect(mp_img)

# 4. 读结果:每只手 21 个关键点,坐标是 0~1 的比例值
for i, hand in enumerate(result.hand_landmarks):
    print(f"手 {i}:{len(hand)} 个关键点")
    print("  手腕(点0):x=%.3f y=%.3f" % (hand[0].x, hand[0].y))
    print("  食指尖(点8):x=%.3f y=%.3f" % (hand[8].x, hand[8].y))

模型与照片的准备命令:

wget https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task
# hand.jpg 自备:拍一张你的手掌,或从手机传一张到树莓派
python demo12_1_hand.py

看到什么算成功:

手 0:21 个关键点
  手腕(点0):x=0.412 y=0.833
  食指尖(点8):x=0.298 y=0.421

读结果:关键点坐标是归一化的比例值(0~1),乘上图片宽高才是像素坐标——这是 MediaPipe 与 OpenCV 原生坐标最大的差异(11-3 里 img[y, x] 用的可是实打实的像素下标)。点 0 固定是手腕、点 4/8/12/16/20 是五根指尖,这套编号全世界统一,下一节 CVZone 的 fingersUp 就是靠判断【指尖在手腕上方还是下方】来数手指的。

五、30 秒实验:坐标系对一对

在 Demo 末尾追加两行,把比例坐标换算成像素坐标:

px, py = int(hand[8].x * w), int(hand[8].y * h)
print("食指尖像素坐标:", px, py)
print("图尺寸:", w, "x", h)

跑一遍:像素坐标必须同时满足 0 <= px < w、0 <= py < h——超出了就说明你乘反了(该乘 w 的乘了 h)。这个【比例 vs 像素】的错位,是 MediaPipe 新手画框画歪的头号原因。

六、常见误区

  1. 把 MediaPipe 当成【更厉害的 YOLO】——它俩是分工不是竞争:YOLO 通用检测(认 80 类、可自己训练),MediaPipe 特定任务(手/脸/姿态,精调到开箱即用但不能自定义目标)。
  2. 看到 deprecation 警告就以为代码坏了——老一代 solutions 接口弃用但能用;真正会坏的是 NumPy 2 冲突,报错信息完全不同。
  3. 喂 BGR 图给 MediaPipe——OpenCV 读图是 BGR(11-3 反复强调),MediaPipe 吃 RGB,中间必须 cvtColor 一次;忘了这一步不会报错,但结果整体错乱。
  4. 在系统 Python 里直接 pip install mediapipe——PEP 668 会拒绝(6-5 的老朋友);而且 NumPy 冲突一旦装进系统层,会连累 apt 装的 OpenCV 一起坏。一律在 venv 里装。
自测三问(点开对答案) 1. MediaPipe 的手部检测一次给出多少个关键点?——**21 个**,点 0 是手腕、点 4/8/12/16/20 是五根指尖。 2. `import mediapipe` 报 `_ARRAY_API` 相关错误,最可能的原因是什么?——venv 里装了 NumPy 2.x 与 ARM 版 MediaPipe 冲突,解法 `pip install "numpy<2"`。 3. MediaPipe 输出的关键点坐标是什么单位?——0~1 的比例值(相对图宽/图高),用之前要乘上像素尺寸。

相关笔记