push models
Browse files- .gitignore +0 -1
- app.py +20 -2
- download_smpl.py +46 -0
- smpl/__MACOSX/._smpl +0 -0
- smpl/__MACOSX/smpl/._.DS_Store +0 -0
- smpl/__MACOSX/smpl/.___init__.py +0 -0
- smpl/__MACOSX/smpl/._models +0 -0
- smpl/__MACOSX/smpl/._smpl_webuser +0 -0
- smpl/__MACOSX/smpl/models/._basicModel_f_lbs_10_207_0_v1.0.0.pkl +3 -0
- smpl/__MACOSX/smpl/models/._basicmodel_m_lbs_10_207_0_v1.0.0.pkl +3 -0
- smpl/__MACOSX/smpl/smpl_webuser/._LICENSE.txt +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/._README.txt +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/.___init__.py +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/._hello_world +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/._lbs.py +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/._posemapper.py +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/._serialization.py +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/._verts.py +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/hello_world/._hello_smpl.py +0 -0
- smpl/__MACOSX/smpl/smpl_webuser/hello_world/._render_smpl.py +0 -0
- smpl/smpl/__init__.py +14 -0
- smpl/smpl/models/basicModel_f_lbs_10_207_0_v1.0.0.pkl +3 -0
- smpl/smpl/models/basicmodel_m_lbs_10_207_0_v1.0.0.pkl +3 -0
- smpl/smpl/smpl_webuser/LICENSE.txt +29 -0
- smpl/smpl/smpl_webuser/README.txt +56 -0
- smpl/smpl/smpl_webuser/__init__.py +13 -0
- smpl/smpl/smpl_webuser/hello_world/hello_smpl.py +64 -0
- smpl/smpl/smpl_webuser/hello_world/render_smpl.py +97 -0
- smpl/smpl/smpl_webuser/lbs.py +80 -0
- smpl/smpl/smpl_webuser/posemapper.py +49 -0
- smpl/smpl/smpl_webuser/serialization.py +137 -0
- smpl/smpl/smpl_webuser/verts.py +103 -0
- smpl_generator.py +35 -15
.gitignore
CHANGED
|
@@ -35,7 +35,6 @@ ENV/
|
|
| 35 |
|
| 36 |
# SMPL models (large files, download separately)
|
| 37 |
models/smpl/
|
| 38 |
-
*.pkl
|
| 39 |
*.npz
|
| 40 |
|
| 41 |
# Trained models
|
|
|
|
| 35 |
|
| 36 |
# SMPL models (large files, download separately)
|
| 37 |
models/smpl/
|
|
|
|
| 38 |
*.npz
|
| 39 |
|
| 40 |
# Trained models
|
app.py
CHANGED
|
@@ -8,12 +8,25 @@ from typing import Optional
|
|
| 8 |
import io
|
| 9 |
import numpy as np
|
| 10 |
from PIL import Image
|
|
|
|
| 11 |
|
| 12 |
from measurement_processor import process_measurements
|
| 13 |
from beta_regressor import predict_betas
|
| 14 |
from smpl_generator import generate_mesh
|
| 15 |
from renderer import render_avatar
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
app = FastAPI(
|
| 18 |
title="Avatar Generation Service",
|
| 19 |
description="Generate 2D avatar images from body measurements using SMPL"
|
|
@@ -69,7 +82,7 @@ async def generate_avatar(measurements: MeasurementRequest):
|
|
| 69 |
measurements_dict = measurements.model_dump(exclude_none=True)
|
| 70 |
normalized = process_measurements(measurements_dict)
|
| 71 |
betas = predict_betas(normalized)
|
| 72 |
-
vertices, faces = generate_mesh(betas)
|
| 73 |
img_np = render_avatar(vertices, faces)
|
| 74 |
|
| 75 |
if img_np.dtype != np.uint8:
|
|
@@ -85,7 +98,12 @@ async def generate_avatar(measurements: MeasurementRequest):
|
|
| 85 |
except ValueError as e:
|
| 86 |
raise HTTPException(status_code=400, detail=str(e))
|
| 87 |
except FileNotFoundError as e:
|
| 88 |
-
raise HTTPException(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
except Exception as e:
|
| 90 |
raise HTTPException(status_code=500, detail=f"Error generating avatar: {str(e)}")
|
| 91 |
|
|
|
|
| 8 |
import io
|
| 9 |
import numpy as np
|
| 10 |
from PIL import Image
|
| 11 |
+
from pathlib import Path
|
| 12 |
|
| 13 |
from measurement_processor import process_measurements
|
| 14 |
from beta_regressor import predict_betas
|
| 15 |
from smpl_generator import generate_mesh
|
| 16 |
from renderer import render_avatar
|
| 17 |
|
| 18 |
+
SMPL_MODEL_PATH = os.getenv("SMPL_MODEL_PATH", "smpl/smpl")
|
| 19 |
+
|
| 20 |
+
smpl_models_dir = Path("smpl/smpl/models")
|
| 21 |
+
if smpl_models_dir.exists():
|
| 22 |
+
print(f"Found SMPL models in {smpl_models_dir}")
|
| 23 |
+
model_files = list(smpl_models_dir.glob("*.pkl"))
|
| 24 |
+
for f in model_files:
|
| 25 |
+
print(f" - {f.name}")
|
| 26 |
+
else:
|
| 27 |
+
print(f"Warning: SMPL models not found in {smpl_models_dir}")
|
| 28 |
+
print(f"Looking in {SMPL_MODEL_PATH}...")
|
| 29 |
+
|
| 30 |
app = FastAPI(
|
| 31 |
title="Avatar Generation Service",
|
| 32 |
description="Generate 2D avatar images from body measurements using SMPL"
|
|
|
|
| 82 |
measurements_dict = measurements.model_dump(exclude_none=True)
|
| 83 |
normalized = process_measurements(measurements_dict)
|
| 84 |
betas = predict_betas(normalized)
|
| 85 |
+
vertices, faces = generate_mesh(betas, model_path=SMPL_MODEL_PATH)
|
| 86 |
img_np = render_avatar(vertices, faces)
|
| 87 |
|
| 88 |
if img_np.dtype != np.uint8:
|
|
|
|
| 98 |
except ValueError as e:
|
| 99 |
raise HTTPException(status_code=400, detail=str(e))
|
| 100 |
except FileNotFoundError as e:
|
| 101 |
+
raise HTTPException(
|
| 102 |
+
status_code=500,
|
| 103 |
+
detail=f"SMPL model not found: {str(e)}. "
|
| 104 |
+
f"Please ensure SMPL model files are in {SMPL_MODEL_PATH}. "
|
| 105 |
+
f"Download from https://smpl.is.tue.mpg.de/ or set SMPL_MODEL_PATH environment variable."
|
| 106 |
+
)
|
| 107 |
except Exception as e:
|
| 108 |
raise HTTPException(status_code=500, detail=f"Error generating avatar: {str(e)}")
|
| 109 |
|
download_smpl.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from huggingface_hub import hf_hub_download
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
def download_smpl_model(model_path: str = "models/smpl"):
|
| 7 |
+
model_dir = Path(model_path)
|
| 8 |
+
model_dir.mkdir(parents=True, exist_ok=True)
|
| 9 |
+
|
| 10 |
+
repo_id = os.getenv("SMPL_MODEL_REPO", "nghorbani/smpl")
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
print(f"Attempting to download SMPL model from {repo_id}...")
|
| 14 |
+
|
| 15 |
+
files_to_download = [
|
| 16 |
+
"SMPL_NEUTRAL.pkl",
|
| 17 |
+
"SMPL_MALE.pkl",
|
| 18 |
+
"SMPL_FEMALE.pkl"
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
for filename in files_to_download:
|
| 22 |
+
try:
|
| 23 |
+
file_path = hf_hub_download(
|
| 24 |
+
repo_id=repo_id,
|
| 25 |
+
filename=filename,
|
| 26 |
+
local_dir=str(model_dir),
|
| 27 |
+
local_dir_use_symlinks=False
|
| 28 |
+
)
|
| 29 |
+
print(f"Downloaded {filename}")
|
| 30 |
+
except Exception as e:
|
| 31 |
+
print(f"Could not download {filename}: {e}")
|
| 32 |
+
|
| 33 |
+
if not any((model_dir / f).exists() for f in files_to_download):
|
| 34 |
+
print("Warning: No SMPL model files found. You may need to download them manually.")
|
| 35 |
+
print("Visit https://smpl.is.tue.mpg.de/ to register and download SMPL models.")
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
return True
|
| 39 |
+
except Exception as e:
|
| 40 |
+
print(f"Error downloading SMPL model: {e}")
|
| 41 |
+
print("You may need to download SMPL models manually from https://smpl.is.tue.mpg.de/")
|
| 42 |
+
return False
|
| 43 |
+
|
| 44 |
+
if __name__ == "__main__":
|
| 45 |
+
download_smpl_model()
|
| 46 |
+
|
smpl/__MACOSX/._smpl
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/._.DS_Store
ADDED
|
Binary file (120 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/.___init__.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/._models
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/._smpl_webuser
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/models/._basicModel_f_lbs_10_207_0_v1.0.0.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:529400160afed98cb2b5af3136368246d5efb321109a79acb9d54ae2630b38cf
|
| 3 |
+
size 239
|
smpl/__MACOSX/smpl/models/._basicmodel_m_lbs_10_207_0_v1.0.0.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cf7699b40c82bf83644ca68a1bf92342376510b7157e228d9cdafc8b928fc93d
|
| 3 |
+
size 239
|
smpl/__MACOSX/smpl/smpl_webuser/._LICENSE.txt
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/._README.txt
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/.___init__.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/._hello_world
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/._lbs.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/._posemapper.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/._serialization.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/._verts.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/hello_world/._hello_smpl.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/__MACOSX/smpl/smpl_webuser/hello_world/._render_smpl.py
ADDED
|
Binary file (239 Bytes). View file
|
|
|
smpl/smpl/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
About this file:
|
| 11 |
+
================
|
| 12 |
+
This is an initialization file to help python look for submodules in this directory.
|
| 13 |
+
|
| 14 |
+
'''
|
smpl/smpl/models/basicModel_f_lbs_10_207_0_v1.0.0.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a583c1b98e4afc19042641f1bae5cd8a1f712a6724886291a7627ec07acd408d
|
| 3 |
+
size 39056454
|
smpl/smpl/models/basicmodel_m_lbs_10_207_0_v1.0.0.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0e8c0bbbbc635dcb166ed29c303fb4bef16ea5f623e5a89263495a9e403575bd
|
| 3 |
+
size 39056404
|
smpl/smpl/smpl_webuser/LICENSE.txt
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Please read carefully the following terms and conditions and any accompanying documentation before you download and/or use the SMPL body model and software, (the "Model"), including 3D meshes, blend weights blend shapes, textures, software, scripts, and animations. By downloading and/or using the Model, you acknowledge that you have read these terms and conditions, understand them, and agree to be bound by them. If you do not agree with these terms and conditions, you must not download and/or use the Model.
|
| 2 |
+
|
| 3 |
+
Ownership
|
| 4 |
+
The Model has been developed at the Max Planck Institute for Intelligent Systems (hereinafter "MPI") and is owned by and proprietary material of the Max-Planck-Gesellschaft zur Förderung der Wissenschaften e.V. (hereinafter “MPG”; MPI and MPG hereinafter collectively “Max-Planck”).
|
| 5 |
+
|
| 6 |
+
License Grant
|
| 7 |
+
Max-Planck grants you a non-exclusive, non-transferable, free of charge right:
|
| 8 |
+
|
| 9 |
+
To download the Model and use it on computers owned, leased or otherwise controlled by you and/or your organisation;
|
| 10 |
+
To use the Model for the sole purpose of performing non-commercial scientific research, non-commercial education, or non-commercial artistic projects.
|
| 11 |
+
Any other use, in particular any use for commercial purposes, is prohibited. This includes, without limitation, incorporation in a commercial product, use in a commercial service, as training data for a commercial product, for commercial ergonomic analysis (e.g. product design, architectural design, etc.), or production of other artifacts for commercial purposes including, for example, web services, movies, television programs, mobile applications, or video games. The Model may not be used for pornographic purposes or to generate pornographic material whether commercial or not. This license also prohibits the use of the Model to train methods/algorithms/neural networks/etc. for commercial use of any kind. The Model may not be reproduced, modified and/or made available in any form to any third party without Max-Planck’s prior written permission. By downloading the Model, you agree not to reverse engineer it.
|
| 12 |
+
|
| 13 |
+
Disclaimer of Representations and Warranties
|
| 14 |
+
You expressly acknowledge and agree that the Model results from basic research, is provided “AS IS”, may contain errors, and that any use of the Model is at your sole risk. MAX-PLANCK MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MODEL, NEITHER EXPRESS NOR IMPLIED, AND THE ABSENCE OF ANY LEGAL OR ACTUAL DEFECTS, WHETHER DISCOVERABLE OR NOT. Specifically, and not to limit the foregoing, Max-Planck makes no representations or warranties (i) regarding the merchantability or fitness for a particular purpose of the Model, (ii) that the use of the Model will not infringe any patents, copyrights or other intellectual property rights of a third party, and (iii) that the use of the Model will not cause any damage of any kind to you or a third party.
|
| 15 |
+
|
| 16 |
+
Limitation of Liability
|
| 17 |
+
Under no circumstances shall Max-Planck be liable for any incidental, special, indirect or consequential damages arising out of or relating to this license, including but not limited to, any lost profits, business interruption, loss of programs or other data, or all other commercial damages or losses, even if advised of the possibility thereof.
|
| 18 |
+
|
| 19 |
+
No Maintenance Services
|
| 20 |
+
You understand and agree that Max-Planck is under no obligation to provide either maintenance services, update services, notices of latent defects, or corrections of defects with regard to the Model. Max-Planck nevertheless reserves the right to update, modify, or discontinue the Model at any time.
|
| 21 |
+
|
| 22 |
+
Publication with SMPL
|
| 23 |
+
You agree to cite the most recent paper describing the model as specified on the download website. This website lists the most up to date bibliographic information on the about page.
|
| 24 |
+
|
| 25 |
+
Media projects with SMPL
|
| 26 |
+
When using SMPL in a media project please give credit to Max Planck Institute for Intelligent Systems. For example: SMPL was used for character animation courtesy of the Max Planck Institute for Intelligent Systems.
|
| 27 |
+
Commercial licensing opportunities
|
| 28 |
+
For commercial use in the fields of medicine, psychology, and biomechanics, please contact ps-license@tue.mpg.de.
|
| 29 |
+
For commercial use in all other fields please contact Body Labs Inc at smpl@bodylabs.com
|
smpl/smpl/smpl_webuser/README.txt
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
License:
|
| 2 |
+
========
|
| 3 |
+
To learn about SMPL, please visit our website: http://smpl.is.tue.mpg
|
| 4 |
+
You can find the SMPL paper at: http://files.is.tue.mpg.de/black/papers/SMPL2015.pdf
|
| 5 |
+
|
| 6 |
+
Visit our downloads page to download some sample animation files (FBX), and python code:
|
| 7 |
+
http://smpl.is.tue.mpg/downloads
|
| 8 |
+
|
| 9 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
System Requirements:
|
| 13 |
+
====================
|
| 14 |
+
Operating system: OSX, Linux
|
| 15 |
+
|
| 16 |
+
Python Dependencies:
|
| 17 |
+
- Numpy & Scipy [http://www.scipy.org/scipylib/download.html]
|
| 18 |
+
- Chumpy [https://github.com/mattloper/chumpy]
|
| 19 |
+
- OpenCV [http://opencv.org/downloads.html]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
Getting Started:
|
| 23 |
+
================
|
| 24 |
+
|
| 25 |
+
1. Extract the Code:
|
| 26 |
+
--------------------
|
| 27 |
+
Extract the 'smpl.zip' file to your home directory (or any other location you wish)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
2. Set the PYTHONPATH:
|
| 31 |
+
----------------------
|
| 32 |
+
We need to update the PYTHONPATH environment variable so that the system knows how to find the SMPL code. Add the following lines to your ~/.bash_profile file (create it if it doesn't exist; Linux users might have ~/.bashrc file instead), replacing ~/smpl with the location where you extracted the smpl.zip file:
|
| 33 |
+
|
| 34 |
+
SMPL_LOCATION=~/smpl
|
| 35 |
+
export PYTHONPATH=$PYTHONPATH:$SMPL_LOCATION
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
Open a new terminal window to check if the python path has been updated by typing the following:
|
| 39 |
+
> echo $PYTHONPATH
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
3. Run the Hello World scripts:
|
| 43 |
+
-------------------------------
|
| 44 |
+
In the new Terminal window, navigate to the smpl/smpl_webuser/hello_world directory. You can run the hello world scripts now by typing the following:
|
| 45 |
+
|
| 46 |
+
> python hello_smpl.py
|
| 47 |
+
|
| 48 |
+
OR
|
| 49 |
+
|
| 50 |
+
> python render_smpl.py
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
Note:
|
| 55 |
+
Both of these scripts will require the dependencies listed above. The scripts are provided as a sample to help you get started.
|
| 56 |
+
|
smpl/smpl/smpl_webuser/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
About this file:
|
| 11 |
+
================
|
| 12 |
+
This is an initialization file to help python look for submodules in this directory.
|
| 13 |
+
'''
|
smpl/smpl/smpl_webuser/hello_world/hello_smpl.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
Please Note:
|
| 11 |
+
============
|
| 12 |
+
This is a demo version of the script for driving the SMPL model with python.
|
| 13 |
+
We would be happy to receive comments, help and suggestions on improving this code
|
| 14 |
+
and in making it available on more platforms.
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
System Requirements:
|
| 18 |
+
====================
|
| 19 |
+
Operating system: OSX, Linux
|
| 20 |
+
|
| 21 |
+
Python Dependencies:
|
| 22 |
+
- Numpy & Scipy [http://www.scipy.org/scipylib/download.html]
|
| 23 |
+
- Chumpy [https://github.com/mattloper/chumpy]
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
About the Script:
|
| 27 |
+
=================
|
| 28 |
+
This script demonstrates a few basic functions to help users get started with using
|
| 29 |
+
the SMPL model. The code shows how to:
|
| 30 |
+
- Load the SMPL model
|
| 31 |
+
- Edit pose & shape parameters of the model to create a new body in a new pose
|
| 32 |
+
- Save the resulting body as a mesh in .OBJ format
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
Running the Hello World code:
|
| 36 |
+
=============================
|
| 37 |
+
Inside Terminal, navigate to the smpl/webuser/hello_world directory. You can run
|
| 38 |
+
the hello world script now by typing the following:
|
| 39 |
+
> python hello_smpl.py
|
| 40 |
+
|
| 41 |
+
'''
|
| 42 |
+
|
| 43 |
+
from smpl_webuser.serialization import load_model
|
| 44 |
+
import numpy as np
|
| 45 |
+
|
| 46 |
+
## Load SMPL model (here we load the female model)
|
| 47 |
+
## Make sure path is correct
|
| 48 |
+
m = load_model( '../../models/basicModel_f_lbs_10_207_0_v1.0.0.pkl' )
|
| 49 |
+
|
| 50 |
+
## Assign random pose and shape parameters
|
| 51 |
+
m.pose[:] = np.random.rand(m.pose.size) * .2
|
| 52 |
+
m.betas[:] = np.random.rand(m.betas.size) * .03
|
| 53 |
+
|
| 54 |
+
## Write to an .obj file
|
| 55 |
+
outmesh_path = './hello_smpl.obj'
|
| 56 |
+
with open( outmesh_path, 'w') as fp:
|
| 57 |
+
for v in m.r:
|
| 58 |
+
fp.write( 'v %f %f %f\n' % ( v[0], v[1], v[2]) )
|
| 59 |
+
|
| 60 |
+
for f in m.f+1: # Faces are 1-based, not 0-based in obj files
|
| 61 |
+
fp.write( 'f %d %d %d\n' % (f[0], f[1], f[2]) )
|
| 62 |
+
|
| 63 |
+
## Print message
|
| 64 |
+
print '..Output mesh saved to: ', outmesh_path
|
smpl/smpl/smpl_webuser/hello_world/render_smpl.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
Please Note:
|
| 11 |
+
============
|
| 12 |
+
This is a demo version of the script for driving the SMPL model with python.
|
| 13 |
+
We would be happy to receive comments, help and suggestions on improving this code
|
| 14 |
+
and in making it available on more platforms.
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
System Requirements:
|
| 18 |
+
====================
|
| 19 |
+
Operating system: OSX, Linux
|
| 20 |
+
|
| 21 |
+
Python Dependencies:
|
| 22 |
+
- Numpy & Scipy [http://www.scipy.org/scipylib/download.html]
|
| 23 |
+
- Chumpy [https://github.com/mattloper/chumpy]
|
| 24 |
+
- OpenCV [http://opencv.org/downloads.html]
|
| 25 |
+
--> (alternatively: matplotlib [http://matplotlib.org/downloads.html])
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
About the Script:
|
| 29 |
+
=================
|
| 30 |
+
This script demonstrates loading the smpl model and rendering it using OpenDR
|
| 31 |
+
to render and OpenCV to display (or alternatively matplotlib can also be used
|
| 32 |
+
for display, as shown in commented code below).
|
| 33 |
+
|
| 34 |
+
This code shows how to:
|
| 35 |
+
- Load the SMPL model
|
| 36 |
+
- Edit pose & shape parameters of the model to create a new body in a new pose
|
| 37 |
+
- Create an OpenDR scene (with a basic renderer, camera & light)
|
| 38 |
+
- Render the scene using OpenCV / matplotlib
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
Running the Hello World code:
|
| 42 |
+
=============================
|
| 43 |
+
Inside Terminal, navigate to the smpl/webuser/hello_world directory. You can run
|
| 44 |
+
the hello world script now by typing the following:
|
| 45 |
+
> python render_smpl.py
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
'''
|
| 49 |
+
|
| 50 |
+
import numpy as np
|
| 51 |
+
from opendr.renderer import ColoredRenderer
|
| 52 |
+
from opendr.lighting import LambertianPointLight
|
| 53 |
+
from opendr.camera import ProjectPoints
|
| 54 |
+
from smpl_webuser.serialization import load_model
|
| 55 |
+
|
| 56 |
+
## Load SMPL model (here we load the female model)
|
| 57 |
+
m = load_model('../../models/basicModel_f_lbs_10_207_0_v1.0.0.pkl')
|
| 58 |
+
|
| 59 |
+
## Assign random pose and shape parameters
|
| 60 |
+
m.pose[:] = np.random.rand(m.pose.size) * .2
|
| 61 |
+
m.betas[:] = np.random.rand(m.betas.size) * .03
|
| 62 |
+
m.pose[0] = np.pi
|
| 63 |
+
|
| 64 |
+
## Create OpenDR renderer
|
| 65 |
+
rn = ColoredRenderer()
|
| 66 |
+
|
| 67 |
+
## Assign attributes to renderer
|
| 68 |
+
w, h = (640, 480)
|
| 69 |
+
|
| 70 |
+
rn.camera = ProjectPoints(v=m, rt=np.zeros(3), t=np.array([0, 0, 2.]), f=np.array([w,w])/2., c=np.array([w,h])/2., k=np.zeros(5))
|
| 71 |
+
rn.frustum = {'near': 1., 'far': 10., 'width': w, 'height': h}
|
| 72 |
+
rn.set(v=m, f=m.f, bgcolor=np.zeros(3))
|
| 73 |
+
|
| 74 |
+
## Construct point light source
|
| 75 |
+
rn.vc = LambertianPointLight(
|
| 76 |
+
f=m.f,
|
| 77 |
+
v=rn.v,
|
| 78 |
+
num_verts=len(m),
|
| 79 |
+
light_pos=np.array([-1000,-1000,-2000]),
|
| 80 |
+
vc=np.ones_like(m)*.9,
|
| 81 |
+
light_color=np.array([1., 1., 1.]))
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
## Show it using OpenCV
|
| 85 |
+
import cv2
|
| 86 |
+
cv2.imshow('render_SMPL', rn.r)
|
| 87 |
+
print ('..Print any key while on the display window')
|
| 88 |
+
cv2.waitKey(0)
|
| 89 |
+
cv2.destroyAllWindows()
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
## Could also use matplotlib to display
|
| 93 |
+
# import matplotlib.pyplot as plt
|
| 94 |
+
# plt.ion()
|
| 95 |
+
# plt.imshow(rn.r)
|
| 96 |
+
# plt.show()
|
| 97 |
+
# import pdb; pdb.set_trace()
|
smpl/smpl/smpl_webuser/lbs.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
About this file:
|
| 11 |
+
================
|
| 12 |
+
This file defines linear blend skinning for the SMPL loader which
|
| 13 |
+
defines the effect of bones and blendshapes on the vertices of the template mesh.
|
| 14 |
+
|
| 15 |
+
Modules included:
|
| 16 |
+
- global_rigid_transformation:
|
| 17 |
+
computes global rotation & translation of the model
|
| 18 |
+
- verts_core: [overloaded function inherited from verts.verts_core]
|
| 19 |
+
computes the blending of joint-influences for each vertex based on type of skinning
|
| 20 |
+
|
| 21 |
+
'''
|
| 22 |
+
|
| 23 |
+
from posemapper import posemap
|
| 24 |
+
import chumpy
|
| 25 |
+
import numpy as np
|
| 26 |
+
|
| 27 |
+
def global_rigid_transformation(pose, J, kintree_table, xp):
|
| 28 |
+
results = {}
|
| 29 |
+
pose = pose.reshape((-1,3))
|
| 30 |
+
id_to_col = {kintree_table[1,i] : i for i in range(kintree_table.shape[1])}
|
| 31 |
+
parent = {i : id_to_col[kintree_table[0,i]] for i in range(1, kintree_table.shape[1])}
|
| 32 |
+
|
| 33 |
+
if xp == chumpy:
|
| 34 |
+
from posemapper import Rodrigues
|
| 35 |
+
rodrigues = lambda x : Rodrigues(x)
|
| 36 |
+
else:
|
| 37 |
+
import cv2
|
| 38 |
+
rodrigues = lambda x : cv2.Rodrigues(x)[0]
|
| 39 |
+
|
| 40 |
+
with_zeros = lambda x : xp.vstack((x, xp.array([[0.0, 0.0, 0.0, 1.0]])))
|
| 41 |
+
results[0] = with_zeros(xp.hstack((rodrigues(pose[0,:]), J[0,:].reshape((3,1)))))
|
| 42 |
+
|
| 43 |
+
for i in range(1, kintree_table.shape[1]):
|
| 44 |
+
results[i] = results[parent[i]].dot(with_zeros(xp.hstack((
|
| 45 |
+
rodrigues(pose[i,:]),
|
| 46 |
+
((J[i,:] - J[parent[i],:]).reshape((3,1)))
|
| 47 |
+
))))
|
| 48 |
+
|
| 49 |
+
pack = lambda x : xp.hstack([np.zeros((4, 3)), x.reshape((4,1))])
|
| 50 |
+
|
| 51 |
+
results = [results[i] for i in sorted(results.keys())]
|
| 52 |
+
results_global = results
|
| 53 |
+
|
| 54 |
+
if True:
|
| 55 |
+
results2 = [results[i] - (pack(
|
| 56 |
+
results[i].dot(xp.concatenate( ( (J[i,:]), 0 ) )))
|
| 57 |
+
) for i in range(len(results))]
|
| 58 |
+
results = results2
|
| 59 |
+
result = xp.dstack(results)
|
| 60 |
+
return result, results_global
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def verts_core(pose, v, J, weights, kintree_table, want_Jtr=False, xp=chumpy):
|
| 64 |
+
A, A_global = global_rigid_transformation(pose, J, kintree_table, xp)
|
| 65 |
+
T = A.dot(weights.T)
|
| 66 |
+
|
| 67 |
+
rest_shape_h = xp.vstack((v.T, np.ones((1, v.shape[0]))))
|
| 68 |
+
|
| 69 |
+
v =(T[:,0,:] * rest_shape_h[0, :].reshape((1, -1)) +
|
| 70 |
+
T[:,1,:] * rest_shape_h[1, :].reshape((1, -1)) +
|
| 71 |
+
T[:,2,:] * rest_shape_h[2, :].reshape((1, -1)) +
|
| 72 |
+
T[:,3,:] * rest_shape_h[3, :].reshape((1, -1))).T
|
| 73 |
+
|
| 74 |
+
v = v[:,:3]
|
| 75 |
+
|
| 76 |
+
if not want_Jtr:
|
| 77 |
+
return v
|
| 78 |
+
Jtr = xp.vstack([g[:3,3] for g in A_global])
|
| 79 |
+
return (v, Jtr)
|
| 80 |
+
|
smpl/smpl/smpl_webuser/posemapper.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
About this file:
|
| 11 |
+
================
|
| 12 |
+
This module defines the mapping of joint-angles to pose-blendshapes.
|
| 13 |
+
|
| 14 |
+
Modules included:
|
| 15 |
+
- posemap:
|
| 16 |
+
computes the joint-to-pose blend shape mapping given a mapping type as input
|
| 17 |
+
|
| 18 |
+
'''
|
| 19 |
+
|
| 20 |
+
import chumpy as ch
|
| 21 |
+
import numpy as np
|
| 22 |
+
import cv2
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class Rodrigues(ch.Ch):
|
| 26 |
+
dterms = 'rt'
|
| 27 |
+
|
| 28 |
+
def compute_r(self):
|
| 29 |
+
return cv2.Rodrigues(self.rt.r)[0]
|
| 30 |
+
|
| 31 |
+
def compute_dr_wrt(self, wrt):
|
| 32 |
+
if wrt is self.rt:
|
| 33 |
+
return cv2.Rodrigues(self.rt.r)[1].T
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def lrotmin(p):
|
| 37 |
+
if isinstance(p, np.ndarray):
|
| 38 |
+
p = p.ravel()[3:]
|
| 39 |
+
return np.concatenate([(cv2.Rodrigues(np.array(pp))[0]-np.eye(3)).ravel() for pp in p.reshape((-1,3))]).ravel()
|
| 40 |
+
if p.ndim != 2 or p.shape[1] != 3:
|
| 41 |
+
p = p.reshape((-1,3))
|
| 42 |
+
p = p[1:]
|
| 43 |
+
return ch.concatenate([(Rodrigues(pp)-ch.eye(3)).ravel() for pp in p]).ravel()
|
| 44 |
+
|
| 45 |
+
def posemap(s):
|
| 46 |
+
if s == 'lrotmin':
|
| 47 |
+
return lrotmin
|
| 48 |
+
else:
|
| 49 |
+
raise Exception('Unknown posemapping: %s' % (str(s),))
|
smpl/smpl/smpl_webuser/serialization.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
About this file:
|
| 11 |
+
================
|
| 12 |
+
This file defines the serialization functions of the SMPL model.
|
| 13 |
+
|
| 14 |
+
Modules included:
|
| 15 |
+
- save_model:
|
| 16 |
+
saves the SMPL model to a given file location as a .pkl file
|
| 17 |
+
- load_model:
|
| 18 |
+
loads the SMPL model from a given file location (i.e. a .pkl file location),
|
| 19 |
+
or a dictionary object.
|
| 20 |
+
|
| 21 |
+
'''
|
| 22 |
+
|
| 23 |
+
__all__ = ['load_model', 'save_model']
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
import cPickle as pickle
|
| 27 |
+
import chumpy as ch
|
| 28 |
+
from chumpy.ch import MatVecMult
|
| 29 |
+
from posemapper import posemap
|
| 30 |
+
from verts import verts_core
|
| 31 |
+
|
| 32 |
+
def save_model(model, fname):
|
| 33 |
+
m0 = model
|
| 34 |
+
trainer_dict = {'v_template': np.asarray(m0.v_template),'J': np.asarray(m0.J),'weights': np.asarray(m0.weights),'kintree_table': m0.kintree_table,'f': m0.f, 'bs_type': m0.bs_type, 'posedirs': np.asarray(m0.posedirs)}
|
| 35 |
+
if hasattr(model, 'J_regressor'):
|
| 36 |
+
trainer_dict['J_regressor'] = m0.J_regressor
|
| 37 |
+
if hasattr(model, 'J_regressor_prior'):
|
| 38 |
+
trainer_dict['J_regressor_prior'] = m0.J_regressor_prior
|
| 39 |
+
if hasattr(model, 'weights_prior'):
|
| 40 |
+
trainer_dict['weights_prior'] = m0.weights_prior
|
| 41 |
+
if hasattr(model, 'shapedirs'):
|
| 42 |
+
trainer_dict['shapedirs'] = m0.shapedirs
|
| 43 |
+
if hasattr(model, 'vert_sym_idxs'):
|
| 44 |
+
trainer_dict['vert_sym_idxs'] = m0.vert_sym_idxs
|
| 45 |
+
if hasattr(model, 'bs_style'):
|
| 46 |
+
trainer_dict['bs_style'] = model.bs_style
|
| 47 |
+
else:
|
| 48 |
+
trainer_dict['bs_style'] = 'lbs'
|
| 49 |
+
pickle.dump(trainer_dict, open(fname, 'w'), -1)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def backwards_compatibility_replacements(dd):
|
| 53 |
+
|
| 54 |
+
# replacements
|
| 55 |
+
if 'default_v' in dd:
|
| 56 |
+
dd['v_template'] = dd['default_v']
|
| 57 |
+
del dd['default_v']
|
| 58 |
+
if 'template_v' in dd:
|
| 59 |
+
dd['v_template'] = dd['template_v']
|
| 60 |
+
del dd['template_v']
|
| 61 |
+
if 'joint_regressor' in dd:
|
| 62 |
+
dd['J_regressor'] = dd['joint_regressor']
|
| 63 |
+
del dd['joint_regressor']
|
| 64 |
+
if 'blendshapes' in dd:
|
| 65 |
+
dd['posedirs'] = dd['blendshapes']
|
| 66 |
+
del dd['blendshapes']
|
| 67 |
+
if 'J' not in dd:
|
| 68 |
+
dd['J'] = dd['joints']
|
| 69 |
+
del dd['joints']
|
| 70 |
+
|
| 71 |
+
# defaults
|
| 72 |
+
if 'bs_style' not in dd:
|
| 73 |
+
dd['bs_style'] = 'lbs'
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def ready_arguments(fname_or_dict):
|
| 78 |
+
|
| 79 |
+
if not isinstance(fname_or_dict, dict):
|
| 80 |
+
dd = pickle.load(open(fname_or_dict))
|
| 81 |
+
else:
|
| 82 |
+
dd = fname_or_dict
|
| 83 |
+
|
| 84 |
+
backwards_compatibility_replacements(dd)
|
| 85 |
+
|
| 86 |
+
want_shapemodel = 'shapedirs' in dd
|
| 87 |
+
nposeparms = dd['kintree_table'].shape[1]*3
|
| 88 |
+
|
| 89 |
+
if 'trans' not in dd:
|
| 90 |
+
dd['trans'] = np.zeros(3)
|
| 91 |
+
if 'pose' not in dd:
|
| 92 |
+
dd['pose'] = np.zeros(nposeparms)
|
| 93 |
+
if 'shapedirs' in dd and 'betas' not in dd:
|
| 94 |
+
dd['betas'] = np.zeros(dd['shapedirs'].shape[-1])
|
| 95 |
+
|
| 96 |
+
for s in ['v_template', 'weights', 'posedirs', 'pose', 'trans', 'shapedirs', 'betas', 'J']:
|
| 97 |
+
if (s in dd) and not hasattr(dd[s], 'dterms'):
|
| 98 |
+
dd[s] = ch.array(dd[s])
|
| 99 |
+
|
| 100 |
+
if want_shapemodel:
|
| 101 |
+
dd['v_shaped'] = dd['shapedirs'].dot(dd['betas'])+dd['v_template']
|
| 102 |
+
v_shaped = dd['v_shaped']
|
| 103 |
+
J_tmpx = MatVecMult(dd['J_regressor'], v_shaped[:,0])
|
| 104 |
+
J_tmpy = MatVecMult(dd['J_regressor'], v_shaped[:,1])
|
| 105 |
+
J_tmpz = MatVecMult(dd['J_regressor'], v_shaped[:,2])
|
| 106 |
+
dd['J'] = ch.vstack((J_tmpx, J_tmpy, J_tmpz)).T
|
| 107 |
+
dd['v_posed'] = v_shaped + dd['posedirs'].dot(posemap(dd['bs_type'])(dd['pose']))
|
| 108 |
+
else:
|
| 109 |
+
dd['v_posed'] = dd['v_template'] + dd['posedirs'].dot(posemap(dd['bs_type'])(dd['pose']))
|
| 110 |
+
|
| 111 |
+
return dd
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def load_model(fname_or_dict):
|
| 116 |
+
dd = ready_arguments(fname_or_dict)
|
| 117 |
+
|
| 118 |
+
args = {
|
| 119 |
+
'pose': dd['pose'],
|
| 120 |
+
'v': dd['v_posed'],
|
| 121 |
+
'J': dd['J'],
|
| 122 |
+
'weights': dd['weights'],
|
| 123 |
+
'kintree_table': dd['kintree_table'],
|
| 124 |
+
'xp': ch,
|
| 125 |
+
'want_Jtr': True,
|
| 126 |
+
'bs_style': dd['bs_style']
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
result, Jtr = verts_core(**args)
|
| 130 |
+
result = result + dd['trans'].reshape((1,3))
|
| 131 |
+
result.J_transformed = Jtr + dd['trans'].reshape((1,3))
|
| 132 |
+
|
| 133 |
+
for k, v in dd.items():
|
| 134 |
+
setattr(result, k, v)
|
| 135 |
+
|
| 136 |
+
return result
|
| 137 |
+
|
smpl/smpl/smpl_webuser/verts.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Copyright 2015 Matthew Loper, Naureen Mahmood and the Max Planck Gesellschaft. All rights reserved.
|
| 3 |
+
This software is provided for research purposes only.
|
| 4 |
+
By using this software you agree to the terms of the SMPL Model license here http://smpl.is.tue.mpg.de/license
|
| 5 |
+
|
| 6 |
+
More information about SMPL is available here http://smpl.is.tue.mpg.
|
| 7 |
+
For comments or questions, please email us at: smpl@tuebingen.mpg.de
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
About this file:
|
| 11 |
+
================
|
| 12 |
+
This file defines the basic skinning modules for the SMPL loader which
|
| 13 |
+
defines the effect of bones and blendshapes on the vertices of the template mesh.
|
| 14 |
+
|
| 15 |
+
Modules included:
|
| 16 |
+
- verts_decorated:
|
| 17 |
+
creates an instance of the SMPL model which inherits model attributes from another
|
| 18 |
+
SMPL model.
|
| 19 |
+
- verts_core: [overloaded function inherited by lbs.verts_core]
|
| 20 |
+
computes the blending of joint-influences for each vertex based on type of skinning
|
| 21 |
+
|
| 22 |
+
'''
|
| 23 |
+
|
| 24 |
+
import chumpy
|
| 25 |
+
import lbs
|
| 26 |
+
from posemapper import posemap
|
| 27 |
+
import scipy.sparse as sp
|
| 28 |
+
from chumpy.ch import MatVecMult
|
| 29 |
+
|
| 30 |
+
def ischumpy(x): return hasattr(x, 'dterms')
|
| 31 |
+
|
| 32 |
+
def verts_decorated(trans, pose,
|
| 33 |
+
v_template, J, weights, kintree_table, bs_style, f,
|
| 34 |
+
bs_type=None, posedirs=None, betas=None, shapedirs=None, want_Jtr=False):
|
| 35 |
+
|
| 36 |
+
for which in [trans, pose, v_template, weights, posedirs, betas, shapedirs]:
|
| 37 |
+
if which is not None:
|
| 38 |
+
assert ischumpy(which)
|
| 39 |
+
|
| 40 |
+
v = v_template
|
| 41 |
+
|
| 42 |
+
if shapedirs is not None:
|
| 43 |
+
if betas is None:
|
| 44 |
+
betas = chumpy.zeros(shapedirs.shape[-1])
|
| 45 |
+
v_shaped = v + shapedirs.dot(betas)
|
| 46 |
+
else:
|
| 47 |
+
v_shaped = v
|
| 48 |
+
|
| 49 |
+
if posedirs is not None:
|
| 50 |
+
v_posed = v_shaped + posedirs.dot(posemap(bs_type)(pose))
|
| 51 |
+
else:
|
| 52 |
+
v_posed = v_shaped
|
| 53 |
+
|
| 54 |
+
v = v_posed
|
| 55 |
+
|
| 56 |
+
if sp.issparse(J):
|
| 57 |
+
regressor = J
|
| 58 |
+
J_tmpx = MatVecMult(regressor, v_shaped[:,0])
|
| 59 |
+
J_tmpy = MatVecMult(regressor, v_shaped[:,1])
|
| 60 |
+
J_tmpz = MatVecMult(regressor, v_shaped[:,2])
|
| 61 |
+
J = chumpy.vstack((J_tmpx, J_tmpy, J_tmpz)).T
|
| 62 |
+
else:
|
| 63 |
+
assert(ischumpy(J))
|
| 64 |
+
|
| 65 |
+
assert(bs_style=='lbs')
|
| 66 |
+
result, Jtr = lbs.verts_core(pose, v, J, weights, kintree_table, want_Jtr=True, xp=chumpy)
|
| 67 |
+
|
| 68 |
+
tr = trans.reshape((1,3))
|
| 69 |
+
result = result + tr
|
| 70 |
+
Jtr = Jtr + tr
|
| 71 |
+
|
| 72 |
+
result.trans = trans
|
| 73 |
+
result.f = f
|
| 74 |
+
result.pose = pose
|
| 75 |
+
result.v_template = v_template
|
| 76 |
+
result.J = J
|
| 77 |
+
result.weights = weights
|
| 78 |
+
result.kintree_table = kintree_table
|
| 79 |
+
result.bs_style = bs_style
|
| 80 |
+
result.bs_type =bs_type
|
| 81 |
+
if posedirs is not None:
|
| 82 |
+
result.posedirs = posedirs
|
| 83 |
+
result.v_posed = v_posed
|
| 84 |
+
if shapedirs is not None:
|
| 85 |
+
result.shapedirs = shapedirs
|
| 86 |
+
result.betas = betas
|
| 87 |
+
result.v_shaped = v_shaped
|
| 88 |
+
if want_Jtr:
|
| 89 |
+
result.J_transformed = Jtr
|
| 90 |
+
return result
|
| 91 |
+
|
| 92 |
+
def verts_core(pose, v, J, weights, kintree_table, bs_style, want_Jtr=False, xp=chumpy):
|
| 93 |
+
|
| 94 |
+
if xp == chumpy:
|
| 95 |
+
assert(hasattr(pose, 'dterms'))
|
| 96 |
+
assert(hasattr(v, 'dterms'))
|
| 97 |
+
assert(hasattr(J, 'dterms'))
|
| 98 |
+
assert(hasattr(weights, 'dterms'))
|
| 99 |
+
|
| 100 |
+
assert(bs_style=='lbs')
|
| 101 |
+
result = lbs.verts_core(pose, v, J, weights, kintree_table, want_Jtr, xp)
|
| 102 |
+
|
| 103 |
+
return result
|
smpl_generator.py
CHANGED
|
@@ -3,23 +3,34 @@ import numpy as np
|
|
| 3 |
from pathlib import Path
|
| 4 |
from typing import Tuple, Optional
|
| 5 |
import smplx
|
|
|
|
| 6 |
|
| 7 |
|
| 8 |
class SMPLGenerator:
|
| 9 |
-
def __init__(self, model_path: str = "
|
| 10 |
self.device = torch.device(device)
|
| 11 |
self.gender = gender
|
| 12 |
-
self.model_path = Path(model_path)
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
try:
|
| 21 |
self.smpl_model = smplx.create(
|
| 22 |
-
|
| 23 |
model_type='smpl',
|
| 24 |
gender=gender,
|
| 25 |
batch_size=1,
|
|
@@ -28,17 +39,26 @@ class SMPLGenerator:
|
|
| 28 |
except Exception as e:
|
| 29 |
try:
|
| 30 |
self.smpl_model = smplx.create(
|
| 31 |
-
|
| 32 |
model_type='smpl',
|
| 33 |
gender=gender,
|
| 34 |
batch_size=1,
|
| 35 |
ext='pkl'
|
| 36 |
).to(self.device)
|
| 37 |
except Exception as e2:
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
def generate_mesh(
|
| 44 |
self,
|
|
@@ -87,7 +107,7 @@ class SMPLGenerator:
|
|
| 87 |
_generator_instance = None
|
| 88 |
|
| 89 |
|
| 90 |
-
def get_generator(model_path: str = "
|
| 91 |
global _generator_instance
|
| 92 |
if _generator_instance is None:
|
| 93 |
_generator_instance = SMPLGenerator(model_path=model_path, gender=gender, device=device)
|
|
@@ -96,7 +116,7 @@ def get_generator(model_path: str = "models/smpl", gender: str = "neutral", devi
|
|
| 96 |
|
| 97 |
def generate_mesh(
|
| 98 |
betas: np.ndarray,
|
| 99 |
-
model_path: str = "
|
| 100 |
gender: str = "neutral",
|
| 101 |
device: str = "cpu"
|
| 102 |
) -> Tuple[np.ndarray, np.ndarray]:
|
|
|
|
| 3 |
from pathlib import Path
|
| 4 |
from typing import Tuple, Optional
|
| 5 |
import smplx
|
| 6 |
+
import os
|
| 7 |
|
| 8 |
|
| 9 |
class SMPLGenerator:
|
| 10 |
+
def __init__(self, model_path: str = "smpl/smpl", gender: str = "neutral", device: str = "cpu"):
|
| 11 |
self.device = torch.device(device)
|
| 12 |
self.gender = gender
|
|
|
|
| 13 |
|
| 14 |
+
model_path_obj = Path(model_path)
|
| 15 |
+
|
| 16 |
+
if not model_path_obj.exists():
|
| 17 |
+
alt_path = Path("smpl/smpl")
|
| 18 |
+
if alt_path.exists():
|
| 19 |
+
model_path_obj = alt_path
|
| 20 |
+
print(f"Using alternative model path: {model_path_obj}")
|
| 21 |
+
else:
|
| 22 |
+
model_path_obj.mkdir(parents=True, exist_ok=True)
|
| 23 |
+
|
| 24 |
+
self.model_path = model_path_obj
|
| 25 |
+
model_path_str = str(self.model_path)
|
| 26 |
+
|
| 27 |
+
if gender == "neutral":
|
| 28 |
+
gender = "male"
|
| 29 |
+
print("Note: Neutral gender not available, using male model")
|
| 30 |
|
| 31 |
try:
|
| 32 |
self.smpl_model = smplx.create(
|
| 33 |
+
model_path=model_path_str,
|
| 34 |
model_type='smpl',
|
| 35 |
gender=gender,
|
| 36 |
batch_size=1,
|
|
|
|
| 39 |
except Exception as e:
|
| 40 |
try:
|
| 41 |
self.smpl_model = smplx.create(
|
| 42 |
+
model_path=model_path_str,
|
| 43 |
model_type='smpl',
|
| 44 |
gender=gender,
|
| 45 |
batch_size=1,
|
| 46 |
ext='pkl'
|
| 47 |
).to(self.device)
|
| 48 |
except Exception as e2:
|
| 49 |
+
try:
|
| 50 |
+
self.smpl_model = smplx.create(
|
| 51 |
+
model_path=model_path_str,
|
| 52 |
+
model_type='smpl',
|
| 53 |
+
gender=gender,
|
| 54 |
+
batch_size=1
|
| 55 |
+
).to(self.device)
|
| 56 |
+
except Exception as e3:
|
| 57 |
+
raise RuntimeError(
|
| 58 |
+
f"Failed to load SMPL model. Error: {str(e3)}. "
|
| 59 |
+
f"Model path: {model_path_str}. "
|
| 60 |
+
f"Please ensure SMPL model files are in {model_path_str}/models/ directory."
|
| 61 |
+
)
|
| 62 |
|
| 63 |
def generate_mesh(
|
| 64 |
self,
|
|
|
|
| 107 |
_generator_instance = None
|
| 108 |
|
| 109 |
|
| 110 |
+
def get_generator(model_path: str = "smpl/smpl", gender: str = "neutral", device: str = "cpu") -> SMPLGenerator:
|
| 111 |
global _generator_instance
|
| 112 |
if _generator_instance is None:
|
| 113 |
_generator_instance = SMPLGenerator(model_path=model_path, gender=gender, device=device)
|
|
|
|
| 116 |
|
| 117 |
def generate_mesh(
|
| 118 |
betas: np.ndarray,
|
| 119 |
+
model_path: str = "smpl/smpl",
|
| 120 |
gender: str = "neutral",
|
| 121 |
device: str = "cpu"
|
| 122 |
) -> Tuple[np.ndarray, np.ndarray]:
|