脚对齐调研学习笔记
目录
LODGE 音乐生成舞蹈:
脚对齐注释版:
Foot alignment: Positioning the feet in contact with the ground.
2. Foot-ground alignment: Ensuring the feet make proper contact with the ground surface.
3. Align feet to ground level.
LODGE 音乐生成舞蹈:
LODGE/metric/foot_skating.py at a898e5914d325ec1016302b2a7760e56437e2b9c · li-ronghui/LODGE · GitHub
脚对齐注释版:
# 导入必要的库和模块
from cgi import test
from turtle import left
import numpy as np
import torch
import os
import sys
# 将当前工作目录添加到系统路径,以便导入项目中的其他模块
sys.path.append(os.getcwd())
# 从自定义模块中导入SMPL-X相关函数和类
from dld.data.render_joints.smplfk import do_smplxfk, ax_from_6v, ax_to_6v, SMPLX_Skeleton
def set_on_ground_139(data, smplx_model, ground_h=0):
"""
调整SMPL-X姿势数据,使脚部接触地面
参数:
data (torch.Tensor): SMPL-X姿势参数,形状为[帧数, 139]
smplx_model (SMPLX_Skeleton): SMPLX模型实例,用于前向运动学计算
ground_h (float): 地面的高度,默认为0
返回:
torch.Tensor: 调整后的姿势数据,确保脚部接触地面
"""
length = data.shape[0]
assert len(data.shape) == 2 and data.shape[1] == 139
# 使用前向运动学计算关节位置
positions = do_smplxfk(data, smplx_model)
# 获取第一帧左右脚趾的Y轴高度
l_toe_h = positions[0, 10, 1]
r_toe_h = positions[0, 11, 1]
# 计算需要调整的高度差
if abs(l_toe_h - r_toe_h) < 0.02: # 如果双脚高度接近,取平均
height = (l_toe_h + r_toe_h)/2
else: # 否则取较小值(更接近地面的脚)
height = min(l_toe_h, r_toe_h)
# 调整全局平移的Y分量,使脚部接触地面
data[:, 5] -= (height - ground_h)
return data
def calc_foot_skating_ratio(data, ground_height=0):
"""
计算脚部滑动比例指标
参数:
data (numpy.ndarray): 输入姿势数据,形状为[帧数, 139/319/135/315]
ground_height (float): 地面高度,默认为0
返回:
tuple: 左/右脚滑动比例 (left_ratio, right_ratio)
"""
smplx = SMPLX_Skeleton() # 初始化SMPLX骨骼模型
# 数据预处理,统一转换为139维格式
if data.shape[1] == 139 or data.shape[1] == 319:
data = torch.from_numpy(data[:,:139])
elif data.shape[1] in [135, 315]:
data = torch.cat([torch.zeros([data.shape[0],4]), torch.from_numpy(data[:,:135])], dim=-1)
else:
raise ValueError(f"输入数据维度错误! 实际维度: {data.shape[1]}")
# 调整姿势数据使脚部接触地面
data = set_on_ground_139(data, smplx, ground_height)
# 前向运动学计算关节三维坐标
with torch.no_grad():
model_xp = do_smplxfk(data, smplx)
# 定义相关关节索引(左/右脚踝和脚趾)
l_ankle_idx, r_ankle_idx, l_foot_idx, r_foot_idx = 7, 8, 10, 11
relevant_joints = [l_ankle_idx, r_ankle_idx, l_foot_idx, r_foot_idx]
# 提取相关关节坐标并计算速度
pred_joint_xyz = model_xp[:, relevant_joints, :] # [T, 4, 3]
pred_vel = torch.zeros_like(pred_joint_xyz)
pred_vel[:-1] = pred_joint_xyz[1:] - pred_joint_xyz[:-1] # 速度计算(位移差)
# 获取脚部高度信息
left_foot_y_ankle = model_xp[:, l_ankle_idx, 1] # 左脚踝Y坐标
right_foot_y_ankle = model_xp[:, r_ankle_idx, 1] # 右脚踝Y坐标
left_foot_y_toe = model_xp[:, l_foot_idx, 1] # 左脚趾Y坐标
right_foot_y_toe = model_xp[:, r_foot_idx, 1] # 右脚趾Y坐标
# 创建脚部接触地面的掩码(基于高度阈值)
left_fc_mask = (left_foot_y_ankle <= (ground_height+0.08)) & (left_foot_y_toe <= (ground_height+0.05))
right_fc_mask = (right_foot_y_ankle <= (ground_height+0.08)) & (right_foot_y_toe <= (ground_height+0.05))
# 分离左右脚的速度数据
left_pred_vel = torch.cat([pred_vel[:, 0:1, :], pred_vel[:, 2:3, :]], dim=1) # 左脚踝和脚趾速度
right_pred_vel = torch.cat([pred_vel[:, 1:2, :], pred_vel[:, 3:4, :]], dim=1) # 右脚踝和脚趾速度
# 将非接触期的速度置零
left_pred_vel[~left_fc_mask] = 0
right_pred_vel[~right_fc_mask] = 0
# 统计脚部接触地面的帧数
left_static_num = torch.sum(left_fc_mask)
right_static_num = torch.sum(right_fc_mask)
# 计算水平面(XZ方向)的速度幅值
left_velocity_tangent = torch.cat([left_pred_vel[..., [0]], left_pred_vel[..., [2]]], dim=-1)
left_speed = torch.abs(torch.mean(left_velocity_tangent, dim=-1)) # [T, 2] -> [T]
right_velocity_tangent = torch.cat([right_pred_vel[..., [0]], right_pred_vel[..., [2]]], dim=-1)
right_speed = torch.abs(torch.mean(right_velocity_tangent, dim=-1))
# 检测滑动帧(速度超过阈值0.01)
left_slide_frames = torch.any(left_speed > 0.01, dim=-1)
right_slide_frames = torch.any(right_speed > 0.01, dim=-1)
# 计算滑动比例
left_ratio = torch.sum(left_slide_frames) / left_static_num if left_static_num > 0 else 0
right_ratio = torch.sum(right_slide_frames) / right_static_num if right_static_num > 0 else 0
return left_ratio.item(), right_ratio.item()
if __name__ == '__main__':
# 测试参数设置
test_dir = '/path/to/test/data' # 替换为实际测试数据路径
ground_height = 0 # 设定地面高度
# 初始化统计列表
left_ratios, right_ratios = [], []
# 遍历测试目录下的所有npy文件
for file in os.listdir(test_dir):
if not file.endswith('.npy'):
continue
# 加载数据并计算滑动比例
data = np.load(os.path.join(test_dir, file))
l_ratio, r_ratio = calc_foot_skating_ratio(data, ground_height)
# 收集结果
left_ratios.append(l_ratio)
right_ratios.append(r_ratio)
# 输出统计结果
print(f"测试目录: {test_dir}")
print(f"左脚平均滑动比例: {np.mean(left_ratios):.4f}")
print(f"右脚平均滑动比例: {np.mean(right_ratios):.4f}")
print(f"整体平均滑动比例: {(np.mean(left_ratios)+np.mean(right_ratios))/2:.4f}")