wyysf radames commited on
Commit
27763e5
·
0 Parent(s):

Duplicate from radames/GenMM-demo

Browse files

Co-authored-by: Radamés Ajna <[email protected]>

.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
+ ![](assets/screenshot.png)
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
+ ![](assets/step2.png)
35
+ ![](assets/step2.1.png)
36
+
37
+
38
+ #### step 3: get your api url.
39
+
40
+ ![](assets/step3.png)
41
+ ![](assets/step3.1.png)
42
+
43
+
44
+ #### step 4: change the api_url and have fun.
45
+
46
+ ![](assets/panel.png)
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

  • SHA256: 058a736974bad98b07cef051ad1761ec4bec2e99576ad42e83d9f64b004862d9
  • Pointer size: 127 Bytes
  • Size of remote file: 79 Bytes
GenMM_demo/assets/panel.png ADDED

Git LFS Details

  • SHA256: 32c2a6bf6a456f868f860b73526c3d6d88808f6ef39f48d6a0a73498bc2a38fb
  • Pointer size: 130 Bytes
  • Size of remote file: 48.1 kB
GenMM_demo/assets/screenshot.png ADDED

Git LFS Details

  • SHA256: ef93a275269bfa3af063dd7c3659837bbfc1b508d8a197085eddf651f706d795
  • Pointer size: 131 Bytes
  • Size of remote file: 302 kB
GenMM_demo/assets/step2.1.png ADDED

Git LFS Details

  • SHA256: 375eec709ac2c7fd3b781af258982894795553b83208a31d8442b5ee4af10799
  • Pointer size: 130 Bytes
  • Size of remote file: 63.8 kB
GenMM_demo/assets/step2.png ADDED

Git LFS Details

  • SHA256: 2c946f7107925bd5faa346e8507ca0c1ba42267414e0957f1e75ae05a2b424f5
  • Pointer size: 131 Bytes
  • Size of remote file: 105 kB
GenMM_demo/assets/step3.1.png ADDED

Git LFS Details

  • SHA256: cd330ddfc55410787e7897f938de9ea6a68b863a71180c41cc1dd3d5a863ea43
  • Pointer size: 131 Bytes
  • Size of remote file: 110 kB
GenMM_demo/assets/step3.png ADDED

Git LFS Details

  • SHA256: 579127440c60d789285be279d37c8586247a4cc51b523769d4c21a86e26948e5
  • Pointer size: 130 Bytes
  • Size of remote file: 78.7 kB
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