SMPL简介
SMPL: A Skinned Multi-Person Linear Model 是一种3D人体建模方法.在数字人或者人物角色三维重建领域有着广泛应用 支持人体的各种形状及动作 可以简单理解为通过训练获取的人物模型 常用的模型有 smpl(身体模型),mano(手部模型),smplh(身体+手部),flame(脸部),smplx(身体+手部+脸部) 官网:SMPL-X https://smpl.is.tue.mpg.de
基本原理
a) 默认模版姿态
b) 添加Beta姿态参数,如图体型Shape已经发生变化
#10个shape参数 前两个参数主要影响身高跟胖瘦
betas = torch.randn([1, model.num_betas], dtype=torch.float32)
c) 添加pose动作参数,还没有发生变化,需要最后一步LBS蒙皮算法
#23个joint关节点参数,每个参数都有xyz3个欧拉角弧度表示
pose = torch.zeros([1,23*3], dtype=torch.float32)
#示例
左腿按Z轴旋转1弧度,大概是40多度
pose[:,2] = 1
#又腿按Z轴旋转-1弧度
pose[:,5] = -1
d) 通过beta跟pose参数产生最终变化
#初始化加载模型
model = smplx.create(model_folder='模型路径', model_type='smpl')
#添加随机beta参数
betas = torch.randn([1, model.num_betas], dtype=torch.float32)
#添加动作参数
pose = torch.zeros([1,23*3], dtype=torch.float32)
pose[:,2] = 1
pose[:,5] = -1
#通过lbs最终算法获取最终变化的顶点vertexs跟joint关节点数据
output = model(betas=betas,body_pose=pose,
return_verts=True)
#顶点数据
vertices = output.vertices.detach().cpu().numpy().squeeze()
#关节点数据
joints = output.joints.detach().cpu().numpy().squeeze()
LBS核心蒙皮代码讲解
def lbs(
betas: Tensor,
pose: Tensor,
v_template: Tensor,
shapedirs: Tensor,
posedirs: Tensor,
J_regressor: Tensor,
parents: Tensor,
lbs_weights: Tensor,
pose2rot: bool = True,
) -> Tuple[Tensor, Tensor]:
batch_size = max(betas.shape[0], pose.shape[0])
device, dtype = betas.device, betas.dtype
# Add shape contribution
# 通过beta姿态参数跟训练好的shapedirs pca主成分分析获取变化后的shape
v_shaped = v_template + blend_shapes(betas, shapedirs)
# Get the joints
# NxJx3 array
#通过J_regressor回归关节点训练数据获取变化后的Joints关节点数据
J = vertices2joints(J_regressor, v_shaped)
# 3. Add pose blend shapes
# N x J x 3 x 3
#初始化对角矩阵
ident = torch.eye(3, dtype=dtype, device=device)
if pose2rot:
#通过罗德里格斯算法获取pose变化后的矩阵
rot_mats = batch_rodrigues(pose.view(-1, 3)).view(
[batch_size, -1, 3, 3])
#获取xyz旋转跟平移矩阵
pose_feature = (rot_mats[:, 1:, :, :] - ident).view([batch_size, -1])
# (N x P) x (P, V * 3) -> N x V x 3
#通过矩阵乘积获取变化后的pose坐标
#posedirs跟shapedirs一样,也是训练好的pose相关PCA主成分数据
pose_offsets = torch.matmul(
pose_feature, posedirs).view(batch_size, -1, 3)
else:
pose_feature = pose[:, 1:].view(batch_size, -1, 3, 3) - ident
rot_mats = pose.view(batch_size, -1, 3, 3)
pose_offsets = torch.matmul(pose_feature.view(batch_size, -1),
posedirs).view(batch_size, -1, 3)
#通过pose变化后的顶点数据
v_posed = pose_offsets + v_shaped
# 4. Get the global joint location
#通过父子及关系获取关节点变化
J_transformed, A = batch_rigid_transform(rot_mats, J, parents, dtype=dtype)
# 5. Do skinning:
# W is N x V x (J + 1)
#蒙皮权重
W = lbs_weights.unsqueeze(dim=0).expand([batch_size, -1, -1])
# (N x V x (J + 1)) x (N x (J + 1) x 16)
num_joints = J_regressor.shape[0]
#获取默认T-POSE数据.所有旋转平移变化都是Tpose基础上变化的
T = torch.matmul(W, A.view(batch_size, num_joints, 16)) \
.view(batch_size, -1, 4, 4)
#改成齐次坐标
homogen_coord = torch.ones([batch_size, v_posed.shape[1], 1],
dtype=dtype, device=device)
v_posed_homo = torch.cat([v_posed, homogen_coord], dim=2)
#T-pose矩阵乘积齐次坐标获取最终的顶点变化
v_homo = torch.matmul(T, torch.unsqueeze(v_posed_homo, dim=-1))
#顶点数据
verts = v_homo[:, :, :3, 0]
return verts, J_transformed
更详细的环境搭建,代码详解,实际应用可查看下面视频