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
相关产品推荐
相关产品推荐

