File size: 4,658 Bytes
c06d036
 
 
 
 
 
 
9efc8b7
c06d036
 
 
 
4f44d19
c06d036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b934a8
c06d036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f44d19
 
c06d036
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import smplx
import torch
import pickle
import numpy as np
import os
import trimesh
import cv2
os.environ['PYOPENGL_PLATFORM'] = 'egl'
import pyrender

root_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../..'))
def get_smplx_model(bs, deivce):
    smpl_model = smplx.create(os.path.join(root_dir, 'deps/smplx/models'),
                                    model_type='smplx',
                                    gender='male',
                                    ext='npz',
                                    batch_size=bs,
                                    ).to(device)
    return smpl_model.eval()


def render_motion_to_video(motion, device, output_path, title, step=2):
    length = motion['transl'].shape[0]
    model = get_smplx_model(length, device)
    vertices = model(
        body_pose = torch.tensor(motion['body_pose'], dtype=torch.float32).to(device),
        transl = torch.tensor(motion['transl'], dtype=torch.float32).to(device),
        global_orient = torch.tensor(motion['global_orient'], dtype=torch.float32).to(device)
    ).vertices.detach().cpu().numpy()
    faces = model.faces
    
    temp_dir = "temp_render_images"
    os.makedirs(temp_dir, exist_ok=True)
    
    width, height = 854, 480
    images = []
    for i in range(0, length, step):
        trimesh_mesh = trimesh.Trimesh(vertices=vertices[i], faces=faces)
        mesh = pyrender.Mesh.from_trimesh(trimesh_mesh)
        
        scene = pyrender.Scene(ambient_light=[0.5, 0.5, 0.5])
        scene.add(mesh)
        
        camera = pyrender.PerspectiveCamera(yfov=np.pi / 3.0)
        angle = -np.pi / 8
        camera_pose = np.array([
            [1, 0, 0, 0],
            [0, np.cos(angle), -np.sin(angle), 2.5],
            [0, np.sin(angle), np.cos(angle), 5],
            [0, 0, 0, 1]
        ])
        scene.add(camera, pose=camera_pose)
        light = pyrender.DirectionalLight(color=[1.0, 1.0, 1.0], intensity=2.0)
        scene.add(light, pose=camera_pose)
        
        r = pyrender.OffscreenRenderer(width, height)
        color, _ = r.render(scene)
        r.delete()
        
        image = cv2.cvtColor(color, cv2.COLOR_RGB2BGR)
        
        img_path = os.path.join(temp_dir, f"frame_{i:04d}.png")
        cv2.imwrite(img_path, image)
        
        font = cv2.FONT_HERSHEY_SIMPLEX
        font_scale = 1.0
        font_color = (0, 55, 0)  # 白色
        font_thickness = 2
        text_size = cv2.getTextSize(title, font, font_scale, font_thickness)[0]
        text_x = (image.shape[1] - text_size[0]) // 2  # 水平居中
        text_y = image.shape[0] - 30  # 底部上方30像素
        cv2.putText(image, title, (text_x, text_y), font, font_scale, font_color, font_thickness)
        
        images.append(image)
    
    if images:
        height, width, _ = images[0].shape
        sampled_fps = 20 / step
        temp_video_path = output_path.replace('.mp4', '_temp.mp4')

        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        video = cv2.VideoWriter(temp_video_path, fourcc, sampled_fps, (width, height))
        for image in images:
            video.write(image)
        video.release()

        try:
            import subprocess
            cmd = [
                'ffmpeg',
                '-y',
                '-i', temp_video_path,
                '-filter:v', f'minterpolate=fps={24}:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1',
                '-c:v', 'libx264',
                '-preset', 'medium',
                '-crf', '18',
                output_path
            ]
            
            subprocess.run(cmd, check=True)
            print(f"帧插值完成,最终视频保存至: {output_path}")
            os.remove(temp_video_path)
        except Exception as e:
            print(f"帧插值失败: {e}")
            os.rename(temp_video_path, output_path)
            print(f"使用原始视频: {output_path}")
    else:
        print("没有图像可用于创建视频")
    
    for file in os.listdir(temp_dir):
        os.remove(os.path.join(temp_dir, file))
    os.rmdir(temp_dir)


from argparse import ArgumentParser
if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument("--motion_path", type=str, required=True)
    parser.add_argument("--title", type=str, default="rendered_motion.mp4")

    args = parser.parse_args()
    with open(args.motion_path, 'rb') as f:
        motion = pickle.load(f)
    output_path = args.motion_path.replace(".pkl", ".mp4")
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    print(args.title)
    render_motion_to_video(motion, device, output_path, args.title)