//  [python]
def _decode_vertices(self, sample, transformer_out): 
        template = sample["template"]
        # jaw = sample["gt_jaw"]
        # exp = sample["exp"]

        # self.flame.v_template = template.squeeze(1).view(-1, 3)
        transformer_out = self.post_transformer(transformer_out)
        batch_size = transformer_out.shape[0]
        T_size = transformer_out.shape[1]
        pose_params = self.flame.eye_pose.expand(batch_size*T_size, -1)

        vector_idx = 0
        if self.predict_jaw:
            rotation_rep_size_ = rotation_rep_size(self.rotation_representation)
            jaw_pose = transformer_out[..., :rotation_rep_size_].view(batch_size*T_size, -1)
            vector_idx += rotation_rep_size_
            if self.rotation_representation in ['mat', 'matrix']:
                jaw_pose = jaw_pose.view(-1, 3, 3) 
                U, S, Vh = torch.linalg.svd(jaw_pose) # do SVD to ensure orthogonality
                jaw_pose = U.contiguous()
            
        else: 
            jaw = sample["gt_jaw"][:, :T_size, ...]
            jaw_pose = jaw.view(batch_size*T_size, -1)
        if self.predict_exp: 
            expression_params = transformer_out[..., vector_idx:].view(batch_size*T_size, -1)
            vector_idx += self.flame_config.n_exp
        else: 
            exp = sample["gt_exp"][:, :T_size, ...]
            expression_params = exp.view(batch_size*T_size, -1)
        
        sample["predicted_exp"] = expression_params.view(batch_size,T_size, -1)
        sample["predicted_jaw"] = jaw_pose.view(batch_size,T_size, -1)
        
        assert vector_idx == transformer_out.shape[-1]

        B = transformer_out.shape[0]
        if "gt_shape" not in sample.keys():
            template = sample["template"]
            assert B == 1, "Batch size must be 1 if we want to change the template inside FLAME"
            self.flame.v_template = template.squeeze(1).view(-1, 3)
            # shape_params = torch.zeros((B*T_size, self.flame_config.n_shape), device=transformer_out.device)
            shape_params = torch.zeros((B, T_size, self.flame_config.n_shape), device=transformer_out.device)
        else: 
            # sample["gt_shape"].shape = (B, n_shape) -> add T dimension and repeat
            shape_params = sample["gt_shape"][:, None, ...].repeat(1, T_size, 1)

        flame_jaw_pose = convert_rot(jaw_pose, self.rotation_representation, self.flame_rotation_rep)
        pose_params = torch.cat([ pose_params[..., :3], flame_jaw_pose], dim=-1)

        with torch.no_grad():
            # vertice_neutral, _, _ = self.flame.forward(shape_params[:, 0, ...], torch.zeros_like(expression_params[0:1, ...])) # compute neutral shape
            zero_exp = torch.zeros((B, expression_params.shape[1]), dtype=shape_params.dtype, device=shape_params.device)
            # compute neutral shape for each batch (but not each frame, unnecessary)
            vertices_neutral, _, _ = self.flame.forward(shape_params[:, 0, ...], zero_exp) # compute neutral shape
            vertices_neutral = vertices_neutral.contiguous().view(vertices_neutral.shape[0], -1)[:, None, ...]

        shape_params = shape_params.view(B * T_size, -1)
        vertices_out, _, _ = self.flame(shape_params, expression_params, pose_params)
        vertices_out = vertices_out.contiguous().view(batch_size, T_size, -1)
        vertices_out = vertices_out - vertices_neutral # compute the offset that is then added to the template shape
        
        return vertices_out