SMPL 数字人模型原理跟代码 debug模式讲解

 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

 

更详细的环境搭建,代码详解,实际应用可查看下面视频

https://www.bilibili.com/video/BV1dDVLecEZV/

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值