OpenCV+mediapipe实现——手势识别(比心特效)

本文章已经生成可运行项目,


目录

效果展示

未知手势

倒赞

胜利剪刀手

ღ( ´・ᴗ・` )比心

实现思路

代码实现思路详解

一、系统架构

二、核心模块实现

1. 手指状态检测 (count_fingers_and_get_status)

2. 手势识别 (detect_gesture)

3. 动画系统

三、关键算法优化

1. 鲁棒性增强

2. 时序稳定性处理

3. 性能优化

四、效果演示示例

 完整代码

应用场景

总结



效果展示

未知手势

倒赞

胜利剪刀手

ღ( ´・ᴗ・` )比心


实现思路

代码实现思路详解

一、系统架构

该手势识别系统采用分层架构设计,主要分为以下三个层次:

  1. 感知层

    • 摄像头视频流输入
    • MediaPipe Hands实时手部关键点检测
    • OpenCV图像处理管道
  2. 逻辑层

    • 手指状态分析(伸直/弯曲)
    • 手势模式识别(静态手势分类)
    • 动画状态管理
  3. 表现层

    • 骨骼可视化渲染
    • 动态特效生成
    • 信息叠加显示(FPS/手势名称)
二、核心模块实现
1. 手指状态检测 (count_fingers_and_get_status)

实现原理

for finger, (tip_id, pip_id) in fingers.items():
    if landmarks[tip_id].y < landmarks[pip_id].y:
        status[finger] = True
  • 关节坐标系:MediaPipe返回的21个手部关键点坐标(归一化0-1)
  • 弯曲判断:通过比较指尖(TIP)与第二关节(PIP)的Y坐标值
  • 拇指特殊处理:根据左右手差异判断X轴方向

存在问题

  • 仅用两个关节判断可能导致误检(如半弯曲状态)
  • 未考虑掌指关节(MCP)的影响

改进方案

# 使用三关节连续判断
tip = landmarks[tip_id].y
pip = landmarks[pip_id].y 
mcp = landmarks[mcp_id].y
if tip < pip < mcp:  # 严格的伸直条件
    status[finger] = True
2. 手势识别 (detect_gesture)

手势判定逻辑树

Root
├── Thumbs Up (👍):拇指伸直 + 四指弯曲
├── Thumbs Down (👎):拇指弯曲 + 四指伸直
├── Victory (✌️):食指+中指伸直
├── Rock (🤟):小指+食指伸直
├── OK (👌):拇指食指接触 + 三指伸直
└── Heart (🤞):拇指食指交叉 + 三指弯曲

可以的关键优化点

# OK手势判断改进
thumb_tip = landmarks[4]
index_tip = landmarks[8]
dist = math.sqrt((thumb_tip.x-index_tip.x)**2 + 
                (thumb_tip.y-index_tip.y)**2 + 
                (thumb_tip.z-index_tip.z)**2)  # 三维距离

if (dist < 0.07 and 
    all([status["middle"], status["ring"], status["pinky"]])):
    return "OK"
3. 动画系统

状态管理机制

HEART_ANIMATION = {
    "hand_id": [start_time, (x, y)],
    # 示例: "Right_123": [162000.0, (320, 240)]
}

动画参数计算

progress = elapsed / ANIMATION_DURATION  # 标准化进度[0,1]
scale = 1 + (MAX_SCALE - 1) * progress  # 非线性缩放
alpha = 255 * (1 - progress**2)        # 二次衰减透明度

多层渲染技术

for i in range(3):
    # 基础层:正常尺寸
    # 中间层:放大120% + 加粗描边
    # 外层:放大140% + 半透明
    font_scale = scale * (0.8 + i*0.2)
    thickness = 2 + i*2
    cv2.putText(...)
三、关键算法优化
1. 鲁棒性增强

三维距离计算

def calculate_3d_distance(p1, p2):
    return math.sqrt(
        (p1.x - p2.x)**2 +
        (p1.y - p2.y)**2 + 
        (p1.z - p2.z)**2
    )

动态阈值调整

# 根据手部尺寸自适应
wrist = landmarks[0]
index_mcp = landmarks[5]
hand_size = calculate_distance(wrist, index_mcp)
contact_threshold = 0.15 * hand_size
2. 时序稳定性处理

状态保持机制

GESTURE_HISTORY = defaultdict(lambda: deque(maxlen=5))

def smooth_detection(hand_id, new_gesture):
    GESTURE_HISTORY[hand_id].append(new_gesture)
    return Counter(GESTURE_HISTORY[hand_id]).most_common(1)[0][0]
3. 性能优化

异步处理管道

def image_processing_thread():
    while True:
        ret, frame = cap.read()
        # 预处理放入队列
        queue.put(frame)

Thread(target=image_processing_thread).start()
四、效果演示示例

手势识别结果

手势类型识别条件可视化效果
👍拇指伸直,四指弯曲绿色文本 + 骨骼高亮
👌拇指食指接触,三指伸直青色文本 + 指尖光晕
🤞交叉接触,三指弯曲粉紫文本 + 爱心动画

性能指标

  • 分辨率:1280x720 时 FPS 35-45
  • 延迟:端到端约80ms
  • 多手势支持:同时识别2双手势

 完整代码

import cv2
import mediapipe as mp
import time
import math

# 初始化 MediaPipe Hands 模块
mp_hands = mp.solutions.hands
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

# 动画全局变量
HEART_ANIMATION = {}  # 存储动画状态 {hand_id: [start_time, base_position]}
ANIMATION_DURATION = 1.5  # 动画持续时间(秒)
MAX_SCALE = 2.5  # 最大缩放倍数
COLORS = [  # 颜色渐变序列
    (203, 192, 255),  # 浅粉色
    (255, 105, 180),  # 热粉色
    (255, 20, 147)  # 深粉色
]


def calculate_distance(point1, point2):
    """计算两个点之间的欧氏距离"""
    return math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2)


def count_fingers_and_get_status(hand_landmarks, hand_label):
    """计算手指伸直状态"""
    count = 0
    status = {}
    landmarks = hand_landmarks.landmark

    # 手指判断
    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

    # 拇指判断
    thumb_tip = landmarks[4]
    thumb_ip = landmarks[3]
    if hand_label == "Right":
        status["thumb"] = thumb_tip.x > thumb_ip.x
    else:
        status["thumb"] = thumb_tip.x < thumb_ip.x
    if status["thumb"]:
        count += 1

    return count, status


def detect_gesture(hand_landmarks, hand_label, status):
    """手势检测"""
    landmarks = hand_landmarks.landmark
    gesture = "Unknown"
    color = (255, 255, 255)

    thumb = status["thumb"]
    index = status["index"]
    middle = status["middle"]
    ring = status["ring"]
    pinky = status["pinky"]

    # 基础手势检测
    if thumb and not any([index, middle, ring, pinky]):
        gesture, color = "Thumbs Up", (0, 255, 0)
    elif not thumb and all([index, middle, ring, pinky]):
        gesture, color = "Thumbs Down", (0, 0, 255)
    elif index and middle and not any([thumb, ring, pinky]):
        gesture, color = "Victory", (255, 0, 255)
    elif pinky and index and not any([thumb, middle, ring]):
        gesture, color = "Rock", (0, 255, 255)
    else:
        thumb_tip = landmarks[4]
        index_tip = landmarks[8]
        if calculate_distance(thumb_tip, index_tip) < 0.05:
            if not any([middle, ring, pinky]):
                gesture, color = "OK", (255, 255, 0)

    # 比心手势检测
    if gesture == "Unknown":
        thumb_tip = landmarks[4]
        index_tip = landmarks[8]
        dist = calculate_distance(thumb_tip, index_tip)
        cross_threshold = 0.06

        if hand_label == "Right":
            cross_cond = thumb_tip.x > index_tip.x
        else:
            cross_cond = thumb_tip.x < index_tip.x

        if (dist < cross_threshold and cross_cond
                and not middle and not ring and not pinky):
            gesture, color = "Heart", (203, 192, 255)

    return gesture, color


# 绘制配置
hand_base_colors = {"Left": (255, 0, 0), "Right": (0, 255, 0)}
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)]
}

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
    gesture_info = []
    current_time = time.time()

    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
            base_color = hand_base_colors[hand_label]

            # 手指状态和手势检测
            count, finger_status = count_fingers_and_get_status(hand_landmarks, hand_label)
            total_fingers += count
            gesture, gesture_color = detect_gesture(hand_landmarks, hand_label, finger_status)

            # 获取关键点坐标
            h, w = frame.shape[:2]
            lm_coords = [(int(lm.x * w), int(lm.y * h)) for lm in hand_landmarks.landmark]

            # 绘制骨骼
            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():
                color = gesture_color if finger_status.get(finger, False) else (100, 100, 100)
                for (s, e) in segments:
                    cv2.line(frame, lm_coords[s], lm_coords[e], color, 3)
                    cv2.circle(frame, lm_coords[s], 4, color, -1)
                    cv2.circle(frame, lm_coords[e], 4, color, -1)

            # 存储手势信息
            gesture_info.append({
                "label": hand_label,
                "gesture": gesture,
                "color": gesture_color,
                "position": lm_coords[0]
            })

            # 更新爱心动画状态
            if gesture == "Heart":
                hand_id = f"{hand_label}_{lm_coords[0][0]}"
                HEART_ANIMATION[hand_id] = [current_time, lm_coords[0]]

    # 清理过期动画
    expired = [k for k, v in HEART_ANIMATION.items() if current_time - v[0] > ANIMATION_DURATION]
    for k in expired:
        del HEART_ANIMATION[k]

    # 绘制爱心动画
    for hand_id, (start_time, base_pos) in HEART_ANIMATION.items():
        elapsed = current_time - start_time
        if elapsed > ANIMATION_DURATION:
            continue

        progress = elapsed / ANIMATION_DURATION
        scale = 1 + (MAX_SCALE - 1) * progress
        alpha = int(255 * (1 - progress ** 2))
        color_idx = min(int(progress * len(COLORS)), len(COLORS) - 1)

        x, y = base_pos
        y_offset = int(100 * progress)

        # 多层爱心绘制
        for i in range(3):
            font_scale = scale * (0.8 + i * 0.2)
            thickness = 2 + i * 2
            overlay = frame.copy()
            cv2.putText(overlay, "Love", (x - 25, y - y_offset + 40),
                        cv2.FONT_HERSHEY_SIMPLEX, font_scale,
                        COLORS[color_idx], thickness)
            frame = cv2.addWeighted(overlay, alpha / 255, frame, 1 - alpha / 255, 0)

    # 绘制手势信息
    for info in gesture_info:
        text = f"{info['label']}: {info['gesture']}"
        cv2.putText(frame, text,
                    (info['position'][0] - 150, info['position'][1] - 40),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, info['color'], 2)

    # 显示FPS和总数
    fps = 1 / (current_time - prev_time) if (current_time - prev_time) > 0 else 0
    prev_time = current_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("Gesture Recognition", frame)
    if cv2.waitKey(1) & 0xFF == 27:
        break

cap.release()
cv2.destroyAllWindows()


应用场景

  1. 智能家居控制
    通过特定手势调节灯光、空调等设备,实现无接触操作。

  2. VR/AR交互
    在虚拟环境中用手势操控物体、切换场景,增强沉浸感。

  3. 医疗康复监测
    实时追踪手指活动度,辅助评估中风或术后康复进度。

  4. 工业远程操控
    用手势安全控制机械臂或设备,适用于危险作业环境。

  5. 教育互动教学
    通过手势操作3D模型、翻动课件,提升课堂互动性。

  6. 车载智能系统
    手势接打电话、调节音量,减少驾驶分心操作。

  7. 公共服务终端
    医院/银行的无接触自助服务,降低交叉感染风险。

  8. 体感游戏控制
    用手势替代传统手柄,实现拳击、抓取等游戏动作。

  9. 无障碍沟通辅助
    实时翻译手语为语音,帮助听障人士交流。

  10. 新零售体验升级
    虚拟试衣手势切换、商品收藏,优化购物体验。


总结

        评论区自然会给出总结

本文章已经生成可运行项目
### 回答1: Python的OpenCV库和MediaPipe工具包是可以一起使用的,以实现手势识别的功能。 首先,需要在Python中安装OpenCV库和MediaPipe工具包。可以使用pip命令来安装它们: ``` pip install opencv-python pip install mediapipe ``` 安装完成后,就可以开始使用了。 首先,导入必要的库: ```python import cv2 import mediapipe as mp ``` 接下来,创建一个MediaPipe的Hand对象和一个OpenCV的VideoCapture对象,用于读取摄像头输入: ```python mp_hands = mp.solutions.hands hands = mp_hands.Hands() cap = cv2.VideoCapture(0) ``` 然后,使用一个循环来读取摄像头输入并进行手势识别: ```python while True: ret, frame = cap.read() if not ret: break frame_RGB = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = hands.process(frame_RGB) if results.multi_handedness: for hand_landmarks in results.multi_hand_landmarks: # 在这里可以对hand_landmarks进行处理和识别手势的操作 cv2.imshow('Gesture Recognition', frame) if cv2.waitKey(1) == ord('q'): break ``` 在循环中,首先将读取到的帧转换为RGB格式,然后使用Hands对象的process方法对该帧进行手势识别。得到的结果存储在results变量中。 在对每个检测到的手部进行循环处理时,可以使用hand_landmarks来获取该手的关键点坐标。可以根据这些关键点的位置和运动轨迹来实现手势识别和分析。 最后,通过cv2.imshow方法显示图像,并使用cv2.waitKey方法等待用户操作。当用户按下"q"键时,循环终止,程序退出。 通过以上步骤,就可以使用Python的OpenCV库和MediaPipe工具包实现手势识别的功能了。当然,实际的手势识别算法和操作需要根据具体需求进行进一步的开发和优化。 ### 回答2: Python OpenCVMediaPipe结合使用可以实现手势识别。首先,我们需要安装必要的库和工具,包括Python、opencv-python、mediapipe和其他依赖项。 然后,我们可以使用MediaPipe提供的HandTracking模块来检测手部的关键点。它使用机器学习模型来识别手势,并返回手部关键点的坐标。我们可以通过OpenCV的视频捕捉模块读取摄像头的实时图像。 接下来,我们通过应用MediaPipe的HandTracking模块获取手部关键点的坐标,并使用OpenCV将这些坐标绘制到图像上,以便我们可以实时看到手部的位置和动作。 完成这些基本的设置后,我们可以定义特定的手势,例如拇指和食指的指尖接触,作为一个简单的示例。我们可以通过检查特定的关键点之间的距离和角度来识别这种手势。如果关键点之间的距离较小并且角度较小,则我们可以确定手势是拇指和食指的指尖接触。 我们可以使用类似的方法来识别其他手势,比如手掌的张开和闭合,拳头的形成等等。我们可以定义一系列规则和阈值来确定特定手势识别。 最后,我们可以根据检测到的手势执行特定的操作。例如,当识别到拇指和食指的指尖接触时,我们可以触发相机的快门,实现手势拍照。 总之,Python的OpenCVMediaPipe结合使用可以实现手势识别。我们可以利用MediaPipe的HandTracking模块检测手部关键点,并使用OpenCV实时绘制手势位置。通过定义特定手势的规则,我们可以识别各种手势并执行相应操作。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WenJGo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值