用 CVZone 的 fingersUp 数出五根手指的伸直状态,再用三条 if 把【剪刀/石头/布】判出来——这一章的 AI 只有前一半:检测靠 MediaPipe 的模型;后一半【这是布】的判断是你自己写的规则,没有训练任何东西。
12-2 已经给了全部家底(findHands+fingersUp),本节是第一次完整走通【取帧→检测→判断→显示】的实验闭环——它和 11-5 的 YOLO 循环、12-3 的推理循环是同一副骨架。学完你会发现:视觉实验的【代码结构】其实只有一种,变的只是中间那段【判断逻辑】。
一、先把语义掰正:这不是训练,是规则判断
教材把本节叫【AI 实验范例】,但严格说:
- 模型干的活:找出手在哪、21 个关键点各在哪(MediaPipe 预训练,你零参与)
- 你写的代码干的活:看【哪几根手指伸直】→ 映射到【剪刀/石头/布】(纯 if 规则)
为什么不直接训练一个手势分类模型?——可以(树莓派上跑 TFLite 手势模型也行),但对三个类别的清晰手势,规则判断又快又稳又好调试:认错了你一眼能看出是哪根手指判错。先规则后模型是边缘 AI 的实用主义顺序。
二、判据:五根手指的真值表
fingersUp 返回 [拇指, 食指, 中指, 无名指, 小指] 的 0/1 列表,三种手势的真值表就固定下来了:
| 手势 | fingersUp | 记法 |
|---|---|---|
| 布 | [1, 1, 1, 1, 1] |
全伸 |
| 石头 | [0, 0, 0, 0, 0] |
全弯 |
| 剪刀 | [0, 1, 1, 0, 0] |
只伸食指+中指 |
前提纪律(12-2 误区 4 的实战版):手腕朝上、掌心或手背正对摄像头;手腕一翻,fingersUp 的上下几何全乱,真值表作废。
三、动手 Demo:实时手势识别
# demo12_4_rps.py —— 剪刀石头布实时识别
import cv2, cvzone
from cvzone.HandTrackingModule import HandDetector
detector = HandDetector(detectionCon=0.8, maxHands=1)
cap = cv2.VideoCapture(0)
cap.set(3, 640); cap.set(4, 480)
def classify(fingers):
if fingers == [1, 1, 1, 1, 1]:
return "Paper"
if fingers == [0, 0, 0, 0, 0]:
return "Rock"
if fingers == [0, 1, 1, 0, 0]:
return "Scissors"
return "..."
while True:
ok, img = cap.read()
if not ok:
break
hands, img = detector.findHands(img)
if hands:
fingers = detector.fingersUp(hands[0])
gesture = classify(fingers)
cvzone.putTextRect(img, gesture, (40, 60), scale=2,
colorR=(0, 128, 255))
cv2.imshow("RPS", img)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release(); cv2.destroyAllWindows()
看到什么算成功:摆出剪刀手,窗口显示 Scissors;握拳变 Rock;张开变 Paper——三种手势切换无明显迟滞。画面上方偶尔显示 ...(过渡姿态),属正常。
读结果:classify 是纯函数——输入五个 0/1、输出字符串,和摄像头毫无关系。这个设计让你可以先离线测逻辑、再接摄像头(见 30 秒实验),是【检测与判断解耦】的最小示范。
四、30 秒实验:不接摄像头,先测判断逻辑
把 classify 抄进一个新文件,喂它假数据:
cases = {
"布": [1, 1, 1, 1, 1],
"石头": [0, 0, 0, 0, 0],
"剪刀": [0, 1, 1, 0, 0],
"六指?": [1, 1, 1, 1, 0],
"比心": [0, 1, 0, 0, 1],
}
for name, fingers in cases.items():
print(name, "->", classify(fingers))
看到什么算成功:前三行正确映射;后两行打印 ...——未定义的姿态落进兜底分支而不是乱猜。亲手验证一遍真值表,比对着屏幕摆十次手势都更能确信【逻辑层没有 bug】;以后摄像头识别错了,你就知道该查检测层(12-2 的几何问题)而不是这里。
五、往前走一步:三个自然的延伸
- 猜拳对战:加
random.choice(["Rock", "Paper", "Scissors"])当电脑出招,定格判断胜负——教材下一章的方向。 - 手势当遥控器:
Rock时开灯、Paper时关灯——用 8-5 的串口三规矩发给 Arduino,或用 9-4 的 machine.UART 发给 Pico;手势版智能家居就此成立。 - 升级成模型分类:姿态更复杂(数字 1~10、比 OK)后真值表爆炸,那时再换 TFLite 手势分类模型——规则的尽头是模型。
六、常见误区
- 以为要做什么训练——本节没有一行训练代码;MediaPipe 检测是现成的,映射是手写的。分清这两半,出错才知道找谁。
- 拿
count当判据——伸直数量 2 可能是剪刀也可能是【比耶走样】的任何姿态;必须比较整张真值表,不能只数个数。 - 过渡态没有兜底——手在半空切换姿势时 fingersUp 是中间值,没有
return "..."兜底就会闪烁显示错误手势;凡分类必留【不确定】出口。 - 在镜像画面上左右不分——
findHands的flipType=True默认把画面当镜像处理,做【左右手区分】的延伸实验时记得这个参数在管什么。
自测三问(点开对答案)
1. 本节的【AI】具体指哪部分?——只有手部检测(MediaPipe 预训练模型);剪刀/石头/布的判断是手写 if 规则。 2. 为什么不能用【伸直手指的数量】当判据?——数量不唯一对应手势(数量 2 的姿态不止剪刀),必须比较完整真值表。 3. 检测与判断解耦的好处是什么?——classify 是纯函数可离线测试;摄像头识别错误时能立刻定位该查哪一层。- ☐ 下一节(12-5 AI 实验范例:TensorFlow Lite+Webcam 实时物体识别)
相关笔记
- CVZone 计算机视觉套件 —— 上一节:findHands 与 fingersUp 的出处
- AI 实验范例:OpenCV 人脸识别 —— 同款语义掰正:检测≠识别
- 在树莓派使用 Python 建立串口通信 —— 手势当遥控器的发送端
- 🍓 树莓派 · 索引 —— 全课路线图