Spaces:
Running
Running
Duplicate from radames/GenMM-demo
Browse filesCo-authored-by: Radamés Ajna <[email protected]>
- .gitattributes +36 -0
- .gitignore +3 -0
- GPS.py +324 -0
- GenMM_demo/.nojekyll +0 -0
- GenMM_demo/README.md +46 -0
- GenMM_demo/assets/Gangnam_Style.fbx +3 -0
- GenMM_demo/assets/Hip_Hop_Dancing.fbx +3 -0
- GenMM_demo/assets/Samba_Dancing.fbx +3 -0
- GenMM_demo/assets/Swing_Dancing.fbx +3 -0
- GenMM_demo/assets/Wave_Hip_Hop_Dance.fbx +3 -0
- GenMM_demo/assets/checker.png +3 -0
- GenMM_demo/assets/panel.png +3 -0
- GenMM_demo/assets/screenshot.png +3 -0
- GenMM_demo/assets/step2.1.png +3 -0
- GenMM_demo/assets/step2.png +3 -0
- GenMM_demo/assets/step3.1.png +3 -0
- GenMM_demo/assets/step3.png +3 -0
- GenMM_demo/css/app.5803d549.css +1 -0
- GenMM_demo/css/chunk-vendors.7d75f85b.css +0 -0
- GenMM_demo/favicon.ico +0 -0
- GenMM_demo/index.html +1 -0
- GenMM_demo/js/681.3863b0a5.js +0 -0
- GenMM_demo/js/681.3863b0a5.js.map +0 -0
- GenMM_demo/js/app.da9e3976.js +2 -0
- GenMM_demo/js/app.da9e3976.js.map +1 -0
- GenMM_demo/js/chunk-vendors.a7ae9901.js +0 -0
- GenMM_demo/js/chunk-vendors.a7ae9901.js.map +0 -0
- NN/losses.py +51 -0
- NN/utils.py +103 -0
- README.md +13 -0
- app.py +82 -0
- configs/random_synthesis.yaml +36 -0
- dataset/tracks_motion.py +183 -0
- requirements.txt +19 -0
- utils/base.py +148 -0
- utils/contact.py +103 -0
- utils/kinematics.py +203 -0
- utils/skeleton.py +347 -0
- utils/transforms.py +399 -0
.gitattributes
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
35 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
36 |
+
*.fbx filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
venv
|
2 |
+
__pycache__
|
3 |
+
.DS_Store
|
GPS.py
ADDED
@@ -0,0 +1,324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import os.path as osp
|
3 |
+
import torch
|
4 |
+
import torch.nn.functional as F
|
5 |
+
import numpy as np
|
6 |
+
import itertools
|
7 |
+
from tensorboardX import SummaryWriter
|
8 |
+
|
9 |
+
from NN.losses import make_criteria
|
10 |
+
from utils.base import logger
|
11 |
+
|
12 |
+
class GPS:
|
13 |
+
def __init__(self,
|
14 |
+
init_mode: str = 'random_synthesis',
|
15 |
+
noise_sigma: float = 1.0,
|
16 |
+
coarse_ratio: float = 0.2,
|
17 |
+
coarse_ratio_factor: float = 6,
|
18 |
+
pyr_factor: float = 0.75,
|
19 |
+
num_stages_limit: int = -1,
|
20 |
+
device: str = 'cuda:0',
|
21 |
+
silent: bool = False
|
22 |
+
):
|
23 |
+
'''
|
24 |
+
Args:
|
25 |
+
init_mode:
|
26 |
+
- 'random_synthesis': init with random seed
|
27 |
+
- 'random': init with random seed
|
28 |
+
noise_sigma: float = 1.0, random noise.
|
29 |
+
coarse_ratio: float = 0.2, ratio at the coarse level.
|
30 |
+
pyr_factor: float = 0.75, pyramid factor.
|
31 |
+
num_stages_limit: int = -1, no limit.
|
32 |
+
device: str = 'cuda:0', default device.
|
33 |
+
silent: bool = False, mute the output.
|
34 |
+
'''
|
35 |
+
self.init_mode = init_mode
|
36 |
+
self.noise_sigma = noise_sigma
|
37 |
+
self.coarse_ratio = coarse_ratio
|
38 |
+
self.coarse_ratio_factor = coarse_ratio_factor
|
39 |
+
self.pyr_factor = pyr_factor
|
40 |
+
self.num_stages_limit = num_stages_limit
|
41 |
+
self.device = torch.device(device)
|
42 |
+
self.silent = silent
|
43 |
+
|
44 |
+
def _get_pyramid_lengths(self, dest, ext=None):
|
45 |
+
"""Get a list of pyramid lengths"""
|
46 |
+
if self.coarse_ratio == -1:
|
47 |
+
self.coarse_ratio = np.around(ext['criteria']['patch_size'] * self.coarse_ratio_factor / dest, 2)
|
48 |
+
|
49 |
+
lengths = [int(np.round(dest * self.coarse_ratio))]
|
50 |
+
while lengths[-1] < dest:
|
51 |
+
lengths.append(int(np.round(lengths[-1] / self.pyr_factor)))
|
52 |
+
if lengths[-1] == lengths[-2]:
|
53 |
+
lengths[-1] += 1
|
54 |
+
lengths[-1] = dest
|
55 |
+
|
56 |
+
return lengths
|
57 |
+
|
58 |
+
def _get_target_pyramid(self, target, ext=None):
|
59 |
+
"""Reads a target motion(s) and create a pyraimd out of it. Ordered in increatorch.sing size"""
|
60 |
+
self._num_target = len(target)
|
61 |
+
lengths = []
|
62 |
+
min_len = 10000
|
63 |
+
for i in range(len(target)):
|
64 |
+
new_length = self._get_pyramid_lengths(len(target[i]), ext)
|
65 |
+
min_len = min(min_len, len(new_length))
|
66 |
+
if self.num_stages_limit != -1:
|
67 |
+
new_length = new_length[:self.num_stages_limit]
|
68 |
+
lengths.append(new_length)
|
69 |
+
for i in range(len(target)):
|
70 |
+
lengths[i] = lengths[i][-min_len:]
|
71 |
+
self.pyraimd_lengths = lengths
|
72 |
+
|
73 |
+
target_pyramid = [[] for _ in range(len(lengths[0]))]
|
74 |
+
for step in range(len(lengths[0])):
|
75 |
+
for i in range(len(target)):
|
76 |
+
length = lengths[i][step]
|
77 |
+
motion = target[i]
|
78 |
+
target_pyramid[step].append(motion.sample(size=length).to(self.device))
|
79 |
+
# target_pyramid[step].append(motion.pos2velo(motion.sample(size=length)))
|
80 |
+
# motion.motion_data = motion.pos2velo(motion.motion_data)
|
81 |
+
# target_pyramid[step].append(motion.sample(size=length))
|
82 |
+
# motion.motion_data = motion.velo2pos(motion.motion_data)
|
83 |
+
|
84 |
+
if not self.silent:
|
85 |
+
print('Levels:', lengths)
|
86 |
+
for i in range(len(target_pyramid)):
|
87 |
+
print(f'Number of clips in target pyramid {i} is {len(target_pyramid[i])}: {[[tgt.min(), tgt.max()] for tgt in target_pyramid[i]]}')
|
88 |
+
|
89 |
+
return target_pyramid
|
90 |
+
|
91 |
+
def _get_initial_motion(self):
|
92 |
+
"""Prepare the initial motion for optimization"""
|
93 |
+
if 'random_synthesis' in str(self.init_mode):
|
94 |
+
m = self.init_mode.split('/')[-1]
|
95 |
+
if m =='random_synthesis':
|
96 |
+
final_length = sum([i[-1] for i in self.pyraimd_lengths])
|
97 |
+
elif 'x' in m:
|
98 |
+
final_length = int(m.replace('x', '')) * sum([i[-1] for i in self.pyraimd_lengths])
|
99 |
+
elif (self.init_mode.split('/')[-1]).isdigit():
|
100 |
+
final_length = int(self.init_mode.split('/')[-1])
|
101 |
+
else:
|
102 |
+
raise ValueError(f'incorrect init_mode: {self.init_mode}')
|
103 |
+
|
104 |
+
self.synthesized_lengths = self._get_pyramid_lengths(final_length)
|
105 |
+
|
106 |
+
else:
|
107 |
+
raise ValueError(f'Unsupported init_mode {self.init_mode}')
|
108 |
+
|
109 |
+
initial_motion = F.interpolate(torch.cat([self.target_pyramid[0][i] for i in range(self._num_target)], dim=-1),
|
110 |
+
size=self.synthesized_lengths[0], mode='linear', align_corners=True)
|
111 |
+
if self.noise_sigma > 0:
|
112 |
+
initial_motion_w_noise = initial_motion + torch.randn_like(initial_motion) * self.noise_sigma
|
113 |
+
initial_motion_w_noise = torch.fmod(initial_motion_w_noise, 1.0)
|
114 |
+
else:
|
115 |
+
initial_motion_w_noise = initial_motion
|
116 |
+
|
117 |
+
if not self.silent:
|
118 |
+
print('Synthesized lengths:', self.synthesized_lengths)
|
119 |
+
print('Initial motion:', initial_motion.min(), initial_motion.max())
|
120 |
+
print('Initial motion with noise:', initial_motion_w_noise.min(), initial_motion_w_noise.max())
|
121 |
+
|
122 |
+
return initial_motion_w_noise
|
123 |
+
|
124 |
+
def run(self, target, mode="backpropagate", ext=None, debug_dir=None):
|
125 |
+
'''
|
126 |
+
Run the patch-based motion synthesis.
|
127 |
+
|
128 |
+
Args:
|
129 |
+
target (torch.Tensor): Target data.
|
130 |
+
mode (str): Optimization mode. Support ['backpropagate', 'match_and_blend']
|
131 |
+
ext (dict): extra data or constrain.
|
132 |
+
debug_dir (str): Debug directory.
|
133 |
+
'''
|
134 |
+
# preprare data
|
135 |
+
self.target_pyramid = self._get_target_pyramid(target, ext)
|
136 |
+
self.synthesized = self._get_initial_motion()
|
137 |
+
if debug_dir is not None:
|
138 |
+
writer = SummaryWriter(log_dir=debug_dir)
|
139 |
+
|
140 |
+
# prepare configuration
|
141 |
+
if mode == "backpropagate":
|
142 |
+
self.synthesized.requires_grad_(True)
|
143 |
+
assert 'criteria' in ext.keys(), 'Please specify a criteria for synthsis.'
|
144 |
+
criteria = make_criteria(ext['criteria']).to(self.device)
|
145 |
+
elif mode == "match_and_blend":
|
146 |
+
self.synthesized.requires_grad_(False)
|
147 |
+
assert 'criteria' in ext.keys(), 'Please specify a criteria for synthsis.'
|
148 |
+
criteria = make_criteria(ext['criteria']).to(self.device)
|
149 |
+
else:
|
150 |
+
raise ValueError(f'Unsupported mode: {mode}')
|
151 |
+
|
152 |
+
# perform synthsis
|
153 |
+
self.pbar = logger(ext['num_itrs'], len(self.target_pyramid))
|
154 |
+
ext['pbar'] = self.pbar
|
155 |
+
for lvl, lvl_target in enumerate(self.target_pyramid):
|
156 |
+
self.pbar.new_lvl()
|
157 |
+
if lvl > 0:
|
158 |
+
with torch.no_grad():
|
159 |
+
self.synthesized = F.interpolate(self.synthesized.detach(), size=self.synthesized_lengths[lvl], mode='linear')
|
160 |
+
if mode == "backpropagate":
|
161 |
+
self.synthesized.requires_grad_(True)
|
162 |
+
|
163 |
+
if mode == "backpropagate": # direct optimize the synthesized motion
|
164 |
+
self.synthesized, losses = GPS.backpropagate(self.synthesized, lvl_target, criteria, ext=ext)
|
165 |
+
elif mode == "match_and_blend":
|
166 |
+
self.synthesized, losses = GPS.match_and_blend(self.synthesized, lvl_target, criteria, ext=ext)
|
167 |
+
|
168 |
+
criteria.clean_cache()
|
169 |
+
if debug_dir:
|
170 |
+
for itr in range(len(losses)):
|
171 |
+
writer.add_scalar(f'optimize/losses_lvl{lvl}', losses[itr], itr)
|
172 |
+
self.pbar.pbar.close()
|
173 |
+
|
174 |
+
|
175 |
+
return self.synthesized.detach()
|
176 |
+
|
177 |
+
@staticmethod
|
178 |
+
def backpropagate(synthesized, targets, criteria=None, ext=None):
|
179 |
+
"""
|
180 |
+
Minimizes criteria(synthesized, target) for num_steps SGD steps
|
181 |
+
Args:
|
182 |
+
targets (torch.Tensor): Target data.
|
183 |
+
ext (dict): extra configurations.
|
184 |
+
"""
|
185 |
+
if criteria is None:
|
186 |
+
assert 'criteria' in ext.keys(), 'Criteria is not set'
|
187 |
+
criteria = make_criteria(ext['criteria']).to(synthesized.device)
|
188 |
+
|
189 |
+
optim = None
|
190 |
+
if 'optimizer' in ext.keys():
|
191 |
+
if ext['optimizer'] == 'Adam':
|
192 |
+
optim = torch.optim.Adam([synthesized], lr=ext['lr'])
|
193 |
+
elif ext['optimizer'] == 'SGD':
|
194 |
+
optim = torch.optim.SGD([synthesized], lr=ext['lr'])
|
195 |
+
elif ext['optimizer'] == 'RMSprop':
|
196 |
+
optim = torch.optim.RMSprop([synthesized], lr=ext['lr'])
|
197 |
+
else:
|
198 |
+
print(f'use default RMSprop optimizer')
|
199 |
+
optim = torch.optim.RMSprop([synthesized], lr=ext['lr']) if optim is None else optim
|
200 |
+
# optim = torch.optim.Adam([synthesized], lr=ext['lr']) if optim is None else optim
|
201 |
+
lr_decay = np.exp(np.log(0.333) / ext['num_itrs'])
|
202 |
+
|
203 |
+
# other constraints
|
204 |
+
trajectory = ext['trajectory'] if 'trajectory' in ext.keys() else None
|
205 |
+
|
206 |
+
losses = []
|
207 |
+
for _i in range(ext['num_itrs']):
|
208 |
+
optim.zero_grad()
|
209 |
+
|
210 |
+
loss = criteria(synthesized, targets)
|
211 |
+
|
212 |
+
if trajectory is not None: ## velo constrain
|
213 |
+
target_traj = F.interpolate(trajectory, size=synthesized.shape[-1], mode='linear')
|
214 |
+
# target_traj = F.interpolate(trajectory, size=synthesized.shape[-1], mode='linear', align_corners=False)
|
215 |
+
target_velo = ext['pos2velo'](target_traj)
|
216 |
+
|
217 |
+
velo_mask = [-3, -1]
|
218 |
+
loss += 1 * F.l1_loss(synthesized[:, velo_mask, :], target_velo[:, velo_mask, :])
|
219 |
+
|
220 |
+
loss.backward()
|
221 |
+
optim.step()
|
222 |
+
|
223 |
+
# Update staus
|
224 |
+
losses.append(loss.item())
|
225 |
+
if 'pbar' in ext.keys():
|
226 |
+
ext['pbar'].step()
|
227 |
+
ext['pbar'].print()
|
228 |
+
|
229 |
+
return synthesized, losses
|
230 |
+
|
231 |
+
@staticmethod
|
232 |
+
@torch.no_grad()
|
233 |
+
def match_and_blend(synthesized, targets, criteria, ext):
|
234 |
+
"""
|
235 |
+
Minimizes criteria(synthesized, target)
|
236 |
+
Args:
|
237 |
+
targets (torch.Tensor): Target data.
|
238 |
+
ext (dict): extra configurations.
|
239 |
+
"""
|
240 |
+
losses = []
|
241 |
+
for _i in range(ext['num_itrs']):
|
242 |
+
if 'parts_list' in ext.keys():
|
243 |
+
def extract_part_motions(motion, parts_list):
|
244 |
+
part_motions = []
|
245 |
+
n_frames = motion.shape[-1]
|
246 |
+
rot, pos = motion[:, :-3, :].reshape(-1, 6, n_frames), motion[:, -3:, :]
|
247 |
+
|
248 |
+
for part in parts_list:
|
249 |
+
# part -= 1
|
250 |
+
part = [i -1 for i in part]
|
251 |
+
|
252 |
+
# print(part)
|
253 |
+
if 0 in part:
|
254 |
+
part_motions += [torch.cat([rot[part].view(1, -1, n_frames), pos.view(1, -1, n_frames)], dim=1)]
|
255 |
+
else:
|
256 |
+
part_motions += [rot[part].view(1, -1, n_frames)]
|
257 |
+
|
258 |
+
return part_motions
|
259 |
+
def combine_part_motions(part_motions, parts_list):
|
260 |
+
assert len(part_motions) == len(parts_list)
|
261 |
+
n_frames = part_motions[0].shape[-1]
|
262 |
+
l = max(list(itertools.chain(*parts_list)))
|
263 |
+
# print(l, n_frames)
|
264 |
+
# motion = torch.zeros((1, (l+1)*6 + 3, n_frames), device=part_motions[0].device)
|
265 |
+
rot = torch.zeros(((l+1), 6, n_frames), device=part_motions[0].device)
|
266 |
+
pos = torch.zeros((1, 3, n_frames), device=part_motions[0].device)
|
267 |
+
div_rot = torch.zeros((l+1), device=part_motions[0].device)
|
268 |
+
div_pos = torch.zeros(1, device=part_motions[0].device)
|
269 |
+
|
270 |
+
for part_motion, part in zip(part_motions, parts_list):
|
271 |
+
part = [i -1 for i in part]
|
272 |
+
|
273 |
+
if 0 in part:
|
274 |
+
# print(part_motion.shape)
|
275 |
+
pos += part_motion[:, -3:, :]
|
276 |
+
div_pos += 1
|
277 |
+
rot[part] += part_motion[:, :-3, :].view(-1, 6, n_frames)
|
278 |
+
div_rot[part] += 1
|
279 |
+
else:
|
280 |
+
rot[part] += part_motion.view(-1, 6, n_frames)
|
281 |
+
div_rot[part] += 1
|
282 |
+
|
283 |
+
# print(div_rot, div_pos)
|
284 |
+
# print(rot.shape)
|
285 |
+
rot = (rot.permute(1, 2, 0) / div_rot).permute(2, 0, 1)
|
286 |
+
pos = pos / div_pos
|
287 |
+
|
288 |
+
return torch.cat([rot.view(1, -1, n_frames), pos.view(1, 3, n_frames)], dim=1)
|
289 |
+
|
290 |
+
# raw_synthesized = synthesized
|
291 |
+
# print(synthesized, synthesized.shape)
|
292 |
+
synthesized_part_motions = extract_part_motions(synthesized, ext['parts_list'])
|
293 |
+
targets_part_motions = [extract_part_motions(target, ext['parts_list']) for target in targets]
|
294 |
+
|
295 |
+
synthesized = []
|
296 |
+
for _j in range(len(synthesized_part_motions)):
|
297 |
+
synthesized_part_motion = synthesized_part_motions[_j]
|
298 |
+
# synthesized += [synthesized_part_motion]
|
299 |
+
targets_part_motion = [target[_j] for target in targets_part_motions]
|
300 |
+
# # print(synthesized_part_motion.shape, targets_part_motion[0].shape)
|
301 |
+
synthesized += [criteria(synthesized_part_motion, targets_part_motion, ext=ext, return_blended_results=True)[0]]
|
302 |
+
|
303 |
+
# print(len(synthesized))
|
304 |
+
|
305 |
+
synthesized = combine_part_motions(synthesized, ext['parts_list'])
|
306 |
+
# print(synthesized, synthesized.shape)
|
307 |
+
# print((raw_synthesized-synthesized > 0.00001).sum())
|
308 |
+
# exit()
|
309 |
+
# print(synthesized.shape)
|
310 |
+
losses = 0
|
311 |
+
|
312 |
+
# exit()
|
313 |
+
|
314 |
+
else:
|
315 |
+
synthesized, loss = criteria(synthesized, targets, ext=ext, return_blended_results=True)
|
316 |
+
|
317 |
+
# Update staus
|
318 |
+
losses.append(loss.item())
|
319 |
+
if 'pbar' in ext.keys():
|
320 |
+
ext['pbar'].step()
|
321 |
+
ext['pbar'].print()
|
322 |
+
|
323 |
+
return synthesized, losses
|
324 |
+
|
GenMM_demo/.nojekyll
ADDED
File without changes
|
GenMM_demo/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This repo contains the web demo for the [GenMM](https://wyysf-98.github.io/GenMM)
|
2 |
+
|
3 |
+
:star: try it now [demo](https://wyysf-98.github.io/GenMM_demo)
|
4 |
+
|
5 |
+
|
6 |
+
### Overview
|
7 |
+
|
8 |
+

|
9 |
+
|
10 |
+
- at the top of the page is the 'Next', 'Pause', 'Restart' and 'Generate' buttons, you can click 'Next' to get the next example.
|
11 |
+
- you can also drag the progress bar at the bottom of the page.
|
12 |
+
- click 'Controls' to expand the control panel.
|
13 |
+
- api-url: using hugging face space api for inference, if you get stuck, please try your own api.
|
14 |
+
- Scene: control the light and ground of the scene.
|
15 |
+
- Visibility: toggle the visibility of the mesh and skeleton.
|
16 |
+
- Synthesis: parameters for the synthesis process.
|
17 |
+
- frames: number of frames to be synthesized.
|
18 |
+
- noise_sigma: noise level to be synthesized.
|
19 |
+
- loop: whether to loop the animation.
|
20 |
+
- completeness: whether control the completeness of the results.
|
21 |
+
- patch_size: size of the patch
|
22 |
+
- coarse_ratio: the ratio of the frame number at coarset level to the input.
|
23 |
+
- pyr_factor: the factor of the pyramid upsampling.
|
24 |
+
- num__steps: number of steps during the optimization process.
|
25 |
+
|
26 |
+
|
27 |
+
### If you have been waiting for a long time for the results to be generated
|
28 |
+
try to fork the 🤗[hugging face space](https://huggingface.co/spaces/wyysf/GenMM) and use your own api.
|
29 |
+
|
30 |
+
#### step 1: create an account for the hugging face.
|
31 |
+
|
32 |
+
#### step 2: duplicate the space.
|
33 |
+
|
34 |
+

|
35 |
+

|
36 |
+
|
37 |
+
|
38 |
+
#### step 3: get your api url.
|
39 |
+
|
40 |
+

|
41 |
+

|
42 |
+
|
43 |
+
|
44 |
+
#### step 4: change the api_url and have fun.
|
45 |
+
|
46 |
+

|
GenMM_demo/assets/Gangnam_Style.fbx
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1fb01bbbedb6f795fe66ae9915281648d2742b06d164d24f751c309f423d3839
|
3 |
+
size 21924800
|
GenMM_demo/assets/Hip_Hop_Dancing.fbx
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:9a41d379cd3afd21f93c0989685ab2550d7d5d7fde2a2c0d901c70e7b6a5521b
|
3 |
+
size 3000800
|
GenMM_demo/assets/Samba_Dancing.fbx
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f62fafc047ebae27ac6dfd1f7075bc3651d48dafc9486b9ca1d3dcb953fe982c
|
3 |
+
size 20736896
|
GenMM_demo/assets/Swing_Dancing.fbx
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:bf1d73ceefccd96e7345a9899cef98550832ef1d664b0484b1e1707b27ccd8c0
|
3 |
+
size 5819936
|
GenMM_demo/assets/Wave_Hip_Hop_Dance.fbx
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:41173f5b455364d4f381555c212780893507d04bca4bc70de2a12952c8c4808f
|
3 |
+
size 3227824
|
GenMM_demo/assets/checker.png
ADDED
![]() |
Git LFS Details
|
GenMM_demo/assets/panel.png
ADDED
![]() |
Git LFS Details
|
GenMM_demo/assets/screenshot.png
ADDED
![]() |
Git LFS Details
|
GenMM_demo/assets/step2.1.png
ADDED
![]() |
Git LFS Details
|
GenMM_demo/assets/step2.png
ADDED
![]() |
Git LFS Details
|
GenMM_demo/assets/step3.1.png
ADDED
![]() |
Git LFS Details
|
GenMM_demo/assets/step3.png
ADDED
![]() |
Git LFS Details
|
GenMM_demo/css/app.5803d549.css
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
#Canvas[data-v-553d8d48]{width:100%;height:100%;position:fixed;left:0;right:0;top:0;bottom:0}#loading-screen[data-v-553d8d48]{display:block;position:absolute;z-index:2;top:0;left:0;width:100%;height:100%;background-color:#fff;opacity:1;transition:opacity 1s}#loading-screen.fade-out[data-v-553d8d48]{opacity:0}#loader[data-v-553d8d48]{display:block;position:relative;left:50%;top:50%;width:150px;height:150px;margin:-75px 0 0 -75px;border-radius:50%;border:3px solid transparent;border-top-color:#cf3e5b;animation:spin-553d8d48 2s linear infinite}#loader[data-v-553d8d48]:before{content:"";position:absolute;top:5px;left:5px;right:5px;bottom:5px;border-radius:50%;border:3px solid transparent;border-top-color:#f2c94a;animation:spin-553d8d48 3s linear infinite}#loader[data-v-553d8d48]:after{content:"";position:absolute;top:15px;left:15px;right:15px;bottom:15px;border-radius:50%;border:3px solid transparent;border-top-color:#52a7db;animation:spin-553d8d48 1.5s linear infinite}@keyframes spin-553d8d48{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}#loader-text[data-v-553d8d48]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:24px;font-weight:700;color:#000}#ProgressBar[data-v-553d8d48]{width:80%;position:fixed;bottom:5%;margin:0 auto;left:0;right:0}#ProgressBar .frame-text[data-v-553d8d48]{width:100%;font-size:15px;font-weight:500;color:#343232;height:22px;line-height:22px;margin-left:5px}[data-v-553d8d48] .el-slider__bar{background-color:#63c6fa}[data-v-553d8d48] .el-slider__runway{background-color:#fff}[data-v-553d8d48] .el-slider__button{border-color:#63c6fa}#app{font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center}
|
GenMM_demo/css/chunk-vendors.7d75f85b.css
ADDED
The diff for this file is too large to render.
See raw diff
|
|
GenMM_demo/favicon.ico
ADDED
|
GenMM_demo/index.html
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/GenMM_demo/favicon.ico"><title>patch-based_motion_synthesis</title><script defer="defer" src="/GenMM_demo/js/chunk-vendors.a7ae9901.js"></script><script defer="defer" src="/GenMM_demo/js/app.da9e3976.js"></script><link href="/GenMM_demo/css/chunk-vendors.7d75f85b.css" rel="stylesheet"><link href="/GenMM_demo/css/app.5803d549.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but patch-based_motion_synthesis doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
GenMM_demo/js/681.3863b0a5.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
GenMM_demo/js/681.3863b0a5.js.map
ADDED
The diff for this file is too large to render.
See raw diff
|
|
GenMM_demo/js/app.da9e3976.js
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
(function(){"use strict";var e={3644:function(e,t,n){var i=n(3862),o=n(4252),a=(n(2834),n(3396));function r(e,t,n,i,o,r){const s=(0,a.up)("MotionSynthesis");return(0,a.wg)(),(0,a.j4)(s)}var s=n(7139);const l=e=>((0,a.dD)("data-v-553d8d48"),e=e(),(0,a.Cn)(),e),d=l((()=>(0,a._)("div",{id:"Canvas"},null,-1))),u=l((()=>(0,a._)("section",{id:"loading-screen"},[(0,a._)("div",{id:"loader"}),(0,a._)("div",{id:"loader-text"},"0%")],-1))),c={id:"ProgressBar"},f={class:"frame-text"},h={id:"Buttons"};function m(e,t,n,o,r,l){const m=(0,a.up)("el-slider"),p=(0,a.up)("el-row"),g=(0,a.up)("ArrowRight"),w=(0,a.up)("el-icon"),y=(0,a.up)("el-button"),_=(0,a.up)("VideoPlay"),v=(0,a.up)("VideoPause"),b=(0,a.up)("RefreshLeft"),x=(0,a.up)("MagicStick");return(0,a.wg)(),(0,a.iD)(a.HY,null,[d,u,(0,a._)("div",c,[(0,a.Wm)(p,null,{default:(0,a.w5)((()=>[(0,a.Wm)(m,{modelValue:e.cur_frame,"onUpdate:modelValue":t[0]||(t[0]=t=>e.cur_frame=t),max:e.num_frames,size:"small",onInput:l.setFrame},null,8,["modelValue","max","onInput"]),(0,a._)("div",f,(0,s.zw)(e.cur_frame+1)+" / "+(0,s.zw)(e.num_frames),1)])),_:1})]),(0,a._)("div",h,[(0,a.Wm)(p,{style:{"justify-content":"center"}},{default:(0,a.w5)((()=>[(0,a.Wm)(y,{type:"primary",color:"##262727",size:"large",onClick:t[1]||(t[1]=e=>l.next())},{default:(0,a.w5)((()=>[(0,a.Uk)(" Next"),(0,a.Wm)(w,{class:"el-icon--right",size:17},{default:(0,a.w5)((()=>[(0,a.Wm)(g)])),_:1})])),_:1}),(0,a.wy)((0,a.Wm)(y,{type:"primary",color:"##262727",size:"large",style:{"margin-left":"10px"},onClick:t[2]||(t[2]=e=>l.play())},{default:(0,a.w5)((()=>[(0,a.Uk)(" Play"),(0,a.Wm)(w,{class:"el-icon--right",size:17},{default:(0,a.w5)((()=>[(0,a.Wm)(_)])),_:1})])),_:1},512),[[i.F8,e.isPause]]),(0,a.wy)((0,a.Wm)(y,{type:"primary",color:"##262727",size:"large",style:{"margin-left":"10px"},onClick:t[3]||(t[3]=e=>l.pause())},{default:(0,a.w5)((()=>[(0,a.Uk)(" Pause"),(0,a.Wm)(w,{class:"el-icon--right",size:17},{default:(0,a.w5)((()=>[(0,a.Wm)(v)])),_:1})])),_:1},512),[[i.F8,!e.isPause]]),(0,a.Wm)(y,{type:"primary",color:"##262727",size:"large",style:{"margin-left":"10px"},onClick:l.restart},{default:(0,a.w5)((()=>[(0,a.Uk)(" Restart"),(0,a.Wm)(w,{class:"el-icon--right",size:17},{default:(0,a.w5)((()=>[(0,a.Wm)(b)])),_:1})])),_:1},8,["onClick"]),(0,a.wy)((0,a.Wm)(y,{id:"SynthesisBtn",type:"primary",color:"##262727",size:"large",style:{"margin-left":"10px"},onClick:l.synthesis},{default:(0,a.w5)((()=>[(0,a.Uk)(" Generate"),(0,a.Wm)(w,{class:"el-icon--right",size:17},{default:(0,a.w5)((()=>[(0,a.Wm)(x)])),_:1})])),_:1},8,["onClick"]),[[i.F8,!e.isSynthesising]]),(0,a.wy)((0,a.Wm)(y,{id:"SynthesisBtn",type:"primary",color:"##262727",size:"large",style:{"margin-left":"10px"},loading:""},{default:(0,a.w5)((()=>[(0,a.Uk)(" Running... ")])),_:1},512),[[i.F8,e.isSynthesising]])])),_:1})])],64)}n(7658);var p=n(7178),g=n(2748),w=n(1288),y=n(1114),_=n(677),v=n(58),b=n(9061);class x extends v.w{constructor(e,t=null,n=null){var i=S(e),o=new b.z;null===t&&(t=new y.Ilk(4635559)),null===n&&(n=new y.Ilk(7363795));var a=[],r=[],s=new y.yGw;s.copy(e.matrixWorld).invert();for(var l=0;l<i.length;l++){var d=i[l];d.parent&&d.parent.isBone&&(a.push(0,0,0),a.push(0,0,0),r.push(t.r,t.g,t.b),r.push(n.r,n.g,n.b))}o.setPositions(a),o.setColors(r);let u=new _.Y({color:16777215,linewidth:.0075,vertexColors:!0,dashed:!0,depthTest:!1,depthWrite:!1});super(o,u),this.type="MySkeletonHelper",this.MySkeletonHelper=!0,this.root=e,this.bones=i,this.matrix=e.matrixWorld,this.matrixAutoUpdate=!1}updateMatrixWorld(e){var t=this.geometry.getAttribute("position"),n=[],i=new y.Pa4,o=new y.yGw,a=new y.yGw;a.copy(this.root.matrixWorld).invert();for(let s=0;s<this.bones.length;s++){var r=this.bones[s];r.parent&&r.parent.isBone&&(o.multiplyMatrices(a,r.matrixWorld),i.setFromMatrixPosition(o),n.push(i.x,i.y,i.z),o.multiplyMatrices(a,r.parent.matrixWorld),i.setFromMatrixPosition(o),n.push(i.x,i.y,i.z))}this.geometry.setPositions(n),t.needsUpdate=!0,super.updateMatrixWorld(e)}}function S(e){const t=[];!0===e.isBone&&t.push(e);for(let n=0;n<e.children.length;n++)t.push.apply(t,S(e.children[n]));return t}const k=x;var M=k,P=n(3959),W=n(3292),C=n(4543),z=n(6573),O=n(9717),j=n(5941);const F=["/GenMM_demo/assets/Wave_Hip_Hop_Dance.fbx","/GenMM_demo/assets/Hip_Hop_Dancing.fbx","/GenMM_demo/assets/Gangnam_Style.fbx","/GenMM_demo/assets/Samba_Dancing.fbx","/GenMM_demo/assets/Swing_Dancing.fbx"];let B,E,T,A,I,L,U,G,R,D,H=0,N=[],X=[],V=[],Z=[];const Y=new y.SUY,q=.1,J=50,K=50,$={fog:.08,hemi_light:!0,dir_light:!0,spot_light:!1,ground:!0},Q={show_mesh:!0,show_skeleton:!1,skeleton_width:5},ee=["https://wyysf-genmm.hf.space/","https://wyysf-genmm-test.hf.space/"];let te=Math.floor(Math.random()*ee.length);const ne={api_url:window.location.href,frames:500,noise_sigma:25,loop:!1,completeness:!1,alpha:.1,patch_size:15,coarse_ratio:.2,pyr_factor:.75,num_steps:3};var ie={name:"MainScene",props:{init_file:{type:String,default:F[0]},scale:{type:Number,default:.01}},data:function(){return{cur_frame:-1,num_frames:0,isPause:!1,isSynthesising:!1,uploadFilename:null,uploadFiles:[]}},mounted(){this.initScene(),this.loadFBXModel(this.init_file),this.animate()},methods:{initScene(){function e(){const e=document.getElementById("Canvas");B=new y.cPb(45,window.innerWidth/window.innerHeight,q,J),B.position.set(0,2.5,5),E=new y.xsS,E.background=new y.Ilk(4342338),E.fog=new y.yo9(4342338,$.fog),L=new y.vmT(16777215,4473924),L.position.set(0,2,0),E.add(L),U=new y.Ox3(16777215,.8),U.position.set(0,5,4),U.castShadow=!0,U.shadow.mapSize.width=2048,U.shadow.mapSize.height=2048,E.add(U),G=new y.PMe,G.angle=Math.PI/8,G.penumbra=.5,G.castShadow=!0,G.position.set(-0,5,3);let t=new y.dpR,i=t.load("./assets/checker.png");i.wrapS=y.rpg,i.wrapT=y.rpg,i.magFilter=y.TyD,i.repeat.set(K,K),R=new y.Kj0(new y._12(K,K),new y.Wid({map:i,side:y.ehD,emissiveIntensity:1})),R.rotation.x=-.5*Math.PI,R.receiveShadow=!0,E.add(R),T=new y.CP7({antialias:!0}),T.setPixelRatio(window.devicePixelRatio),T.setSize(window.innerWidth,window.innerHeight),T.shadowMap.enabled=!0,e.appendChild(T.domElement);const o=new C.z(B,T.domElement);o.target.set(0,1,0),o.mouseButtons={LEFT:y.RsA.ROTATE,MIDDLE:y.RsA.PAN},o.update(),window.addEventListener("resize",n),A=new P.Z,e.appendChild(A.dom)}function t(){I=new W.XS({width:220}),I.add(ne,"api_url");const e=I.addFolder("Scene");e.add($,"hemi_light").onChange((function(e){e?E.add(L):E.remove(L)})),e.add($,"dir_light").onChange((function(e){e?E.add(U):E.remove(U)})),e.add($,"spot_light").onChange((function(e){e?E.add(G):E.remove(G)})),e.add($,"ground").onChange((function(e){e?E.add(R):E.remove(R)})),e.add($,"fog",0,.25,.01).onChange((function(e){E.fog=new y.yo9(4342338,e)})),e.close();const t=I.addFolder("Visibility");t.add(Q,"show_mesh").onChange((function(e){for(let t=0;t<X.length;t++)X[t].visible=e})),t.add(Q,"show_skeleton").onChange((function(e){for(let t=0;t<V.length;t++)V[t].visible=e})).listen(),t.add(Q,"skeleton_width",1,10,.5).onChange((function(e){for(let t=0;t<V.length;t++)V[t].material.linewidth=e/1e3})),t.close();const n=I.addFolder("Synthesis");n.add(ne,"frames",1,2e3,1).listen(),n.add(ne,"noise_sigma",0,50,1),n.add(ne,"loop"),n.add(ne,"completeness").onChange((function(){ne.completeness?i.show():i.hide()}));let i=n.add(ne,"alpha",.001,1,.001).hide();n.add(ne,"patch_size",1,30,1),n.add(ne,"coarse_ratio",.1,1,.01),n.add(ne,"pyr_factor",.25,1,.01),n.add(ne,"num_steps",1,50,1),n.close(),I.close()}function n(){B.aspect=window.innerWidth/window.innerHeight,B.updateProjectionMatrix(),T.setSize(window.innerWidth,window.innerHeight)}e(),t()},reset(){for(let e=0;e<X.length;e++)E.remove(X[e]);for(let e=0;e<V.length;e++)E.remove(V[e]);N=[],Z=[],X=[],V=[],Z=[]},animate(){let e=this;requestAnimationFrame(e.animate);const t=Y.getDelta(),n=Math.max(...Z),i=Z.indexOf(n);for(let o=0;o<N.length;o++)if((n===-1/0||N[o].time<=Z[o])&&N[o].update(t),o===i&&(e.cur_frame=Math.round(N[o].time*e.num_frames/Z[o]),e.cur_frame%=e.num_frames),N[o].time>n)for(const e of N)e.setTime(0);T.render(E,B),A.update()},loadFBXobject(e){let t=this,n=new y.Xcj(e);for(let a=0;a<e.animations.length;a++)0!=e.animations[a].tracks.length&&(e.animations[0]=e.animations[a]);let i=n.clipAction(e.animations[0]);i.play(),e.traverse((function(e){e.isMesh&&(e.castShadow=!0,e.receiveShadow=!0)})),e.scale.multiplyScalar(t.scale),E.add(e);let o=new M(e);o.material.linewidth=Q.skeleton_width/1e3,o.visible=Q.show_skeleton,E.add(o),Z.push(e.animations[0].duration),D=e.animations[0].duration/(e.animations[0].tracks[0].times.length-1),t.num_frames=Math.round(Math.max(...Z)/D)+1,N.push(n),X.push(e),V.push(o)},loadFBXModel(e){let t=this;function n(e,t){const n=Math.round(e/t*100);document.getElementById("loader-text").textContent=`${n}%`}const i=new y.lLk;i.onStart=(e,t,i)=>{const o=document.getElementById("loading-screen");o.style.display="block",o.classList.remove("fade-out"),n(t,i)},i.onProgress=(e,t,i)=>{n(t,i)},i.onLoad=()=>{const e=document.getElementById("loading-screen");e.classList.add("fade-out"),e.addEventListener("transitionend",(e=>{e.target.style.display="none",e.target.classList.remove("fade-out")}))};const o=new z.y(i);o.load(e,t.loadFBXobject,void 0,(e=>{j.error("Error loading FBX model:",e)}))},restart(){for(const e of N)e.setTime(0);this.cur_frame=0},play(){for(const e of N)e.timeScale=1;this.isPause=!1},next(){H+=1,this.reset(),j.log(F[H]),this.loadFBXModel(F[H]),H>=F.length-1&&(H=-1)},pause(){for(const e of N)e.timeScale=0;this.isPause=!0},setFrame(e){const t=Math.max(...Z);for(const n of N){let i=n.timeScale;n.timeScale=1,n.setTime(t*e/this.num_frames),n.timeScale=i}},synthesis(){let e=this;async function t(e){const t=await(0,w.Lp)(ne.api_url),n=await t.predict("/predict",[JSON.stringify(e)]);return n}e.isSynthesising=!0,t({setting:ne,tracks:X[0].animations[0].toJSON()["tracks"],scale:e.scale}).then((t=>{(0,p.z8)({message:(0,a.h)("p",null,[(0,a.h)("span",null,"Generated in "),(0,a.h)("i",{style:"color: red"},t.data[0].time.toFixed(4).toString()),(0,a.h)("span",null," s on CPU without any training.")]),duration:3e3});let n=O.d9(X[0]),i=new y.Xcj(n),o=y.m7l.parse(t.data[0]),r=i.clipAction(o);r.play(),n.traverse((function(e){e.isMesh&&(e.castShadow=!0,e.receiveShadow=!0)})),E.add(n);let s=new M(n,new y.Ilk(12138830),new y.Ilk(14454850));s.material.linewidth=Q.skeleton_width/1e3,s.visible=!1,E.add(s);for(const e of N)e.setTime(0);Z.push(o.duration),e.num_frames=Math.round(Math.max(...Z)/D)+1,X.push(n),N.push(i),V.push(s);let l=[[0,0],[-2,-1],[-1,-1],[0,-1],[1,-1],[2,-1],[-2,-2],[-1,-2],[0,-2],[1,-2],[2,-2],[-2,-3],[-1,-3],[0,-3],[1,-3],[2,-3],[-2,-4],[-1,-4],[0,-4],[1,-4],[2,-4],[-2,-5],[-1,-5],[0,-5],[1,-5],[2,-5]];for(let e=0;e<X.length;e++)X[e].position.x=l[e][0],X[e].position.z=l[e][1];e.isSynthesising=!1}))}},components:{ArrowRight:g.olP,VideoPlay:g.GhU,VideoPause:g.lBU,RefreshLeft:g.Ufu,MagicStick:g.l8K}},oe=n(89);const ae=(0,oe.Z)(ie,[["render",m],["__scopeId","data-v-553d8d48"]]);var re=ae,se={name:"App",components:{MotionSynthesis:re}};const le=(0,oe.Z)(se,[["render",r]]);var de=le;const ue=(0,i.ri)(de);ue.use(o.Z),ue.mount("#app")}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var a=t[i]={id:i,loaded:!1,exports:{}};return e[i].call(a.exports,a,a.exports,n),a.loaded=!0,a.exports}n.m=e,function(){var e=[];n.O=function(t,i,o,a){if(!i){var r=1/0;for(u=0;u<e.length;u++){i=e[u][0],o=e[u][1],a=e[u][2];for(var s=!0,l=0;l<i.length;l++)(!1&a||r>=a)&&Object.keys(n.O).every((function(e){return n.O[e](i[l])}))?i.splice(l--,1):(s=!1,a<r&&(r=a));if(s){e.splice(u--,1);var d=o();void 0!==d&&(t=d)}}return t}a=a||0;for(var u=e.length;u>0&&e[u-1][2]>a;u--)e[u]=e[u-1];e[u]=[i,o,a]}}(),function(){n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,{a:t}),t}}(),function(){var e,t=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__};n.t=function(i,o){if(1&o&&(i=this(i)),8&o)return i;if("object"===typeof i&&i){if(4&o&&i.__esModule)return i;if(16&o&&"function"===typeof i.then)return i}var a=Object.create(null);n.r(a);var r={};e=e||[null,t({}),t([]),t(t)];for(var s=2&o&&i;"object"==typeof s&&!~e.indexOf(s);s=t(s))Object.getOwnPropertyNames(s).forEach((function(e){r[e]=function(){return i[e]}}));return r["default"]=function(){return i},n.d(a,r),a}}(),function(){n.d=function(e,t){for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})}}(),function(){n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce((function(t,i){return n.f[i](e,t),t}),[]))}}(),function(){n.u=function(e){return"js/"+e+".3863b0a5.js"}}(),function(){n.miniCssF=function(e){}}(),function(){n.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}()}(),function(){n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}}(),function(){var e={},t="patch-based_motion_synthesis:";n.l=function(i,o,a,r){if(e[i])e[i].push(o);else{var s,l;if(void 0!==a)for(var d=document.getElementsByTagName("script"),u=0;u<d.length;u++){var c=d[u];if(c.getAttribute("src")==i||c.getAttribute("data-webpack")==t+a){s=c;break}}s||(l=!0,s=document.createElement("script"),s.charset="utf-8",s.timeout=120,n.nc&&s.setAttribute("nonce",n.nc),s.setAttribute("data-webpack",t+a),s.src=i),e[i]=[o];var f=function(t,n){s.onerror=s.onload=null,clearTimeout(h);var o=e[i];if(delete e[i],s.parentNode&&s.parentNode.removeChild(s),o&&o.forEach((function(e){return e(n)})),t)return t(n)},h=setTimeout(f.bind(null,void 0,{type:"timeout",target:s}),12e4);s.onerror=f.bind(null,s.onerror),s.onload=f.bind(null,s.onload),l&&document.head.appendChild(s)}}}(),function(){n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}}(),function(){n.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e}}(),function(){n.p="/"}(),function(){var e={143:0};n.f.j=function(t,i){var o=n.o(e,t)?e[t]:void 0;if(0!==o)if(o)i.push(o[2]);else{var a=new Promise((function(n,i){o=e[t]=[n,i]}));i.push(o[2]=a);var r=n.p+n.u(t),s=new Error,l=function(i){if(n.o(e,t)&&(o=e[t],0!==o&&(e[t]=void 0),o)){var a=i&&("load"===i.type?"missing":i.type),r=i&&i.target&&i.target.src;s.message="Loading chunk "+t+" failed.\n("+a+": "+r+")",s.name="ChunkLoadError",s.type=a,s.request=r,o[1](s)}};n.l(r,l,"chunk-"+t,t)}},n.O.j=function(t){return 0===e[t]};var t=function(t,i){var o,a,r=i[0],s=i[1],l=i[2],d=0;if(r.some((function(t){return 0!==e[t]}))){for(o in s)n.o(s,o)&&(n.m[o]=s[o]);if(l)var u=l(n)}for(t&&t(i);d<r.length;d++)a=r[d],n.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return n.O(u)},i=self["webpackChunkpatch_based_motion_synthesis"]=self["webpackChunkpatch_based_motion_synthesis"]||[];i.forEach(t.bind(null,0)),i.push=t.bind(null,i.push.bind(i))}();var i=n.O(void 0,[998],(function(){return n(3644)}));i=n.O(i)})();
|
2 |
+
//# sourceMappingURL=app.da9e3976.js.map
|
GenMM_demo/js/app.da9e3976.js.map
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"version":3,"file":"js/app.da9e3976.js","mappings":"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","sources":["webpack://patch-based_motion_synthesis/./src/App.vue","webpack://patch-based_motion_synthesis/./src/components/MotionSynthesis_random_synthesis.vue","webpack://patch-based_motion_synthesis/./src/components/MySkeletonHelper.vue","webpack://patch-based_motion_synthesis/./src/components/MySkeletonHelper.vue?a625","webpack://patch-based_motion_synthesis/./src/components/MotionSynthesis_random_synthesis.vue?6b1a","webpack://patch-based_motion_synthesis/./src/App.vue?7ccd","webpack://patch-based_motion_synthesis/./src/main.js","webpack://patch-based_motion_synthesis/webpack/bootstrap","webpack://patch-based_motion_synthesis/webpack/runtime/chunk loaded","webpack://patch-based_motion_synthesis/webpack/runtime/compat get default export","webpack://patch-based_motion_synthesis/webpack/runtime/create fake namespace object","webpack://patch-based_motion_synthesis/webpack/runtime/define property getters","webpack://patch-based_motion_synthesis/webpack/runtime/ensure chunk","webpack://patch-based_motion_synthesis/webpack/runtime/get javascript chunk filename","webpack://patch-based_motion_synthesis/webpack/runtime/get mini-css chunk filename","webpack://patch-based_motion_synthesis/webpack/runtime/global","webpack://patch-based_motion_synthesis/webpack/runtime/hasOwnProperty shorthand","webpack://patch-based_motion_synthesis/webpack/runtime/load script","webpack://patch-based_motion_synthesis/webpack/runtime/make namespace object","webpack://patch-based_motion_synthesis/webpack/runtime/node module decorator","webpack://patch-based_motion_synthesis/webpack/runtime/publicPath","webpack://patch-based_motion_synthesis/webpack/runtime/jsonp chunk loading","webpack://patch-based_motion_synthesis/webpack/startup"],"sourcesContent":["<template>\n <MotionSynthesis/>\n</template>\n\n<script>\nimport MotionSynthesis from './components/MotionSynthesis_random_synthesis.vue'\n// import MotionSynthesis from './components/MotionSynthesis_bvh.vue'\n\nexport default {\n name: 'App',\n components: {\n MotionSynthesis\n }\n}\n</script>\n\n<style>\n#app {\n font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n}\n</style>\n","<template>\n <div id=\"Canvas\"></div>\n\n <section id=\"loading-screen\">\n <div id=\"loader\"></div>\n <div id=\"loader-text\">0%</div>\n </section>\n\n <div id=\"ProgressBar\">\n <el-row>\n <el-slider\n v-model=\"cur_frame\"\n :max=\"num_frames\"\n size=\"small\"\n @input=\"setFrame\"\n />\n <div class=\"frame-text\">{{ cur_frame + 1 }} / {{ num_frames }}</div>\n </el-row>\n </div>\n\n <div id=\"Buttons\">\n <el-row style=\"justify-content: center\">\n <el-button type=\"primary\" color=\"##262727\" size=\"large\" @click=\"next()\">\n Next<el-icon class=\"el-icon--right\" :size=\"17\">\n <ArrowRight />\n </el-icon>\n </el-button>\n <el-button\n v-show=\"isPause\"\n type=\"primary\"\n color=\"##262727\"\n size=\"large\"\n style=\"margin-left: 10px\"\n @click=\"play()\"\n >\n Play<el-icon class=\"el-icon--right\" :size=\"17\">\n <VideoPlay />\n </el-icon>\n </el-button>\n <el-button\n v-show=\"!isPause\"\n type=\"primary\"\n color=\"##262727\"\n size=\"large\"\n style=\"margin-left: 10px\"\n @click=\"pause()\"\n >\n Pause<el-icon class=\"el-icon--right\" :size=\"17\">\n <VideoPause />\n </el-icon>\n </el-button>\n\n <el-button\n type=\"primary\"\n color=\"##262727\"\n size=\"large\"\n style=\"margin-left: 10px\"\n @click=\"restart\"\n >\n Restart<el-icon class=\"el-icon--right\" :size=\"17\">\n <RefreshLeft />\n </el-icon>\n </el-button>\n\n <el-button\n v-show=\"!isSynthesising\"\n id=\"SynthesisBtn\"\n type=\"primary\"\n color=\"##262727\"\n size=\"large\"\n style=\"margin-left: 10px\"\n @click=\"synthesis\"\n >\n Generate<el-icon class=\"el-icon--right\" :size=\"17\">\n <MagicStick />\n </el-icon>\n </el-button>\n <el-button\n v-show=\"isSynthesising\"\n id=\"SynthesisBtn\"\n type=\"primary\"\n color=\"##262727\"\n size=\"large\"\n style=\"margin-left: 10px\"\n loading\n >\n Running...\n </el-button>\n </el-row>\n </div>\n</template>\n\n<script type=\"module\">\nimport { ElMessage } from \"element-plus\";\nimport {\n ArrowRight,\n RefreshLeft,\n VideoPlay,\n VideoPause,\n MagicStick,\n} from \"@element-plus/icons-vue\";\n\nimport { h } from \"vue\";\nimport { client } from \"@gradio/client\";\nimport * as THREE from \"three\";\nimport MySkeletonHelper from \"./MySkeletonHelper\"; // rewrite SkeletonHelper to support Chrome on MacOS\nimport Stats from \"three/examples/jsm/libs/stats.module.js\";\nimport { GUI } from \"three/examples/jsm/libs/lil-gui.module.min.js\";\nimport { OrbitControls } from \"three/examples/jsm/controls/OrbitControls.js\";\nimport { FBXLoader } from \"three/examples/jsm/loaders/FBXLoader.js\";\nimport * as SkeletonUtils from \"three/examples/jsm/utils/SkeletonUtils.js\";\n\n// base scene and model\nconst fbx_models = [\n \"/GenMM_demo/assets/Wave_Hip_Hop_Dance.fbx\",\n \"/GenMM_demo/assets/Hip_Hop_Dancing.fbx\",\n \"/GenMM_demo/assets/Gangnam_Style.fbx\",\n \"/GenMM_demo/assets/Samba_Dancing.fbx\",\n \"/GenMM_demo/assets/Swing_Dancing.fbx\",\n];\nlet cur_fbx_model = 0;\nlet camera, scene, renderer, stats, panel;\nlet hemiLight, dirLight, spotLight, ground;\nlet mixers = [],\n meshes = [],\n skeletons = [],\n durations = [],\n duration_delta;\nconst clock = new THREE.Clock();\nconst near = 0.1,\n far = 50; // camera setting\nconst ground_size = 50; // ground size\nconst scene_setting = {\n fog: 0.08,\n hemi_light: true,\n dir_light: true,\n spot_light: false,\n ground: true,\n};\nconst visibility_setting = {\n show_mesh: true,\n show_skeleton: false,\n skeleton_width: 5,\n};\nconst api_urls = [\n \"https://wyysf-genmm.hf.space/\",\n \"https://wyysf-genmm-test.hf.space/\"\n];\nlet _url_id = Math.floor(Math.random() * api_urls.length);;\n// for synthesis\nconst synthesis_setting = {\n api_url: api_urls[_url_id],\n frames: 500,\n noise_sigma: 25.0,\n loop: false,\n completeness: false,\n alpha: 0.1,\n patch_size: 15,\n coarse_ratio: 0.2,\n pyr_factor: 0.75,\n num_steps: 3,\n};\n\nexport default {\n name: \"MainScene\",\n props: {\n init_file: {\n type: String,\n default: fbx_models[0],\n },\n scale: {\n type: Number,\n default: 0.01,\n },\n },\n data: function () {\n return {\n cur_frame: -1,\n num_frames: 0,\n isPause: false,\n isSynthesising: false,\n uploadFilename: null,\n uploadFiles: [],\n };\n },\n mounted() {\n this.initScene();\n this.loadFBXModel(this.init_file);\n this.animate();\n },\n methods: {\n initScene() {\n function create_scene() {\n const container = document.getElementById(\"Canvas\");\n\n camera = new THREE.PerspectiveCamera(\n 45,\n window.innerWidth / window.innerHeight,\n near,\n far\n );\n // camera.position.set(-2, 2, 4.2);\n camera.position.set(0, 2.5, 5);\n\n scene = new THREE.Scene();\n scene.background = new THREE.Color(0x424242);\n scene.fog = new THREE.FogExp2(0x424242, scene_setting.fog);\n\n hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);\n hemiLight.position.set(0, 2, 0);\n scene.add(hemiLight);\n\n dirLight = new THREE.DirectionalLight(0xffffff, 0.8);\n dirLight.position.set(0, 5, 4);\n dirLight.castShadow = true;\n dirLight.shadow.mapSize.width = 2048;\n dirLight.shadow.mapSize.height = 2048;\n scene.add(dirLight);\n\n spotLight = new THREE.SpotLight();\n spotLight.angle = Math.PI / 8;\n spotLight.penumbra = 0.5;\n spotLight.castShadow = true;\n spotLight.position.set(-0, 5, 3);\n\n // Ground\n let loader = new THREE.TextureLoader();\n let texture = loader.load(\"./assets/checker.png\");\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.RepeatWrapping;\n texture.magFilter = THREE.NearestFilter;\n texture.repeat.set(ground_size, ground_size);\n ground = new THREE.Mesh(\n new THREE.PlaneGeometry(ground_size, ground_size),\n new THREE.MeshStandardMaterial({\n map: texture,\n side: THREE.DoubleSide,\n emissiveIntensity: 1,\n })\n );\n ground.rotation.x = Math.PI * -0.5;\n ground.receiveShadow = true;\n scene.add(ground);\n\n // renderer\n renderer = new THREE.WebGLRenderer({ antialias: true });\n renderer.setPixelRatio(window.devicePixelRatio);\n renderer.setSize(window.innerWidth, window.innerHeight);\n renderer.shadowMap.enabled = true;\n container.appendChild(renderer.domElement);\n\n // controls\n const controls = new OrbitControls(camera, renderer.domElement);\n controls.target.set(0, 1, 0);\n controls.mouseButtons = {\n LEFT: THREE.MOUSE.ROTATE,\n MIDDLE: THREE.MOUSE.PAN,\n };\n controls.update();\n\n window.addEventListener(\"resize\", onWindowResize);\n\n // Stats\n stats = new Stats();\n container.appendChild(stats.dom);\n }\n\n function create_panel() {\n panel = new GUI({ width: 220 });\n panel.add(synthesis_setting, \"api_url\");\n\n const scene_folder = panel.addFolder(\"Scene\");\n scene_folder\n .add(scene_setting, \"hemi_light\")\n .onChange(function (value) {\n if (value) {\n scene.add(hemiLight);\n } else {\n scene.remove(hemiLight);\n }\n });\n scene_folder.add(scene_setting, \"dir_light\").onChange(function (value) {\n if (value) {\n scene.add(dirLight);\n } else {\n scene.remove(dirLight);\n }\n });\n scene_folder\n .add(scene_setting, \"spot_light\")\n .onChange(function (value) {\n if (value) {\n scene.add(spotLight);\n } else {\n scene.remove(spotLight);\n }\n });\n scene_folder.add(scene_setting, \"ground\").onChange(function (value) {\n if (value) {\n scene.add(ground);\n } else {\n scene.remove(ground);\n }\n });\n scene_folder\n .add(scene_setting, \"fog\", 0.0, 0.25, 0.01)\n .onChange(function (value) {\n scene.fog = new THREE.FogExp2(0x424242, value);\n });\n scene_folder.close();\n\n const visibility_folder = panel.addFolder(\"Visibility\");\n visibility_folder\n .add(visibility_setting, \"show_mesh\")\n .onChange(function (value) {\n for (let i = 0; i < meshes.length; i++) {\n meshes[i].visible = value;\n }\n });\n visibility_folder\n .add(visibility_setting, \"show_skeleton\")\n .onChange(function (value) {\n for (let i = 0; i < skeletons.length; i++) {\n skeletons[i].visible = value;\n }\n })\n .listen();\n visibility_folder\n .add(visibility_setting, \"skeleton_width\", 1, 10, 0.5)\n .onChange(function (value) {\n for (let i = 0; i < skeletons.length; i++) {\n skeletons[i].material.linewidth = value / 1000;\n }\n });\n visibility_folder.close();\n\n const synthesis_folder = panel.addFolder(\"Synthesis\");\n synthesis_folder.add(synthesis_setting, \"frames\", 1, 2000, 1).listen();\n synthesis_folder.add(synthesis_setting, \"noise_sigma\", 0.0, 50.0, 1);\n synthesis_folder.add(synthesis_setting, \"loop\");\n synthesis_folder\n .add(synthesis_setting, \"completeness\")\n .onChange(function () {\n if (synthesis_setting.completeness) {\n alpha_setting.show();\n } else {\n alpha_setting.hide();\n }\n });\n let alpha_setting = synthesis_folder\n .add(synthesis_setting, \"alpha\", 0.001, 1, 0.001)\n .hide();\n synthesis_folder.add(synthesis_setting, \"patch_size\", 1, 30, 1);\n synthesis_folder.add(synthesis_setting, \"coarse_ratio\", 0.1, 1.0, 0.01);\n synthesis_folder.add(synthesis_setting, \"pyr_factor\", 0.25, 1.0, 0.01);\n synthesis_folder.add(synthesis_setting, \"num_steps\", 1, 50, 1);\n synthesis_folder.close();\n\n panel.close();\n }\n\n function onWindowResize() {\n camera.aspect = window.innerWidth / window.innerHeight;\n camera.updateProjectionMatrix();\n renderer.setSize(window.innerWidth, window.innerHeight);\n }\n\n create_scene();\n create_panel();\n },\n\n reset() {\n for (let i = 0; i < meshes.length; i++) {\n scene.remove(meshes[i]);\n }\n for (let i = 0; i < skeletons.length; i++) {\n scene.remove(skeletons[i]);\n }\n mixers = [];\n (durations = []), (meshes = []);\n (skeletons = []), (durations = []);\n },\n\n animate() {\n let _this = this;\n\n requestAnimationFrame(_this.animate);\n\n const delta = clock.getDelta();\n const max_duration = Math.max(...durations),\n max_duration_index = durations.indexOf(max_duration);\n for (let i = 0; i < mixers.length; i++) {\n if (max_duration === -Infinity || mixers[i].time <= durations[i]) {\n mixers[i].update(delta);\n }\n if (i === max_duration_index) {\n _this.cur_frame = Math.round(\n (mixers[i].time * _this.num_frames) / durations[i]\n );\n _this.cur_frame %= _this.num_frames;\n }\n if (mixers[i].time > max_duration) {\n for (const mixer of mixers) mixer.setTime(0);\n }\n }\n\n renderer.render(scene, camera);\n stats.update();\n },\n\n loadFBXobject(object) {\n let _this = this;\n\n let mixer = new THREE.AnimationMixer(object);\n // console.log(object.animations);\n for (let i = 0; i < object.animations.length; i++) {\n if (object.animations[i].tracks.length == 0) {\n continue;\n }\n object.animations[0] = object.animations[i];\n }\n // console.log(object.animations[0]);\n let action = mixer.clipAction(object.animations[0]);\n action.play();\n\n object.traverse(function (child) {\n if (child.isMesh) {\n child.castShadow = true;\n child.receiveShadow = true;\n }\n });\n object.scale.multiplyScalar(_this.scale);\n scene.add(object);\n\n let skeleton = new MySkeletonHelper(object);\n skeleton.material.linewidth = visibility_setting.skeleton_width / 1000;\n skeleton.visible = visibility_setting.show_skeleton;\n scene.add(skeleton);\n\n // update frames\n // synthesis_setting.frames = object.animations[0].tracks[0].times.length;\n durations.push(object.animations[0].duration);\n duration_delta =\n object.animations[0].duration /\n (object.animations[0].tracks[0].times.length - 1);\n _this.num_frames =\n Math.round(Math.max(...durations) / duration_delta) + 1;\n\n mixers.push(mixer);\n meshes.push(object);\n skeletons.push(skeleton);\n },\n\n loadFBXModel(fbx_model) {\n let _this = this;\n function updateProgress(loaded, total) {\n const percentComplete = Math.round((loaded / total) * 100);\n document.getElementById(\n \"loader-text\"\n ).textContent = `${percentComplete}%`;\n }\n\n const loadingManager = new THREE.LoadingManager();\n loadingManager.onStart = (url, loaded, total) => {\n const loadingScreen = document.getElementById(\"loading-screen\");\n loadingScreen.style.display = \"block\";\n loadingScreen.classList.remove(\"fade-out\");\n updateProgress(loaded, total);\n };\n\n loadingManager.onProgress = (url, loaded, total) => {\n updateProgress(loaded, total);\n };\n\n loadingManager.onLoad = () => {\n const loadingScreen = document.getElementById(\"loading-screen\");\n loadingScreen.classList.add(\"fade-out\");\n loadingScreen.addEventListener(\"transitionend\", (event) => {\n event.target.style.display = \"none\";\n event.target.classList.remove(\"fade-out\");\n });\n };\n\n const loader = new FBXLoader(loadingManager);\n loader.load(fbx_model, _this.loadFBXobject, undefined, (error) => {\n console.error(\"Error loading FBX model:\", error);\n });\n },\n\n // Buttons\n restart() {\n for (const mixer of mixers) {\n mixer.setTime(0);\n }\n\n this.cur_frame = 0;\n },\n\n play() {\n for (const mixer of mixers) mixer.timeScale = 1;\n this.isPause = false;\n },\n\n next() {\n cur_fbx_model += 1;\n this.reset();\n console.log(fbx_models[cur_fbx_model]);\n this.loadFBXModel(fbx_models[cur_fbx_model]);\n if (cur_fbx_model >= fbx_models.length - 1) cur_fbx_model = -1;\n },\n\n pause() {\n for (const mixer of mixers) mixer.timeScale = 0;\n this.isPause = true;\n },\n\n setFrame(cur_frame) {\n const max_duration = Math.max(...durations);\n for (const mixer of mixers) {\n let timeScale = mixer.timeScale;\n mixer.timeScale = 1;\n mixer.setTime((max_duration * cur_frame) / this.num_frames);\n mixer.timeScale = timeScale;\n }\n },\n\n // Main function\n synthesis() {\n let _this = this;\n _this.isSynthesising = true;\n\n async function query(data) {\n const app = await client(synthesis_setting.api_url);\n const result = await app.predict(\"/predict\", [JSON.stringify(data)]);\n return result;\n }\n\n query({\n setting: synthesis_setting,\n tracks: meshes[0].animations[0].toJSON()[\"tracks\"],\n scale: _this.scale,\n }).then((res) => {\n // console.log(res.data[0].time.toFixed(4));\n ElMessage({\n message: h(\"p\", null, [\n h(\"span\", null, \"Generated in \"),\n h(\n \"i\",\n { style: \"color: red\" },\n res.data[0].time.toFixed(4).toString()\n ),\n h(\"span\", null, \" s on CPU without any training.\"),\n ]),\n duration: 3000,\n });\n\n let syn_mesh = SkeletonUtils.clone(meshes[0]);\n let syn_mixer = new THREE.AnimationMixer(syn_mesh);\n let animation = THREE.AnimationClip.parse(res.data[0]);\n let action = syn_mixer.clipAction(animation);\n\n action.play();\n\n syn_mesh.traverse(function (child) {\n if (child.isMesh) {\n child.castShadow = true;\n child.receiveShadow = true;\n }\n });\n scene.add(syn_mesh);\n\n let skeleton = new MySkeletonHelper(\n syn_mesh,\n new THREE.Color(0xb9394e),\n new THREE.Color(0xdc9042)\n );\n skeleton.material.linewidth = visibility_setting.skeleton_width / 1000;\n skeleton.visible = false;\n scene.add(skeleton);\n\n for (const mixer of mixers) mixer.setTime(0);\n durations.push(animation.duration);\n _this.num_frames =\n Math.round(Math.max(...durations) / duration_delta) + 1;\n\n meshes.push(syn_mesh);\n mixers.push(syn_mixer);\n skeletons.push(skeleton);\n\n // change position\n let pos = [\n [0, 0],\n [-2, -1],\n [-1, -1],\n [0, -1],\n [1, -1],\n [2, -1],\n [-2, -2],\n [-1, -2],\n [0, -2],\n [1, -2],\n [2, -2],\n [-2, -3],\n [-1, -3],\n [0, -3],\n [1, -3],\n [2, -3],\n [-2, -4],\n [-1, -4],\n [0, -4],\n [1, -4],\n [2, -4],\n [-2, -5],\n [-1, -5],\n [0, -5],\n [1, -5],\n [2, -5],\n ];\n for (let i = 0; i < meshes.length; i++) {\n meshes[i].position.x = pos[i][0];\n meshes[i].position.z = pos[i][1];\n }\n\n _this.isSynthesising = false;\n });\n },\n },\n components: {\n ArrowRight,\n VideoPlay,\n VideoPause,\n RefreshLeft,\n MagicStick,\n },\n};\n</script>\n\n<style scoped>\n#Canvas {\n width: 100%;\n height: 100%;\n position: fixed;\n left: 0px;\n right: 0px;\n top: 0px;\n bottom: 0px;\n}\n#loading-screen {\n display: block;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: #ffffff;\n opacity: 1;\n transition: 1s opacity;\n}\n#loading-screen.fade-out {\n opacity: 0;\n}\n#loader {\n display: block;\n position: relative;\n left: 50%;\n top: 50%;\n width: 150px;\n height: 150px;\n margin: -75px 0 0 -75px;\n border-radius: 50%;\n border: 3px solid transparent;\n border-top-color: #cf3e5b;\n -webkit-animation: spin 2s linear infinite;\n animation: spin 2s linear infinite;\n}\n#loader:before {\n content: \"\";\n position: absolute;\n top: 5px;\n left: 5px;\n right: 5px;\n bottom: 5px;\n border-radius: 50%;\n border: 3px solid transparent;\n border-top-color: #f2c94a;\n -webkit-animation: spin 3s linear infinite;\n animation: spin 3s linear infinite;\n}\n#loader:after {\n content: \"\";\n position: absolute;\n top: 15px;\n left: 15px;\n right: 15px;\n bottom: 15px;\n border-radius: 50%;\n border: 3px solid transparent;\n border-top-color: #52a7db;\n -webkit-animation: spin 1.5s linear infinite;\n animation: spin 1.5s linear infinite;\n}\n@-webkit-keyframes spin {\n 0% {\n -webkit-transform: rotate(0deg);\n -ms-transform: rotate(0deg);\n transform: rotate(0deg);\n }\n 100% {\n -webkit-transform: rotate(360deg);\n -ms-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@keyframes spin {\n 0% {\n -webkit-transform: rotate(0deg);\n -ms-transform: rotate(0deg);\n transform: rotate(0deg);\n }\n 100% {\n -webkit-transform: rotate(360deg);\n -ms-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n#loader-text {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n font-size: 24px;\n font-weight: bold;\n color: #000000;\n}\n#ProgressBar {\n width: 80%;\n position: fixed;\n bottom: 5%;\n margin: 0 auto;\n left: 0;\n right: 0;\n}\n\n#ProgressBar .frame-text {\n width: 100%;\n font-size: 15px;\n font-weight: 500;\n color: #343232;\n height: 22px;\n line-height: 22px;\n margin-left: 5px;\n}\n\n:deep(.el-slider__bar) {\n background-color: #63c6fa;\n}\n\n:deep(.el-slider__runway) {\n background-color: #ffffff;\n}\n\n:deep(.el-slider__button) {\n border-color: #63c6fa;\n}\n</style>\n","<script type=\"module\">\nimport * as THREE from 'three';\nimport { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';\nimport { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2.js';\nimport { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';\n\nexport default class MySkeletonHelper extends LineSegments2 {\n\t// constructor(object, color1=null, color2=null, filter=null) {\n\tconstructor(object, color1=null, color2=null) {\n\t\t// var bones = getBoneList(object, filter);\n\t\tvar bones = getBoneList(object);\n\t\tvar geometry = new LineSegmentsGeometry();\n\t\tif (color1 === null) {\n\t\t\tcolor1 = new THREE.Color(0x46BBA7);\n\t\t}\n\t\tif (color2 === null) {\n\t\t\tcolor2 = new THREE.Color(0x705CD3);\n\t\t}\n\n\t\tvar vertices = [];\n\t\tvar colors = [];\n\t\tvar matrix_world_inverse = new THREE.Matrix4();\n\n\t\tmatrix_world_inverse.copy(object.matrixWorld).invert();\n\n\t\tfor (var i = 0; i < bones.length; i++) {\n\t\t\tvar bone = bones[i];\n\n\t\t\t// First bone is the root and doesn't need a line.\n\t\t\t// Each bone has a start point (its parent) and an end point.\n\t\t\tif (bone.parent && bone.parent.isBone) {\n\t\t\t\tvertices.push(0, 0, 0);\n\t\t\t\tvertices.push(0, 0, 0);\n\t\t\t\tcolors.push(color1.r, color1.g, color1.b);\n\t\t\t\tcolors.push(color2.r, color2.g, color2.b);\n\t\t\t}\n\t\t}\n\n\t\tgeometry.setPositions(vertices);\n\t\tgeometry.setColors(colors);\n\n\t\tlet material = new LineMaterial({\n\t\t\tcolor: 0xffffff,\n\t\t\tlinewidth: 0.0075, // in pixels\n\t\t\tvertexColors: true,\n\t\t\tdashed: true,\n\t\t\tdepthTest: false,\n\t\t\tdepthWrite: false,\n\t\t});\n\n\t\tsuper(geometry, material);\n\t\tthis.type = 'MySkeletonHelper';\n\t\tthis.MySkeletonHelper = true;\n\n\t\tthis.root = object;\n\t\tthis.bones = bones;\n\n\t\tthis.matrix = object.matrixWorld;\n\t\tthis.matrixAutoUpdate = false;\n\t}\n\n\tupdateMatrixWorld(force) {\n\t\tvar position = this.geometry.getAttribute('position');\n\n\t\tvar vertices = [];\n\t\tvar vector = new THREE.Vector3();\n\t\tvar bone_matrix = new THREE.Matrix4();\n\t\tvar matrix_world_inverse = new THREE.Matrix4();\n\n\t\tmatrix_world_inverse.copy(this.root.matrixWorld).invert();\n\n\t\tfor (let i = 0; i < this.bones.length; i++) {\n\t\t\tvar bone = this.bones[i];\n\n\t\t\tif (bone.parent && bone.parent.isBone) {\n\t\t\t\tbone_matrix.multiplyMatrices(matrix_world_inverse, bone.matrixWorld);\n\t\t\t\tvector.setFromMatrixPosition(bone_matrix);\n\t\t\t\tvertices.push(vector.x, vector.y, vector.z);\n\n\t\t\t\tbone_matrix.multiplyMatrices(matrix_world_inverse, bone.parent.matrixWorld);\n\t\t\t\tvector.setFromMatrixPosition(bone_matrix);\n\t\t\t\tvertices.push(vector.x, vector.y, vector.z);\n\t\t\t}\n\t\t}\n\n\t\tthis.geometry.setPositions(vertices);\n\n\t\tposition.needsUpdate = true;\n\t\tsuper.updateMatrixWorld(force);\n\t}\n}\n\n\nfunction getBoneList(object) {\n\tconst boneList = [];\n\t// console.log(filter)\n\tif (object.isBone === true) {\n\t\t// console.log(object)\n\t\tboneList.push(object);\n\t}\n\n\tfor (let i = 0; i < object.children.length; i++) {\n\t\tboneList.push.apply(boneList, getBoneList(object.children[i]));\n\t}\n\n\treturn boneList;\n\n}\n\n\n</script>\n","import script from \"./MySkeletonHelper.vue?vue&type=script&lang=js\"\nexport * from \"./MySkeletonHelper.vue?vue&type=script&lang=js\"\n\nconst __exports__ = script;\n\nexport default __exports__","import { render } from \"./MotionSynthesis_random_synthesis.vue?vue&type=template&id=553d8d48&scoped=true\"\nimport script from \"./MotionSynthesis_random_synthesis.vue?vue&type=script&lang=js\"\nexport * from \"./MotionSynthesis_random_synthesis.vue?vue&type=script&lang=js\"\n\nimport \"./MotionSynthesis_random_synthesis.vue?vue&type=style&index=0&id=553d8d48&scoped=true&lang=css\"\n\nimport exportComponent from \"../../node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-553d8d48\"]])\n\nexport default __exports__","import { render } from \"./App.vue?vue&type=template&id=49d4c533\"\nimport script from \"./App.vue?vue&type=script&lang=js\"\nexport * from \"./App.vue?vue&type=script&lang=js\"\n\nimport \"./App.vue?vue&type=style&index=0&id=49d4c533&lang=css\"\n\nimport exportComponent from \"../node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render]])\n\nexport default __exports__","import { createApp } from 'vue'\nimport ElementPlus from \"element-plus\"\nimport 'element-plus/theme-chalk/index.css'\nimport App from './App.vue'\n\nconst app = createApp(App)\n\napp.use(ElementPlus)\napp.mount('#app')","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\tloaded: false,\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Flag the module as loaded\n\tmodule.loaded = true;\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","var deferred = [];\n__webpack_require__.O = function(result, chunkIds, fn, priority) {\n\tif(chunkIds) {\n\t\tpriority = priority || 0;\n\t\tfor(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar chunkIds = deferred[i][0];\n\t\tvar fn = deferred[i][1];\n\t\tvar priority = deferred[i][2];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every(function(key) { return __webpack_require__.O[key](chunkIds[j]); })) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = function(module) {\n\tvar getter = module && module.__esModule ?\n\t\tfunction() { return module['default']; } :\n\t\tfunction() { return module; };\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","var getProto = Object.getPrototypeOf ? function(obj) { return Object.getPrototypeOf(obj); } : function(obj) { return obj.__proto__; };\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach(function(key) { def[key] = function() { return value[key]; }; });\n\t}\n\tdef['default'] = function() { return value; };\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = function(chunkId) {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce(function(promises, key) {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = function(chunkId) {\n\t// return url for filenames based on template\n\treturn \"js/\" + chunkId + \".\" + \"3863b0a5\" + \".js\";\n};","// This function allow to reference async chunks\n__webpack_require__.miniCssF = function(chunkId) {\n\t// return url for filenames based on template\n\treturn undefined;\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","var inProgress = {};\nvar dataWebpackPrefix = \"patch-based_motion_synthesis:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = function(url, done, key, chunkId) {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = function(prev, event) {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach(function(fn) { return fn(event); });\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nmd = function(module) {\n\tmodule.paths = [];\n\tif (!module.children) module.children = [];\n\treturn module;\n};","__webpack_require__.p = \"/\";","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t143: 0\n};\n\n__webpack_require__.f.j = function(chunkId, promises) {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; });\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = function(event) {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = function(chunkId) { return installedChunks[chunkId] === 0; };\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = function(parentChunkLoadingFunction, data) {\n\tvar chunkIds = data[0];\n\tvar moreModules = data[1];\n\tvar runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some(function(id) { return installedChunks[id] !== 0; })) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunkpatch_based_motion_synthesis\"] = self[\"webpackChunkpatch_based_motion_synthesis\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","// startup\n// Load entry module and return exports\n// This entry module depends on other loaded chunks and execution need to be delayed\nvar __webpack_exports__ = __webpack_require__.O(undefined, [998], function() { return __webpack_require__(3644); })\n__webpack_exports__ = __webpack_require__.O(__webpack_exports__);\n"],"names":["_createBlock","_component_MotionSynthesis","_createElementVNode","id","class","_hoisted_1","_hoisted_2","_hoisted_3","_createVNode","_component_el_row","_component_el_slider","_ctx","cur_frame","$event","max","num_frames","size","onInput","$options","setFrame","_hoisted_4","_toDisplayString","_hoisted_5","style","_component_el_button","type","color","onClick","_cache","next","_component_el_icon","_component_ArrowRight","play","_component_VideoPlay","isPause","pause","_component_VideoPause","restart","_component_RefreshLeft","synthesis","_component_MagicStick","isSynthesising","loading","MySkeletonHelper","LineSegments2","constructor","object","color1","color2","bones","getBoneList","geometry","LineSegmentsGeometry","THREE","vertices","colors","matrix_world_inverse","copy","matrixWorld","invert","i","length","bone","parent","isBone","push","r","g","b","setPositions","setColors","material","LineMaterial","linewidth","vertexColors","dashed","depthTest","depthWrite","super","this","root","matrix","matrixAutoUpdate","updateMatrixWorld","force","position","getAttribute","vector","bone_matrix","multiplyMatrices","setFromMatrixPosition","x","y","z","needsUpdate","boneList","children","apply","__exports__","fbx_models","camera","scene","renderer","stats","panel","hemiLight","dirLight","spotLight","ground","duration_delta","cur_fbx_model","mixers","meshes","skeletons","durations","clock","near","far","ground_size","scene_setting","fog","hemi_light","dir_light","spot_light","visibility_setting","show_mesh","show_skeleton","skeleton_width","api_urls","_url_id","Math","floor","random","synthesis_setting","api_url","frames","noise_sigma","loop","completeness","alpha","patch_size","coarse_ratio","pyr_factor","num_steps","name","props","init_file","String","default","scale","Number","data","uploadFilename","uploadFiles","mounted","initScene","loadFBXModel","animate","methods","create_scene","container","document","getElementById","window","innerWidth","innerHeight","set","background","add","castShadow","shadow","mapSize","width","height","angle","PI","penumbra","loader","texture","load","wrapS","wrapT","magFilter","repeat","map","side","emissiveIntensity","rotation","receiveShadow","antialias","setPixelRatio","devicePixelRatio","setSize","shadowMap","enabled","appendChild","domElement","controls","OrbitControls","target","mouseButtons","LEFT","ROTATE","MIDDLE","PAN","update","addEventListener","onWindowResize","Stats","dom","create_panel","GUI","scene_folder","addFolder","onChange","value","remove","close","visibility_folder","visible","listen","synthesis_folder","alpha_setting","show","hide","aspect","updateProjectionMatrix","reset","_this","requestAnimationFrame","delta","getDelta","max_duration","max_duration_index","indexOf","Infinity","time","round","mixer","setTime","render","loadFBXobject","animations","tracks","action","clipAction","traverse","child","isMesh","multiplyScalar","skeleton","duration","times","fbx_model","updateProgress","loaded","total","percentComplete","textContent","loadingManager","onStart","url","loadingScreen","display","classList","onProgress","onLoad","event","FBXLoader","undefined","error","console","timeScale","log","async","query","app","client","result","predict","JSON","stringify","setting","toJSON","then","res","ElMessage","message","h","toFixed","toString","syn_mesh","SkeletonUtils","syn_mixer","animation","parse","pos","components","ArrowRight","VideoPlay","VideoPause","RefreshLeft","MagicStick","MotionSynthesis","createApp","App","use","ElementPlus","mount","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","exports","module","__webpack_modules__","call","m","deferred","O","chunkIds","fn","priority","notFulfilled","fulfilled","j","Object","keys","every","key","splice","n","getter","__esModule","d","a","leafPrototypes","getProto","getPrototypeOf","obj","__proto__","t","mode","ns","create","def","current","getOwnPropertyNames","forEach","definition","o","defineProperty","enumerable","get","f","e","chunkId","Promise","all","reduce","promises","u","miniCssF","globalThis","Function","prop","prototype","hasOwnProperty","inProgress","dataWebpackPrefix","l","done","script","needAttach","scripts","getElementsByTagName","s","createElement","charset","timeout","nc","setAttribute","src","onScriptComplete","prev","onerror","onload","clearTimeout","doneFns","parentNode","removeChild","setTimeout","bind","head","Symbol","toStringTag","nmd","paths","p","installedChunks","installedChunkData","promise","resolve","reject","Error","loadingEnded","errorType","realSrc","request","webpackJsonpCallback","parentChunkLoadingFunction","moreModules","runtime","some","chunkLoadingGlobal","self","__webpack_exports__"],"sourceRoot":""}
|
GenMM_demo/js/chunk-vendors.a7ae9901.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
GenMM_demo/js/chunk-vendors.a7ae9901.js.map
ADDED
The diff for this file is too large to render.
See raw diff
|
|
NN/losses.py
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
|
4 |
+
from .utils import extract_patches, combine_patches, efficient_cdist, get_NNs_Dists
|
5 |
+
|
6 |
+
def make_criteria(conf):
|
7 |
+
if conf['type'] == 'PatchCoherentLoss':
|
8 |
+
return PatchCoherentLoss(conf['patch_size'], stride=conf['stride'], loop=conf['loop'], coherent_alpha=conf['coherent_alpha'])
|
9 |
+
elif conf['type'] == 'SWDLoss':
|
10 |
+
raise NotImplementedError('SWDLoss is not implemented')
|
11 |
+
else:
|
12 |
+
raise ValueError('Invalid criteria: {}'.format(conf['criteria']))
|
13 |
+
|
14 |
+
class PatchCoherentLoss(torch.nn.Module):
|
15 |
+
def __init__(self, patch_size=7, stride=1, loop=False, coherent_alpha=None, cache=False):
|
16 |
+
super(PatchCoherentLoss, self).__init__()
|
17 |
+
self.patch_size = patch_size
|
18 |
+
self.stride = stride
|
19 |
+
self.loop = loop
|
20 |
+
self.coherent_alpha = coherent_alpha
|
21 |
+
assert self.stride == 1, "Only support stride of 1"
|
22 |
+
# assert self.patch_size % 2 == 1, "Only support odd patch size"
|
23 |
+
self.cache = cache
|
24 |
+
if cache:
|
25 |
+
self.cached_data = None
|
26 |
+
|
27 |
+
def forward(self, X, Ys, dist_wrapper=None, ext=None, return_blended_results=False):
|
28 |
+
"""For each patch in input X find its NN in target Y and sum the their distances"""
|
29 |
+
assert X.shape[0] == 1, "Only support batch size of 1"
|
30 |
+
dist_fn = lambda X, Y: dist_wrapper(efficient_cdist, X, Y) if dist_wrapper is not None else efficient_cdist(X, Y)
|
31 |
+
|
32 |
+
x_patches = extract_patches(X, self.patch_size, self.stride, loop=self.loop)
|
33 |
+
|
34 |
+
if not self.cache or self.cached_data is None:
|
35 |
+
y_patches = []
|
36 |
+
for y in Ys:
|
37 |
+
y_patches += [extract_patches(y, self.patch_size, self.stride, loop=False)]
|
38 |
+
y_patches = torch.cat(y_patches, dim=1)
|
39 |
+
self.cached_data = y_patches
|
40 |
+
else:
|
41 |
+
y_patches = self.cached_data
|
42 |
+
|
43 |
+
nnf, dist = get_NNs_Dists(dist_fn, x_patches.squeeze(0), y_patches.squeeze(0), self.coherent_alpha)
|
44 |
+
|
45 |
+
if return_blended_results:
|
46 |
+
return combine_patches(X.shape, y_patches[:, nnf, :], self.patch_size, self.stride, loop=self.loop), dist.mean()
|
47 |
+
else:
|
48 |
+
return dist.mean()
|
49 |
+
|
50 |
+
def clean_cache(self):
|
51 |
+
self.cached_data = None
|
NN/utils.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn.functional as F
|
3 |
+
import unfoldNd
|
4 |
+
|
5 |
+
def extract_patches(x, patch_size, stride, loop=False):
|
6 |
+
"""Extract patches from a motion sequence"""
|
7 |
+
b, c, _t = x.shape
|
8 |
+
|
9 |
+
# manually padding to loop
|
10 |
+
if loop:
|
11 |
+
half = patch_size // 2
|
12 |
+
front, tail = x[:,:,:half], x[:,:,-half:]
|
13 |
+
x = torch.concat([tail, x, front], dim=-1)
|
14 |
+
|
15 |
+
x_patches = unfoldNd.unfoldNd(x, kernel_size=patch_size, stride=stride).transpose(1, 2).reshape(b, -1, c, patch_size)
|
16 |
+
|
17 |
+
return x_patches.view(b, -1, c * patch_size)
|
18 |
+
|
19 |
+
def combine_patches(x_shape, ys, patch_size, stride, loop=False):
|
20 |
+
"""Combine motion patches"""
|
21 |
+
# manually handle to loop
|
22 |
+
out_shape = [*x_shape]
|
23 |
+
if loop:
|
24 |
+
padding = patch_size // 2
|
25 |
+
out_shape[-1] = out_shape[-1] + padding * 2
|
26 |
+
|
27 |
+
combined = unfoldNd.foldNd(ys.permute(0, 2, 1), output_size=tuple(out_shape[-1:]), kernel_size=patch_size, stride=stride)
|
28 |
+
|
29 |
+
# normal fold matrix
|
30 |
+
input_ones = torch.ones(tuple(out_shape), dtype=ys.dtype, device=ys.device)
|
31 |
+
divisor = unfoldNd.unfoldNd(input_ones, kernel_size=patch_size, stride=stride)
|
32 |
+
divisor = unfoldNd.foldNd(divisor, output_size=tuple(out_shape[-1:]), kernel_size=patch_size, stride=stride)
|
33 |
+
combined = (combined / divisor).squeeze(dim=0).unsqueeze(0)
|
34 |
+
|
35 |
+
if loop:
|
36 |
+
half = patch_size // 2
|
37 |
+
front, tail = combined[:,:,:half], combined[:,:,-half:]
|
38 |
+
combined[:, :, half:2 * half] = (combined[:, :, half:2 * half] + tail) / 2
|
39 |
+
combined[:, :, - 2 * half:-half] = (front + combined[:, :, - 2 * half:-half]) / 2
|
40 |
+
combined = combined[:, :, half:-half]
|
41 |
+
|
42 |
+
return combined
|
43 |
+
|
44 |
+
|
45 |
+
def efficient_cdist(X, Y):
|
46 |
+
"""
|
47 |
+
Pytorch efficient way of computing distances between all vectors in X and Y, i.e (X[:, None] - Y[None, :])**2
|
48 |
+
Get the nearest neighbor index from Y for each X
|
49 |
+
:param X: (n1, d) tensor
|
50 |
+
:param Y: (n2, d) tensor
|
51 |
+
Returns a n2 n1 of indices
|
52 |
+
"""
|
53 |
+
dist = (X * X).sum(1)[:, None] + (Y * Y).sum(1)[None, :] - 2.0 * torch.mm(X, torch.transpose(Y, 0, 1))
|
54 |
+
d = X.shape[1]
|
55 |
+
dist /= d # normalize by size of vector to make dists independent of the size of d ( use same alpha for all patche-sizes)
|
56 |
+
return dist # DO NOT use torch.sqrt
|
57 |
+
|
58 |
+
|
59 |
+
def get_col_mins_efficient(dist_fn, X, Y, b=1024):
|
60 |
+
"""
|
61 |
+
Computes the l2 distance to the closest x or each y.
|
62 |
+
:param X: (n1, d) tensor
|
63 |
+
:param Y: (n2, d) tensor
|
64 |
+
Returns n1 long array of L2 distances
|
65 |
+
"""
|
66 |
+
n_batches = len(Y) // b
|
67 |
+
mins = torch.zeros(Y.shape[0], dtype=X.dtype, device=X.device)
|
68 |
+
for i in range(n_batches):
|
69 |
+
mins[i * b:(i + 1) * b] = dist_fn(X, Y[i * b:(i + 1) * b]).min(0)[0]
|
70 |
+
if len(Y) % b != 0:
|
71 |
+
mins[n_batches * b:] = dist_fn(X, Y[n_batches * b:]).min(0)[0]
|
72 |
+
|
73 |
+
return mins
|
74 |
+
|
75 |
+
|
76 |
+
def get_NNs_Dists(dist_fn, X, Y, alpha=None, b=1024):
|
77 |
+
"""
|
78 |
+
Get the nearest neighbor index from Y for each X.
|
79 |
+
Avoids holding a (n1 * n2) amtrix in order to reducing memory footprint to (b * max(n1,n2)).
|
80 |
+
:param X: (n1, d) tensor
|
81 |
+
:param Y: (n2, d) tensor
|
82 |
+
Returns a n2 n1 of indices amd distances
|
83 |
+
"""
|
84 |
+
if alpha is not None:
|
85 |
+
normalizing_row = get_col_mins_efficient(dist_fn, X, Y, b=b)
|
86 |
+
normalizing_row = alpha + normalizing_row[None, :]
|
87 |
+
else:
|
88 |
+
normalizing_row = 1
|
89 |
+
|
90 |
+
NNs = torch.zeros(X.shape[0], dtype=torch.long, device=X.device)
|
91 |
+
Dists = torch.zeros(X.shape[0], dtype=torch.float, device=X.device)
|
92 |
+
|
93 |
+
n_batches = len(X) // b
|
94 |
+
for i in range(n_batches):
|
95 |
+
dists = dist_fn(X[i * b:(i + 1) * b], Y) / normalizing_row
|
96 |
+
NNs[i * b:(i + 1) * b] = dists.min(1)[1]
|
97 |
+
Dists[i * b:(i + 1) * b] = dists.min(1)[0]
|
98 |
+
if len(X) % b != 0:
|
99 |
+
dists = dist_fn(X[n_batches * b:], Y) / normalizing_row
|
100 |
+
NNs[n_batches * b:] = dists.min(1)[1]
|
101 |
+
Dists[n_batches * b: ] = dists.min(1)[0]
|
102 |
+
|
103 |
+
return NNs, Dists
|
README.md
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: GenMM
|
3 |
+
emoji: 🌍
|
4 |
+
colorFrom: purple
|
5 |
+
colorTo: red
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 3.33.1
|
8 |
+
app_file: app.py
|
9 |
+
pinned: false
|
10 |
+
duplicated_from: radames/GenMM-demo
|
11 |
+
---
|
12 |
+
|
13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import time
|
3 |
+
|
4 |
+
import uvicorn
|
5 |
+
from pathlib import Path
|
6 |
+
from fastapi import FastAPI
|
7 |
+
from fastapi.responses import RedirectResponse
|
8 |
+
from fastapi.staticfiles import StaticFiles
|
9 |
+
|
10 |
+
from dataset.tracks_motion import TracksMotion
|
11 |
+
from GPS import GPS
|
12 |
+
import gradio as gr
|
13 |
+
|
14 |
+
|
15 |
+
def _synthesis(synthesis_setting, motion_data):
|
16 |
+
model = GPS(
|
17 |
+
init_mode=f"random_synthesis/{synthesis_setting['frames']}",
|
18 |
+
noise_sigma=synthesis_setting["noise_sigma"],
|
19 |
+
coarse_ratio=0.2,
|
20 |
+
pyr_factor=synthesis_setting["pyr_factor"],
|
21 |
+
num_stages_limit=-1,
|
22 |
+
silent=True,
|
23 |
+
device="cpu",
|
24 |
+
)
|
25 |
+
|
26 |
+
synthesized_motion = model.run(
|
27 |
+
motion_data,
|
28 |
+
mode="match_and_blend",
|
29 |
+
ext={
|
30 |
+
"criteria": {
|
31 |
+
"type": "PatchCoherentLoss",
|
32 |
+
"patch_size": synthesis_setting["patch_size"],
|
33 |
+
"stride": synthesis_setting["stride"]
|
34 |
+
if "stride" in synthesis_setting.keys()
|
35 |
+
else 1,
|
36 |
+
"loop": synthesis_setting["loop"],
|
37 |
+
"coherent_alpha": synthesis_setting["alpha"]
|
38 |
+
if synthesis_setting["completeness"]
|
39 |
+
else None,
|
40 |
+
},
|
41 |
+
"optimizer": "match_and_blend",
|
42 |
+
"num_itrs": synthesis_setting["num_steps"],
|
43 |
+
},
|
44 |
+
)
|
45 |
+
|
46 |
+
return synthesized_motion
|
47 |
+
|
48 |
+
|
49 |
+
def synthesis(data):
|
50 |
+
data = json.loads(data)
|
51 |
+
# create track object
|
52 |
+
data["setting"]["coarse_ratio"] = -1
|
53 |
+
motion_data = TracksMotion(data["tracks"], scale=data["scale"])
|
54 |
+
start = time.time()
|
55 |
+
synthesized_motion = _synthesis(data["setting"], [motion_data])
|
56 |
+
end = time.time()
|
57 |
+
data["time"] = end - start
|
58 |
+
data["tracks"] = motion_data.parse(synthesized_motion)
|
59 |
+
|
60 |
+
return data
|
61 |
+
|
62 |
+
|
63 |
+
with gr.Blocks() as demo:
|
64 |
+
gr.HTML(
|
65 |
+
"""<iframe src="/GenMM_demo/" width="100%" height="700px" style="border:none;">"""
|
66 |
+
)
|
67 |
+
json_in = gr.JSON(visible=False)
|
68 |
+
json_out = gr.JSON(visible=False)
|
69 |
+
btn = gr.Button("Synthesize", visible=False)
|
70 |
+
btn.click(synthesis, inputs=[json_in], outputs=[json_out], api_name="predict")
|
71 |
+
|
72 |
+
|
73 |
+
app = FastAPI()
|
74 |
+
|
75 |
+
static_dir = Path("./GenMM_demo")
|
76 |
+
app.mount("/GenMM_demo", StaticFiles(directory=static_dir, html=True), name="static")
|
77 |
+
app = gr.mount_gradio_app(app, demo, path="/")
|
78 |
+
|
79 |
+
|
80 |
+
# serve the app
|
81 |
+
if __name__ == "__main__":
|
82 |
+
uvicorn.run("app:app", host="0.0.0.0", port=7860)
|
configs/random_synthesis.yaml
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
outout_dir: './output/random_synthesis'
|
2 |
+
|
3 |
+
# for GANimator BVH data
|
4 |
+
skeleton_aware: true
|
5 |
+
use_velo: true
|
6 |
+
repr: 'repr6d'
|
7 |
+
contact: true
|
8 |
+
keep_y_pos: true
|
9 |
+
joint_reduction: true
|
10 |
+
|
11 |
+
|
12 |
+
# for synthesis
|
13 |
+
coarse_ratio: -1
|
14 |
+
coarse_ratio_factor: 10
|
15 |
+
pyr_factor: 0.75
|
16 |
+
num_stages_limit: -1
|
17 |
+
noise_sigma: 10.0
|
18 |
+
patch_size: 11
|
19 |
+
loop: false
|
20 |
+
loss_type: 'PatchCoherent'
|
21 |
+
coherent_alpha: 0.01
|
22 |
+
optimizer: 'RMSprop'
|
23 |
+
lr: 0.01
|
24 |
+
num_steps: 3
|
25 |
+
decay_rate: 0.9
|
26 |
+
decay_steps: 0.9
|
27 |
+
|
28 |
+
# for visualization (only for blender render)
|
29 |
+
visualization: true
|
30 |
+
fbx_path: null
|
31 |
+
reso: '[1920, 1080]'
|
32 |
+
samples: 64
|
33 |
+
fps: 30
|
34 |
+
frame_end: -1
|
35 |
+
camera_pos: '[0, -8, 2.5]'
|
36 |
+
target_pos: '[0, 2, 0.5]'
|
dataset/tracks_motion.py
ADDED
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from os.path import join as pjoin
|
3 |
+
import numpy as np
|
4 |
+
import copy
|
5 |
+
import torch
|
6 |
+
import torch.nn.functional as F
|
7 |
+
from utils.transforms import quat2repr6d, quat2euler, repr6d2quat
|
8 |
+
|
9 |
+
class TracksParser():
|
10 |
+
def __init__(self, tracks_json, scale=1.0, requires_contact=False, joint_reduction=False):
|
11 |
+
assert requires_contact==False, 'contact is not implemented for tracks data yet!!!'
|
12 |
+
|
13 |
+
self.tracks_json = tracks_json
|
14 |
+
self.scale = scale
|
15 |
+
self.requires_contact = requires_contact
|
16 |
+
self.joint_reduction = joint_reduction
|
17 |
+
|
18 |
+
self.skeleton_names = []
|
19 |
+
self.rotations = []
|
20 |
+
for i, track in enumerate(self.tracks_json):
|
21 |
+
# print(i, track['name'])
|
22 |
+
self.skeleton_names.append(track['name'])
|
23 |
+
if i == 0:
|
24 |
+
assert track['type'] == 'vector'
|
25 |
+
self.position = np.array(track['values']).reshape(-1, 3) * self.scale
|
26 |
+
self.num_frames = self.position.shape[0]
|
27 |
+
else:
|
28 |
+
assert track['type'] == 'quaternion' # DEAFULT: quaternion
|
29 |
+
rotation = np.array(track['values']).reshape(-1, 4)
|
30 |
+
if rotation.shape[0] == 0:
|
31 |
+
rotation = np.zeros((self.num_frames, 4))
|
32 |
+
elif rotation.shape[0] < self.num_frames:
|
33 |
+
rotation = np.repeat(rotation, self.num_frames // rotation.shape[0], axis=0)
|
34 |
+
elif rotation.shape[0] > self.num_frames:
|
35 |
+
rotation = rotation[:self.num_frames]
|
36 |
+
self.rotations += [rotation]
|
37 |
+
self.rotations = np.array(self.rotations, dtype=np.float32)
|
38 |
+
|
39 |
+
def to_tensor(self, repr='euler', rot_only=False):
|
40 |
+
if repr not in ['euler', 'quat', 'quaternion', 'repr6d']:
|
41 |
+
raise Exception('Unknown rotation representation')
|
42 |
+
rotations = self.get_rotation(repr=repr)
|
43 |
+
positions = self.get_position()
|
44 |
+
|
45 |
+
if rot_only:
|
46 |
+
return rotations.reshape(rotations.shape[0], -1)
|
47 |
+
|
48 |
+
if self.requires_contact:
|
49 |
+
virtual_contact = torch.zeros_like(rotations[:, :len(self.skeleton.contact_id)])
|
50 |
+
virtual_contact[..., 0] = self.contact_label
|
51 |
+
rotations = torch.cat([rotations, virtual_contact], dim=1)
|
52 |
+
|
53 |
+
rotations = rotations.reshape(rotations.shape[0], -1)
|
54 |
+
return torch.cat((rotations, positions), dim=-1)
|
55 |
+
|
56 |
+
def get_rotation(self, repr='quat'):
|
57 |
+
if repr == 'quaternion' or repr == 'quat' or repr == 'repr6d':
|
58 |
+
rotations = torch.tensor(self.rotations, dtype=torch.float).transpose(0, 1)
|
59 |
+
if repr == 'repr6d':
|
60 |
+
rotations = quat2repr6d(rotations)
|
61 |
+
if repr == 'euler':
|
62 |
+
rotations = quat2euler(rotations)
|
63 |
+
return rotations
|
64 |
+
|
65 |
+
def get_position(self):
|
66 |
+
return torch.tensor(self.position, dtype=torch.float32)
|
67 |
+
|
68 |
+
class TracksMotion:
|
69 |
+
def __init__(self, tracks_json, scale=1.0, repr='repr6d', padding=False,
|
70 |
+
use_velo=True, contact=False, keep_y_pos=True, joint_reduction=False):
|
71 |
+
self.scale = scale
|
72 |
+
self.tracks = TracksParser(tracks_json, scale, requires_contact=contact, joint_reduction=joint_reduction)
|
73 |
+
self.raw_motion = self.tracks.to_tensor(repr=repr)
|
74 |
+
self.extra = {
|
75 |
+
|
76 |
+
}
|
77 |
+
|
78 |
+
self.repr = repr
|
79 |
+
if repr == 'quat':
|
80 |
+
self.n_rot = 4
|
81 |
+
elif repr == 'repr6d':
|
82 |
+
self.n_rot = 6
|
83 |
+
elif repr == 'euler':
|
84 |
+
self.n_rot = 3
|
85 |
+
self.padding = padding
|
86 |
+
self.use_velo = use_velo
|
87 |
+
self.contact = contact
|
88 |
+
self.keep_y_pos = keep_y_pos
|
89 |
+
self.joint_reduction = joint_reduction
|
90 |
+
|
91 |
+
self.raw_motion = self.raw_motion.permute(1, 0).unsqueeze_(0) # Shape = (1, n_channel, n_frames)
|
92 |
+
self.extra['global_pos'] = self.raw_motion[:, -3:, :]
|
93 |
+
|
94 |
+
if padding:
|
95 |
+
self.n_pad = self.n_rot - 3 # pad position channels
|
96 |
+
paddings = torch.zeros_like(self.raw_motion[:, :self.n_pad])
|
97 |
+
self.raw_motion = torch.cat((self.raw_motion, paddings), dim=1)
|
98 |
+
else:
|
99 |
+
self.n_pad = 0
|
100 |
+
self.raw_motion = torch.cat((self.raw_motion[:, :-3-self.n_pad], self.raw_motion[:, -3-self.n_pad:]), dim=1)
|
101 |
+
|
102 |
+
if self.use_velo:
|
103 |
+
self.msk = [-3, -2, -1] if not keep_y_pos else [-3, -1]
|
104 |
+
self.raw_motion = self.pos2velo(self.raw_motion)
|
105 |
+
|
106 |
+
self.n_contact = len(self.tracks.skeleton.contact_id) if contact else 0
|
107 |
+
|
108 |
+
@property
|
109 |
+
def n_channels(self):
|
110 |
+
return self.raw_motion.shape[1]
|
111 |
+
|
112 |
+
def __len__(self):
|
113 |
+
return self.raw_motion.shape[-1]
|
114 |
+
|
115 |
+
def pos2velo(self, pos):
|
116 |
+
msk = [i - self.n_pad for i in self.msk]
|
117 |
+
velo = pos.detach().clone().to(pos.device)
|
118 |
+
velo[:, msk, 1:] = pos[:, msk, 1:] - pos[:, msk, :-1]
|
119 |
+
self.begin_pos = pos[:, msk, 0].clone()
|
120 |
+
velo[:, msk, 0] = pos[:, msk, 1]
|
121 |
+
return velo
|
122 |
+
|
123 |
+
def velo2pos(self, velo):
|
124 |
+
msk = [i - self.n_pad for i in self.msk]
|
125 |
+
pos = velo.detach().clone().to(velo.device)
|
126 |
+
pos[:, msk, 0] = self.begin_pos.to(velo.device)
|
127 |
+
pos[:, msk] = torch.cumsum(velo[:, msk], dim=-1)
|
128 |
+
return pos
|
129 |
+
|
130 |
+
def motion2pos(self, motion):
|
131 |
+
if not self.use_velo:
|
132 |
+
return motion
|
133 |
+
else:
|
134 |
+
self.velo2pos(motion.clone())
|
135 |
+
|
136 |
+
def sample(self, size=None, slerp=False, align_corners=False):
|
137 |
+
if size is None:
|
138 |
+
return {'motion': self.raw_motion, 'extra': self.extra}
|
139 |
+
else:
|
140 |
+
if slerp:
|
141 |
+
raise NotImplementedError('slerp is not not implemented yet!!!')
|
142 |
+
else:
|
143 |
+
motion = F.interpolate(self.raw_motion, size=size, mode='linear', align_corners=align_corners)
|
144 |
+
extra = {}
|
145 |
+
if 'global_pos' in self.extra.keys():
|
146 |
+
extra['global_pos'] = F.interpolate(self.extra['global_pos'], size=size, mode='linear', align_corners=align_corners)
|
147 |
+
|
148 |
+
return motion
|
149 |
+
# return {'motion': motion, 'extra': extra}
|
150 |
+
|
151 |
+
def parse(self, motion, keep_velo=False,):
|
152 |
+
"""
|
153 |
+
No batch support here!!!
|
154 |
+
:returns tracks_json
|
155 |
+
"""
|
156 |
+
motion = motion.clone()
|
157 |
+
|
158 |
+
if self.use_velo and not keep_velo:
|
159 |
+
motion = self.velo2pos(motion)
|
160 |
+
if self.n_pad:
|
161 |
+
motion = motion[:, :-self.n_pad]
|
162 |
+
if self.contact:
|
163 |
+
raise NotImplementedError('contact is not implemented yet!!!')
|
164 |
+
|
165 |
+
motion = motion.squeeze().permute(1, 0)
|
166 |
+
pos = motion[..., -3:] / self.scale
|
167 |
+
rot = motion[..., :-3].reshape(motion.shape[0], -1, self.n_rot)
|
168 |
+
if self.repr == 'repr6d':
|
169 |
+
rot = repr6d2quat(rot)
|
170 |
+
elif self.repr == 'euler':
|
171 |
+
raise NotImplementedError('parse "euler is not implemented yet!!!')
|
172 |
+
|
173 |
+
times = []
|
174 |
+
out_tracks_json = copy.deepcopy(self.tracks.tracks_json)
|
175 |
+
for i, _track in enumerate(out_tracks_json):
|
176 |
+
if i == 0:
|
177 |
+
times = [ j * out_tracks_json[i]['times'][1] for j in range(motion.shape[0])]
|
178 |
+
out_tracks_json[i]['values'] = pos.flatten().detach().cpu().numpy().tolist()
|
179 |
+
else:
|
180 |
+
out_tracks_json[i]['values'] = rot[:, i-1, :].flatten().detach().cpu().numpy().tolist()
|
181 |
+
out_tracks_json[i]['times'] = times
|
182 |
+
|
183 |
+
return out_tracks_json
|
requirements.txt
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
torch
|
2 |
+
filterpy==1.4.5
|
3 |
+
torchvision==0.12.0
|
4 |
+
tensorboardX==2.5
|
5 |
+
protobuf==3.20.1
|
6 |
+
scipy==1.7.3
|
7 |
+
tqdm==4.62.3
|
8 |
+
unfoldNd
|
9 |
+
flask==2.1.3
|
10 |
+
flask-cors==3.0.10
|
11 |
+
pyyaml>=5.3.1
|
12 |
+
requests
|
13 |
+
tensorboard
|
14 |
+
transforms3d
|
15 |
+
imageio
|
16 |
+
imageio-ffmpeg
|
17 |
+
fastapi
|
18 |
+
gradio
|
19 |
+
uvicorn
|
utils/base.py
ADDED
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import os.path as osp
|
3 |
+
import sys
|
4 |
+
import time
|
5 |
+
import yaml
|
6 |
+
import imageio
|
7 |
+
import random
|
8 |
+
import shutil
|
9 |
+
import random
|
10 |
+
import numpy as np
|
11 |
+
import torch
|
12 |
+
from tqdm import tqdm
|
13 |
+
import matplotlib.pyplot as plt
|
14 |
+
|
15 |
+
class ConfigParser():
|
16 |
+
def __init__(self, args):
|
17 |
+
"""
|
18 |
+
class to parse configuration.
|
19 |
+
"""
|
20 |
+
args = args.parse_args()
|
21 |
+
self.cfg = self.merge_config_file(args)
|
22 |
+
|
23 |
+
# set random seed
|
24 |
+
self.set_seed()
|
25 |
+
|
26 |
+
def __str__(self):
|
27 |
+
return str(self.cfg.__dict__)
|
28 |
+
|
29 |
+
def __getattr__(self, name):
|
30 |
+
"""
|
31 |
+
Access items use dot.notation.
|
32 |
+
"""
|
33 |
+
return self.cfg.__dict__[name]
|
34 |
+
|
35 |
+
def __getitem__(self, name):
|
36 |
+
"""
|
37 |
+
Access items like ordinary dict.
|
38 |
+
"""
|
39 |
+
return self.cfg.__dict__[name]
|
40 |
+
|
41 |
+
def merge_config_file(self, args, allow_invalid=True):
|
42 |
+
"""
|
43 |
+
Load json config file and merge the arguments
|
44 |
+
"""
|
45 |
+
assert args.config is not None
|
46 |
+
with open(args.config, 'r') as f:
|
47 |
+
cfg = yaml.safe_load(f)
|
48 |
+
if 'config' in cfg.keys():
|
49 |
+
del cfg['config']
|
50 |
+
f.close()
|
51 |
+
invalid_args = list(set(cfg.keys()) - set(dir(args)))
|
52 |
+
if invalid_args and not allow_invalid:
|
53 |
+
raise ValueError(f"Invalid args {invalid_args} in {args.config}.")
|
54 |
+
|
55 |
+
for k in list(cfg.keys()):
|
56 |
+
if k in args.__dict__.keys() and args.__dict__[k] is not None:
|
57 |
+
print('=========> overwrite config: {} = {}'.format(k, args.__dict__[k]))
|
58 |
+
del cfg[k]
|
59 |
+
|
60 |
+
args.__dict__.update(cfg)
|
61 |
+
|
62 |
+
return args
|
63 |
+
|
64 |
+
def set_seed(self):
|
65 |
+
''' set random seed for random, numpy and torch. '''
|
66 |
+
if 'seed' not in self.cfg.__dict__.keys():
|
67 |
+
return
|
68 |
+
if self.cfg.seed is None:
|
69 |
+
self.cfg.seed = int(time.time()) % 1000000
|
70 |
+
print('=========> set random seed: {}'.format(self.cfg.seed))
|
71 |
+
# fix random seeds for reproducibility
|
72 |
+
random.seed(self.cfg.seed)
|
73 |
+
np.random.seed(self.cfg.seed)
|
74 |
+
torch.manual_seed(self.cfg.seed)
|
75 |
+
torch.cuda.manual_seed(self.cfg.seed)
|
76 |
+
|
77 |
+
def save_codes_and_config(self, save_path):
|
78 |
+
"""
|
79 |
+
save codes and config to $save_path.
|
80 |
+
"""
|
81 |
+
cur_codes_path = osp.dirname(osp.dirname(os.path.abspath(__file__)))
|
82 |
+
if os.path.exists(save_path):
|
83 |
+
shutil.rmtree(save_path)
|
84 |
+
shutil.copytree(cur_codes_path, osp.join(save_path, 'codes'), \
|
85 |
+
ignore=shutil.ignore_patterns('*debug*', '*data*', '*output*', '*exps*', '*.txt', '*.json', '*.mp4', '*.png', '*.jpg', '*.bvh', '*.csv', '*.pth', '*.tar', '*.npz'))
|
86 |
+
|
87 |
+
with open(osp.join(save_path, 'config.yaml'), 'w') as f:
|
88 |
+
f.write(yaml.dump(self.cfg.__dict__))
|
89 |
+
f.close()
|
90 |
+
|
91 |
+
|
92 |
+
# other utils
|
93 |
+
class logger:
|
94 |
+
"""Keeps track of the levels and steps of optimization. Logs it via TQDM"""
|
95 |
+
def __init__(self, n_steps, n_lvls):
|
96 |
+
self.n_steps = n_steps
|
97 |
+
self.n_lvls = n_lvls
|
98 |
+
self.lvl = -1
|
99 |
+
self.lvl_step = 0
|
100 |
+
self.steps = 0
|
101 |
+
self.pbar = tqdm(total=self.n_lvls * self.n_steps, desc='Starting')
|
102 |
+
|
103 |
+
def step(self):
|
104 |
+
self.pbar.update(1)
|
105 |
+
self.steps += 1
|
106 |
+
self.lvl_step += 1
|
107 |
+
|
108 |
+
def new_lvl(self):
|
109 |
+
self.lvl += 1
|
110 |
+
self.lvl_step = 0
|
111 |
+
|
112 |
+
def print(self):
|
113 |
+
self.pbar.set_description(f'Lvl {self.lvl}/{self.n_lvls-1}, step {self.lvl_step}/{self.n_steps}')
|
114 |
+
|
115 |
+
|
116 |
+
def set_seed(seed):
|
117 |
+
if seed is not None:
|
118 |
+
random.seed(seed)
|
119 |
+
np.random.seed(seed)
|
120 |
+
torch.manual_seed(seed)
|
121 |
+
torch.cuda.manual_seed(seed)
|
122 |
+
|
123 |
+
|
124 |
+
# debug utils
|
125 |
+
def draw_trajectory(trajectory, save_path=None, anim=True):
|
126 |
+
r = max(abs(trajectory.min()), trajectory.max())
|
127 |
+
if anim:
|
128 |
+
imgs = []
|
129 |
+
for i in tqdm(range(1, trajectory.shape[0])):
|
130 |
+
plt.plot(trajectory[:i, 0], trajectory[:i, 2], color='red')
|
131 |
+
plt.xlim(-r-1, r+1)
|
132 |
+
plt.ylim(-r-1, r+1)
|
133 |
+
plt.savefig(save_path + '.png')
|
134 |
+
imgs += [imageio.imread(save_path + '.png')]
|
135 |
+
imageio.mimwrite(save_path + '.mp4', imgs)
|
136 |
+
plt.close()
|
137 |
+
else:
|
138 |
+
# plt.scatter(trajectory[:, 0], trajectory[:, 1], trajectory[:, 2])
|
139 |
+
plt.plot(trajectory[:, 0], trajectory[:, 2], color='red')
|
140 |
+
plt.xlim(-r*1.5, r*1.5)
|
141 |
+
plt.ylim(-r*1.5, r*1.5)
|
142 |
+
if save_path is not None:
|
143 |
+
plt.savefig(save_path + '.png')
|
144 |
+
plt.close()
|
145 |
+
|
146 |
+
# velo = self.raw_motion[0, self.mask, :].numpy()
|
147 |
+
# print(velo.shape)
|
148 |
+
# imgs = []
|
utils/contact.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
|
3 |
+
|
4 |
+
def foot_contact_by_height(pos):
|
5 |
+
eps = 0.25
|
6 |
+
return (-eps < pos[..., 1]) * (pos[..., 1] < eps)
|
7 |
+
|
8 |
+
|
9 |
+
def velocity(pos, padding=False):
|
10 |
+
velo = pos[1:, ...] - pos[:-1, ...]
|
11 |
+
velo_norm = torch.norm(velo, dim=-1)
|
12 |
+
if padding:
|
13 |
+
pad = torch.zeros_like(velo_norm[:1, :])
|
14 |
+
velo_norm = torch.cat([pad, velo_norm], dim=0)
|
15 |
+
return velo_norm
|
16 |
+
|
17 |
+
|
18 |
+
def foot_contact(pos, ref_height=1., threshold=0.018):
|
19 |
+
velo_norm = velocity(pos)
|
20 |
+
contact = velo_norm < threshold
|
21 |
+
contact = contact.int()
|
22 |
+
padding = torch.zeros_like(contact)
|
23 |
+
contact = torch.cat([padding[:1, :], contact], dim=0)
|
24 |
+
return contact
|
25 |
+
|
26 |
+
|
27 |
+
def alpha(t):
|
28 |
+
return 2.0 * t * t * t - 3.0 * t * t + 1
|
29 |
+
|
30 |
+
|
31 |
+
def lerp(a, l, r):
|
32 |
+
return (1 - a) * l + a * r
|
33 |
+
|
34 |
+
|
35 |
+
def constrain_from_contact(contact, glb, fid='TBD', L=5):
|
36 |
+
"""
|
37 |
+
:param contact: contact label
|
38 |
+
:param glb: original global position
|
39 |
+
:param fid: joint id to fix, corresponding to the order in contact
|
40 |
+
:param L: frame to look forward/backward
|
41 |
+
:return:
|
42 |
+
"""
|
43 |
+
T = glb.shape[0]
|
44 |
+
|
45 |
+
for i, fidx in enumerate(fid): # fidx: index of the foot joint
|
46 |
+
fixed = contact[:, i] # [T]
|
47 |
+
s = 0
|
48 |
+
while s < T:
|
49 |
+
while s < T and fixed[s] == 0:
|
50 |
+
s += 1
|
51 |
+
if s >= T:
|
52 |
+
break
|
53 |
+
t = s
|
54 |
+
avg = glb[t, fidx].clone()
|
55 |
+
while t + 1 < T and fixed[t + 1] == 1:
|
56 |
+
t += 1
|
57 |
+
avg += glb[t, fidx].clone()
|
58 |
+
avg /= (t - s + 1)
|
59 |
+
|
60 |
+
for j in range(s, t + 1):
|
61 |
+
glb[j, fidx] = avg.clone()
|
62 |
+
s = t + 1
|
63 |
+
|
64 |
+
for s in range(T):
|
65 |
+
if fixed[s] == 1:
|
66 |
+
continue
|
67 |
+
l, r = None, None
|
68 |
+
consl, consr = False, False
|
69 |
+
for k in range(L):
|
70 |
+
if s - k - 1 < 0:
|
71 |
+
break
|
72 |
+
if fixed[s - k - 1]:
|
73 |
+
l = s - k - 1
|
74 |
+
consl = True
|
75 |
+
break
|
76 |
+
for k in range(L):
|
77 |
+
if s + k + 1 >= T:
|
78 |
+
break
|
79 |
+
if fixed[s + k + 1]:
|
80 |
+
r = s + k + 1
|
81 |
+
consr = True
|
82 |
+
break
|
83 |
+
if not consl and not consr:
|
84 |
+
continue
|
85 |
+
if consl and consr:
|
86 |
+
litp = lerp(alpha(1.0 * (s - l + 1) / (L + 1)),
|
87 |
+
glb[s, fidx], glb[l, fidx])
|
88 |
+
ritp = lerp(alpha(1.0 * (r - s + 1) / (L + 1)),
|
89 |
+
glb[s, fidx], glb[r, fidx])
|
90 |
+
itp = lerp(alpha(1.0 * (s - l + 1) / (r - l + 1)),
|
91 |
+
ritp, litp)
|
92 |
+
glb[s, fidx] = itp.clone()
|
93 |
+
continue
|
94 |
+
if consl:
|
95 |
+
litp = lerp(alpha(1.0 * (s - l + 1) / (L + 1)),
|
96 |
+
glb[s, fidx], glb[l, fidx])
|
97 |
+
glb[s, fidx] = litp.clone()
|
98 |
+
continue
|
99 |
+
if consr:
|
100 |
+
ritp = lerp(alpha(1.0 * (r - s + 1) / (L + 1)),
|
101 |
+
glb[s, fidx], glb[r, fidx])
|
102 |
+
glb[s, fidx] = ritp.clone()
|
103 |
+
return glb
|
utils/kinematics.py
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
from utils.transforms import quat2mat, repr6d2mat, euler2mat
|
3 |
+
|
4 |
+
|
5 |
+
class ForwardKinematics:
|
6 |
+
def __init__(self, parents, offsets=None):
|
7 |
+
self.parents = parents
|
8 |
+
if offsets is not None and len(offsets.shape) == 2:
|
9 |
+
offsets = offsets.unsqueeze(0)
|
10 |
+
self.offsets = offsets
|
11 |
+
|
12 |
+
def forward(self, rots, offsets=None, global_pos=None):
|
13 |
+
"""
|
14 |
+
Forward Kinematics: returns a per-bone transformation
|
15 |
+
@param rots: local joint rotations (batch_size, bone_num, 3, 3)
|
16 |
+
@param offsets: (batch_size, bone_num, 3) or None
|
17 |
+
@param global_pos: global_position: (batch_size, 3) or keep it as in offsets (default)
|
18 |
+
@return: (batch_szie, bone_num, 3, 4)
|
19 |
+
"""
|
20 |
+
rots = rots.clone()
|
21 |
+
if offsets is None:
|
22 |
+
offsets = self.offsets.to(rots.device)
|
23 |
+
if global_pos is None:
|
24 |
+
global_pos = offsets[:, 0]
|
25 |
+
|
26 |
+
pos = torch.zeros((rots.shape[0], rots.shape[1], 3), device=rots.device)
|
27 |
+
rest_pos = torch.zeros_like(pos)
|
28 |
+
res = torch.zeros((rots.shape[0], rots.shape[1], 3, 4), device=rots.device)
|
29 |
+
|
30 |
+
pos[:, 0] = global_pos
|
31 |
+
rest_pos[:, 0] = offsets[:, 0]
|
32 |
+
|
33 |
+
for i, p in enumerate(self.parents):
|
34 |
+
if i != 0:
|
35 |
+
rots[:, i] = torch.matmul(rots[:, p], rots[:, i])
|
36 |
+
pos[:, i] = torch.matmul(rots[:, p], offsets[:, i].unsqueeze(-1)).squeeze(-1) + pos[:, p]
|
37 |
+
rest_pos[:, i] = rest_pos[:, p] + offsets[:, i]
|
38 |
+
|
39 |
+
res[:, i, :3, :3] = rots[:, i]
|
40 |
+
res[:, i, :, 3] = torch.matmul(rots[:, i], -rest_pos[:, i].unsqueeze(-1)).squeeze(-1) + pos[:, i]
|
41 |
+
|
42 |
+
return res
|
43 |
+
|
44 |
+
def accumulate(self, local_rots):
|
45 |
+
"""
|
46 |
+
Get global joint rotation from local rotations
|
47 |
+
@param local_rots: (batch_size, n_bone, 3, 3)
|
48 |
+
@return: global_rotations
|
49 |
+
"""
|
50 |
+
res = torch.empty_like(local_rots)
|
51 |
+
for i, p in enumerate(self.parents):
|
52 |
+
if i == 0:
|
53 |
+
res[:, i] = local_rots[:, i]
|
54 |
+
else:
|
55 |
+
res[:, i] = torch.matmul(res[:, p], local_rots[:, i])
|
56 |
+
return res
|
57 |
+
|
58 |
+
def unaccumulate(self, global_rots):
|
59 |
+
"""
|
60 |
+
Get local joint rotation from global rotations
|
61 |
+
@param global_rots: (batch_size, n_bone, 3, 3)
|
62 |
+
@return: local_rotations
|
63 |
+
"""
|
64 |
+
res = torch.empty_like(global_rots)
|
65 |
+
inv = torch.empty_like(global_rots)
|
66 |
+
|
67 |
+
for i, p in enumerate(self.parents):
|
68 |
+
if i == 0:
|
69 |
+
inv[:, i] = global_rots[:, i].transpose(-2, -1)
|
70 |
+
res[:, i] = global_rots[:, i]
|
71 |
+
continue
|
72 |
+
res[:, i] = torch.matmul(inv[:, p], global_rots[:, i])
|
73 |
+
inv[:, i] = torch.matmul(res[:, i].transpose(-2, -1), inv[:, p])
|
74 |
+
|
75 |
+
return res
|
76 |
+
|
77 |
+
|
78 |
+
class ForwardKinematicsJoint:
|
79 |
+
def __init__(self, parents, offset):
|
80 |
+
self.parents = parents
|
81 |
+
self.offset = offset
|
82 |
+
|
83 |
+
'''
|
84 |
+
rotation should have shape batch_size * Joint_num * (3/4) * Time
|
85 |
+
position should have shape batch_size * 3 * Time
|
86 |
+
offset should have shape batch_size * Joint_num * 3
|
87 |
+
output have shape batch_size * Time * Joint_num * 3
|
88 |
+
'''
|
89 |
+
|
90 |
+
def forward(self, rotation: torch.Tensor, position: torch.Tensor, offset=None,
|
91 |
+
world=True):
|
92 |
+
'''
|
93 |
+
if not quater and rotation.shape[-2] != 3: raise Exception('Unexpected shape of rotation')
|
94 |
+
if quater and rotation.shape[-2] != 4: raise Exception('Unexpected shape of rotation')
|
95 |
+
rotation = rotation.permute(0, 3, 1, 2)
|
96 |
+
position = position.permute(0, 2, 1)
|
97 |
+
'''
|
98 |
+
if rotation.shape[-1] == 6:
|
99 |
+
transform = repr6d2mat(rotation)
|
100 |
+
elif rotation.shape[-1] == 4:
|
101 |
+
norm = torch.norm(rotation, dim=-1, keepdim=True)
|
102 |
+
rotation = rotation / norm
|
103 |
+
transform = quat2mat(rotation)
|
104 |
+
elif rotation.shape[-1] == 3:
|
105 |
+
transform = euler2mat(rotation)
|
106 |
+
else:
|
107 |
+
raise Exception('Only accept quaternion rotation input')
|
108 |
+
result = torch.empty(transform.shape[:-2] + (3,), device=position.device)
|
109 |
+
|
110 |
+
if offset is None:
|
111 |
+
offset = self.offset
|
112 |
+
offset = offset.reshape((-1, 1, offset.shape[-2], offset.shape[-1], 1))
|
113 |
+
|
114 |
+
result[..., 0, :] = position
|
115 |
+
for i, pi in enumerate(self.parents):
|
116 |
+
if pi == -1:
|
117 |
+
assert i == 0
|
118 |
+
continue
|
119 |
+
|
120 |
+
result[..., i, :] = torch.matmul(transform[..., pi, :, :], offset[..., i, :, :]).squeeze()
|
121 |
+
transform[..., i, :, :] = torch.matmul(transform[..., pi, :, :].clone(), transform[..., i, :, :].clone())
|
122 |
+
if world: result[..., i, :] += result[..., pi, :]
|
123 |
+
return result
|
124 |
+
|
125 |
+
|
126 |
+
class InverseKinematicsJoint:
|
127 |
+
def __init__(self, rotations: torch.Tensor, positions: torch.Tensor, offset, parents, constrains):
|
128 |
+
self.rotations = rotations.detach().clone()
|
129 |
+
self.rotations.requires_grad_(True)
|
130 |
+
self.position = positions.detach().clone()
|
131 |
+
self.position.requires_grad_(True)
|
132 |
+
|
133 |
+
self.parents = parents
|
134 |
+
self.offset = offset
|
135 |
+
self.constrains = constrains
|
136 |
+
|
137 |
+
self.optimizer = torch.optim.Adam([self.position, self.rotations], lr=1e-3, betas=(0.9, 0.999))
|
138 |
+
self.criteria = torch.nn.MSELoss()
|
139 |
+
|
140 |
+
self.fk = ForwardKinematicsJoint(parents, offset)
|
141 |
+
|
142 |
+
self.glb = None
|
143 |
+
|
144 |
+
def step(self):
|
145 |
+
self.optimizer.zero_grad()
|
146 |
+
glb = self.fk.forward(self.rotations, self.position)
|
147 |
+
loss = self.criteria(glb, self.constrains)
|
148 |
+
loss.backward()
|
149 |
+
self.optimizer.step()
|
150 |
+
self.glb = glb
|
151 |
+
return loss.item()
|
152 |
+
|
153 |
+
|
154 |
+
class InverseKinematicsJoint2:
|
155 |
+
def __init__(self, rotations: torch.Tensor, positions: torch.Tensor, offset, parents, constrains, cid,
|
156 |
+
lambda_rec_rot=1., lambda_rec_pos=1., use_velo=False):
|
157 |
+
self.use_velo = use_velo
|
158 |
+
self.rotations_ori = rotations.detach().clone()
|
159 |
+
self.rotations = rotations.detach().clone()
|
160 |
+
self.rotations.requires_grad_(True)
|
161 |
+
self.position_ori = positions.detach().clone()
|
162 |
+
self.position = positions.detach().clone()
|
163 |
+
if self.use_velo:
|
164 |
+
self.position[1:] = self.position[1:] - self.position[:-1]
|
165 |
+
self.position.requires_grad_(True)
|
166 |
+
|
167 |
+
self.parents = parents
|
168 |
+
self.offset = offset
|
169 |
+
self.constrains = constrains.detach().clone()
|
170 |
+
self.cid = cid
|
171 |
+
|
172 |
+
self.lambda_rec_rot = lambda_rec_rot
|
173 |
+
self.lambda_rec_pos = lambda_rec_pos
|
174 |
+
|
175 |
+
self.optimizer = torch.optim.Adam([self.position, self.rotations], lr=1e-3, betas=(0.9, 0.999))
|
176 |
+
self.criteria = torch.nn.MSELoss()
|
177 |
+
|
178 |
+
self.fk = ForwardKinematicsJoint(parents, offset)
|
179 |
+
|
180 |
+
self.glb = None
|
181 |
+
|
182 |
+
def step(self):
|
183 |
+
self.optimizer.zero_grad()
|
184 |
+
if self.use_velo:
|
185 |
+
position = torch.cumsum(self.position, dim=0)
|
186 |
+
else:
|
187 |
+
position = self.position
|
188 |
+
glb = self.fk.forward(self.rotations, position)
|
189 |
+
self.constrain_loss = self.criteria(glb[:, self.cid], self.constrains)
|
190 |
+
self.rec_loss_rot = self.criteria(self.rotations, self.rotations_ori)
|
191 |
+
self.rec_loss_pos = self.criteria(self.position, self.position_ori)
|
192 |
+
loss = self.constrain_loss + self.rec_loss_rot * self.lambda_rec_rot + self.rec_loss_pos * self.lambda_rec_pos
|
193 |
+
loss.backward()
|
194 |
+
self.optimizer.step()
|
195 |
+
self.glb = glb
|
196 |
+
return loss.item()
|
197 |
+
|
198 |
+
def get_position(self):
|
199 |
+
if self.use_velo:
|
200 |
+
position = torch.cumsum(self.position.detach(), dim=0)
|
201 |
+
else:
|
202 |
+
position = self.position.detach()
|
203 |
+
return position
|
utils/skeleton.py
ADDED
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
import torch.nn.functional as F
|
4 |
+
import math
|
5 |
+
import numpy as np
|
6 |
+
|
7 |
+
|
8 |
+
class SkeletonConv(nn.Module):
|
9 |
+
def __init__(self, neighbour_list, in_channels, out_channels, kernel_size, joint_num, stride=1, padding=0,
|
10 |
+
bias=True, padding_mode='zeros', add_offset=False, in_offset_channel=0):
|
11 |
+
super(SkeletonConv, self).__init__()
|
12 |
+
|
13 |
+
if in_channels % joint_num != 0 or out_channels % joint_num != 0:
|
14 |
+
raise Exception('in/out channels should be divided by joint_num')
|
15 |
+
self.in_channels_per_joint = in_channels // joint_num
|
16 |
+
self.out_channels_per_joint = out_channels // joint_num
|
17 |
+
|
18 |
+
if padding_mode == 'zeros': padding_mode = 'constant'
|
19 |
+
|
20 |
+
self.expanded_neighbour_list = []
|
21 |
+
self.expanded_neighbour_list_offset = []
|
22 |
+
self.neighbour_list = neighbour_list
|
23 |
+
self.add_offset = add_offset
|
24 |
+
self.joint_num = joint_num
|
25 |
+
|
26 |
+
self.stride = stride
|
27 |
+
self.dilation = 1
|
28 |
+
self.groups = 1
|
29 |
+
self.padding = padding
|
30 |
+
self.padding_mode = padding_mode
|
31 |
+
self._padding_repeated_twice = (padding, padding)
|
32 |
+
|
33 |
+
for neighbour in neighbour_list:
|
34 |
+
expanded = []
|
35 |
+
for k in neighbour:
|
36 |
+
for i in range(self.in_channels_per_joint):
|
37 |
+
expanded.append(k * self.in_channels_per_joint + i)
|
38 |
+
self.expanded_neighbour_list.append(expanded)
|
39 |
+
|
40 |
+
if self.add_offset:
|
41 |
+
self.offset_enc = SkeletonLinear(neighbour_list, in_offset_channel * len(neighbour_list), out_channels)
|
42 |
+
|
43 |
+
for neighbour in neighbour_list:
|
44 |
+
expanded = []
|
45 |
+
for k in neighbour:
|
46 |
+
for i in range(add_offset):
|
47 |
+
expanded.append(k * in_offset_channel + i)
|
48 |
+
self.expanded_neighbour_list_offset.append(expanded)
|
49 |
+
|
50 |
+
self.weight = torch.zeros(out_channels, in_channels, kernel_size)
|
51 |
+
if bias:
|
52 |
+
self.bias = torch.zeros(out_channels)
|
53 |
+
else:
|
54 |
+
self.register_parameter('bias', None)
|
55 |
+
|
56 |
+
self.mask = torch.zeros_like(self.weight)
|
57 |
+
for i, neighbour in enumerate(self.expanded_neighbour_list):
|
58 |
+
self.mask[self.out_channels_per_joint * i: self.out_channels_per_joint * (i + 1), neighbour, ...] = 1
|
59 |
+
self.mask = nn.Parameter(self.mask, requires_grad=False)
|
60 |
+
|
61 |
+
self.description = 'SkeletonConv(in_channels_per_armature={}, out_channels_per_armature={}, kernel_size={}, ' \
|
62 |
+
'joint_num={}, stride={}, padding={}, bias={})'.format(
|
63 |
+
in_channels // joint_num, out_channels // joint_num, kernel_size, joint_num, stride, padding, bias
|
64 |
+
)
|
65 |
+
|
66 |
+
self.reset_parameters()
|
67 |
+
|
68 |
+
def reset_parameters(self):
|
69 |
+
for i, neighbour in enumerate(self.expanded_neighbour_list):
|
70 |
+
""" Use temporary variable to avoid assign to copy of slice, which might lead to un expected result """
|
71 |
+
tmp = torch.zeros_like(self.weight[self.out_channels_per_joint * i: self.out_channels_per_joint * (i + 1),
|
72 |
+
neighbour, ...])
|
73 |
+
nn.init.kaiming_uniform_(tmp, a=math.sqrt(5))
|
74 |
+
self.weight[self.out_channels_per_joint * i: self.out_channels_per_joint * (i + 1),
|
75 |
+
neighbour, ...] = tmp
|
76 |
+
if self.bias is not None:
|
77 |
+
fan_in, _ = nn.init._calculate_fan_in_and_fan_out(
|
78 |
+
self.weight[self.out_channels_per_joint * i: self.out_channels_per_joint * (i + 1), neighbour, ...])
|
79 |
+
bound = 1 / math.sqrt(fan_in)
|
80 |
+
tmp = torch.zeros_like(
|
81 |
+
self.bias[self.out_channels_per_joint * i: self.out_channels_per_joint * (i + 1)])
|
82 |
+
nn.init.uniform_(tmp, -bound, bound)
|
83 |
+
self.bias[self.out_channels_per_joint * i: self.out_channels_per_joint * (i + 1)] = tmp
|
84 |
+
|
85 |
+
self.weight = nn.Parameter(self.weight)
|
86 |
+
if self.bias is not None:
|
87 |
+
self.bias = nn.Parameter(self.bias)
|
88 |
+
|
89 |
+
def set_offset(self, offset):
|
90 |
+
if not self.add_offset: raise Exception('Wrong Combination of Parameters')
|
91 |
+
self.offset = offset.reshape(offset.shape[0], -1)
|
92 |
+
|
93 |
+
def forward(self, input):
|
94 |
+
weight_masked = self.weight * self.mask
|
95 |
+
res = F.conv1d(F.pad(input, self._padding_repeated_twice, mode=self.padding_mode),
|
96 |
+
weight_masked, self.bias, self.stride,
|
97 |
+
0, self.dilation, self.groups)
|
98 |
+
|
99 |
+
if self.add_offset:
|
100 |
+
offset_res = self.offset_enc(self.offset)
|
101 |
+
offset_res = offset_res.reshape(offset_res.shape + (1, ))
|
102 |
+
res += offset_res / 100
|
103 |
+
return res
|
104 |
+
|
105 |
+
def __repr__(self):
|
106 |
+
return self.description
|
107 |
+
|
108 |
+
|
109 |
+
class SkeletonLinear(nn.Module):
|
110 |
+
def __init__(self, neighbour_list, in_channels, out_channels, extra_dim1=False):
|
111 |
+
super(SkeletonLinear, self).__init__()
|
112 |
+
self.neighbour_list = neighbour_list
|
113 |
+
self.in_channels = in_channels
|
114 |
+
self.out_channels = out_channels
|
115 |
+
self.in_channels_per_joint = in_channels // len(neighbour_list)
|
116 |
+
self.out_channels_per_joint = out_channels // len(neighbour_list)
|
117 |
+
self.extra_dim1 = extra_dim1
|
118 |
+
self.expanded_neighbour_list = []
|
119 |
+
|
120 |
+
for neighbour in neighbour_list:
|
121 |
+
expanded = []
|
122 |
+
for k in neighbour:
|
123 |
+
for i in range(self.in_channels_per_joint):
|
124 |
+
expanded.append(k * self.in_channels_per_joint + i)
|
125 |
+
self.expanded_neighbour_list.append(expanded)
|
126 |
+
|
127 |
+
self.weight = torch.zeros(out_channels, in_channels)
|
128 |
+
self.mask = torch.zeros(out_channels, in_channels)
|
129 |
+
self.bias = nn.Parameter(torch.Tensor(out_channels))
|
130 |
+
|
131 |
+
self.reset_parameters()
|
132 |
+
|
133 |
+
def reset_parameters(self):
|
134 |
+
for i, neighbour in enumerate(self.expanded_neighbour_list):
|
135 |
+
tmp = torch.zeros_like(
|
136 |
+
self.weight[i*self.out_channels_per_joint: (i + 1)*self.out_channels_per_joint, neighbour]
|
137 |
+
)
|
138 |
+
self.mask[i*self.out_channels_per_joint: (i + 1)*self.out_channels_per_joint, neighbour] = 1
|
139 |
+
nn.init.kaiming_uniform_(tmp, a=math.sqrt(5))
|
140 |
+
self.weight[i*self.out_channels_per_joint: (i + 1)*self.out_channels_per_joint, neighbour] = tmp
|
141 |
+
|
142 |
+
fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.weight)
|
143 |
+
bound = 1 / math.sqrt(fan_in)
|
144 |
+
nn.init.uniform_(self.bias, -bound, bound)
|
145 |
+
|
146 |
+
self.weight = nn.Parameter(self.weight)
|
147 |
+
self.mask = nn.Parameter(self.mask, requires_grad=False)
|
148 |
+
|
149 |
+
def forward(self, input):
|
150 |
+
input = input.reshape(input.shape[0], -1)
|
151 |
+
weight_masked = self.weight * self.mask
|
152 |
+
res = F.linear(input, weight_masked, self.bias)
|
153 |
+
if self.extra_dim1: res = res.reshape(res.shape + (1,))
|
154 |
+
return res
|
155 |
+
|
156 |
+
|
157 |
+
class SkeletonPoolJoint(nn.Module):
|
158 |
+
def __init__(self, topology, pooling_mode, channels_per_joint, last_pool=False):
|
159 |
+
super(SkeletonPoolJoint, self).__init__()
|
160 |
+
|
161 |
+
if pooling_mode != 'mean':
|
162 |
+
raise Exception('Unimplemented pooling mode in matrix_implementation')
|
163 |
+
|
164 |
+
self.joint_num = len(topology)
|
165 |
+
self.parent = topology
|
166 |
+
self.pooling_list = []
|
167 |
+
self.pooling_mode = pooling_mode
|
168 |
+
|
169 |
+
self.pooling_map = [-1 for _ in range(len(self.parent))]
|
170 |
+
self.child = [-1 for _ in range(len(self.parent))]
|
171 |
+
children_cnt = [0 for _ in range(len(self.parent))]
|
172 |
+
for x, pa in enumerate(self.parent):
|
173 |
+
if pa < 0: continue
|
174 |
+
children_cnt[pa] += 1
|
175 |
+
self.child[pa] = x
|
176 |
+
self.pooling_map[0] = 0
|
177 |
+
for x in range(len(self.parent)):
|
178 |
+
if children_cnt[x] == 0 or (children_cnt[x] == 1 and children_cnt[self.child[x]] > 1):
|
179 |
+
while children_cnt[x] <= 1:
|
180 |
+
pa = self.parent[x]
|
181 |
+
if last_pool:
|
182 |
+
seq = [x]
|
183 |
+
while pa != -1 and children_cnt[pa] == 1:
|
184 |
+
seq = [pa] + seq
|
185 |
+
x = pa
|
186 |
+
pa = self.parent[x]
|
187 |
+
self.pooling_list.append(seq)
|
188 |
+
break
|
189 |
+
else:
|
190 |
+
if pa != -1 and children_cnt[pa] == 1:
|
191 |
+
self.pooling_list.append([pa, x])
|
192 |
+
x = self.parent[pa]
|
193 |
+
else:
|
194 |
+
self.pooling_list.append([x, ])
|
195 |
+
break
|
196 |
+
elif children_cnt[x] > 1:
|
197 |
+
self.pooling_list.append([x, ])
|
198 |
+
|
199 |
+
self.description = 'SkeletonPool(in_joint_num={}, out_joint_num={})'.format(
|
200 |
+
len(topology), len(self.pooling_list),
|
201 |
+
)
|
202 |
+
|
203 |
+
self.pooling_list.sort(key=lambda x:x[0])
|
204 |
+
for i, a in enumerate(self.pooling_list):
|
205 |
+
for j in a:
|
206 |
+
self.pooling_map[j] = i
|
207 |
+
|
208 |
+
self.output_joint_num = len(self.pooling_list)
|
209 |
+
self.new_topology = [-1 for _ in range(len(self.pooling_list))]
|
210 |
+
for i, x in enumerate(self.pooling_list):
|
211 |
+
if i < 1: continue
|
212 |
+
self.new_topology[i] = self.pooling_map[self.parent[x[0]]]
|
213 |
+
|
214 |
+
self.weight = torch.zeros(len(self.pooling_list) * channels_per_joint, self.joint_num * channels_per_joint)
|
215 |
+
|
216 |
+
for i, pair in enumerate(self.pooling_list):
|
217 |
+
for j in pair:
|
218 |
+
for c in range(channels_per_joint):
|
219 |
+
self.weight[i * channels_per_joint + c, j * channels_per_joint + c] = 1.0 / len(pair)
|
220 |
+
|
221 |
+
self.weight = nn.Parameter(self.weight, requires_grad=False)
|
222 |
+
|
223 |
+
def forward(self, input: torch.Tensor):
|
224 |
+
return torch.matmul(self.weight, input.unsqueeze(-1)).squeeze(-1)
|
225 |
+
|
226 |
+
|
227 |
+
class SkeletonPool(nn.Module):
|
228 |
+
def __init__(self, edges, pooling_mode, channels_per_edge, last_pool=False):
|
229 |
+
super(SkeletonPool, self).__init__()
|
230 |
+
|
231 |
+
if pooling_mode != 'mean':
|
232 |
+
raise Exception('Unimplemented pooling mode in matrix_implementation')
|
233 |
+
|
234 |
+
self.channels_per_edge = channels_per_edge
|
235 |
+
self.pooling_mode = pooling_mode
|
236 |
+
self.edge_num = len(edges) + 1
|
237 |
+
self.seq_list = []
|
238 |
+
self.pooling_list = []
|
239 |
+
self.new_edges = []
|
240 |
+
degree = [0] * 100
|
241 |
+
|
242 |
+
for edge in edges:
|
243 |
+
degree[edge[0]] += 1
|
244 |
+
degree[edge[1]] += 1
|
245 |
+
|
246 |
+
def find_seq(j, seq):
|
247 |
+
nonlocal self, degree, edges
|
248 |
+
|
249 |
+
if degree[j] > 2 and j != 0:
|
250 |
+
self.seq_list.append(seq)
|
251 |
+
seq = []
|
252 |
+
|
253 |
+
if degree[j] == 1:
|
254 |
+
self.seq_list.append(seq)
|
255 |
+
return
|
256 |
+
|
257 |
+
for idx, edge in enumerate(edges):
|
258 |
+
if edge[0] == j:
|
259 |
+
find_seq(edge[1], seq + [idx])
|
260 |
+
|
261 |
+
find_seq(0, [])
|
262 |
+
for seq in self.seq_list:
|
263 |
+
if last_pool:
|
264 |
+
self.pooling_list.append(seq)
|
265 |
+
continue
|
266 |
+
if len(seq) % 2 == 1:
|
267 |
+
self.pooling_list.append([seq[0]])
|
268 |
+
self.new_edges.append(edges[seq[0]])
|
269 |
+
seq = seq[1:]
|
270 |
+
for i in range(0, len(seq), 2):
|
271 |
+
self.pooling_list.append([seq[i], seq[i + 1]])
|
272 |
+
self.new_edges.append([edges[seq[i]][0], edges[seq[i + 1]][1]])
|
273 |
+
|
274 |
+
# add global position
|
275 |
+
self.pooling_list.append([self.edge_num - 1])
|
276 |
+
|
277 |
+
self.description = 'SkeletonPool(in_edge_num={}, out_edge_num={})'.format(
|
278 |
+
len(edges), len(self.pooling_list)
|
279 |
+
)
|
280 |
+
|
281 |
+
self.weight = torch.zeros(len(self.pooling_list) * channels_per_edge, self.edge_num * channels_per_edge)
|
282 |
+
|
283 |
+
for i, pair in enumerate(self.pooling_list):
|
284 |
+
for j in pair:
|
285 |
+
for c in range(channels_per_edge):
|
286 |
+
self.weight[i * channels_per_edge + c, j * channels_per_edge + c] = 1.0 / len(pair)
|
287 |
+
|
288 |
+
self.weight = nn.Parameter(self.weight, requires_grad=False)
|
289 |
+
|
290 |
+
def forward(self, input: torch.Tensor):
|
291 |
+
return torch.matmul(self.weight, input)
|
292 |
+
|
293 |
+
|
294 |
+
class SkeletonUnpool(nn.Module):
|
295 |
+
def __init__(self, pooling_list, channels_per_edge):
|
296 |
+
super(SkeletonUnpool, self).__init__()
|
297 |
+
self.pooling_list = pooling_list
|
298 |
+
self.input_joint_num = len(pooling_list)
|
299 |
+
self.output_joint_num = 0
|
300 |
+
self.channels_per_edge = channels_per_edge
|
301 |
+
for t in self.pooling_list:
|
302 |
+
self.output_joint_num += len(t)
|
303 |
+
|
304 |
+
self.description = 'SkeletonUnpool(in_joint_num={}, out_joint_num={})'.format(
|
305 |
+
self.input_joint_num, self.output_joint_num,
|
306 |
+
)
|
307 |
+
|
308 |
+
self.weight = torch.zeros(self.output_joint_num * channels_per_edge, self.input_joint_num * channels_per_edge)
|
309 |
+
|
310 |
+
for i, pair in enumerate(self.pooling_list):
|
311 |
+
for j in pair:
|
312 |
+
for c in range(channels_per_edge):
|
313 |
+
self.weight[j * channels_per_edge + c, i * channels_per_edge + c] = 1
|
314 |
+
|
315 |
+
self.weight = nn.Parameter(self.weight)
|
316 |
+
self.weight.requires_grad_(False)
|
317 |
+
|
318 |
+
def forward(self, input: torch.Tensor):
|
319 |
+
return torch.matmul(self.weight, input.unsqueeze(-1)).squeeze(-1)
|
320 |
+
|
321 |
+
|
322 |
+
def find_neighbor_joint(parents, threshold):
|
323 |
+
n_joint = len(parents)
|
324 |
+
dist_mat = np.empty((n_joint, n_joint), dtype=np.int)
|
325 |
+
dist_mat[:, :] = 100000
|
326 |
+
for i, p in enumerate(parents):
|
327 |
+
dist_mat[i, i] = 0
|
328 |
+
if i != 0:
|
329 |
+
dist_mat[i, p] = dist_mat[p, i] = 1
|
330 |
+
|
331 |
+
"""
|
332 |
+
Floyd's algorithm
|
333 |
+
"""
|
334 |
+
for k in range(n_joint):
|
335 |
+
for i in range(n_joint):
|
336 |
+
for j in range(n_joint):
|
337 |
+
dist_mat[i, j] = min(dist_mat[i, j], dist_mat[i, k] + dist_mat[k, j])
|
338 |
+
|
339 |
+
neighbor_list = []
|
340 |
+
for i in range(n_joint):
|
341 |
+
neighbor = []
|
342 |
+
for j in range(n_joint):
|
343 |
+
if dist_mat[i, j] <= threshold:
|
344 |
+
neighbor.append(j)
|
345 |
+
neighbor_list.append(neighbor)
|
346 |
+
|
347 |
+
return neighbor_list
|
utils/transforms.py
ADDED
@@ -0,0 +1,399 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import torch
|
3 |
+
|
4 |
+
|
5 |
+
def batch_mm(matrix, matrix_batch):
|
6 |
+
"""
|
7 |
+
https://github.com/pytorch/pytorch/issues/14489#issuecomment-607730242
|
8 |
+
:param matrix: Sparse or dense matrix, size (m, n).
|
9 |
+
:param matrix_batch: Batched dense matrices, size (b, n, k).
|
10 |
+
:return: The batched matrix-matrix product, size (m, n) x (b, n, k) = (b, m, k).
|
11 |
+
"""
|
12 |
+
batch_size = matrix_batch.shape[0]
|
13 |
+
# Stack the vector batch into columns. (b, n, k) -> (n, b, k) -> (n, b*k)
|
14 |
+
vectors = matrix_batch.transpose(0, 1).reshape(matrix.shape[1], -1)
|
15 |
+
|
16 |
+
# A matrix-matrix product is a batched matrix-vector product of the columns.
|
17 |
+
# And then reverse the reshaping. (m, n) x (n, b*k) = (m, b*k) -> (m, b, k) -> (b, m, k)
|
18 |
+
return matrix.mm(vectors).reshape(matrix.shape[0], batch_size, -1).transpose(1, 0)
|
19 |
+
|
20 |
+
|
21 |
+
def aa2quat(rots, form='wxyz', unified_orient=True):
|
22 |
+
"""
|
23 |
+
Convert angle-axis representation to wxyz quaternion and to the half plan (w >= 0)
|
24 |
+
@param rots: angle-axis rotations, (*, 3)
|
25 |
+
@param form: quaternion format, either 'wxyz' or 'xyzw'
|
26 |
+
@param unified_orient: Use unified orientation for quaternion (quaternion is dual cover of SO3)
|
27 |
+
:return:
|
28 |
+
"""
|
29 |
+
angles = rots.norm(dim=-1, keepdim=True)
|
30 |
+
norm = angles.clone()
|
31 |
+
norm[norm < 1e-8] = 1
|
32 |
+
axis = rots / norm
|
33 |
+
quats = torch.empty(rots.shape[:-1] + (4,), device=rots.device, dtype=rots.dtype)
|
34 |
+
angles = angles * 0.5
|
35 |
+
if form == 'wxyz':
|
36 |
+
quats[..., 0] = torch.cos(angles.squeeze(-1))
|
37 |
+
quats[..., 1:] = torch.sin(angles) * axis
|
38 |
+
elif form == 'xyzw':
|
39 |
+
quats[..., :3] = torch.sin(angles) * axis
|
40 |
+
quats[..., 3] = torch.cos(angles.squeeze(-1))
|
41 |
+
|
42 |
+
if unified_orient:
|
43 |
+
idx = quats[..., 0] < 0
|
44 |
+
quats[idx, :] *= -1
|
45 |
+
|
46 |
+
return quats
|
47 |
+
|
48 |
+
|
49 |
+
def quat2aa(quats):
|
50 |
+
"""
|
51 |
+
Convert wxyz quaternions to angle-axis representation
|
52 |
+
:param quats:
|
53 |
+
:return:
|
54 |
+
"""
|
55 |
+
_cos = quats[..., 0]
|
56 |
+
xyz = quats[..., 1:]
|
57 |
+
_sin = xyz.norm(dim=-1)
|
58 |
+
norm = _sin.clone()
|
59 |
+
norm[norm < 1e-7] = 1
|
60 |
+
axis = xyz / norm.unsqueeze(-1)
|
61 |
+
angle = torch.atan2(_sin, _cos) * 2
|
62 |
+
return axis * angle.unsqueeze(-1)
|
63 |
+
|
64 |
+
|
65 |
+
def quat2mat(quats: torch.Tensor):
|
66 |
+
"""
|
67 |
+
Convert (w, x, y, z) quaternions to 3x3 rotation matrix
|
68 |
+
:param quats: quaternions of shape (..., 4)
|
69 |
+
:return: rotation matrices of shape (..., 3, 3)
|
70 |
+
"""
|
71 |
+
qw = quats[..., 0]
|
72 |
+
qx = quats[..., 1]
|
73 |
+
qy = quats[..., 2]
|
74 |
+
qz = quats[..., 3]
|
75 |
+
|
76 |
+
x2 = qx + qx
|
77 |
+
y2 = qy + qy
|
78 |
+
z2 = qz + qz
|
79 |
+
xx = qx * x2
|
80 |
+
yy = qy * y2
|
81 |
+
wx = qw * x2
|
82 |
+
xy = qx * y2
|
83 |
+
yz = qy * z2
|
84 |
+
wy = qw * y2
|
85 |
+
xz = qx * z2
|
86 |
+
zz = qz * z2
|
87 |
+
wz = qw * z2
|
88 |
+
|
89 |
+
m = torch.empty(quats.shape[:-1] + (3, 3), device=quats.device, dtype=quats.dtype)
|
90 |
+
m[..., 0, 0] = 1.0 - (yy + zz)
|
91 |
+
m[..., 0, 1] = xy - wz
|
92 |
+
m[..., 0, 2] = xz + wy
|
93 |
+
m[..., 1, 0] = xy + wz
|
94 |
+
m[..., 1, 1] = 1.0 - (xx + zz)
|
95 |
+
m[..., 1, 2] = yz - wx
|
96 |
+
m[..., 2, 0] = xz - wy
|
97 |
+
m[..., 2, 1] = yz + wx
|
98 |
+
m[..., 2, 2] = 1.0 - (xx + yy)
|
99 |
+
|
100 |
+
return m
|
101 |
+
|
102 |
+
|
103 |
+
def quat2euler(q, order='xyz', degrees=True):
|
104 |
+
"""
|
105 |
+
Convert (w, x, y, z) quaternions to xyz euler angles. This is used for bvh output.
|
106 |
+
"""
|
107 |
+
q0 = q[..., 0]
|
108 |
+
q1 = q[..., 1]
|
109 |
+
q2 = q[..., 2]
|
110 |
+
q3 = q[..., 3]
|
111 |
+
es = torch.empty(q0.shape + (3,), device=q.device, dtype=q.dtype)
|
112 |
+
|
113 |
+
if order == 'xyz':
|
114 |
+
es[..., 2] = torch.atan2(2 * (q0 * q3 - q1 * q2), q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3)
|
115 |
+
es[..., 1] = torch.asin((2 * (q1 * q3 + q0 * q2)).clip(-1, 1))
|
116 |
+
es[..., 0] = torch.atan2(2 * (q0 * q1 - q2 * q3), q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3)
|
117 |
+
else:
|
118 |
+
raise NotImplementedError('Cannot convert to ordering %s' % order)
|
119 |
+
|
120 |
+
if degrees:
|
121 |
+
es = es * 180 / np.pi
|
122 |
+
|
123 |
+
return es
|
124 |
+
|
125 |
+
|
126 |
+
def euler2mat(rots, order='xyz'):
|
127 |
+
axis = {'x': torch.tensor((1, 0, 0), device=rots.device),
|
128 |
+
'y': torch.tensor((0, 1, 0), device=rots.device),
|
129 |
+
'z': torch.tensor((0, 0, 1), device=rots.device)}
|
130 |
+
|
131 |
+
rots = rots / 180 * np.pi
|
132 |
+
mats = []
|
133 |
+
for i in range(3):
|
134 |
+
aa = axis[order[i]] * rots[..., i].unsqueeze(-1)
|
135 |
+
mats.append(aa2mat(aa))
|
136 |
+
return mats[0] @ (mats[1] @ mats[2])
|
137 |
+
|
138 |
+
|
139 |
+
def aa2mat(rots):
|
140 |
+
"""
|
141 |
+
Convert angle-axis representation to rotation matrix
|
142 |
+
:param rots: angle-axis representation
|
143 |
+
:return:
|
144 |
+
"""
|
145 |
+
quat = aa2quat(rots)
|
146 |
+
mat = quat2mat(quat)
|
147 |
+
return mat
|
148 |
+
|
149 |
+
|
150 |
+
def mat2quat(R) -> torch.Tensor:
|
151 |
+
'''
|
152 |
+
https://github.com/duolu/pyrotation/blob/master/pyrotation/pyrotation.py
|
153 |
+
Convert a rotation matrix to a unit quaternion.
|
154 |
+
|
155 |
+
This uses the Shepperd’s method for numerical stability.
|
156 |
+
'''
|
157 |
+
|
158 |
+
# The rotation matrix must be orthonormal
|
159 |
+
|
160 |
+
w2 = (1 + R[..., 0, 0] + R[..., 1, 1] + R[..., 2, 2])
|
161 |
+
x2 = (1 + R[..., 0, 0] - R[..., 1, 1] - R[..., 2, 2])
|
162 |
+
y2 = (1 - R[..., 0, 0] + R[..., 1, 1] - R[..., 2, 2])
|
163 |
+
z2 = (1 - R[..., 0, 0] - R[..., 1, 1] + R[..., 2, 2])
|
164 |
+
|
165 |
+
yz = (R[..., 1, 2] + R[..., 2, 1])
|
166 |
+
xz = (R[..., 2, 0] + R[..., 0, 2])
|
167 |
+
xy = (R[..., 0, 1] + R[..., 1, 0])
|
168 |
+
|
169 |
+
wx = (R[..., 2, 1] - R[..., 1, 2])
|
170 |
+
wy = (R[..., 0, 2] - R[..., 2, 0])
|
171 |
+
wz = (R[..., 1, 0] - R[..., 0, 1])
|
172 |
+
|
173 |
+
w = torch.empty_like(x2)
|
174 |
+
x = torch.empty_like(x2)
|
175 |
+
y = torch.empty_like(x2)
|
176 |
+
z = torch.empty_like(x2)
|
177 |
+
|
178 |
+
flagA = (R[..., 2, 2] < 0) * (R[..., 0, 0] > R[..., 1, 1])
|
179 |
+
flagB = (R[..., 2, 2] < 0) * (R[..., 0, 0] <= R[..., 1, 1])
|
180 |
+
flagC = (R[..., 2, 2] >= 0) * (R[..., 0, 0] < -R[..., 1, 1])
|
181 |
+
flagD = (R[..., 2, 2] >= 0) * (R[..., 0, 0] >= -R[..., 1, 1])
|
182 |
+
|
183 |
+
x[flagA] = torch.sqrt(x2[flagA])
|
184 |
+
w[flagA] = wx[flagA] / x[flagA]
|
185 |
+
y[flagA] = xy[flagA] / x[flagA]
|
186 |
+
z[flagA] = xz[flagA] / x[flagA]
|
187 |
+
|
188 |
+
y[flagB] = torch.sqrt(y2[flagB])
|
189 |
+
w[flagB] = wy[flagB] / y[flagB]
|
190 |
+
x[flagB] = xy[flagB] / y[flagB]
|
191 |
+
z[flagB] = yz[flagB] / y[flagB]
|
192 |
+
|
193 |
+
z[flagC] = torch.sqrt(z2[flagC])
|
194 |
+
w[flagC] = wz[flagC] / z[flagC]
|
195 |
+
x[flagC] = xz[flagC] / z[flagC]
|
196 |
+
y[flagC] = yz[flagC] / z[flagC]
|
197 |
+
|
198 |
+
w[flagD] = torch.sqrt(w2[flagD])
|
199 |
+
x[flagD] = wx[flagD] / w[flagD]
|
200 |
+
y[flagD] = wy[flagD] / w[flagD]
|
201 |
+
z[flagD] = wz[flagD] / w[flagD]
|
202 |
+
|
203 |
+
# if R[..., 2, 2] < 0:
|
204 |
+
#
|
205 |
+
# if R[..., 0, 0] > R[..., 1, 1]:
|
206 |
+
#
|
207 |
+
# x = torch.sqrt(x2)
|
208 |
+
# w = wx / x
|
209 |
+
# y = xy / x
|
210 |
+
# z = xz / x
|
211 |
+
#
|
212 |
+
# else:
|
213 |
+
#
|
214 |
+
# y = torch.sqrt(y2)
|
215 |
+
# w = wy / y
|
216 |
+
# x = xy / y
|
217 |
+
# z = yz / y
|
218 |
+
#
|
219 |
+
# else:
|
220 |
+
#
|
221 |
+
# if R[..., 0, 0] < -R[..., 1, 1]:
|
222 |
+
#
|
223 |
+
# z = torch.sqrt(z2)
|
224 |
+
# w = wz / z
|
225 |
+
# x = xz / z
|
226 |
+
# y = yz / z
|
227 |
+
#
|
228 |
+
# else:
|
229 |
+
#
|
230 |
+
# w = torch.sqrt(w2)
|
231 |
+
# x = wx / w
|
232 |
+
# y = wy / w
|
233 |
+
# z = wz / w
|
234 |
+
|
235 |
+
res = [w, x, y, z]
|
236 |
+
res = [z.unsqueeze(-1) for z in res]
|
237 |
+
|
238 |
+
return torch.cat(res, dim=-1) / 2
|
239 |
+
|
240 |
+
|
241 |
+
def quat2repr6d(quat):
|
242 |
+
mat = quat2mat(quat)
|
243 |
+
res = mat[..., :2, :]
|
244 |
+
res = res.reshape(res.shape[:-2] + (6, ))
|
245 |
+
return res
|
246 |
+
|
247 |
+
|
248 |
+
def repr6d2mat(repr):
|
249 |
+
x = repr[..., :3]
|
250 |
+
y = repr[..., 3:]
|
251 |
+
x = x / x.norm(dim=-1, keepdim=True)
|
252 |
+
z = torch.cross(x, y)
|
253 |
+
z = z / z.norm(dim=-1, keepdim=True)
|
254 |
+
y = torch.cross(z, x)
|
255 |
+
res = [x, y, z]
|
256 |
+
res = [v.unsqueeze(-2) for v in res]
|
257 |
+
mat = torch.cat(res, dim=-2)
|
258 |
+
return mat
|
259 |
+
|
260 |
+
|
261 |
+
def repr6d2quat(repr) -> torch.Tensor:
|
262 |
+
x = repr[..., :3]
|
263 |
+
y = repr[..., 3:]
|
264 |
+
x = x / x.norm(dim=-1, keepdim=True)
|
265 |
+
z = torch.cross(x, y)
|
266 |
+
z = z / z.norm(dim=-1, keepdim=True)
|
267 |
+
y = torch.cross(z, x)
|
268 |
+
res = [x, y, z]
|
269 |
+
res = [v.unsqueeze(-2) for v in res]
|
270 |
+
mat = torch.cat(res, dim=-2)
|
271 |
+
return mat2quat(mat)
|
272 |
+
|
273 |
+
|
274 |
+
def inv_affine(mat):
|
275 |
+
"""
|
276 |
+
Calculate the inverse of any affine transformation
|
277 |
+
"""
|
278 |
+
affine = torch.zeros((mat.shape[:2] + (1, 4)))
|
279 |
+
affine[..., 3] = 1
|
280 |
+
vert_mat = torch.cat((mat, affine), dim=2)
|
281 |
+
vert_mat_inv = torch.inverse(vert_mat)
|
282 |
+
return vert_mat_inv[..., :3, :]
|
283 |
+
|
284 |
+
|
285 |
+
def inv_rigid_affine(mat):
|
286 |
+
"""
|
287 |
+
Calculate the inverse of a rigid affine transformation
|
288 |
+
"""
|
289 |
+
res = mat.clone()
|
290 |
+
res[..., :3] = mat[..., :3].transpose(-2, -1)
|
291 |
+
res[..., 3] = -torch.matmul(res[..., :3], mat[..., 3].unsqueeze(-1)).squeeze(-1)
|
292 |
+
return res
|
293 |
+
|
294 |
+
|
295 |
+
def generate_pose(batch_size, device, uniform=False, factor=1, root_rot=False, n_bone=None, ee=None):
|
296 |
+
if n_bone is None: n_bone = 24
|
297 |
+
if ee is not None:
|
298 |
+
if root_rot:
|
299 |
+
ee.append(0)
|
300 |
+
n_bone_ = n_bone
|
301 |
+
n_bone = len(ee)
|
302 |
+
axis = torch.randn((batch_size, n_bone, 3), device=device)
|
303 |
+
axis /= axis.norm(dim=-1, keepdim=True)
|
304 |
+
if uniform:
|
305 |
+
angle = torch.rand((batch_size, n_bone, 1), device=device) * np.pi
|
306 |
+
else:
|
307 |
+
angle = torch.randn((batch_size, n_bone, 1), device=device) * np.pi / 6 * factor
|
308 |
+
angle.clamp(-np.pi, np.pi)
|
309 |
+
poses = axis * angle
|
310 |
+
if ee is not None:
|
311 |
+
res = torch.zeros((batch_size, n_bone_, 3), device=device)
|
312 |
+
for i, id in enumerate(ee):
|
313 |
+
res[:, id] = poses[:, i]
|
314 |
+
poses = res
|
315 |
+
poses = poses.reshape(batch_size, -1)
|
316 |
+
if not root_rot:
|
317 |
+
poses[..., :3] = 0
|
318 |
+
return poses
|
319 |
+
|
320 |
+
|
321 |
+
def slerp(l, r, t, unit=True):
|
322 |
+
"""
|
323 |
+
:param l: shape = (*, n)
|
324 |
+
:param r: shape = (*, n)
|
325 |
+
:param t: shape = (*)
|
326 |
+
:param unit: If l and h are unit vectors
|
327 |
+
:return:
|
328 |
+
"""
|
329 |
+
eps = 1e-8
|
330 |
+
if not unit:
|
331 |
+
l_n = l / torch.norm(l, dim=-1, keepdim=True)
|
332 |
+
r_n = r / torch.norm(r, dim=-1, keepdim=True)
|
333 |
+
else:
|
334 |
+
l_n = l
|
335 |
+
r_n = r
|
336 |
+
omega = torch.acos((l_n * r_n).sum(dim=-1).clamp(-1, 1))
|
337 |
+
dom = torch.sin(omega)
|
338 |
+
|
339 |
+
flag = dom < eps
|
340 |
+
|
341 |
+
res = torch.empty_like(l_n)
|
342 |
+
t_t = t[flag].unsqueeze(-1)
|
343 |
+
res[flag] = (1 - t_t) * l_n[flag] + t_t * r_n[flag]
|
344 |
+
|
345 |
+
flag = ~ flag
|
346 |
+
|
347 |
+
t_t = t[flag]
|
348 |
+
d_t = dom[flag]
|
349 |
+
va = torch.sin((1 - t_t) * omega[flag]) / d_t
|
350 |
+
vb = torch.sin(t_t * omega[flag]) / d_t
|
351 |
+
res[flag] = (va.unsqueeze(-1) * l_n[flag] + vb.unsqueeze(-1) * r_n[flag])
|
352 |
+
return res
|
353 |
+
|
354 |
+
|
355 |
+
def slerp_quat(l, r, t):
|
356 |
+
"""
|
357 |
+
slerp for unit quaternions
|
358 |
+
:param l: (*, 4) unit quaternion
|
359 |
+
:param r: (*, 4) unit quaternion
|
360 |
+
:param t: (*) scalar between 0 and 1
|
361 |
+
"""
|
362 |
+
t = t.expand(l.shape[:-1])
|
363 |
+
flag = (l * r).sum(dim=-1) >= 0
|
364 |
+
res = torch.empty_like(l)
|
365 |
+
res[flag] = slerp(l[flag], r[flag], t[flag])
|
366 |
+
flag = ~ flag
|
367 |
+
res[flag] = slerp(-l[flag], r[flag], t[flag])
|
368 |
+
return res
|
369 |
+
|
370 |
+
|
371 |
+
# def slerp_6d(l, r, t):
|
372 |
+
# l_q = repr6d2quat(l)
|
373 |
+
# r_q = repr6d2quat(r)
|
374 |
+
# res_q = slerp_quat(l_q, r_q, t)
|
375 |
+
# return quat2repr6d(res_q)
|
376 |
+
|
377 |
+
|
378 |
+
def interpolate_6d(input, size):
|
379 |
+
"""
|
380 |
+
:param input: (batch_size, n_channels, length)
|
381 |
+
:param size: required output size for temporal axis
|
382 |
+
:return:
|
383 |
+
"""
|
384 |
+
batch = input.shape[0]
|
385 |
+
length = input.shape[-1]
|
386 |
+
input = input.reshape((batch, -1, 6, length))
|
387 |
+
input = input.permute(0, 1, 3, 2) # (batch_size, n_joint, length, 6)
|
388 |
+
input_q = repr6d2quat(input)
|
389 |
+
idx = torch.tensor(list(range(size)), device=input_q.device, dtype=torch.float) / size * (length - 1)
|
390 |
+
idx_l = torch.floor(idx)
|
391 |
+
t = idx - idx_l
|
392 |
+
idx_l = idx_l.long()
|
393 |
+
idx_r = idx_l + 1
|
394 |
+
t = t.reshape((1, 1, -1))
|
395 |
+
res_q = slerp_quat(input_q[..., idx_l, :], input_q[..., idx_r, :], t)
|
396 |
+
res = quat2repr6d(res_q) # shape = (batch_size, n_joint, t, 6)
|
397 |
+
res = res.permute(0, 1, 3, 2)
|
398 |
+
res = res.reshape((batch, -1, size))
|
399 |
+
return res
|