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

CVZone 是盖在 OpenCV 与 MediaPipe 上面的一层【积木】:把 MediaPipe 三十行的手部检测代码压成三行,把画角框、带底色文字、拼九宫格这些样板活封装成函数——它自己不是 AI 模型,是省你打字的封装层。

📝 本节和前面几节的关系

上一节你已经手动用了 MediaPipe(下载模型、转 RGB、读比例坐标——每步都得自己来);本节的 CVZone 把这些样板全部吞进肚子里。关系不是替代而是分工:12-1 教你看清流水线内部,12-2 让你以后写实验时不必每次重搭。11-5 的 YOLO 三行出结果,本节的 CVZone 是【手部任务版的三行出结果】。

一、CVZone 是什么:积木层的定位

把本章三个工具摆在一起看就清楚了:

你写的实验代码
   ↓
CVZone          ← 12-2:三行搞定手部/人脸/姿态,附赠绘图工具箱
   ↓
MediaPipe       ← 12-1:Google 的检测流水线(真模型在这里)
   ↓
OpenCV          ← 11-2:读图、显示、画框(视觉界的瑞士军刀)

CVZone 干的三类事: 1. 压缩样板:HandDetector 一行创建,findHands 一行调用,自动画好骨架 2. 高频小工具:cornerRect(画带角的漂亮框)、putTextRect(带底色的文字)、stackImages(多画面拼一张)、FPS(帧率计) 3. 统一返回格式:所有检测器都返回【列表+字典】,取第几只手、取坐标的写法全一致

一句话:MediaPipe 是发动机,CVZone 是带方向盘的整车。想懂原理拆开看(12-1),想快点跑装上就用(本节)。

二、安装与体检(同一个 NumPy 坑)

cd ~/ai && source venv/bin/activate
pip install cvzone

CVZone 会自动把 MediaPipe 一起装上(它是依赖),所以 12-1 的 NumPy 2 冲突在这一样会犯——装完先跑版本体检:

import cvzone, cv2, numpy, mediapipe
print("cvzone    ", cvzone.__version__)
print("mediapipe ", mediapipe.__version__)
print("numpy     ", numpy.__version__)
⚠️ 老教程哪几处已失效(本章防坑段 ②)
  1. pip install cvzone 若连带把 NumPy 升到 2.x,import mediapipe 会当场炸——一律先 pip install "numpy<2" 再装 cvzone(顺序反过来也一样,装完必体检)。
  2. 老教程的 detector.findPosition(img):新版返回值改成了字典列表(hand["lmList"]),老写法返回两个值的部分版本已变——照抄老代码拿不到坐标时,先 print(hands) 看清字典里有什么键。
  3. CVZone 的序列号模块(SerialModule) 老教程常用它跟 Arduino 通信——本章 8-5 已经学过原生 pyserial 的三规矩,没必要绕回封装层。

三、HandDetector:本节的明星

from cvzone.HandTrackingModule import HandDetector

detector = HandDetector(detectionCon=0.8, maxHands=2)   # 置信度、最多几只手

四个高频方法(这就是 12-4 猜拳实验的全部家底):

方法 干什么 返回
findHands(img, draw=True) 找手并自动画骨架 画面+手列表
hand["lmList"] 21 个关键点坐标(已是像素,与 12-1 的比例值不同!) 列表
fingersUp(hand) 判断五根手指各自伸直没有 [0,1,1,0,0] 这样五个 0/1
findDistance(8, 12, img) 量两指尖距离 距离+画面+信息

注意教材标题里那句【电脑视觉套件】的诚实之处:CVZone 没有训练过【这是剪刀】——fingersUp 只告诉你五根手指的姿态,【剪刀还是石头】的判断下一节你自己写 if。

四、动手 Demo:摄像头实时数手指(CVZone 三行版)

对照 12-1 的三十行 Demo,感受积木层的厚度:

# demo12_2_fingers.py —— CVZone 实时数手指
import cv2, cvzone
from cvzone.HandTrackingModule import HandDetector

cap = cv2.VideoCapture(0)                 # USB 摄像头(10-6 的 V4L2 之路)
cap.set(3, 640); cap.set(4, 480)          # 分辨率压到 640x480,帧率优先
detector = HandDetector(detectionCon=0.8, maxHands=1)

while True:
    ok, img = cap.read()
    if not ok:
        break
    hands, img = detector.findHands(img)  # 找手+自动画骨架
    if hands:
        hand = hands[0]
        fingers = detector.fingersUp(hand)
        count = fingers.count(1)
        cvzone.putTextRect(img, f"Fingers: {count}", (40, 60), scale=2)
    cv2.imshow("CVZone", img)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break
cap.release(); cv2.destroyAllWindows()

看到什么算成功:窗口里你的手被绿色骨架线连着,左上角实时显示 Fingers: 3 这类数字;五指张开显示 5、握拳显示 0。树莓派 4 上约 7-8 FPS(够用不流畅),Pi 5 明显更顺。

读结果:fingersUp 的判据是【指尖(点 8/12/16/20)与对应指根的上下关系】+拇指的特殊横比——所以手腕朝下时数字会全错,这不是 bug 是几何:先学会把手腕朝上,再谈识别。

五、30 秒实验:不用摄像头的拼图工具

没接摄像头也能玩 CVZone——stackImages 是纯图像运算:

import cv2, cvzone
img = cv2.imread("hand.jpg")              # 任意一张图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray3 = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)   # 灰度也升回三通道才能拼
stacked = cvzone.stackImages([img, gray3, gray3, img], 2, 0.5)
cv2.imshow("stack", stacked)
cv2.waitKey(0)

看到什么算成功:一个 2×2 的四宫格窗口(两格彩色、两格灰度)。顺手验证 11-3 的知识点:把 gray3 换回两通道的 gray 再拼一次,stackImages 会直接报错——通道数不同的图不能拼,这就是它内部帮你查的那道岗。

六、常见误区

  1. 以为 CVZone 是又一个 AI 框架——它零模型,全是封装;模型还是 MediaPipe 的,装它只是装了 12-1 的【方便版外壳】。
  2. 混用两代的坐标单位——12-1 Tasks API 给比例值(0~1),CVZone 的 lmList 给像素值;拿比例值当像素下标去索引数组,画出的点会缩在图的一角。
  3. 追求满分辨率——1280×720 下 MediaPipe 在 Pi 4 会掉到 3 FPS 以下;640×480 是实时视觉的甜点位(和 11-5 喂小图是同一条法则)。
  4. 手腕朝下还怪算法不准——fingersUp 基于上下几何关系,握拳/张手的姿态对了但手腕翻转,结果必然错;实验先把姿态摆标准,再谈鲁棒性。
自测三问(点开对答案) 1. CVZone 内部依赖哪两个库?——OpenCV 和 MediaPipe(它自己不含任何模型)。 2. `fingersUp` 返回什么?——五个 0/1 组成的列表,依次代表拇指、食指、中指、无名指、小指是否伸直。 3. CVZone 的关键点坐标和 12-1 Tasks API 的关键点坐标差在哪?——CVZone 给像素值,Tasks API 给 0~1 比例值。

相关笔记