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

Mediapipe Pose Landmarks坐标超出[0,1]范围问题求解

解决Mediapipe PoseLandmarker坐标超出[0,1]范围的问题

核心原因

Mediapipe v0.10.x的PoseLandmarker(Task Library模块)与旧版mediapipe.solutions.pose的坐标逻辑存在差异:旧文档描述的[0,1]范围针对的是旧Solutions模块,而新版Task Library的PoseLandmarker会输出超出该范围的坐标——这是设计行为,用于表示身体部位位于画面外的预测位置。此外,图像预处理时的拉伸变形也可能加剧坐标超出的情况。

解决方案

1. 手动裁剪到[0,1]范围(简单直接)

如果业务场景必须使用严格在[0,1]内的归一化坐标,可以通过手动限制数值范围实现,这是对超出画面外的坐标的合理截断:

def clamp_normalized_coords(x, y):
    clamped_x = max(0.0, min(x, 1.0))
    clamped_y = max(0.0, min(y, 1.0))
    return clamped_x, clamped_y

在回调函数中使用:

def callback(self, result, output_image, timestamp_ms):
    if(result.pose_landmarks):
        self.result = result.pose_landmarks[0]
        for idx, elem in enumerate(self.result):
            clamped_x, clamped_y = clamp_normalized_coords(elem.x, elem.y)
            # 使用clamped_x和clamped_y进行后续处理

2. 修正图像预处理(解决拉伸导致的坐标偏差)

你的代码中直接将摄像头图像拉伸到224x224,破坏了原始图像的宽高比,会导致坐标映射出现偏差。改用保持宽高比的letterbox填充方式预处理图像,再还原坐标:

首先添加letterbox预处理函数:

def letterbox(image, target_size=(224, 224)):
    h, w = image.shape[:2]
    target_h, target_w = target_size
    # 计算缩放比例,取宽高中较小的比例
    scale = min(target_w / w, target_h / h)
    new_w = int(w * scale)
    new_h = int(h * scale)
    # 缩放图像
    resized_image = cv2.resize(image, (new_w, new_h))
    # 创建填充画布
    canvas = np.full((target_h, target_w, 3), 128, dtype=np.uint8)
    # 计算填充位置
    pad_x = (target_w - new_w) // 2
    pad_y = (target_h - new_h) // 2
    # 将缩放后的图像放到画布中央
    canvas[pad_y:pad_y+new_h, pad_x:pad_x+new_w] = resized_image
    return canvas, scale, pad_x, pad_y

修改detect_pose方法中的图像预处理部分:

def detect_pose(self):
    cap = cv2.VideoCapture(0)
    with self.PoseLandmarker.create_from_options(self.options) as landmarker:
        while cap.isOpened():
            _, image = cap.read()
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            # 使用letterbox替代直接resize
            image, scale, pad_x, pad_y = letterbox(image, (224, 224))
            mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=image)
            frame_timestamp_ms = int(time.time() * 1000)
            # 将缩放参数传递给回调
            self.scale = scale
            self.pad_x = pad_x
            self.pad_y = pad_y
            landmarker.detect_async(mp_image, frame_timestamp_ms)

在回调函数中还原到原始图像的归一化坐标,并可选裁剪:

def callback(self, result, output_image, timestamp_ms):
    if(result.pose_landmarks):
        self.result = result.pose_landmarks[0]
        target_size = 224
        for idx, elem in enumerate(self.result):
            # 还原到原始图像的归一化坐标
            original_x = (elem.x * target_size - self.pad_x) / (target_size * self.scale)
            original_y = (elem.y * target_size - self.pad_y) / (target_size * self.scale)
            # 可选:裁剪到[0,1]范围
            clamped_x = max(0.0, min(original_x, 1.0))
            clamped_y = max(0.0, min(original_y, 1.0))
            # 后续处理逻辑

3. 接受超出范围的坐标(符合模型设计)

如果业务场景需要处理身体部位在画面外的情况,可以直接使用原始输出坐标,这类坐标可用于判断部位是否在画面内,或进行后续空间位置计算。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:04:55