篮球裁判犯规识别系统(六) 用 Python + MediaPipe 实现实时手指计数
·
手势识别是人机交互中的常用功能。本文演示如何用 Python + MediaPipe + OpenCV 实现实时手指计数,并在摄像头画面上显示左右手手指数量。用这个模块来判断运动员的号码
功能亮点
-
实时检测左右手。
-
自动识别手指伸直情况。
-
可显示左右手手指数量。
-
可扩展为手势控制、游戏交互等场景。
环境准备
pip install mediapipe opencv-python
Python 3.8+ 推荐。
核心思路
-
MediaPipe Hands 用于识别手部关键点。
-
拇指根据左右手不同方向判断是否伸直。
-
其他四指根据指尖与指根(PIP 关节)y 坐标判断伸直状态。
-
在画面上显示左右手的手指数量。
代码实现
import mediapipe as mp
import cv2
class FingerCounter:
def __init__(self):
mp_hands = mp.solutions.hands
self.hands = mp_hands.Hands(
static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5
)
self.drawing = mp.solutions.drawing_utils
@staticmethod
def count_fingers(lms, hand_label): # 新增 hand_label 参数
tips = [4, 8, 12, 16, 20]
fingers = 0
# 拇指:根据左右手调整判断方向
if hand_label == "Right":
# 右手:拇指尖 x < 关节 x 表示张开
if lms.landmark[tips[0]].x < lms.landmark[tips[0] - 1].x:
fingers += 1
else: # Left hand
# 左手:拇指尖 x > 关节 x 表示张开(镜像)
if lms.landmark[tips[0]].x > lms.landmark[tips[0] - 1].x:
fingers += 1
# 其他四指:指尖 y < 指根(PIP关节,即 index-2)表示伸直(通用)
for i in range(1, 5):
if lms.landmark[tips[i]].y < lms.landmark[tips[i] - 2].y:
fingers += 1
return fingers
def detect(self, frame, left_kp, right_kp, fps, draw=False,save_frame=False, save_path="frame.jpg"):
"""
返回:数字总和
触发条件:至少一只手腕高于肩,并保持 0.5 秒
"""
if not hasattr(self, "raise_frames"):
self.raise_frames = 0
if save_frame:
cv2.imwrite(save_path, frame)
left_sh, left_el, left_wr = left_kp
right_sh, right_el, right_wr = right_kp
# 判断哪只手举起
right_up = left_wr[1] < left_sh[1]
left_up = right_wr[1] < right_sh[1]
any_up = left_up or right_up
half_sec_frames = int(1 * fps)
if any_up:
self.raise_frames += 1
else:
# 一旦手放下立即重置,避免残留
self.raise_frames = 0
# print("raise_frames:", self.raise_frames)
# 未达到持续时间,直接返回 None
if self.raise_frames <= half_sec_frames:
return None
# -----------------------
# 到达触发条件 → 开始识别手指
# -----------------------
results = self.hands.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
left_num = right_num = 0
if results.multi_hand_landmarks:
for lm, handed in zip(results.multi_hand_landmarks, results.multi_handedness):
if draw:
self.drawing.draw_landmarks(frame, lm, mp.solutions.hands.HAND_CONNECTIONS)
num = self.count_fingers(lm, handed.classification[0].label)
if handed.classification[0].label == "Left":
left_num = num
else:
right_num = num
# print("left_num,right_num :",left_num,right_num)
# -----------------------
# 关键修正:根据举手模式返回数字
# -----------------------
if left_up and not right_up:
# print("left_up")
return left_num
if right_up and not left_up:
# print("right_up")
return right_num
if left_up and right_up:
return left_num + right_num
return None
def main():
fc = FingerCounter()
cap = cv2.VideoCapture(0) # 打开默认摄像头
fps = cap.get(cv2.CAP_PROP_FPS)
if fps == 0:
fps = 30 # 某些摄像头不返回fps,默认30
print("摄像头已启动,按 q 退出")
while True:
ret, frame = cap.read()
frame = cv2.flip(frame, 1)
if not ret:
break
# 你目前 detect() 的参数里必须传肩、肘、腕坐标,但摄像头里没有这些数据
# 所以这里先传假值(手腕永远不高于肩部 → 不会触发计数)
# 你只是测试 count_fingers() 准不准,所以直接绕过触发条件
# ---------------------------------------------------
# 绕过触发条件:直接手动调用 hands 识别然后 count_fingers
# ---------------------------------------------------
results = fc.hands.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
if results.multi_hand_landmarks:
for lm, handed in zip(results.multi_hand_landmarks, results.multi_handedness):
# 绘制
fc.drawing.draw_landmarks(frame, lm, mp.solutions.hands.HAND_CONNECTIONS)
# 数手指
num = fc.count_fingers(lm, handed.classification[0].label)
# 在画面上显示
label = handed.classification[0].label
cv2.putText(frame, f"{label}: {num}", (10, 40 if label=='Left' else 80),
cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0,255,0), 3)
cv2.imshow("Finger Counter Test", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
使用说明
-
运行脚本,会打开摄像头窗口。
-
将手放在摄像头前,可以看到左右手手指数量实时显示。
-
按
q关闭窗口。
源码自取:通过网盘分享的文件:basketball_foul_detect.zip
链接: https://pan.baidu.com/s/1kwi9Mn_v97PBRb7kn5sFRw?pwd=emd3 提取码: emd3
更多推荐

所有评论(0)