Skip to content

Commit 47a9f47

Browse files
author
shentong7
committed
added video recon and refactored the code.
1 parent f5c8dc6 commit 47a9f47

16 files changed

+1297
-255
lines changed

README.md

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
# 3DMM model fitting using Pytorch
22

3-
This is a fitting framework implemented in Pytorch for reconstructing faces from images using BFM.
3+
This is a fitting framework implemented in Pytorch for reconstructing the face in an image or a video using a 3DMM model.
44

5-
The frame only uses Pytorch modules and a differentiable renderer from pytorch3d. The whole module is differentiable and can be integrated into other systems for the gradient propagation.
5+
The framework only uses Pytorch modules and a differentiable renderer from pytorch3d. The whole module is differentiable and can be integrated into other systems for the gradient propagation.
6+
7+
## Updates
8+
- :star2: Refactored the code to make future extension easier (other 3DMM models will be supported). The hyper-parameters are also re-organized.
9+
- :star2: Added support for monocular video reconstruction.
10+
- :star2: Added support for multi-gpu when processing long videos.
611

712
<p align="center">
813
<img src="gifs/demo.gif" alt="demo" width="512px">
914
</p>
1015

16+
<p align="center">
17+
<img src="gifs/video_demo.gif" alt="demo" width="830px">
18+
</p>
19+
1120
## Installation
1221
### Requirements
1322
- [pytorch3d](https://github.com/facebookresearch/pytorch3d) It might require a specific version of Pytorch to make pytorch3d run succussfully on gpus, please follow the official instructions.
1423
- Please refer to "requirements.txt" for other dependences.
1524
- [Basel Face Model 2009 (BFM09)](https://faces.dmi.unibas.ch/bfm/main.php?nav=1-0&id=basel_face_model)
1625
- [Expression Basis](https://github.com/Juyong/3DFace) extra expression basis.
1726

18-
## Instruction
27+
## How to use
28+
### Installation
1929
1. Clone the repo:
2030
```
2131
git clone https://github.com/ascust/3DMM-Fitting-Pytorch
@@ -26,17 +36,43 @@ cd 3DMM-Fitting-Pytorch
2636

2737
3. Download the Expression Basis. Go to the [repo](https://github.com/Juyong/3DFace), download the "CoarseData" and put "Exp_Pca.bin" into "BFM".
2838

29-
4. Convert the BMF parameters by:
39+
4. Convert the BFM parameters by:
3040
```
31-
python convert_bfm_data.py
41+
python convert_bfm09_data.py
3242
```
3343

34-
5. Run the code on specific images by:
44+
### Single Image Reconstruction
45+
```
46+
python fit_single_img.py --img_path data/000002.jpg --res_folder results
47+
```
48+
The results are stored in "results" folder.
49+
50+
51+
### Monocular Video Reconstruction
52+
To fit a video, simply run:
53+
```
54+
python fit_video.py --v_path data/sample_video.mp4 --res_folder results
55+
```
56+
The script will extract frames, detact features and fit all frames.
57+
58+
Fitting a video is a bit different from fitting an image, because frames are not isolated. In this implementation, we first estimate shape and texture of the target face using some of the frames (indicated by --nframes_shape). Then we estimate the other coefficients (expression, rotatation etc.) for each frame and keep the shape and texture coefficients fixed.
59+
60+
For the first frame, we use much more iterations to get a good starting point. For remaining frames, each is initialized from the previous estimation.Pose regulerizers are also imposed to increase temporal consistency.
61+
62+
Please check the reconstructed video in the result folder.
63+
64+
### Multi-gpu and -process support
65+
66+
It could take quite a while to process a long video, so multi-gpu and multi-process are also supported to accelerate fitting. To use this feature, simply run:
3567
```
36-
python fit.py --img data/000002.jpg
68+
python fit_video.py --v_path data/sample_video.mp4 --res_folder results --ngpus 4 --nworkers 4
3769
```
38-
The code will do rigid fitting as well as non-rigid fitting using landmarks as well as the image as supervision.
70+
Here we use 4 gpus and 4 workers. We can also use more workers to assign each gpu with multiple workers. The video will be evenly split and each clip will be fit respectively.
3971

72+
### Hyperparameters
73+
There are bunch of parameters that might require further tuning.
74+
The iteration numbers for non-rigid fitting "--first_nrf_iters" and "--rest_nrf_iters" affect the fitting speed a lot since we have to render the 3d mesh in each iteration. Try to change it to find a trade-off between speed and accuracy.
75+
If the result is not good, try to play with the parameters.
4076

4177
## Acknowledgement
4278
The code is partially borrowed from [Deep3DFaceReconstrution](https://github.com/microsoft/Deep3DFaceReconstruction), which is a Tensorflow-based deep reconstruction method using CNNs. Please note that our framework does not require any pretrained deep models. We estimate the parameters directly using the landmarks and photometric loss as the supervision.

convert_bfm09_data.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from scipy.io import loadmat, savemat
2+
import numpy as np
3+
from array import array
4+
5+
# load expression basis
6+
7+
8+
def LoadExpBasis():
9+
n_vertex = 53215
10+
Expbin = open('BFM/Exp_Pca.bin', 'rb')
11+
exp_dim = array('i')
12+
exp_dim.fromfile(Expbin, 1)
13+
expMU = array('f')
14+
expPC = array('f')
15+
expMU.fromfile(Expbin, 3*n_vertex)
16+
expPC.fromfile(Expbin, 3*exp_dim[0]*n_vertex)
17+
18+
expPC = np.array(expPC)
19+
expPC = np.reshape(expPC, [exp_dim[0], -1])
20+
expPC = np.transpose(expPC)
21+
22+
expEV = np.loadtxt('BFM/std_exp.txt')
23+
24+
return expPC, expEV
25+
26+
# transfer original BFM09 to our face model
27+
28+
29+
def transferBFM09():
30+
original_BFM = loadmat('BFM/01_MorphableModel.mat')
31+
shapePC = original_BFM['shapePC'] # shape basis
32+
shapeEV = original_BFM['shapeEV'] # corresponding eigen value
33+
shapeMU = original_BFM['shapeMU'] # mean face
34+
texPC = original_BFM['texPC'] # texture basis
35+
texEV = original_BFM['texEV'] # eigen value
36+
texMU = original_BFM['texMU'] # mean texture
37+
38+
expPC, expEV = LoadExpBasis()
39+
40+
# transfer BFM09 to our face model
41+
42+
idBase = shapePC*np.reshape(shapeEV, [-1, 199])
43+
idBase = idBase/1e5 # unify the scale to decimeter
44+
idBase = idBase[:, :80] # use only first 80 basis
45+
46+
exBase = expPC*np.reshape(expEV, [-1, 79])
47+
exBase = exBase/1e5 # unify the scale to decimeter
48+
exBase = exBase[:, :64] # use only first 64 basis
49+
50+
texBase = texPC*np.reshape(texEV, [-1, 199])
51+
texBase = texBase[:, :80] # use only first 80 basis
52+
53+
# our face model is cropped align face landmarks which contains only 35709 vertex.
54+
# original BFM09 contains 53490 vertex, and expression basis provided by JuYong contains 53215 vertex.
55+
# thus we select corresponding vertex to get our face model.
56+
57+
index_exp = loadmat('BFM/BFM_front_idx.mat')
58+
index_exp = index_exp['idx'].astype(
59+
np.int32) - 1 # starts from 0 (to 53215)
60+
61+
index_shape = loadmat('BFM/BFM_exp_idx.mat')
62+
index_shape = index_shape['trimIndex'].astype(
63+
np.int32) - 1 # starts from 0 (to 53490)
64+
index_shape = index_shape[index_exp]
65+
66+
idBase = np.reshape(idBase, [-1, 3, 80])
67+
idBase = idBase[index_shape, :, :]
68+
idBase = np.reshape(idBase, [-1, 80])
69+
70+
texBase = np.reshape(texBase, [-1, 3, 80])
71+
texBase = texBase[index_shape, :, :]
72+
texBase = np.reshape(texBase, [-1, 80])
73+
74+
exBase = np.reshape(exBase, [-1, 3, 64])
75+
exBase = exBase[index_exp, :, :]
76+
exBase = np.reshape(exBase, [-1, 64])
77+
78+
meanshape = np.reshape(shapeMU, [-1, 3])/1e5
79+
meanshape = meanshape[index_shape, :]
80+
meanshape = np.reshape(meanshape, [1, -1])
81+
82+
meantex = np.reshape(texMU, [-1, 3])
83+
meantex = meantex[index_shape, :]
84+
meantex = np.reshape(meantex, [1, -1])
85+
86+
# other info contains triangles, region used for computing photometric loss,
87+
# region used for skin texture regularization, and 68 landmarks index etc.
88+
other_info = loadmat('BFM/facemodel_info.mat')
89+
frontmask2_idx = other_info['frontmask2_idx']
90+
skinmask = other_info['skinmask']
91+
keypoints = other_info['keypoints']
92+
point_buf = other_info['point_buf']
93+
tri = other_info['tri']
94+
tri_mask2 = other_info['tri_mask2']
95+
96+
# save our face model
97+
savemat('BFM/BFM09_model_info.mat', {'meanshape': meanshape, 'meantex': meantex, 'idBase': idBase, 'exBase': exBase, 'texBase': texBase,
98+
'tri': tri, 'point_buf': point_buf, 'tri_mask2': tri_mask2, 'keypoints': keypoints, 'frontmask2_idx': frontmask2_idx, 'skinmask': skinmask})
99+
100+
101+
if __name__ == '__main__':
102+
transferBFM09()

convert_bfm_data.py

Lines changed: 0 additions & 96 deletions
This file was deleted.

0 commit comments

Comments
 (0)