nexusbert commited on
Commit
32d5b17
·
1 Parent(s): 5d4d48e

push models

Browse files
Files changed (33) hide show
  1. .gitignore +0 -1
  2. app.py +20 -2
  3. download_smpl.py +46 -0
  4. smpl/__MACOSX/._smpl +0 -0
  5. smpl/__MACOSX/smpl/._.DS_Store +0 -0
  6. smpl/__MACOSX/smpl/.___init__.py +0 -0
  7. smpl/__MACOSX/smpl/._models +0 -0
  8. smpl/__MACOSX/smpl/._smpl_webuser +0 -0
  9. smpl/__MACOSX/smpl/models/._basicModel_f_lbs_10_207_0_v1.0.0.pkl +3 -0
  10. smpl/__MACOSX/smpl/models/._basicmodel_m_lbs_10_207_0_v1.0.0.pkl +3 -0
  11. smpl/__MACOSX/smpl/smpl_webuser/._LICENSE.txt +0 -0
  12. smpl/__MACOSX/smpl/smpl_webuser/._README.txt +0 -0
  13. smpl/__MACOSX/smpl/smpl_webuser/.___init__.py +0 -0
  14. smpl/__MACOSX/smpl/smpl_webuser/._hello_world +0 -0
  15. smpl/__MACOSX/smpl/smpl_webuser/._lbs.py +0 -0
  16. smpl/__MACOSX/smpl/smpl_webuser/._posemapper.py +0 -0
  17. smpl/__MACOSX/smpl/smpl_webuser/._serialization.py +0 -0
  18. smpl/__MACOSX/smpl/smpl_webuser/._verts.py +0 -0
  19. smpl/__MACOSX/smpl/smpl_webuser/hello_world/._hello_smpl.py +0 -0
  20. smpl/__MACOSX/smpl/smpl_webuser/hello_world/._render_smpl.py +0 -0
  21. smpl/smpl/__init__.py +14 -0
  22. smpl/smpl/models/basicModel_f_lbs_10_207_0_v1.0.0.pkl +3 -0
  23. smpl/smpl/models/basicmodel_m_lbs_10_207_0_v1.0.0.pkl +3 -0
  24. smpl/smpl/smpl_webuser/LICENSE.txt +29 -0
  25. smpl/smpl/smpl_webuser/README.txt +56 -0
  26. smpl/smpl/smpl_webuser/__init__.py +13 -0
  27. smpl/smpl/smpl_webuser/hello_world/hello_smpl.py +64 -0
  28. smpl/smpl/smpl_webuser/hello_world/render_smpl.py +97 -0
  29. smpl/smpl/smpl_webuser/lbs.py +80 -0
  30. smpl/smpl/smpl_webuser/posemapper.py +49 -0
  31. smpl/smpl/smpl_webuser/serialization.py +137 -0
  32. smpl/smpl/smpl_webuser/verts.py +103 -0
  33. 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(status_code=500, detail=f"SMPL model not found: {str(e)}")
 
 
 
 
 
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 = "models/smpl", gender: str = "neutral", device: str = "cpu"):
10
  self.device = torch.device(device)
11
  self.gender = gender
12
- self.model_path = Path(model_path)
13
 
14
- if not self.model_path.exists():
15
- raise FileNotFoundError(
16
- f"SMPL model not found at {model_path}. "
17
- f"Please download SMPL models from https://smpl.is.tue.mpg.de/"
18
- )
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  try:
21
  self.smpl_model = smplx.create(
22
- str(self.model_path),
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
- str(self.model_path),
32
  model_type='smpl',
33
  gender=gender,
34
  batch_size=1,
35
  ext='pkl'
36
  ).to(self.device)
37
  except Exception as e2:
38
- raise RuntimeError(
39
- f"Failed to load SMPL model: {e}. "
40
- f"Please ensure SMPL model files are in {model_path}"
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 = "models/smpl", gender: str = "neutral", device: str = "cpu") -> SMPLGenerator:
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 = "models/smpl",
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]: