Spaces:
Runtime error
Runtime error
Commit
·
f0a0274
1
Parent(s):
90b88a3
Update render method
Browse files- .gitignore +0 -1
- app.py +9 -56
- mGPT/render/blender/render.py +177 -0
- render.py +76 -0
.gitignore
CHANGED
|
@@ -8,7 +8,6 @@ __pycache__/
|
|
| 8 |
.DS_Store
|
| 9 |
pyglet
|
| 10 |
app2.py
|
| 11 |
-
render.py
|
| 12 |
cache
|
| 13 |
|
| 14 |
# Distribution / packaging
|
|
|
|
| 8 |
.DS_Store
|
| 9 |
pyglet
|
| 10 |
app2.py
|
|
|
|
| 11 |
cache
|
| 12 |
|
| 13 |
# Distribution / packaging
|
app.py
CHANGED
|
@@ -7,27 +7,18 @@ os.system('pip install pyglet==1.4.0a1')
|
|
| 7 |
os.system('pip install triangle==20220202')
|
| 8 |
|
| 9 |
import gradio as gr
|
| 10 |
-
import random
|
| 11 |
import torch
|
| 12 |
import time
|
| 13 |
-
import cv2
|
| 14 |
import numpy as np
|
| 15 |
-
import OpenGL.GL as gl
|
| 16 |
-
import imageio
|
| 17 |
import pytorch_lightning as pl
|
| 18 |
-
import
|
| 19 |
from pathlib import Path
|
| 20 |
from mGPT.data.build_data import build_data
|
| 21 |
from mGPT.models.build_model import build_model
|
| 22 |
from mGPT.config import parse_args
|
| 23 |
-
from scipy.spatial.transform import Rotation as RRR
|
| 24 |
-
import mGPT.render.matplot.plot_3d_global as plot_3d
|
| 25 |
-
from mGPT.render.pyrender.hybrik_loc2rot import HybrIKJointsToRotmat
|
| 26 |
-
from mGPT.render.pyrender.smpl_render import SMPLRender
|
| 27 |
from transformers import WhisperProcessor, WhisperForConditionalGeneration
|
| 28 |
import librosa
|
| 29 |
from huggingface_hub import snapshot_download
|
| 30 |
-
import eventlet
|
| 31 |
|
| 32 |
# Load model
|
| 33 |
cfg = parse_args(phase="webui") # parse config file
|
|
@@ -105,55 +96,17 @@ def render_motion(data, feats, method='fast'):
|
|
| 105 |
fname = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(
|
| 106 |
time.time())) + str(np.random.randint(10000, 99999))
|
| 107 |
video_fname = fname + '.mp4'
|
| 108 |
-
feats_fname = fname + '.npy'
|
|
|
|
| 109 |
output_npy_path = os.path.join(output_dir, feats_fname)
|
|
|
|
| 110 |
output_mp4_path = os.path.join(output_dir, video_fname)
|
| 111 |
np.save(output_npy_path, feats)
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
pose_generator = HybrIKJointsToRotmat()
|
| 118 |
-
pose = pose_generator(data)
|
| 119 |
-
pose = np.concatenate([
|
| 120 |
-
pose,
|
| 121 |
-
np.stack([np.stack([np.eye(3)] * pose.shape[0], 0)] * 2, 1)
|
| 122 |
-
], 1)
|
| 123 |
-
shape = [768, 768]
|
| 124 |
-
render = SMPLRender(cfg.RENDER.SMPL_MODEL_PATH)
|
| 125 |
-
|
| 126 |
-
r = RRR.from_rotvec(np.array([np.pi, 0.0, 0.0]))
|
| 127 |
-
pose[:, 0] = np.matmul(r.as_matrix().reshape(1, 3, 3), pose[:, 0])
|
| 128 |
-
vid = []
|
| 129 |
-
aroot = data[:, 0]
|
| 130 |
-
aroot[:, 1:] = -aroot[:, 1:]
|
| 131 |
-
params = dict(pred_shape=np.zeros([1, 10]),
|
| 132 |
-
pred_root=aroot,
|
| 133 |
-
pred_pose=pose)
|
| 134 |
-
render.init_renderer([shape[0], shape[1], 3], params)
|
| 135 |
-
for i in range(data.shape[0]):
|
| 136 |
-
renderImg = render.render(i)
|
| 137 |
-
vid.append(renderImg)
|
| 138 |
-
|
| 139 |
-
out = np.stack(vid, axis=0)
|
| 140 |
-
output_gif_path = output_mp4_path[:-4] + '.gif'
|
| 141 |
-
imageio.mimwrite(output_gif_path, out, duration=50)
|
| 142 |
-
out_video = mp.VideoFileClip(output_gif_path)
|
| 143 |
-
out_video.write_videofile(output_mp4_path)
|
| 144 |
-
del out, render
|
| 145 |
-
|
| 146 |
-
elif method == 'fast':
|
| 147 |
-
output_gif_path = output_mp4_path[:-4] + '.gif'
|
| 148 |
-
if len(data.shape) == 3:
|
| 149 |
-
data = data[None]
|
| 150 |
-
if isinstance(data, torch.Tensor):
|
| 151 |
-
data = data.cpu().numpy()
|
| 152 |
-
pose_vis = plot_3d.draw_to_batch(data, [''], [output_gif_path])
|
| 153 |
-
out_video = mp.VideoFileClip(output_gif_path)
|
| 154 |
-
out_video.write_videofile(output_mp4_path)
|
| 155 |
-
del pose_vis
|
| 156 |
-
|
| 157 |
return output_mp4_path, video_fname, output_npy_path, feats_fname
|
| 158 |
|
| 159 |
|
|
|
|
| 7 |
os.system('pip install triangle==20220202')
|
| 8 |
|
| 9 |
import gradio as gr
|
|
|
|
| 10 |
import torch
|
| 11 |
import time
|
|
|
|
| 12 |
import numpy as np
|
|
|
|
|
|
|
| 13 |
import pytorch_lightning as pl
|
| 14 |
+
import subprocess
|
| 15 |
from pathlib import Path
|
| 16 |
from mGPT.data.build_data import build_data
|
| 17 |
from mGPT.models.build_model import build_model
|
| 18 |
from mGPT.config import parse_args
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
from transformers import WhisperProcessor, WhisperForConditionalGeneration
|
| 20 |
import librosa
|
| 21 |
from huggingface_hub import snapshot_download
|
|
|
|
| 22 |
|
| 23 |
# Load model
|
| 24 |
cfg = parse_args(phase="webui") # parse config file
|
|
|
|
| 96 |
fname = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(
|
| 97 |
time.time())) + str(np.random.randint(10000, 99999))
|
| 98 |
video_fname = fname + '.mp4'
|
| 99 |
+
feats_fname = f"{fname}_feats" + '.npy'
|
| 100 |
+
data_fname = f"{fname}_joints" + '.npy'
|
| 101 |
output_npy_path = os.path.join(output_dir, feats_fname)
|
| 102 |
+
output_joints_path = os.path.join(output_dir, data_fname)
|
| 103 |
output_mp4_path = os.path.join(output_dir, video_fname)
|
| 104 |
np.save(output_npy_path, feats)
|
| 105 |
+
np.save(output_joints_path, data)
|
| 106 |
+
|
| 107 |
+
cmd3 = ["xvfb-run", "python", "-m", "render", "--joints_path", output_joints_path, "--method", method, "--output_mp4_path", output_mp4_path, "--smpl_model_path", cfg.RENDER.SMPL_MODEL_PATH]
|
| 108 |
+
subprocess.run(cmd3)
|
| 109 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
return output_mp4_path, video_fname, output_npy_path, feats_fname
|
| 111 |
|
| 112 |
|
mGPT/render/blender/render.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
import smplx
|
| 5 |
+
# import bpy
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
from .camera import Camera
|
| 9 |
+
from .floor import get_trajectory, plot_floor, show_traj
|
| 10 |
+
from .sampler import get_frameidx
|
| 11 |
+
from .scene import setup_scene # noqa
|
| 12 |
+
from .tools import delete_objs, load_numpy_vertices_into_blender, style_detect
|
| 13 |
+
from .vertices import prepare_vertices
|
| 14 |
+
from mGPT.utils.joints import smplh_to_mmm_scaling_factor
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def prune_begin_end(data, perc):
|
| 18 |
+
to_remove = int(len(data) * perc)
|
| 19 |
+
if to_remove == 0:
|
| 20 |
+
return data
|
| 21 |
+
return data[to_remove:-to_remove]
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def render_current_frame(path):
|
| 25 |
+
bpy.context.scene.render.filepath = path
|
| 26 |
+
bpy.ops.render.render(use_viewport=True, write_still=True)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def render(npydata,
|
| 30 |
+
frames_folder,
|
| 31 |
+
*,
|
| 32 |
+
mode,
|
| 33 |
+
model_path,
|
| 34 |
+
faces_path,
|
| 35 |
+
gt=False,
|
| 36 |
+
exact_frame=None,
|
| 37 |
+
num=8,
|
| 38 |
+
downsample=True,
|
| 39 |
+
canonicalize=True,
|
| 40 |
+
always_on_floor=False,
|
| 41 |
+
denoising=True,
|
| 42 |
+
oldrender=True,
|
| 43 |
+
res="high",
|
| 44 |
+
init=True,
|
| 45 |
+
accelerator='gpu',
|
| 46 |
+
device=[0]):
|
| 47 |
+
if init:
|
| 48 |
+
# Setup the scene (lights / render engine / resolution etc)
|
| 49 |
+
setup_scene(res=res,
|
| 50 |
+
denoising=denoising,
|
| 51 |
+
oldrender=oldrender,
|
| 52 |
+
accelerator=accelerator,
|
| 53 |
+
device=device)
|
| 54 |
+
|
| 55 |
+
is_mesh, is_smplx, jointstype = style_detect(npydata)
|
| 56 |
+
|
| 57 |
+
if not is_mesh:
|
| 58 |
+
npydata = npydata * smplh_to_mmm_scaling_factor
|
| 59 |
+
|
| 60 |
+
if is_smplx:
|
| 61 |
+
smplx_model_male = smplx.create(model_path,
|
| 62 |
+
model_type='smplx',
|
| 63 |
+
gender='male',
|
| 64 |
+
ext='npz',
|
| 65 |
+
num_betas=10,
|
| 66 |
+
flat_hand_mean=True,
|
| 67 |
+
use_pca=False)
|
| 68 |
+
faces_path = smplx_model_male.faces
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# Put everything in this folder
|
| 73 |
+
if mode == "video":
|
| 74 |
+
if always_on_floor:
|
| 75 |
+
frames_folder += "_of"
|
| 76 |
+
os.makedirs(frames_folder, exist_ok=True)
|
| 77 |
+
# if it is a mesh, it is already downsampled
|
| 78 |
+
if downsample and not is_mesh:
|
| 79 |
+
npydata = npydata[::8]
|
| 80 |
+
elif mode == "sequence":
|
| 81 |
+
img_name, ext = os.path.splitext(frames_folder)
|
| 82 |
+
if always_on_floor:
|
| 83 |
+
img_name += "_of"
|
| 84 |
+
img_path = f"{img_name}{ext}"
|
| 85 |
+
|
| 86 |
+
elif mode == "frame":
|
| 87 |
+
img_name, ext = os.path.splitext(frames_folder)
|
| 88 |
+
if always_on_floor:
|
| 89 |
+
img_name += "_of"
|
| 90 |
+
img_path = f"{img_name}_{exact_frame}{ext}"
|
| 91 |
+
|
| 92 |
+
# remove X% of begining and end
|
| 93 |
+
# as it is almost always static
|
| 94 |
+
# in this part
|
| 95 |
+
if mode == "sequence":
|
| 96 |
+
perc = 0.2
|
| 97 |
+
npydata = prune_begin_end(npydata, perc)
|
| 98 |
+
|
| 99 |
+
if is_mesh:
|
| 100 |
+
from .meshes import Meshes
|
| 101 |
+
data = Meshes(npydata,
|
| 102 |
+
gt=gt,
|
| 103 |
+
mode=mode,
|
| 104 |
+
faces_path=faces_path,
|
| 105 |
+
canonicalize=canonicalize,
|
| 106 |
+
always_on_floor=always_on_floor,
|
| 107 |
+
is_smplx=is_smplx)
|
| 108 |
+
else:
|
| 109 |
+
from .joints import Joints
|
| 110 |
+
data = Joints(npydata,
|
| 111 |
+
gt=gt,
|
| 112 |
+
mode=mode,
|
| 113 |
+
canonicalize=canonicalize,
|
| 114 |
+
always_on_floor=always_on_floor,
|
| 115 |
+
jointstype=jointstype)
|
| 116 |
+
|
| 117 |
+
# Number of frames possible to render
|
| 118 |
+
nframes = len(data)
|
| 119 |
+
|
| 120 |
+
# Show the trajectory
|
| 121 |
+
show_traj(data.trajectory)
|
| 122 |
+
|
| 123 |
+
# Create a floor
|
| 124 |
+
plot_floor(data.data, big_plane=False)
|
| 125 |
+
|
| 126 |
+
# initialize the camera
|
| 127 |
+
camera = Camera(first_root=data.get_root(0), mode=mode, is_mesh=is_mesh)
|
| 128 |
+
|
| 129 |
+
frameidx = get_frameidx(mode=mode,
|
| 130 |
+
nframes=nframes,
|
| 131 |
+
exact_frame=exact_frame,
|
| 132 |
+
frames_to_keep=num)
|
| 133 |
+
|
| 134 |
+
nframes_to_render = len(frameidx)
|
| 135 |
+
|
| 136 |
+
# center the camera to the middle
|
| 137 |
+
if mode == "sequence":
|
| 138 |
+
camera.update(data.get_mean_root())
|
| 139 |
+
|
| 140 |
+
imported_obj_names = []
|
| 141 |
+
for index, frameidx in enumerate(frameidx):
|
| 142 |
+
if mode == "sequence":
|
| 143 |
+
frac = index / (nframes_to_render - 1)
|
| 144 |
+
mat = data.get_sequence_mat(frac)
|
| 145 |
+
else:
|
| 146 |
+
mat = data.mat
|
| 147 |
+
camera.update(data.get_root(frameidx))
|
| 148 |
+
|
| 149 |
+
islast = index == (nframes_to_render - 1)
|
| 150 |
+
|
| 151 |
+
objname = data.load_in_blender(frameidx, mat)
|
| 152 |
+
name = f"{str(index).zfill(4)}"
|
| 153 |
+
|
| 154 |
+
if mode == "video":
|
| 155 |
+
path = os.path.join(frames_folder, f"frame_{name}.png")
|
| 156 |
+
else:
|
| 157 |
+
path = img_path
|
| 158 |
+
|
| 159 |
+
if mode == "sequence":
|
| 160 |
+
imported_obj_names.extend(objname)
|
| 161 |
+
elif mode == "frame":
|
| 162 |
+
camera.update(data.get_root(frameidx))
|
| 163 |
+
|
| 164 |
+
if mode != "sequence" or islast:
|
| 165 |
+
render_current_frame(path)
|
| 166 |
+
delete_objs(objname)
|
| 167 |
+
|
| 168 |
+
bpy.ops.wm.save_as_mainfile(filepath=frames_folder.replace('.png','.blend').replace('_frames','.blend'))
|
| 169 |
+
|
| 170 |
+
# remove every object created
|
| 171 |
+
delete_objs(imported_obj_names)
|
| 172 |
+
delete_objs(["Plane", "myCurve", "Cylinder"])
|
| 173 |
+
|
| 174 |
+
if mode == "video":
|
| 175 |
+
return frames_folder
|
| 176 |
+
else:
|
| 177 |
+
return img_path
|
render.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from argparse import ArgumentParser
|
| 3 |
+
import numpy as np
|
| 4 |
+
import OpenGL.GL as gl
|
| 5 |
+
import imageio
|
| 6 |
+
import cv2
|
| 7 |
+
import random
|
| 8 |
+
import torch
|
| 9 |
+
import moviepy.editor as mp
|
| 10 |
+
from scipy.spatial.transform import Rotation as RRR
|
| 11 |
+
import mGPT.render.matplot.plot_3d_global as plot_3d
|
| 12 |
+
from mGPT.render.pyrender.hybrik_loc2rot import HybrIKJointsToRotmat
|
| 13 |
+
from mGPT.render.pyrender.smpl_render import SMPLRender
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
if __name__ == '__main__':
|
| 17 |
+
|
| 18 |
+
parser = ArgumentParser()
|
| 19 |
+
parser.add_argument('--joints_path', type=str, help='Path to joints data')
|
| 20 |
+
parser.add_argument('--method', type=str, help='Method for rendering')
|
| 21 |
+
parser.add_argument('--output_mp4_path', type=str, help='Path to output MP4 file')
|
| 22 |
+
parser.add_argument('--smpl_model_path', type=str, help='Path to SMPL model')
|
| 23 |
+
|
| 24 |
+
args = parser.parse_args()
|
| 25 |
+
|
| 26 |
+
joints_path = args.joints_path
|
| 27 |
+
method = args.method
|
| 28 |
+
output_mp4_path = args.output_mp4_path
|
| 29 |
+
smpl_model_path = args.smpl_model_path
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
data = np.load(joints_path)
|
| 33 |
+
|
| 34 |
+
if method == 'slow':
|
| 35 |
+
if len(data.shape) == 4:
|
| 36 |
+
data = data[0]
|
| 37 |
+
data = data - data[0, 0]
|
| 38 |
+
pose_generator = HybrIKJointsToRotmat()
|
| 39 |
+
pose = pose_generator(data)
|
| 40 |
+
pose = np.concatenate([
|
| 41 |
+
pose,
|
| 42 |
+
np.stack([np.stack([np.eye(3)] * pose.shape[0], 0)] * 2, 1)
|
| 43 |
+
], 1)
|
| 44 |
+
shape = [768, 768]
|
| 45 |
+
render = SMPLRender(smpl_model_path)
|
| 46 |
+
|
| 47 |
+
r = RRR.from_rotvec(np.array([np.pi, 0.0, 0.0]))
|
| 48 |
+
pose[:, 0] = np.matmul(r.as_matrix().reshape(1, 3, 3), pose[:, 0])
|
| 49 |
+
vid = []
|
| 50 |
+
aroot = data[:, 0]
|
| 51 |
+
aroot[:, 1:] = -aroot[:, 1:]
|
| 52 |
+
params = dict(pred_shape=np.zeros([1, 10]),
|
| 53 |
+
pred_root=aroot,
|
| 54 |
+
pred_pose=pose)
|
| 55 |
+
render.init_renderer([shape[0], shape[1], 3], params)
|
| 56 |
+
for i in range(data.shape[0]):
|
| 57 |
+
renderImg = render.render(i)
|
| 58 |
+
vid.append(renderImg)
|
| 59 |
+
|
| 60 |
+
out = np.stack(vid, axis=0)
|
| 61 |
+
output_gif_path = output_mp4_path[:-4] + '.gif'
|
| 62 |
+
imageio.mimwrite(output_gif_path, out, duration=50)
|
| 63 |
+
out_video = mp.VideoFileClip(output_gif_path)
|
| 64 |
+
out_video.write_videofile(output_mp4_path)
|
| 65 |
+
del out, render
|
| 66 |
+
|
| 67 |
+
elif method == 'fast':
|
| 68 |
+
output_gif_path = output_mp4_path[:-4] + '.gif'
|
| 69 |
+
if len(data.shape) == 3:
|
| 70 |
+
data = data[None]
|
| 71 |
+
if isinstance(data, torch.Tensor):
|
| 72 |
+
data = data.cpu().numpy()
|
| 73 |
+
pose_vis = plot_3d.draw_to_batch(data, [''], [output_gif_path])
|
| 74 |
+
out_video = mp.VideoFileClip(output_gif_path)
|
| 75 |
+
out_video.write_videofile(output_mp4_path)
|
| 76 |
+
del pose_vis
|