Spaces:
Sleeping
Sleeping
Commit
·
5ac1897
1
Parent(s):
19d6f8b
feat: CPU demo
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +17 -0
- Dockerfile +37 -0
- LICENSE +21 -0
- README.md +4 -5
- configs/.gitignore +0 -0
- configs/README.md +31 -0
- configs/_hub_/README.md +7 -0
- configs/_hub_/datasets.yaml +71 -0
- configs/_hub_/models.yaml +57 -0
- configs/base.yaml +33 -0
- configs/callback/ckpt/all-p1k.yaml +5 -0
- configs/callback/ckpt/top1-p1k.yaml +5 -0
- configs/callback/skelify-spin/i10kb1.yaml +19 -0
- configs/callback/skelify-spin/i230kb1.yaml +16 -0
- configs/callback/skelify-spin/i80.yaml +15 -0
- configs/callback/skelify-spin/read_only.yaml +18 -0
- configs/data/skel-hmr2_fashion.yaml +71 -0
- configs/data/skel-hsr_v1_4ds.yaml +84 -0
- configs/data/skel-hsr_v1_full.yaml +96 -0
- configs/exp/default.yaml +16 -0
- configs/exp/hsr/train.yaml +47 -0
- configs/exp/skelify-full.yaml +21 -0
- configs/exp/skelify-refiner.yaml +21 -0
- configs/pipeline/hmr2.yaml +43 -0
- configs/pipeline/hsr.yaml +49 -0
- configs/pipeline/skelify-full.yaml +98 -0
- configs/pipeline/skelify-refiner.yaml +37 -0
- configs/policy/README.md +3 -0
- configs/policy/default.yaml +5 -0
- data_inputs/.gitignore +2 -0
- data_inputs/body_models.tar.gz +0 -0
- data_inputs/description.md +21 -0
- data_inputs/example_imgs/ballerina.png +0 -0
- data_inputs/example_imgs/exercise.jpeg +0 -0
- data_inputs/example_imgs/jump_high.jpg +0 -0
- data_outputs/.gitignore +0 -0
- lib/__init__.py +1 -0
- lib/body_models/abstract_skeletons.py +123 -0
- lib/body_models/common.py +69 -0
- lib/body_models/moyo_smplx_wrapper.py +77 -0
- lib/body_models/skel/__init__.py +0 -0
- lib/body_models/skel/alignment/align_config.py +72 -0
- lib/body_models/skel/alignment/align_config_joint.py +72 -0
- lib/body_models/skel/alignment/aligner.py +469 -0
- lib/body_models/skel/alignment/losses.py +47 -0
- lib/body_models/skel/alignment/riggid_parts_mask.pkl +0 -0
- lib/body_models/skel/alignment/utils.py +119 -0
- lib/body_models/skel/config.py +2 -0
- lib/body_models/skel/fit_osim/Fitting_SKEL_to_osim.md +20 -0
- lib/body_models/skel/fit_osim/mappings/full_body.yaml +29 -0
.gitignore
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Caches.
|
2 |
+
.DS_Store
|
3 |
+
__pycache__
|
4 |
+
*.egg-info
|
5 |
+
|
6 |
+
# VEnv.
|
7 |
+
.hsmr_env
|
8 |
+
|
9 |
+
# Hydra dev.
|
10 |
+
outputs
|
11 |
+
|
12 |
+
# Development tools.
|
13 |
+
.vscode
|
14 |
+
pyrightconfig.json
|
15 |
+
|
16 |
+
# Logs.
|
17 |
+
gpu_monitor.log
|
Dockerfile
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
2 |
+
# you will also find guides on how best to write your Dockerfile
|
3 |
+
|
4 |
+
FROM python:3.10
|
5 |
+
|
6 |
+
WORKDIR /code
|
7 |
+
|
8 |
+
COPY ./requirements_part1.txt /code/requirements_part1.txt
|
9 |
+
COPY ./requirements_part2.txt /code/requirements_part2.txt
|
10 |
+
|
11 |
+
RUN apt-get update -y && \
|
12 |
+
apt-get upgrade -y && \
|
13 |
+
apt-get install -y libglfw3-dev && \
|
14 |
+
apt-get install -y libgles2-mesa-dev && \
|
15 |
+
apt-get install -y aria2 && \
|
16 |
+
pip install --no-cache-dir --upgrade -r /code/requirements_part1.txt && \
|
17 |
+
pip install --no-cache-dir --upgrade -r /code/requirements_part2.txt
|
18 |
+
|
19 |
+
# Set up a new user named "user" with user ID 1000
|
20 |
+
RUN useradd -m -u 1000 user
|
21 |
+
|
22 |
+
# Switch to the "user" user
|
23 |
+
USER user
|
24 |
+
|
25 |
+
|
26 |
+
# Set home to the user's home directory
|
27 |
+
ENV HOME=/home/user \
|
28 |
+
PATH=/home/user/.local/bin:$PATH
|
29 |
+
|
30 |
+
# Set the working directory to the user's home directory
|
31 |
+
WORKDIR $HOME/app
|
32 |
+
|
33 |
+
# Copy the current directory contents into the container at $HOME/app setting the owner to the user
|
34 |
+
COPY --chown=user . $HOME/app
|
35 |
+
|
36 |
+
|
37 |
+
CMD ["bash", "tools/start.sh"]
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2024 Yan XIA
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
CHANGED
@@ -1,11 +1,10 @@
|
|
1 |
---
|
2 |
title: HSMR
|
3 |
emoji: 💀
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
-
sdk:
|
7 |
-
|
8 |
-
app_file: tools/service.py
|
9 |
pinned: true
|
10 |
---
|
11 |
|
|
|
1 |
---
|
2 |
title: HSMR
|
3 |
emoji: 💀
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: pink
|
6 |
+
sdk: docker
|
7 |
+
app_port: 7860
|
|
|
8 |
pinned: true
|
9 |
---
|
10 |
|
configs/.gitignore
ADDED
File without changes
|
configs/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Instructions for the Configuration System
|
2 |
+
|
3 |
+
The configuration system I used here is based on [Hydra](https://hydra.cc/). However, I made some small 'hack' to achieve some better features, such as `_hub_`. It might be a little bit hard to understand at first, but a comprehensive guidance is provided. Check `README.md` in each directory for more details.
|
4 |
+
|
5 |
+
## Philosophy
|
6 |
+
|
7 |
+
- Easy to modify and maintain.
|
8 |
+
- Help you to code with clear structure.
|
9 |
+
- Consistency.
|
10 |
+
- Easy to trace and identify specific item.
|
11 |
+
|
12 |
+
## Some Ideas
|
13 |
+
|
14 |
+
- Less ListConfig, or ListConfig only for real list data.
|
15 |
+
- Dumped list will be unfolded, each element occupies one line, and it's annoying when presenting.
|
16 |
+
- List things are not friendly to command line arguments supports.
|
17 |
+
- For defaults list, `_self_` must be explicitly specified.
|
18 |
+
- Items before `_self_` means 'based on those items'.
|
19 |
+
- Items after `_self_` means 'import those items'.
|
20 |
+
|
21 |
+
### COMPOSITION OVER INHERITANCE
|
22 |
+
|
23 |
+
Do not use "overrides" AS MUCH AS POSSIBLE, except when the changes are really tiny. Since it's hard to identify which term is actually used without running the code. Instead, the `default.yaml` serves like a template, you are supposed to copy it and modify it to create a new configuration.
|
24 |
+
|
25 |
+
### REFERENCE OVER COPY
|
26 |
+
|
27 |
+
If you want to use one things for many times (across each experiments or across each components in one experiment), you'd better use `${...}` to reference the Hydra object. So that you only need to modify one place while ensuring the consistency. Think about where to put the source of the object, `_hub_` is recommended but may not be the best choice every time.
|
28 |
+
|
29 |
+
### PREPARE EVERYTHING YOU NEED LOCALLY
|
30 |
+
|
31 |
+
Sometimes you might want to use some configurations outside the class configuration (I mean in the coding process). In that case, I recommend you to reference these things again in local configuration package. It might be too redundant, but it will bring cleaner code.
|
configs/_hub_/README.md
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# _hub
|
2 |
+
|
3 |
+
Configs here shouldn't be used as what Hydra calls a "config group". Configs here serve as a hub for other configs to reference. Most things here usually won't be used in a certain experiment.
|
4 |
+
|
5 |
+
For example, the details of each datasets can be defined here, and than I only need to reference the `Human3.6M` dataset through `${_hub_.datasets.h36m}` in other configs.
|
6 |
+
|
7 |
+
Each `.yaml` file here will be loaded separately in `base.yaml`. Check the `base.yaml` to understand how it works.
|
configs/_hub_/datasets.yaml
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
train:
|
2 |
+
hsmr: # Fitted according to the SMPL's vertices.
|
3 |
+
# Standard 4 datasets.
|
4 |
+
mpi_inf_3dhp:
|
5 |
+
name: 'HSMR-MPI-INF-3DHP-train-pruned'
|
6 |
+
urls: ${_pm_.inputs}/hsmr_training_data/mpi_inf_3dhp-tars/{000000..000012}.tar
|
7 |
+
epoch_size: 12_000
|
8 |
+
h36m:
|
9 |
+
name: 'HSMR-H36M-train'
|
10 |
+
urls: ${_pm_.inputs}/hsmr_training_data/h36m-tars/{000000..000312}.tar
|
11 |
+
epoch_size: 314_000
|
12 |
+
mpii:
|
13 |
+
name: 'HSMR-MPII-train'
|
14 |
+
urls: ${_pm_.inputs}/hsmr_training_data/mpii-tars/{000000..000009}.tar
|
15 |
+
epoch_size: 10_000
|
16 |
+
coco14:
|
17 |
+
name: 'HSMR-COCO14-train'
|
18 |
+
urls: ${_pm_.inputs}/hsmr_training_data/coco14-tars/{000000..000017}.tar
|
19 |
+
epoch_size: 18_000
|
20 |
+
# The rest full datasets for HMR2.0.
|
21 |
+
coco14vit:
|
22 |
+
name: 'HSMR-COCO14-vit-train'
|
23 |
+
urls: ${_pm_.inputs}/hsmr_training_data/coco14vit-tars/{000000..000044}.tar
|
24 |
+
epoch_size: 45_000
|
25 |
+
aic:
|
26 |
+
name: 'HSMR-AIC-train'
|
27 |
+
urls: ${_pm_.inputs}/hsmr_training_data/aic-tars/{000000..000209}.tar
|
28 |
+
epoch_size: 210_000
|
29 |
+
ava:
|
30 |
+
name: 'HSMR-AVA-train'
|
31 |
+
urls: ${_pm_.inputs}/hsmr_training_data/ava-tars/{000000..000184}.tar
|
32 |
+
epoch_size: 185_000
|
33 |
+
insta:
|
34 |
+
name: 'HSMR-INSTA-train'
|
35 |
+
urls: ${_pm_.inputs}/hsmr_training_data/insta-tars/{000000..003657}.tar
|
36 |
+
epoch_size: 3_658_000
|
37 |
+
|
38 |
+
mocap:
|
39 |
+
bioamass_v1:
|
40 |
+
dataset_file: ${_pm_.inputs}/datasets/amass_skel/data_v1.npz
|
41 |
+
pve_threshold: 0.05
|
42 |
+
cmu_mocap:
|
43 |
+
dataset_file: ${_pm_.inputs}/hmr2_training_data/cmu_mocap.npz
|
44 |
+
|
45 |
+
eval:
|
46 |
+
h36m_val_p2:
|
47 |
+
dataset_file: ${_pm_.inputs}/hmr2_evaluation_data/h36m_val_p2.npz
|
48 |
+
img_root: ${_pm_.inputs}/datasets/h36m/images
|
49 |
+
kp_list: [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 43]
|
50 |
+
use_hips: true
|
51 |
+
|
52 |
+
3dpw_test:
|
53 |
+
dataset_file: ${_pm_.inputs}/hmr2_evaluation_data/3dpw_test.npz
|
54 |
+
img_root: ${_pm_.inputs}/datasets/3dpw
|
55 |
+
kp_list: [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 43]
|
56 |
+
use_hips: false
|
57 |
+
|
58 |
+
posetrack_val:
|
59 |
+
dataset_file: ${_pm_.inputs}/hmr2_evaluation_data/posetrack_2018_val.npz
|
60 |
+
img_root: ${_pm_.inputs}/datasets/posetrack/posetrack2018/posetrack_data/
|
61 |
+
kp_list: [0] # dummy
|
62 |
+
|
63 |
+
lsp_extended:
|
64 |
+
dataset_file: ${_pm_.inputs}/hmr2_evaluation_data/hr-lspet_train.npz
|
65 |
+
img_root: ${_pm_.inputs}/datasets/hr-lspet
|
66 |
+
kp_list: [0] # dummy
|
67 |
+
|
68 |
+
coco_val:
|
69 |
+
dataset_file: ${_pm_.inputs}/hmr2_evaluation_data/coco_val.npz
|
70 |
+
img_root: ${_pm_.inputs}/datasets/coco
|
71 |
+
kp_list: [0] # dummy
|
configs/_hub_/models.yaml
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body_models:
|
2 |
+
|
3 |
+
skel_mix_hsmr:
|
4 |
+
_target_: lib.body_models.skel_wrapper.SKELWrapper
|
5 |
+
model_path: '${_pm_.inputs}/body_models/skel'
|
6 |
+
gender: male # Use male since we don't have neutral model.
|
7 |
+
joint_regressor_extra: '${_pm_.inputs}/body_models/SMPL_to_J19.pkl'
|
8 |
+
joint_regressor_custom: '${_pm_.inputs}/body_models/J_regressor_SKIL_mix_MALE.pkl'
|
9 |
+
|
10 |
+
skel_hsmr:
|
11 |
+
_target_: lib.body_models.skel_wrapper.SKELWrapper
|
12 |
+
model_path: '${_pm_.inputs}/body_models/skel'
|
13 |
+
gender: male # Use male since we don't have neutral model.
|
14 |
+
joint_regressor_extra: '${_pm_.inputs}/body_models/SMPL_to_J19.pkl'
|
15 |
+
# joint_regressor_custom: '${_pm_.inputs}/body_models/J_regressor_SMPL_MALE.pkl'
|
16 |
+
|
17 |
+
smpl_hsmr:
|
18 |
+
_target_: lib.body_models.smpl_wrapper.SMPLWrapper
|
19 |
+
model_path: '${_pm_.inputs}/body_models/smpl'
|
20 |
+
gender: male # align with skel_hsmr
|
21 |
+
num_body_joints: 23
|
22 |
+
joint_regressor_extra: '${_pm_.inputs}/body_models/SMPL_to_J19.pkl'
|
23 |
+
|
24 |
+
smpl_hsmr_neutral:
|
25 |
+
_target_: lib.body_models.smpl_wrapper.SMPLWrapper
|
26 |
+
model_path: '${_pm_.inputs}/body_models/smpl'
|
27 |
+
gender: neutral # align with skel_hsmr
|
28 |
+
num_body_joints: 23
|
29 |
+
joint_regressor_extra: '${_pm_.inputs}/body_models/SMPL_to_J19.pkl'
|
30 |
+
|
31 |
+
backbones:
|
32 |
+
|
33 |
+
vit_b:
|
34 |
+
_target_: lib.modeling.networks.backbones.ViT
|
35 |
+
img_size: [256, 192]
|
36 |
+
patch_size: 16
|
37 |
+
embed_dim: 768
|
38 |
+
depth: 12
|
39 |
+
num_heads: 12
|
40 |
+
ratio: 1
|
41 |
+
use_checkpoint: False
|
42 |
+
mlp_ratio: 4
|
43 |
+
qkv_bias: True
|
44 |
+
drop_path_rate: 0.3
|
45 |
+
|
46 |
+
vit_h:
|
47 |
+
_target_: lib.modeling.networks.backbones.ViT
|
48 |
+
img_size: [256, 192]
|
49 |
+
patch_size: 16
|
50 |
+
embed_dim: 1280
|
51 |
+
depth: 32
|
52 |
+
num_heads: 16
|
53 |
+
ratio: 1
|
54 |
+
use_checkpoint: False
|
55 |
+
mlp_ratio: 4
|
56 |
+
qkv_bias: True
|
57 |
+
drop_path_rate: 0.55
|
configs/base.yaml
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defaults:
|
2 |
+
# Load each sub-hub.
|
3 |
+
- _hub_/datasets@_hub_.datasets
|
4 |
+
- _hub_/models@_hub_.models
|
5 |
+
|
6 |
+
# Set up the template.
|
7 |
+
- _self_
|
8 |
+
|
9 |
+
# Register some defaults.
|
10 |
+
- exp: default
|
11 |
+
- policy: default
|
12 |
+
|
13 |
+
|
14 |
+
# exp_name: !!null # Name of experiment will determine the output folder.
|
15 |
+
exp_name: '${exp_topic}-${exp_tag}'
|
16 |
+
exp_topic: !!null # Theme of the experiment.
|
17 |
+
exp_tag: 'debug' # Tag of the experiment.
|
18 |
+
|
19 |
+
|
20 |
+
output_dir: ${_pm_.root}/data_outputs/exp/${exp_name} # Output directory for the experiment.
|
21 |
+
# output_dir: ${_pm_.root}/data_outputs/exp/${now:%Y-%m-%d}/${exp_name} # Output directory for the experiment.
|
22 |
+
|
23 |
+
hydra:
|
24 |
+
run:
|
25 |
+
dir: ${output_dir}
|
26 |
+
|
27 |
+
|
28 |
+
# Information from `PathManager`, which will be automatically set by entrypoint wrapper.
|
29 |
+
# The related implementation is in `lib/utils/cfg_utils.py`:`entrypoint_with_args`.
|
30 |
+
_pm_:
|
31 |
+
root: !!null
|
32 |
+
inputs: !!null
|
33 |
+
outputs: !!null
|
configs/callback/ckpt/all-p1k.yaml
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: pytorch_lightning.callbacks.ModelCheckpoint
|
2 |
+
dirpath: '${output_dir}/checkpoints'
|
3 |
+
save_last: True
|
4 |
+
every_n_train_steps: 1000
|
5 |
+
save_top_k: -1 # save all ckpts
|
configs/callback/ckpt/top1-p1k.yaml
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: pytorch_lightning.callbacks.ModelCheckpoint
|
2 |
+
dirpath: '${output_dir}/checkpoints'
|
3 |
+
save_last: True
|
4 |
+
every_n_train_steps: 1000
|
5 |
+
save_top_k: 1 # save only the last checkpoint
|
configs/callback/skelify-spin/i10kb1.yaml
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defaults:
|
2 |
+
- _self_
|
3 |
+
# Import the SKELify-SPIN callback.
|
4 |
+
- /pipeline/skelify-refiner@skelify
|
5 |
+
|
6 |
+
_target_: lib.modeling.callbacks.SKELifySPIN
|
7 |
+
|
8 |
+
# This configuration are for frozen backbone exp with batch_size = 24000
|
9 |
+
|
10 |
+
cfg:
|
11 |
+
interval: 10
|
12 |
+
batch_size: 24000 # when greater than `interval * dataloader's batch_size`, it's equivalent to that
|
13 |
+
max_batches_per_round: 1 # only the latest k * batch_size items are SPINed to save time
|
14 |
+
# better_pgt_fn: '${_pm_.inputs}/datasets/skel_training_data/spin/better_pseudo_gt.npz'
|
15 |
+
better_pgt_fn: '${output_dir}/better_pseudo_gt.npz'
|
16 |
+
skip_warm_up_steps: 10000
|
17 |
+
# skip_warm_up_steps: 0
|
18 |
+
update_better_pgt: True
|
19 |
+
valid_betas_threshold: 2
|
configs/callback/skelify-spin/i230kb1.yaml
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defaults:
|
2 |
+
- _self_
|
3 |
+
# Import the SKELify-SPIN callback.
|
4 |
+
- /pipeline/skelify-refiner@skelify
|
5 |
+
|
6 |
+
_target_: lib.modeling.callbacks.SKELifySPIN
|
7 |
+
|
8 |
+
cfg:
|
9 |
+
interval: 230
|
10 |
+
batch_size: 18000 # when greater than `interval * dataloader's batch_size`, it's equivalent to that
|
11 |
+
max_batches_per_round: 1 # only the latest k * batch_size items are SPINed to save time
|
12 |
+
# better_pgt_fn: '${_pm_.inputs}/datasets/skel_training_data/spin/better_pseudo_gt.npz'
|
13 |
+
better_pgt_fn: '${output_dir}/better_pseudo_gt.npz'
|
14 |
+
skip_warm_up_steps: 5000
|
15 |
+
update_better_pgt: True
|
16 |
+
valid_betas_threshold: 2
|
configs/callback/skelify-spin/i80.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defaults:
|
2 |
+
- _self_
|
3 |
+
# Import the SKELify-SPIN callback.
|
4 |
+
- /pipeline/skelify-refiner@skelify
|
5 |
+
|
6 |
+
_target_: lib.modeling.callbacks.SKELifySPIN
|
7 |
+
|
8 |
+
cfg:
|
9 |
+
interval: 80
|
10 |
+
batch_size: 24000 # when greater than `interval * dataloader's batch_size`, it's equivalent to that
|
11 |
+
# better_pgt_fn: '${_pm_.inputs}/datasets/skel_training_data/spin/better_pseudo_gt.npz'
|
12 |
+
better_pgt_fn: '${output_dir}/better_pseudo_gt.npz'
|
13 |
+
skip_warm_up_steps: 5000
|
14 |
+
update_better_pgt: True
|
15 |
+
valid_betas_threshold: 2
|
configs/callback/skelify-spin/read_only.yaml
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defaults:
|
2 |
+
- _self_
|
3 |
+
# Import the SKELify-SPIN callback.
|
4 |
+
- /pipeline/skelify-refiner@skelify
|
5 |
+
|
6 |
+
_target_: lib.modeling.callbacks.SKELifySPIN
|
7 |
+
|
8 |
+
# This configuration are for frozen backbone exp with batch_size = 24000
|
9 |
+
|
10 |
+
cfg:
|
11 |
+
interval: 0
|
12 |
+
batch_size: 0 # when greater than `interval * dataloader's batch_size`, it's equivalent to that
|
13 |
+
max_batches_per_round: 0 # only the latest k * batch_size items are SPINed to save time
|
14 |
+
# better_pgt_fn: '${_pm_.inputs}/datasets/skel_training_data/spin/better_pseudo_gt.npz'
|
15 |
+
better_pgt_fn: '${output_dir}/better_pseudo_gt.npz'
|
16 |
+
skip_warm_up_steps: 0
|
17 |
+
update_better_pgt: True
|
18 |
+
valid_betas_threshold: 2
|
configs/data/skel-hmr2_fashion.yaml
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: lib.data.modules.hmr2_fashion.skel_wds.DataModule
|
2 |
+
|
3 |
+
name: HMR2_fashion_WDS
|
4 |
+
|
5 |
+
cfg:
|
6 |
+
|
7 |
+
train:
|
8 |
+
shared_ds_opt: # TODO: modify this
|
9 |
+
SUPPRESS_KP_CONF_THRESH: 0.3
|
10 |
+
FILTER_NUM_KP: 4
|
11 |
+
FILTER_NUM_KP_THRESH: 0.0
|
12 |
+
FILTER_REPROJ_THRESH: 31000
|
13 |
+
SUPPRESS_BETAS_THRESH: 3.0
|
14 |
+
SUPPRESS_BAD_POSES: False
|
15 |
+
POSES_BETAS_SIMULTANEOUS: True
|
16 |
+
FILTER_NO_POSES: False
|
17 |
+
BETAS_REG: True
|
18 |
+
|
19 |
+
datasets:
|
20 |
+
- name: 'H36M'
|
21 |
+
item: ${_hub_.datasets.train.hsmr.h36m}
|
22 |
+
weight: 0.3
|
23 |
+
- name: 'MPII'
|
24 |
+
item: ${_hub_.datasets.train.hsmr.mpii}
|
25 |
+
weight: 0.1
|
26 |
+
- name: 'COCO14'
|
27 |
+
item: ${_hub_.datasets.train.hsmr.coco14}
|
28 |
+
weight: 0.4
|
29 |
+
- name: 'MPI-INF-3DHP'
|
30 |
+
item: ${_hub_.datasets.train.hsmr.mpi_inf_3dhp}
|
31 |
+
weight: 0.2
|
32 |
+
|
33 |
+
dataloader:
|
34 |
+
drop_last: True
|
35 |
+
batch_size: 300
|
36 |
+
num_workers: 6
|
37 |
+
prefetch_factor: 2
|
38 |
+
|
39 |
+
eval:
|
40 |
+
datasets:
|
41 |
+
- name: 'LSP-EXTENDED'
|
42 |
+
item: ${_hub_.datasets.eval.lsp_extended}
|
43 |
+
- name: 'H36M-VAL-P2'
|
44 |
+
item: ${_hub_.datasets.eval.h36m_val_p2}
|
45 |
+
- name: '3DPW-TEST'
|
46 |
+
item: ${_hub_.datasets.eval.3dpw_test}
|
47 |
+
- name: 'POSETRACK-VAL'
|
48 |
+
item: ${_hub_.datasets.eval.posetrack_val}
|
49 |
+
- name: 'COCO-VAL'
|
50 |
+
item: ${_hub_.datasets.eval.coco_val}
|
51 |
+
|
52 |
+
|
53 |
+
dataloader:
|
54 |
+
shuffle: False
|
55 |
+
batch_size: 300
|
56 |
+
num_workers: 6
|
57 |
+
|
58 |
+
policy: ${policy}
|
59 |
+
|
60 |
+
# TODO: modify this
|
61 |
+
augm:
|
62 |
+
SCALE_FACTOR: 0.3
|
63 |
+
ROT_FACTOR: 30
|
64 |
+
TRANS_FACTOR: 0.02
|
65 |
+
COLOR_SCALE: 0.2
|
66 |
+
ROT_AUG_RATE: 0.6
|
67 |
+
TRANS_AUG_RATE: 0.5
|
68 |
+
DO_FLIP: True
|
69 |
+
FLIP_AUG_RATE: 0.5
|
70 |
+
EXTREME_CROP_AUG_RATE: 0.10
|
71 |
+
EXTREME_CROP_AUG_LEVEL: 1
|
configs/data/skel-hsr_v1_4ds.yaml
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: lib.data.modules.hsmr_v1.data_module.DataModule
|
2 |
+
|
3 |
+
name: SKEL_HSMR_V1
|
4 |
+
|
5 |
+
cfg:
|
6 |
+
|
7 |
+
train:
|
8 |
+
cfg:
|
9 |
+
# Loader settings.
|
10 |
+
suppress_pgt_params_pve_max_thresh: 0.06 # Mark PVE-MAX larger than this value as invalid.
|
11 |
+
suppress_kp_conf_thresh: 0.3 # Mark key-point confidence smaller than this value as invalid.
|
12 |
+
suppress_betas_thresh: 3.0 # Mark betas having components larger than this value as invalid.
|
13 |
+
poses_betas_simultaneous: True # Sync poses and betas for the small person.
|
14 |
+
filter_insufficient_kp_cnt: 4
|
15 |
+
suppress_insufficient_kp_thresh: 0.0
|
16 |
+
filter_reproj_err_thresh: 31000
|
17 |
+
regularize_invalid_betas: True
|
18 |
+
# Others.
|
19 |
+
image_augmentation: ${...image_augmentation}
|
20 |
+
policy: ${...policy}
|
21 |
+
|
22 |
+
datasets:
|
23 |
+
- name: 'H36M'
|
24 |
+
item: ${_hub_.datasets.train.hsmr.h36m}
|
25 |
+
weight: 0.3
|
26 |
+
- name: 'MPII'
|
27 |
+
item: ${_hub_.datasets.train.hsmr.mpii}
|
28 |
+
weight: 0.1
|
29 |
+
- name: 'COCO14'
|
30 |
+
item: ${_hub_.datasets.train.hsmr.coco14}
|
31 |
+
weight: 0.4
|
32 |
+
- name: 'MPI-INF-3DHP'
|
33 |
+
item: ${_hub_.datasets.train.hsmr.mpi_inf_3dhp}
|
34 |
+
weight: 0.2
|
35 |
+
|
36 |
+
dataloader:
|
37 |
+
drop_last: True
|
38 |
+
batch_size: 300
|
39 |
+
num_workers: 6
|
40 |
+
prefetch_factor: 2
|
41 |
+
|
42 |
+
# mocap:
|
43 |
+
# cfg: ${_hub_.datasets.mocap.bioamass_v1}
|
44 |
+
# dataloader:
|
45 |
+
# batch_size: 600 # num_train:2 * batch_size:300 (from HMR2.0's cfg)
|
46 |
+
# drop_last: True
|
47 |
+
# shuffle: True
|
48 |
+
# num_workers: 1
|
49 |
+
|
50 |
+
eval:
|
51 |
+
cfg:
|
52 |
+
image_augmentation: ${...image_augmentation}
|
53 |
+
policy: ${...policy}
|
54 |
+
|
55 |
+
datasets:
|
56 |
+
- name: 'LSP-EXTENDED'
|
57 |
+
item: ${_hub_.datasets.eval.lsp_extended}
|
58 |
+
- name: 'H36M-VAL-P2'
|
59 |
+
item: ${_hub_.datasets.eval.h36m_val_p2}
|
60 |
+
- name: '3DPW-TEST'
|
61 |
+
item: ${_hub_.datasets.eval.3dpw_test}
|
62 |
+
- name: 'POSETRACK-VAL'
|
63 |
+
item: ${_hub_.datasets.eval.posetrack_val}
|
64 |
+
- name: 'COCO-VAL'
|
65 |
+
item: ${_hub_.datasets.eval.coco_val}
|
66 |
+
|
67 |
+
dataloader:
|
68 |
+
shuffle: False
|
69 |
+
batch_size: 300
|
70 |
+
num_workers: 6
|
71 |
+
|
72 |
+
# Augmentation settings.
|
73 |
+
image_augmentation:
|
74 |
+
trans_factor: 0.02
|
75 |
+
bbox_scale_factor: 0.3
|
76 |
+
rot_aug_rate: 0.6
|
77 |
+
rot_factor: 30
|
78 |
+
do_flip: True
|
79 |
+
flip_aug_rate: 0.5
|
80 |
+
extreme_crop_aug_rate: 0.10
|
81 |
+
half_color_scale: 0.2
|
82 |
+
|
83 |
+
# Others.
|
84 |
+
policy: ${policy}
|
configs/data/skel-hsr_v1_full.yaml
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: lib.data.modules.hsmr_v1.data_module.DataModule
|
2 |
+
|
3 |
+
name: SKEL_HSMR_V1
|
4 |
+
|
5 |
+
cfg:
|
6 |
+
|
7 |
+
train:
|
8 |
+
cfg:
|
9 |
+
# Loader settings.
|
10 |
+
suppress_pgt_params_pve_max_thresh: 0.06 # Mark PVE-MAX larger than this value as invalid.
|
11 |
+
suppress_kp_conf_thresh: 0.3 # Mark key-point confidence smaller than this value as invalid.
|
12 |
+
suppress_betas_thresh: 3.0 # Mark betas having components larger than this value as invalid.
|
13 |
+
poses_betas_simultaneous: True # Sync poses and betas for the small person.
|
14 |
+
filter_insufficient_kp_cnt: 4
|
15 |
+
suppress_insufficient_kp_thresh: 0.0
|
16 |
+
filter_reproj_err_thresh: 31000
|
17 |
+
regularize_invalid_betas: True
|
18 |
+
# Others.
|
19 |
+
image_augmentation: ${...image_augmentation}
|
20 |
+
policy: ${...policy}
|
21 |
+
|
22 |
+
datasets:
|
23 |
+
- name: 'H36M'
|
24 |
+
item: ${_hub_.datasets.train.hsmr.h36m}
|
25 |
+
weight: 0.1
|
26 |
+
- name: 'MPII'
|
27 |
+
item: ${_hub_.datasets.train.hsmr.mpii}
|
28 |
+
weight: 0.1
|
29 |
+
- name: 'COCO14'
|
30 |
+
item: ${_hub_.datasets.train.hsmr.coco14}
|
31 |
+
weight: 0.1
|
32 |
+
- name: 'COCO14-ViTPose'
|
33 |
+
item: ${_hub_.datasets.train.hsmr.coco14vit}
|
34 |
+
weight: 0.1
|
35 |
+
- name: 'MPI-INF-3DHP'
|
36 |
+
item: ${_hub_.datasets.train.hsmr.mpi_inf_3dhp}
|
37 |
+
weight: 0.02
|
38 |
+
- name: 'AVA'
|
39 |
+
item: ${_hub_.datasets.train.hsmr.ava}
|
40 |
+
weight: 0.19
|
41 |
+
- name: 'AIC'
|
42 |
+
item: ${_hub_.datasets.train.hsmr.aic}
|
43 |
+
weight: 0.19
|
44 |
+
- name: 'INSTA'
|
45 |
+
item: ${_hub_.datasets.train.hsmr.insta}
|
46 |
+
weight: 0.2
|
47 |
+
|
48 |
+
dataloader:
|
49 |
+
drop_last: True
|
50 |
+
batch_size: 300
|
51 |
+
num_workers: 6
|
52 |
+
prefetch_factor: 2
|
53 |
+
|
54 |
+
mocap:
|
55 |
+
cfg: ${_hub_.datasets.mocap.bioamass_v1}
|
56 |
+
dataloader:
|
57 |
+
batch_size: 600 # num_train:2 * batch_size:300 (from HMR2.0's cfg)
|
58 |
+
drop_last: True
|
59 |
+
shuffle: True
|
60 |
+
num_workers: 1
|
61 |
+
|
62 |
+
eval:
|
63 |
+
cfg:
|
64 |
+
image_augmentation: ${...image_augmentation}
|
65 |
+
policy: ${...policy}
|
66 |
+
|
67 |
+
datasets:
|
68 |
+
- name: 'LSP-EXTENDED'
|
69 |
+
item: ${_hub_.datasets.eval.lsp_extended}
|
70 |
+
- name: 'H36M-VAL-P2'
|
71 |
+
item: ${_hub_.datasets.eval.h36m_val_p2}
|
72 |
+
- name: '3DPW-TEST'
|
73 |
+
item: ${_hub_.datasets.eval.3dpw_test}
|
74 |
+
- name: 'POSETRACK-VAL'
|
75 |
+
item: ${_hub_.datasets.eval.posetrack_val}
|
76 |
+
- name: 'COCO-VAL'
|
77 |
+
item: ${_hub_.datasets.eval.coco_val}
|
78 |
+
|
79 |
+
dataloader:
|
80 |
+
shuffle: False
|
81 |
+
batch_size: 300
|
82 |
+
num_workers: 6
|
83 |
+
|
84 |
+
# Augmentation settings.
|
85 |
+
image_augmentation:
|
86 |
+
trans_factor: 0.02
|
87 |
+
bbox_scale_factor: 0.3
|
88 |
+
rot_aug_rate: 0.6
|
89 |
+
rot_factor: 30
|
90 |
+
do_flip: True
|
91 |
+
flip_aug_rate: 0.5
|
92 |
+
extreme_crop_aug_rate: 0.10
|
93 |
+
half_color_scale: 0.2
|
94 |
+
|
95 |
+
# Others.
|
96 |
+
policy: ${policy}
|
configs/exp/default.yaml
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# @package _global_
|
2 |
+
defaults:
|
3 |
+
# Use absolute path, cause the root in this file is `configs/exp/` rather than `configs/`.
|
4 |
+
# - /data: ...
|
5 |
+
|
6 |
+
# Configurations in this file should have the highest priority.
|
7 |
+
- _self_
|
8 |
+
|
9 |
+
# ======= Overwrite Section =======
|
10 |
+
# (Do not use as much as possible!)
|
11 |
+
|
12 |
+
|
13 |
+
|
14 |
+
# ====== Main Section ======
|
15 |
+
|
16 |
+
exp_topic: !!null # Name of experiment will determine the output folder.
|
configs/exp/hsr/train.yaml
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# @package _global_
|
2 |
+
defaults:
|
3 |
+
- /pipeline: hsmr
|
4 |
+
- /data: skel-hsmr_v1_full
|
5 |
+
# Configurations in this file
|
6 |
+
- _self_
|
7 |
+
# Import callbacks.
|
8 |
+
- /callback/ckpt/[email protected]
|
9 |
+
# - /callback/skelify-spin/[email protected]
|
10 |
+
- /callback/skelify-spin/[email protected]
|
11 |
+
|
12 |
+
# ======= Overwrite Section =======
|
13 |
+
# (Do not use as much as possible!)
|
14 |
+
|
15 |
+
pipeline:
|
16 |
+
cfg:
|
17 |
+
backbone: ${_hub_.models.backbones.vit_h}
|
18 |
+
backbone_ckpt: '${_pm_.inputs}/backbone/vitpose_backbone.pth'
|
19 |
+
freeze_backbone: False
|
20 |
+
|
21 |
+
data:
|
22 |
+
cfg:
|
23 |
+
train:
|
24 |
+
dataloader:
|
25 |
+
batch_size: 78
|
26 |
+
|
27 |
+
|
28 |
+
# ====== Main Section ======
|
29 |
+
|
30 |
+
exp_topic: 'HSMR-train-vit_h'
|
31 |
+
|
32 |
+
enable_time_monitor: False
|
33 |
+
|
34 |
+
seed: NULL
|
35 |
+
ckpt_path: NULL
|
36 |
+
|
37 |
+
logger:
|
38 |
+
interval: 1000
|
39 |
+
interval_skelify: 10
|
40 |
+
samples_per_record: 5
|
41 |
+
|
42 |
+
task: 'fit'
|
43 |
+
pl_trainer:
|
44 |
+
devices: 8
|
45 |
+
max_epochs: 100
|
46 |
+
deterministic: false
|
47 |
+
precision: 16
|
configs/exp/skelify-full.yaml
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# @package _global_
|
2 |
+
defaults:
|
3 |
+
- /pipeline: skelify-full
|
4 |
+
- /data: skel-hsmr_v1_full
|
5 |
+
# Configurations in this file should have the highest priority.
|
6 |
+
- _self_
|
7 |
+
|
8 |
+
# ======= Overwrite Section =======
|
9 |
+
# (Do not use as much as possible!)
|
10 |
+
|
11 |
+
|
12 |
+
|
13 |
+
# ====== Main Section ======
|
14 |
+
|
15 |
+
exp_topic: 'SKELify-Full'
|
16 |
+
|
17 |
+
enable_time_monitor: True
|
18 |
+
|
19 |
+
logger:
|
20 |
+
interval: 1
|
21 |
+
samples_per_record: 8
|
configs/exp/skelify-refiner.yaml
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# @package _global_
|
2 |
+
defaults:
|
3 |
+
- /pipeline: skelify-refiner
|
4 |
+
- /data: skel-hsmr_v1_full
|
5 |
+
# Configurations in this file should have the highest priority.
|
6 |
+
- _self_
|
7 |
+
|
8 |
+
# ======= Overwrite Section =======
|
9 |
+
# (Do not use as much as possible!)
|
10 |
+
|
11 |
+
|
12 |
+
|
13 |
+
# ====== Main Section ======
|
14 |
+
|
15 |
+
exp_topic: 'SKELify-Refiner'
|
16 |
+
|
17 |
+
enable_time_monitor: True
|
18 |
+
|
19 |
+
logger:
|
20 |
+
interval: 2
|
21 |
+
samples_per_record: 8
|
configs/pipeline/hmr2.yaml
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: lib.modeling.pipelines.HMR2Pipeline
|
2 |
+
|
3 |
+
name: HMR2
|
4 |
+
|
5 |
+
cfg:
|
6 |
+
# Body Models
|
7 |
+
SMPL: ${_hub_.models.body_models.smpl_hsmr_neutral}
|
8 |
+
|
9 |
+
# Backbone and its checkpoint.
|
10 |
+
backbone: ${_hub_.models.backbones.vit_h}
|
11 |
+
backbone_ckpt: ???
|
12 |
+
|
13 |
+
# Head to get the parameters.
|
14 |
+
head:
|
15 |
+
_target_: lib.modeling.networks.heads.SMPLTransformerDecoderHead
|
16 |
+
cfg:
|
17 |
+
transformer_decoder:
|
18 |
+
depth: 6
|
19 |
+
heads: 8
|
20 |
+
mlp_dim: 1024
|
21 |
+
dim_head: 64
|
22 |
+
dropout: 0.0
|
23 |
+
emb_dropout: 0.0
|
24 |
+
norm: 'layer'
|
25 |
+
context_dim: ${....backbone.embed_dim}
|
26 |
+
|
27 |
+
optimizer:
|
28 |
+
_target_: torch.optim.AdamW
|
29 |
+
lr: 1e-5
|
30 |
+
weight_decay: 1e-4
|
31 |
+
|
32 |
+
# This may be redesigned, e.g., we can add a loss object to maintain the calculation of the loss, or a callback.
|
33 |
+
loss_weights:
|
34 |
+
kp3d: 0.05
|
35 |
+
kp2d: 0.01
|
36 |
+
poses_orient: 0.002
|
37 |
+
poses_body: 0.001
|
38 |
+
betas: 0.0005
|
39 |
+
adversarial: 0.0005
|
40 |
+
# adversarial: 0.0
|
41 |
+
|
42 |
+
policy: ${policy}
|
43 |
+
logger: ${logger}
|
configs/pipeline/hsr.yaml
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: lib.modeling.pipelines.HSMRPipeline
|
2 |
+
|
3 |
+
name: HSMR
|
4 |
+
|
5 |
+
cfg:
|
6 |
+
pd_poses_repr: 'rotation_6d' # poses representation for prediction, choices: 'euler_angle' | 'rotation_6d'
|
7 |
+
sp_poses_repr: 'rotation_matrix' # poses representation for supervision, choices: 'euler_angle' | 'rotation_matrix'
|
8 |
+
|
9 |
+
# Body Models
|
10 |
+
# SKEL: ${_hub_.models.body_models.skel_hsmr}
|
11 |
+
SKEL: ${_hub_.models.body_models.skel_mix_hsmr}
|
12 |
+
|
13 |
+
# Backbone and its checkpoint.
|
14 |
+
backbone: ???
|
15 |
+
backbone_ckpt: ???
|
16 |
+
|
17 |
+
# Head to get the parameters.
|
18 |
+
head:
|
19 |
+
_target_: lib.modeling.networks.heads.SKELTransformerDecoderHead
|
20 |
+
cfg:
|
21 |
+
pd_poses_repr: ${...pd_poses_repr}
|
22 |
+
transformer_decoder:
|
23 |
+
depth: 6
|
24 |
+
heads: 8
|
25 |
+
mlp_dim: 1024
|
26 |
+
dim_head: 64
|
27 |
+
dropout: 0.0
|
28 |
+
emb_dropout: 0.0
|
29 |
+
norm: 'layer'
|
30 |
+
context_dim: ${....backbone.embed_dim}
|
31 |
+
|
32 |
+
optimizer:
|
33 |
+
_target_: torch.optim.AdamW
|
34 |
+
lr: 1e-5
|
35 |
+
weight_decay: 1e-4
|
36 |
+
|
37 |
+
# This may be redesigned, e.g., we can add a loss object to maintain the calculation of the loss, or a callback.
|
38 |
+
loss_weights:
|
39 |
+
kp3d: 0.05
|
40 |
+
kp2d: 0.01
|
41 |
+
# prior: 0.0005
|
42 |
+
prior: 0.0
|
43 |
+
poses_orient: 0.002
|
44 |
+
poses_body: 0.001
|
45 |
+
betas: 0.0005
|
46 |
+
# adversarial: 0.0005
|
47 |
+
|
48 |
+
policy: ${policy}
|
49 |
+
logger: ${logger}
|
configs/pipeline/skelify-full.yaml
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: lib.modeling.optim.SKELify
|
2 |
+
|
3 |
+
name: SKELify
|
4 |
+
|
5 |
+
cfg:
|
6 |
+
skel_model: ${_hub_.models.body_models.skel_mix_hsmr}
|
7 |
+
|
8 |
+
_f_normalize_kp2d: True
|
9 |
+
_f_normalize_kp2d_to_mean: False
|
10 |
+
_w_angle_prior_scale: 1.7
|
11 |
+
|
12 |
+
phases:
|
13 |
+
# ================================
|
14 |
+
# ⛩️ Part 1: Camera initialization.
|
15 |
+
# --------------------------------
|
16 |
+
STAGE-camera-init:
|
17 |
+
max_loop: 30
|
18 |
+
params_keys: ['cam_t', 'poses_orient']
|
19 |
+
parts: ['torso']
|
20 |
+
optimizer: ${...optimizer}
|
21 |
+
losses:
|
22 |
+
f_normalize_kp2d: ${...._f_normalize_kp2d}
|
23 |
+
f_normalize_kp2d_to_mean: ${...._f_normalize_kp2d_to_mean}
|
24 |
+
w_depth: 100.0
|
25 |
+
w_reprojection: 1.78
|
26 |
+
# ================================
|
27 |
+
|
28 |
+
# ================================
|
29 |
+
# ⛩️ Part 2: Overall optimization.
|
30 |
+
# --------------------------------
|
31 |
+
STAGE-overall-1:
|
32 |
+
max_loop: 30
|
33 |
+
params_keys: ['cam_t', 'poses_orient', 'poses_body', 'betas']
|
34 |
+
parts: ['all']
|
35 |
+
optimizer: ${...optimizer}
|
36 |
+
losses:
|
37 |
+
f_normalize_kp2d: ${...._f_normalize_kp2d}
|
38 |
+
f_normalize_kp2d_to_mean: ${...._f_normalize_kp2d_to_mean}
|
39 |
+
w_reprojection: 1.0
|
40 |
+
w_shape_prior: 100.0
|
41 |
+
w_angle_prior: 404.0
|
42 |
+
w_angle_prior_scale: ${...._w_angle_prior_scale} # TODO: Finalize it.
|
43 |
+
# --------------------------------
|
44 |
+
STAGE-overall-2:
|
45 |
+
max_loop: 30
|
46 |
+
params_keys: ['cam_t', 'poses_orient', 'poses_body', 'betas']
|
47 |
+
optimizer: ${...optimizer}
|
48 |
+
parts: ['all']
|
49 |
+
losses:
|
50 |
+
f_normalize_kp2d: ${...._f_normalize_kp2d}
|
51 |
+
f_normalize_kp2d_to_mean: ${...._f_normalize_kp2d_to_mean}
|
52 |
+
w_reprojection: 1.0
|
53 |
+
w_shape_prior: 50.0
|
54 |
+
w_angle_prior: 404.0
|
55 |
+
w_angle_prior_scale: ${...._w_angle_prior_scale} # TODO: Finalize it.
|
56 |
+
# --------------------------------
|
57 |
+
STAGE-overall-3:
|
58 |
+
max_loop: 30
|
59 |
+
params_keys: ['cam_t', 'poses_orient', 'poses_body', 'betas']
|
60 |
+
parts: ['all']
|
61 |
+
optimizer: ${...optimizer}
|
62 |
+
losses:
|
63 |
+
f_normalize_kp2d: ${...._f_normalize_kp2d}
|
64 |
+
f_normalize_kp2d_to_mean: ${...._f_normalize_kp2d_to_mean}
|
65 |
+
w_reprojection: 1.0
|
66 |
+
w_shape_prior: 10.0
|
67 |
+
w_angle_prior: 57.4
|
68 |
+
w_angle_prior_scale: ${...._w_angle_prior_scale} # TODO: Finalize it.
|
69 |
+
# --------------------------------
|
70 |
+
STAGE-overall-4:
|
71 |
+
max_loop: 30
|
72 |
+
params_keys: ['cam_t', 'poses_orient', 'poses_body', 'betas']
|
73 |
+
parts: ['all']
|
74 |
+
optimizer: ${...optimizer}
|
75 |
+
losses:
|
76 |
+
f_normalize_kp2d: ${...._f_normalize_kp2d}
|
77 |
+
f_normalize_kp2d_to_mean: ${...._f_normalize_kp2d_to_mean}
|
78 |
+
w_reprojection: 1.0
|
79 |
+
w_shape_prior: 5.0
|
80 |
+
w_angle_prior: 4.78
|
81 |
+
w_angle_prior_scale: ${...._w_angle_prior_scale} # TODO: Finalize it.
|
82 |
+
# ================================
|
83 |
+
|
84 |
+
optimizer:
|
85 |
+
_target_: torch.optim.LBFGS
|
86 |
+
lr: 1
|
87 |
+
line_search_fn: 'strong_wolfe'
|
88 |
+
tolerance_grad: ${..early_quit_thresholds.abs}
|
89 |
+
tolerance_change: ${..early_quit_thresholds.rel}
|
90 |
+
|
91 |
+
early_quit_thresholds:
|
92 |
+
abs: 1e-9
|
93 |
+
rel: 1e-9
|
94 |
+
|
95 |
+
img_patch_size: ${policy.img_patch_size}
|
96 |
+
focal_length: ${policy.focal_length}
|
97 |
+
|
98 |
+
logger: ${logger}
|
configs/pipeline/skelify-refiner.yaml
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: lib.modeling.optim.SKELify
|
2 |
+
|
3 |
+
name: SKELify-Refiner
|
4 |
+
|
5 |
+
cfg:
|
6 |
+
skel_model: ${_hub_.models.body_models.skel_mix_hsmr}
|
7 |
+
|
8 |
+
phases:
|
9 |
+
STAGE-refine:
|
10 |
+
max_loop: 10
|
11 |
+
params_keys: ['cam_t', 'poses_orient', 'poses_body', 'betas']
|
12 |
+
optimizer: ${...optimizer}
|
13 |
+
losses:
|
14 |
+
f_normalize_kp2d: True
|
15 |
+
f_normalize_kp2d_to_mean: False
|
16 |
+
w_reprojection: 1.0
|
17 |
+
w_shape_prior: 5.0
|
18 |
+
w_angle_prior: 4.78
|
19 |
+
w_angle_prior_scale: 0.17
|
20 |
+
parts: ['all']
|
21 |
+
# ================================
|
22 |
+
|
23 |
+
optimizer:
|
24 |
+
_target_: torch.optim.LBFGS
|
25 |
+
lr: 1
|
26 |
+
line_search_fn: 'strong_wolfe'
|
27 |
+
tolerance_grad: ${..early_quit_thresholds.abs}
|
28 |
+
tolerance_change: ${..early_quit_thresholds.rel}
|
29 |
+
|
30 |
+
early_quit_thresholds:
|
31 |
+
abs: 1e-7
|
32 |
+
rel: 1e-9
|
33 |
+
|
34 |
+
img_patch_size: ${policy.img_patch_size}
|
35 |
+
focal_length: ${policy.focal_length}
|
36 |
+
|
37 |
+
logger: ${logger}
|
configs/policy/README.md
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# policy
|
2 |
+
|
3 |
+
Configs here define frequently used values that are shared across multiple sub-modules. They usually cannot belongs to any specific sub-module.
|
configs/policy/default.yaml
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
img_patch_size: 256
|
2 |
+
focal_length: 5000
|
3 |
+
img_mean: [0.485, 0.456, 0.406]
|
4 |
+
img_std: [0.229, 0.224, 0.225]
|
5 |
+
bbox_shape: null
|
data_inputs/.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
body_models
|
2 |
+
released_models
|
data_inputs/body_models.tar.gz
ADDED
File without changes
|
data_inputs/description.md
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<center><b><font size='10'>
|
2 |
+
H<font color='CAB6A5'>S</font><font color='98BACB'>M</font>R
|
3 |
+
</font></b></center>
|
4 |
+
<center><b><font size='6'>
|
5 |
+
Reconstructing <font color='98BACB'>Humans</font> with a <br> <font color='CAB6A5'>Biomechanically</font> Accurate Skeleton
|
6 |
+
</font></b></center>
|
7 |
+
<center><b>
|
8 |
+
<a href=#>Project Page</a>
|
9 |
+
|
|
10 |
+
<a href=#>GitHub Repo</a>
|
11 |
+
|
|
12 |
+
<a href=#>Paper</a>
|
13 |
+
</b></center>
|
14 |
+
|
15 |
+
> Reconstructing Humans with a Biomechanically Accurate Skeleton <br>
|
16 |
+
> [Yan Xia](https://scholar.isshikih.top),
|
17 |
+
> [Xiaowei Zhou](https://xzhou.me),
|
18 |
+
> [Etienne Vouga](https://www.cs.utexas.edu/~evouga/),
|
19 |
+
> [Qixing Huang](https://www.cs.utexas.edu/~huangqx/),
|
20 |
+
> [Georgios Pavlakos](https://geopavlakos.github.io/) <br>
|
21 |
+
> *CVPR 2025*
|
data_inputs/example_imgs/ballerina.png
ADDED
![]() |
data_inputs/example_imgs/exercise.jpeg
ADDED
![]() |
data_inputs/example_imgs/jump_high.jpg
ADDED
![]() |
data_outputs/.gitignore
ADDED
File without changes
|
lib/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
from .version import __version__
|
lib/body_models/abstract_skeletons.py
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# "Skeletons" here means virtual "bones" between joints. It is used to draw the skeleton on the image.
|
2 |
+
|
3 |
+
class Skeleton():
|
4 |
+
bones = []
|
5 |
+
bone_colors = []
|
6 |
+
chains = []
|
7 |
+
parent = []
|
8 |
+
|
9 |
+
|
10 |
+
class Skeleton_SMPL24(Skeleton):
|
11 |
+
# The joints definition are copied from
|
12 |
+
# [ROMP](https://github.com/Arthur151/ROMP/blob/4eebd3647f57d291d26423e51f0d514ff7197cb3/romp/lib/constants.py#L58).
|
13 |
+
chains = [
|
14 |
+
[0, 1, 4, 7, 10], # left leg
|
15 |
+
[0, 2, 5, 8, 11], # right leg
|
16 |
+
[0, 3, 6, 9, 12, 15], # spine & head
|
17 |
+
[12, 13, 16, 18, 20, 22], # left arm
|
18 |
+
[12, 14, 17, 19, 21, 23], # right arm
|
19 |
+
]
|
20 |
+
bones = [
|
21 |
+
[ 0, 1], [ 1, 4], [ 4, 7], [ 7, 10], # left leg
|
22 |
+
[ 0, 2], [ 2, 5], [ 5, 8], [ 8, 11], # right leg
|
23 |
+
[ 0, 3], [ 3, 6], [ 6, 9], [ 9, 12], [12, 15], # spine & head
|
24 |
+
[12, 13], [13, 16], [16, 18], [18, 20], [20, 22], # left arm
|
25 |
+
[12, 14], [14, 17], [17, 19], [19, 21], [21, 23], # right arm
|
26 |
+
]
|
27 |
+
bone_colors = [
|
28 |
+
[127, 0, 0], [148, 21, 21], [169, 41, 41], [191, 63, 63], # red
|
29 |
+
[ 0, 127, 0], [ 21, 148, 21], [ 41, 169, 41], [ 63, 191, 63], # green
|
30 |
+
[ 0, 0, 127], [ 15, 15, 143], [ 31, 31, 159], [ 47, 47, 175], [ 63, 63, 191], # blue
|
31 |
+
[ 0, 127, 127], [ 15, 143, 143], [ 31, 159, 159], [ 47, 175, 175], [ 63, 191, 191], # cyan
|
32 |
+
[127, 0, 127], [143, 15, 143], [159, 31, 159], [175, 47, 175], [191, 63, 191], # magenta
|
33 |
+
]
|
34 |
+
parent = [-1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 12, 12, 13, 14, 16, 17, 18, 19, 20, 21]
|
35 |
+
|
36 |
+
|
37 |
+
class Skeleton_SMPL22(Skeleton):
|
38 |
+
chains = [
|
39 |
+
[0, 1, 4, 7, 10], # left leg
|
40 |
+
[0, 2, 5, 8, 11], # right leg
|
41 |
+
[0, 3, 6, 9, 12, 15], # spine & head
|
42 |
+
[12, 13, 16, 18, 20], # left arm
|
43 |
+
[12, 14, 17, 19, 21], # right arm
|
44 |
+
]
|
45 |
+
bones = [
|
46 |
+
[ 0, 1], [ 1, 4], [ 4, 7], [ 7, 10], # left leg
|
47 |
+
[ 0, 2], [ 2, 5], [ 5, 8], [ 8, 11], # right leg
|
48 |
+
[ 0, 3], [ 3, 6], [ 6, 9], [ 9, 12], [12, 15], # spine & head
|
49 |
+
[12, 13], [13, 16], [16, 18], [18, 20], # left arm
|
50 |
+
[12, 14], [14, 17], [17, 19], [19, 21], # right arm
|
51 |
+
]
|
52 |
+
bone_colors = [
|
53 |
+
[127, 0, 0], [148, 21, 21], [169, 41, 41], [191, 63, 63], # red
|
54 |
+
[ 0, 127, 0], [ 21, 148, 21], [ 41, 169, 41], [ 63, 191, 63], # green
|
55 |
+
[ 0, 0, 127], [ 15, 15, 143], [ 31, 31, 159], [ 47, 47, 175], [ 63, 63, 191], # blue
|
56 |
+
[ 0, 127, 127], [ 15, 143, 143], [ 31, 159, 159], [ 47, 175, 175], # cyan
|
57 |
+
[127, 0, 127], [143, 15, 143], [159, 31, 159], [175, 47, 175], # magenta
|
58 |
+
]
|
59 |
+
parent = [-1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 12, 12, 13, 14, 16, 17, 18, 19]
|
60 |
+
|
61 |
+
|
62 |
+
class Skeleton_SKEL24(Skeleton):
|
63 |
+
chains = [
|
64 |
+
[ 0, 6, 7, 8, 9, 10], # left leg
|
65 |
+
[ 0, 1, 2, 3, 4, 5], # right leg
|
66 |
+
[ 0, 11, 12, 13], # spine & head
|
67 |
+
[12, 19, 20, 21, 22, 23], # left arm
|
68 |
+
[12, 14, 15, 16, 17, 18], # right arm
|
69 |
+
]
|
70 |
+
bones = [
|
71 |
+
[ 0, 6], [ 6, 7], [ 7, 8], [ 8, 9], [ 9, 10], # left leg
|
72 |
+
[ 0, 1], [ 1, 2], [ 2, 3], [ 3, 4], [ 4, 5], # right leg
|
73 |
+
[ 0, 11], [11, 12], [12, 13], # spine & head
|
74 |
+
[12, 19], [19, 20], [20, 21], [21, 22], [22, 23], # left arm
|
75 |
+
[12, 14], [14, 15], [15, 16], [16, 17], [17, 18], # right arm
|
76 |
+
]
|
77 |
+
bone_colors = [
|
78 |
+
[127, 0, 0], [148, 21, 21], [169, 41, 41], [191, 63, 63], [191, 63, 63], # red
|
79 |
+
[ 0, 127, 0], [ 21, 148, 21], [ 41, 169, 41], [ 63, 191, 63], [ 63, 191, 63], # green
|
80 |
+
[ 0, 0, 127], [ 31, 31, 159], [ 63, 63, 191], # blue
|
81 |
+
[ 0, 127, 127], [ 15, 143, 143], [ 31, 159, 159], [ 47, 175, 175], [ 63, 191, 191], # cyan
|
82 |
+
[127, 0, 127], [143, 15, 143], [159, 31, 159], [175, 47, 175], [191, 63, 191], # magenta
|
83 |
+
]
|
84 |
+
parent = [-1, 0, 1, 2, 3, 4, 0, 6, 7, 8, 9, 0, 11, 12, 12, 19, 20, 21, 22, 12, 14, 15, 16, 17]
|
85 |
+
|
86 |
+
|
87 |
+
class Skeleton_OpenPose25(Skeleton):
|
88 |
+
''' https://www.researchgate.net/figure/Twenty-five-keypoints-of-the-OpenPose-software-model_fig1_374116819 '''
|
89 |
+
chain = [
|
90 |
+
[ 8, 12, 13, 14, 19, 20], # left leg
|
91 |
+
[14, 21], # left heel
|
92 |
+
[ 8, 9, 10, 11, 22, 23], # right leg
|
93 |
+
[11, 24], # right heel
|
94 |
+
[ 8, 1, 0], # spine & head
|
95 |
+
[ 0, 16, 18], # left face
|
96 |
+
[ 0, 15, 17], # right face
|
97 |
+
[ 1, 5, 6, 7], # left arm
|
98 |
+
[ 1, 2, 3, 4], # right arm
|
99 |
+
]
|
100 |
+
bones = [
|
101 |
+
[ 8, 12], [12, 13], [13, 14], # left leg
|
102 |
+
[14, 19], [19, 20], [14, 21], # left foot
|
103 |
+
[ 8, 9], [ 9, 10], [10, 11], # right leg
|
104 |
+
[11, 22], [22, 23], [11, 24], # right foot
|
105 |
+
[ 8, 1], [ 1, 0], # spine & head
|
106 |
+
[ 0, 16], [16, 18], # left face
|
107 |
+
[ 0, 15], [15, 17], # right face
|
108 |
+
[ 1, 5], [ 5, 6], [ 6, 7], # left arm
|
109 |
+
[ 1, 2], [ 2, 3], [ 3, 4], # right arm
|
110 |
+
]
|
111 |
+
bone_colors = [
|
112 |
+
[ 95, 0, 255], [ 79, 0, 255], [ 83, 0, 255], # dark blue
|
113 |
+
[ 31, 0, 255], [ 15, 0, 255], [ 0, 0, 255], # dark blue
|
114 |
+
[127, 205, 255], [127, 205, 255], [ 95, 205, 255], # light blue
|
115 |
+
[ 63, 205, 255], [ 31, 205, 255], [ 0, 205, 255], # light blue
|
116 |
+
[255, 0, 0], [255, 0, 0], # red
|
117 |
+
[191, 63, 63], [191, 63, 191], # magenta
|
118 |
+
[255, 0, 127], [255, 0, 255], # purple
|
119 |
+
[127, 255, 0], [ 63, 255, 0], [ 0, 255, 0], # green
|
120 |
+
[255, 127, 0], [255, 191, 0], [255, 255, 0], # yellow
|
121 |
+
|
122 |
+
]
|
123 |
+
parent = [1, 8, 1, 2, 3, 1, 5, 6, -1, 8, 9, 10, 8, 12, 13, 0, 0, 15, 16, 14, 19, 14, 11, 22, 11]
|
lib/body_models/common.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from lib.kits.basic import *
|
2 |
+
|
3 |
+
from smplx import SMPL
|
4 |
+
|
5 |
+
from lib.platform import PM
|
6 |
+
from lib.body_models.skel_wrapper import SKELWrapper as SKEL
|
7 |
+
from lib.body_models.smpl_wrapper import SMPLWrapper
|
8 |
+
|
9 |
+
def make_SMPL(gender='neutral', device='cuda:0'):
|
10 |
+
return SMPL(
|
11 |
+
gender = gender,
|
12 |
+
model_path = PM.inputs / 'body_models' / 'smpl',
|
13 |
+
).to(device)
|
14 |
+
|
15 |
+
|
16 |
+
def make_SMPL_hmr2(gender='neutral', device='cuda:0'):
|
17 |
+
''' SKEL doesn't have neutral model, so align with SKEL, using male. '''
|
18 |
+
return SMPLWrapper(
|
19 |
+
gender = gender,
|
20 |
+
model_path = PM.inputs / 'body_models' / 'smpl',
|
21 |
+
num_body_joints = 23,
|
22 |
+
joint_regressor_extra = PM.inputs / 'body_models/SMPL_to_J19.pkl',
|
23 |
+
).to(device)
|
24 |
+
|
25 |
+
|
26 |
+
|
27 |
+
def make_SKEL(gender='male', device='cuda:0'):
|
28 |
+
''' We don't have neutral model for SKEL, so use male for now. '''
|
29 |
+
return make_SKEL_mix_joints(gender, device)
|
30 |
+
|
31 |
+
|
32 |
+
def make_SKEL_smpl_joints(gender='male', device='cuda:0'):
|
33 |
+
''' We don't have neutral model for SKEL, so use male for now. '''
|
34 |
+
return SKEL(
|
35 |
+
gender = gender,
|
36 |
+
model_path = PM.inputs / 'body_models' / 'skel',
|
37 |
+
joint_regressor_extra = PM.inputs / 'body_models' / 'SMPL_to_J19.pkl',
|
38 |
+
joint_regressor_custom = PM.inputs / 'body_models' / 'J_regressor_SMPL_MALE.pkl',
|
39 |
+
).to(device)
|
40 |
+
|
41 |
+
|
42 |
+
def make_SKEL_skel_joints(gender='male', device='cuda:0'):
|
43 |
+
''' We don't have neutral model for SKEL, so use male for now. '''
|
44 |
+
return SKEL(
|
45 |
+
gender = gender,
|
46 |
+
model_path = PM.inputs / 'body_models' / 'skel',
|
47 |
+
joint_regressor_extra = PM.inputs / 'body_models' / 'SMPL_to_J19.pkl',
|
48 |
+
).to(device)
|
49 |
+
|
50 |
+
|
51 |
+
def make_SKEL_mix_joints(gender='male', device='cuda:0'):
|
52 |
+
''' We don't have neutral model for SKEL, so use male for now. '''
|
53 |
+
return SKEL(
|
54 |
+
gender = gender,
|
55 |
+
model_path = PM.inputs / 'body_models' / 'skel',
|
56 |
+
joint_regressor_extra = PM.inputs / 'body_models' / 'SMPL_to_J19.pkl',
|
57 |
+
joint_regressor_custom = PM.inputs / 'body_models' / 'J_regressor_SKEL_mix_MALE.pkl',
|
58 |
+
).to(device)
|
59 |
+
|
60 |
+
|
61 |
+
def make_SMPLX_moyo(v_template_path:Union[str, Path], batch_size:int=1, device='cuda:0'):
|
62 |
+
from lib.body_models.moyo_smplx_wrapper import MoYoSMPLX
|
63 |
+
|
64 |
+
return MoYoSMPLX(
|
65 |
+
model_path = PM.inputs / 'body_models' / 'smplx',
|
66 |
+
v_template_path = v_template_path,
|
67 |
+
batch_size = batch_size,
|
68 |
+
device = device,
|
69 |
+
)
|
lib/body_models/moyo_smplx_wrapper.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from lib.kits.basic import *
|
2 |
+
|
3 |
+
import smplx
|
4 |
+
from psbody.mesh import Mesh
|
5 |
+
|
6 |
+
class MoYoSMPLX(smplx.SMPLX):
|
7 |
+
|
8 |
+
def __init__(
|
9 |
+
self,
|
10 |
+
model_path : Union[str, Path],
|
11 |
+
v_template_path : Union[str, Path],
|
12 |
+
batch_size = 1,
|
13 |
+
n_betas = 10,
|
14 |
+
device = 'cpu'
|
15 |
+
):
|
16 |
+
|
17 |
+
if isinstance(v_template_path, Path):
|
18 |
+
v_template_path = str(v_template_path)
|
19 |
+
|
20 |
+
# Load the `v_template`.
|
21 |
+
v_template_mesh = Mesh(filename=v_template_path)
|
22 |
+
v_template = to_tensor(v_template_mesh.v, device=device)
|
23 |
+
|
24 |
+
self.n_betas = n_betas
|
25 |
+
|
26 |
+
# Create the `body_model_params`.
|
27 |
+
body_model_params = {
|
28 |
+
'model_path' : model_path,
|
29 |
+
'gender' : 'neutral',
|
30 |
+
'v_template' : v_template.float(),
|
31 |
+
'batch_size' : batch_size,
|
32 |
+
'create_global_orient' : True,
|
33 |
+
'create_body_pose' : True,
|
34 |
+
'create_betas' : True,
|
35 |
+
'num_betas' : self.n_betas, # They actually don't use num_betas.
|
36 |
+
'create_left_hand_pose' : True,
|
37 |
+
'create_right_hand_pose' : True,
|
38 |
+
'create_expression' : True,
|
39 |
+
'create_jaw_pose' : True,
|
40 |
+
'create_leye_pose' : True,
|
41 |
+
'create_reye_pose' : True,
|
42 |
+
'create_transl' : True,
|
43 |
+
'use_pca' : False,
|
44 |
+
'flat_hand_mean' : True,
|
45 |
+
'dtype' : torch.float32,
|
46 |
+
}
|
47 |
+
|
48 |
+
super().__init__(**body_model_params)
|
49 |
+
self = self.to(device)
|
50 |
+
|
51 |
+
def forward(self, **kwargs):
|
52 |
+
''' Only all parameters are passed, the batch_size will be flexible adjusted. '''
|
53 |
+
assert 'global_orient' in kwargs, '`global_orient` is required for the forward pass.'
|
54 |
+
assert 'body_pose' in kwargs, '`body_pose` is required for the forward pass.'
|
55 |
+
B = kwargs['global_orient'].shape[0]
|
56 |
+
body_pose = kwargs['body_pose']
|
57 |
+
|
58 |
+
if 'left_hand_pose' not in kwargs:
|
59 |
+
kwargs['left_hand_pose'] = body_pose.new_zeros((B, 45))
|
60 |
+
get_logger().warning('`left_hand_pose` is not provided, but it\'s expected, set to zeros.')
|
61 |
+
if 'right_hand_pose' not in kwargs:
|
62 |
+
kwargs['right_hand_pose'] = body_pose.new_zeros((B, 45))
|
63 |
+
get_logger().warning('`left_hand_pose` is not provided, but it\'s expected, set to zeros.')
|
64 |
+
if 'transl' not in kwargs:
|
65 |
+
kwargs['transl'] = body_pose.new_zeros((B, 3))
|
66 |
+
if 'betas' not in kwargs:
|
67 |
+
kwargs['betas'] = body_pose.new_zeros((B, self.n_betas))
|
68 |
+
if 'expression' not in kwargs:
|
69 |
+
kwargs['expression'] = body_pose.new_zeros((B, 10))
|
70 |
+
if 'jaw_pose' not in kwargs:
|
71 |
+
kwargs['jaw_pose'] = body_pose.new_zeros((B, 3))
|
72 |
+
if 'leye_pose' not in kwargs:
|
73 |
+
kwargs['leye_pose'] = body_pose.new_zeros((B, 3))
|
74 |
+
if 'reye_pose' not in kwargs:
|
75 |
+
kwargs['reye_pose'] = body_pose.new_zeros((B, 3))
|
76 |
+
|
77 |
+
return super().forward(**kwargs)
|
lib/body_models/skel/__init__.py
ADDED
File without changes
|
lib/body_models/skel/alignment/align_config.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
""" Optimization config file. In 'optim_steps' we define each optimization steps.
|
2 |
+
Each step inherit and overwrite the parameters of the previous step."""
|
3 |
+
|
4 |
+
config = {
|
5 |
+
'keepalive_meshviewer': False,
|
6 |
+
'optim_steps':
|
7 |
+
[
|
8 |
+
{
|
9 |
+
'description' : 'Adjust the root orientation and translation',
|
10 |
+
'use_basic_loss': True,
|
11 |
+
'lr': 1,
|
12 |
+
'max_iter': 20,
|
13 |
+
'num_steps': 10,
|
14 |
+
'line_search_fn': 'strong_wolfe', #'strong_wolfe',
|
15 |
+
'tolerance_change': 1e-7,# 1e-4, #0.01
|
16 |
+
'mode' : 'root_only',
|
17 |
+
|
18 |
+
'l_verts_loose': 300,
|
19 |
+
'l_time_loss': 0,#5e2,
|
20 |
+
|
21 |
+
'l_joint': 0.0,
|
22 |
+
'l_verts': 0,
|
23 |
+
'l_scapula_loss': 0.0,
|
24 |
+
'l_spine_loss': 0.0,
|
25 |
+
'l_pose_loss': 0.0,
|
26 |
+
|
27 |
+
|
28 |
+
},
|
29 |
+
# Adjust the upper limbs
|
30 |
+
{
|
31 |
+
'description' : 'Adjust the upper limbs pose',
|
32 |
+
'lr': 0.1,
|
33 |
+
'max_iter': 20,
|
34 |
+
'num_steps': 10,
|
35 |
+
'tolerance_change': 1e-7,
|
36 |
+
'mode' : 'fixed_upper_limbs', #'fixed_root',
|
37 |
+
|
38 |
+
'l_verts_loose': 600,
|
39 |
+
'l_joint': 1e3,
|
40 |
+
'l_time_loss': 0,# 5e2,
|
41 |
+
'l_pose_loss': 1e-4,
|
42 |
+
},
|
43 |
+
# Adjust the whole body
|
44 |
+
{
|
45 |
+
'description' : 'Adjust the whole body pose with fixed root',
|
46 |
+
'lr': 0.1,
|
47 |
+
'max_iter': 20,
|
48 |
+
'num_steps': 10,
|
49 |
+
'tolerance_change': 1e-7,
|
50 |
+
'mode' : 'fixed_root', #'fixed_root',
|
51 |
+
|
52 |
+
'l_verts_loose': 600,
|
53 |
+
'l_joint': 1e3,
|
54 |
+
'l_time_loss': 0,
|
55 |
+
'l_pose_loss': 1e-4,
|
56 |
+
},
|
57 |
+
#
|
58 |
+
{
|
59 |
+
'description' : 'Free optimization',
|
60 |
+
'lr': 0.1,
|
61 |
+
'max_iter': 20,
|
62 |
+
'num_steps': 10,
|
63 |
+
'tolerance_change': 1e-7,
|
64 |
+
'mode' : 'free', #'fixed_root',
|
65 |
+
|
66 |
+
'l_verts_loose': 600,
|
67 |
+
'l_joint': 1e3,
|
68 |
+
'l_time_loss':0,
|
69 |
+
'l_pose_loss': 1e-4,
|
70 |
+
},
|
71 |
+
]
|
72 |
+
}
|
lib/body_models/skel/alignment/align_config_joint.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
""" Optimization config file. In 'optim_steps' we define each optimization steps.
|
2 |
+
Each step inherit and overwrite the parameters of the previous step."""
|
3 |
+
|
4 |
+
config = {
|
5 |
+
'keepalive_meshviewer': False,
|
6 |
+
'optim_steps':
|
7 |
+
[
|
8 |
+
{
|
9 |
+
'description' : 'Adjust the root orientation and translation',
|
10 |
+
'use_basic_loss': True,
|
11 |
+
'lr': 1,
|
12 |
+
'max_iter': 20,
|
13 |
+
'num_steps': 10,
|
14 |
+
'line_search_fn': 'strong_wolfe', #'strong_wolfe',
|
15 |
+
'tolerance_change': 1e-7,# 1e-4, #0.01
|
16 |
+
'mode' : 'root_only',
|
17 |
+
|
18 |
+
'l_verts_loose': 300,
|
19 |
+
'l_time_loss': 0,#5e2,
|
20 |
+
|
21 |
+
'l_joint': 0.0,
|
22 |
+
'l_verts': 0,
|
23 |
+
'l_scapula_loss': 0.0,
|
24 |
+
'l_spine_loss': 0.0,
|
25 |
+
'l_pose_loss': 0.0,
|
26 |
+
|
27 |
+
|
28 |
+
},
|
29 |
+
# Adjust the upper limbs
|
30 |
+
{
|
31 |
+
'description' : 'Adjust the upper limbs pose',
|
32 |
+
'lr': 0.1,
|
33 |
+
'max_iter': 20,
|
34 |
+
'num_steps': 10,
|
35 |
+
'tolerance_change': 1e-7,
|
36 |
+
'mode' : 'fixed_upper_limbs', #'fixed_root',
|
37 |
+
|
38 |
+
'l_verts_loose': 600,
|
39 |
+
'l_joint': 2e3,
|
40 |
+
'l_time_loss': 0,# 5e2,
|
41 |
+
'l_pose_loss': 1e-3,
|
42 |
+
},
|
43 |
+
# Adjust the whole body
|
44 |
+
{
|
45 |
+
'description' : 'Adjust the whole body pose with fixed root',
|
46 |
+
'lr': 0.1,
|
47 |
+
'max_iter': 20,
|
48 |
+
'num_steps': 10,
|
49 |
+
'tolerance_change': 1e-7,
|
50 |
+
'mode' : 'fixed_root', #'fixed_root',
|
51 |
+
|
52 |
+
'l_verts_loose': 600,
|
53 |
+
'l_joint': 1e3,
|
54 |
+
'l_time_loss': 0,
|
55 |
+
'l_pose_loss': 1e-4,
|
56 |
+
},
|
57 |
+
#
|
58 |
+
{
|
59 |
+
'description' : 'Free optimization',
|
60 |
+
'lr': 0.1,
|
61 |
+
'max_iter': 20,
|
62 |
+
'num_steps': 10,
|
63 |
+
'tolerance_change': 1e-7,
|
64 |
+
'mode' : 'free', #'fixed_root',
|
65 |
+
|
66 |
+
'l_verts_loose': 300,
|
67 |
+
'l_joint': 2e3,
|
68 |
+
'l_time_loss':0,
|
69 |
+
'l_pose_loss': 1e-4,
|
70 |
+
},
|
71 |
+
]
|
72 |
+
}
|
lib/body_models/skel/alignment/aligner.py
ADDED
@@ -0,0 +1,469 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
"""
|
3 |
+
Copyright©2023 Max-Planck-Gesellschaft zur Förderung
|
4 |
+
der Wissenschaften e.V. (MPG). acting on behalf of its Max Planck Institute
|
5 |
+
for Intelligent Systems. All rights reserved.
|
6 |
+
|
7 |
+
Author: Soyong Shin, Marilyn Keller
|
8 |
+
See https://skel.is.tue.mpg.de/license.html for licensing and contact information.
|
9 |
+
"""
|
10 |
+
|
11 |
+
import traceback
|
12 |
+
import math
|
13 |
+
import os
|
14 |
+
import pickle
|
15 |
+
import torch
|
16 |
+
import smplx
|
17 |
+
import omegaconf
|
18 |
+
import torch.nn.functional as F
|
19 |
+
from psbody.mesh import Mesh, MeshViewer, MeshViewers
|
20 |
+
from tqdm import trange
|
21 |
+
from pathlib import Path
|
22 |
+
|
23 |
+
import lib.body_models.skel.config as cg
|
24 |
+
from lib.body_models.skel.skel_model import SKEL
|
25 |
+
from .losses import compute_anchor_pose, compute_anchor_trans, compute_pose_loss, compute_scapula_loss, compute_spine_loss, compute_time_loss, pretty_loss_print
|
26 |
+
from .utils import location_to_spheres, to_numpy, to_params, to_torch
|
27 |
+
from .align_config import config
|
28 |
+
from .align_config_joint import config as config_joint
|
29 |
+
|
30 |
+
class SkelFitter(object):
|
31 |
+
|
32 |
+
def __init__(self, gender, device, num_betas=10, export_meshes=False, joint_optim=False) -> None:
|
33 |
+
|
34 |
+
self.smpl = smplx.create(cg.smpl_folder, model_type='smpl', gender=gender, num_betas=num_betas, batch_size=1, export_meshes=False).to(device)
|
35 |
+
self.skel = SKEL(gender).to(device)
|
36 |
+
self.gender = gender
|
37 |
+
self.device = device
|
38 |
+
self.num_betas = num_betas
|
39 |
+
# Instanciate masks used for the vertex to vertex fitting
|
40 |
+
fitting_mask_file = Path(__file__).parent / 'riggid_parts_mask.pkl'
|
41 |
+
fitting_indices = pickle.load(open(fitting_mask_file, 'rb'))
|
42 |
+
fitting_mask = torch.zeros(6890, dtype=torch.bool, device=self.device)
|
43 |
+
fitting_mask[fitting_indices] = 1
|
44 |
+
self.fitting_mask = fitting_mask.reshape(1, -1, 1).to(self.device) # 1xVx1 to be applied to verts that are BxVx3
|
45 |
+
|
46 |
+
smpl_torso_joints = [0,3]
|
47 |
+
verts_mask = (self.smpl.lbs_weights[:,smpl_torso_joints]>0.5).sum(dim=-1)>0
|
48 |
+
self.torso_verts_mask = verts_mask.unsqueeze(0).unsqueeze(-1) # Because verts are of shape BxVx3
|
49 |
+
|
50 |
+
self.export_meshes = export_meshes
|
51 |
+
|
52 |
+
|
53 |
+
# make the cfg being an object using omegaconf
|
54 |
+
if joint_optim:
|
55 |
+
self.cfg = omegaconf.OmegaConf.create(config_joint)
|
56 |
+
else:
|
57 |
+
self.cfg = omegaconf.OmegaConf.create(config)
|
58 |
+
|
59 |
+
# Instanciate the mesh viewer to visualize the fitting
|
60 |
+
if('DISABLE_VIEWER' in os.environ):
|
61 |
+
self.mv = None
|
62 |
+
print("\n DISABLE_VIEWER flag is set, running in headless mode")
|
63 |
+
else:
|
64 |
+
self.mv = MeshViewers((1,2), keepalive=self.cfg.keepalive_meshviewer)
|
65 |
+
|
66 |
+
|
67 |
+
def run_fit(self,
|
68 |
+
trans_in,
|
69 |
+
betas_in,
|
70 |
+
poses_in,
|
71 |
+
batch_size=20,
|
72 |
+
skel_data_init=None,
|
73 |
+
force_recompute=False,
|
74 |
+
debug=False,
|
75 |
+
watch_frame=0,
|
76 |
+
freevert_mesh=None,
|
77 |
+
opt_sequence=False,
|
78 |
+
fix_poses=False,
|
79 |
+
variant_exp=''):
|
80 |
+
"""Align SKEL to a SMPL sequence."""
|
81 |
+
|
82 |
+
self.nb_frames = poses_in.shape[0]
|
83 |
+
self.watch_frame = watch_frame
|
84 |
+
self.is_skel_data_init = skel_data_init is not None
|
85 |
+
self.force_recompute = force_recompute
|
86 |
+
|
87 |
+
print('Fitting {} frames'.format(self.nb_frames))
|
88 |
+
print('Watching frame: {}'.format(watch_frame))
|
89 |
+
|
90 |
+
# Initialize SKEL torch params
|
91 |
+
body_params = self._init_params(betas_in, poses_in, trans_in, skel_data_init, variant_exp)
|
92 |
+
|
93 |
+
# We cut the whole sequence in batches for parallel optimization
|
94 |
+
if batch_size > self.nb_frames:
|
95 |
+
batch_size = self.nb_frames
|
96 |
+
print('Batch size is larger than the number of frames. Setting batch size to {}'.format(batch_size))
|
97 |
+
|
98 |
+
n_batch = math.ceil(self.nb_frames/batch_size)
|
99 |
+
pbar = trange(n_batch, desc='Running batch optimization')
|
100 |
+
|
101 |
+
# Initialize the res dict to store the per frame result skel parameters
|
102 |
+
out_keys = ['poses', 'betas', 'trans']
|
103 |
+
if self.export_meshes:
|
104 |
+
out_keys += ['skel_v', 'skin_v', 'smpl_v']
|
105 |
+
res_dict = {key: [] for key in out_keys}
|
106 |
+
|
107 |
+
res_dict['gender'] = self.gender
|
108 |
+
if self.export_meshes:
|
109 |
+
res_dict['skel_f'] = self.skel.skel_f.cpu().numpy().copy()
|
110 |
+
res_dict['skin_f'] = self.skel.skin_f.cpu().numpy().copy()
|
111 |
+
res_dict['smpl_f'] = self.smpl.faces
|
112 |
+
|
113 |
+
# Iterate over the batches to fit the whole sequence
|
114 |
+
for i in pbar:
|
115 |
+
|
116 |
+
if debug:
|
117 |
+
# Only run the first batch to test, ignore the rest
|
118 |
+
if i > 1:
|
119 |
+
continue
|
120 |
+
|
121 |
+
# Get batch start and end indices
|
122 |
+
i_start = i * batch_size
|
123 |
+
i_end = min((i+1) * batch_size, self.nb_frames)
|
124 |
+
|
125 |
+
# Fit the batch
|
126 |
+
betas, poses, trans, verts = self._fit_batch(body_params, i, i_start, i_end, enable_time=opt_sequence, fix_poses=fix_poses)
|
127 |
+
# if torch.isnan(betas).any() \
|
128 |
+
# or torch.isnan(poses).any() \
|
129 |
+
# or torch.isnan(trans).any():
|
130 |
+
# print(f'Nan values detected.')
|
131 |
+
# raise ValueError('Nan values detected in the output.')
|
132 |
+
|
133 |
+
# Store ethe results
|
134 |
+
res_dict['poses'].append(poses)
|
135 |
+
res_dict['betas'].append(betas)
|
136 |
+
res_dict['trans'].append(trans)
|
137 |
+
if self.export_meshes:
|
138 |
+
# Store the meshes vertices
|
139 |
+
skel_output = self.skel.forward(poses=poses, betas=betas, trans=trans, poses_type='skel', skelmesh=True)
|
140 |
+
res_dict['skel_v'].append(skel_output.skel_verts)
|
141 |
+
res_dict['skin_v'].append(skel_output.skin_verts)
|
142 |
+
res_dict['smpl_v'].append(verts)
|
143 |
+
|
144 |
+
if opt_sequence:
|
145 |
+
# Initialize the next frames with current frame
|
146 |
+
body_params['poses_skel'][i_end:] = poses[-1:].detach()
|
147 |
+
body_params['trans_skel'][i_end:] = trans[-1].detach()
|
148 |
+
body_params['betas_skel'][i_end:] = betas[-1:].detach()
|
149 |
+
|
150 |
+
# Concatenate the batches and convert to numpy
|
151 |
+
for key, val in res_dict.items():
|
152 |
+
if isinstance(val, list):
|
153 |
+
res_dict[key] = torch.cat(val, dim=0).detach().cpu().numpy()
|
154 |
+
|
155 |
+
return res_dict
|
156 |
+
|
157 |
+
def _init_params(self, betas_smpl, poses_smpl, trans_smpl, skel_data_init=None, variant_exp=''):
|
158 |
+
""" Return initial SKEL parameters from SMPL data dictionary and an optional SKEL data dictionary."""
|
159 |
+
|
160 |
+
if skel_data_init is None or self.force_recompute:
|
161 |
+
|
162 |
+
poses_skel = torch.zeros((self.nb_frames, self.skel.num_q_params), device=self.device)
|
163 |
+
if variant_exp == '' or variant_exp == '_official_old':
|
164 |
+
poses_skel[:, :3] = poses_smpl[:, :3] # Global orient are similar between SMPL and SKEL, so init with SMPL angles
|
165 |
+
elif variant_exp == '_official_fix':
|
166 |
+
# https://github.com/MarilynKeller/SKEL/commit/d1f6ff62235c142ba010158e00e21fd4fe25807f#diff-09188717a56a42e9589e9bd289f9ddb4fb53160e03c81a7ced70b3a84c1d9d0bR157
|
167 |
+
pass
|
168 |
+
elif variant_exp == '_my_fix':
|
169 |
+
gt_orient_aa = poses_smpl[:, :3]
|
170 |
+
# IMPORTANT: The alignment comes from `exp/inspect_skel/archive/orientation.py`.
|
171 |
+
from lib.utils.geometry.rotation import axis_angle_to_matrix, matrix_to_euler_angles
|
172 |
+
gt_orient_mat = axis_angle_to_matrix(gt_orient_aa)
|
173 |
+
gt_orient_ea = matrix_to_euler_angles(gt_orient_mat, 'YXZ')
|
174 |
+
flip = torch.tensor([-1, 1, 1], device=self.device)
|
175 |
+
poses_skel[:, :3] = gt_orient_ea[:, [2, 1, 0]] * flip
|
176 |
+
else:
|
177 |
+
raise ValueError(f'Unknown variant_exp {variant_exp}')
|
178 |
+
|
179 |
+
betas_skel = torch.zeros((self.nb_frames, 10), device=self.device)
|
180 |
+
betas_skel[:] = betas_smpl[..., :10]
|
181 |
+
|
182 |
+
trans_skel = trans_smpl # Translation is similar between SMPL and SKEL, so init with SMPL translation
|
183 |
+
|
184 |
+
else:
|
185 |
+
# Load from previous alignment
|
186 |
+
betas_skel = to_torch(skel_data_init['betas'], self.device)
|
187 |
+
poses_skel = to_torch(skel_data_init['poses'], self.device)
|
188 |
+
trans_skel = to_torch(skel_data_init['trans'], self.device)
|
189 |
+
|
190 |
+
# Make a dictionary out of the necessary body parameters
|
191 |
+
body_params = {
|
192 |
+
'betas_skel': betas_skel,
|
193 |
+
'poses_skel': poses_skel,
|
194 |
+
'trans_skel': trans_skel,
|
195 |
+
'betas_smpl': betas_smpl,
|
196 |
+
'poses_smpl': poses_smpl,
|
197 |
+
'trans_smpl': trans_smpl
|
198 |
+
}
|
199 |
+
|
200 |
+
return body_params
|
201 |
+
|
202 |
+
|
203 |
+
def _fit_batch(self, body_params, i, i_start, i_end, enable_time=False, fix_poses=False):
|
204 |
+
""" Create parameters for the batch and run the optimization."""
|
205 |
+
|
206 |
+
# Sample a batch ver
|
207 |
+
body_params = { key: val[i_start:i_end] for key, val in body_params.items()}
|
208 |
+
|
209 |
+
# SMPL params
|
210 |
+
betas_smpl = body_params['betas_smpl']
|
211 |
+
poses_smpl = body_params['poses_smpl']
|
212 |
+
trans_smpl = body_params['trans_smpl']
|
213 |
+
|
214 |
+
# SKEL params
|
215 |
+
betas = to_params(body_params['betas_skel'], device=self.device)
|
216 |
+
poses = to_params(body_params['poses_skel'], device=self.device)
|
217 |
+
trans = to_params(body_params['trans_skel'], device=self.device)
|
218 |
+
|
219 |
+
if 'verts' in body_params:
|
220 |
+
verts = body_params['verts']
|
221 |
+
else:
|
222 |
+
# Run a SMPL forward pass to get the SMPL body vertices
|
223 |
+
smpl_output = self.smpl(betas=betas_smpl, body_pose=poses_smpl[:,3:], transl=trans_smpl, global_orient=poses_smpl[:,:3])
|
224 |
+
verts = smpl_output.vertices
|
225 |
+
|
226 |
+
# Optimize
|
227 |
+
config = self.cfg.optim_steps
|
228 |
+
current_cfg = config[0]
|
229 |
+
|
230 |
+
# from lib.kits.debug import set_trace
|
231 |
+
# set_trace()
|
232 |
+
|
233 |
+
try:
|
234 |
+
if fix_poses:
|
235 |
+
# for ci, cfg in enumerate(config[1:]):
|
236 |
+
for ci, cfg in enumerate([config[-1]]): # To debug, only run the last step
|
237 |
+
current_cfg.update(cfg)
|
238 |
+
print(f'Step {ci+1}: {current_cfg.description}')
|
239 |
+
self._optim([trans,betas], poses, betas, trans, verts, current_cfg, enable_time)
|
240 |
+
else:
|
241 |
+
if not enable_time or not self.is_skel_data_init:
|
242 |
+
# Optimize the global rotation and translation for the initial fitting
|
243 |
+
print(f'Step 0: {current_cfg.description}')
|
244 |
+
self._optim([trans,poses], poses, betas, trans, verts, current_cfg, enable_time)
|
245 |
+
|
246 |
+
for ci, cfg in enumerate(config[1:]):
|
247 |
+
# for ci, cfg in enumerate([config[-1]]): # To debug, only run the last step
|
248 |
+
current_cfg.update(cfg)
|
249 |
+
print(f'Step {ci+1}: {current_cfg.description}')
|
250 |
+
self._optim([poses], poses, betas, trans, verts, current_cfg, enable_time)
|
251 |
+
|
252 |
+
|
253 |
+
# # Refine by optimizing the whole body
|
254 |
+
# cfg.update(self.cfg_optim[])
|
255 |
+
# cfg.update({'mode' : 'free', 'tolerance_change': 0.0001, 'l_joint': 0.2e4})
|
256 |
+
# self._optim([trans, poses], poses, betas, trans, verts, cfg)
|
257 |
+
except Exception as e:
|
258 |
+
print(e)
|
259 |
+
traceback.print_exc()
|
260 |
+
# from lib.kits.debug import set_trace
|
261 |
+
# set_trace()
|
262 |
+
|
263 |
+
return betas, poses, trans, verts
|
264 |
+
|
265 |
+
def _optim(self,
|
266 |
+
params,
|
267 |
+
poses,
|
268 |
+
betas,
|
269 |
+
trans,
|
270 |
+
verts,
|
271 |
+
cfg,
|
272 |
+
enable_time=False,
|
273 |
+
):
|
274 |
+
|
275 |
+
# regress anatomical joints from SMPL's vertices
|
276 |
+
anat_joints = torch.einsum('bik,ji->bjk', [verts, self.skel.J_regressor_osim])
|
277 |
+
dJ=torch.zeros((poses.shape[0], 24, 3), device=betas.device)
|
278 |
+
|
279 |
+
# Create the optimizer
|
280 |
+
optimizer = torch.optim.LBFGS(params,
|
281 |
+
lr=cfg.lr,
|
282 |
+
max_iter=cfg.max_iter,
|
283 |
+
line_search_fn=cfg.line_search_fn,
|
284 |
+
tolerance_change=cfg.tolerance_change)
|
285 |
+
|
286 |
+
poses_init = poses.detach().clone()
|
287 |
+
trans_init = trans.detach().clone()
|
288 |
+
|
289 |
+
def closure():
|
290 |
+
optimizer.zero_grad()
|
291 |
+
|
292 |
+
# fi = self.watch_frame #frame of the batch to display
|
293 |
+
# output = self.skel.forward(poses=poses[fi:fi+1],
|
294 |
+
# betas=betas[fi:fi+1],
|
295 |
+
# trans=trans[fi:fi+1],
|
296 |
+
# poses_type='skel',
|
297 |
+
# dJ=dJ[fi:fi+1],
|
298 |
+
# skelmesh=True)
|
299 |
+
|
300 |
+
# self._fstep_plot(output, cfg, verts[fi:fi+1], anat_joints[fi:fi+1], )
|
301 |
+
|
302 |
+
loss_dict = self._fitting_loss(poses,
|
303 |
+
poses_init,
|
304 |
+
betas,
|
305 |
+
trans,
|
306 |
+
trans_init,
|
307 |
+
dJ,
|
308 |
+
anat_joints,
|
309 |
+
verts,
|
310 |
+
cfg,
|
311 |
+
enable_time)
|
312 |
+
|
313 |
+
# print(pretty_loss_print(loss_dict))
|
314 |
+
|
315 |
+
loss = sum(loss_dict.values())
|
316 |
+
loss.backward()
|
317 |
+
|
318 |
+
return loss
|
319 |
+
|
320 |
+
for step_i in range(cfg.num_steps):
|
321 |
+
loss = optimizer.step(closure).item()
|
322 |
+
|
323 |
+
def _get_masks(self, cfg):
|
324 |
+
pose_mask = torch.ones((self.skel.num_q_params)).to(self.device).unsqueeze(0)
|
325 |
+
verts_mask = torch.ones_like(self.fitting_mask)
|
326 |
+
joint_mask = torch.ones((self.skel.num_joints, 3)).to(self.device).unsqueeze(0).bool()
|
327 |
+
|
328 |
+
# Mask vertices
|
329 |
+
if cfg.mode=='root_only':
|
330 |
+
# Only optimize the global rotation of the body, i.e. the first 3 angles of the pose
|
331 |
+
pose_mask[:] = 0 # Only optimize for the global rotation
|
332 |
+
pose_mask[:,:3] = 1
|
333 |
+
# Only fit the thorax vertices to recover the proper body orientation and translation
|
334 |
+
verts_mask = self.torso_verts_mask
|
335 |
+
|
336 |
+
elif cfg.mode=='fixed_upper_limbs':
|
337 |
+
upper_limbs_joints = [0,1,2,3,6,9,12,15,17]
|
338 |
+
verts_mask = (self.smpl.lbs_weights[:,upper_limbs_joints]>0.5).sum(dim=-1)>0
|
339 |
+
verts_mask = verts_mask.unsqueeze(0).unsqueeze(-1)
|
340 |
+
|
341 |
+
joint_mask[:, [3,4,5,8,9,10,18,23], :] = 0 # Do not try to match the joints of the upper limbs
|
342 |
+
|
343 |
+
pose_mask[:] = 1
|
344 |
+
pose_mask[:,:3] = 0 # Block the global rotation
|
345 |
+
pose_mask[:,19] = 0 # block the lumbar twist
|
346 |
+
# pose_mask[:, 36:39] = 0
|
347 |
+
# pose_mask[:, 43:46] = 0
|
348 |
+
# pose_mask[:, 62:65] = 0
|
349 |
+
# pose_mask[:, 62:65] = 0
|
350 |
+
|
351 |
+
elif cfg.mode=='fixed_root':
|
352 |
+
pose_mask[:] = 1
|
353 |
+
pose_mask[:,:3] = 0 # Block the global rotation
|
354 |
+
# pose_mask[:,19] = 0 # block the lumbar twist
|
355 |
+
|
356 |
+
# The orientation of the upper limbs is often wrong in SMPL so ignore these vertices for the finale step
|
357 |
+
upper_limbs_joints = [1,2,16,17]
|
358 |
+
verts_mask = (self.smpl.lbs_weights[:,upper_limbs_joints]>0.5).sum(dim=-1)>0
|
359 |
+
verts_mask = torch.logical_not(verts_mask)
|
360 |
+
verts_mask = verts_mask.unsqueeze(0).unsqueeze(-1)
|
361 |
+
|
362 |
+
elif cfg.mode=='free':
|
363 |
+
verts_mask = torch.ones_like(self.fitting_mask )
|
364 |
+
|
365 |
+
joint_mask[:]=0
|
366 |
+
joint_mask[:, [19,14], :] = 1 # Only fir the scapula join to avoid collapsing shoulders
|
367 |
+
|
368 |
+
else:
|
369 |
+
raise ValueError(f'Unknown mode {cfg.mode}')
|
370 |
+
|
371 |
+
return pose_mask, verts_mask, joint_mask
|
372 |
+
|
373 |
+
def _fitting_loss(self,
|
374 |
+
poses,
|
375 |
+
poses_init,
|
376 |
+
betas,
|
377 |
+
trans,
|
378 |
+
trans_init,
|
379 |
+
dJ,
|
380 |
+
anat_joints,
|
381 |
+
verts,
|
382 |
+
cfg,
|
383 |
+
enable_time=False):
|
384 |
+
|
385 |
+
loss_dict = {}
|
386 |
+
|
387 |
+
|
388 |
+
pose_mask, verts_mask, joint_mask = self._get_masks(cfg)
|
389 |
+
poses = poses * pose_mask + poses_init * (1-pose_mask)
|
390 |
+
|
391 |
+
# Mask joints to not optimize before computing the losses
|
392 |
+
|
393 |
+
output = self.skel.forward(poses=poses, betas=betas, trans=trans, poses_type='skel', dJ=dJ, skelmesh=False)
|
394 |
+
|
395 |
+
# Fit the SMPL vertices
|
396 |
+
# We know the skinning of the forearm and the neck are not perfect,
|
397 |
+
# so we create a mask of the SMPL vertices that are important to fit, like the hands and the head
|
398 |
+
loss_dict['verts_loss_loose'] = cfg.l_verts_loose * (verts_mask * (output.skin_verts - verts)**2).sum() / (((verts_mask).sum()*self.nb_frames))
|
399 |
+
|
400 |
+
# Fit the regressed joints, this avoids collapsing shoulders
|
401 |
+
# loss_dict['joint_loss'] = cfg.l_joint * F.mse_loss(output.joints, anat_joints)
|
402 |
+
loss_dict['joint_loss'] = cfg.l_joint * (joint_mask * (output.joints - anat_joints)**2).mean()
|
403 |
+
|
404 |
+
# Time consistancy
|
405 |
+
if poses.shape[0] > 1 and enable_time:
|
406 |
+
# This avoids unstable hips orientationZ
|
407 |
+
loss_dict['time_loss'] = cfg.l_time_loss * F.mse_loss(poses[1:], poses[:-1])
|
408 |
+
|
409 |
+
loss_dict['pose_loss'] = cfg.l_pose_loss * compute_pose_loss(poses, poses_init)
|
410 |
+
|
411 |
+
if cfg.use_basic_loss is False:
|
412 |
+
# These losses can be used to regularize the optimization but are not always necessary
|
413 |
+
loss_dict['anch_rot'] = cfg.l_anch_pose * compute_anchor_pose(poses, poses_init)
|
414 |
+
loss_dict['anch_trans'] = cfg.l_anch_trans * compute_anchor_trans(trans, trans_init)
|
415 |
+
|
416 |
+
loss_dict['verts_loss'] = cfg.l_verts * (verts_mask * self.fitting_mask * (output.skin_verts - verts)**2).sum() / (self.fitting_mask*verts_mask).sum()
|
417 |
+
|
418 |
+
# Regularize the pose
|
419 |
+
loss_dict['scapula_loss'] = cfg.l_scapula_loss * compute_scapula_loss(poses)
|
420 |
+
loss_dict['spine_loss'] = cfg.l_spine_loss * compute_spine_loss(poses)
|
421 |
+
|
422 |
+
# Adjust the losses of all the pose regularizations sub losses with the pose_reg_factor value
|
423 |
+
for key in ['scapula_loss', 'spine_loss', 'pose_loss']:
|
424 |
+
loss_dict[key] = cfg.pose_reg_factor * loss_dict[key]
|
425 |
+
|
426 |
+
return loss_dict
|
427 |
+
|
428 |
+
def _fstep_plot(self, output, cfg, verts, anat_joints):
|
429 |
+
"Function to plot each step"
|
430 |
+
|
431 |
+
if('DISABLE_VIEWER' in os.environ):
|
432 |
+
return
|
433 |
+
|
434 |
+
pose_mask, verts_mask, joint_mask = self._get_masks(cfg)
|
435 |
+
|
436 |
+
skin_err_value = ((output.skin_verts[0] - verts[0])**2).sum(dim=-1).sqrt()
|
437 |
+
skin_err_value = skin_err_value / 0.05
|
438 |
+
skin_err_value = to_numpy(skin_err_value)
|
439 |
+
|
440 |
+
skin_mesh = Mesh(v=to_numpy(output.skin_verts[0]), f=[], vc='white')
|
441 |
+
skel_mesh = Mesh(v=to_numpy(output.skel_verts[0]), f=self.skel.skel_f.cpu().numpy(), vc='white')
|
442 |
+
|
443 |
+
# Display vertex distance on SMPL
|
444 |
+
smpl_verts = to_numpy(verts[0])
|
445 |
+
smpl_mesh = Mesh(v=smpl_verts, f=self.smpl.faces)
|
446 |
+
smpl_mesh.set_vertex_colors_from_weights(skin_err_value, scale_to_range_1=False)
|
447 |
+
|
448 |
+
smpl_mesh_masked = Mesh(v=smpl_verts[to_numpy(verts_mask[0,:,0])], f=[], vc='green')
|
449 |
+
smpl_mesh_pc = Mesh(v=smpl_verts, f=[], vc='green')
|
450 |
+
|
451 |
+
skin_mesh_err = Mesh(v=to_numpy(output.skin_verts[0]), f=self.skel.skin_f.cpu().numpy(), vc='white')
|
452 |
+
skin_mesh_err.set_vertex_colors_from_weights(skin_err_value, scale_to_range_1=False)
|
453 |
+
# List the meshes to display
|
454 |
+
meshes_left = [skin_mesh_err, smpl_mesh_pc]
|
455 |
+
meshes_right = [smpl_mesh_masked, skin_mesh, skel_mesh]
|
456 |
+
|
457 |
+
if cfg.l_joint > 0:
|
458 |
+
# Plot the joints
|
459 |
+
meshes_right += location_to_spheres(to_numpy(output.joints[joint_mask[:,:,0]]), color=(1,0,0), radius=0.02)
|
460 |
+
meshes_right += location_to_spheres(to_numpy(anat_joints[joint_mask[:,:,0]]), color=(0,1,0), radius=0.02) \
|
461 |
+
|
462 |
+
|
463 |
+
self.mv[0][0].set_dynamic_meshes(meshes_left)
|
464 |
+
self.mv[0][1].set_dynamic_meshes(meshes_right)
|
465 |
+
|
466 |
+
# print(poses[frame_to_watch, :3])
|
467 |
+
# print(trans[frame_to_watch])
|
468 |
+
# print(betas[frame_to_watch, :3])
|
469 |
+
# mv.get_keypress()
|
lib/body_models/skel/alignment/losses.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
|
3 |
+
def compute_scapula_loss(poses):
|
4 |
+
|
5 |
+
scapula_indices = [26, 27, 28, 36, 37, 38]
|
6 |
+
|
7 |
+
scapula_poses = poses[:, scapula_indices]
|
8 |
+
scapula_loss = torch.linalg.norm(scapula_poses, ord=2)
|
9 |
+
return scapula_loss
|
10 |
+
|
11 |
+
def compute_spine_loss(poses):
|
12 |
+
|
13 |
+
spine_indices = range(17, 25)
|
14 |
+
|
15 |
+
spine_poses = poses[:, spine_indices]
|
16 |
+
spine_loss = torch.linalg.norm(spine_poses, ord=2)
|
17 |
+
return spine_loss
|
18 |
+
|
19 |
+
def compute_pose_loss(poses, pose_init):
|
20 |
+
|
21 |
+
pose_loss = torch.linalg.norm(poses[:, 3:], ord=2) # The global rotation should not be constrained
|
22 |
+
return pose_loss
|
23 |
+
|
24 |
+
def compute_anchor_pose(poses, pose_init):
|
25 |
+
|
26 |
+
pose_loss = torch.nn.functional.mse_loss(poses[:, :3], pose_init[:, :3])
|
27 |
+
return pose_loss
|
28 |
+
|
29 |
+
def compute_anchor_trans(trans, trans_init):
|
30 |
+
|
31 |
+
trans_loss = torch.nn.functional.mse_loss(trans, trans_init)
|
32 |
+
return trans_loss
|
33 |
+
|
34 |
+
def compute_time_loss(poses):
|
35 |
+
|
36 |
+
pose_delta = poses[1:] - poses[:-1]
|
37 |
+
time_loss = torch.linalg.norm(pose_delta, ord=2)
|
38 |
+
return time_loss
|
39 |
+
|
40 |
+
def pretty_loss_print(loss_dict):
|
41 |
+
# Pretty print the loss on the form loss val | loss1 val1 | loss2 val2
|
42 |
+
# Start with the total loss
|
43 |
+
loss = sum(loss_dict.values())
|
44 |
+
pretty_loss = f'{loss:.4f}'
|
45 |
+
for key, val in loss_dict.items():
|
46 |
+
pretty_loss += f' | {key} {val:.4f}'
|
47 |
+
return pretty_loss
|
lib/body_models/skel/alignment/riggid_parts_mask.pkl
ADDED
Binary file (11.9 kB). View file
|
|
lib/body_models/skel/alignment/utils.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import os
|
3 |
+
import pickle
|
4 |
+
import torch
|
5 |
+
import numpy as np
|
6 |
+
from psbody.mesh.sphere import Sphere
|
7 |
+
|
8 |
+
# to_params = lambda x: torch.from_numpy(x).float().to(self.device).requires_grad_(True)
|
9 |
+
# to_torch = lambda x: torch.from_numpy(x).float().to(self.device)
|
10 |
+
|
11 |
+
def to_params(x, device):
|
12 |
+
return x.to(device).requires_grad_(True)
|
13 |
+
|
14 |
+
def to_torch(x, device):
|
15 |
+
return torch.from_numpy(x).float().to(device)
|
16 |
+
|
17 |
+
def to_numpy(x):
|
18 |
+
return x.detach().cpu().numpy()
|
19 |
+
|
20 |
+
def load_smpl_seq(smpl_seq_path, gender=None, straighten_hands=False):
|
21 |
+
|
22 |
+
if not os.path.exists(smpl_seq_path):
|
23 |
+
raise Exception('Path does not exist: {}'.format(smpl_seq_path))
|
24 |
+
|
25 |
+
if smpl_seq_path.endswith('.pkl'):
|
26 |
+
data_dict = pickle.load(open(smpl_seq_path, 'rb'))
|
27 |
+
|
28 |
+
elif smpl_seq_path.endswith('.npz'):
|
29 |
+
data_dict = np.load(smpl_seq_path, allow_pickle=True)
|
30 |
+
|
31 |
+
if data_dict.files == ['pred_smpl_parms', 'verts', 'pred_cam_t']:
|
32 |
+
data_dict = data_dict['pred_smpl_parms'].item()# ['global_orient', 'body_pose', 'body_pose_axis_angle', 'global_orient_axis_angle', 'betas']
|
33 |
+
else:
|
34 |
+
data_dict = {key: data_dict[key] for key in data_dict.keys()} # convert to python dict
|
35 |
+
else:
|
36 |
+
raise Exception('Unknown file format: {}. Supported formats are .pkl and .npz'.format(smpl_seq_path))
|
37 |
+
|
38 |
+
# Instanciate a dictionary with the keys expected by the fitter
|
39 |
+
data_fixed = {}
|
40 |
+
|
41 |
+
# Get gender
|
42 |
+
if 'gender' not in data_dict:
|
43 |
+
assert gender is not None, f"The provided SMPL data dictionary does not contain gender, you need to pass it in command line"
|
44 |
+
data_fixed['gender'] = gender
|
45 |
+
elif not isinstance(data_dict['gender'], str):
|
46 |
+
# In some npz, the gender type happens to be: array('male', dtype='<U4'). So we convert it to string
|
47 |
+
data_fixed['gender'] = str(data_dict['gender'])
|
48 |
+
else:
|
49 |
+
data_fixed['gender'] = gender
|
50 |
+
|
51 |
+
# convert tensors to numpy arrays
|
52 |
+
for key, val in data_dict.items():
|
53 |
+
if isinstance(val, torch.Tensor):
|
54 |
+
data_dict[key] = val.detach().cpu().numpy()
|
55 |
+
|
56 |
+
# Get the SMPL pose
|
57 |
+
if 'poses' in data_dict:
|
58 |
+
poses = data_dict['poses']
|
59 |
+
elif 'body_pose_axis_angle' in data_dict and 'global_orient_axis_angle' in data_dict:
|
60 |
+
# assert 'global_orient' in data_dict and 'body_pose' in data_dict, f"Could not find poses in {smpl_seq_path}. Available keys: {data_dict.keys()})"
|
61 |
+
poses = np.concatenate([data_dict['global_orient_axis_angle'], data_dict['body_pose_axis_angle']], axis=1)
|
62 |
+
poses = poses.reshape(-1, 72)
|
63 |
+
elif 'body_pose' in data_dict and 'global_orient' in data_dict:
|
64 |
+
poses = np.concatenate([data_dict['global_orient_axis_angle'], data_dict['body_pose_axis_angle']], axis=-1)
|
65 |
+
else:
|
66 |
+
raise Exception(f"Could not find poses in {smpl_seq_path}. Available keys: {data_dict.keys()})")
|
67 |
+
|
68 |
+
if poses.shape[1] == 156:
|
69 |
+
# Those are SMPL+H poses, we remove the hand poses to keep only the body poses
|
70 |
+
smpl_poses = np.zeros((poses.shape[0], 72))
|
71 |
+
smpl_poses[:, :72-2*3] = poses[:, :72-2*3] # We leave params for SMPL joints 22 and 23 to zero as these DOF are not present in SMPLH
|
72 |
+
poses = smpl_poses
|
73 |
+
|
74 |
+
# Set SMPL joints 22 and 23 to zero as SKEL has rigid hands
|
75 |
+
if straighten_hands:
|
76 |
+
poses[:, 72-2*3:] = 0
|
77 |
+
|
78 |
+
data_fixed['poses'] = poses
|
79 |
+
|
80 |
+
# Translation
|
81 |
+
if 'trans' not in data_dict:
|
82 |
+
data_fixed['trans'] = np.zeros((poses.shape[0], 3))
|
83 |
+
else:
|
84 |
+
data_fixed['trans'] = data_dict['trans']
|
85 |
+
|
86 |
+
# Get betas
|
87 |
+
betas = data_dict['betas'][..., :10] # Keep only the 10 first betas
|
88 |
+
if len(betas.shape) == 1 and len(poses.shape) == 2:
|
89 |
+
betas = betas[None, :] # Add a batch dimension
|
90 |
+
data_fixed['betas'] = betas
|
91 |
+
|
92 |
+
for key in ['trans', 'poses', 'betas', 'gender']:
|
93 |
+
assert key in data_fixed.keys(), f'Could not find {key} in {smpl_seq_path}. Available keys: {data_fixed.keys()})'
|
94 |
+
|
95 |
+
out_dict = {}
|
96 |
+
out_dict['trans'] = data_fixed['trans']
|
97 |
+
out_dict['poses'] = data_fixed['poses']
|
98 |
+
out_dict['betas'] = data_fixed['betas']
|
99 |
+
out_dict['gender'] = data_fixed['gender']
|
100 |
+
|
101 |
+
return out_dict
|
102 |
+
|
103 |
+
|
104 |
+
def location_to_spheres(loc, color=(1,0,0), radius=0.02):
|
105 |
+
"""Given an array of 3D points, return a list of spheres located at those positions.
|
106 |
+
|
107 |
+
Args:
|
108 |
+
loc (numpy.array): Nx3 array giving 3D positions
|
109 |
+
color (tuple, optional): One RGB float color vector to color the spheres. Defaults to (1,0,0).
|
110 |
+
radius (float, optional): Radius of the spheres in meters. Defaults to 0.02.
|
111 |
+
|
112 |
+
Returns:
|
113 |
+
list: List of spheres Mesh
|
114 |
+
"""
|
115 |
+
|
116 |
+
cL = [Sphere(np.asarray([loc[i, 0], loc[i, 1], loc[i, 2]]), radius).to_mesh() for i in range(loc.shape[0])]
|
117 |
+
for spL in cL:
|
118 |
+
spL.set_vertex_colors(np.array(color))
|
119 |
+
return cL
|
lib/body_models/skel/config.py
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
skel_folder = '/u/yanxia/workspace/data/body_models/skel_models_v1.1'
|
2 |
+
smpl_folder = '/data/yanxia/code/SKEL/models/'
|
lib/body_models/skel/fit_osim/Fitting_SKEL_to_osim.md
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Fitting SKEL to OpenSim
|
2 |
+
|
3 |
+
Here we explain how to fit the SKEL model to an OpenSim model sequence to get a surface mesh. We do not provide fitting code yet but if you are interested in this direction, this folder should give you a good starting point.
|
4 |
+
|
5 |
+
As an example, we will use the OpenSim model from https://simtk.org/projects/full_body.
|
6 |
+
Download the model and sample data from (this page)[https://simtk.org/projects/full_body]. You should get the zip folder: `FullBodyModelwSampleSim.-latest.zip` and unzip it. This folder contains a .osim OpenSim model and example .mot motion sequences.
|
7 |
+
|
8 |
+
You can visualize the sequence with:
|
9 |
+
|
10 |
+
```bash
|
11 |
+
python examples/load_osim.py --osim /path/to/FullBodyModelwSampleSim.-latest/ModelWithSampleSimulations-4.0/SimulationDataAndSetupFiles-4.0/Rajagopal2015.osim --mot /path/to/FullBodyModelwSampleSim.-latest/ModelWithSampleSimulations-4.0/SimulationDataAndSetupFiles-4.0/IK/results_run/ik_output_run.mot
|
12 |
+
```
|
13 |
+
|
14 |
+
To fit SKEL to this sequence, we need to map each joint of this OpenSim model to a joint of the SKEL model. For the current model, the mapping is in `SKEL/skel/fit_osim/mappings/full_body.yaml`. If you use another model, you will need to create your own mapping yaml for that model.
|
15 |
+
|
16 |
+
The following script will then show you how to get the needed joints trajectories to fit SKEL to this OpenSim sequence:
|
17 |
+
|
18 |
+
```
|
19 |
+
python SKEL/skel/fit_osim/osim_fitter.py
|
20 |
+
```
|
lib/body_models/skel/fit_osim/mappings/full_body.yaml
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Mapping to SKEL
|
2 |
+
|
3 |
+
joints_mapping: # For each joint of the OpenSim model, specify the corresponding joint in SKEL
|
4 |
+
pelvis : pelvis
|
5 |
+
femur_r : femur_r
|
6 |
+
tibia_r : tibia_r
|
7 |
+
talus_r : talus_r
|
8 |
+
calcn_r : calcn_r
|
9 |
+
toes_r : toes_r
|
10 |
+
femur_l : femur_l
|
11 |
+
tibia_l : tibia_l
|
12 |
+
talus_l : talus_l
|
13 |
+
calcn_l : calcn_l
|
14 |
+
toes_l : toes_l
|
15 |
+
torso : thorax # This changes
|
16 |
+
humerus_r : humerus_r
|
17 |
+
ulna_r : ulna_r
|
18 |
+
radius_r : radius_r
|
19 |
+
hand_r : hand_r
|
20 |
+
humerus_l : humerus_l
|
21 |
+
ulna_l : ulna_l
|
22 |
+
radius_l : radius_l
|
23 |
+
hand_l : hand_l
|
24 |
+
|
25 |
+
markers_mapping: #Given specific markers of the OpenSim model, specify the corresponding vertex index on SMPL/SKEL skin
|
26 |
+
RFAradius : 5573 #RWRA
|
27 |
+
RFAulna : 5568 #RWRB
|
28 |
+
LFAradius : 2112 #LWRA
|
29 |
+
LFAulna : 2108 #LWRB
|