OpenCV+Madiapipe实现——手指竖起数量判断(计算机视觉:附完整源码)


目录

效果展示

应用场景

1. 人机交互(HCI - Human-Computer Interaction)

2. 游戏控制

3. 智能家居控制

4. 无接触式操作(医疗 & 公共场景)

5. 手语识别

6. 机器人控制

7. 运动分析 & 体感健身

8. 远程会议手势互动

代码详解

1. 初始化必要的模块

2. 计算手指数

3. 绘制手部信息

4. 统计并显示信息

5. 处理视频流

完整代码

总结


效果展示

手指竖起统计



应用场景

1. 人机交互(HCI - Human-Computer Interaction)

  • 手势控制

    • 用手指数量或特定手势控制电脑、智能设备(如切换幻灯片、控制音量、播放/暂停视频等)。

    • 结合 手势识别模型 实现更多复杂交互,如 手势拖拽点击滑动


2. 游戏控制

  • 体感游戏

    • 通过手势控制角色移动、跳跃、攻击等操作(如 VR/AR 游戏)。

    • 结合 OpenCV 的 手势跟踪 技术,可以开发简单的基于手势的游戏,如“猜拳游戏”或“石头剪刀布”。


3. 智能家居控制

  • 手势开关灯、调节音量、切换电视频道

    • 结合 IoT 设备,用手势控制智能家居设备。

    • 例如 比出 5 根手指开灯比出 1 根手指关灯


4. 无接触式操作(医疗 & 公共场景)

  • 手势识别替代按钮操作,减少物理接触:

    • 医院:医生在无菌环境下控制计算机(如切换病人 X 光片)。

    • 银行/电梯:使用手势控制 ATM 机、无接触式电梯按钮。


5. 手语识别

  • 结合手指计数和角度检测,实现简单的 手语识别,帮助听障人士与他人交流。


6. 机器人控制

  • 机器人通过手势接收指令,例如:

    • 伸出 1 根手指 → 前进

    • 伸出 2 根手指 → 后退

    • 伸出 5 根手指 → 停止


7. 运动分析 & 体感健身

  • 识别健身动作,如瑜伽手势、太极动作检测

  • 结合 姿态识别(Pose Estimation),跟踪手势姿势,进行训练指导(如纠正动作)。


8. 远程会议手势互动

  • 在视频会议(Zoom、Teams)中使用手势:

    • 举起 1 根手指 → 发送 "请发言" 信号

    • 伸出 5 根手指 → 发送 "赞同" 信号

    • 比出 "OK" 手势 → 发送 "同意" 信息+


代码详解

1. 初始化必要的模块

  • 使用 cv2.VideoCapture(0) 访问摄像头,获取视频流。

  • 通过 mediapipe.solutions.hands.Hands() 初始化 MediaPipe Hands 模块,用于检测手部关键点。

2. 计算手指数

  • 手指识别逻辑

    • 食指、中指、无名指、小指:比较指尖(tip)和指关节(pip)的 y 轴坐标,判断是否竖起。

    • 拇指:由于拇指是水平展开的,需要根据 x 轴坐标判断竖起状态(左手和右手的判断方向相反)。

  • 使用 count_fingers_and_get_status() 方法遍历每根手指,计算伸直的手指数并存储状态信息。

3. 绘制手部信息

  • 通过 MediaPipe 提供的 hand_landmarks.landmark 获取 21 个关键点坐标,并将它们转换为图像坐标系。

  • 绘制关键点和骨骼

    • 关键点(关节点)使用 单独的颜色 标注,与骨骼线颜色不同。

    • 手指骨骼线的颜色:

      • 手指竖起时(raised) → 亮色(红色或绿色)。

      • 手指弯曲时(not raised) → 暗色(深红或深绿)。

    • 手腕到手指根部的线条采用 基础颜色,用于区分左右手。

4. 统计并显示信息

  • 统计手指数总和,并在屏幕上显示:

    • 每只手单独统计手指数,并在手腕附近显示该手的 手指数和左右手标签

    • 在画面左上角显示 总手指数(Total Fingers)

  • 计算 FPS(帧率) 以评估性能,并在屏幕上实时显示。

5. 处理视频流

  • 通过 cv2.flip(frame, 1) 进行水平翻转,使其符合用户视觉习惯。

  • 使用 cv2.imshow() 实时显示处理后的视频流。

  • 按下 Esc 键 退出程序。


优点:

  • 颜色区分明显:

    • 左右手信息颜色不同,方便区分。

    • 骨骼颜色 区分 竖起和弯曲 的手指,使检测结果更直观。

    • 关键点颜色 与骨骼颜色不同,使整体显示更清晰。

  • 计算量较低:

    • 只进行最必要的计算(避免过多 for 循环)。

    • FPS 显示方便评估性能。

        当前版本用于 实时手指计数、手势识别 等应用,如果需要进一步扩展,比如 手势控制系统,可以在 count_fingers_and_get_status() 里增加 手势模式判断。如果这篇文章点赞数量不错,我可以将其升级。


完整代码

import cv2
import mediapipe as mp
import time

# 初始化 MediaPipe Hands 模块
mp_hands = mp.solutions.hands
# 不直接使用 mp_drawing.draw_landmarks,因为我们需要自定义绘制颜色

# 设置检测参数
hands = mp_hands.Hands(static_image_mode=False,
                       max_num_hands=2,
                       min_detection_confidence=0.5,
                       min_tracking_confidence=0.5)

cap = cv2.VideoCapture(0)

prev_time = 0

def count_fingers_and_get_status(hand_landmarks, hand_label):
    """
    计算每根手指是否伸直,并返回伸直的手指数量及状态字典
    返回:
        count: 总计伸直的手指数
        status: 字典,key 为 'thumb', 'index', 'middle', 'ring', 'pinky',值为 True/False
    """
    count = 0
    status = {}
    landmarks = hand_landmarks.landmark

    # 食指、中指、无名指、小指判断:
    # 对应 finger: tip 与 pip(取第二个关节,即:指尖与关节 6,10,14,18 对比)
    fingers = {
        "index": (8, 6),
        "middle": (12, 10),
        "ring": (16, 14),
        "pinky": (20, 18)
    }
    for finger, (tip_id, pip_id) in fingers.items():
        if landmarks[tip_id].y < landmarks[pip_id].y:
            status[finger] = True
            count += 1
        else:
            status[finger] = False

    # 拇指判断:
    # 右手:如果拇指 tip 在 x 轴上比拇指 IP(3号点)靠右,则认为伸直;左手则相反
    thumb_tip = landmarks[4]
    thumb_ip = landmarks[3]
    if hand_label == "Right":
        if thumb_tip.x > thumb_ip.x:
            status["thumb"] = True
            count += 1
        else:
            status["thumb"] = False
    else:  # Left hand
        if thumb_tip.x < thumb_ip.x:
            status["thumb"] = True
            count += 1
        else:
            status["thumb"] = False

    return count, status

# 为各手指的骨骼绘制定义分段(不包括腕部到基部的连接)
finger_segments = {
    "thumb": [(1, 2), (2, 3), (3, 4)],
    "index": [(5, 6), (6, 7), (7, 8)],
    "middle": [(9, 10), (10, 11), (11, 12)],
    "ring": [(13, 14), (14, 15), (15, 16)],
    "pinky": [(17, 18), (18, 19), (19, 20)]
}

# 定义不同手的基色(用于文本、腕部到各手指根部的连接)
hand_base_colors = {
    "Left": (255, 0, 0),    # 蓝色(BGR格式)
    "Right": (0, 255, 0)    # 绿色
}
# 定义手指伸直与否的颜色(针对骨骼线),这里分别定义“高亮”和“暗淡”的颜色
finger_colors = {
    "Left": {"raised": (255, 100, 100), "not": (100, 0, 0)},
    "Right": {"raised": (100, 255, 100), "not": (0, 100, 0)}
}

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    frame = cv2.flip(frame, 1)
    img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    results = hands.process(img_rgb)

    total_fingers = 0

    if results.multi_hand_landmarks and results.multi_handedness:
        for hand_landmarks, handedness in zip(results.multi_hand_landmarks, results.multi_handedness):
            # 获取左右手标签
            hand_label = handedness.classification[0].label  # "Left" 或 "Right"
            base_color = hand_base_colors[hand_label]
            f_color = finger_colors[hand_label]

            # 计算手指伸直数量和状态
            count, finger_status = count_fingers_and_get_status(hand_landmarks, hand_label)
            total_fingers += count

            h, w, _ = frame.shape
            # 将所有 21 个关键点转换为图像坐标
            lm_coords = []
            for lm in hand_landmarks.landmark:
                lm_coords.append((int(lm.x * w), int(lm.y * h)))

            # 绘制腕部到各手指根部的连接线(腕部为 0 点,各手指根部分别为:thumb->1, index->5, middle->9, ring->13, pinky->17)
            for root in [1, 5, 9, 13, 17]:
                cv2.line(frame, lm_coords[0], lm_coords[root], base_color, 2)

            # 绘制各手指骨骼线,并根据是否伸直改变颜色
            for finger, segments in finger_segments.items():
                # 根据 finger_status 选择颜色
                color = f_color["raised"] if finger_status.get(finger, False) else f_color["not"]
                for (start, end) in segments:
                    cv2.line(frame, lm_coords[start], lm_coords[end], color, 3)
                    # 绘制小圆点标记关键点
                    cv2.circle(frame, lm_coords[start], 4, color, -1)
                    cv2.circle(frame, lm_coords[end], 4, color, -1)

            # 在手附近显示该手的手指数量(在第一个关键点附近显示)
            cx, cy = lm_coords[0]
            cv2.putText(frame, f'{hand_label}: {count}', (cx - 30, cy - 20),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.8, base_color, 2)

    # FPS 计算
    curr_time = time.time()
    fps = 1 / (curr_time - prev_time) if (curr_time - prev_time) > 0 else 0
    prev_time = curr_time
    cv2.putText(frame, f'FPS: {int(fps)}', (10, 80),
                cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2)
    cv2.putText(frame, f'Total Fingers: {total_fingers}', (10, 40),
                cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 2)

    cv2.imshow("Finger Count", frame)
    if cv2.waitKey(1) & 0xFF == 27:
        break

cap.release()
cv2.destroyAllWindows()

总结

        期待更新吗?

一键三连吧铁子们,这真的对我很有帮助!!!!我们再见!!!

一键三连吧铁子们,这真的对我很有帮助!!!!我们再见!!!

一键三连吧铁子们,这真的对我很有帮助!!!!我们再见!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值