Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitignore +156 -0
- GPT_eval_multi.py +121 -0
- LICENSE +201 -0
- README.md +8 -0
- VQ_eval.py +95 -0
- app.py +379 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/.gitattributes +34 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/README.md +15 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualization/plot_3d_global.py +129 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/config.py +40 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/customloss.py +222 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/prior.py +230 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/smplify.py +279 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/render_mesh.py +33 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/simplify_loc2rot.py +131 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/vis_utils.py +66 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/app.py +329 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/packages.txt +4 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.coveragerc +5 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.flake8 +8 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.gitignore +106 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.pre-commit-config.yaml +6 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.travis.yml +43 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/LICENSE +21 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/MANIFEST.in +5 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/README.md +92 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/Makefile +23 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/make.bat +35 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/api/index.rst +59 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/conf.py +352 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/cameras.rst +26 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/index.rst +20 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/lighting.rst +21 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/models.rst +143 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/offscreen.rst +87 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/index.rst +41 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/requirements.txt +14 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/setup.py +76 -0
- c125d6fbf8bda65345b6edda6e04f79faf4d67cd/requirements.txt +27 -0
- checkpoints/kit/kit/Comp_v6_KLD005/opt.txt +54 -0
- checkpoints/kit/kit/text_mot_match/eval/E005.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E010.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E015.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E020.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E025.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E030.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E035.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E040.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E045.txt +4 -0
- checkpoints/kit/kit/text_mot_match/eval/E050.txt +4 -0
.gitignore
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
pip-wheel-metadata/
|
| 24 |
+
share/python-wheels/
|
| 25 |
+
*.egg-info/
|
| 26 |
+
.installed.cfg
|
| 27 |
+
*.egg
|
| 28 |
+
MANIFEST
|
| 29 |
+
|
| 30 |
+
# PyInstaller
|
| 31 |
+
# Usually these files are written by a python script from a template
|
| 32 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 33 |
+
*.manifest
|
| 34 |
+
*.spec
|
| 35 |
+
|
| 36 |
+
# Installer logs
|
| 37 |
+
pip-log.txt
|
| 38 |
+
pip-delete-this-directory.txt
|
| 39 |
+
|
| 40 |
+
# Unit test / coverage reports
|
| 41 |
+
htmlcov/
|
| 42 |
+
.tox/
|
| 43 |
+
.nox/
|
| 44 |
+
.coverage
|
| 45 |
+
.coverage.*
|
| 46 |
+
.cache
|
| 47 |
+
nosetests.xml
|
| 48 |
+
coverage.xml
|
| 49 |
+
*.cover
|
| 50 |
+
*.py,cover
|
| 51 |
+
.hypothesis/
|
| 52 |
+
.pytest_cache/
|
| 53 |
+
|
| 54 |
+
# Translations
|
| 55 |
+
*.mo
|
| 56 |
+
*.pot
|
| 57 |
+
|
| 58 |
+
# Django stuff:
|
| 59 |
+
*.log
|
| 60 |
+
local_settings.py
|
| 61 |
+
db.sqlite3
|
| 62 |
+
db.sqlite3-journal
|
| 63 |
+
|
| 64 |
+
# Flask stuff:
|
| 65 |
+
instance/
|
| 66 |
+
.webassets-cache
|
| 67 |
+
|
| 68 |
+
# Scrapy stuff:
|
| 69 |
+
.scrapy
|
| 70 |
+
|
| 71 |
+
# Sphinx documentation
|
| 72 |
+
docs/_build/
|
| 73 |
+
|
| 74 |
+
# PyBuilder
|
| 75 |
+
target/
|
| 76 |
+
|
| 77 |
+
# Jupyter Notebook
|
| 78 |
+
.ipynb_checkpoints
|
| 79 |
+
|
| 80 |
+
# IPython
|
| 81 |
+
profile_default/
|
| 82 |
+
ipython_config.py
|
| 83 |
+
|
| 84 |
+
# pyenv
|
| 85 |
+
.python-version
|
| 86 |
+
|
| 87 |
+
# pipenv
|
| 88 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 89 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 90 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 91 |
+
# install all needed dependencies.
|
| 92 |
+
#Pipfile.lock
|
| 93 |
+
|
| 94 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
| 95 |
+
__pypackages__/
|
| 96 |
+
|
| 97 |
+
# Celery stuff
|
| 98 |
+
celerybeat-schedule
|
| 99 |
+
celerybeat.pid
|
| 100 |
+
|
| 101 |
+
# SageMath parsed files
|
| 102 |
+
*.sage.py
|
| 103 |
+
|
| 104 |
+
# Environments
|
| 105 |
+
.env
|
| 106 |
+
.venv
|
| 107 |
+
env/
|
| 108 |
+
venv/
|
| 109 |
+
ENV/
|
| 110 |
+
env.bak/
|
| 111 |
+
venv.bak/
|
| 112 |
+
|
| 113 |
+
# Spyder project settings
|
| 114 |
+
.spyderproject
|
| 115 |
+
.spyproject
|
| 116 |
+
|
| 117 |
+
# Rope project settings
|
| 118 |
+
.ropeproject
|
| 119 |
+
|
| 120 |
+
# mkdocs documentation
|
| 121 |
+
/site
|
| 122 |
+
|
| 123 |
+
# mypy
|
| 124 |
+
.mypy_cache/
|
| 125 |
+
.dmypy.json
|
| 126 |
+
dmypy.json
|
| 127 |
+
|
| 128 |
+
# Pyre type checker
|
| 129 |
+
.pyre/
|
| 130 |
+
|
| 131 |
+
.vscode
|
| 132 |
+
dataset/dataset_TM_train_cb1_temp.py
|
| 133 |
+
train_gpt_cnn_temp.py
|
| 134 |
+
train_gpt_cnn_mask.py
|
| 135 |
+
start.sh
|
| 136 |
+
start_eval.sh
|
| 137 |
+
config.json
|
| 138 |
+
output_GPT_Final
|
| 139 |
+
output_vqfinal
|
| 140 |
+
output_transformer
|
| 141 |
+
glove
|
| 142 |
+
checkpoints
|
| 143 |
+
dataset/HumanML3D
|
| 144 |
+
dataset/KIT-ML
|
| 145 |
+
output
|
| 146 |
+
matrix_multi.py
|
| 147 |
+
body_models
|
| 148 |
+
render_final_diffuse.py
|
| 149 |
+
render_final_mdm.py
|
| 150 |
+
pretrained
|
| 151 |
+
MDM
|
| 152 |
+
Motiondiffusion
|
| 153 |
+
Visualize_temp.py
|
| 154 |
+
new.sh
|
| 155 |
+
T2M_render
|
| 156 |
+
render_final_t2m.py
|
GPT_eval_multi.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
import numpy as np
|
| 4 |
+
from torch.utils.tensorboard import SummaryWriter
|
| 5 |
+
import json
|
| 6 |
+
import clip
|
| 7 |
+
|
| 8 |
+
import options.option_transformer as option_trans
|
| 9 |
+
import models.vqvae as vqvae
|
| 10 |
+
import utils.utils_model as utils_model
|
| 11 |
+
import utils.eval_trans as eval_trans
|
| 12 |
+
from dataset import dataset_TM_eval
|
| 13 |
+
import models.t2m_trans as trans
|
| 14 |
+
from options.get_eval_option import get_opt
|
| 15 |
+
from models.evaluator_wrapper import EvaluatorModelWrapper
|
| 16 |
+
import warnings
|
| 17 |
+
warnings.filterwarnings('ignore')
|
| 18 |
+
|
| 19 |
+
##### ---- Exp dirs ---- #####
|
| 20 |
+
args = option_trans.get_args_parser()
|
| 21 |
+
torch.manual_seed(args.seed)
|
| 22 |
+
|
| 23 |
+
args.out_dir = os.path.join(args.out_dir, f'{args.exp_name}')
|
| 24 |
+
os.makedirs(args.out_dir, exist_ok = True)
|
| 25 |
+
|
| 26 |
+
##### ---- Logger ---- #####
|
| 27 |
+
logger = utils_model.get_logger(args.out_dir)
|
| 28 |
+
writer = SummaryWriter(args.out_dir)
|
| 29 |
+
logger.info(json.dumps(vars(args), indent=4, sort_keys=True))
|
| 30 |
+
|
| 31 |
+
from utils.word_vectorizer import WordVectorizer
|
| 32 |
+
w_vectorizer = WordVectorizer('./glove', 'our_vab')
|
| 33 |
+
val_loader = dataset_TM_eval.DATALoader(args.dataname, True, 32, w_vectorizer)
|
| 34 |
+
|
| 35 |
+
dataset_opt_path = 'checkpoints/kit/Comp_v6_KLD005/opt.txt' if args.dataname == 'kit' else 'checkpoints/t2m/Comp_v6_KLD005/opt.txt'
|
| 36 |
+
|
| 37 |
+
wrapper_opt = get_opt(dataset_opt_path, torch.device('cuda'))
|
| 38 |
+
eval_wrapper = EvaluatorModelWrapper(wrapper_opt)
|
| 39 |
+
|
| 40 |
+
##### ---- Network ---- #####
|
| 41 |
+
|
| 42 |
+
## load clip model and datasets
|
| 43 |
+
clip_model, clip_preprocess = clip.load("ViT-B/32", device=torch.device('cuda'), jit=False) # Must set jit=False for training
|
| 44 |
+
clip.model.convert_weights(clip_model) # Actually this line is unnecessary since clip by default already on float16
|
| 45 |
+
clip_model.eval()
|
| 46 |
+
for p in clip_model.parameters():
|
| 47 |
+
p.requires_grad = False
|
| 48 |
+
|
| 49 |
+
net = vqvae.HumanVQVAE(args, ## use args to define different parameters in different quantizers
|
| 50 |
+
args.nb_code,
|
| 51 |
+
args.code_dim,
|
| 52 |
+
args.output_emb_width,
|
| 53 |
+
args.down_t,
|
| 54 |
+
args.stride_t,
|
| 55 |
+
args.width,
|
| 56 |
+
args.depth,
|
| 57 |
+
args.dilation_growth_rate)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
trans_encoder = trans.Text2Motion_Transformer(num_vq=args.nb_code,
|
| 61 |
+
embed_dim=args.embed_dim_gpt,
|
| 62 |
+
clip_dim=args.clip_dim,
|
| 63 |
+
block_size=args.block_size,
|
| 64 |
+
num_layers=args.num_layers,
|
| 65 |
+
n_head=args.n_head_gpt,
|
| 66 |
+
drop_out_rate=args.drop_out_rate,
|
| 67 |
+
fc_rate=args.ff_rate)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
print ('loading checkpoint from {}'.format(args.resume_pth))
|
| 71 |
+
ckpt = torch.load(args.resume_pth, map_location='cpu')
|
| 72 |
+
net.load_state_dict(ckpt['net'], strict=True)
|
| 73 |
+
net.eval()
|
| 74 |
+
net.cuda()
|
| 75 |
+
|
| 76 |
+
if args.resume_trans is not None:
|
| 77 |
+
print ('loading transformer checkpoint from {}'.format(args.resume_trans))
|
| 78 |
+
ckpt = torch.load(args.resume_trans, map_location='cpu')
|
| 79 |
+
trans_encoder.load_state_dict(ckpt['trans'], strict=True)
|
| 80 |
+
trans_encoder.train()
|
| 81 |
+
trans_encoder.cuda()
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
fid = []
|
| 85 |
+
div = []
|
| 86 |
+
top1 = []
|
| 87 |
+
top2 = []
|
| 88 |
+
top3 = []
|
| 89 |
+
matching = []
|
| 90 |
+
multi = []
|
| 91 |
+
repeat_time = 20
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
for i in range(repeat_time):
|
| 95 |
+
best_fid, best_iter, best_div, best_top1, best_top2, best_top3, best_matching, best_multi, writer, logger = eval_trans.evaluation_transformer_test(args.out_dir, val_loader, net, trans_encoder, logger, writer, 0, best_fid=1000, best_iter=0, best_div=100, best_top1=0, best_top2=0, best_top3=0, best_matching=100, best_multi=0, clip_model=clip_model, eval_wrapper=eval_wrapper, draw=False, savegif=False, save=False, savenpy=(i==0))
|
| 96 |
+
fid.append(best_fid)
|
| 97 |
+
div.append(best_div)
|
| 98 |
+
top1.append(best_top1)
|
| 99 |
+
top2.append(best_top2)
|
| 100 |
+
top3.append(best_top3)
|
| 101 |
+
matching.append(best_matching)
|
| 102 |
+
multi.append(best_multi)
|
| 103 |
+
|
| 104 |
+
print('final result:')
|
| 105 |
+
print('fid: ', sum(fid)/repeat_time)
|
| 106 |
+
print('div: ', sum(div)/repeat_time)
|
| 107 |
+
print('top1: ', sum(top1)/repeat_time)
|
| 108 |
+
print('top2: ', sum(top2)/repeat_time)
|
| 109 |
+
print('top3: ', sum(top3)/repeat_time)
|
| 110 |
+
print('matching: ', sum(matching)/repeat_time)
|
| 111 |
+
print('multi: ', sum(multi)/repeat_time)
|
| 112 |
+
|
| 113 |
+
fid = np.array(fid)
|
| 114 |
+
div = np.array(div)
|
| 115 |
+
top1 = np.array(top1)
|
| 116 |
+
top2 = np.array(top2)
|
| 117 |
+
top3 = np.array(top3)
|
| 118 |
+
matching = np.array(matching)
|
| 119 |
+
multi = np.array(multi)
|
| 120 |
+
msg_final = f"FID. {np.mean(fid):.3f}, conf. {np.std(fid)*1.96/np.sqrt(repeat_time):.3f}, Diversity. {np.mean(div):.3f}, conf. {np.std(div)*1.96/np.sqrt(repeat_time):.3f}, TOP1. {np.mean(top1):.3f}, conf. {np.std(top1)*1.96/np.sqrt(repeat_time):.3f}, TOP2. {np.mean(top2):.3f}, conf. {np.std(top2)*1.96/np.sqrt(repeat_time):.3f}, TOP3. {np.mean(top3):.3f}, conf. {np.std(top3)*1.96/np.sqrt(repeat_time):.3f}, Matching. {np.mean(matching):.3f}, conf. {np.std(matching)*1.96/np.sqrt(repeat_time):.3f}, Multi. {np.mean(multi):.3f}, conf. {np.std(multi)*1.96/np.sqrt(repeat_time):.3f}"
|
| 121 |
+
logger.info(msg_final)
|
LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright 2023 tencent
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: T2M-GPT
|
| 3 |
+
app_file: app.py
|
| 4 |
+
sdk: gradio
|
| 5 |
+
sdk_version: 3.50.2
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
VQ_eval.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from torch.utils.tensorboard import SummaryWriter
|
| 6 |
+
import numpy as np
|
| 7 |
+
import models.vqvae as vqvae
|
| 8 |
+
import options.option_vq as option_vq
|
| 9 |
+
import utils.utils_model as utils_model
|
| 10 |
+
from dataset import dataset_TM_eval
|
| 11 |
+
import utils.eval_trans as eval_trans
|
| 12 |
+
from options.get_eval_option import get_opt
|
| 13 |
+
from models.evaluator_wrapper import EvaluatorModelWrapper
|
| 14 |
+
import warnings
|
| 15 |
+
warnings.filterwarnings('ignore')
|
| 16 |
+
import numpy as np
|
| 17 |
+
##### ---- Exp dirs ---- #####
|
| 18 |
+
args = option_vq.get_args_parser()
|
| 19 |
+
torch.manual_seed(args.seed)
|
| 20 |
+
|
| 21 |
+
args.out_dir = os.path.join(args.out_dir, f'{args.exp_name}')
|
| 22 |
+
os.makedirs(args.out_dir, exist_ok = True)
|
| 23 |
+
|
| 24 |
+
##### ---- Logger ---- #####
|
| 25 |
+
logger = utils_model.get_logger(args.out_dir)
|
| 26 |
+
writer = SummaryWriter(args.out_dir)
|
| 27 |
+
logger.info(json.dumps(vars(args), indent=4, sort_keys=True))
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
from utils.word_vectorizer import WordVectorizer
|
| 31 |
+
w_vectorizer = WordVectorizer('./glove', 'our_vab')
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
dataset_opt_path = 'checkpoints/kit/Comp_v6_KLD005/opt.txt' if args.dataname == 'kit' else 'checkpoints/t2m/Comp_v6_KLD005/opt.txt'
|
| 35 |
+
|
| 36 |
+
wrapper_opt = get_opt(dataset_opt_path, torch.device('cuda'))
|
| 37 |
+
eval_wrapper = EvaluatorModelWrapper(wrapper_opt)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
##### ---- Dataloader ---- #####
|
| 41 |
+
args.nb_joints = 21 if args.dataname == 'kit' else 22
|
| 42 |
+
|
| 43 |
+
val_loader = dataset_TM_eval.DATALoader(args.dataname, True, 32, w_vectorizer, unit_length=2**args.down_t)
|
| 44 |
+
|
| 45 |
+
##### ---- Network ---- #####
|
| 46 |
+
net = vqvae.HumanVQVAE(args, ## use args to define different parameters in different quantizers
|
| 47 |
+
args.nb_code,
|
| 48 |
+
args.code_dim,
|
| 49 |
+
args.output_emb_width,
|
| 50 |
+
args.down_t,
|
| 51 |
+
args.stride_t,
|
| 52 |
+
args.width,
|
| 53 |
+
args.depth,
|
| 54 |
+
args.dilation_growth_rate,
|
| 55 |
+
args.vq_act,
|
| 56 |
+
args.vq_norm)
|
| 57 |
+
|
| 58 |
+
if args.resume_pth :
|
| 59 |
+
logger.info('loading checkpoint from {}'.format(args.resume_pth))
|
| 60 |
+
ckpt = torch.load(args.resume_pth, map_location='cpu')
|
| 61 |
+
net.load_state_dict(ckpt['net'], strict=True)
|
| 62 |
+
net.train()
|
| 63 |
+
net.cuda()
|
| 64 |
+
|
| 65 |
+
fid = []
|
| 66 |
+
div = []
|
| 67 |
+
top1 = []
|
| 68 |
+
top2 = []
|
| 69 |
+
top3 = []
|
| 70 |
+
matching = []
|
| 71 |
+
repeat_time = 20
|
| 72 |
+
for i in range(repeat_time):
|
| 73 |
+
best_fid, best_iter, best_div, best_top1, best_top2, best_top3, best_matching, writer, logger = eval_trans.evaluation_vqvae(args.out_dir, val_loader, net, logger, writer, 0, best_fid=1000, best_iter=0, best_div=100, best_top1=0, best_top2=0, best_top3=0, best_matching=100, eval_wrapper=eval_wrapper, draw=False, save=False, savenpy=(i==0))
|
| 74 |
+
fid.append(best_fid)
|
| 75 |
+
div.append(best_div)
|
| 76 |
+
top1.append(best_top1)
|
| 77 |
+
top2.append(best_top2)
|
| 78 |
+
top3.append(best_top3)
|
| 79 |
+
matching.append(best_matching)
|
| 80 |
+
print('final result:')
|
| 81 |
+
print('fid: ', sum(fid)/repeat_time)
|
| 82 |
+
print('div: ', sum(div)/repeat_time)
|
| 83 |
+
print('top1: ', sum(top1)/repeat_time)
|
| 84 |
+
print('top2: ', sum(top2)/repeat_time)
|
| 85 |
+
print('top3: ', sum(top3)/repeat_time)
|
| 86 |
+
print('matching: ', sum(matching)/repeat_time)
|
| 87 |
+
|
| 88 |
+
fid = np.array(fid)
|
| 89 |
+
div = np.array(div)
|
| 90 |
+
top1 = np.array(top1)
|
| 91 |
+
top2 = np.array(top2)
|
| 92 |
+
top3 = np.array(top3)
|
| 93 |
+
matching = np.array(matching)
|
| 94 |
+
msg_final = f"FID. {np.mean(fid):.3f}, conf. {np.std(fid)*1.96/np.sqrt(repeat_time):.3f}, Diversity. {np.mean(div):.3f}, conf. {np.std(div)*1.96/np.sqrt(repeat_time):.3f}, TOP1. {np.mean(top1):.3f}, conf. {np.std(top1)*1.96/np.sqrt(repeat_time):.3f}, TOP2. {np.mean(top2):.3f}, conf. {np.std(top2)*1.96/np.sqrt(repeat_time):.3f}, TOP3. {np.mean(top3):.3f}, conf. {np.std(top3)*1.96/np.sqrt(repeat_time):.3f}, Matching. {np.mean(matching):.3f}, conf. {np.std(matching)*1.96/np.sqrt(repeat_time):.3f}"
|
| 95 |
+
logger.info(msg_final)
|
app.py
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
import OpenGL.GL as gl
|
| 4 |
+
# os.environ["PYOPENGL_PLATFORM"] = "egl"
|
| 5 |
+
os.environ["MESA_GL_VERSION_OVERRIDE"] = "4.1"
|
| 6 |
+
# os.system('pip install /home/user/app/pyrender')
|
| 7 |
+
|
| 8 |
+
sys.argv = ['VQ-Trans/GPT_eval_multi.py']
|
| 9 |
+
os.chdir('VQ-Trans')
|
| 10 |
+
|
| 11 |
+
sys.path.append('./VQ-Trans')
|
| 12 |
+
sys.path.append('./pyrender')
|
| 13 |
+
|
| 14 |
+
import options.option_transformer as option_trans
|
| 15 |
+
from huggingface_hub import snapshot_download
|
| 16 |
+
model_path = snapshot_download(repo_id="vumichien/T2M-GPT")
|
| 17 |
+
|
| 18 |
+
args = option_trans.get_args_parser()
|
| 19 |
+
|
| 20 |
+
args.dataname = 't2m'
|
| 21 |
+
args.resume_pth = f'{model_path}/VQVAE/net_last.pth'
|
| 22 |
+
args.resume_trans = f'{model_path}/VQTransformer_corruption05/net_best_fid.pth'
|
| 23 |
+
args.down_t = 2
|
| 24 |
+
args.depth = 3
|
| 25 |
+
args.block_size = 51
|
| 26 |
+
|
| 27 |
+
import clip
|
| 28 |
+
import torch
|
| 29 |
+
import numpy as np
|
| 30 |
+
import models.vqvae as vqvae
|
| 31 |
+
import models.t2m_trans as trans
|
| 32 |
+
from utils.motion_process import recover_from_ric
|
| 33 |
+
import visualization.plot_3d_global as plot_3d
|
| 34 |
+
from models.rotation2xyz import Rotation2xyz
|
| 35 |
+
import numpy as np
|
| 36 |
+
from trimesh import Trimesh
|
| 37 |
+
import gc
|
| 38 |
+
|
| 39 |
+
import torch
|
| 40 |
+
from visualize.simplify_loc2rot import joints2smpl
|
| 41 |
+
import pyrender
|
| 42 |
+
# import matplotlib.pyplot as plt
|
| 43 |
+
|
| 44 |
+
import io
|
| 45 |
+
import imageio
|
| 46 |
+
from shapely import geometry
|
| 47 |
+
import trimesh
|
| 48 |
+
from pyrender.constants import RenderFlags
|
| 49 |
+
import math
|
| 50 |
+
# import ffmpeg
|
| 51 |
+
# from PIL import Image
|
| 52 |
+
import hashlib
|
| 53 |
+
import gradio as gr
|
| 54 |
+
import moviepy.editor as mp
|
| 55 |
+
from datetime import datetime
|
| 56 |
+
|
| 57 |
+
## load clip model and datasets
|
| 58 |
+
is_cuda = torch.cuda.is_available()
|
| 59 |
+
device = torch.device("cuda" if is_cuda else "cpu")
|
| 60 |
+
print(device)
|
| 61 |
+
clip_model, clip_preprocess = clip.load("ViT-B/32", device=device, jit=False, download_root='./') # Must set jit=False for training
|
| 62 |
+
|
| 63 |
+
if is_cuda:
|
| 64 |
+
clip.model.convert_weights(clip_model)
|
| 65 |
+
|
| 66 |
+
clip_model.eval()
|
| 67 |
+
for p in clip_model.parameters():
|
| 68 |
+
p.requires_grad = False
|
| 69 |
+
|
| 70 |
+
net = vqvae.HumanVQVAE(args, ## use args to define different parameters in different quantizers
|
| 71 |
+
args.nb_code,
|
| 72 |
+
args.code_dim,
|
| 73 |
+
args.output_emb_width,
|
| 74 |
+
args.down_t,
|
| 75 |
+
args.stride_t,
|
| 76 |
+
args.width,
|
| 77 |
+
args.depth,
|
| 78 |
+
args.dilation_growth_rate)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
trans_encoder = trans.Text2Motion_Transformer(num_vq=args.nb_code,
|
| 82 |
+
embed_dim=1024,
|
| 83 |
+
clip_dim=args.clip_dim,
|
| 84 |
+
block_size=args.block_size,
|
| 85 |
+
num_layers=9,
|
| 86 |
+
n_head=16,
|
| 87 |
+
drop_out_rate=args.drop_out_rate,
|
| 88 |
+
fc_rate=args.ff_rate)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
print('loading checkpoint from {}'.format(args.resume_pth))
|
| 92 |
+
ckpt = torch.load(args.resume_pth, map_location='cpu')
|
| 93 |
+
net.load_state_dict(ckpt['net'], strict=True)
|
| 94 |
+
net.eval()
|
| 95 |
+
|
| 96 |
+
print('loading transformer checkpoint from {}'.format(args.resume_trans))
|
| 97 |
+
ckpt = torch.load(args.resume_trans, map_location='cpu')
|
| 98 |
+
trans_encoder.load_state_dict(ckpt['trans'], strict=True)
|
| 99 |
+
trans_encoder.eval()
|
| 100 |
+
|
| 101 |
+
mean = torch.from_numpy(np.load(f'{model_path}/meta/mean.npy'))
|
| 102 |
+
std = torch.from_numpy(np.load(f'{model_path}/meta/std.npy'))
|
| 103 |
+
|
| 104 |
+
if is_cuda:
|
| 105 |
+
net.cuda()
|
| 106 |
+
trans_encoder.cuda()
|
| 107 |
+
mean = mean.cuda()
|
| 108 |
+
std = std.cuda()
|
| 109 |
+
|
| 110 |
+
def ensure_directory(path):
|
| 111 |
+
"""Tạo thư mục nếu chưa tồn tại"""
|
| 112 |
+
if not os.path.exists(path):
|
| 113 |
+
os.makedirs(path)
|
| 114 |
+
print(f"Created directory: {path}")
|
| 115 |
+
|
| 116 |
+
def get_output_path(output_dir, filename, extension):
|
| 117 |
+
"""Tạo đường dẫn đầy đủ cho file output"""
|
| 118 |
+
ensure_directory(output_dir)
|
| 119 |
+
if not filename.endswith(extension):
|
| 120 |
+
filename += extension
|
| 121 |
+
return os.path.join(output_dir, filename)
|
| 122 |
+
|
| 123 |
+
def render(motions, output_dir='output', filename='results', device_id=0):
|
| 124 |
+
"""
|
| 125 |
+
Render motion với tùy chọn thư mục và tên file
|
| 126 |
+
|
| 127 |
+
Args:
|
| 128 |
+
motions: Motion data
|
| 129 |
+
output_dir: Thư mục lưu kết quả (mặc định: 'output')
|
| 130 |
+
filename: Tên file không có extension (mặc định: 'results')
|
| 131 |
+
device_id: GPU device ID
|
| 132 |
+
"""
|
| 133 |
+
frames, njoints, nfeats = motions.shape
|
| 134 |
+
MINS = motions.min(axis=0).min(axis=0)
|
| 135 |
+
MAXS = motions.max(axis=0).max(axis=0)
|
| 136 |
+
|
| 137 |
+
height_offset = MINS[1]
|
| 138 |
+
motions[:, :, 1] -= height_offset
|
| 139 |
+
trajec = motions[:, 0, [0, 2]]
|
| 140 |
+
is_cuda = torch.cuda.is_available()
|
| 141 |
+
j2s = joints2smpl(num_frames=frames, device_id=0, cuda=is_cuda)
|
| 142 |
+
rot2xyz = Rotation2xyz(device=device)
|
| 143 |
+
faces = rot2xyz.smpl_model.faces
|
| 144 |
+
|
| 145 |
+
# Tạo đường dẫn cho file .pt
|
| 146 |
+
pt_path = get_output_path(output_dir, f'{filename}_pred', '.pt')
|
| 147 |
+
|
| 148 |
+
if not os.path.exists(pt_path):
|
| 149 |
+
print(f'Running SMPLify, it may take a few minutes.')
|
| 150 |
+
motion_tensor, opt_dict = j2s.joint2smpl(motions)
|
| 151 |
+
|
| 152 |
+
vertices = rot2xyz(torch.tensor(motion_tensor).clone(), mask=None,
|
| 153 |
+
pose_rep='rot6d', translation=True, glob=True,
|
| 154 |
+
jointstype='vertices',
|
| 155 |
+
vertstrans=True)
|
| 156 |
+
vertices = vertices.detach().cpu()
|
| 157 |
+
torch.save(vertices, pt_path)
|
| 158 |
+
else:
|
| 159 |
+
vertices = torch.load(pt_path)
|
| 160 |
+
|
| 161 |
+
frames = vertices.shape[3]
|
| 162 |
+
print(vertices.shape)
|
| 163 |
+
MINS = torch.min(torch.min(vertices[0], axis=0)[0], axis=1)[0]
|
| 164 |
+
MAXS = torch.max(torch.max(vertices[0], axis=0)[0], axis=1)[0]
|
| 165 |
+
|
| 166 |
+
out_list = []
|
| 167 |
+
|
| 168 |
+
minx = MINS[0] - 0.5
|
| 169 |
+
maxx = MAXS[0] + 0.5
|
| 170 |
+
minz = MINS[2] - 0.5
|
| 171 |
+
maxz = MAXS[2] + 0.5
|
| 172 |
+
polygon = geometry.Polygon([[minx, minz], [minx, maxz], [maxx, maxz], [maxx, minz]])
|
| 173 |
+
polygon_mesh = trimesh.creation.extrude_polygon(polygon, 1e-5)
|
| 174 |
+
|
| 175 |
+
vid = []
|
| 176 |
+
for i in range(frames):
|
| 177 |
+
if i % 10 == 0:
|
| 178 |
+
print(f"Processing frame {i}/{frames}")
|
| 179 |
+
|
| 180 |
+
mesh = Trimesh(vertices=vertices[0, :, :, i].squeeze().tolist(), faces=faces)
|
| 181 |
+
|
| 182 |
+
base_color = (0.11, 0.53, 0.8, 0.5)
|
| 183 |
+
material = pyrender.MetallicRoughnessMaterial(
|
| 184 |
+
metallicFactor=0.7,
|
| 185 |
+
alphaMode='OPAQUE',
|
| 186 |
+
baseColorFactor=base_color
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
mesh = pyrender.Mesh.from_trimesh(mesh, material=material)
|
| 190 |
+
|
| 191 |
+
polygon_mesh.visual.face_colors = [0, 0, 0, 0.21]
|
| 192 |
+
polygon_render = pyrender.Mesh.from_trimesh(polygon_mesh, smooth=False)
|
| 193 |
+
|
| 194 |
+
bg_color = [1, 1, 1, 0.8]
|
| 195 |
+
scene = pyrender.Scene(bg_color=bg_color, ambient_light=(0.4, 0.4, 0.4))
|
| 196 |
+
|
| 197 |
+
sx, sy, tx, ty = [0.75, 0.75, 0, 0.10]
|
| 198 |
+
|
| 199 |
+
camera = pyrender.PerspectiveCamera(yfov=(np.pi / 3.0))
|
| 200 |
+
light = pyrender.DirectionalLight(color=[1,1,1], intensity=300)
|
| 201 |
+
|
| 202 |
+
scene.add(mesh)
|
| 203 |
+
|
| 204 |
+
c = np.pi / 2
|
| 205 |
+
scene.add(polygon_render, pose=np.array([[ 1, 0, 0, 0],
|
| 206 |
+
[ 0, np.cos(c), -np.sin(c), MINS[1].cpu().numpy()],
|
| 207 |
+
[ 0, np.sin(c), np.cos(c), 0],
|
| 208 |
+
[ 0, 0, 0, 1]]))
|
| 209 |
+
|
| 210 |
+
light_pose = np.eye(4)
|
| 211 |
+
light_pose[:3, 3] = [0, -1, 1]
|
| 212 |
+
scene.add(light, pose=light_pose.copy())
|
| 213 |
+
|
| 214 |
+
light_pose[:3, 3] = [0, 1, 1]
|
| 215 |
+
scene.add(light, pose=light_pose.copy())
|
| 216 |
+
|
| 217 |
+
light_pose[:3, 3] = [1, 1, 2]
|
| 218 |
+
scene.add(light, pose=light_pose.copy())
|
| 219 |
+
|
| 220 |
+
c = -np.pi / 6
|
| 221 |
+
scene.add(camera, pose=[[ 1, 0, 0, (minx+maxx).cpu().numpy()/2],
|
| 222 |
+
[ 0, np.cos(c), -np.sin(c), 1.5],
|
| 223 |
+
[ 0, np.sin(c), np.cos(c), max(4, minz.cpu().numpy()+(1.5-MINS[1].cpu().numpy())*2, (maxx-minx).cpu().numpy())],
|
| 224 |
+
[ 0, 0, 0, 1]
|
| 225 |
+
])
|
| 226 |
+
|
| 227 |
+
r = pyrender.OffscreenRenderer(960, 960)
|
| 228 |
+
color, _ = r.render(scene, flags=RenderFlags.RGBA)
|
| 229 |
+
vid.append(color)
|
| 230 |
+
r.delete()
|
| 231 |
+
|
| 232 |
+
out = np.stack(vid, axis=0)
|
| 233 |
+
|
| 234 |
+
# Tạo đường dẫn cho file GIF và MP4
|
| 235 |
+
gif_path = get_output_path(output_dir, filename, '.gif')
|
| 236 |
+
mp4_path = get_output_path(output_dir, filename, '.mp4')
|
| 237 |
+
|
| 238 |
+
imageio.mimwrite(gif_path, out, duration=50)
|
| 239 |
+
out_video = mp.VideoFileClip(gif_path)
|
| 240 |
+
out_video.write_videofile(mp4_path)
|
| 241 |
+
|
| 242 |
+
print(f"Results saved to: {mp4_path}")
|
| 243 |
+
|
| 244 |
+
del out, vertices
|
| 245 |
+
return mp4_path
|
| 246 |
+
|
| 247 |
+
def predict(clip_text, method='fast', output_dir='output', filename=''):
|
| 248 |
+
"""
|
| 249 |
+
Predict motion with custom output settings
|
| 250 |
+
|
| 251 |
+
Args:
|
| 252 |
+
clip_text: Text prompt
|
| 253 |
+
method: 'fast' or 'slow'
|
| 254 |
+
output_dir: Output directory
|
| 255 |
+
filename: Custom filename (if empty, will use hash or timestamp)
|
| 256 |
+
"""
|
| 257 |
+
gc.collect()
|
| 258 |
+
print('prompt text instruction: {}'.format(clip_text))
|
| 259 |
+
|
| 260 |
+
# Tạo tên file nếu không được cung cấp
|
| 261 |
+
if not filename.strip():
|
| 262 |
+
if method == 'fast':
|
| 263 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 264 |
+
filename = f"motion_{timestamp}"
|
| 265 |
+
else:
|
| 266 |
+
filename = hashlib.md5(clip_text.encode()).hexdigest()
|
| 267 |
+
|
| 268 |
+
# Xử lý text với CLIP
|
| 269 |
+
if torch.cuda.is_available():
|
| 270 |
+
text = clip.tokenize([clip_text], truncate=True).cuda()
|
| 271 |
+
else:
|
| 272 |
+
text = clip.tokenize([clip_text], truncate=True)
|
| 273 |
+
|
| 274 |
+
feat_clip_text = clip_model.encode_text(text).float()
|
| 275 |
+
index_motion = trans_encoder.sample(feat_clip_text[0:1], False)
|
| 276 |
+
pred_pose = net.forward_decoder(index_motion)
|
| 277 |
+
pred_xyz = recover_from_ric((pred_pose*std+mean).float(), 22)
|
| 278 |
+
|
| 279 |
+
if method == 'fast':
|
| 280 |
+
xyz = pred_xyz.reshape(1, -1, 22, 3)
|
| 281 |
+
|
| 282 |
+
# Tạo đường dẫn cho fast method
|
| 283 |
+
gif_path = get_output_path(output_dir, filename, '.gif')
|
| 284 |
+
mp4_path = get_output_path(output_dir, filename, '.mp4')
|
| 285 |
+
|
| 286 |
+
pose_vis = plot_3d.draw_to_batch(xyz.detach().cpu().numpy(), title_batch=None, outname=[gif_path])
|
| 287 |
+
out_video = mp.VideoFileClip(gif_path)
|
| 288 |
+
out_video.write_videofile(mp4_path)
|
| 289 |
+
|
| 290 |
+
print(f"Fast render results saved to: {mp4_path}")
|
| 291 |
+
return mp4_path
|
| 292 |
+
|
| 293 |
+
elif method == 'slow':
|
| 294 |
+
output_path = render(pred_xyz.detach().cpu().numpy().squeeze(axis=0),
|
| 295 |
+
output_dir=output_dir,
|
| 296 |
+
filename=filename,
|
| 297 |
+
device_id=0)
|
| 298 |
+
return output_path
|
| 299 |
+
|
| 300 |
+
# ---- Gradio Layout -----
|
| 301 |
+
video_out = gr.Video(label="Motion", mirror_webcam=False, interactive=False)
|
| 302 |
+
demo = gr.Blocks()
|
| 303 |
+
demo.encrypt = False
|
| 304 |
+
|
| 305 |
+
with demo:
|
| 306 |
+
gr.Markdown('''
|
| 307 |
+
<div>
|
| 308 |
+
<h1 style='text-align: center'>Generating Human Motion from Textual Descriptions (T2M-GPT)</h1>
|
| 309 |
+
This space uses <a href='https://mael-zys.github.io/T2M-GPT/' target='_blank'><b>T2M-GPT models</b></a> based on Vector Quantised-Variational AutoEncoder (VQ-VAE) and Generative Pre-trained Transformer (GPT) for human motion generation from textural descriptions🤗
|
| 310 |
+
</div>
|
| 311 |
+
''')
|
| 312 |
+
with gr.Row():
|
| 313 |
+
with gr.Column():
|
| 314 |
+
gr.Markdown('''
|
| 315 |
+
<figure>
|
| 316 |
+
<img src="https://huggingface.co/vumichien/T2M-GPT/resolve/main/demo_slow1.gif" alt="Demo Slow", width="425", height=480/>
|
| 317 |
+
<figcaption> a man starts off in an up right position with botg arms extended out by his sides, he then brings his arms down to his body and claps his hands together. after this he wals down amd the the left where he proceeds to sit on a seat
|
| 318 |
+
</figcaption>
|
| 319 |
+
</figure>
|
| 320 |
+
''')
|
| 321 |
+
with gr.Column():
|
| 322 |
+
gr.Markdown('''
|
| 323 |
+
<figure>
|
| 324 |
+
<img src="https://huggingface.co/vumichien/T2M-GPT/resolve/main/demo_slow2.gif" alt="Demo Slow 2", width="425", height=480/>
|
| 325 |
+
<figcaption> a person puts their hands together, leans forwards slightly then swings the arms from right to left
|
| 326 |
+
</figcaption>
|
| 327 |
+
</figure>
|
| 328 |
+
''')
|
| 329 |
+
with gr.Column():
|
| 330 |
+
gr.Markdown('''
|
| 331 |
+
<figure>
|
| 332 |
+
<img src="https://huggingface.co/vumichien/T2M-GPT/resolve/main/demo_slow3.gif" alt="Demo Slow 3", width="425", height=480/>
|
| 333 |
+
<figcaption> a man is practicing the waltz with a partner
|
| 334 |
+
</figcaption>
|
| 335 |
+
</figure>
|
| 336 |
+
''')
|
| 337 |
+
with gr.Row():
|
| 338 |
+
with gr.Column():
|
| 339 |
+
gr.Markdown('''
|
| 340 |
+
### Generate human motion by **T2M-GPT**
|
| 341 |
+
##### Step 1. Give prompt text describing human motion
|
| 342 |
+
##### Step 2. Choose method to render output (Fast: Sketch skeleton; Slow: SMPL mesh)
|
| 343 |
+
##### Step 3. Specify output directory and filename (optional)
|
| 344 |
+
##### Step 4. Generate output and enjoy
|
| 345 |
+
''')
|
| 346 |
+
with gr.Column():
|
| 347 |
+
with gr.Row():
|
| 348 |
+
text_prompt = gr.Textbox(label="Text prompt", lines=1, interactive=True)
|
| 349 |
+
method = gr.Dropdown(["slow", "fast"], label="Method", value="slow")
|
| 350 |
+
with gr.Row():
|
| 351 |
+
output_dir = gr.Textbox(label="Output Directory", value="output", interactive=True)
|
| 352 |
+
filename = gr.Textbox(label="Filename (without extension)", placeholder="Leave empty for auto-generated name", interactive=True)
|
| 353 |
+
with gr.Row():
|
| 354 |
+
generate_btn = gr.Button("Generate")
|
| 355 |
+
generate_btn.click(predict, [text_prompt, method, output_dir, filename], [video_out], api_name="generate")
|
| 356 |
+
with gr.Row():
|
| 357 |
+
video_out.render()
|
| 358 |
+
with gr.Row():
|
| 359 |
+
gr.Markdown('''
|
| 360 |
+
### You can test by following examples:
|
| 361 |
+
''')
|
| 362 |
+
examples = gr.Examples(
|
| 363 |
+
examples=[
|
| 364 |
+
["a person jogs in place, slowly at first, then increases speed. they then back up and squat down.", "slow", "output", "jogging_motion"],
|
| 365 |
+
["a man steps forward and does a handstand", "slow", "output", "handstand_motion"],
|
| 366 |
+
["a man rises from the ground, walks in a circle and sits back down on the ground", "slow", "output", "circle_walk"],
|
| 367 |
+
["a man starts off in an up right position with botg arms extended out by his sides, he then brings his arms down to his body and claps his hands together. after this he wals down amd the the left where he proceeds to sit on a seat", "slow", "output", "clap_and_sit"],
|
| 368 |
+
["a person puts their hands together, leans forwards slightly then swings the arms from right to left","slow", "output", "swing_arms"],
|
| 369 |
+
["a man is practicing the waltz with a partner","slow", "output", "waltz_dance"],
|
| 370 |
+
],
|
| 371 |
+
label="Examples",
|
| 372 |
+
inputs=[text_prompt, method, output_dir, filename],
|
| 373 |
+
outputs=[video_out],
|
| 374 |
+
fn=predict,
|
| 375 |
+
cache_examples=True,
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
demo.launch(debug=True, server_name="0.0.0.0", server_port=8000, inbrowser=True, share=True)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/.gitattributes
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Generate Human Motion
|
| 3 |
+
emoji: 🏃
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 4.36.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
tags:
|
| 12 |
+
- making-demos
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualization/plot_3d_global.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import matplotlib.pyplot as plt
|
| 3 |
+
import numpy as np
|
| 4 |
+
import io
|
| 5 |
+
import matplotlib
|
| 6 |
+
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
| 7 |
+
import mpl_toolkits.mplot3d.axes3d as p3
|
| 8 |
+
from textwrap import wrap
|
| 9 |
+
import imageio
|
| 10 |
+
|
| 11 |
+
def plot_3d_motion(args, figsize=(10, 10), fps=120, radius=4):
|
| 12 |
+
matplotlib.use('Agg')
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
joints, out_name, title = args
|
| 16 |
+
|
| 17 |
+
data = joints.copy().reshape(len(joints), -1, 3)
|
| 18 |
+
|
| 19 |
+
nb_joints = joints.shape[1]
|
| 20 |
+
smpl_kinetic_chain = [[0, 11, 12, 13, 14, 15], [0, 16, 17, 18, 19, 20], [0, 1, 2, 3, 4], [3, 5, 6, 7], [3, 8, 9, 10]] if nb_joints == 21 else [[0, 2, 5, 8, 11], [0, 1, 4, 7, 10], [0, 3, 6, 9, 12, 15], [9, 14, 17, 19, 21], [9, 13, 16, 18, 20]]
|
| 21 |
+
limits = 1000 if nb_joints == 21 else 2
|
| 22 |
+
MINS = data.min(axis=0).min(axis=0)
|
| 23 |
+
MAXS = data.max(axis=0).max(axis=0)
|
| 24 |
+
colors = ['red', 'blue', 'black', 'red', 'blue',
|
| 25 |
+
'darkblue', 'darkblue', 'darkblue', 'darkblue', 'darkblue',
|
| 26 |
+
'darkred', 'darkred', 'darkred', 'darkred', 'darkred']
|
| 27 |
+
frame_number = data.shape[0]
|
| 28 |
+
# print(data.shape)
|
| 29 |
+
|
| 30 |
+
height_offset = MINS[1]
|
| 31 |
+
data[:, :, 1] -= height_offset
|
| 32 |
+
trajec = data[:, 0, [0, 2]]
|
| 33 |
+
|
| 34 |
+
data[..., 0] -= data[:, 0:1, 0]
|
| 35 |
+
data[..., 2] -= data[:, 0:1, 2]
|
| 36 |
+
|
| 37 |
+
def update(index):
|
| 38 |
+
|
| 39 |
+
def init():
|
| 40 |
+
ax.set_xlim(-limits, limits)
|
| 41 |
+
ax.set_ylim(-limits, limits)
|
| 42 |
+
ax.set_zlim(0, limits)
|
| 43 |
+
ax.grid(b=False)
|
| 44 |
+
def plot_xzPlane(minx, maxx, miny, minz, maxz):
|
| 45 |
+
## Plot a plane XZ
|
| 46 |
+
verts = [
|
| 47 |
+
[minx, miny, minz],
|
| 48 |
+
[minx, miny, maxz],
|
| 49 |
+
[maxx, miny, maxz],
|
| 50 |
+
[maxx, miny, minz]
|
| 51 |
+
]
|
| 52 |
+
xz_plane = Poly3DCollection([verts])
|
| 53 |
+
xz_plane.set_facecolor((0.5, 0.5, 0.5, 0.5))
|
| 54 |
+
ax.add_collection3d(xz_plane)
|
| 55 |
+
fig = plt.figure(figsize=(480/96., 320/96.), dpi=96) if nb_joints == 21 else plt.figure(figsize=(10, 10), dpi=96)
|
| 56 |
+
if title is not None :
|
| 57 |
+
wraped_title = '\n'.join(wrap(title, 40))
|
| 58 |
+
fig.suptitle(wraped_title, fontsize=16)
|
| 59 |
+
ax = p3.Axes3D(fig)
|
| 60 |
+
|
| 61 |
+
init()
|
| 62 |
+
|
| 63 |
+
ax.lines = []
|
| 64 |
+
ax.collections = []
|
| 65 |
+
ax.view_init(elev=110, azim=-90)
|
| 66 |
+
ax.dist = 7.5
|
| 67 |
+
# ax =
|
| 68 |
+
plot_xzPlane(MINS[0] - trajec[index, 0], MAXS[0] - trajec[index, 0], 0, MINS[2] - trajec[index, 1],
|
| 69 |
+
MAXS[2] - trajec[index, 1])
|
| 70 |
+
# ax.scatter(data[index, :22, 0], data[index, :22, 1], data[index, :22, 2], color='black', s=3)
|
| 71 |
+
|
| 72 |
+
if index > 1:
|
| 73 |
+
ax.plot3D(trajec[:index, 0] - trajec[index, 0], np.zeros_like(trajec[:index, 0]),
|
| 74 |
+
trajec[:index, 1] - trajec[index, 1], linewidth=1.0,
|
| 75 |
+
color='blue')
|
| 76 |
+
# ax = plot_xzPlane(ax, MINS[0], MAXS[0], 0, MINS[2], MAXS[2])
|
| 77 |
+
|
| 78 |
+
for i, (chain, color) in enumerate(zip(smpl_kinetic_chain, colors)):
|
| 79 |
+
# print(color)
|
| 80 |
+
if i < 5:
|
| 81 |
+
linewidth = 4.0
|
| 82 |
+
else:
|
| 83 |
+
linewidth = 2.0
|
| 84 |
+
ax.plot3D(data[index, chain, 0], data[index, chain, 1], data[index, chain, 2], linewidth=linewidth,
|
| 85 |
+
color=color)
|
| 86 |
+
# print(trajec[:index, 0].shape)
|
| 87 |
+
|
| 88 |
+
plt.axis('off')
|
| 89 |
+
ax.set_xticklabels([])
|
| 90 |
+
ax.set_yticklabels([])
|
| 91 |
+
ax.set_zticklabels([])
|
| 92 |
+
|
| 93 |
+
if out_name is not None :
|
| 94 |
+
plt.savefig(out_name, dpi=96)
|
| 95 |
+
plt.close()
|
| 96 |
+
|
| 97 |
+
else :
|
| 98 |
+
io_buf = io.BytesIO()
|
| 99 |
+
fig.savefig(io_buf, format='raw', dpi=96)
|
| 100 |
+
io_buf.seek(0)
|
| 101 |
+
# print(fig.bbox.bounds)
|
| 102 |
+
arr = np.reshape(np.frombuffer(io_buf.getvalue(), dtype=np.uint8),
|
| 103 |
+
newshape=(int(fig.bbox.bounds[3]), int(fig.bbox.bounds[2]), -1))
|
| 104 |
+
io_buf.close()
|
| 105 |
+
plt.close()
|
| 106 |
+
return arr
|
| 107 |
+
|
| 108 |
+
out = []
|
| 109 |
+
for i in range(frame_number) :
|
| 110 |
+
out.append(update(i))
|
| 111 |
+
out = np.stack(out, axis=0)
|
| 112 |
+
return torch.from_numpy(out)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def draw_to_batch(smpl_joints_batch, title_batch=None, outname=None) :
|
| 116 |
+
|
| 117 |
+
batch_size = len(smpl_joints_batch)
|
| 118 |
+
out = []
|
| 119 |
+
for i in range(batch_size) :
|
| 120 |
+
out.append(plot_3d_motion([smpl_joints_batch[i], None, title_batch[i] if title_batch is not None else None]))
|
| 121 |
+
if outname is not None:
|
| 122 |
+
imageio.mimsave(outname[i], np.array(out[-1]), fps=20)
|
| 123 |
+
out = torch.stack(out, axis=0)
|
| 124 |
+
return out
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/config.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
# Map joints Name to SMPL joints idx
|
| 4 |
+
JOINT_MAP = {
|
| 5 |
+
'MidHip': 0,
|
| 6 |
+
'LHip': 1, 'LKnee': 4, 'LAnkle': 7, 'LFoot': 10,
|
| 7 |
+
'RHip': 2, 'RKnee': 5, 'RAnkle': 8, 'RFoot': 11,
|
| 8 |
+
'LShoulder': 16, 'LElbow': 18, 'LWrist': 20, 'LHand': 22,
|
| 9 |
+
'RShoulder': 17, 'RElbow': 19, 'RWrist': 21, 'RHand': 23,
|
| 10 |
+
'spine1': 3, 'spine2': 6, 'spine3': 9, 'Neck': 12, 'Head': 15,
|
| 11 |
+
'LCollar':13, 'Rcollar' :14,
|
| 12 |
+
'Nose':24, 'REye':26, 'LEye':26, 'REar':27, 'LEar':28,
|
| 13 |
+
'LHeel': 31, 'RHeel': 34,
|
| 14 |
+
'OP RShoulder': 17, 'OP LShoulder': 16,
|
| 15 |
+
'OP RHip': 2, 'OP LHip': 1,
|
| 16 |
+
'OP Neck': 12,
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
full_smpl_idx = range(24)
|
| 20 |
+
key_smpl_idx = [0, 1, 4, 7, 2, 5, 8, 17, 19, 21, 16, 18, 20]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
AMASS_JOINT_MAP = {
|
| 24 |
+
'MidHip': 0,
|
| 25 |
+
'LHip': 1, 'LKnee': 4, 'LAnkle': 7, 'LFoot': 10,
|
| 26 |
+
'RHip': 2, 'RKnee': 5, 'RAnkle': 8, 'RFoot': 11,
|
| 27 |
+
'LShoulder': 16, 'LElbow': 18, 'LWrist': 20,
|
| 28 |
+
'RShoulder': 17, 'RElbow': 19, 'RWrist': 21,
|
| 29 |
+
'spine1': 3, 'spine2': 6, 'spine3': 9, 'Neck': 12, 'Head': 15,
|
| 30 |
+
'LCollar':13, 'Rcollar' :14,
|
| 31 |
+
}
|
| 32 |
+
amass_idx = range(22)
|
| 33 |
+
amass_smpl_idx = range(22)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
SMPL_MODEL_DIR = "./body_models/"
|
| 37 |
+
GMM_MODEL_DIR = "./visualize/joints2smpl/smpl_models/"
|
| 38 |
+
SMPL_MEAN_FILE = "./visualize/joints2smpl/smpl_models/neutral_smpl_mean_params.h5"
|
| 39 |
+
# for collsion
|
| 40 |
+
Part_Seg_DIR = "./visualize/joints2smpl/smpl_models/smplx_parts_segm.pkl"
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/customloss.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn.functional as F
|
| 3 |
+
from visualize.joints2smpl.src import config
|
| 4 |
+
|
| 5 |
+
# Guassian
|
| 6 |
+
def gmof(x, sigma):
|
| 7 |
+
"""
|
| 8 |
+
Geman-McClure error function
|
| 9 |
+
"""
|
| 10 |
+
x_squared = x ** 2
|
| 11 |
+
sigma_squared = sigma ** 2
|
| 12 |
+
return (sigma_squared * x_squared) / (sigma_squared + x_squared)
|
| 13 |
+
|
| 14 |
+
# angle prior
|
| 15 |
+
def angle_prior(pose):
|
| 16 |
+
"""
|
| 17 |
+
Angle prior that penalizes unnatural bending of the knees and elbows
|
| 18 |
+
"""
|
| 19 |
+
# We subtract 3 because pose does not include the global rotation of the model
|
| 20 |
+
return torch.exp(
|
| 21 |
+
pose[:, [55 - 3, 58 - 3, 12 - 3, 15 - 3]] * torch.tensor([1., -1., -1, -1.], device=pose.device)) ** 2
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def perspective_projection(points, rotation, translation,
|
| 25 |
+
focal_length, camera_center):
|
| 26 |
+
"""
|
| 27 |
+
This function computes the perspective projection of a set of points.
|
| 28 |
+
Input:
|
| 29 |
+
points (bs, N, 3): 3D points
|
| 30 |
+
rotation (bs, 3, 3): Camera rotation
|
| 31 |
+
translation (bs, 3): Camera translation
|
| 32 |
+
focal_length (bs,) or scalar: Focal length
|
| 33 |
+
camera_center (bs, 2): Camera center
|
| 34 |
+
"""
|
| 35 |
+
batch_size = points.shape[0]
|
| 36 |
+
K = torch.zeros([batch_size, 3, 3], device=points.device)
|
| 37 |
+
K[:, 0, 0] = focal_length
|
| 38 |
+
K[:, 1, 1] = focal_length
|
| 39 |
+
K[:, 2, 2] = 1.
|
| 40 |
+
K[:, :-1, -1] = camera_center
|
| 41 |
+
|
| 42 |
+
# Transform points
|
| 43 |
+
points = torch.einsum('bij,bkj->bki', rotation, points)
|
| 44 |
+
points = points + translation.unsqueeze(1)
|
| 45 |
+
|
| 46 |
+
# Apply perspective distortion
|
| 47 |
+
projected_points = points / points[:, :, -1].unsqueeze(-1)
|
| 48 |
+
|
| 49 |
+
# Apply camera intrinsics
|
| 50 |
+
projected_points = torch.einsum('bij,bkj->bki', K, projected_points)
|
| 51 |
+
|
| 52 |
+
return projected_points[:, :, :-1]
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def body_fitting_loss(body_pose, betas, model_joints, camera_t, camera_center,
|
| 56 |
+
joints_2d, joints_conf, pose_prior,
|
| 57 |
+
focal_length=5000, sigma=100, pose_prior_weight=4.78,
|
| 58 |
+
shape_prior_weight=5, angle_prior_weight=15.2,
|
| 59 |
+
output='sum'):
|
| 60 |
+
"""
|
| 61 |
+
Loss function for body fitting
|
| 62 |
+
"""
|
| 63 |
+
batch_size = body_pose.shape[0]
|
| 64 |
+
rotation = torch.eye(3, device=body_pose.device).unsqueeze(0).expand(batch_size, -1, -1)
|
| 65 |
+
|
| 66 |
+
projected_joints = perspective_projection(model_joints, rotation, camera_t,
|
| 67 |
+
focal_length, camera_center)
|
| 68 |
+
|
| 69 |
+
# Weighted robust reprojection error
|
| 70 |
+
reprojection_error = gmof(projected_joints - joints_2d, sigma)
|
| 71 |
+
reprojection_loss = (joints_conf ** 2) * reprojection_error.sum(dim=-1)
|
| 72 |
+
|
| 73 |
+
# Pose prior loss
|
| 74 |
+
pose_prior_loss = (pose_prior_weight ** 2) * pose_prior(body_pose, betas)
|
| 75 |
+
|
| 76 |
+
# Angle prior for knees and elbows
|
| 77 |
+
angle_prior_loss = (angle_prior_weight ** 2) * angle_prior(body_pose).sum(dim=-1)
|
| 78 |
+
|
| 79 |
+
# Regularizer to prevent betas from taking large values
|
| 80 |
+
shape_prior_loss = (shape_prior_weight ** 2) * (betas ** 2).sum(dim=-1)
|
| 81 |
+
|
| 82 |
+
total_loss = reprojection_loss.sum(dim=-1) + pose_prior_loss + angle_prior_loss + shape_prior_loss
|
| 83 |
+
|
| 84 |
+
if output == 'sum':
|
| 85 |
+
return total_loss.sum()
|
| 86 |
+
elif output == 'reprojection':
|
| 87 |
+
return reprojection_loss
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
# --- get camera fitting loss -----
|
| 91 |
+
def camera_fitting_loss(model_joints, camera_t, camera_t_est, camera_center,
|
| 92 |
+
joints_2d, joints_conf,
|
| 93 |
+
focal_length=5000, depth_loss_weight=100):
|
| 94 |
+
"""
|
| 95 |
+
Loss function for camera optimization.
|
| 96 |
+
"""
|
| 97 |
+
# Project model joints
|
| 98 |
+
batch_size = model_joints.shape[0]
|
| 99 |
+
rotation = torch.eye(3, device=model_joints.device).unsqueeze(0).expand(batch_size, -1, -1)
|
| 100 |
+
projected_joints = perspective_projection(model_joints, rotation, camera_t,
|
| 101 |
+
focal_length, camera_center)
|
| 102 |
+
|
| 103 |
+
# get the indexed four
|
| 104 |
+
op_joints = ['OP RHip', 'OP LHip', 'OP RShoulder', 'OP LShoulder']
|
| 105 |
+
op_joints_ind = [config.JOINT_MAP[joint] for joint in op_joints]
|
| 106 |
+
gt_joints = ['RHip', 'LHip', 'RShoulder', 'LShoulder']
|
| 107 |
+
gt_joints_ind = [config.JOINT_MAP[joint] for joint in gt_joints]
|
| 108 |
+
|
| 109 |
+
reprojection_error_op = (joints_2d[:, op_joints_ind] -
|
| 110 |
+
projected_joints[:, op_joints_ind]) ** 2
|
| 111 |
+
reprojection_error_gt = (joints_2d[:, gt_joints_ind] -
|
| 112 |
+
projected_joints[:, gt_joints_ind]) ** 2
|
| 113 |
+
|
| 114 |
+
# Check if for each example in the batch all 4 OpenPose detections are valid, otherwise use the GT detections
|
| 115 |
+
# OpenPose joints are more reliable for this task, so we prefer to use them if possible
|
| 116 |
+
is_valid = (joints_conf[:, op_joints_ind].min(dim=-1)[0][:, None, None] > 0).float()
|
| 117 |
+
reprojection_loss = (is_valid * reprojection_error_op + (1 - is_valid) * reprojection_error_gt).sum(dim=(1, 2))
|
| 118 |
+
|
| 119 |
+
# Loss that penalizes deviation from depth estimate
|
| 120 |
+
depth_loss = (depth_loss_weight ** 2) * (camera_t[:, 2] - camera_t_est[:, 2]) ** 2
|
| 121 |
+
|
| 122 |
+
total_loss = reprojection_loss + depth_loss
|
| 123 |
+
return total_loss.sum()
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
# #####--- body fitiing loss -----
|
| 128 |
+
def body_fitting_loss_3d(body_pose, preserve_pose,
|
| 129 |
+
betas, model_joints, camera_translation,
|
| 130 |
+
j3d, pose_prior,
|
| 131 |
+
joints3d_conf,
|
| 132 |
+
sigma=100, pose_prior_weight=4.78*1.5,
|
| 133 |
+
shape_prior_weight=5.0, angle_prior_weight=15.2,
|
| 134 |
+
joint_loss_weight=500.0,
|
| 135 |
+
pose_preserve_weight=0.0,
|
| 136 |
+
use_collision=False,
|
| 137 |
+
model_vertices=None, model_faces=None,
|
| 138 |
+
search_tree=None, pen_distance=None, filter_faces=None,
|
| 139 |
+
collision_loss_weight=1000
|
| 140 |
+
):
|
| 141 |
+
"""
|
| 142 |
+
Loss function for body fitting
|
| 143 |
+
"""
|
| 144 |
+
batch_size = body_pose.shape[0]
|
| 145 |
+
|
| 146 |
+
#joint3d_loss = (joint_loss_weight ** 2) * gmof((model_joints + camera_translation) - j3d, sigma).sum(dim=-1)
|
| 147 |
+
|
| 148 |
+
joint3d_error = gmof((model_joints + camera_translation) - j3d, sigma)
|
| 149 |
+
|
| 150 |
+
joint3d_loss_part = (joints3d_conf ** 2) * joint3d_error.sum(dim=-1)
|
| 151 |
+
joint3d_loss = ((joint_loss_weight ** 2) * joint3d_loss_part).sum(dim=-1)
|
| 152 |
+
|
| 153 |
+
# Pose prior loss
|
| 154 |
+
pose_prior_loss = (pose_prior_weight ** 2) * pose_prior(body_pose, betas)
|
| 155 |
+
# Angle prior for knees and elbows
|
| 156 |
+
angle_prior_loss = (angle_prior_weight ** 2) * angle_prior(body_pose).sum(dim=-1)
|
| 157 |
+
# Regularizer to prevent betas from taking large values
|
| 158 |
+
shape_prior_loss = (shape_prior_weight ** 2) * (betas ** 2).sum(dim=-1)
|
| 159 |
+
|
| 160 |
+
collision_loss = 0.0
|
| 161 |
+
# Calculate the loss due to interpenetration
|
| 162 |
+
if use_collision:
|
| 163 |
+
triangles = torch.index_select(
|
| 164 |
+
model_vertices, 1,
|
| 165 |
+
model_faces).view(batch_size, -1, 3, 3)
|
| 166 |
+
|
| 167 |
+
with torch.no_grad():
|
| 168 |
+
collision_idxs = search_tree(triangles)
|
| 169 |
+
|
| 170 |
+
# Remove unwanted collisions
|
| 171 |
+
if filter_faces is not None:
|
| 172 |
+
collision_idxs = filter_faces(collision_idxs)
|
| 173 |
+
|
| 174 |
+
if collision_idxs.ge(0).sum().item() > 0:
|
| 175 |
+
collision_loss = torch.sum(collision_loss_weight * pen_distance(triangles, collision_idxs))
|
| 176 |
+
|
| 177 |
+
pose_preserve_loss = (pose_preserve_weight ** 2) * ((body_pose - preserve_pose) ** 2).sum(dim=-1)
|
| 178 |
+
|
| 179 |
+
# print('joint3d_loss', joint3d_loss.shape)
|
| 180 |
+
# print('pose_prior_loss', pose_prior_loss.shape)
|
| 181 |
+
# print('angle_prior_loss', angle_prior_loss.shape)
|
| 182 |
+
# print('shape_prior_loss', shape_prior_loss.shape)
|
| 183 |
+
# print('collision_loss', collision_loss)
|
| 184 |
+
# print('pose_preserve_loss', pose_preserve_loss.shape)
|
| 185 |
+
|
| 186 |
+
total_loss = joint3d_loss + pose_prior_loss + angle_prior_loss + shape_prior_loss + collision_loss + pose_preserve_loss
|
| 187 |
+
|
| 188 |
+
return total_loss.sum()
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
# #####--- get camera fitting loss -----
|
| 192 |
+
def camera_fitting_loss_3d(model_joints, camera_t, camera_t_est,
|
| 193 |
+
j3d, joints_category="orig", depth_loss_weight=100.0):
|
| 194 |
+
"""
|
| 195 |
+
Loss function for camera optimization.
|
| 196 |
+
"""
|
| 197 |
+
model_joints = model_joints + camera_t
|
| 198 |
+
# # get the indexed four
|
| 199 |
+
# op_joints = ['OP RHip', 'OP LHip', 'OP RShoulder', 'OP LShoulder']
|
| 200 |
+
# op_joints_ind = [config.JOINT_MAP[joint] for joint in op_joints]
|
| 201 |
+
#
|
| 202 |
+
# j3d_error_loss = (j3d[:, op_joints_ind] -
|
| 203 |
+
# model_joints[:, op_joints_ind]) ** 2
|
| 204 |
+
|
| 205 |
+
gt_joints = ['RHip', 'LHip', 'RShoulder', 'LShoulder']
|
| 206 |
+
gt_joints_ind = [config.JOINT_MAP[joint] for joint in gt_joints]
|
| 207 |
+
|
| 208 |
+
if joints_category=="orig":
|
| 209 |
+
select_joints_ind = [config.JOINT_MAP[joint] for joint in gt_joints]
|
| 210 |
+
elif joints_category=="AMASS":
|
| 211 |
+
select_joints_ind = [config.AMASS_JOINT_MAP[joint] for joint in gt_joints]
|
| 212 |
+
else:
|
| 213 |
+
print("NO SUCH JOINTS CATEGORY!")
|
| 214 |
+
|
| 215 |
+
j3d_error_loss = (j3d[:, select_joints_ind] -
|
| 216 |
+
model_joints[:, gt_joints_ind]) ** 2
|
| 217 |
+
|
| 218 |
+
# Loss that penalizes deviation from depth estimate
|
| 219 |
+
depth_loss = (depth_loss_weight**2) * (camera_t - camera_t_est)**2
|
| 220 |
+
|
| 221 |
+
total_loss = j3d_error_loss + depth_loss
|
| 222 |
+
return total_loss.sum()
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/prior.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
# Max-Planck-Gesellschaft zur Förderung der Wissenschaften e.V. (MPG) is
|
| 4 |
+
# holder of all proprietary rights on this computer program.
|
| 5 |
+
# You can only use this computer program if you have closed
|
| 6 |
+
# a license agreement with MPG or you get the right to use the computer
|
| 7 |
+
# program from someone who is authorized to grant you that right.
|
| 8 |
+
# Any use of the computer program without a valid license is prohibited and
|
| 9 |
+
# liable to prosecution.
|
| 10 |
+
#
|
| 11 |
+
# Copyright©2019 Max-Planck-Gesellschaft zur Förderung
|
| 12 |
+
# der Wissenschaften e.V. (MPG). acting on behalf of its Max Planck Institute
|
| 13 |
+
# for Intelligent Systems. All rights reserved.
|
| 14 |
+
#
|
| 15 |
+
# Contact: ps-license@tuebingen.mpg.de
|
| 16 |
+
|
| 17 |
+
from __future__ import absolute_import
|
| 18 |
+
from __future__ import print_function
|
| 19 |
+
from __future__ import division
|
| 20 |
+
|
| 21 |
+
import sys
|
| 22 |
+
import os
|
| 23 |
+
|
| 24 |
+
import time
|
| 25 |
+
import pickle
|
| 26 |
+
|
| 27 |
+
import numpy as np
|
| 28 |
+
|
| 29 |
+
import torch
|
| 30 |
+
import torch.nn as nn
|
| 31 |
+
|
| 32 |
+
DEFAULT_DTYPE = torch.float32
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def create_prior(prior_type, **kwargs):
|
| 36 |
+
if prior_type == 'gmm':
|
| 37 |
+
prior = MaxMixturePrior(**kwargs)
|
| 38 |
+
elif prior_type == 'l2':
|
| 39 |
+
return L2Prior(**kwargs)
|
| 40 |
+
elif prior_type == 'angle':
|
| 41 |
+
return SMPLifyAnglePrior(**kwargs)
|
| 42 |
+
elif prior_type == 'none' or prior_type is None:
|
| 43 |
+
# Don't use any pose prior
|
| 44 |
+
def no_prior(*args, **kwargs):
|
| 45 |
+
return 0.0
|
| 46 |
+
prior = no_prior
|
| 47 |
+
else:
|
| 48 |
+
raise ValueError('Prior {}'.format(prior_type) + ' is not implemented')
|
| 49 |
+
return prior
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class SMPLifyAnglePrior(nn.Module):
|
| 53 |
+
def __init__(self, dtype=torch.float32, **kwargs):
|
| 54 |
+
super(SMPLifyAnglePrior, self).__init__()
|
| 55 |
+
|
| 56 |
+
# Indices for the roration angle of
|
| 57 |
+
# 55: left elbow, 90deg bend at -np.pi/2
|
| 58 |
+
# 58: right elbow, 90deg bend at np.pi/2
|
| 59 |
+
# 12: left knee, 90deg bend at np.pi/2
|
| 60 |
+
# 15: right knee, 90deg bend at np.pi/2
|
| 61 |
+
angle_prior_idxs = np.array([55, 58, 12, 15], dtype=np.int64)
|
| 62 |
+
angle_prior_idxs = torch.tensor(angle_prior_idxs, dtype=torch.long)
|
| 63 |
+
self.register_buffer('angle_prior_idxs', angle_prior_idxs)
|
| 64 |
+
|
| 65 |
+
angle_prior_signs = np.array([1, -1, -1, -1],
|
| 66 |
+
dtype=np.float32 if dtype == torch.float32
|
| 67 |
+
else np.float64)
|
| 68 |
+
angle_prior_signs = torch.tensor(angle_prior_signs,
|
| 69 |
+
dtype=dtype)
|
| 70 |
+
self.register_buffer('angle_prior_signs', angle_prior_signs)
|
| 71 |
+
|
| 72 |
+
def forward(self, pose, with_global_pose=False):
|
| 73 |
+
''' Returns the angle prior loss for the given pose
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
pose: (Bx[23 + 1] * 3) torch tensor with the axis-angle
|
| 77 |
+
representation of the rotations of the joints of the SMPL model.
|
| 78 |
+
Kwargs:
|
| 79 |
+
with_global_pose: Whether the pose vector also contains the global
|
| 80 |
+
orientation of the SMPL model. If not then the indices must be
|
| 81 |
+
corrected.
|
| 82 |
+
Returns:
|
| 83 |
+
A sze (B) tensor containing the angle prior loss for each element
|
| 84 |
+
in the batch.
|
| 85 |
+
'''
|
| 86 |
+
angle_prior_idxs = self.angle_prior_idxs - (not with_global_pose) * 3
|
| 87 |
+
return torch.exp(pose[:, angle_prior_idxs] *
|
| 88 |
+
self.angle_prior_signs).pow(2)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class L2Prior(nn.Module):
|
| 92 |
+
def __init__(self, dtype=DEFAULT_DTYPE, reduction='sum', **kwargs):
|
| 93 |
+
super(L2Prior, self).__init__()
|
| 94 |
+
|
| 95 |
+
def forward(self, module_input, *args):
|
| 96 |
+
return torch.sum(module_input.pow(2))
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class MaxMixturePrior(nn.Module):
|
| 100 |
+
|
| 101 |
+
def __init__(self, prior_folder='prior',
|
| 102 |
+
num_gaussians=6, dtype=DEFAULT_DTYPE, epsilon=1e-16,
|
| 103 |
+
use_merged=True,
|
| 104 |
+
**kwargs):
|
| 105 |
+
super(MaxMixturePrior, self).__init__()
|
| 106 |
+
|
| 107 |
+
if dtype == DEFAULT_DTYPE:
|
| 108 |
+
np_dtype = np.float32
|
| 109 |
+
elif dtype == torch.float64:
|
| 110 |
+
np_dtype = np.float64
|
| 111 |
+
else:
|
| 112 |
+
print('Unknown float type {}, exiting!'.format(dtype))
|
| 113 |
+
sys.exit(-1)
|
| 114 |
+
|
| 115 |
+
self.num_gaussians = num_gaussians
|
| 116 |
+
self.epsilon = epsilon
|
| 117 |
+
self.use_merged = use_merged
|
| 118 |
+
gmm_fn = 'gmm_{:02d}.pkl'.format(num_gaussians)
|
| 119 |
+
|
| 120 |
+
full_gmm_fn = os.path.join(prior_folder, gmm_fn)
|
| 121 |
+
if not os.path.exists(full_gmm_fn):
|
| 122 |
+
print('The path to the mixture prior "{}"'.format(full_gmm_fn) +
|
| 123 |
+
' does not exist, exiting!')
|
| 124 |
+
sys.exit(-1)
|
| 125 |
+
|
| 126 |
+
with open(full_gmm_fn, 'rb') as f:
|
| 127 |
+
gmm = pickle.load(f, encoding='latin1')
|
| 128 |
+
|
| 129 |
+
if type(gmm) == dict:
|
| 130 |
+
means = gmm['means'].astype(np_dtype)
|
| 131 |
+
covs = gmm['covars'].astype(np_dtype)
|
| 132 |
+
weights = gmm['weights'].astype(np_dtype)
|
| 133 |
+
elif 'sklearn.mixture.gmm.GMM' in str(type(gmm)):
|
| 134 |
+
means = gmm.means_.astype(np_dtype)
|
| 135 |
+
covs = gmm.covars_.astype(np_dtype)
|
| 136 |
+
weights = gmm.weights_.astype(np_dtype)
|
| 137 |
+
else:
|
| 138 |
+
print('Unknown type for the prior: {}, exiting!'.format(type(gmm)))
|
| 139 |
+
sys.exit(-1)
|
| 140 |
+
|
| 141 |
+
self.register_buffer('means', torch.tensor(means, dtype=dtype))
|
| 142 |
+
|
| 143 |
+
self.register_buffer('covs', torch.tensor(covs, dtype=dtype))
|
| 144 |
+
|
| 145 |
+
precisions = [np.linalg.inv(cov) for cov in covs]
|
| 146 |
+
precisions = np.stack(precisions).astype(np_dtype)
|
| 147 |
+
|
| 148 |
+
self.register_buffer('precisions',
|
| 149 |
+
torch.tensor(precisions, dtype=dtype))
|
| 150 |
+
|
| 151 |
+
# The constant term:
|
| 152 |
+
sqrdets = np.array([(np.sqrt(np.linalg.det(c)))
|
| 153 |
+
for c in gmm['covars']])
|
| 154 |
+
const = (2 * np.pi)**(69 / 2.)
|
| 155 |
+
|
| 156 |
+
nll_weights = np.asarray(gmm['weights'] / (const *
|
| 157 |
+
(sqrdets / sqrdets.min())))
|
| 158 |
+
nll_weights = torch.tensor(nll_weights, dtype=dtype).unsqueeze(dim=0)
|
| 159 |
+
self.register_buffer('nll_weights', nll_weights)
|
| 160 |
+
|
| 161 |
+
weights = torch.tensor(gmm['weights'], dtype=dtype).unsqueeze(dim=0)
|
| 162 |
+
self.register_buffer('weights', weights)
|
| 163 |
+
|
| 164 |
+
self.register_buffer('pi_term',
|
| 165 |
+
torch.log(torch.tensor(2 * np.pi, dtype=dtype)))
|
| 166 |
+
|
| 167 |
+
cov_dets = [np.log(np.linalg.det(cov.astype(np_dtype)) + epsilon)
|
| 168 |
+
for cov in covs]
|
| 169 |
+
self.register_buffer('cov_dets',
|
| 170 |
+
torch.tensor(cov_dets, dtype=dtype))
|
| 171 |
+
|
| 172 |
+
# The dimensionality of the random variable
|
| 173 |
+
self.random_var_dim = self.means.shape[1]
|
| 174 |
+
|
| 175 |
+
def get_mean(self):
|
| 176 |
+
''' Returns the mean of the mixture '''
|
| 177 |
+
mean_pose = torch.matmul(self.weights, self.means)
|
| 178 |
+
return mean_pose
|
| 179 |
+
|
| 180 |
+
def merged_log_likelihood(self, pose, betas):
|
| 181 |
+
diff_from_mean = pose.unsqueeze(dim=1) - self.means
|
| 182 |
+
|
| 183 |
+
prec_diff_prod = torch.einsum('mij,bmj->bmi',
|
| 184 |
+
[self.precisions, diff_from_mean])
|
| 185 |
+
diff_prec_quadratic = (prec_diff_prod * diff_from_mean).sum(dim=-1)
|
| 186 |
+
|
| 187 |
+
curr_loglikelihood = 0.5 * diff_prec_quadratic - \
|
| 188 |
+
torch.log(self.nll_weights)
|
| 189 |
+
# curr_loglikelihood = 0.5 * (self.cov_dets.unsqueeze(dim=0) +
|
| 190 |
+
# self.random_var_dim * self.pi_term +
|
| 191 |
+
# diff_prec_quadratic
|
| 192 |
+
# ) - torch.log(self.weights)
|
| 193 |
+
|
| 194 |
+
min_likelihood, _ = torch.min(curr_loglikelihood, dim=1)
|
| 195 |
+
return min_likelihood
|
| 196 |
+
|
| 197 |
+
def log_likelihood(self, pose, betas, *args, **kwargs):
|
| 198 |
+
''' Create graph operation for negative log-likelihood calculation
|
| 199 |
+
'''
|
| 200 |
+
likelihoods = []
|
| 201 |
+
|
| 202 |
+
for idx in range(self.num_gaussians):
|
| 203 |
+
mean = self.means[idx]
|
| 204 |
+
prec = self.precisions[idx]
|
| 205 |
+
cov = self.covs[idx]
|
| 206 |
+
diff_from_mean = pose - mean
|
| 207 |
+
|
| 208 |
+
curr_loglikelihood = torch.einsum('bj,ji->bi',
|
| 209 |
+
[diff_from_mean, prec])
|
| 210 |
+
curr_loglikelihood = torch.einsum('bi,bi->b',
|
| 211 |
+
[curr_loglikelihood,
|
| 212 |
+
diff_from_mean])
|
| 213 |
+
cov_term = torch.log(torch.det(cov) + self.epsilon)
|
| 214 |
+
curr_loglikelihood += 0.5 * (cov_term +
|
| 215 |
+
self.random_var_dim *
|
| 216 |
+
self.pi_term)
|
| 217 |
+
likelihoods.append(curr_loglikelihood)
|
| 218 |
+
|
| 219 |
+
log_likelihoods = torch.stack(likelihoods, dim=1)
|
| 220 |
+
min_idx = torch.argmin(log_likelihoods, dim=1)
|
| 221 |
+
weight_component = self.nll_weights[:, min_idx]
|
| 222 |
+
weight_component = -torch.log(weight_component)
|
| 223 |
+
|
| 224 |
+
return weight_component + log_likelihoods[:, min_idx]
|
| 225 |
+
|
| 226 |
+
def forward(self, pose, betas):
|
| 227 |
+
if self.use_merged:
|
| 228 |
+
return self.merged_log_likelihood(pose, betas)
|
| 229 |
+
else:
|
| 230 |
+
return self.log_likelihood(pose, betas)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/joints2smpl/src/smplify.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import os, sys
|
| 3 |
+
import pickle
|
| 4 |
+
import smplx
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
sys.path.append(os.path.dirname(__file__))
|
| 8 |
+
from customloss import (camera_fitting_loss,
|
| 9 |
+
body_fitting_loss,
|
| 10 |
+
camera_fitting_loss_3d,
|
| 11 |
+
body_fitting_loss_3d,
|
| 12 |
+
)
|
| 13 |
+
from prior import MaxMixturePrior
|
| 14 |
+
from visualize.joints2smpl.src import config
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@torch.no_grad()
|
| 19 |
+
def guess_init_3d(model_joints,
|
| 20 |
+
j3d,
|
| 21 |
+
joints_category="orig"):
|
| 22 |
+
"""Initialize the camera translation via triangle similarity, by using the torso joints .
|
| 23 |
+
:param model_joints: SMPL model with pre joints
|
| 24 |
+
:param j3d: 25x3 array of Kinect Joints
|
| 25 |
+
:returns: 3D vector corresponding to the estimated camera translation
|
| 26 |
+
"""
|
| 27 |
+
# get the indexed four
|
| 28 |
+
gt_joints = ['RHip', 'LHip', 'RShoulder', 'LShoulder']
|
| 29 |
+
gt_joints_ind = [config.JOINT_MAP[joint] for joint in gt_joints]
|
| 30 |
+
|
| 31 |
+
if joints_category=="orig":
|
| 32 |
+
joints_ind_category = [config.JOINT_MAP[joint] for joint in gt_joints]
|
| 33 |
+
elif joints_category=="AMASS":
|
| 34 |
+
joints_ind_category = [config.AMASS_JOINT_MAP[joint] for joint in gt_joints]
|
| 35 |
+
else:
|
| 36 |
+
print("NO SUCH JOINTS CATEGORY!")
|
| 37 |
+
|
| 38 |
+
sum_init_t = (j3d[:, joints_ind_category] - model_joints[:, gt_joints_ind]).sum(dim=1)
|
| 39 |
+
init_t = sum_init_t / 4.0
|
| 40 |
+
return init_t
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
# SMPLIfy 3D
|
| 44 |
+
class SMPLify3D():
|
| 45 |
+
"""Implementation of SMPLify, use 3D joints."""
|
| 46 |
+
|
| 47 |
+
def __init__(self,
|
| 48 |
+
smplxmodel,
|
| 49 |
+
step_size=1e-2,
|
| 50 |
+
batch_size=1,
|
| 51 |
+
num_iters=100,
|
| 52 |
+
use_collision=False,
|
| 53 |
+
use_lbfgs=True,
|
| 54 |
+
joints_category="orig",
|
| 55 |
+
device=torch.device('cuda:0'),
|
| 56 |
+
):
|
| 57 |
+
|
| 58 |
+
# Store options
|
| 59 |
+
self.batch_size = batch_size
|
| 60 |
+
self.device = device
|
| 61 |
+
self.step_size = step_size
|
| 62 |
+
|
| 63 |
+
self.num_iters = num_iters
|
| 64 |
+
# --- choose optimizer
|
| 65 |
+
self.use_lbfgs = use_lbfgs
|
| 66 |
+
# GMM pose prior
|
| 67 |
+
self.pose_prior = MaxMixturePrior(prior_folder=config.GMM_MODEL_DIR,
|
| 68 |
+
num_gaussians=8,
|
| 69 |
+
dtype=torch.float32).to(device)
|
| 70 |
+
# collision part
|
| 71 |
+
self.use_collision = use_collision
|
| 72 |
+
if self.use_collision:
|
| 73 |
+
self.part_segm_fn = config.Part_Seg_DIR
|
| 74 |
+
|
| 75 |
+
# reLoad SMPL-X model
|
| 76 |
+
self.smpl = smplxmodel
|
| 77 |
+
|
| 78 |
+
self.model_faces = smplxmodel.faces_tensor.view(-1)
|
| 79 |
+
|
| 80 |
+
# select joint joint_category
|
| 81 |
+
self.joints_category = joints_category
|
| 82 |
+
|
| 83 |
+
if joints_category=="orig":
|
| 84 |
+
self.smpl_index = config.full_smpl_idx
|
| 85 |
+
self.corr_index = config.full_smpl_idx
|
| 86 |
+
elif joints_category=="AMASS":
|
| 87 |
+
self.smpl_index = config.amass_smpl_idx
|
| 88 |
+
self.corr_index = config.amass_idx
|
| 89 |
+
else:
|
| 90 |
+
self.smpl_index = None
|
| 91 |
+
self.corr_index = None
|
| 92 |
+
print("NO SUCH JOINTS CATEGORY!")
|
| 93 |
+
|
| 94 |
+
# ---- get the man function here ------
|
| 95 |
+
def __call__(self, init_pose, init_betas, init_cam_t, j3d, conf_3d=1.0, seq_ind=0):
|
| 96 |
+
"""Perform body fitting.
|
| 97 |
+
Input:
|
| 98 |
+
init_pose: SMPL pose estimate
|
| 99 |
+
init_betas: SMPL betas estimate
|
| 100 |
+
init_cam_t: Camera translation estimate
|
| 101 |
+
j3d: joints 3d aka keypoints
|
| 102 |
+
conf_3d: confidence for 3d joints
|
| 103 |
+
seq_ind: index of the sequence
|
| 104 |
+
Returns:
|
| 105 |
+
vertices: Vertices of optimized shape
|
| 106 |
+
joints: 3D joints of optimized shape
|
| 107 |
+
pose: SMPL pose parameters of optimized shape
|
| 108 |
+
betas: SMPL beta parameters of optimized shape
|
| 109 |
+
camera_translation: Camera translation
|
| 110 |
+
"""
|
| 111 |
+
|
| 112 |
+
# # # add the mesh inter-section to avoid
|
| 113 |
+
search_tree = None
|
| 114 |
+
pen_distance = None
|
| 115 |
+
filter_faces = None
|
| 116 |
+
|
| 117 |
+
if self.use_collision:
|
| 118 |
+
from mesh_intersection.bvh_search_tree import BVH
|
| 119 |
+
import mesh_intersection.loss as collisions_loss
|
| 120 |
+
from mesh_intersection.filter_faces import FilterFaces
|
| 121 |
+
|
| 122 |
+
search_tree = BVH(max_collisions=8)
|
| 123 |
+
|
| 124 |
+
pen_distance = collisions_loss.DistanceFieldPenetrationLoss(
|
| 125 |
+
sigma=0.5, point2plane=False, vectorized=True, penalize_outside=True)
|
| 126 |
+
|
| 127 |
+
if self.part_segm_fn:
|
| 128 |
+
# Read the part segmentation
|
| 129 |
+
part_segm_fn = os.path.expandvars(self.part_segm_fn)
|
| 130 |
+
with open(part_segm_fn, 'rb') as faces_parents_file:
|
| 131 |
+
face_segm_data = pickle.load(faces_parents_file, encoding='latin1')
|
| 132 |
+
faces_segm = face_segm_data['segm']
|
| 133 |
+
faces_parents = face_segm_data['parents']
|
| 134 |
+
# Create the module used to filter invalid collision pairs
|
| 135 |
+
filter_faces = FilterFaces(
|
| 136 |
+
faces_segm=faces_segm, faces_parents=faces_parents,
|
| 137 |
+
ign_part_pairs=None).to(device=self.device)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
# Split SMPL pose to body pose and global orientation
|
| 141 |
+
body_pose = init_pose[:, 3:].detach().clone()
|
| 142 |
+
global_orient = init_pose[:, :3].detach().clone()
|
| 143 |
+
betas = init_betas.detach().clone()
|
| 144 |
+
|
| 145 |
+
# use guess 3d to get the initial
|
| 146 |
+
smpl_output = self.smpl(global_orient=global_orient,
|
| 147 |
+
body_pose=body_pose,
|
| 148 |
+
betas=betas)
|
| 149 |
+
model_joints = smpl_output.joints
|
| 150 |
+
|
| 151 |
+
init_cam_t = guess_init_3d(model_joints, j3d, self.joints_category).unsqueeze(1).detach()
|
| 152 |
+
camera_translation = init_cam_t.clone()
|
| 153 |
+
|
| 154 |
+
preserve_pose = init_pose[:, 3:].detach().clone()
|
| 155 |
+
# -------------Step 1: Optimize camera translation and body orientation--------
|
| 156 |
+
# Optimize only camera translation and body orientation
|
| 157 |
+
body_pose.requires_grad = False
|
| 158 |
+
betas.requires_grad = False
|
| 159 |
+
global_orient.requires_grad = True
|
| 160 |
+
camera_translation.requires_grad = True
|
| 161 |
+
|
| 162 |
+
camera_opt_params = [global_orient, camera_translation]
|
| 163 |
+
|
| 164 |
+
if self.use_lbfgs:
|
| 165 |
+
camera_optimizer = torch.optim.LBFGS(camera_opt_params, max_iter=self.num_iters,
|
| 166 |
+
lr=self.step_size, line_search_fn='strong_wolfe')
|
| 167 |
+
for i in range(10):
|
| 168 |
+
def closure():
|
| 169 |
+
camera_optimizer.zero_grad()
|
| 170 |
+
smpl_output = self.smpl(global_orient=global_orient,
|
| 171 |
+
body_pose=body_pose,
|
| 172 |
+
betas=betas)
|
| 173 |
+
model_joints = smpl_output.joints
|
| 174 |
+
# print('model_joints', model_joints.shape)
|
| 175 |
+
# print('camera_translation', camera_translation.shape)
|
| 176 |
+
# print('init_cam_t', init_cam_t.shape)
|
| 177 |
+
# print('j3d', j3d.shape)
|
| 178 |
+
loss = camera_fitting_loss_3d(model_joints, camera_translation,
|
| 179 |
+
init_cam_t, j3d, self.joints_category)
|
| 180 |
+
loss.backward()
|
| 181 |
+
return loss
|
| 182 |
+
|
| 183 |
+
camera_optimizer.step(closure)
|
| 184 |
+
else:
|
| 185 |
+
camera_optimizer = torch.optim.Adam(camera_opt_params, lr=self.step_size, betas=(0.9, 0.999))
|
| 186 |
+
|
| 187 |
+
for i in range(20):
|
| 188 |
+
smpl_output = self.smpl(global_orient=global_orient,
|
| 189 |
+
body_pose=body_pose,
|
| 190 |
+
betas=betas)
|
| 191 |
+
model_joints = smpl_output.joints
|
| 192 |
+
|
| 193 |
+
loss = camera_fitting_loss_3d(model_joints[:, self.smpl_index], camera_translation,
|
| 194 |
+
init_cam_t, j3d[:, self.corr_index], self.joints_category)
|
| 195 |
+
camera_optimizer.zero_grad()
|
| 196 |
+
loss.backward()
|
| 197 |
+
camera_optimizer.step()
|
| 198 |
+
|
| 199 |
+
# Fix camera translation after optimizing camera
|
| 200 |
+
# --------Step 2: Optimize body joints --------------------------
|
| 201 |
+
# Optimize only the body pose and global orientation of the body
|
| 202 |
+
body_pose.requires_grad = True
|
| 203 |
+
global_orient.requires_grad = True
|
| 204 |
+
camera_translation.requires_grad = True
|
| 205 |
+
|
| 206 |
+
# --- if we use the sequence, fix the shape
|
| 207 |
+
if seq_ind == 0:
|
| 208 |
+
betas.requires_grad = True
|
| 209 |
+
body_opt_params = [body_pose, betas, global_orient, camera_translation]
|
| 210 |
+
else:
|
| 211 |
+
betas.requires_grad = False
|
| 212 |
+
body_opt_params = [body_pose, global_orient, camera_translation]
|
| 213 |
+
|
| 214 |
+
if self.use_lbfgs:
|
| 215 |
+
body_optimizer = torch.optim.LBFGS(body_opt_params, max_iter=self.num_iters,
|
| 216 |
+
lr=self.step_size, line_search_fn='strong_wolfe')
|
| 217 |
+
for i in range(self.num_iters):
|
| 218 |
+
def closure():
|
| 219 |
+
body_optimizer.zero_grad()
|
| 220 |
+
smpl_output = self.smpl(global_orient=global_orient,
|
| 221 |
+
body_pose=body_pose,
|
| 222 |
+
betas=betas)
|
| 223 |
+
model_joints = smpl_output.joints
|
| 224 |
+
model_vertices = smpl_output.vertices
|
| 225 |
+
|
| 226 |
+
loss = body_fitting_loss_3d(body_pose, preserve_pose, betas, model_joints[:, self.smpl_index], camera_translation,
|
| 227 |
+
j3d[:, self.corr_index], self.pose_prior,
|
| 228 |
+
joints3d_conf=conf_3d,
|
| 229 |
+
joint_loss_weight=600.0,
|
| 230 |
+
pose_preserve_weight=5.0,
|
| 231 |
+
use_collision=self.use_collision,
|
| 232 |
+
model_vertices=model_vertices, model_faces=self.model_faces,
|
| 233 |
+
search_tree=search_tree, pen_distance=pen_distance, filter_faces=filter_faces)
|
| 234 |
+
loss.backward()
|
| 235 |
+
return loss
|
| 236 |
+
|
| 237 |
+
body_optimizer.step(closure)
|
| 238 |
+
else:
|
| 239 |
+
body_optimizer = torch.optim.Adam(body_opt_params, lr=self.step_size, betas=(0.9, 0.999))
|
| 240 |
+
|
| 241 |
+
for i in range(self.num_iters):
|
| 242 |
+
smpl_output = self.smpl(global_orient=global_orient,
|
| 243 |
+
body_pose=body_pose,
|
| 244 |
+
betas=betas)
|
| 245 |
+
model_joints = smpl_output.joints
|
| 246 |
+
model_vertices = smpl_output.vertices
|
| 247 |
+
|
| 248 |
+
loss = body_fitting_loss_3d(body_pose, preserve_pose, betas, model_joints[:, self.smpl_index], camera_translation,
|
| 249 |
+
j3d[:, self.corr_index], self.pose_prior,
|
| 250 |
+
joints3d_conf=conf_3d,
|
| 251 |
+
joint_loss_weight=600.0,
|
| 252 |
+
use_collision=self.use_collision,
|
| 253 |
+
model_vertices=model_vertices, model_faces=self.model_faces,
|
| 254 |
+
search_tree=search_tree, pen_distance=pen_distance, filter_faces=filter_faces)
|
| 255 |
+
body_optimizer.zero_grad()
|
| 256 |
+
loss.backward()
|
| 257 |
+
body_optimizer.step()
|
| 258 |
+
|
| 259 |
+
# Get final loss value
|
| 260 |
+
with torch.no_grad():
|
| 261 |
+
smpl_output = self.smpl(global_orient=global_orient,
|
| 262 |
+
body_pose=body_pose,
|
| 263 |
+
betas=betas, return_full_pose=True)
|
| 264 |
+
model_joints = smpl_output.joints
|
| 265 |
+
model_vertices = smpl_output.vertices
|
| 266 |
+
|
| 267 |
+
final_loss = body_fitting_loss_3d(body_pose, preserve_pose, betas, model_joints[:, self.smpl_index], camera_translation,
|
| 268 |
+
j3d[:, self.corr_index], self.pose_prior,
|
| 269 |
+
joints3d_conf=conf_3d,
|
| 270 |
+
joint_loss_weight=600.0,
|
| 271 |
+
use_collision=self.use_collision, model_vertices=model_vertices, model_faces=self.model_faces,
|
| 272 |
+
search_tree=search_tree, pen_distance=pen_distance, filter_faces=filter_faces)
|
| 273 |
+
|
| 274 |
+
vertices = smpl_output.vertices.detach()
|
| 275 |
+
joints = smpl_output.joints.detach()
|
| 276 |
+
pose = torch.cat([global_orient, body_pose], dim=-1).detach()
|
| 277 |
+
betas = betas.detach()
|
| 278 |
+
|
| 279 |
+
return vertices, joints, pose, betas, camera_translation, final_loss
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/render_mesh.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import os
|
| 3 |
+
from visualize import vis_utils
|
| 4 |
+
import shutil
|
| 5 |
+
from tqdm import tqdm
|
| 6 |
+
|
| 7 |
+
if __name__ == '__main__':
|
| 8 |
+
parser = argparse.ArgumentParser()
|
| 9 |
+
parser.add_argument("--input_path", type=str, required=True, help='stick figure mp4 file to be rendered.')
|
| 10 |
+
parser.add_argument("--cuda", type=bool, default=True, help='')
|
| 11 |
+
parser.add_argument("--device", type=int, default=0, help='')
|
| 12 |
+
params = parser.parse_args()
|
| 13 |
+
|
| 14 |
+
assert params.input_path.endswith('.mp4')
|
| 15 |
+
parsed_name = os.path.basename(params.input_path).replace('.mp4', '').replace('sample', '').replace('rep', '')
|
| 16 |
+
sample_i, rep_i = [int(e) for e in parsed_name.split('_')]
|
| 17 |
+
npy_path = os.path.join(os.path.dirname(params.input_path), 'results.npy')
|
| 18 |
+
out_npy_path = params.input_path.replace('.mp4', '_smpl_params.npy')
|
| 19 |
+
assert os.path.exists(npy_path)
|
| 20 |
+
results_dir = params.input_path.replace('.mp4', '_obj')
|
| 21 |
+
if os.path.exists(results_dir):
|
| 22 |
+
shutil.rmtree(results_dir)
|
| 23 |
+
os.makedirs(results_dir)
|
| 24 |
+
|
| 25 |
+
npy2obj = vis_utils.npy2obj(npy_path, sample_i, rep_i,
|
| 26 |
+
device=params.device, cuda=params.cuda)
|
| 27 |
+
|
| 28 |
+
print('Saving obj files to [{}]'.format(os.path.abspath(results_dir)))
|
| 29 |
+
for frame_i in tqdm(range(npy2obj.real_num_frames)):
|
| 30 |
+
npy2obj.save_obj(os.path.join(results_dir, 'frame{:03d}.obj'.format(frame_i)), frame_i)
|
| 31 |
+
|
| 32 |
+
print('Saving SMPL params to [{}]'.format(os.path.abspath(out_npy_path)))
|
| 33 |
+
npy2obj.save_npy(out_npy_path)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/simplify_loc2rot.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import os
|
| 3 |
+
import torch
|
| 4 |
+
from visualize.joints2smpl.src import config
|
| 5 |
+
import smplx
|
| 6 |
+
import h5py
|
| 7 |
+
from visualize.joints2smpl.src.smplify import SMPLify3D
|
| 8 |
+
from tqdm import tqdm
|
| 9 |
+
import utils.rotation_conversions as geometry
|
| 10 |
+
import argparse
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class joints2smpl:
|
| 14 |
+
|
| 15 |
+
def __init__(self, num_frames, device_id, cuda=True):
|
| 16 |
+
self.device = torch.device("cuda:" + str(device_id) if cuda else "cpu")
|
| 17 |
+
# self.device = torch.device("cpu")
|
| 18 |
+
self.batch_size = num_frames
|
| 19 |
+
self.num_joints = 22 # for HumanML3D
|
| 20 |
+
self.joint_category = "AMASS"
|
| 21 |
+
self.num_smplify_iters = 150
|
| 22 |
+
self.fix_foot = False
|
| 23 |
+
print(config.SMPL_MODEL_DIR)
|
| 24 |
+
smplmodel = smplx.create(config.SMPL_MODEL_DIR,
|
| 25 |
+
model_type="smpl", gender="neutral", ext="pkl",
|
| 26 |
+
batch_size=self.batch_size).to(self.device)
|
| 27 |
+
|
| 28 |
+
# ## --- load the mean pose as original ----
|
| 29 |
+
smpl_mean_file = config.SMPL_MEAN_FILE
|
| 30 |
+
|
| 31 |
+
file = h5py.File(smpl_mean_file, 'r')
|
| 32 |
+
self.init_mean_pose = torch.from_numpy(file['pose'][:]).unsqueeze(0).repeat(self.batch_size, 1).float().to(self.device)
|
| 33 |
+
self.init_mean_shape = torch.from_numpy(file['shape'][:]).unsqueeze(0).repeat(self.batch_size, 1).float().to(self.device)
|
| 34 |
+
self.cam_trans_zero = torch.Tensor([0.0, 0.0, 0.0]).unsqueeze(0).to(self.device)
|
| 35 |
+
#
|
| 36 |
+
|
| 37 |
+
# # #-------------initialize SMPLify
|
| 38 |
+
self.smplify = SMPLify3D(smplxmodel=smplmodel,
|
| 39 |
+
batch_size=self.batch_size,
|
| 40 |
+
joints_category=self.joint_category,
|
| 41 |
+
num_iters=self.num_smplify_iters,
|
| 42 |
+
device=self.device)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def npy2smpl(self, npy_path):
|
| 46 |
+
out_path = npy_path.replace('.npy', '_rot.npy')
|
| 47 |
+
motions = np.load(npy_path, allow_pickle=True)[None][0]
|
| 48 |
+
# print_batch('', motions)
|
| 49 |
+
n_samples = motions['motion'].shape[0]
|
| 50 |
+
all_thetas = []
|
| 51 |
+
for sample_i in tqdm(range(n_samples)):
|
| 52 |
+
thetas, _ = self.joint2smpl(motions['motion'][sample_i].transpose(2, 0, 1)) # [nframes, njoints, 3]
|
| 53 |
+
all_thetas.append(thetas.cpu().numpy())
|
| 54 |
+
motions['motion'] = np.concatenate(all_thetas, axis=0)
|
| 55 |
+
print('motions', motions['motion'].shape)
|
| 56 |
+
|
| 57 |
+
print(f'Saving [{out_path}]')
|
| 58 |
+
np.save(out_path, motions)
|
| 59 |
+
exit()
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def joint2smpl(self, input_joints, init_params=None):
|
| 64 |
+
_smplify = self.smplify # if init_params is None else self.smplify_fast
|
| 65 |
+
pred_pose = torch.zeros(self.batch_size, 72).to(self.device)
|
| 66 |
+
pred_betas = torch.zeros(self.batch_size, 10).to(self.device)
|
| 67 |
+
pred_cam_t = torch.zeros(self.batch_size, 3).to(self.device)
|
| 68 |
+
keypoints_3d = torch.zeros(self.batch_size, self.num_joints, 3).to(self.device)
|
| 69 |
+
|
| 70 |
+
# run the whole seqs
|
| 71 |
+
num_seqs = input_joints.shape[0]
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# joints3d = input_joints[idx] # *1.2 #scale problem [check first]
|
| 75 |
+
keypoints_3d = torch.Tensor(input_joints).to(self.device).float()
|
| 76 |
+
|
| 77 |
+
# if idx == 0:
|
| 78 |
+
if init_params is None:
|
| 79 |
+
pred_betas = self.init_mean_shape
|
| 80 |
+
pred_pose = self.init_mean_pose
|
| 81 |
+
pred_cam_t = self.cam_trans_zero
|
| 82 |
+
else:
|
| 83 |
+
pred_betas = init_params['betas']
|
| 84 |
+
pred_pose = init_params['pose']
|
| 85 |
+
pred_cam_t = init_params['cam']
|
| 86 |
+
|
| 87 |
+
if self.joint_category == "AMASS":
|
| 88 |
+
confidence_input = torch.ones(self.num_joints)
|
| 89 |
+
# make sure the foot and ankle
|
| 90 |
+
if self.fix_foot == True:
|
| 91 |
+
confidence_input[7] = 1.5
|
| 92 |
+
confidence_input[8] = 1.5
|
| 93 |
+
confidence_input[10] = 1.5
|
| 94 |
+
confidence_input[11] = 1.5
|
| 95 |
+
else:
|
| 96 |
+
print("Such category not settle down!")
|
| 97 |
+
|
| 98 |
+
new_opt_vertices, new_opt_joints, new_opt_pose, new_opt_betas, \
|
| 99 |
+
new_opt_cam_t, new_opt_joint_loss = _smplify(
|
| 100 |
+
pred_pose.detach(),
|
| 101 |
+
pred_betas.detach(),
|
| 102 |
+
pred_cam_t.detach(),
|
| 103 |
+
keypoints_3d,
|
| 104 |
+
conf_3d=confidence_input.to(self.device),
|
| 105 |
+
# seq_ind=idx
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
thetas = new_opt_pose.reshape(self.batch_size, 24, 3)
|
| 109 |
+
thetas = geometry.matrix_to_rotation_6d(geometry.axis_angle_to_matrix(thetas)) # [bs, 24, 6]
|
| 110 |
+
root_loc = torch.tensor(keypoints_3d[:, 0]) # [bs, 3]
|
| 111 |
+
root_loc = torch.cat([root_loc, torch.zeros_like(root_loc)], dim=-1).unsqueeze(1) # [bs, 1, 6]
|
| 112 |
+
thetas = torch.cat([thetas, root_loc], dim=1).unsqueeze(0).permute(0, 2, 3, 1) # [1, 25, 6, 196]
|
| 113 |
+
|
| 114 |
+
return thetas.clone().detach(), {'pose': new_opt_joints[0, :24].flatten().clone().detach(), 'betas': new_opt_betas.clone().detach(), 'cam': new_opt_cam_t.clone().detach()}
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
if __name__ == '__main__':
|
| 118 |
+
parser = argparse.ArgumentParser()
|
| 119 |
+
parser.add_argument("--input_path", type=str, required=True, help='Blender file or dir with blender files')
|
| 120 |
+
parser.add_argument("--cuda", type=bool, default=True, help='')
|
| 121 |
+
parser.add_argument("--device", type=int, default=0, help='')
|
| 122 |
+
params = parser.parse_args()
|
| 123 |
+
|
| 124 |
+
simplify = joints2smpl(device_id=params.device, cuda=params.cuda)
|
| 125 |
+
|
| 126 |
+
if os.path.isfile(params.input_path) and params.input_path.endswith('.npy'):
|
| 127 |
+
simplify.npy2smpl(params.input_path)
|
| 128 |
+
elif os.path.isdir(params.input_path):
|
| 129 |
+
files = [os.path.join(params.input_path, f) for f in os.listdir(params.input_path) if f.endswith('.npy')]
|
| 130 |
+
for f in files:
|
| 131 |
+
simplify.npy2smpl(f)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/VQ-Trans/visualize/vis_utils.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from model.rotation2xyz import Rotation2xyz
|
| 2 |
+
import numpy as np
|
| 3 |
+
from trimesh import Trimesh
|
| 4 |
+
import os
|
| 5 |
+
import torch
|
| 6 |
+
from visualize.simplify_loc2rot import joints2smpl
|
| 7 |
+
|
| 8 |
+
class npy2obj:
|
| 9 |
+
def __init__(self, npy_path, sample_idx, rep_idx, device=0, cuda=True):
|
| 10 |
+
self.npy_path = npy_path
|
| 11 |
+
self.motions = np.load(self.npy_path, allow_pickle=True)
|
| 12 |
+
if self.npy_path.endswith('.npz'):
|
| 13 |
+
self.motions = self.motions['arr_0']
|
| 14 |
+
self.motions = self.motions[None][0]
|
| 15 |
+
self.rot2xyz = Rotation2xyz(device='cpu')
|
| 16 |
+
self.faces = self.rot2xyz.smpl_model.faces
|
| 17 |
+
self.bs, self.njoints, self.nfeats, self.nframes = self.motions['motion'].shape
|
| 18 |
+
self.opt_cache = {}
|
| 19 |
+
self.sample_idx = sample_idx
|
| 20 |
+
self.total_num_samples = self.motions['num_samples']
|
| 21 |
+
self.rep_idx = rep_idx
|
| 22 |
+
self.absl_idx = self.rep_idx*self.total_num_samples + self.sample_idx
|
| 23 |
+
self.num_frames = self.motions['motion'][self.absl_idx].shape[-1]
|
| 24 |
+
self.j2s = joints2smpl(num_frames=self.num_frames, device_id=device, cuda=cuda)
|
| 25 |
+
|
| 26 |
+
if self.nfeats == 3:
|
| 27 |
+
print(f'Running SMPLify For sample [{sample_idx}], repetition [{rep_idx}], it may take a few minutes.')
|
| 28 |
+
motion_tensor, opt_dict = self.j2s.joint2smpl(self.motions['motion'][self.absl_idx].transpose(2, 0, 1)) # [nframes, njoints, 3]
|
| 29 |
+
self.motions['motion'] = motion_tensor.cpu().numpy()
|
| 30 |
+
elif self.nfeats == 6:
|
| 31 |
+
self.motions['motion'] = self.motions['motion'][[self.absl_idx]]
|
| 32 |
+
self.bs, self.njoints, self.nfeats, self.nframes = self.motions['motion'].shape
|
| 33 |
+
self.real_num_frames = self.motions['lengths'][self.absl_idx]
|
| 34 |
+
|
| 35 |
+
self.vertices = self.rot2xyz(torch.tensor(self.motions['motion']), mask=None,
|
| 36 |
+
pose_rep='rot6d', translation=True, glob=True,
|
| 37 |
+
jointstype='vertices',
|
| 38 |
+
# jointstype='smpl', # for joint locations
|
| 39 |
+
vertstrans=True)
|
| 40 |
+
self.root_loc = self.motions['motion'][:, -1, :3, :].reshape(1, 1, 3, -1)
|
| 41 |
+
self.vertices += self.root_loc
|
| 42 |
+
|
| 43 |
+
def get_vertices(self, sample_i, frame_i):
|
| 44 |
+
return self.vertices[sample_i, :, :, frame_i].squeeze().tolist()
|
| 45 |
+
|
| 46 |
+
def get_trimesh(self, sample_i, frame_i):
|
| 47 |
+
return Trimesh(vertices=self.get_vertices(sample_i, frame_i),
|
| 48 |
+
faces=self.faces)
|
| 49 |
+
|
| 50 |
+
def save_obj(self, save_path, frame_i):
|
| 51 |
+
mesh = self.get_trimesh(0, frame_i)
|
| 52 |
+
with open(save_path, 'w') as fw:
|
| 53 |
+
mesh.export(fw, 'obj')
|
| 54 |
+
return save_path
|
| 55 |
+
|
| 56 |
+
def save_npy(self, save_path):
|
| 57 |
+
data_dict = {
|
| 58 |
+
'motion': self.motions['motion'][0, :, :, :self.real_num_frames],
|
| 59 |
+
'thetas': self.motions['motion'][0, :-1, :, :self.real_num_frames],
|
| 60 |
+
'root_translation': self.motions['motion'][0, -1, :3, :self.real_num_frames],
|
| 61 |
+
'faces': self.faces,
|
| 62 |
+
'vertices': self.vertices[0, :, :, :self.real_num_frames],
|
| 63 |
+
'text': self.motions['text'][0],
|
| 64 |
+
'length': self.real_num_frames,
|
| 65 |
+
}
|
| 66 |
+
np.save(save_path, data_dict)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/app.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
import OpenGL.GL as gl
|
| 4 |
+
os.environ["PYOPENGL_PLATFORM"] = "egl"
|
| 5 |
+
os.environ["MESA_GL_VERSION_OVERRIDE"] = "4.1"
|
| 6 |
+
os.system('pip install /home/user/app/pyrender')
|
| 7 |
+
|
| 8 |
+
sys.argv = ['VQ-Trans/GPT_eval_multi.py']
|
| 9 |
+
os.chdir('VQ-Trans')
|
| 10 |
+
|
| 11 |
+
sys.path.append('/home/user/app/VQ-Trans')
|
| 12 |
+
sys.path.append('/home/user/app/pyrender')
|
| 13 |
+
|
| 14 |
+
import options.option_transformer as option_trans
|
| 15 |
+
from huggingface_hub import snapshot_download
|
| 16 |
+
model_path = snapshot_download(repo_id="vumichien/T2M-GPT")
|
| 17 |
+
|
| 18 |
+
args = option_trans.get_args_parser()
|
| 19 |
+
|
| 20 |
+
args.dataname = 't2m'
|
| 21 |
+
args.resume_pth = f'{model_path}/VQVAE/net_last.pth'
|
| 22 |
+
args.resume_trans = f'{model_path}/VQTransformer_corruption05/net_best_fid.pth'
|
| 23 |
+
args.down_t = 2
|
| 24 |
+
args.depth = 3
|
| 25 |
+
args.block_size = 51
|
| 26 |
+
|
| 27 |
+
import clip
|
| 28 |
+
import torch
|
| 29 |
+
import numpy as np
|
| 30 |
+
import models.vqvae as vqvae
|
| 31 |
+
import models.t2m_trans as trans
|
| 32 |
+
from utils.motion_process import recover_from_ric
|
| 33 |
+
import visualization.plot_3d_global as plot_3d
|
| 34 |
+
from models.rotation2xyz import Rotation2xyz
|
| 35 |
+
import numpy as np
|
| 36 |
+
from trimesh import Trimesh
|
| 37 |
+
import gc
|
| 38 |
+
|
| 39 |
+
import torch
|
| 40 |
+
from visualize.simplify_loc2rot import joints2smpl
|
| 41 |
+
import pyrender
|
| 42 |
+
# import matplotlib.pyplot as plt
|
| 43 |
+
|
| 44 |
+
import io
|
| 45 |
+
import imageio
|
| 46 |
+
from shapely import geometry
|
| 47 |
+
import trimesh
|
| 48 |
+
from pyrender.constants import RenderFlags
|
| 49 |
+
import math
|
| 50 |
+
# import ffmpeg
|
| 51 |
+
# from PIL import Image
|
| 52 |
+
import hashlib
|
| 53 |
+
import gradio as gr
|
| 54 |
+
import moviepy.editor as mp
|
| 55 |
+
|
| 56 |
+
## load clip model and datasets
|
| 57 |
+
is_cuda = torch.cuda.is_available()
|
| 58 |
+
device = torch.device("cuda" if is_cuda else "cpu")
|
| 59 |
+
print(device)
|
| 60 |
+
clip_model, clip_preprocess = clip.load("ViT-B/32", device=device, jit=False, download_root='./') # Must set jit=False for training
|
| 61 |
+
|
| 62 |
+
if is_cuda:
|
| 63 |
+
clip.model.convert_weights(clip_model)
|
| 64 |
+
|
| 65 |
+
clip_model.eval()
|
| 66 |
+
for p in clip_model.parameters():
|
| 67 |
+
p.requires_grad = False
|
| 68 |
+
|
| 69 |
+
net = vqvae.HumanVQVAE(args, ## use args to define different parameters in different quantizers
|
| 70 |
+
args.nb_code,
|
| 71 |
+
args.code_dim,
|
| 72 |
+
args.output_emb_width,
|
| 73 |
+
args.down_t,
|
| 74 |
+
args.stride_t,
|
| 75 |
+
args.width,
|
| 76 |
+
args.depth,
|
| 77 |
+
args.dilation_growth_rate)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
trans_encoder = trans.Text2Motion_Transformer(num_vq=args.nb_code,
|
| 81 |
+
embed_dim=1024,
|
| 82 |
+
clip_dim=args.clip_dim,
|
| 83 |
+
block_size=args.block_size,
|
| 84 |
+
num_layers=9,
|
| 85 |
+
n_head=16,
|
| 86 |
+
drop_out_rate=args.drop_out_rate,
|
| 87 |
+
fc_rate=args.ff_rate)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
print('loading checkpoint from {}'.format(args.resume_pth))
|
| 91 |
+
ckpt = torch.load(args.resume_pth, map_location='cpu')
|
| 92 |
+
net.load_state_dict(ckpt['net'], strict=True)
|
| 93 |
+
net.eval()
|
| 94 |
+
|
| 95 |
+
print('loading transformer checkpoint from {}'.format(args.resume_trans))
|
| 96 |
+
ckpt = torch.load(args.resume_trans, map_location='cpu')
|
| 97 |
+
trans_encoder.load_state_dict(ckpt['trans'], strict=True)
|
| 98 |
+
trans_encoder.eval()
|
| 99 |
+
|
| 100 |
+
mean = torch.from_numpy(np.load(f'{model_path}/meta/mean.npy'))
|
| 101 |
+
std = torch.from_numpy(np.load(f'{model_path}/meta/std.npy'))
|
| 102 |
+
|
| 103 |
+
if is_cuda:
|
| 104 |
+
net.cuda()
|
| 105 |
+
trans_encoder.cuda()
|
| 106 |
+
mean = mean.cuda()
|
| 107 |
+
std = std.cuda()
|
| 108 |
+
|
| 109 |
+
def render(motions, device_id=0, name='test_vis'):
|
| 110 |
+
frames, njoints, nfeats = motions.shape
|
| 111 |
+
MINS = motions.min(axis=0).min(axis=0)
|
| 112 |
+
MAXS = motions.max(axis=0).max(axis=0)
|
| 113 |
+
|
| 114 |
+
height_offset = MINS[1]
|
| 115 |
+
motions[:, :, 1] -= height_offset
|
| 116 |
+
trajec = motions[:, 0, [0, 2]]
|
| 117 |
+
is_cuda = torch.cuda.is_available()
|
| 118 |
+
# device = torch.device("cuda" if is_cuda else "cpu")
|
| 119 |
+
j2s = joints2smpl(num_frames=frames, device_id=0, cuda=is_cuda)
|
| 120 |
+
rot2xyz = Rotation2xyz(device=device)
|
| 121 |
+
faces = rot2xyz.smpl_model.faces
|
| 122 |
+
|
| 123 |
+
if not os.path.exists(f'output/{name}_pred.pt'):
|
| 124 |
+
print(f'Running SMPLify, it may take a few minutes.')
|
| 125 |
+
motion_tensor, opt_dict = j2s.joint2smpl(motions) # [nframes, njoints, 3]
|
| 126 |
+
|
| 127 |
+
vertices = rot2xyz(torch.tensor(motion_tensor).clone(), mask=None,
|
| 128 |
+
pose_rep='rot6d', translation=True, glob=True,
|
| 129 |
+
jointstype='vertices',
|
| 130 |
+
vertstrans=True)
|
| 131 |
+
vertices = vertices.detach().cpu()
|
| 132 |
+
torch.save(vertices, f'output/{name}_pred.pt')
|
| 133 |
+
else:
|
| 134 |
+
vertices = torch.load(f'output/{name}_pred.pt')
|
| 135 |
+
frames = vertices.shape[3] # shape: 1, nb_frames, 3, nb_joints
|
| 136 |
+
print(vertices.shape)
|
| 137 |
+
MINS = torch.min(torch.min(vertices[0], axis=0)[0], axis=1)[0]
|
| 138 |
+
MAXS = torch.max(torch.max(vertices[0], axis=0)[0], axis=1)[0]
|
| 139 |
+
|
| 140 |
+
out_list = []
|
| 141 |
+
|
| 142 |
+
minx = MINS[0] - 0.5
|
| 143 |
+
maxx = MAXS[0] + 0.5
|
| 144 |
+
minz = MINS[2] - 0.5
|
| 145 |
+
maxz = MAXS[2] + 0.5
|
| 146 |
+
polygon = geometry.Polygon([[minx, minz], [minx, maxz], [maxx, maxz], [maxx, minz]])
|
| 147 |
+
polygon_mesh = trimesh.creation.extrude_polygon(polygon, 1e-5)
|
| 148 |
+
|
| 149 |
+
vid = []
|
| 150 |
+
for i in range(frames):
|
| 151 |
+
if i % 10 == 0:
|
| 152 |
+
print(i)
|
| 153 |
+
|
| 154 |
+
mesh = Trimesh(vertices=vertices[0, :, :, i].squeeze().tolist(), faces=faces)
|
| 155 |
+
|
| 156 |
+
base_color = (0.11, 0.53, 0.8, 0.5)
|
| 157 |
+
## OPAQUE rendering without alpha
|
| 158 |
+
## BLEND rendering consider alpha
|
| 159 |
+
material = pyrender.MetallicRoughnessMaterial(
|
| 160 |
+
metallicFactor=0.7,
|
| 161 |
+
alphaMode='OPAQUE',
|
| 162 |
+
baseColorFactor=base_color
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
mesh = pyrender.Mesh.from_trimesh(mesh, material=material)
|
| 167 |
+
|
| 168 |
+
polygon_mesh.visual.face_colors = [0, 0, 0, 0.21]
|
| 169 |
+
polygon_render = pyrender.Mesh.from_trimesh(polygon_mesh, smooth=False)
|
| 170 |
+
|
| 171 |
+
bg_color = [1, 1, 1, 0.8]
|
| 172 |
+
scene = pyrender.Scene(bg_color=bg_color, ambient_light=(0.4, 0.4, 0.4))
|
| 173 |
+
|
| 174 |
+
sx, sy, tx, ty = [0.75, 0.75, 0, 0.10]
|
| 175 |
+
|
| 176 |
+
camera = pyrender.PerspectiveCamera(yfov=(np.pi / 3.0))
|
| 177 |
+
|
| 178 |
+
light = pyrender.DirectionalLight(color=[1,1,1], intensity=300)
|
| 179 |
+
|
| 180 |
+
scene.add(mesh)
|
| 181 |
+
|
| 182 |
+
c = np.pi / 2
|
| 183 |
+
|
| 184 |
+
scene.add(polygon_render, pose=np.array([[ 1, 0, 0, 0],
|
| 185 |
+
|
| 186 |
+
[ 0, np.cos(c), -np.sin(c), MINS[1].cpu().numpy()],
|
| 187 |
+
|
| 188 |
+
[ 0, np.sin(c), np.cos(c), 0],
|
| 189 |
+
|
| 190 |
+
[ 0, 0, 0, 1]]))
|
| 191 |
+
|
| 192 |
+
light_pose = np.eye(4)
|
| 193 |
+
light_pose[:3, 3] = [0, -1, 1]
|
| 194 |
+
scene.add(light, pose=light_pose.copy())
|
| 195 |
+
|
| 196 |
+
light_pose[:3, 3] = [0, 1, 1]
|
| 197 |
+
scene.add(light, pose=light_pose.copy())
|
| 198 |
+
|
| 199 |
+
light_pose[:3, 3] = [1, 1, 2]
|
| 200 |
+
scene.add(light, pose=light_pose.copy())
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
c = -np.pi / 6
|
| 204 |
+
|
| 205 |
+
scene.add(camera, pose=[[ 1, 0, 0, (minx+maxx).cpu().numpy()/2],
|
| 206 |
+
|
| 207 |
+
[ 0, np.cos(c), -np.sin(c), 1.5],
|
| 208 |
+
|
| 209 |
+
[ 0, np.sin(c), np.cos(c), max(4, minz.cpu().numpy()+(1.5-MINS[1].cpu().numpy())*2, (maxx-minx).cpu().numpy())],
|
| 210 |
+
|
| 211 |
+
[ 0, 0, 0, 1]
|
| 212 |
+
])
|
| 213 |
+
|
| 214 |
+
# render scene
|
| 215 |
+
r = pyrender.OffscreenRenderer(960, 960)
|
| 216 |
+
|
| 217 |
+
color, _ = r.render(scene, flags=RenderFlags.RGBA)
|
| 218 |
+
# Image.fromarray(color).save(outdir+'/'+name+'_'+str(i)+'.png')
|
| 219 |
+
|
| 220 |
+
vid.append(color)
|
| 221 |
+
|
| 222 |
+
r.delete()
|
| 223 |
+
|
| 224 |
+
out = np.stack(vid, axis=0)
|
| 225 |
+
imageio.mimwrite(f'output/results.gif', out, duration=50)
|
| 226 |
+
out_video = mp.VideoFileClip(f'output/results.gif')
|
| 227 |
+
out_video.write_videofile("output/results.mp4")
|
| 228 |
+
del out, vertices
|
| 229 |
+
return f'output/results.mp4'
|
| 230 |
+
|
| 231 |
+
def predict(clip_text, method='fast'):
|
| 232 |
+
gc.collect()
|
| 233 |
+
print('prompt text instruction: {}'.format(clip_text))
|
| 234 |
+
if torch.cuda.is_available():
|
| 235 |
+
text = clip.tokenize([clip_text], truncate=True).cuda()
|
| 236 |
+
else:
|
| 237 |
+
text = clip.tokenize([clip_text], truncate=True)
|
| 238 |
+
feat_clip_text = clip_model.encode_text(text).float()
|
| 239 |
+
index_motion = trans_encoder.sample(feat_clip_text[0:1], False)
|
| 240 |
+
pred_pose = net.forward_decoder(index_motion)
|
| 241 |
+
pred_xyz = recover_from_ric((pred_pose*std+mean).float(), 22)
|
| 242 |
+
output_name = hashlib.md5(clip_text.encode()).hexdigest()
|
| 243 |
+
if method == 'fast':
|
| 244 |
+
xyz = pred_xyz.reshape(1, -1, 22, 3)
|
| 245 |
+
pose_vis = plot_3d.draw_to_batch(xyz.detach().cpu().numpy(), title_batch=None, outname=[f'output/results.gif'])
|
| 246 |
+
out_video = mp.VideoFileClip("output/results.gif")
|
| 247 |
+
out_video.write_videofile("output/results.mp4")
|
| 248 |
+
return f'output/results.mp4'
|
| 249 |
+
elif method == 'slow':
|
| 250 |
+
output_path = render(pred_xyz.detach().cpu().numpy().squeeze(axis=0), device_id=0, name=output_name)
|
| 251 |
+
return output_path
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
# ---- Gradio Layout -----
|
| 255 |
+
video_out = gr.Video(label="Motion", mirror_webcam=False, interactive=False)
|
| 256 |
+
demo = gr.Blocks()
|
| 257 |
+
demo.encrypt = False
|
| 258 |
+
|
| 259 |
+
with demo:
|
| 260 |
+
gr.Markdown('''
|
| 261 |
+
<div>
|
| 262 |
+
<h1 style='text-align: center'>Generating Human Motion from Textual Descriptions (T2M-GPT)</h1>
|
| 263 |
+
This space uses <a href='https://mael-zys.github.io/T2M-GPT/' target='_blank'><b>T2M-GPT models</b></a> based on Vector Quantised-Variational AutoEncoder (VQ-VAE) and Generative Pre-trained Transformer (GPT) for human motion generation from textural descriptions🤗
|
| 264 |
+
</div>
|
| 265 |
+
''')
|
| 266 |
+
with gr.Row():
|
| 267 |
+
with gr.Column():
|
| 268 |
+
gr.Markdown('''
|
| 269 |
+
<figure>
|
| 270 |
+
<img src="https://huggingface.co/vumichien/T2M-GPT/resolve/main/demo_slow1.gif" alt="Demo Slow", width="425", height=480/>
|
| 271 |
+
<figcaption> a man starts off in an up right position with botg arms extended out by his sides, he then brings his arms down to his body and claps his hands together. after this he wals down amd the the left where he proceeds to sit on a seat
|
| 272 |
+
</figcaption>
|
| 273 |
+
</figure>
|
| 274 |
+
''')
|
| 275 |
+
with gr.Column():
|
| 276 |
+
gr.Markdown('''
|
| 277 |
+
<figure>
|
| 278 |
+
<img src="https://huggingface.co/vumichien/T2M-GPT/resolve/main/demo_slow2.gif" alt="Demo Slow 2", width="425", height=480/>
|
| 279 |
+
<figcaption> a person puts their hands together, leans forwards slightly then swings the arms from right to left
|
| 280 |
+
</figcaption>
|
| 281 |
+
</figure>
|
| 282 |
+
''')
|
| 283 |
+
with gr.Column():
|
| 284 |
+
gr.Markdown('''
|
| 285 |
+
<figure>
|
| 286 |
+
<img src="https://huggingface.co/vumichien/T2M-GPT/resolve/main/demo_slow3.gif" alt="Demo Slow 3", width="425", height=480/>
|
| 287 |
+
<figcaption> a man is practicing the waltz with a partner
|
| 288 |
+
</figcaption>
|
| 289 |
+
</figure>
|
| 290 |
+
''')
|
| 291 |
+
with gr.Row():
|
| 292 |
+
with gr.Column():
|
| 293 |
+
gr.Markdown('''
|
| 294 |
+
### Generate human motion by **T2M-GPT**
|
| 295 |
+
##### Step 1. Give prompt text describing human motion
|
| 296 |
+
##### Step 2. Choice method to render output (Fast: Sketch skeleton; Slow: SMPL mesh, only work with GPU and running time around 2 mins)
|
| 297 |
+
##### Step 3. Generate output and enjoy
|
| 298 |
+
''')
|
| 299 |
+
with gr.Column():
|
| 300 |
+
with gr.Row():
|
| 301 |
+
text_prompt = gr.Textbox(label="Text prompt", lines=1, interactive=True)
|
| 302 |
+
method = gr.Dropdown(["slow", "fast"], label="Method", value="slow")
|
| 303 |
+
with gr.Row():
|
| 304 |
+
generate_btn = gr.Button("Generate")
|
| 305 |
+
generate_btn.click(predict, [text_prompt, method], [video_out], api_name="generate")
|
| 306 |
+
with gr.Row():
|
| 307 |
+
video_out.render()
|
| 308 |
+
with gr.Row():
|
| 309 |
+
gr.Markdown('''
|
| 310 |
+
### You can test by following examples:
|
| 311 |
+
''')
|
| 312 |
+
examples = gr.Examples(
|
| 313 |
+
examples=[
|
| 314 |
+
["a person jogs in place, slowly at first, then increases speed. they then back up and squat down.", "slow"],
|
| 315 |
+
["a man steps forward and does a handstand", "slow"],
|
| 316 |
+
["a man rises from the ground, walks in a circle and sits back down on the ground", "slow"],
|
| 317 |
+
["a man starts off in an up right position with botg arms extended out by his sides, he then brings his arms down to his body and claps his hands together. after this he wals down amd the the left where he proceeds to sit on a seat", "slow"],
|
| 318 |
+
["a person puts their hands together, leans forwards slightly then swings the arms from right to left","slow"],
|
| 319 |
+
["a man is practicing the waltz with a partner","slow"],
|
| 320 |
+
],
|
| 321 |
+
label="Examples",
|
| 322 |
+
inputs=[text_prompt, method],
|
| 323 |
+
outputs=[video_out],
|
| 324 |
+
fn=predict,
|
| 325 |
+
cache_examples=True,
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
demo.launch(debug=True)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/packages.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
libgl1-mesa-dev
|
| 2 |
+
libglu1-mesa-dev
|
| 3 |
+
freeglut3-dev
|
| 4 |
+
mesa-common-dev
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.coveragerc
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[report]
|
| 2 |
+
exclude_lines =
|
| 3 |
+
def __repr__
|
| 4 |
+
def __str__
|
| 5 |
+
@abc.abstractmethod
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.flake8
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[flake8]
|
| 2 |
+
ignore = E231,W504,F405,F403
|
| 3 |
+
max-line-length = 79
|
| 4 |
+
select = B,C,E,F,W,T4,B9
|
| 5 |
+
exclude =
|
| 6 |
+
docs/source/conf.py,
|
| 7 |
+
__pycache__,
|
| 8 |
+
examples/*
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.gitignore
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
docs/**/generated/**
|
| 7 |
+
|
| 8 |
+
# C extensions
|
| 9 |
+
*.so
|
| 10 |
+
|
| 11 |
+
# Distribution / packaging
|
| 12 |
+
.Python
|
| 13 |
+
build/
|
| 14 |
+
develop-eggs/
|
| 15 |
+
dist/
|
| 16 |
+
downloads/
|
| 17 |
+
eggs/
|
| 18 |
+
.eggs/
|
| 19 |
+
lib/
|
| 20 |
+
lib64/
|
| 21 |
+
parts/
|
| 22 |
+
sdist/
|
| 23 |
+
var/
|
| 24 |
+
wheels/
|
| 25 |
+
*.egg-info/
|
| 26 |
+
.installed.cfg
|
| 27 |
+
*.egg
|
| 28 |
+
MANIFEST
|
| 29 |
+
|
| 30 |
+
# PyInstaller
|
| 31 |
+
# Usually these files are written by a python script from a template
|
| 32 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 33 |
+
*.manifest
|
| 34 |
+
*.spec
|
| 35 |
+
|
| 36 |
+
# Installer logs
|
| 37 |
+
pip-log.txt
|
| 38 |
+
pip-delete-this-directory.txt
|
| 39 |
+
|
| 40 |
+
# Unit test / coverage reports
|
| 41 |
+
htmlcov/
|
| 42 |
+
.tox/
|
| 43 |
+
.coverage
|
| 44 |
+
.coverage.*
|
| 45 |
+
.cache
|
| 46 |
+
nosetests.xml
|
| 47 |
+
coverage.xml
|
| 48 |
+
*.cover
|
| 49 |
+
.hypothesis/
|
| 50 |
+
.pytest_cache/
|
| 51 |
+
|
| 52 |
+
# Translations
|
| 53 |
+
*.mo
|
| 54 |
+
*.pot
|
| 55 |
+
|
| 56 |
+
# Django stuff:
|
| 57 |
+
*.log
|
| 58 |
+
local_settings.py
|
| 59 |
+
db.sqlite3
|
| 60 |
+
|
| 61 |
+
# Flask stuff:
|
| 62 |
+
instance/
|
| 63 |
+
.webassets-cache
|
| 64 |
+
|
| 65 |
+
# Scrapy stuff:
|
| 66 |
+
.scrapy
|
| 67 |
+
|
| 68 |
+
# Sphinx documentation
|
| 69 |
+
docs/_build/
|
| 70 |
+
|
| 71 |
+
# PyBuilder
|
| 72 |
+
target/
|
| 73 |
+
|
| 74 |
+
# Jupyter Notebook
|
| 75 |
+
.ipynb_checkpoints
|
| 76 |
+
|
| 77 |
+
# pyenv
|
| 78 |
+
.python-version
|
| 79 |
+
|
| 80 |
+
# celery beat schedule file
|
| 81 |
+
celerybeat-schedule
|
| 82 |
+
|
| 83 |
+
# SageMath parsed files
|
| 84 |
+
*.sage.py
|
| 85 |
+
|
| 86 |
+
# Environments
|
| 87 |
+
.env
|
| 88 |
+
.venv
|
| 89 |
+
env/
|
| 90 |
+
venv/
|
| 91 |
+
ENV/
|
| 92 |
+
env.bak/
|
| 93 |
+
venv.bak/
|
| 94 |
+
|
| 95 |
+
# Spyder project settings
|
| 96 |
+
.spyderproject
|
| 97 |
+
.spyproject
|
| 98 |
+
|
| 99 |
+
# Rope project settings
|
| 100 |
+
.ropeproject
|
| 101 |
+
|
| 102 |
+
# mkdocs documentation
|
| 103 |
+
/site
|
| 104 |
+
|
| 105 |
+
# mypy
|
| 106 |
+
.mypy_cache/
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
repos:
|
| 2 |
+
- repo: https://gitlab.com/pycqa/flake8
|
| 3 |
+
rev: 3.7.1
|
| 4 |
+
hooks:
|
| 5 |
+
- id: flake8
|
| 6 |
+
exclude: ^setup.py
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/.travis.yml
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
language: python
|
| 2 |
+
sudo: required
|
| 3 |
+
dist: xenial
|
| 4 |
+
|
| 5 |
+
python:
|
| 6 |
+
- '3.6'
|
| 7 |
+
- '3.7'
|
| 8 |
+
|
| 9 |
+
before_install:
|
| 10 |
+
# Pre-install osmesa
|
| 11 |
+
- sudo apt update
|
| 12 |
+
- sudo wget https://github.com/mmatl/travis_debs/raw/master/xenial/mesa_18.3.3-0.deb
|
| 13 |
+
- sudo dpkg -i ./mesa_18.3.3-0.deb || true
|
| 14 |
+
- sudo apt install -f
|
| 15 |
+
- git clone https://github.com/mmatl/pyopengl.git
|
| 16 |
+
- cd pyopengl
|
| 17 |
+
- pip install .
|
| 18 |
+
- cd ..
|
| 19 |
+
|
| 20 |
+
install:
|
| 21 |
+
- pip install .
|
| 22 |
+
# - pip install -q pytest pytest-cov coveralls
|
| 23 |
+
- pip install pytest pytest-cov coveralls
|
| 24 |
+
- pip install ./pyopengl
|
| 25 |
+
|
| 26 |
+
script:
|
| 27 |
+
- PYOPENGL_PLATFORM=osmesa pytest --cov=pyrender tests
|
| 28 |
+
|
| 29 |
+
after_success:
|
| 30 |
+
- coveralls || true
|
| 31 |
+
|
| 32 |
+
deploy:
|
| 33 |
+
provider: pypi
|
| 34 |
+
skip_existing: true
|
| 35 |
+
user: mmatl
|
| 36 |
+
on:
|
| 37 |
+
tags: true
|
| 38 |
+
branch: master
|
| 39 |
+
password:
|
| 40 |
+
secure: O4WWMbTYb2eVYIO4mMOVa6/xyhX7mPvJpd96cxfNvJdyuqho8VapOhzqsI5kahMB1hFjWWr61yR4+Ru5hoDYf3XA6BQVk8eCY9+0H7qRfvoxex71lahKAqfHLMoE1xNdiVTgl+QN9hYjOnopLod24rx8I8eXfpHu/mfCpuTYGyLlNcDP5St3bXpXLPB5wg8Jo1YRRv6W/7fKoXyuWjewk9cJAS0KrEgnDnSkdwm6Pb+80B2tcbgdGvpGaByw5frndwKiMUMgVUownepDU5POQq2p29wwn9lCvRucULxjEgO+63jdbZRj5fNutLarFa2nISfYnrd72LOyDfbJubwAzzAIsy2JbFORyeHvCgloiuE9oE7a9oOQt/1QHBoIV0seiawMWn55Yp70wQ7HlJs4xSGJWCGa5+9883QRNsvj420atkb3cgO8P+PXwiwTi78Dq7Z/xHqccsU0b8poqBneQoA+pUGgNnF6V7Z8e9RsCcse2gAWSZWuOK3ua+9xCgH7I7MeL3afykr2aJ+yFCoYJMFrUjJeodMX2RbL0q+3FzIPZeGW3WdhTEAL9TSKRcJBSQTskaQlZx/OcpobxS7t3d2S68CCLG9uMTqOTYws55WZ1etalA75sRk9K2MR7ZGjZW3jdtvMViISc/t6Rrjea1GE8ZHGJC6/IeLIWA2c7nc=
|
| 41 |
+
distributions: sdist bdist_wheel
|
| 42 |
+
notifications:
|
| 43 |
+
email: false
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2019 Matthew Matl
|
| 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.
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/MANIFEST.in
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Include the license
|
| 2 |
+
include LICENSE
|
| 3 |
+
include README.rst
|
| 4 |
+
include pyrender/fonts/*
|
| 5 |
+
include pyrender/shaders/*
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Pyrender
|
| 2 |
+
|
| 3 |
+
[](https://travis-ci.org/mmatl/pyrender)
|
| 4 |
+
[](https://pyrender.readthedocs.io/en/latest/?badge=latest)
|
| 5 |
+
[](https://coveralls.io/github/mmatl/pyrender?branch=master)
|
| 6 |
+
[](https://badge.fury.io/py/pyrender)
|
| 7 |
+
[](https://pepy.tech/project/pyrender)
|
| 8 |
+
|
| 9 |
+
Pyrender is a pure Python (2.7, 3.4, 3.5, 3.6) library for physically-based
|
| 10 |
+
rendering and visualization.
|
| 11 |
+
It is designed to meet the [glTF 2.0 specification from Khronos](https://www.khronos.org/gltf/).
|
| 12 |
+
|
| 13 |
+
Pyrender is lightweight, easy to install, and simple to use.
|
| 14 |
+
It comes packaged with both an intuitive scene viewer and a headache-free
|
| 15 |
+
offscreen renderer with support for GPU-accelerated rendering on headless
|
| 16 |
+
servers, which makes it perfect for machine learning applications.
|
| 17 |
+
|
| 18 |
+
Extensive documentation, including a quickstart guide, is provided [here](https://pyrender.readthedocs.io/en/latest/).
|
| 19 |
+
|
| 20 |
+
For a minimal working example of GPU-accelerated offscreen rendering using EGL,
|
| 21 |
+
check out the [EGL Google CoLab Notebook](https://colab.research.google.com/drive/1pcndwqeY8vker3bLKQNJKr3B-7-SYenE?usp=sharing).
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
<p align="center">
|
| 25 |
+
<img width="48%" src="https://github.com/mmatl/pyrender/blob/master/docs/source/_static/rotation.gif?raw=true" alt="GIF of Viewer"/>
|
| 26 |
+
<img width="48%" src="https://github.com/mmatl/pyrender/blob/master/docs/source/_static/damaged_helmet.png?raw=true" alt="Damaged Helmet"/>
|
| 27 |
+
</p>
|
| 28 |
+
|
| 29 |
+
## Installation
|
| 30 |
+
You can install pyrender directly from pip.
|
| 31 |
+
|
| 32 |
+
```bash
|
| 33 |
+
pip install pyrender
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
## Features
|
| 37 |
+
|
| 38 |
+
Despite being lightweight, pyrender has lots of features, including:
|
| 39 |
+
|
| 40 |
+
* Simple interoperation with the amazing [trimesh](https://github.com/mikedh/trimesh) project,
|
| 41 |
+
which enables out-of-the-box support for dozens of mesh types, including OBJ,
|
| 42 |
+
STL, DAE, OFF, PLY, and GLB.
|
| 43 |
+
* An easy-to-use scene viewer with support for animation, showing face and vertex
|
| 44 |
+
normals, toggling lighting conditions, and saving images and GIFs.
|
| 45 |
+
* An offscreen rendering module that supports OSMesa and EGL backends.
|
| 46 |
+
* Shadow mapping for directional and spot lights.
|
| 47 |
+
* Metallic-roughness materials for physically-based rendering, including several
|
| 48 |
+
types of texture and normal mapping.
|
| 49 |
+
* Transparency.
|
| 50 |
+
* Depth and color image generation.
|
| 51 |
+
|
| 52 |
+
## Sample Usage
|
| 53 |
+
|
| 54 |
+
For sample usage, check out the [quickstart
|
| 55 |
+
guide](https://pyrender.readthedocs.io/en/latest/examples/index.html) or one of
|
| 56 |
+
the Google CoLab Notebooks:
|
| 57 |
+
|
| 58 |
+
* [EGL Google CoLab Notebook](https://colab.research.google.com/drive/1pcndwqeY8vker3bLKQNJKr3B-7-SYenE?usp=sharing)
|
| 59 |
+
|
| 60 |
+
## Viewer Keyboard and Mouse Controls
|
| 61 |
+
|
| 62 |
+
When using the viewer, the basic controls for moving about the scene are as follows:
|
| 63 |
+
|
| 64 |
+
* To rotate the camera about the center of the scene, hold the left mouse button and drag the cursor.
|
| 65 |
+
* To rotate the camera about its viewing axis, hold `CTRL` left mouse button and drag the cursor.
|
| 66 |
+
* To pan the camera, do one of the following:
|
| 67 |
+
* Hold `SHIFT`, then hold the left mouse button and drag the cursor.
|
| 68 |
+
* Hold the middle mouse button and drag the cursor.
|
| 69 |
+
* To zoom the camera in or out, do one of the following:
|
| 70 |
+
* Scroll the mouse wheel.
|
| 71 |
+
* Hold the right mouse button and drag the cursor.
|
| 72 |
+
|
| 73 |
+
The available keyboard commands are as follows:
|
| 74 |
+
|
| 75 |
+
* `a`: Toggles rotational animation mode.
|
| 76 |
+
* `c`: Toggles backface culling.
|
| 77 |
+
* `f`: Toggles fullscreen mode.
|
| 78 |
+
* `h`: Toggles shadow rendering.
|
| 79 |
+
* `i`: Toggles axis display mode (no axes, world axis, mesh axes, all axes).
|
| 80 |
+
* `l`: Toggles lighting mode (scene lighting, Raymond lighting, or direct lighting).
|
| 81 |
+
* `m`: Toggles face normal visualization.
|
| 82 |
+
* `n`: Toggles vertex normal visualization.
|
| 83 |
+
* `o`: Toggles orthographic camera mode.
|
| 84 |
+
* `q`: Quits the viewer.
|
| 85 |
+
* `r`: Starts recording a GIF, and pressing again stops recording and opens a file dialog.
|
| 86 |
+
* `s`: Opens a file dialog to save the current view as an image.
|
| 87 |
+
* `w`: Toggles wireframe mode (scene default, flip wireframes, all wireframe, or all solid).
|
| 88 |
+
* `z`: Resets the camera to the default view.
|
| 89 |
+
|
| 90 |
+
As a note, displaying shadows significantly slows down rendering, so if you're
|
| 91 |
+
experiencing low framerates, just kill shadows or reduce the number of lights in
|
| 92 |
+
your scene.
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/Makefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Minimal makefile for Sphinx documentation
|
| 2 |
+
#
|
| 3 |
+
|
| 4 |
+
# You can set these variables from the command line.
|
| 5 |
+
SPHINXOPTS =
|
| 6 |
+
SPHINXBUILD = sphinx-build
|
| 7 |
+
SOURCEDIR = source
|
| 8 |
+
BUILDDIR = build
|
| 9 |
+
|
| 10 |
+
# Put it first so that "make" without argument is like "make help".
|
| 11 |
+
help:
|
| 12 |
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
| 13 |
+
|
| 14 |
+
.PHONY: help Makefile
|
| 15 |
+
|
| 16 |
+
clean:
|
| 17 |
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
| 18 |
+
rm -rf ./source/generated/*
|
| 19 |
+
|
| 20 |
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
| 21 |
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
| 22 |
+
%: Makefile
|
| 23 |
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/make.bat
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@ECHO OFF
|
| 2 |
+
|
| 3 |
+
pushd %~dp0
|
| 4 |
+
|
| 5 |
+
REM Command file for Sphinx documentation
|
| 6 |
+
|
| 7 |
+
if "%SPHINXBUILD%" == "" (
|
| 8 |
+
set SPHINXBUILD=sphinx-build
|
| 9 |
+
)
|
| 10 |
+
set SOURCEDIR=source
|
| 11 |
+
set BUILDDIR=build
|
| 12 |
+
|
| 13 |
+
if "%1" == "" goto help
|
| 14 |
+
|
| 15 |
+
%SPHINXBUILD% >NUL 2>NUL
|
| 16 |
+
if errorlevel 9009 (
|
| 17 |
+
echo.
|
| 18 |
+
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
| 19 |
+
echo.installed, then set the SPHINXBUILD environment variable to point
|
| 20 |
+
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
| 21 |
+
echo.may add the Sphinx directory to PATH.
|
| 22 |
+
echo.
|
| 23 |
+
echo.If you don't have Sphinx installed, grab it from
|
| 24 |
+
echo.http://sphinx-doc.org/
|
| 25 |
+
exit /b 1
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
| 29 |
+
goto end
|
| 30 |
+
|
| 31 |
+
:help
|
| 32 |
+
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
| 33 |
+
|
| 34 |
+
:end
|
| 35 |
+
popd
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/api/index.rst
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Pyrender API Documentation
|
| 2 |
+
==========================
|
| 3 |
+
|
| 4 |
+
Constants
|
| 5 |
+
---------
|
| 6 |
+
.. automodapi:: pyrender.constants
|
| 7 |
+
:no-inheritance-diagram:
|
| 8 |
+
:no-main-docstr:
|
| 9 |
+
:no-heading:
|
| 10 |
+
|
| 11 |
+
Cameras
|
| 12 |
+
-------
|
| 13 |
+
.. automodapi:: pyrender.camera
|
| 14 |
+
:no-inheritance-diagram:
|
| 15 |
+
:no-main-docstr:
|
| 16 |
+
:no-heading:
|
| 17 |
+
|
| 18 |
+
Lighting
|
| 19 |
+
--------
|
| 20 |
+
.. automodapi:: pyrender.light
|
| 21 |
+
:no-inheritance-diagram:
|
| 22 |
+
:no-main-docstr:
|
| 23 |
+
:no-heading:
|
| 24 |
+
|
| 25 |
+
Objects
|
| 26 |
+
-------
|
| 27 |
+
.. automodapi:: pyrender
|
| 28 |
+
:no-inheritance-diagram:
|
| 29 |
+
:no-main-docstr:
|
| 30 |
+
:no-heading:
|
| 31 |
+
:skip: Camera, DirectionalLight, Light, OffscreenRenderer, Node
|
| 32 |
+
:skip: OrthographicCamera, PerspectiveCamera, PointLight, RenderFlags
|
| 33 |
+
:skip: Renderer, Scene, SpotLight, TextAlign, Viewer, GLTF
|
| 34 |
+
|
| 35 |
+
Scenes
|
| 36 |
+
------
|
| 37 |
+
.. automodapi:: pyrender
|
| 38 |
+
:no-inheritance-diagram:
|
| 39 |
+
:no-main-docstr:
|
| 40 |
+
:no-heading:
|
| 41 |
+
:skip: Camera, DirectionalLight, Light, OffscreenRenderer
|
| 42 |
+
:skip: OrthographicCamera, PerspectiveCamera, PointLight, RenderFlags
|
| 43 |
+
:skip: Renderer, SpotLight, TextAlign, Viewer, Sampler, Texture, Material
|
| 44 |
+
:skip: MetallicRoughnessMaterial, Primitive, Mesh, GLTF
|
| 45 |
+
|
| 46 |
+
On-Screen Viewer
|
| 47 |
+
----------------
|
| 48 |
+
.. automodapi:: pyrender.viewer
|
| 49 |
+
:no-inheritance-diagram:
|
| 50 |
+
:no-inherited-members:
|
| 51 |
+
:no-main-docstr:
|
| 52 |
+
:no-heading:
|
| 53 |
+
|
| 54 |
+
Off-Screen Rendering
|
| 55 |
+
--------------------
|
| 56 |
+
.. automodapi:: pyrender.offscreen
|
| 57 |
+
:no-inheritance-diagram:
|
| 58 |
+
:no-main-docstr:
|
| 59 |
+
:no-heading:
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/conf.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
#
|
| 3 |
+
# core documentation build configuration file, created by
|
| 4 |
+
# sphinx-quickstart on Sun Oct 16 14:33:48 2016.
|
| 5 |
+
#
|
| 6 |
+
# This file is execfile()d with the current directory set to its
|
| 7 |
+
# containing dir.
|
| 8 |
+
#
|
| 9 |
+
# Note that not all possible configuration values are present in this
|
| 10 |
+
# autogenerated file.
|
| 11 |
+
#
|
| 12 |
+
# All configuration values have a default; values that are commented out
|
| 13 |
+
# serve to show the default.
|
| 14 |
+
|
| 15 |
+
import sys
|
| 16 |
+
import os
|
| 17 |
+
from pyrender import __version__
|
| 18 |
+
from sphinx.domains.python import PythonDomain
|
| 19 |
+
|
| 20 |
+
# If extensions (or modules to document with autodoc) are in another directory,
|
| 21 |
+
# add these directories to sys.path here. If the directory is relative to the
|
| 22 |
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
| 23 |
+
sys.path.insert(0, os.path.abspath('../../'))
|
| 24 |
+
|
| 25 |
+
# -- General configuration ------------------------------------------------
|
| 26 |
+
|
| 27 |
+
# If your documentation needs a minimal Sphinx version, state it here.
|
| 28 |
+
#needs_sphinx = '1.0'
|
| 29 |
+
|
| 30 |
+
# Add any Sphinx extension module names here, as strings. They can be
|
| 31 |
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
| 32 |
+
# ones.
|
| 33 |
+
extensions = [
|
| 34 |
+
'sphinx.ext.autodoc',
|
| 35 |
+
'sphinx.ext.autosummary',
|
| 36 |
+
'sphinx.ext.coverage',
|
| 37 |
+
'sphinx.ext.githubpages',
|
| 38 |
+
'sphinx.ext.intersphinx',
|
| 39 |
+
'sphinx.ext.napoleon',
|
| 40 |
+
'sphinx.ext.viewcode',
|
| 41 |
+
'sphinx_automodapi.automodapi',
|
| 42 |
+
'sphinx_automodapi.smart_resolver'
|
| 43 |
+
]
|
| 44 |
+
numpydoc_class_members_toctree = False
|
| 45 |
+
automodapi_toctreedirnm = 'generated'
|
| 46 |
+
automodsumm_inherited_members = True
|
| 47 |
+
|
| 48 |
+
# Add any paths that contain templates here, relative to this directory.
|
| 49 |
+
templates_path = ['_templates']
|
| 50 |
+
|
| 51 |
+
# The suffix(es) of source filenames.
|
| 52 |
+
# You can specify multiple suffix as a list of string:
|
| 53 |
+
# source_suffix = ['.rst', '.md']
|
| 54 |
+
source_suffix = '.rst'
|
| 55 |
+
|
| 56 |
+
# The encoding of source files.
|
| 57 |
+
#source_encoding = 'utf-8-sig'
|
| 58 |
+
|
| 59 |
+
# The master toctree document.
|
| 60 |
+
master_doc = 'index'
|
| 61 |
+
|
| 62 |
+
# General information about the project.
|
| 63 |
+
project = u'pyrender'
|
| 64 |
+
copyright = u'2018, Matthew Matl'
|
| 65 |
+
author = u'Matthew Matl'
|
| 66 |
+
|
| 67 |
+
# The version info for the project you're documenting, acts as replacement for
|
| 68 |
+
# |version| and |release|, also used in various other places throughout the
|
| 69 |
+
# built documents.
|
| 70 |
+
#
|
| 71 |
+
# The short X.Y version.
|
| 72 |
+
version = __version__
|
| 73 |
+
# The full version, including alpha/beta/rc tags.
|
| 74 |
+
release = __version__
|
| 75 |
+
|
| 76 |
+
# The language for content autogenerated by Sphinx. Refer to documentation
|
| 77 |
+
# for a list of supported languages.
|
| 78 |
+
#
|
| 79 |
+
# This is also used if you do content translation via gettext catalogs.
|
| 80 |
+
# Usually you set "language" from the command line for these cases.
|
| 81 |
+
language = None
|
| 82 |
+
|
| 83 |
+
# There are two options for replacing |today|: either, you set today to some
|
| 84 |
+
# non-false value, then it is used:
|
| 85 |
+
#today = ''
|
| 86 |
+
# Else, today_fmt is used as the format for a strftime call.
|
| 87 |
+
#today_fmt = '%B %d, %Y'
|
| 88 |
+
|
| 89 |
+
# List of patterns, relative to source directory, that match files and
|
| 90 |
+
# directories to ignore when looking for source files.
|
| 91 |
+
exclude_patterns = []
|
| 92 |
+
|
| 93 |
+
# The reST default role (used for this markup: `text`) to use for all
|
| 94 |
+
# documents.
|
| 95 |
+
#default_role = None
|
| 96 |
+
|
| 97 |
+
# If true, '()' will be appended to :func: etc. cross-reference text.
|
| 98 |
+
#add_function_parentheses = True
|
| 99 |
+
|
| 100 |
+
# If true, the current module name will be prepended to all description
|
| 101 |
+
# unit titles (such as .. function::).
|
| 102 |
+
#add_module_names = True
|
| 103 |
+
|
| 104 |
+
# If true, sectionauthor and moduleauthor directives will be shown in the
|
| 105 |
+
# output. They are ignored by default.
|
| 106 |
+
#show_authors = False
|
| 107 |
+
|
| 108 |
+
# The name of the Pygments (syntax highlighting) style to use.
|
| 109 |
+
pygments_style = 'sphinx'
|
| 110 |
+
|
| 111 |
+
# A list of ignored prefixes for module index sorting.
|
| 112 |
+
#modindex_common_prefix = []
|
| 113 |
+
|
| 114 |
+
# If true, keep warnings as "system message" paragraphs in the built documents.
|
| 115 |
+
#keep_warnings = False
|
| 116 |
+
|
| 117 |
+
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
| 118 |
+
todo_include_todos = False
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
# -- Options for HTML output ----------------------------------------------
|
| 122 |
+
|
| 123 |
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
| 124 |
+
# a list of builtin themes.
|
| 125 |
+
import sphinx_rtd_theme
|
| 126 |
+
html_theme = 'sphinx_rtd_theme'
|
| 127 |
+
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
| 128 |
+
|
| 129 |
+
# Theme options are theme-specific and customize the look and feel of a theme
|
| 130 |
+
# further. For a list of options available for each theme, see the
|
| 131 |
+
# documentation.
|
| 132 |
+
#html_theme_options = {}
|
| 133 |
+
|
| 134 |
+
# Add any paths that contain custom themes here, relative to this directory.
|
| 135 |
+
#html_theme_path = []
|
| 136 |
+
|
| 137 |
+
# The name for this set of Sphinx documents. If None, it defaults to
|
| 138 |
+
# "<project> v<release> documentation".
|
| 139 |
+
#html_title = None
|
| 140 |
+
|
| 141 |
+
# A shorter title for the navigation bar. Default is the same as html_title.
|
| 142 |
+
#html_short_title = None
|
| 143 |
+
|
| 144 |
+
# The name of an image file (relative to this directory) to place at the top
|
| 145 |
+
# of the sidebar.
|
| 146 |
+
#html_logo = None
|
| 147 |
+
|
| 148 |
+
# The name of an image file (relative to this directory) to use as a favicon of
|
| 149 |
+
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
| 150 |
+
# pixels large.
|
| 151 |
+
#html_favicon = None
|
| 152 |
+
|
| 153 |
+
# Add any paths that contain custom static files (such as style sheets) here,
|
| 154 |
+
# relative to this directory. They are copied after the builtin static files,
|
| 155 |
+
# so a file named "default.css" will overwrite the builtin "default.css".
|
| 156 |
+
html_static_path = ['_static']
|
| 157 |
+
|
| 158 |
+
# Add any extra paths that contain custom files (such as robots.txt or
|
| 159 |
+
# .htaccess) here, relative to this directory. These files are copied
|
| 160 |
+
# directly to the root of the documentation.
|
| 161 |
+
#html_extra_path = []
|
| 162 |
+
|
| 163 |
+
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
| 164 |
+
# using the given strftime format.
|
| 165 |
+
#html_last_updated_fmt = '%b %d, %Y'
|
| 166 |
+
|
| 167 |
+
# If true, SmartyPants will be used to convert quotes and dashes to
|
| 168 |
+
# typographically correct entities.
|
| 169 |
+
#html_use_smartypants = True
|
| 170 |
+
|
| 171 |
+
# Custom sidebar templates, maps document names to template names.
|
| 172 |
+
#html_sidebars = {}
|
| 173 |
+
|
| 174 |
+
# Additional templates that should be rendered to pages, maps page names to
|
| 175 |
+
# template names.
|
| 176 |
+
#html_additional_pages = {}
|
| 177 |
+
|
| 178 |
+
# If false, no module index is generated.
|
| 179 |
+
#html_domain_indices = True
|
| 180 |
+
|
| 181 |
+
# If false, no index is generated.
|
| 182 |
+
#html_use_index = True
|
| 183 |
+
|
| 184 |
+
# If true, the index is split into individual pages for each letter.
|
| 185 |
+
#html_split_index = False
|
| 186 |
+
|
| 187 |
+
# If true, links to the reST sources are added to the pages.
|
| 188 |
+
#html_show_sourcelink = True
|
| 189 |
+
|
| 190 |
+
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
| 191 |
+
#html_show_sphinx = True
|
| 192 |
+
|
| 193 |
+
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
| 194 |
+
#html_show_copyright = True
|
| 195 |
+
|
| 196 |
+
# If true, an OpenSearch description file will be output, and all pages will
|
| 197 |
+
# contain a <link> tag referring to it. The value of this option must be the
|
| 198 |
+
# base URL from which the finished HTML is served.
|
| 199 |
+
#html_use_opensearch = ''
|
| 200 |
+
|
| 201 |
+
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
| 202 |
+
#html_file_suffix = None
|
| 203 |
+
|
| 204 |
+
# Language to be used for generating the HTML full-text search index.
|
| 205 |
+
# Sphinx supports the following languages:
|
| 206 |
+
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
|
| 207 |
+
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
|
| 208 |
+
#html_search_language = 'en'
|
| 209 |
+
|
| 210 |
+
# A dictionary with options for the search language support, empty by default.
|
| 211 |
+
# Now only 'ja' uses this config value
|
| 212 |
+
#html_search_options = {'type': 'default'}
|
| 213 |
+
|
| 214 |
+
# The name of a javascript file (relative to the configuration directory) that
|
| 215 |
+
# implements a search results scorer. If empty, the default will be used.
|
| 216 |
+
#html_search_scorer = 'scorer.js'
|
| 217 |
+
|
| 218 |
+
# Output file base name for HTML help builder.
|
| 219 |
+
htmlhelp_basename = 'coredoc'
|
| 220 |
+
|
| 221 |
+
# -- Options for LaTeX output ---------------------------------------------
|
| 222 |
+
|
| 223 |
+
latex_elements = {
|
| 224 |
+
# The paper size ('letterpaper' or 'a4paper').
|
| 225 |
+
#'papersize': 'letterpaper',
|
| 226 |
+
|
| 227 |
+
# The font size ('10pt', '11pt' or '12pt').
|
| 228 |
+
#'pointsize': '10pt',
|
| 229 |
+
|
| 230 |
+
# Additional stuff for the LaTeX preamble.
|
| 231 |
+
#'preamble': '',
|
| 232 |
+
|
| 233 |
+
# Latex figure (float) alignment
|
| 234 |
+
#'figure_align': 'htbp',
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
# Grouping the document tree into LaTeX files. List of tuples
|
| 238 |
+
# (source start file, target name, title,
|
| 239 |
+
# author, documentclass [howto, manual, or own class]).
|
| 240 |
+
latex_documents = [
|
| 241 |
+
(master_doc, 'pyrender.tex', u'pyrender Documentation',
|
| 242 |
+
u'Matthew Matl', 'manual'),
|
| 243 |
+
]
|
| 244 |
+
|
| 245 |
+
# The name of an image file (relative to this directory) to place at the top of
|
| 246 |
+
# the title page.
|
| 247 |
+
#latex_logo = None
|
| 248 |
+
|
| 249 |
+
# For "manual" documents, if this is true, then toplevel headings are parts,
|
| 250 |
+
# not chapters.
|
| 251 |
+
#latex_use_parts = False
|
| 252 |
+
|
| 253 |
+
# If true, show page references after internal links.
|
| 254 |
+
#latex_show_pagerefs = False
|
| 255 |
+
|
| 256 |
+
# If true, show URL addresses after external links.
|
| 257 |
+
#latex_show_urls = False
|
| 258 |
+
|
| 259 |
+
# Documents to append as an appendix to all manuals.
|
| 260 |
+
#latex_appendices = []
|
| 261 |
+
|
| 262 |
+
# If false, no module index is generated.
|
| 263 |
+
#latex_domain_indices = True
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
# -- Options for manual page output ---------------------------------------
|
| 267 |
+
|
| 268 |
+
# One entry per manual page. List of tuples
|
| 269 |
+
# (source start file, name, description, authors, manual section).
|
| 270 |
+
man_pages = [
|
| 271 |
+
(master_doc, 'pyrender', u'pyrender Documentation',
|
| 272 |
+
[author], 1)
|
| 273 |
+
]
|
| 274 |
+
|
| 275 |
+
# If true, show URL addresses after external links.
|
| 276 |
+
#man_show_urls = False
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
# -- Options for Texinfo output -------------------------------------------
|
| 280 |
+
|
| 281 |
+
# Grouping the document tree into Texinfo files. List of tuples
|
| 282 |
+
# (source start file, target name, title, author,
|
| 283 |
+
# dir menu entry, description, category)
|
| 284 |
+
texinfo_documents = [
|
| 285 |
+
(master_doc, 'pyrender', u'pyrender Documentation',
|
| 286 |
+
author, 'pyrender', 'One line description of project.',
|
| 287 |
+
'Miscellaneous'),
|
| 288 |
+
]
|
| 289 |
+
|
| 290 |
+
# Documents to append as an appendix to all manuals.
|
| 291 |
+
#texinfo_appendices = []
|
| 292 |
+
|
| 293 |
+
# If false, no module index is generated.
|
| 294 |
+
#texinfo_domain_indices = True
|
| 295 |
+
|
| 296 |
+
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
| 297 |
+
#texinfo_show_urls = 'footnote'
|
| 298 |
+
|
| 299 |
+
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
| 300 |
+
#texinfo_no_detailmenu = False
|
| 301 |
+
|
| 302 |
+
intersphinx_mapping = {
|
| 303 |
+
'python' : ('https://docs.python.org/', None),
|
| 304 |
+
'pyrender' : ('https://pyrender.readthedocs.io/en/latest/', None),
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
# Autosummary fix
|
| 308 |
+
autosummary_generate = True
|
| 309 |
+
|
| 310 |
+
# Try to suppress multiple-definition warnings by always taking the shorter
|
| 311 |
+
# path when two or more paths have the same base module
|
| 312 |
+
|
| 313 |
+
class MyPythonDomain(PythonDomain):
|
| 314 |
+
|
| 315 |
+
def find_obj(self, env, modname, classname, name, type, searchmode=0):
|
| 316 |
+
"""Ensures an object always resolves to the desired module
|
| 317 |
+
if defined there."""
|
| 318 |
+
orig_matches = PythonDomain.find_obj(
|
| 319 |
+
self, env, modname, classname, name, type, searchmode
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
if len(orig_matches) <= 1:
|
| 323 |
+
return orig_matches
|
| 324 |
+
|
| 325 |
+
# If multiple matches, try to take the shortest if all the modules are
|
| 326 |
+
# the same
|
| 327 |
+
first_match_name_sp = orig_matches[0][0].split('.')
|
| 328 |
+
base_name = first_match_name_sp[0]
|
| 329 |
+
min_len = len(first_match_name_sp)
|
| 330 |
+
best_match = orig_matches[0]
|
| 331 |
+
|
| 332 |
+
for match in orig_matches[1:]:
|
| 333 |
+
match_name = match[0]
|
| 334 |
+
match_name_sp = match_name.split('.')
|
| 335 |
+
match_base = match_name_sp[0]
|
| 336 |
+
|
| 337 |
+
# If we have mismatched bases, return them all to trigger warnings
|
| 338 |
+
if match_base != base_name:
|
| 339 |
+
return orig_matches
|
| 340 |
+
|
| 341 |
+
# Otherwise, check and see if it's shorter
|
| 342 |
+
if len(match_name_sp) < min_len:
|
| 343 |
+
min_len = len(match_name_sp)
|
| 344 |
+
best_match = match
|
| 345 |
+
|
| 346 |
+
return (best_match,)
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def setup(sphinx):
|
| 350 |
+
"""Use MyPythonDomain in place of PythonDomain"""
|
| 351 |
+
sphinx.override_domain(MyPythonDomain)
|
| 352 |
+
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/cameras.rst
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _camera_guide:
|
| 2 |
+
|
| 3 |
+
Creating Cameras
|
| 4 |
+
================
|
| 5 |
+
|
| 6 |
+
Pyrender supports three camera types -- :class:`.PerspectiveCamera` and
|
| 7 |
+
:class:`.IntrinsicsCamera` types,
|
| 8 |
+
which render scenes as a human would see them, and
|
| 9 |
+
:class:`.OrthographicCamera` types, which preserve distances between points.
|
| 10 |
+
|
| 11 |
+
Creating cameras is easy -- just specify their basic attributes:
|
| 12 |
+
|
| 13 |
+
>>> pc = pyrender.PerspectiveCamera(yfov=np.pi / 3.0, aspectRatio=1.414)
|
| 14 |
+
>>> oc = pyrender.OrthographicCamera(xmag=1.0, ymag=1.0)
|
| 15 |
+
|
| 16 |
+
For more information, see the Khronos group's documentation here_:
|
| 17 |
+
|
| 18 |
+
.. _here: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#projection-matrices
|
| 19 |
+
|
| 20 |
+
When you add cameras to the scene, make sure that you're using OpenGL camera
|
| 21 |
+
coordinates to specify their pose. See the illustration below for details.
|
| 22 |
+
Basically, the camera z-axis points away from the scene, the x-axis points
|
| 23 |
+
right in image space, and the y-axis points up in image space.
|
| 24 |
+
|
| 25 |
+
.. image:: /_static/camera_coords.png
|
| 26 |
+
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/index.rst
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _guide:
|
| 2 |
+
|
| 3 |
+
User Guide
|
| 4 |
+
==========
|
| 5 |
+
|
| 6 |
+
This section contains guides on how to use Pyrender to quickly visualize
|
| 7 |
+
your 3D data, including a quickstart guide and more detailed descriptions
|
| 8 |
+
of each part of the rendering pipeline.
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
.. toctree::
|
| 12 |
+
:maxdepth: 2
|
| 13 |
+
|
| 14 |
+
quickstart.rst
|
| 15 |
+
models.rst
|
| 16 |
+
lighting.rst
|
| 17 |
+
cameras.rst
|
| 18 |
+
scenes.rst
|
| 19 |
+
offscreen.rst
|
| 20 |
+
viewer.rst
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/lighting.rst
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _lighting_guide:
|
| 2 |
+
|
| 3 |
+
Creating Lights
|
| 4 |
+
===============
|
| 5 |
+
|
| 6 |
+
Pyrender supports three types of punctual light:
|
| 7 |
+
|
| 8 |
+
- :class:`.PointLight`: Point-based light sources, such as light bulbs.
|
| 9 |
+
- :class:`.SpotLight`: A conical light source, like a flashlight.
|
| 10 |
+
- :class:`.DirectionalLight`: A general light that does not attenuate with
|
| 11 |
+
distance.
|
| 12 |
+
|
| 13 |
+
Creating lights is easy -- just specify their basic attributes:
|
| 14 |
+
|
| 15 |
+
>>> pl = pyrender.PointLight(color=[1.0, 1.0, 1.0], intensity=2.0)
|
| 16 |
+
>>> sl = pyrender.SpotLight(color=[1.0, 1.0, 1.0], intensity=2.0,
|
| 17 |
+
... innerConeAngle=0.05, outerConeAngle=0.5)
|
| 18 |
+
>>> dl = pyrender.DirectionalLight(color=[1.0, 1.0, 1.0], intensity=2.0)
|
| 19 |
+
|
| 20 |
+
For more information about how these lighting models are implemented,
|
| 21 |
+
see their class documentation.
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/models.rst
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _model_guide:
|
| 2 |
+
|
| 3 |
+
Loading and Configuring Models
|
| 4 |
+
==============================
|
| 5 |
+
The first step to any rendering application is loading your models.
|
| 6 |
+
Pyrender implements the GLTF 2.0 specification, which means that all
|
| 7 |
+
models are composed of a hierarchy of objects.
|
| 8 |
+
|
| 9 |
+
At the top level, we have a :class:`.Mesh`. The :class:`.Mesh` is
|
| 10 |
+
basically a wrapper of any number of :class:`.Primitive` types,
|
| 11 |
+
which actually represent geometry that can be drawn to the screen.
|
| 12 |
+
|
| 13 |
+
Primitives are composed of a variety of parameters, including
|
| 14 |
+
vertex positions, vertex normals, color and texture information,
|
| 15 |
+
and triangle indices if smooth rendering is desired.
|
| 16 |
+
They can implement point clouds, triangular meshes, or lines
|
| 17 |
+
depending on how you configure their data and set their
|
| 18 |
+
:attr:`.Primitive.mode` parameter.
|
| 19 |
+
|
| 20 |
+
Although you can create primitives yourself if you want to,
|
| 21 |
+
it's probably easier to just use the utility functions provided
|
| 22 |
+
in the :class:`.Mesh` class.
|
| 23 |
+
|
| 24 |
+
Creating Triangular Meshes
|
| 25 |
+
--------------------------
|
| 26 |
+
|
| 27 |
+
Simple Construction
|
| 28 |
+
~~~~~~~~~~~~~~~~~~~
|
| 29 |
+
Pyrender allows you to create a :class:`.Mesh` containing a
|
| 30 |
+
triangular mesh model directly from a :class:`~trimesh.base.Trimesh` object
|
| 31 |
+
using the :meth:`.Mesh.from_trimesh` static method.
|
| 32 |
+
|
| 33 |
+
>>> import trimesh
|
| 34 |
+
>>> import pyrender
|
| 35 |
+
>>> import numpy as np
|
| 36 |
+
>>> tm = trimesh.load('examples/models/fuze.obj')
|
| 37 |
+
>>> m = pyrender.Mesh.from_trimesh(tm)
|
| 38 |
+
>>> m.primitives
|
| 39 |
+
[<pyrender.primitive.Primitive at 0x7fbb0af60e50>]
|
| 40 |
+
|
| 41 |
+
You can also create a single :class:`.Mesh` from a list of
|
| 42 |
+
:class:`~trimesh.base.Trimesh` objects:
|
| 43 |
+
|
| 44 |
+
>>> tms = [trimesh.creation.icosahedron(), trimesh.creation.cylinder()]
|
| 45 |
+
>>> m = pyrender.Mesh.from_trimesh(tms)
|
| 46 |
+
[<pyrender.primitive.Primitive at 0x7fbb0c2b74d0>,
|
| 47 |
+
<pyrender.primitive.Primitive at 0x7fbb0c2b7550>]
|
| 48 |
+
|
| 49 |
+
Vertex Smoothing
|
| 50 |
+
~~~~~~~~~~~~~~~~
|
| 51 |
+
|
| 52 |
+
The :meth:`.Mesh.from_trimesh` method has a few additional optional parameters.
|
| 53 |
+
If you want to render the mesh without interpolating face normals, which can
|
| 54 |
+
be useful for meshes that are supposed to be angular (e.g. a cube), you
|
| 55 |
+
can specify ``smooth=False``.
|
| 56 |
+
|
| 57 |
+
>>> m = pyrender.Mesh.from_trimesh(tm, smooth=False)
|
| 58 |
+
|
| 59 |
+
Per-Face or Per-Vertex Coloration
|
| 60 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 61 |
+
|
| 62 |
+
If you have an untextured trimesh, you can color it in with per-face or
|
| 63 |
+
per-vertex colors:
|
| 64 |
+
|
| 65 |
+
>>> tm.visual.vertex_colors = np.random.uniform(size=tm.vertices.shape)
|
| 66 |
+
>>> tm.visual.face_colors = np.random.uniform(size=tm.faces.shape)
|
| 67 |
+
>>> m = pyrender.Mesh.from_trimesh(tm)
|
| 68 |
+
|
| 69 |
+
Instancing
|
| 70 |
+
~~~~~~~~~~
|
| 71 |
+
|
| 72 |
+
If you want to render many copies of the same mesh at different poses,
|
| 73 |
+
you can statically create a vast array of them in an efficient manner.
|
| 74 |
+
Simply specify the ``poses`` parameter to be a list of ``N`` 4x4 homogenous
|
| 75 |
+
transformation matrics that position the meshes relative to their common
|
| 76 |
+
base frame:
|
| 77 |
+
|
| 78 |
+
>>> tfs = np.tile(np.eye(4), (3,1,1))
|
| 79 |
+
>>> tfs[1,:3,3] = [0.1, 0.0, 0.0]
|
| 80 |
+
>>> tfs[2,:3,3] = [0.2, 0.0, 0.0]
|
| 81 |
+
>>> tfs
|
| 82 |
+
array([[[1. , 0. , 0. , 0. ],
|
| 83 |
+
[0. , 1. , 0. , 0. ],
|
| 84 |
+
[0. , 0. , 1. , 0. ],
|
| 85 |
+
[0. , 0. , 0. , 1. ]],
|
| 86 |
+
[[1. , 0. , 0. , 0.1],
|
| 87 |
+
[0. , 1. , 0. , 0. ],
|
| 88 |
+
[0. , 0. , 1. , 0. ],
|
| 89 |
+
[0. , 0. , 0. , 1. ]],
|
| 90 |
+
[[1. , 0. , 0. , 0.2],
|
| 91 |
+
[0. , 1. , 0. , 0. ],
|
| 92 |
+
[0. , 0. , 1. , 0. ],
|
| 93 |
+
[0. , 0. , 0. , 1. ]]])
|
| 94 |
+
|
| 95 |
+
>>> m = pyrender.Mesh.from_trimesh(tm, poses=tfs)
|
| 96 |
+
|
| 97 |
+
Custom Materials
|
| 98 |
+
~~~~~~~~~~~~~~~~
|
| 99 |
+
|
| 100 |
+
You can also specify a custom material for any triangular mesh you create
|
| 101 |
+
in the ``material`` parameter of :meth:`.Mesh.from_trimesh`.
|
| 102 |
+
The main material supported by Pyrender is the
|
| 103 |
+
:class:`.MetallicRoughnessMaterial`.
|
| 104 |
+
The metallic-roughness model supports rendering highly-realistic objects across
|
| 105 |
+
a wide gamut of materials.
|
| 106 |
+
|
| 107 |
+
For more information, see the documentation of the
|
| 108 |
+
:class:`.MetallicRoughnessMaterial` constructor or look at the Khronos_
|
| 109 |
+
documentation for more information.
|
| 110 |
+
|
| 111 |
+
.. _Khronos: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materials
|
| 112 |
+
|
| 113 |
+
Creating Point Clouds
|
| 114 |
+
---------------------
|
| 115 |
+
|
| 116 |
+
Point Sprites
|
| 117 |
+
~~~~~~~~~~~~~
|
| 118 |
+
Pyrender also allows you to create a :class:`.Mesh` containing a
|
| 119 |
+
point cloud directly from :class:`numpy.ndarray` instances
|
| 120 |
+
using the :meth:`.Mesh.from_points` static method.
|
| 121 |
+
|
| 122 |
+
Simply provide a list of points and optional per-point colors and normals.
|
| 123 |
+
|
| 124 |
+
>>> pts = tm.vertices.copy()
|
| 125 |
+
>>> colors = np.random.uniform(size=pts.shape)
|
| 126 |
+
>>> m = pyrender.Mesh.from_points(pts, colors=colors)
|
| 127 |
+
|
| 128 |
+
Point clouds created in this way will be rendered as square point sprites.
|
| 129 |
+
|
| 130 |
+
.. image:: /_static/points.png
|
| 131 |
+
|
| 132 |
+
Point Spheres
|
| 133 |
+
~~~~~~~~~~~~~
|
| 134 |
+
If you have a monochromatic point cloud and would like to render it with
|
| 135 |
+
spheres, you can render it by instancing a spherical trimesh:
|
| 136 |
+
|
| 137 |
+
>>> sm = trimesh.creation.uv_sphere(radius=0.1)
|
| 138 |
+
>>> sm.visual.vertex_colors = [1.0, 0.0, 0.0]
|
| 139 |
+
>>> tfs = np.tile(np.eye(4), (len(pts), 1, 1))
|
| 140 |
+
>>> tfs[:,:3,3] = pts
|
| 141 |
+
>>> m = pyrender.Mesh.from_trimesh(sm, poses=tfs)
|
| 142 |
+
|
| 143 |
+
.. image:: /_static/points2.png
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/examples/offscreen.rst
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _offscreen_guide:
|
| 2 |
+
|
| 3 |
+
Offscreen Rendering
|
| 4 |
+
===================
|
| 5 |
+
|
| 6 |
+
.. note::
|
| 7 |
+
If you're using a headless server, you'll need to use either EGL (for
|
| 8 |
+
GPU-accelerated rendering) or OSMesa (for CPU-only software rendering).
|
| 9 |
+
If you're using OSMesa, be sure that you've installed it properly. See
|
| 10 |
+
:ref:`osmesa` for details.
|
| 11 |
+
|
| 12 |
+
Choosing a Backend
|
| 13 |
+
------------------
|
| 14 |
+
|
| 15 |
+
Once you have a scene set up with its geometry, cameras, and lights,
|
| 16 |
+
you can render it using the :class:`.OffscreenRenderer`. Pyrender supports
|
| 17 |
+
three backends for offscreen rendering:
|
| 18 |
+
|
| 19 |
+
- Pyglet, the same engine that runs the viewer. This requires an active
|
| 20 |
+
display manager, so you can't run it on a headless server. This is the
|
| 21 |
+
default option.
|
| 22 |
+
- OSMesa, a software renderer.
|
| 23 |
+
- EGL, which allows for GPU-accelerated rendering without a display manager.
|
| 24 |
+
|
| 25 |
+
If you want to use OSMesa or EGL, you need to set the ``PYOPENGL_PLATFORM``
|
| 26 |
+
environment variable before importing pyrender or any other OpenGL library.
|
| 27 |
+
You can do this at the command line:
|
| 28 |
+
|
| 29 |
+
.. code-block:: bash
|
| 30 |
+
|
| 31 |
+
PYOPENGL_PLATFORM=osmesa python render.py
|
| 32 |
+
|
| 33 |
+
or at the top of your Python script:
|
| 34 |
+
|
| 35 |
+
.. code-block:: bash
|
| 36 |
+
|
| 37 |
+
# Top of main python script
|
| 38 |
+
import os
|
| 39 |
+
os.environ['PYOPENGL_PLATFORM'] = 'egl'
|
| 40 |
+
|
| 41 |
+
The handle for EGL is ``egl``, and the handle for OSMesa is ``osmesa``.
|
| 42 |
+
|
| 43 |
+
Running the Renderer
|
| 44 |
+
--------------------
|
| 45 |
+
|
| 46 |
+
Once you've set your environment variable appropriately, create your scene and
|
| 47 |
+
then configure the :class:`.OffscreenRenderer` object with a window width,
|
| 48 |
+
a window height, and a size for point-cloud points:
|
| 49 |
+
|
| 50 |
+
>>> r = pyrender.OffscreenRenderer(viewport_width=640,
|
| 51 |
+
... viewport_height=480,
|
| 52 |
+
... point_size=1.0)
|
| 53 |
+
|
| 54 |
+
Then, just call the :meth:`.OffscreenRenderer.render` function:
|
| 55 |
+
|
| 56 |
+
>>> color, depth = r.render(scene)
|
| 57 |
+
|
| 58 |
+
.. image:: /_static/scene.png
|
| 59 |
+
|
| 60 |
+
This will return a ``(w,h,3)`` channel floating-point color image and
|
| 61 |
+
a ``(w,h)`` floating-point depth image rendered from the scene's main camera.
|
| 62 |
+
|
| 63 |
+
You can customize the rendering process by using flag options from
|
| 64 |
+
:class:`.RenderFlags` and bitwise or-ing them together. For example,
|
| 65 |
+
the following code renders a color image with an alpha channel
|
| 66 |
+
and enables shadow mapping for all directional lights:
|
| 67 |
+
|
| 68 |
+
>>> flags = RenderFlags.RGBA | RenderFlags.SHADOWS_DIRECTIONAL
|
| 69 |
+
>>> color, depth = r.render(scene, flags=flags)
|
| 70 |
+
|
| 71 |
+
Once you're done with the offscreen renderer, you need to close it before you
|
| 72 |
+
can run a different renderer or open the viewer for the same scene:
|
| 73 |
+
|
| 74 |
+
>>> r.delete()
|
| 75 |
+
|
| 76 |
+
Google CoLab Examples
|
| 77 |
+
---------------------
|
| 78 |
+
|
| 79 |
+
For a minimal working example of offscreen rendering using OSMesa,
|
| 80 |
+
see the `OSMesa Google CoLab notebook`_.
|
| 81 |
+
|
| 82 |
+
.. _OSMesa Google CoLab notebook: https://colab.research.google.com/drive/1Z71mHIc-Sqval92nK290vAsHZRUkCjUx
|
| 83 |
+
|
| 84 |
+
For a minimal working example of offscreen rendering using EGL,
|
| 85 |
+
see the `EGL Google CoLab notebook`_.
|
| 86 |
+
|
| 87 |
+
.. _EGL Google CoLab notebook: https://colab.research.google.com/drive/1rTLHk0qxh4dn8KNe-mCnN8HAWdd2_BEh
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/docs/source/index.rst
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. core documentation master file, created by
|
| 2 |
+
sphinx-quickstart on Sun Oct 16 14:33:48 2016.
|
| 3 |
+
You can adapt this file completely to your liking, but it should at least
|
| 4 |
+
contain the root `toctree` directive.
|
| 5 |
+
|
| 6 |
+
Pyrender Documentation
|
| 7 |
+
========================
|
| 8 |
+
Pyrender is a pure Python (2.7, 3.4, 3.5, 3.6) library for physically-based
|
| 9 |
+
rendering and visualization.
|
| 10 |
+
It is designed to meet the glTF 2.0 specification_ from Khronos
|
| 11 |
+
|
| 12 |
+
.. _specification: https://www.khronos.org/gltf/
|
| 13 |
+
|
| 14 |
+
Pyrender is lightweight, easy to install, and simple to use.
|
| 15 |
+
It comes packaged with both an intuitive scene viewer and a headache-free
|
| 16 |
+
offscreen renderer with support for GPU-accelerated rendering on headless
|
| 17 |
+
servers, which makes it perfect for machine learning applications.
|
| 18 |
+
Check out the :ref:`guide` for a full tutorial, or fork me on
|
| 19 |
+
Github_.
|
| 20 |
+
|
| 21 |
+
.. _Github: https://github.com/mmatl/pyrender
|
| 22 |
+
|
| 23 |
+
.. image:: _static/rotation.gif
|
| 24 |
+
|
| 25 |
+
.. image:: _static/damaged_helmet.png
|
| 26 |
+
|
| 27 |
+
.. toctree::
|
| 28 |
+
:maxdepth: 2
|
| 29 |
+
|
| 30 |
+
install/index.rst
|
| 31 |
+
examples/index.rst
|
| 32 |
+
api/index.rst
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
Indices and tables
|
| 36 |
+
==================
|
| 37 |
+
|
| 38 |
+
* :ref:`genindex`
|
| 39 |
+
* :ref:`modindex`
|
| 40 |
+
* :ref:`search`
|
| 41 |
+
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/requirements.txt
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
freetype-py
|
| 2 |
+
imageio
|
| 3 |
+
networkx
|
| 4 |
+
numpy
|
| 5 |
+
Pillow
|
| 6 |
+
pyglet==1.4.0a1
|
| 7 |
+
PyOpenGL
|
| 8 |
+
PyOpenGL_accelerate
|
| 9 |
+
six
|
| 10 |
+
trimesh
|
| 11 |
+
sphinx
|
| 12 |
+
sphinx_rtd_theme
|
| 13 |
+
sphinx-automodapi
|
| 14 |
+
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/pyrender/setup.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Setup of pyrender Python codebase.
|
| 3 |
+
|
| 4 |
+
Author: Matthew Matl
|
| 5 |
+
"""
|
| 6 |
+
import sys
|
| 7 |
+
from setuptools import setup
|
| 8 |
+
|
| 9 |
+
# load __version__
|
| 10 |
+
exec(open('pyrender/version.py').read())
|
| 11 |
+
|
| 12 |
+
def get_imageio_dep():
|
| 13 |
+
if sys.version[0] == "2":
|
| 14 |
+
return 'imageio<=2.6.1'
|
| 15 |
+
return 'imageio'
|
| 16 |
+
|
| 17 |
+
requirements = [
|
| 18 |
+
'freetype-py', # For font loading
|
| 19 |
+
get_imageio_dep(), # For Image I/O
|
| 20 |
+
'networkx', # For the scene graph
|
| 21 |
+
'numpy', # Numpy
|
| 22 |
+
'Pillow', # For Trimesh texture conversions
|
| 23 |
+
'pyglet>=1.4.10', # For the pyglet viewer
|
| 24 |
+
'PyOpenGL~=3.1.0', # For OpenGL
|
| 25 |
+
# 'PyOpenGL_accelerate~=3.1.0', # For OpenGL
|
| 26 |
+
'scipy', # Because of trimesh missing dep
|
| 27 |
+
'six', # For Python 2/3 interop
|
| 28 |
+
'trimesh', # For meshes
|
| 29 |
+
]
|
| 30 |
+
|
| 31 |
+
dev_requirements = [
|
| 32 |
+
'flake8', # Code formatting checker
|
| 33 |
+
'pre-commit', # Pre-commit hooks
|
| 34 |
+
'pytest', # Code testing
|
| 35 |
+
'pytest-cov', # Coverage testing
|
| 36 |
+
'tox', # Automatic virtualenv testing
|
| 37 |
+
]
|
| 38 |
+
|
| 39 |
+
docs_requirements = [
|
| 40 |
+
'sphinx', # General doc library
|
| 41 |
+
'sphinx_rtd_theme', # RTD theme for sphinx
|
| 42 |
+
'sphinx-automodapi' # For generating nice tables
|
| 43 |
+
]
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
setup(
|
| 47 |
+
name = 'pyrender',
|
| 48 |
+
version=__version__,
|
| 49 |
+
description='Easy-to-use Python renderer for 3D visualization',
|
| 50 |
+
long_description='A simple implementation of Physically-Based Rendering '
|
| 51 |
+
'(PBR) in Python. Compliant with the glTF 2.0 standard.',
|
| 52 |
+
author='Matthew Matl',
|
| 53 |
+
author_email='matthewcmatl@gmail.com',
|
| 54 |
+
license='MIT License',
|
| 55 |
+
url = 'https://github.com/mmatl/pyrender',
|
| 56 |
+
classifiers = [
|
| 57 |
+
'Development Status :: 4 - Beta',
|
| 58 |
+
'License :: OSI Approved :: MIT License',
|
| 59 |
+
'Operating System :: POSIX :: Linux',
|
| 60 |
+
'Operating System :: MacOS :: MacOS X',
|
| 61 |
+
'Programming Language :: Python :: 2.7',
|
| 62 |
+
'Programming Language :: Python :: 3.5',
|
| 63 |
+
'Programming Language :: Python :: 3.6',
|
| 64 |
+
'Natural Language :: English',
|
| 65 |
+
'Topic :: Scientific/Engineering'
|
| 66 |
+
],
|
| 67 |
+
keywords = 'rendering graphics opengl 3d visualization pbr gltf',
|
| 68 |
+
packages = ['pyrender', 'pyrender.platforms'],
|
| 69 |
+
setup_requires = requirements,
|
| 70 |
+
install_requires = requirements,
|
| 71 |
+
extras_require={
|
| 72 |
+
'dev': dev_requirements,
|
| 73 |
+
'docs': docs_requirements,
|
| 74 |
+
},
|
| 75 |
+
include_package_data=True
|
| 76 |
+
)
|
c125d6fbf8bda65345b6edda6e04f79faf4d67cd/requirements.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
git+https://github.com/openai/CLIP.git
|
| 2 |
+
numpy==1.23.3
|
| 3 |
+
matplotlib==3.4.3
|
| 4 |
+
matplotlib-inline==0.1.2
|
| 5 |
+
transformers
|
| 6 |
+
h5py
|
| 7 |
+
smplx
|
| 8 |
+
shapely
|
| 9 |
+
freetype-py
|
| 10 |
+
imageio
|
| 11 |
+
networkx
|
| 12 |
+
numpy
|
| 13 |
+
Pillow
|
| 14 |
+
pyglet==1.4.0a1
|
| 15 |
+
PyOpenGL==3.1.7
|
| 16 |
+
PyOpenGL_accelerate==3.1.7
|
| 17 |
+
six
|
| 18 |
+
trimesh
|
| 19 |
+
sphinx
|
| 20 |
+
sphinx_rtd_theme
|
| 21 |
+
sphinx-automodapi
|
| 22 |
+
mapbox_earcut
|
| 23 |
+
chumpy
|
| 24 |
+
gdown
|
| 25 |
+
MoviePy
|
| 26 |
+
ffmpeg
|
| 27 |
+
torch
|
checkpoints/kit/kit/Comp_v6_KLD005/opt.txt
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
------------ Options -------------
|
| 2 |
+
batch_size: 32
|
| 3 |
+
checkpoints_dir: ./checkpoints
|
| 4 |
+
dataset_name: kit
|
| 5 |
+
decomp_name: Decomp_SP001_SM001_H512
|
| 6 |
+
dim_att_vec: 512
|
| 7 |
+
dim_dec_hidden: 1024
|
| 8 |
+
dim_movement2_dec_hidden: 512
|
| 9 |
+
dim_movement_dec_hidden: 512
|
| 10 |
+
dim_movement_enc_hidden: 512
|
| 11 |
+
dim_movement_latent: 512
|
| 12 |
+
dim_msd_hidden: 512
|
| 13 |
+
dim_pos_hidden: 1024
|
| 14 |
+
dim_pri_hidden: 1024
|
| 15 |
+
dim_seq_de_hidden: 512
|
| 16 |
+
dim_seq_en_hidden: 512
|
| 17 |
+
dim_text_hidden: 512
|
| 18 |
+
dim_z: 128
|
| 19 |
+
early_stop_count: 3
|
| 20 |
+
estimator_mod: bigru
|
| 21 |
+
eval_every_e: 5
|
| 22 |
+
feat_bias: 5
|
| 23 |
+
fixed_steps: 5
|
| 24 |
+
gpu_id: 2
|
| 25 |
+
input_z: False
|
| 26 |
+
is_continue: True
|
| 27 |
+
is_train: True
|
| 28 |
+
lambda_fake: 10
|
| 29 |
+
lambda_gan_l: 0.1
|
| 30 |
+
lambda_gan_mt: 0.1
|
| 31 |
+
lambda_gan_mv: 0.1
|
| 32 |
+
lambda_kld: 0.005
|
| 33 |
+
lambda_rec: 1
|
| 34 |
+
lambda_rec_init: 1
|
| 35 |
+
lambda_rec_mot: 1
|
| 36 |
+
lambda_rec_mov: 1
|
| 37 |
+
log_every: 50
|
| 38 |
+
lr: 0.0002
|
| 39 |
+
max_sub_epoch: 50
|
| 40 |
+
max_text_len: 20
|
| 41 |
+
n_layers_dec: 1
|
| 42 |
+
n_layers_msd: 2
|
| 43 |
+
n_layers_pos: 1
|
| 44 |
+
n_layers_pri: 1
|
| 45 |
+
n_layers_seq_de: 2
|
| 46 |
+
n_layers_seq_en: 1
|
| 47 |
+
name: Comp_v6_KLD005
|
| 48 |
+
num_experts: 4
|
| 49 |
+
save_every_e: 10
|
| 50 |
+
save_latest: 500
|
| 51 |
+
text_enc_mod: bigru
|
| 52 |
+
tf_ratio: 0.4
|
| 53 |
+
unit_length: 4
|
| 54 |
+
-------------- End ----------------
|
checkpoints/kit/kit/text_mot_match/eval/E005.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
3.134 2.099 3.881 3.921 2.285 3.744 2.655 1.721 1.946 2.250 2.924 6.876 3.502 1.861 5.818 3.507 8.488 1.692 1.776 4.583 1.980 2.727 3.955 3.607 5.916 3.186 3.832 1.700 1.664 2.896 3.318 2.128
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
4.801 7.064 6.329 6.437 3.465 6.363 8.897 6.655 10.889 6.358 8.022 8.131 3.472 9.457 10.489 3.636 9.595 9.930 12.090 6.514 10.054 2.828 11.206 9.073 6.163 10.645 7.251 7.684 13.491 3.869 8.233 5.459
|
checkpoints/kit/kit/text_mot_match/eval/E010.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
1.908 2.165 2.165 3.126 1.697 1.459 2.442 1.858 1.139 1.622 2.096 2.478 4.929 3.777 4.985 2.417 1.533 2.688 4.114 2.632 3.061 2.188 5.152 2.518 3.600 4.669 8.488 2.839 3.971 2.343 3.527 3.871
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
11.756 2.332 11.629 8.415 1.482 5.168 9.379 10.146 11.044 11.025 11.965 5.885 10.289 5.902 8.258 11.976 8.073 7.024 9.437 4.496 3.879 7.686 4.651 4.576 2.901 14.143 2.428 8.259 5.828 7.087 12.836 13.889
|
checkpoints/kit/kit/text_mot_match/eval/E015.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
3.850 4.731 5.109 2.265 1.610 2.926 2.168 2.637 2.084 1.510 2.175 2.338 1.857 5.098 1.801 2.207 2.303 1.409 2.606 1.605 2.917 1.752 3.372 2.717 3.066 2.843 2.329 2.640 2.341 4.156 3.331 8.131
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
6.084 8.782 9.299 5.081 10.574 12.375 6.629 3.773 12.802 17.637 15.346 12.001 11.877 8.885 5.704 7.574 10.524 11.066 13.124 9.736 2.497 16.383 10.255 14.209 15.128 12.731 10.557 15.367 11.058 11.331 2.683 9.325
|
checkpoints/kit/kit/text_mot_match/eval/E020.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
1.720 2.590 1.074 3.083 2.166 2.835 6.362 2.816 2.871 1.793 3.421 6.261 1.957 5.514 2.695 2.254 1.668 1.689 4.970 2.880 1.581 4.509 2.255 3.921 2.240 2.384 2.844 2.736 4.322 3.335 3.728 2.677
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
11.181 11.564 13.729 7.366 12.419 12.882 8.641 18.567 7.485 7.284 11.086 8.577 5.952 4.970 14.443 13.611 11.813 10.937 13.638 11.140 14.285 8.947 13.830 14.733 11.218 3.280 2.429 11.807 11.222 9.967 10.158 10.779
|
checkpoints/kit/kit/text_mot_match/eval/E025.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
3.553 1.793 2.145 1.160 1.568 0.923 2.591 7.707 2.747 12.233 5.216 1.305 1.507 2.035 1.736 2.155 1.751 1.618 2.041 2.239 2.825 2.352 3.072 2.020 1.761 5.428 2.252 1.604 2.319 2.266 3.278 3.328
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
11.462 3.411 1.404 11.581 3.764 11.409 14.073 4.259 12.997 3.549 7.593 12.030 12.991 7.647 12.337 12.592 14.496 14.496 13.530 13.371 12.775 14.828 11.200 17.627 9.128 9.617 9.297 6.782 14.615 2.304 9.306 10.946
|
checkpoints/kit/kit/text_mot_match/eval/E030.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
7.378 4.702 10.228 2.154 7.611 0.943 1.770 1.255 5.552 3.161 2.180 2.252 3.278 1.767 1.787 1.450 1.546 2.051 1.476 1.828 1.829 1.423 1.150 2.198 9.395 1.748 2.700 2.304 3.917 4.163 2.811 2.562
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
3.884 11.919 5.835 6.898 5.753 19.333 10.732 16.049 10.197 11.546 13.682 12.106 12.993 11.981 13.373 13.464 11.788 11.267 16.719 9.747 4.581 14.785 13.978 9.505 7.465 11.392 11.481 9.757 11.077 16.272 15.701 12.256
|
checkpoints/kit/kit/text_mot_match/eval/E035.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
2.095 9.020 2.176 1.030 1.653 1.306 1.963 4.513 1.906 1.186 2.731 1.678 1.395 2.232 1.394 4.946 1.057 4.450 3.671 2.931 3.655 1.374 1.361 6.001 5.699 1.579 10.237 2.940 1.622 2.032 7.194 1.069
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
13.345 14.638 14.061 19.142 8.843 12.152 15.600 7.368 15.635 13.863 15.644 3.686 13.388 15.807 20.432 11.571 13.876 8.513 13.236 19.330 8.771 14.880 13.005 10.810 12.430 10.102 4.440 18.083 3.135 13.201 5.465 8.700
|
checkpoints/kit/kit/text_mot_match/eval/E040.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
2.531 1.118 4.384 1.787 1.595 2.184 1.371 1.264 2.086 1.477 1.123 1.240 1.040 11.428 3.469 1.511 1.782 2.528 6.645 1.197 1.967 1.188 3.484 5.019 1.024 1.892 3.185 1.017 2.682 1.116 1.047 1.691
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
12.010 18.700 16.704 10.839 15.398 8.070 9.340 14.570 8.946 18.806 4.114 12.699 11.821 4.733 15.875 14.064 17.230 16.277 14.135 22.007 18.454 13.210 8.825 8.937 9.880 16.306 13.555 13.649 5.738 12.752 12.994 1.158
|
checkpoints/kit/kit/text_mot_match/eval/E045.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
1.412 2.614 2.471 0.842 2.629 7.116 7.275 5.793 1.034 8.645 4.851 2.357 1.155 1.208 1.168 1.698 1.550 1.132 7.423 2.531 1.147 2.240 1.575 1.232 1.455 2.134 1.421 3.723 2.887 1.759 3.806 4.387
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
10.150 13.459 14.645 13.875 8.746 12.398 13.303 16.393 22.565 9.265 13.325 9.629 18.867 15.043 18.596 19.413 17.903 8.695 4.948 13.620 14.171 19.699 15.496 11.753 12.914 17.017 7.990 14.401 11.768 8.648 13.926 13.402
|
checkpoints/kit/kit/text_mot_match/eval/E050.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Positive Pairs Distance
|
| 2 |
+
5.304 2.162 1.767 7.675 1.446 5.696 2.277 0.717 1.477 12.431 3.673 1.286 2.633 1.883 2.555 1.399 11.572 1.303 3.411 1.521 3.885 0.984 1.210 1.038 5.024 5.886 1.283 1.026 2.056 1.738 3.654 2.659
|
| 3 |
+
Negative Pairs Distance
|
| 4 |
+
17.692 17.993 15.431 10.788 22.819 16.591 23.417 13.203 7.453 17.521 19.102 17.595 7.883 20.475 11.371 17.178 5.380 4.884 17.968 18.298 13.822 19.112 11.842 10.838 14.131 14.906 16.252 14.499 13.878 2.300 11.417 21.558
|