news 2026/5/29 20:32:12

想玩转人体姿态估计?从零开始用Python解析LSP数据集(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
想玩转人体姿态估计?从零开始用Python解析LSP数据集(附完整代码)

从零解析LSP数据集:Python实战人体姿态估计数据预处理

在计算机视觉领域,人体姿态估计一直是热门研究方向之一。而想要入门这个领域,第一步往往不是搭建复杂的神经网络模型,而是学会如何处理原始数据集。Leeds Sports Pose(LSP)作为经典的人体姿态估计基准数据集,包含了2000张运动场景图像和对应的14个关键点标注,是初学者理想的练手材料。但当你真正下载这个数据集后,可能会被MATLAB格式的.mat文件和复杂的目录结构搞得一头雾水。

本文将带你一步步用Python解析LSP数据集,从读取原始数据到可视化验证,最终转换为更通用的JSON格式。不同于简单的数据集介绍,我们聚焦于"拿到数据后第一步该做什么"这个实际问题,提供可直接复用的代码和清晰的思路。无论你是刚接触计算机视觉的学生,还是想扩展技能树的开发者,都能通过这个实战案例掌握数据处理的关键技能。

1. 理解LSP数据集结构

LSP数据集解压后通常包含以下文件和目录:

LSP_dataset/ ├── images/ # 原始图像(2000张JPEG) ├── visualized/ # 带标注的可视化图像(2000张) ├── joints.mat # MATLAB格式的标注数据 └── README.txt # 数据集说明文件

关键点标注存储在joints.mat文件中,这是一个MATLAB格式的二进制文件,包含一个3×14×2000的矩阵。其中:

  • 第一个维度(3)分别表示:x坐标、y坐标和可见性(0不可见,1可见)
  • 第二个维度(14)对应14个人体关键点
  • 第三个维度(2000)对应2000张图像

14个关键点的顺序固定为:

  1. 右脚踝
  2. 右膝盖
  3. 右髋部
  4. 左髋部
  5. 左膝盖
  6. 左脚踝
  7. 右手腕
  8. 右肘部
  9. 右肩部
  10. 左肩部
  11. 左肘部
  12. 左手腕
  13. 颈部
  14. 头顶

2. 环境准备与依赖安装

在开始处理数据前,我们需要准备Python环境并安装必要的库。推荐使用Python 3.7+版本,并创建一个干净的虚拟环境:

python -m venv lsp_env source lsp_env/bin/activate # Linux/Mac # 或 lsp_env\Scripts\activate # Windows

安装所需依赖库:

pip install numpy scipy matplotlib opencv-python tqdm

这些库的作用分别是:

  • numpy:处理多维数组数据
  • scipy:读取MATLAB格式文件
  • matplotlib:数据可视化
  • opencv-python:图像处理
  • tqdm:进度条显示

3. 解析MATLAB标注文件

使用Python解析.mat文件的核心是scipy.io.loadmat函数。下面我们创建一个Python脚本来提取关键点信息:

import numpy as np from scipy.io import loadmat def parse_lsp_mat(mat_path): """ 解析LSP数据集的joints.mat文件 参数: mat_path: joints.mat文件路径 返回: joints_array: 形状为(2000, 14, 3)的numpy数组 2000张图像,14个关键点,每个点有(x,y,visibility)三个值 """ mat_data = loadmat(mat_path) joints = mat_data['joints'] # 提取关键数据 # 调整数组维度顺序为(图像数量, 关键点数量, 坐标+可见性) joints_array = np.transpose(joints, (2, 1, 0)) return joints_array

调用这个函数并检查数据:

joints_array = parse_lsp_mat('LSP_dataset/joints.mat') print(f"数据集形状: {joints_array.shape}") print("第一张图像的第一个关键点信息(x,y,visibility):") print(joints_array[0, 0])

4. 可视化验证标注数据

为了确保我们正确解析了数据,最好的方法是可视化原始图像和对应的关键点。下面是一个可视化函数:

import cv2 import matplotlib.pyplot as plt def visualize_keypoints(img_path, keypoints, visibility_threshold=0.5): """ 可视化图像和关键点 参数: img_path: 图像路径 keypoints: 14个关键点的坐标和可见性,形状为(14,3) visibility_threshold: 可见性阈值 """ img = cv2.imread(img_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转换颜色通道 plt.figure(figsize=(10, 10)) plt.imshow(img) # 定义关键点连接线(骨架) skeleton = [ (13, 12), # 头顶到颈部 (12, 8), # 颈部到右肩 (8, 7), # 右肩到右肘 (7, 6), # 右肘到右手腕 (12, 9), # 颈部到左肩 (9, 10), # 左肩到左肘 (10, 11), # 左肘到左手腕 (12, 2), # 颈部到右髋 (2, 1), # 右髋到右膝 (1, 0), # 右膝到右脚踝 (12, 3), # 颈部到左髋 (3, 4), # 左髋到左膝 (4, 5) # 左膝到左脚踝 ] # 绘制关键点 for i, (x, y, vis) in enumerate(keypoints): if vis > visibility_threshold: plt.scatter(x, y, color='red', s=50) plt.text(x, y, str(i), color='white', fontsize=8) # 绘制骨架连线 for (i, j) in skeleton: if (keypoints[i, 2] > visibility_threshold and keypoints[j, 2] > visibility_threshold): plt.plot( [keypoints[i, 0], keypoints[j, 0]], [keypoints[i, 1], keypoints[j, 1]], linewidth=2, color='green' ) plt.axis('off') plt.show()

使用示例:

# 选择第一张图像进行可视化 img_idx = 0 img_path = f"LSP_dataset/images/im{img_idx+1:04d}.jpg" keypoints = joints_array[img_idx] visualize_keypoints(img_path, keypoints)

5. 转换为通用数据格式

为了更方便地在不同框架中使用这些数据,我们可以将其转换为JSON格式。下面是将整个数据集转换为JSON的函数:

import json import os from tqdm import tqdm def convert_lsp_to_json(dataset_dir, output_path): """ 将LSP数据集转换为JSON格式 参数: dataset_dir: 数据集根目录 output_path: 输出的JSON文件路径 """ # 解析MATLAB文件 mat_path = os.path.join(dataset_dir, "joints.mat") joints_array = parse_lsp_mat(mat_path) # 准备JSON数据结构 data = { "info": { "dataset": "Leeds Sports Pose (LSP)", "keypoints": [ "right_ankle", "right_knee", "right_hip", "left_hip", "left_knee", "left_ankle", "right_wrist", "right_elbow", "right_shoulder", "left_shoulder", "left_elbow", "left_wrist", "neck", "head_top" ], "skeleton": [ [13, 12], [12, 8], [8, 7], [7, 6], [12, 9], [9, 10], [10, 11], [12, 2], [2, 1], [1, 0], [12, 3], [3, 4], [4, 5] ] }, "images": [] } # 遍历所有图像 image_dir = os.path.join(dataset_dir, "images") total_images = joints_array.shape[0] for img_idx in tqdm(range(total_images), desc="Processing images"): img_name = f"im{img_idx+1:04d}.jpg" img_path = os.path.join(image_dir, img_name) # 获取图像尺寸 img = cv2.imread(img_path) height, width = img.shape[:2] # 准备关键点数据 keypoints = [] for x, y, vis in joints_array[img_idx]: keypoints.extend([float(x), float(y), float(vis)]) # 添加到JSON结构 data["images"].append({ "file_name": img_name, "width": width, "height": height, "keypoints": keypoints }) # 保存为JSON文件 with open(output_path, "w") as f: json.dump(data, f, indent=2) print(f"转换完成,结果已保存到 {output_path}")

调用这个函数:

convert_lsp_to_json("LSP_dataset", "lsp_dataset.json")

生成的JSON文件结构如下:

{ "info": { "dataset": "Leeds Sports Pose (LSP)", "keypoints": [ "right_ankle", "right_knee", "right_hip", "left_hip", "left_knee", "left_ankle", "right_wrist", "right_elbow", "right_shoulder", "left_shoulder", "left_elbow", "left_wrist", "neck", "head_top" ], "skeleton": [ [13, 12], [12, 8], [8, 7], [7, 6], [12, 9], [9, 10], [10, 11], [12, 2], [2, 1], [1, 0], [12, 3], [3, 4], [4, 5] ] }, "images": [ { "file_name": "im0001.jpg", "width": 202, "height": 202, "keypoints": [x1,y1,vis1, x2,y2,vis2, ..., x14,y14,vis14] }, ... ] }

6. 数据增强与预处理技巧

原始数据直接用于训练可能效果不佳,这里介绍几个实用的预处理技巧:

6.1 关键点归一化

将关键点坐标归一化到[0,1]范围,使模型不受图像绝对尺寸影响:

def normalize_keypoints(keypoints, img_width, img_height): """ 归一化关键点坐标 参数: keypoints: 原始关键点数组,形状为(14,3) img_width: 图像宽度 img_height: 图像高度 返回: 归一化后的关键点数组 """ normalized = keypoints.copy() normalized[:, 0] /= img_width # x坐标归一化 normalized[:, 1] /= img_height # y坐标归一化 return normalized

6.2 数据增强示例:随机水平翻转

水平翻转是姿态估计中常用的数据增强方法,但需要注意左右关键点的对应关系:

def horizontal_flip(image, keypoints): """ 水平翻转图像和关键点 参数: image: 原始图像(numpy数组) keypoints: 原始关键点数组,形状为(14,3) 返回: flipped_image: 翻转后的图像 flipped_keypoints: 翻转后的关键点 """ # 翻转图像 flipped_image = cv2.flip(image, 1) # 定义左右对称的关键点索引 left_right_pairs = [ (0, 5), # 右脚踝 <-> 左脚踝 (1, 4), # 右膝盖 <-> 左膝盖 (2, 3), # 右髋部 <-> 左髋部 (6, 11), # 右手腕 <-> 左手腕 (7, 10), # 右肘部 <-> 左肘部 (8, 9) # 右肩部 <-> 左肩部 ] # 创建翻转后的关键点数组 flipped_keypoints = keypoints.copy() width = image.shape[1] # 处理对称关键点 for i, j in left_right_pairs: flipped_keypoints[i], flipped_keypoints[j] = keypoints[j].copy(), keypoints[i].copy() flipped_keypoints[i, 0] = width - keypoints[j, 0] # 调整x坐标 flipped_keypoints[j, 0] = width - keypoints[i, 0] # 处理不对称关键点(颈部和头顶) flipped_keypoints[12, 0] = width - keypoints[12, 0] # 颈部 flipped_keypoints[13, 0] = width - keypoints[13, 0] # 头顶 return flipped_image, flipped_keypoints

6.3 创建PyTorch数据集类

为了更方便地在PyTorch中使用LSP数据集,我们可以创建一个自定义Dataset类:

import torch from torch.utils.data import Dataset class LSPDataset(Dataset): def __init__(self, json_path, transform=None): """ 初始化LSP数据集 参数: json_path: 转换后的JSON文件路径 transform: 数据增强变换 """ with open(json_path) as f: self.data = json.load(f) self.transform = transform self.keypoint_names = self.data["info"]["keypoints"] self.skeleton = self.data["info"]["skeleton"] def __len__(self): return len(self.data["images"]) def __getitem__(self, idx): img_info = self.data["images"][idx] img_path = os.path.join("LSP_dataset", "images", img_info["file_name"]) # 加载图像 image = cv2.imread(img_path) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 获取关键点(形状为[14,3]) keypoints = np.array(img_info["keypoints"]).reshape(-1, 3) # 应用变换 if self.transform: image, keypoints = self.transform(image, keypoints) # 转换为torch张量 image = torch.from_numpy(image).permute(2, 0, 1).float() / 255.0 keypoints = torch.from_numpy(keypoints).float() return image, keypoints

使用示例:

dataset = LSPDataset("lsp_dataset.json") print(f"数据集大小: {len(dataset)}") # 获取第一个样本 image, keypoints = dataset[0] print(f"图像形状: {image.shape}") print(f"关键点形状: {keypoints.shape}")

7. 常见问题与解决方案

在实际处理LSP数据集时,可能会遇到以下问题:

7.1 关键点可见性处理

LSP数据集中的关键点可见性标记有时不够准确。建议的处理策略:

  • 训练时:对不可见关键点(x,y)坐标使用零值,并在损失函数中通过可见性标记加权
  • 评估时:只计算可见关键点的精度
def prepare_for_training(keypoints): """ 准备训练用的关键点数据 参数: keypoints: 原始关键点数组,形状为(14,3) 返回: processed: 处理后的关键点,不可见点的坐标置零 """ processed = keypoints.copy() invisible = processed[:, 2] < 0.5 # 可见性阈值 processed[invisible, :2] = 0 # 不可见点的坐标置零 return processed

7.2 图像尺寸不一致

虽然LSP图像尺寸相近,但仍存在细微差异。建议统一调整尺寸:

def resize_image_and_keypoints(image, keypoints, target_size=(256, 256)): """ 调整图像尺寸并相应调整关键点坐标 参数: image: 原始图像 keypoints: 原始关键点数组,形状为(14,3) target_size: 目标尺寸(宽,高) 返回: resized_image: 调整后的图像 resized_keypoints: 调整后的关键点 """ h, w = image.shape[:2] new_w, new_h = target_size # 调整图像尺寸 resized_image = cv2.resize(image, target_size) # 调整关键点坐标 resized_keypoints = keypoints.copy() resized_keypoints[:, 0] = keypoints[:, 0] * (new_w / w) resized_keypoints[:, 1] = keypoints[:, 1] * (new_h / h) return resized_image, resized_keypoints

7.3 数据不平衡问题

LSP数据集包含不同运动类别,某些姿势可能样本较少。解决方案:

  • 过采样:复制少数类别的样本
  • 数据增强:对少数类别使用更激进的数据增强
  • 类别加权:在损失函数中为不同类别设置不同权重
def analyze_pose_distribution(json_path): """ 分析姿势分布情况(简化版) 参数: json_path: 转换后的JSON文件路径 """ with open(json_path) as f: data = json.load(f) # 这里简化分析,实际应根据图像内容分类 # 例如通过关键点角度判断姿势类型 print("总样本数:", len(data["images"])) print("关键点名称:", data["info"]["keypoints"])

处理LSP数据集时,我发现在可视化阶段最容易出现问题。特别是当关键点坐标超出图像范围时,可视化会失败。一个实用的调试技巧是添加边界检查:

def safe_visualize(img_path, keypoints, img_size=(202, 202)): """ 带边界检查的可视化函数 参数: img_path: 图像路径 keypoints: 关键点数组,形状为(14,3) img_size: 图像尺寸(宽,高) """ # 检查关键点是否在图像范围内 valid = np.logical_and.reduce([ keypoints[:, 0] >= 0, keypoints[:, 0] < img_size[0], keypoints[:, 1] >= 0, keypoints[:, 1] < img_size[1], keypoints[:, 2] > 0.5 # 可见 ]) if not np.all(valid): print(f"警告: 图像 {img_path} 中有 {np.sum(~valid)} 个关键点在图像外") # 调用原始可视化函数 visualize_keypoints(img_path, keypoints)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 20:32:09

终极指南:免费让老旧Mac升级到最新macOS系统

终极指南&#xff1a;免费让老旧Mac升级到最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 你是否还在为苹果官方停止支持的旧款Mac感到困扰&…

作者头像 李华
网站建设 2026/5/29 20:32:07

C51中?C_INITSEG段的原理与应用解析

1. 深入解析C51中的?C_INITSEG段在Keil C51开发环境中&#xff0c;?C_INITSEG是一个经常出现在链接器MAP文件中的特殊段。这个段对于理解C51程序的初始化机制至关重要。让我们从一个实际案例开始&#xff1a;假设你在代码中声明了一个初始化变量&#xff1a;int i 0x1000;编…

作者头像 李华
网站建设 2026/5/29 20:31:24

为什么92%的Lindy项目卡在租约条款映射环节?资深租赁架构师首次公开4类非标合同智能归一化方案

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Lindy租赁管理自动化的演进逻辑与行业困局 Lindy租赁管理系统的自动化并非技术驱动的线性跃迁&#xff0c;而是由合规压力、资产周转效率瓶颈与租户体验断层共同倒逼形成的结构性演进。早期系统多依赖E…

作者头像 李华
网站建设 2026/5/29 20:27:19

EasyOCR实战:从安装到自定义模型训练,打造你的专属身份证识别工具

EasyOCR实战&#xff1a;从安装到自定义模型训练&#xff0c;打造你的专属身份证识别工具身份证识别是OCR技术中最具挑战性的场景之一。不同于普通文档&#xff0c;身份证包含密集排版、复杂背景、防伪图案等干扰因素&#xff0c;通用OCR模型往往难以达到业务要求的准确率。本文…

作者头像 李华
网站建设 2026/5/29 20:27:15

维度建模有哪些类型?详解维度建模三大模型

这两年&#xff0c;AI很热&#xff0c;很多企业都在加速推进数字化转型。但真正落地时&#xff0c;不少问题也被迅速放大了。比如指标口径不一致、报表结果对不上、业务和技术理解脱节&#xff0c;甚至连最基础的数据分析都很难稳定支撑。这些问题表面上出在应用层&#xff0c;…

作者头像 李华