美文网首页
smpl 模型是什么?

smpl 模型是什么?

作者: Joyner2018 | 来源:发表于2022-10-17 00:32 被阅读0次

    动画制作相关术语

    Vertex(顶点):动画模型可以看成多个小三角形(四边形)组成,每个小三角形就可以看成一个顶点。顶点越多,动画模型越精细。
    骨骼点:人体的一些关节点,类似于人体姿态估计的关键点。每个骨骼点都由一个三元组作为参数去控制(可以查看欧拉角,四元数相关概念)
    蒙皮:将模型从一个姿态转变为另一个姿态,使用的转换矩阵叫做蒙皮矩阵。
    骨骼蒙皮(Rig):建立骨骼点和顶点的关联关系。每个骨骼点会关联许多顶点,并且每一个顶点权重不一样。通过这种关联关系,就可以通过控制骨骼点的旋转向量来控制整个人运动。
    纹理贴图:动画人体模型的表面纹理,即衣服裤子这些。
    texture map:将3D多边形网格表面的纹理展开到2D平面,得到纹理图像
    混合形状(BlendShape):控制动画角色运动有两种,一种是上面说的利用Rig,还有一种是利用BlendShape。比如:生成一种笑脸和正常脸,那么通过BlendShape就可以自动生成二者过渡的动画。这种方式相比于利用Rig,可以不定义骨骼点,比较方便。它指相对于base shape的变形(deformation),这种deformation是通常被表示为顶点的偏移量(vertex displacements),是由某种参数有关的function确定的
    混合蒙皮技术(Blend Skinning) :一种模型网格(mesh)随内在的骨骼结构(skeletal structure)变形的方法。网格的每个顶点(vertex)对于不同的关节点有不同的影响权重(weighted influence),顶点在变形时,形变量与这个权重相关
    LBS(Linear Blend Skinning ):线性混合蒙皮。使用最广泛,但是在关节处会产生不真实的变形
    DQBS(dual-quaternion blend skinning ):双四元数混合蒙皮。
    顶点权重(vertex weights):用于变形网格mesh
    uv map:将3D多边形网格展开到2D平面得到 UV图像
    拓扑(topology):重新拓扑是将高分辨率模型转换为可用于动画的较小模型的过程。两个mesh拓扑结构相同是指两个mesh上面任一个三角面片的三个顶点的ID是一样的(如某一个三角面片三个顶点是2,5,8;另一个mesh上也必有一个2,5,8组成的三角面片)
    pose-dependent blend shape:姿势相关的混合变形
    regressor from shape to joint locations: 形状到关节位置的回归函数

    smpl介绍

    SMPL(Skinned Multi-Person Linear Model)是一种裸体的(skinned),基于顶点(vertex-based)的人体三维模型,能够精确地表示人体的不同形状(shape)和姿态(pose)。

    SMPL适用于动画领域,可以随姿态变化自然的变形,并伴随软组织的自然运动。SMPL与现有的许多图形渲染管线都是兼容的。

    SMPL是一种可学习的模型,通过训练可以更好地拟合人体的形状和不同姿态下的形变。

    它将身体形状分为identity-dependent shape和non-rigid pose-dependent shape。人体可以理解为是一个基础模型和在该模型基础上进行形变的总和,在形变基础上进行PCA,得到刻画形状的低维参数——形状参数(shape);同时,使用运动树表示人体的姿势,即运动树每个关节点和父节点的旋转关系,该关系可以表示为三维向量,最终每个关节点的局部旋转向量构成了SPML模型的姿势参数(pose)。

    这种方法与传统的LBS的最大的不同在于其提出的人体姿态影像体表形貌的方法,这种方法可以模拟人的肌肉在肢体运动过程中的凸起和凹陷。因此可以避免人体在运动过程中的表面失真,可以精准的刻画人的肌肉拉伸以及收缩运动的形貌。

    在SMPL文章中介绍了SMPL的总体模型,这个模型是通过训练得到,就是一些参数, 该模型中β和θ是其中的输入参数,其中β代表人体高矮胖瘦、头身比等比例的10个参数,是一个10-D的vector。θ是代表人体整体运动位姿和24个关节相对角度的75(24*3+3;每个关节点3个自由度,再加上3个根节点)个参数,是一个3K-D的vector(代表pose,其中K为骨架节点数,3是每个关节具有的3个自由度)。 β参数是Shape Blend Pose参数,可以通过10个增量模板控制人体形状变化: 具体而言:每个参数控制人体形态的变化可以通过动图来刻画。

    SMPL骨架的节点个数为24,标注了人体影响姿态的几个主要关节,即:

    smpl_names = [
                    'Left_Hip', 'Right_Hip', 'Waist', 'Left_Knee', 'Right_Knee',
                    'Upper_Waist', 'Left_Ankle', 'Right_Ankle', 'Chest',
                    'Left_Toe', 'Right_Toe', 'Base_Neck', 'Left_Shoulder',
                    'Right_Shoulder', 'Upper_Neck', 'Left_Arm', 'Right_Arm',
                    'Left_Elbow', 'Right_Elbow', 'Left_Wrist', 'Right_Wrist',
                    'Left_Finger', 'Right_Finger'
                ]
    
    smpl.jpg

    加上position的三个维度,则该模型最终总的输入就是10+3+3x24=85-D的数据。
    根据输入的数据,对标准模型进行一步步的变化,大概流程就是:

    1. Add shape blend shapes(缩放)
    2. Infer shape-dependent joint locations.(根据shape调整joint)
    3. Add pose blend shapes(胖瘦变形)
    4. Get the global joint location(摆pose)
    5. Do skinning(给骨架包裹外皮)

    最终生成的模型是具有6980个顶点的mesh。

    smpl 10个shape参数分别对应的物理意义:(实际有50个参数,开源的只有10个)smpl官网的unity模型可以用slider 控制参数变化

    0 代表整个人体的胖瘦和大小,初始为0的情况下,正数变瘦小,负数变大胖(±5)
    1 侧面压缩拉伸,正数压缩
    2 正数变胖大
    3 负数肚子变大很多,人体缩小
    4 代表 chest、hip、abdomen的大小,初始为0的情况下,正数变大,负数变小(±5)
    5 负数表示大肚子+整体变瘦
    6 正数表示肚子变得特别大的情况下,其他部位非常瘦小
    7 正数表示身体被纵向挤压
    8 正数表示横向表胖
    9 正数表示肩膀变宽

    2.相关参数:
    顶点 vertical: N=6890

    关节 joint: K=23

    网格对男女具有相同的拓扑结构,空间分辨率可变,干净的四元数结构,分割成多部分,有初始混合权重和骨骼蒙皮。

    代码介绍

    SMPL的python版本在官方网站有两个,分别是SMPL_python_v.1.0.0,SMPL_python_v.1.1.0。区别是:SMPL_python_v.1.0.0不完备,只提供了10个shape PCA coefficients的模型。SMPL_python_v.1.1.0提供了3个性别300shape PCA coefficients的模型。

    以SMPL_python_v1.1.0为例,其中包含了三个models和操作models的基础脚本。
    三个模型是male,female,netrual的pkl格式模型,以netrual为例,我们看下其中的数据结构。

    import pickle
    with open(model_path, 'rb') as f:
        smpl = pickle.load(f, encoding='latin1')
    
    'J_regressor_prior': [24, 6890], scipy.sparse.csc.csc_matrix
    # 面部
    'f': [13776, 3], numpy.ndarray
    # regressor array that is used to calculate the 3d joints from the position of the vertices
    'J_regressor': [24, 6890], scipy.sparse.csc.csc_matrix
    # indices of parents for each joints
    'kintree_table': [2, 24], numpy.ndarray
    'J': [24, 3], numpy.ndarray
    'weights_prior': [6890, 24], numpy.ndarray
    # linear blend skinning weights that represent how much the rotation matrix of each parr affects each vertex
    'weights': [6890, 24], numpy.ndarray
    # pose blend shape basis, pose PCA coefficients
    'posedirs': [6890, 3, 207], numpy.ndarray
    'bs_style': 'lbs'
    # the vertices of the template model
    'v_template': [6890, 3], numpy.ndarray
    # tensor of PCA shape displacements
    'shapedirs': [6890, 3, 300], chumpy.ch.Ch
    'bs_type': 'lrotmin'
    
    def forward_shape(self, betas):
        v_shaped = self.v_template + blend_shapes(betas, self.shapedirs)
        return SMPLOutput(vertices=v_shaped, betas=betas, v_shaped=v_shaped)
    
    def forward(self, betas, body_pose, global_orient, transl,
            return_verts=True, return_full_pose=False, pose2rot=True, **kwargs):
        full_pose = torch.cat([global_orient, body_pose], dim=1)
    
        vertices, joints = lbs(beta, full_pose, self.v_template, self.shapedirs,
                self.posedirs, self.J_regressor, self.parents, self.lbs_weights,
                pose2rot=pose2rot)
    
        return SMPLOutput(vertices, global_orient=global_orient, body_pose=body_pose,
            joints=joints, betas=betas, full_pose=full_pose)
    

    核心是在lbs.py中。

    # add shape contribution
    v_shaped = v_template + blend_shapes(betas, shapedirs)
    # Get the joints
    J = vertices2joints(J_regressor, v_shaped)
    # add pose blend shapes
    ident = torch.eye(3, dtype=dtype, device=device)
    pose_feature = pose[:, 1:].view(batch_size, -1, 3, 3) - ident
    # [N, P] * [P, V*3] -> [N, V, 3]
    pose_offsets = torch.matmul(pose_feature.view(batch_size, -1),
                                        posedirs).view(batch_size, -1, 3)
    v_posed = pose_offsets + v_shaped
    # Get the global joint location
    rot_mats = pose.view(batch_size, -1, 3, 3)
    J_transformed, A = batch_rigid_transform(rot_mats, J, parents, dtype=dtype)
    # Do skinning
    W = lbs_weights.unsqueeze(dim=0).expand([batch_size, -1, -1])
    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)
    v_homo = torch.matmul(T, torch.unsqueeze(v_posed_homo, dim=-1))
    
    verts = v_homo[:, :, :3, 0]
    

    代码顺序是shape blend shape + pose blend shape -> skinning。返回的是6890*3的模型顶点和n个3d关键点坐标。
    对于结构体中的s c i p y . s p a r s e . c s c . c s c _ m a t r i x scipy.sparse.csc.csc_matrixscipy.sparse.csc.csc_matrix类型,在处理过程中可以通过以下代码转为n u m p y . n d a r r a y numpy.ndarraynumpy.ndarray类型。

    def to_np(array, dtype=np.float32):
        if 'scipy.sparse' in str(type(array)):
            array = array.todense()
        return np.array(array, dtype=dtype)
    

    SMPL和SMPL-H的拓扑结构是一样的。
    python3中没法识别chumpy.ch.Ch格式,为兼容python3,需要将该该格式的数据转为numpy.ndarray格式。

    output_dict = {}
    for key, data in body_data.iteritems():
    if 'chumpy' in str(type(data)):
        output_dict[key] = np.array(data)
    else:
        output_dict[key] = data
    
    with open(out_path, 'wb') as f:
        pickle.dump(output_dict, f)
    

    有的工程会用到J_regressor_extra.npy补充额外关键点。

    J_regressor_extra: [9, 6890], numpy.ndarray
    extra_joints = vertice2joints(J_regressor_extra, smpl_output.vertices)
    joints = torch.cat([smpl_output.joints, extra_joints], dim=1)
    

    参考

    https://blog.csdn.net/u011994454/article/details/120759217
    https://www.cnblogs.com/sariel-sakura/p/14321818.html

    相关文章

      网友评论

          本文标题:smpl 模型是什么?

          本文链接:https://www.haomeiwen.com/subject/avkjzrtx.html