原理(已有 shape、pose 参数版)

  1. 根据 shape、pose 参数,得到 T-pose 下的 vertices,joints。(根据参数产生相应的形变)

  2. 根据 pose 参数求得每个关节相对其父节点的偏移量,产生一个关于点的 global transformation

  3. 将 transformation 应用到 vertices 上,根据施加动作的 vertices 产生 human mesh

参考

原理、代码:

https://khanhha.github.io/posts/SMPL-model-introduction/

模型训练(其实仅仅可视化是用不着的):

https://www.cnblogs.com/sariel-sakura/p/14321818.html

代码(numpy 版)

(放最核心的一段)

def update(self):
    """
    Called automatically when parameters are updated.

    """
    # how beta affect body shape
    v_shaped = self.shapedirs.dot(self.beta) + self.v_template

    # joints location
    self.J = self.J_regressor.dot(v_shaped) 
    pose_cube = self.pose.reshape((-1, 1, 3)) # pose_cube: (24, 1, 3)
    # rotation matrix for each joint
    self.R = self.rodrigues(pose_cube) # self.R: (24, 3, 3)
    I_cube = np.broadcast_to(
        np.expand_dims(np.eye(3), axis=0),
        (self.R.shape[0]-1, 3, 3)
    )
    lrotmin = (self.R[1:] - I_cube).ravel() # Irotmin: (23*3*3, 1)
    # how pose affect body shape in zero pose
    v_posed = v_shaped + self.posedirs.dot(lrotmin)  # posedir: (6890, 3, 207), v_posed: (6890, 3)
    # 注意 pose 参数是每个关节点相对其父节点的旋转,这一步没有相对旋转的操作,只是让 vertices 产生与 pose 有关的形变

    # world transformation of each joint
    G = np.empty((self.kintree_table.shape[1], 4, 4))  # G: (24, 4, 4)
    G[0] = self.with_zeros(np.hstack((self.R[0], self.J[0, :].reshape([3, 1]))))
    # R[0]是joint0的旋转矩阵,J[0,:]是joint0的坐标,这里把它转成列向量。hstack水平堆叠
    # with_zeros是把3x4变成齐次的4x4,底下加(0,0,0,1)
    # G[0] 表示根节点的变换(包含坐标和旋转矩阵,相对于全局坐标系)
    for i in range(1, self.kintree_table.shape[1]):
        G[i] = G[self.parent[i]].dot(
        self.with_zeros(
            np.hstack(
            [self.R[i],((self.J[i, :]-self.J[self.parent[i],:]).reshape([3,1]))]
            )
        )
        )
        # G[i]=G[[parent[i]]]乘上joint_i的相对parent[i]的变换
    # 减掉最开始的 T-pose 下 joint 位置的影响
    G = G - self.pack(   
        np.matmul(   # matmul 有广播机制,使得可以matmul
        G,
        np.hstack([self.J, np.zeros([24, 1])]).reshape([24, 4, 1])
        )
        )
    # transformation of each vertex 
    T = np.tensordot(self.weights, G, axes=[[1], [0]])    # T: (6890,4,4)
    # weights: the transformation weights for each joint (6890, 24)
    rest_shape_h = np.hstack((v_posed, np.ones([v_posed.shape[0], 1])))  # (6890, 4)
    v = np.matmul(T, rest_shape_h.reshape([-1, 4, 1])).reshape([-1, 4])[:, :3] # 形变后的顶点位置
    self.verts = v + self.trans.reshape([1, 3])  # trans 平移向量

使用体验

首先要下载 SMPL 模型到本地/服务器。

通过在 /data/liumengyin/forder1/SMPL 文件夹下运行 smpl_np.py 或者 smpl_torch.py (同时在主函数修改 shape 和 pose 参数),可以获得一个 .obj 格式的 SMPL 人体模型。

目前尚不清楚视频怎么处理。