You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MediaPipe无法在实时流绘制手部关键点,遇NoneType错误求助

问题排查与修复:MediaPipe实时手部关键点绘制失败及NoneType错误

核心问题分析

  1. LIVE_STREAM模式异步逻辑误用:使用RunningMode.LIVE_STREAM时,detect_async()是异步调用,不会直接返回检测结果——结果仅通过你定义的result_callback函数返回。原代码试图直接获取detect_async()的返回值,必然得到None,这就是触发NoneType错误的根源。
  2. 图像格式不匹配:OpenCV读取的帧是BGR格式,而MediaPipe要求输入为RGB(对应mp.ImageFormat.SRGB),直接传入会导致颜色异常,甚至影响检测精度。
  3. 冗余代码:存在重复导入mediapipe、numpy的情况,无意义且易混淆。

方案一:改用IMAGE模式(推荐,逻辑更简单)

这种模式下每帧同步检测,直接获取结果,无需回调,完全适配实时摄像头场景:

import mediapipe as mp
import cv2
import numpy as np
from mediapipe import solutions
from mediapipe.framework.formats import landmark_pb2

# 配置参数
MARGIN = 10  # 像素
FONT_SIZE = 1
FONT_THICKNESS = 1
HANDEDNESS_TEXT_COLOR = (88, 205, 54)  # 亮绿色

# 初始化MediaPipe组件
BaseOptions = mp.tasks.BaseOptions
HandLandmarker = mp.tasks.vision.HandLandmarker
HandLandmarkerOptions = mp.tasks.vision.HandLandmarkerOptions
VisionRunningMode = mp.tasks.vision.RunningMode

# 创建HandLandmarker实例(使用IMAGE同步模式)
options = HandLandmarkerOptions(
    base_options=BaseOptions(model_asset_path='hand_landmarker.task'),
    running_mode=VisionRunningMode.IMAGE)

with HandLandmarker.create_from_options(options) as landmarker:
    cap = cv2.VideoCapture(0)
    
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        # 将OpenCV的BGR格式转为MediaPipe所需的RGB格式
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_rgb)
        
        # 同步检测手部关键点
        result = landmarker.detect(mp_image)
        
        # 绘制关键点及连接
        if result.hand_landmarks:
            for idx, hand_landmarks in enumerate(result.hand_landmarks):
                # 转换为MediaPipe要求的NormalizedLandmarkList格式
                hand_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
                hand_landmarks_proto.landmark.extend([
                    landmark_pb2.NormalizedLandmark(x=lm.x, y=lm.y, z=lm.z) 
                    for lm in hand_landmarks
                ])
                # 在原BGR帧上绘制(适配OpenCV显示格式)
                solutions.drawing_utils.draw_landmarks(
                    frame,
                    hand_landmarks_proto,
                    solutions.hands.HAND_CONNECTIONS,
                    solutions.drawing_styles.get_default_hand_landmarks_style(),
                    solutions.drawing_styles.get_default_hand_connections_style())
                
                # 可选:绘制左右手标签
                handedness = result.handedness[idx][0].category_name
                cv2.putText(frame, handedness, 
                            (int(hand_landmarks[0].x * frame.shape[1]) - MARGIN, 
                             int(hand_landmarks[0].y * frame.shape[0]) - MARGIN),
                            cv2.FONT_HERSHEY_SIMPLEX, FONT_SIZE, HANDEDNESS_TEXT_COLOR, FONT_THICKNESS)
        
        # 显示画面
        cv2.imshow('Hand Landmarks', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    cap.release()
    cv2.destroyAllWindows()

方案二:正确使用LIVE_STREAM模式(异步回调)

如果必须使用异步流模式,需通过线程安全队列传递结果(回调函数在后台线程执行,不能直接调用OpenCV窗口操作):

import mediapipe as mp
import cv2
import time
import numpy as np
from mediapipe import solutions
from mediapipe.framework.formats import landmark_pb2
from queue import Queue

# 配置参数
MARGIN = 10  # 像素
FONT_SIZE = 1
FONT_THICKNESS = 1
HANDEDNESS_TEXT_COLOR = (88, 205, 54)  # 亮绿色

# 线程安全队列,用于传递检测结果到主线程
result_queue = Queue(maxsize=1)

# 回调函数:将结果存入队列
def store_result(result: mp.tasks.vision.HandLandmarkerResult, output_image: mp.Image, timestamp_ms: int):
    if not result_queue.full():
        result_queue.put((result, output_image))

# 初始化MediaPipe组件
BaseOptions = mp.tasks.BaseOptions
HandLandmarker = mp.tasks.vision.HandLandmarker
HandLandmarkerOptions = mp.tasks.vision.HandLandmarkerOptions
VisionRunningMode = mp.tasks.vision.RunningMode

options = HandLandmarkerOptions(
    base_options=BaseOptions(model_asset_path='hand_landmarker.task'),
    running_mode=VisionRunningMode.LIVE_STREAM,
    result_callback=store_result)

with HandLandmarker.create_from_options(options) as landmarker:
    cap = cv2.VideoCapture(0)
    
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        # BGR转RGB
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_rgb)
        timestamp = int(round(time.time() * 1000))
        
        # 异步提交帧进行检测
        landmarker.detect_async(mp_image, timestamp)
        
        # 从队列获取结果(非阻塞)
        if not result_queue.empty():
            result, output_image = result_queue.get()
            if result.hand_landmarks:
                # 转回BGR格式用于显示
                display_frame = cv2.cvtColor(output_image.numpy_view(), cv2.COLOR_RGB2BGR)
                for idx, hand_landmarks in enumerate(result.hand_landmarks):
                    hand_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
                    hand_landmarks_proto.landmark.extend([
                        landmark_pb2.NormalizedLandmark(x=lm.x, y=lm.y, z=lm.z) 
                        for lm in hand_landmarks
                    ])
                    solutions.drawing_utils.draw_landmarks(
                        display_frame,
                        hand_landmarks_proto,
                        solutions.hands.HAND_CONNECTIONS,
                        solutions.drawing_styles.get_default_hand_landmarks_style(),
                        solutions.drawing_styles.get_default_hand_connections_style())
                    
                    # 绘制左右手标签
                    handedness = result.handedness[idx][0].category_name
                    cv2.putText(display_frame, handedness, 
                                (int(hand_landmarks[0].x * display_frame.shape[1]) - MARGIN, 
                                 int(hand_landmarks[0].y * display_frame.shape[0]) - MARGIN),
                                cv2.FONT_HERSHEY_SIMPLEX, FONT_SIZE, HANDEDNESS_TEXT_COLOR, FONT_THICKNESS)
                cv2.imshow('Hand Landmarks', display_frame)
        else:
            # 无结果时显示原帧
            cv2.imshow('Hand Landmarks', frame)
        
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    cap.release()
    cv2.destroyAllWindows()

注意事项

  • 确保hand_landmarker.task文件存在于当前工作目录,或提供完整文件路径。
  • 方案一的同步模式逻辑简单易维护,适合大多数实时场景;方案二的异步模式适合高性能设备,处理更流畅。
  • OpenCV窗口操作必须在主线程执行,LIVE_STREAM模式的回调属于后台线程,不能直接在回调中调用cv2.imshow(),否则会导致程序崩溃或无响应。

内容的提问来源于stack exchange,提问作者RohitSanjay00

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:45:02