├── .gitignore ├── .gitmodules ├── BFM ├── BFM_exp_idx.mat ├── BFM_front_idx.mat ├── facemodel_info.mat ├── select_vertex_id.mat ├── similarity_Lm3D_all.mat └── std_exp.txt ├── LICENSE ├── SECURITY.md ├── data_loader.py ├── demo.py ├── face_decoder.py ├── images ├── albedo.png ├── alignment.png ├── camera.png ├── example.gif ├── example.png ├── extreme.png ├── lm3d.png └── lm5p.png ├── inception_resnet_v1.py ├── input ├── 000002.jpg ├── 000002.txt ├── 000006.jpg ├── 000006.txt ├── 000007.jpg ├── 000007.txt ├── 000031.jpg ├── 000031.txt ├── 000033.jpg ├── 000033.txt ├── 000037.jpg ├── 000037.txt ├── 000050.jpg ├── 000050.txt ├── 000055.jpg ├── 000055.txt ├── 000114.jpg ├── 000114.txt ├── 000125.jpg ├── 000125.txt ├── 000126.jpg ├── 000126.txt ├── 015259.jpg ├── 015259.txt ├── 015270.jpg ├── 015270.txt ├── 015309.jpg ├── 015309.txt ├── 015310.jpg ├── 015310.txt ├── 015316.jpg ├── 015316.txt ├── 015384.jpg ├── 015384.txt ├── vd006.png ├── vd006.txt ├── vd025.png ├── vd025.txt ├── vd026.png ├── vd026.txt ├── vd034.png ├── vd034.txt ├── vd051.png ├── vd051.txt ├── vd070.png ├── vd070.txt ├── vd092.png ├── vd092.txt ├── vd102.png └── vd102.txt ├── losses.py ├── networks.py ├── options.py ├── preprocess_img.py ├── readme.md ├── reconstruction_model.py ├── renderer └── __init__.py ├── skin.py ├── train.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tf_mesh_renderer"] 2 | path = tf_mesh_renderer 3 | url = https://github.com/google/tf_mesh_renderer.git 4 | -------------------------------------------------------------------------------- /BFM/BFM_exp_idx.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/BFM/BFM_exp_idx.mat -------------------------------------------------------------------------------- /BFM/BFM_front_idx.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/BFM/BFM_front_idx.mat -------------------------------------------------------------------------------- /BFM/facemodel_info.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/BFM/facemodel_info.mat -------------------------------------------------------------------------------- /BFM/select_vertex_id.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/BFM/select_vertex_id.mat -------------------------------------------------------------------------------- /BFM/similarity_Lm3D_all.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/BFM/similarity_Lm3D_all.mat -------------------------------------------------------------------------------- /BFM/std_exp.txt: -------------------------------------------------------------------------------- 1 | 453980 257264 263068 211890 135873 184721 47055.6 72732 62787.4 106226 56708.5 51439.8 34887.1 44378.7 51813.4 31030.7 23354.9 23128.1 19400 21827.6 22767.7 22057.4 19894.3 16172.8 17142.7 10035.3 14727.5 12972.5 10763.8 8953.93 8682.62 8941.81 6342.3 5205.3 7065.65 6083.35 6678.88 4666.63 5082.89 5134.76 4908.16 3964.93 3739.95 3180.09 2470.45 1866.62 1624.71 2423.74 1668.53 1471.65 1194.52 782.102 815.044 835.782 834.937 744.496 575.146 633.76 705.685 753.409 620.306 673.326 766.189 619.866 559.93 357.264 396.472 556.849 455.048 460.592 400.735 326.702 279.428 291.535 326.584 305.664 287.816 283.642 276.19 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /data_loader.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.contrib.data import prefetch_to_device, shuffle_and_repeat, map_and_batch 3 | import os 4 | import glob 5 | import numpy as np 6 | os.environ["TF_CPP_MIN_LOG_LEVEL"] = '2' 7 | ############################################################################################### 8 | # data loader for training stage 9 | ############################################################################################### 10 | def _parse_function(image_path,lm_path,mask_path): 11 | 12 | # input image 13 | x = tf.read_file(image_path) 14 | img = tf.image.decode_png(x, channels=3) 15 | img = tf.cast(img,tf.float32) 16 | img = img[:,:,::-1] 17 | 18 | # ground truth landmark 19 | x2 = tf.read_file(lm_path) 20 | lm = tf.decode_raw(x2,tf.float64) 21 | lm = tf.cast(lm,tf.float32) 22 | lm = tf.reshape(lm,[68,2]) 23 | 24 | # skin mask 25 | x3 = tf.read_file(mask_path) 26 | mask = tf.image.decode_png(x3, channels=3) 27 | mask = tf.cast(mask,tf.float32) 28 | 29 | return img,lm,mask 30 | 31 | def check_lm_bin(dataset,lm_path): 32 | if not os.path.isdir(os.path.join(dataset,'lm_bin')): 33 | os.makedirs(os.path.join(dataset,'lm_bin')) 34 | for i in range(len(lm_path)): 35 | lm = np.loadtxt(lm_path[i]) 36 | lm = np.reshape(lm,[-1]) 37 | lm.tofile(os.path.join(dataset,'lm_bin',lm_path[i].split('/')[-1].replace('txt','bin'))) 38 | 39 | def load_dataset(opt,train=True): 40 | if train: 41 | data_path = opt.data_path 42 | else: 43 | data_path = opt.val_data_path 44 | image_path_all = [] 45 | lm_path_all = [] 46 | mask_path_all = [] 47 | 48 | for dataset in data_path: 49 | image_path = glob.glob(dataset + '/' + '*.png') 50 | image_path.sort() 51 | lm_path_ = [os.path.join(dataset,'lm',f.split('/')[-1].replace('png','txt')) for f in image_path] 52 | lm_path_.sort() 53 | mask_path = [os.path.join(dataset,'mask',f.split('/')[-1]) for f in image_path] 54 | mask_path.sort() 55 | 56 | # check if landmark binary files exist 57 | check_lm_bin(dataset,lm_path_) 58 | 59 | lm_path = [os.path.join(dataset,'lm_bin',f.split('/')[-1].replace('png','bin')) for f in image_path] 60 | lm_path.sort() 61 | 62 | image_path_all += image_path 63 | mask_path_all += mask_path 64 | lm_path_all += lm_path 65 | 66 | dataset_num = len(image_path_all) 67 | 68 | dataset = tf.data.Dataset.from_tensor_slices((image_path_all,lm_path_all,mask_path_all)) 69 | dataset = dataset. \ 70 | apply(shuffle_and_repeat(dataset_num)). \ 71 | apply(map_and_batch(_parse_function, opt.batch_size, num_parallel_batches=4, drop_remainder=True)). \ 72 | apply(prefetch_to_device('/gpu:0', None)) # When using dataset.prefetch, use buffer_size=None to let it detect optimal buffer size 73 | 74 | inputs_iterator = dataset.make_one_shot_iterator() 75 | return inputs_iterator 76 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | from PIL import Image 4 | import os 5 | import glob 6 | import platform 7 | import argparse 8 | from scipy.io import loadmat,savemat 9 | 10 | from preprocess_img import align_img 11 | from utils import * 12 | from face_decoder import Face3D 13 | from options import Option 14 | 15 | is_windows = platform.system() == "Windows" 16 | 17 | def parse_args(): 18 | 19 | desc = "Deep3DFaceReconstruction" 20 | parser = argparse.ArgumentParser(description=desc) 21 | 22 | parser.add_argument('--pretrain_weights', type=str, default=None, help='path for pre-trained model') 23 | parser.add_argument('--use_pb', type=int, default=1, help='validation data folder') 24 | 25 | return parser.parse_args() 26 | 27 | def restore_weights(sess,opt): 28 | var_list = tf.trainable_variables() 29 | g_list = tf.global_variables() 30 | 31 | # add batch normalization params into trainable variables 32 | bn_moving_vars = [g for g in g_list if 'moving_mean' in g.name] 33 | bn_moving_vars += [g for g in g_list if 'moving_variance' in g.name] 34 | var_list +=bn_moving_vars 35 | 36 | # create saver to save and restore weights 37 | saver = tf.train.Saver(var_list = var_list) 38 | saver.restore(sess,opt.pretrain_weights) 39 | 40 | def demo(): 41 | # input and output folder 42 | args = parse_args() 43 | 44 | image_path = 'input' 45 | save_path = 'output' 46 | if not os.path.exists(save_path): 47 | os.makedirs(save_path) 48 | img_list = glob.glob(image_path + '/' + '*.png') 49 | img_list +=glob.glob(image_path + '/' + '*.jpg') 50 | 51 | # read BFM face model 52 | # transfer original BFM model to our model 53 | if not os.path.isfile('./BFM/BFM_model_front.mat'): 54 | transferBFM09() 55 | 56 | # read standard landmarks for preprocessing images 57 | lm3D = load_lm3d() 58 | n = 0 59 | 60 | # build reconstruction model 61 | with tf.Graph().as_default() as graph: 62 | 63 | with tf.device('/cpu:0'): 64 | opt = Option(is_train=False) 65 | opt.batch_size = 1 66 | opt.pretrain_weights = args.pretrain_weights 67 | FaceReconstructor = Face3D() 68 | images = tf.placeholder(name = 'input_imgs', shape = [opt.batch_size,224,224,3], dtype = tf.float32) 69 | 70 | if args.use_pb and os.path.isfile('network/FaceReconModel.pb'): 71 | print('Using pre-trained .pb file.') 72 | graph_def = load_graph('network/FaceReconModel.pb') 73 | tf.import_graph_def(graph_def,name='resnet',input_map={'input_imgs:0': images}) 74 | # output coefficients of R-Net (dim = 257) 75 | coeff = graph.get_tensor_by_name('resnet/coeff:0') 76 | else: 77 | print('Using pre-trained .ckpt file: %s'%opt.pretrain_weights) 78 | import networks 79 | coeff = networks.R_Net(images,is_training=False) 80 | 81 | # reconstructing faces 82 | FaceReconstructor.Reconstruction_Block(coeff,opt) 83 | face_shape = FaceReconstructor.face_shape_t 84 | face_texture = FaceReconstructor.face_texture 85 | face_color = FaceReconstructor.face_color 86 | landmarks_2d = FaceReconstructor.landmark_p 87 | recon_img = FaceReconstructor.render_imgs 88 | tri = FaceReconstructor.facemodel.face_buf 89 | 90 | 91 | with tf.Session() as sess: 92 | if not args.use_pb : 93 | restore_weights(sess,opt) 94 | 95 | print('reconstructing...') 96 | for file in img_list: 97 | n += 1 98 | print(n) 99 | # load images and corresponding 5 facial landmarks 100 | img,lm = load_img(file,file.replace('png','txt').replace('jpg','txt')) 101 | # preprocess input image 102 | input_img,lm_new,transform_params = align_img(img,lm,lm3D) 103 | 104 | coeff_,face_shape_,face_texture_,face_color_,landmarks_2d_,recon_img_,tri_ = sess.run([coeff,\ 105 | face_shape,face_texture,face_color,landmarks_2d,recon_img,tri],feed_dict = {images: input_img}) 106 | 107 | 108 | # reshape outputs 109 | input_img = np.squeeze(input_img) 110 | face_shape_ = np.squeeze(face_shape_, (0)) 111 | face_texture_ = np.squeeze(face_texture_, (0)) 112 | face_color_ = np.squeeze(face_color_, (0)) 113 | landmarks_2d_ = np.squeeze(landmarks_2d_, (0)) 114 | if not is_windows: 115 | recon_img_ = np.squeeze(recon_img_, (0)) 116 | 117 | # save output files 118 | if not is_windows: 119 | savemat(os.path.join(save_path,file.split(os.path.sep)[-1].replace('.png','.mat').replace('jpg','mat')),{'cropped_img':input_img[:,:,::-1],'recon_img':recon_img_,'coeff':coeff_,\ 120 | 'face_shape':face_shape_,'face_texture':face_texture_,'face_color':face_color_,'lm_68p':landmarks_2d_,'lm_5p':lm_new}) 121 | save_obj(os.path.join(save_path,file.split(os.path.sep)[-1].replace('.png','_mesh.obj').replace('.jpg','_mesh.obj')),face_shape_,tri_,np.clip(face_color_,0,255)/255) # 3D reconstruction face (in canonical view) 122 | 123 | if __name__ == '__main__': 124 | demo() 125 | -------------------------------------------------------------------------------- /face_decoder.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import math as m 3 | import numpy as np 4 | from scipy.io import loadmat 5 | import platform 6 | 7 | is_windows = platform.system() == "Windows" 8 | 9 | if not is_windows: 10 | from renderer import mesh_renderer 11 | ############################################################################################### 12 | # Reconstruct 3D face based on output coefficients and facemodel 13 | ############################################################################################### 14 | 15 | # BFM 3D face model 16 | class BFM(): 17 | def __init__(self,model_path = './BFM/BFM_model_front.mat'): 18 | model = loadmat(model_path) 19 | self.meanshape = tf.constant(model['meanshape']) # mean face shape. [3*N,1] 20 | self.idBase = tf.constant(model['idBase']) # identity basis. [3*N,80] 21 | self.exBase = tf.constant(model['exBase'].astype(np.float32)) # expression basis. [3*N,64] 22 | self.meantex = tf.constant(model['meantex']) # mean face texture. [3*N,1] (0-255) 23 | self.texBase = tf.constant(model['texBase']) # texture basis. [3*N,80] 24 | self.point_buf = tf.constant(model['point_buf']) # face indices for each vertex that lies in. starts from 1. [N,8] 25 | self.face_buf = tf.constant(model['tri']) # vertex indices for each face. starts from 1. [F,3] 26 | self.front_mask_render = tf.squeeze(tf.constant(model['frontmask2_idx'])) # vertex indices for small face region to compute photometric error. starts from 1. 27 | self.mask_face_buf = tf.constant(model['tri_mask2']) # vertex indices for each face from small face region. starts from 1. [f,3] 28 | self.skin_mask = tf.squeeze(tf.constant(model['skinmask'])) # vertex indices for pre-defined skin region to compute reflectance loss 29 | self.keypoints = tf.squeeze(tf.constant(model['keypoints'])) # vertex indices for 68 landmarks. starts from 1. [68,1] 30 | 31 | # Analytic 3D face 32 | class Face3D(): 33 | def __init__(self): 34 | facemodel = BFM() 35 | self.facemodel = facemodel 36 | 37 | # analytic 3D face reconstructions with coefficients from R-Net 38 | def Reconstruction_Block(self,coeff,opt): 39 | #coeff: [batchsize,257] reconstruction coefficients 40 | 41 | id_coeff,ex_coeff,tex_coeff,angles,translation,gamma,camera_scale,f_scale = self.Split_coeff(coeff) 42 | # [batchsize,N,3] canonical face shape in BFM space 43 | face_shape = self.Shape_formation_block(id_coeff,ex_coeff,self.facemodel) 44 | # [batchsize,N,3] vertex texture (in RGB order) 45 | face_texture = self.Texture_formation_block(tex_coeff,self.facemodel) 46 | # [batchsize,3,3] rotation matrix for face shape 47 | rotation = self.Compute_rotation_matrix(angles) 48 | # [batchsize,N,3] vertex normal 49 | face_norm = self.Compute_norm(face_shape,self.facemodel) 50 | norm_r = tf.matmul(face_norm,rotation) 51 | 52 | # do rigid transformation for face shape using predicted rotation and translation 53 | face_shape_t = self.Rigid_transform_block(face_shape,rotation,translation) 54 | # compute 2d landmark projections 55 | # landmark_p: [batchsize,68,2] 56 | face_landmark_t = self.Compute_landmark(face_shape_t,self.facemodel) 57 | landmark_p = self.Projection_block(face_landmark_t,camera_scale,f_scale) 58 | 59 | # [batchsize,N,3] vertex color (in RGB order) 60 | face_color = self.Illumination_block(face_texture, norm_r, gamma) 61 | 62 | # reconstruction images and region masks for computing photometric loss 63 | render_imgs,img_mask,img_mask_crop = self.Render_block(face_shape_t,norm_r,face_color,camera_scale,f_scale,self.facemodel,opt.batch_size,opt.is_train) 64 | 65 | self.id_coeff = id_coeff 66 | self.ex_coeff = ex_coeff 67 | self.tex_coeff = tex_coeff 68 | self.f_scale = f_scale 69 | self.gamma = gamma 70 | self.face_shape = face_shape 71 | self.face_shape_t = face_shape_t 72 | self.face_texture = face_texture 73 | self.face_color = face_color 74 | self.landmark_p = landmark_p 75 | self.render_imgs = render_imgs 76 | self.img_mask = img_mask 77 | self.img_mask_crop = img_mask_crop 78 | 79 | #---------------------------------------------------------------------------------------------- 80 | def Split_coeff(self,coeff): 81 | 82 | id_coeff = coeff[:,:80] 83 | ex_coeff = coeff[:,80:144] 84 | tex_coeff = coeff[:,144:224] 85 | angles = coeff[:,224:227] 86 | gamma = coeff[:,227:254] 87 | translation = coeff[:,254:257] 88 | camera_scale = tf.ones([tf.shape(coeff)[0],1]) 89 | f_scale = tf.ones([tf.shape(coeff)[0],1]) 90 | 91 | return id_coeff,ex_coeff,tex_coeff,angles,translation,gamma,camera_scale,f_scale 92 | 93 | def Shape_formation_block(self,id_coeff,ex_coeff,facemodel): 94 | face_shape = tf.einsum('ij,aj->ai',facemodel.idBase,id_coeff) + \ 95 | tf.einsum('ij,aj->ai',facemodel.exBase,ex_coeff) + facemodel.meanshape 96 | 97 | # reshape face shape to [batchsize,N,3] 98 | face_shape = tf.reshape(face_shape,[tf.shape(face_shape)[0],-1,3]) 99 | # re-centering the face shape with mean shape 100 | face_shape = face_shape - tf.reshape(tf.reduce_mean(tf.reshape(facemodel.meanshape,[-1,3]),0),[1,1,3]) 101 | 102 | return face_shape 103 | 104 | def Compute_norm(self,face_shape,facemodel): 105 | shape = face_shape 106 | face_id = facemodel.face_buf 107 | point_id = facemodel.point_buf 108 | 109 | # face_id and point_id index starts from 1 110 | face_id = tf.cast(face_id - 1,tf.int32) 111 | point_id = tf.cast(point_id - 1,tf.int32) 112 | 113 | #compute normal for each face 114 | v1 = tf.gather(shape,face_id[:,0], axis = 1) 115 | v2 = tf.gather(shape,face_id[:,1], axis = 1) 116 | v3 = tf.gather(shape,face_id[:,2], axis = 1) 117 | e1 = v1 - v2 118 | e2 = v2 - v3 119 | face_norm = tf.cross(e1,e2) 120 | 121 | face_norm = tf.nn.l2_normalize(face_norm, dim = 2) # normalized face_norm first 122 | face_norm = tf.concat([face_norm,tf.zeros([tf.shape(face_shape)[0],1,3])], axis = 1) 123 | 124 | #compute normal for each vertex using one-ring neighborhood 125 | v_norm = tf.reduce_sum(tf.gather(face_norm, point_id, axis = 1), axis = 2) 126 | v_norm = tf.nn.l2_normalize(v_norm, dim = 2) 127 | 128 | return v_norm 129 | 130 | def Texture_formation_block(self,tex_coeff,facemodel): 131 | face_texture = tf.einsum('ij,aj->ai',facemodel.texBase,tex_coeff) + facemodel.meantex 132 | 133 | # reshape face texture to [batchsize,N,3], note that texture is in RGB order 134 | face_texture = tf.reshape(face_texture,[tf.shape(face_texture)[0],-1,3]) 135 | 136 | return face_texture 137 | 138 | def Compute_rotation_matrix(self,angles): 139 | n_data = tf.shape(angles)[0] 140 | 141 | # compute rotation matrix for X-axis, Y-axis, Z-axis respectively 142 | rotation_X = tf.concat([tf.ones([n_data,1]), 143 | tf.zeros([n_data,3]), 144 | tf.reshape(tf.cos(angles[:,0]),[n_data,1]), 145 | -tf.reshape(tf.sin(angles[:,0]),[n_data,1]), 146 | tf.zeros([n_data,1]), 147 | tf.reshape(tf.sin(angles[:,0]),[n_data,1]), 148 | tf.reshape(tf.cos(angles[:,0]),[n_data,1])], 149 | axis = 1 150 | ) 151 | 152 | rotation_Y = tf.concat([tf.reshape(tf.cos(angles[:,1]),[n_data,1]), 153 | tf.zeros([n_data,1]), 154 | tf.reshape(tf.sin(angles[:,1]),[n_data,1]), 155 | tf.zeros([n_data,1]), 156 | tf.ones([n_data,1]), 157 | tf.zeros([n_data,1]), 158 | -tf.reshape(tf.sin(angles[:,1]),[n_data,1]), 159 | tf.zeros([n_data,1]), 160 | tf.reshape(tf.cos(angles[:,1]),[n_data,1])], 161 | axis = 1 162 | ) 163 | 164 | rotation_Z = tf.concat([tf.reshape(tf.cos(angles[:,2]),[n_data,1]), 165 | -tf.reshape(tf.sin(angles[:,2]),[n_data,1]), 166 | tf.zeros([n_data,1]), 167 | tf.reshape(tf.sin(angles[:,2]),[n_data,1]), 168 | tf.reshape(tf.cos(angles[:,2]),[n_data,1]), 169 | tf.zeros([n_data,3]), 170 | tf.ones([n_data,1])], 171 | axis = 1 172 | ) 173 | 174 | rotation_X = tf.reshape(rotation_X,[n_data,3,3]) 175 | rotation_Y = tf.reshape(rotation_Y,[n_data,3,3]) 176 | rotation_Z = tf.reshape(rotation_Z,[n_data,3,3]) 177 | 178 | # R = RzRyRx 179 | rotation = tf.matmul(tf.matmul(rotation_Z,rotation_Y),rotation_X) 180 | 181 | rotation = tf.transpose(rotation, perm = [0,2,1]) 182 | 183 | return rotation 184 | 185 | def Projection_block(self,face_shape,camera_scale,f_scale): 186 | 187 | # pre-defined camera focal for pespective projection 188 | focal = tf.constant(1015.0) 189 | focal = focal*f_scale 190 | focal = tf.reshape(focal,[-1,1]) 191 | batchsize = tf.shape(focal)[0] 192 | 193 | # define camera position 194 | camera_pos = tf.reshape(tf.constant([0.0,0.0,10.0]),[1,1,3])*tf.reshape(camera_scale,[-1,1,1]) 195 | reverse_z = tf.tile(tf.reshape(tf.constant([1.0,0,0,0,1,0,0,0,-1.0]),[1,3,3]),[tf.shape(face_shape)[0],1,1]) 196 | 197 | # compute projection matrix 198 | p_matrix = tf.concat([focal,tf.zeros([batchsize,1]),112.*tf.ones([batchsize,1]),tf.zeros([batchsize,1]),focal,112.*tf.ones([batchsize,1]),tf.zeros([batchsize,2]),tf.ones([batchsize,1])],axis = 1) 199 | p_matrix = tf.reshape(p_matrix,[-1,3,3]) 200 | 201 | # convert z in world space to the distance to camera 202 | face_shape = tf.matmul(face_shape,reverse_z) + camera_pos 203 | aug_projection = tf.matmul(face_shape,tf.transpose(p_matrix,[0,2,1])) 204 | 205 | # [batchsize, N,2] 2d face projection 206 | face_projection = aug_projection[:,:,0:2]/tf.reshape(aug_projection[:,:,2],[tf.shape(face_shape)[0],tf.shape(aug_projection)[1],1]) 207 | 208 | 209 | return face_projection 210 | 211 | 212 | def Compute_landmark(self,face_shape,facemodel): 213 | 214 | # compute 3D landmark postitions with pre-computed 3D face shape 215 | keypoints_idx = facemodel.keypoints 216 | keypoints_idx = tf.cast(keypoints_idx - 1,tf.int32) 217 | face_landmark = tf.gather(face_shape,keypoints_idx,axis = 1) 218 | 219 | return face_landmark 220 | 221 | def Illumination_block(self,face_texture,norm_r,gamma): 222 | n_data = tf.shape(gamma)[0] 223 | n_point = tf.shape(norm_r)[1] 224 | gamma = tf.reshape(gamma,[n_data,3,9]) 225 | # set initial lighting with an ambient lighting 226 | init_lit = tf.constant([0.8,0,0,0,0,0,0,0,0]) 227 | gamma = gamma + tf.reshape(init_lit,[1,1,9]) 228 | 229 | # compute vertex color using SH function approximation 230 | a0 = m.pi 231 | a1 = 2*m.pi/tf.sqrt(3.0) 232 | a2 = 2*m.pi/tf.sqrt(8.0) 233 | c0 = 1/tf.sqrt(4*m.pi) 234 | c1 = tf.sqrt(3.0)/tf.sqrt(4*m.pi) 235 | c2 = 3*tf.sqrt(5.0)/tf.sqrt(12*m.pi) 236 | 237 | Y = tf.concat([tf.tile(tf.reshape(a0*c0,[1,1,1]),[n_data,n_point,1]), 238 | tf.expand_dims(-a1*c1*norm_r[:,:,1],2), 239 | tf.expand_dims(a1*c1*norm_r[:,:,2],2), 240 | tf.expand_dims(-a1*c1*norm_r[:,:,0],2), 241 | tf.expand_dims(a2*c2*norm_r[:,:,0]*norm_r[:,:,1],2), 242 | tf.expand_dims(-a2*c2*norm_r[:,:,1]*norm_r[:,:,2],2), 243 | tf.expand_dims(a2*c2*0.5/tf.sqrt(3.0)*(3*tf.square(norm_r[:,:,2])-1),2), 244 | tf.expand_dims(-a2*c2*norm_r[:,:,0]*norm_r[:,:,2],2), 245 | tf.expand_dims(a2*c2*0.5*(tf.square(norm_r[:,:,0])-tf.square(norm_r[:,:,1])),2)],axis = 2) 246 | 247 | color_r = tf.squeeze(tf.matmul(Y,tf.expand_dims(gamma[:,0,:],2)),axis = 2) 248 | color_g = tf.squeeze(tf.matmul(Y,tf.expand_dims(gamma[:,1,:],2)),axis = 2) 249 | color_b = tf.squeeze(tf.matmul(Y,tf.expand_dims(gamma[:,2,:],2)),axis = 2) 250 | 251 | #[batchsize,N,3] vertex color in RGB order 252 | face_color = tf.stack([color_r*face_texture[:,:,0],color_g*face_texture[:,:,1],color_b*face_texture[:,:,2]],axis = 2) 253 | 254 | return face_color 255 | 256 | def Rigid_transform_block(self,face_shape,rotation,translation): 257 | # do rigid transformation for 3D face shape 258 | face_shape_r = tf.matmul(face_shape,rotation) 259 | face_shape_t = face_shape_r + tf.reshape(translation,[tf.shape(face_shape)[0],1,3]) 260 | 261 | return face_shape_t 262 | 263 | def Render_block(self,face_shape,face_norm,face_color,camera_scale,f_scale,facemodel,batchsize,is_train=True): 264 | if is_train and is_windows: 265 | raise ValueError('Not support training with Windows environment.') 266 | 267 | if is_windows: 268 | return [],[],[] 269 | 270 | # render reconstruction images 271 | n_vex = int(facemodel.idBase.shape[0].value/3) 272 | fov_y = 2*tf.atan(112./(1015.*f_scale))*180./m.pi 273 | fov_y = tf.reshape(fov_y,[batchsize]) 274 | # full face region 275 | face_shape = tf.reshape(face_shape,[batchsize,n_vex,3]) 276 | face_norm = tf.reshape(face_norm,[batchsize,n_vex,3]) 277 | face_color = tf.reshape(face_color,[batchsize,n_vex,3]) 278 | 279 | # pre-defined cropped face region 280 | mask_face_shape = tf.gather(face_shape,tf.cast(facemodel.front_mask_render-1,tf.int32),axis = 1) 281 | mask_face_norm = tf.gather(face_norm,tf.cast(facemodel.front_mask_render-1,tf.int32),axis = 1) 282 | mask_face_color = tf.gather(face_color,tf.cast(facemodel.front_mask_render-1,tf.int32),axis = 1) 283 | 284 | # setting cammera settings 285 | camera_position = tf.constant([[0,0,10.0]])*tf.reshape(camera_scale,[-1,1]) 286 | camera_lookat = tf.constant([0,0,0.0]) 287 | camera_up = tf.constant([0,1.0,0]) 288 | 289 | # setting light source position(intensities are set to 0 because we have computed the vertex color) 290 | light_positions = tf.tile(tf.reshape(tf.constant([0,0,1e5]),[1,1,3]),[batchsize,1,1]) 291 | light_intensities = tf.tile(tf.reshape(tf.constant([0.0,0.0,0.0]),[1,1,3]),[batchsize,1,1]) 292 | ambient_color = tf.tile(tf.reshape(tf.constant([1.0,1,1]),[1,3]),[batchsize,1]) 293 | 294 | #using tf_mesh_renderer for rasterization (https://github.com/google/tf_mesh_renderer) 295 | # img: [batchsize,224,224,3] images in RGB order (0-255) 296 | # mask:[batchsize,224,224,1] transparency for img ({0,1} value) 297 | with tf.device('/cpu:0'): 298 | img_rgba = mesh_renderer.mesh_renderer(face_shape, 299 | tf.cast(facemodel.face_buf-1,tf.int32), 300 | face_norm, 301 | face_color, 302 | camera_position = camera_position, 303 | camera_lookat = camera_lookat, 304 | camera_up = camera_up, 305 | light_positions = light_positions, 306 | light_intensities = light_intensities, 307 | image_width = 224, 308 | image_height = 224, 309 | fov_y = fov_y, 310 | near_clip = 0.01, 311 | far_clip = 50.0, 312 | ambient_color = ambient_color) 313 | 314 | img = img_rgba[:,:,:,:3] 315 | mask = img_rgba[:,:,:,3:] 316 | 317 | img = tf.cast(img[:,:,:,::-1],tf.float32) #transfer RGB to BGR 318 | mask = tf.cast(mask,tf.float32) # full face region 319 | 320 | if is_train: 321 | # compute mask for small face region 322 | with tf.device('/cpu:0'): 323 | img_crop_rgba = mesh_renderer.mesh_renderer(mask_face_shape, 324 | tf.cast(facemodel.mask_face_buf-1,tf.int32), 325 | mask_face_norm, 326 | mask_face_color, 327 | camera_position = camera_position, 328 | camera_lookat = camera_lookat, 329 | camera_up = camera_up, 330 | light_positions = light_positions, 331 | light_intensities = light_intensities, 332 | image_width = 224, 333 | image_height = 224, 334 | fov_y = fov_y, 335 | near_clip = 0.01, 336 | far_clip = 50.0, 337 | ambient_color = ambient_color) 338 | 339 | mask_f = img_crop_rgba[:,:,:,3:] 340 | mask_f = tf.cast(mask_f,tf.float32) # small face region 341 | return img,mask,mask_f 342 | 343 | img_rgba = tf.cast(tf.clip_by_value(img_rgba,0,255),tf.float32) 344 | 345 | return img_rgba,mask,mask 346 | -------------------------------------------------------------------------------- /images/albedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/albedo.png -------------------------------------------------------------------------------- /images/alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/alignment.png -------------------------------------------------------------------------------- /images/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/camera.png -------------------------------------------------------------------------------- /images/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/example.gif -------------------------------------------------------------------------------- /images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/example.png -------------------------------------------------------------------------------- /images/extreme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/extreme.png -------------------------------------------------------------------------------- /images/lm3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/lm3d.png -------------------------------------------------------------------------------- /images/lm5p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/images/lm5p.png -------------------------------------------------------------------------------- /inception_resnet_v1.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | 16 | """Contains the definition of the Inception Resnet V1 architecture. 17 | As described in http://arxiv.org/abs/1602.07261. 18 | Inception-v4, Inception-ResNet and the Impact of Residual Connections 19 | on Learning 20 | Christian Szegedy, Sergey Ioffe, Vincent Vanhoucke, Alex Alemi 21 | """ 22 | from __future__ import absolute_import 23 | from __future__ import division 24 | from __future__ import print_function 25 | 26 | import tensorflow as tf 27 | import tensorflow.contrib.slim as slim 28 | 29 | 30 | # Inception-Resnet-A 31 | def block35(net, scale=1.0, activation_fn=tf.nn.relu, scope=None, reuse=None): 32 | """Builds the 35x35 resnet block.""" 33 | with tf.variable_scope(scope, 'Block35', [net], reuse=reuse): 34 | with tf.variable_scope('Branch_0'): 35 | tower_conv = slim.conv2d(net, 32, 1, scope='Conv2d_1x1') 36 | with tf.variable_scope('Branch_1'): 37 | tower_conv1_0 = slim.conv2d(net, 32, 1, scope='Conv2d_0a_1x1') 38 | tower_conv1_1 = slim.conv2d(tower_conv1_0, 32, 3, scope='Conv2d_0b_3x3') 39 | with tf.variable_scope('Branch_2'): 40 | tower_conv2_0 = slim.conv2d(net, 32, 1, scope='Conv2d_0a_1x1') 41 | tower_conv2_1 = slim.conv2d(tower_conv2_0, 32, 3, scope='Conv2d_0b_3x3') 42 | tower_conv2_2 = slim.conv2d(tower_conv2_1, 32, 3, scope='Conv2d_0c_3x3') 43 | mixed = tf.concat([tower_conv, tower_conv1_1, tower_conv2_2], 3) 44 | up = slim.conv2d(mixed, net.get_shape()[3], 1, normalizer_fn=None, 45 | activation_fn=None, scope='Conv2d_1x1') 46 | net += scale * up 47 | if activation_fn: 48 | net = activation_fn(net) 49 | return net 50 | 51 | # Inception-Resnet-B 52 | def block17(net, scale=1.0, activation_fn=tf.nn.relu, scope=None, reuse=None): 53 | """Builds the 17x17 resnet block.""" 54 | with tf.variable_scope(scope, 'Block17', [net], reuse=reuse): 55 | with tf.variable_scope('Branch_0'): 56 | tower_conv = slim.conv2d(net, 128, 1, scope='Conv2d_1x1') 57 | with tf.variable_scope('Branch_1'): 58 | tower_conv1_0 = slim.conv2d(net, 128, 1, scope='Conv2d_0a_1x1') 59 | tower_conv1_1 = slim.conv2d(tower_conv1_0, 128, [1, 7], 60 | scope='Conv2d_0b_1x7') 61 | tower_conv1_2 = slim.conv2d(tower_conv1_1, 128, [7, 1], 62 | scope='Conv2d_0c_7x1') 63 | mixed = tf.concat([tower_conv, tower_conv1_2], 3) 64 | up = slim.conv2d(mixed, net.get_shape()[3], 1, normalizer_fn=None, 65 | activation_fn=None, scope='Conv2d_1x1') 66 | net += scale * up 67 | if activation_fn: 68 | net = activation_fn(net) 69 | return net 70 | 71 | 72 | # Inception-Resnet-C 73 | def block8(net, scale=1.0, activation_fn=tf.nn.relu, scope=None, reuse=None): 74 | """Builds the 8x8 resnet block.""" 75 | with tf.variable_scope(scope, 'Block8', [net], reuse=reuse): 76 | with tf.variable_scope('Branch_0'): 77 | tower_conv = slim.conv2d(net, 192, 1, scope='Conv2d_1x1') 78 | with tf.variable_scope('Branch_1'): 79 | tower_conv1_0 = slim.conv2d(net, 192, 1, scope='Conv2d_0a_1x1') 80 | tower_conv1_1 = slim.conv2d(tower_conv1_0, 192, [1, 3], 81 | scope='Conv2d_0b_1x3') 82 | tower_conv1_2 = slim.conv2d(tower_conv1_1, 192, [3, 1], 83 | scope='Conv2d_0c_3x1') 84 | mixed = tf.concat([tower_conv, tower_conv1_2], 3) 85 | up = slim.conv2d(mixed, net.get_shape()[3], 1, normalizer_fn=None, 86 | activation_fn=None, scope='Conv2d_1x1') 87 | net += scale * up 88 | if activation_fn: 89 | net = activation_fn(net) 90 | return net 91 | 92 | def reduction_a(net, k, l, m, n): 93 | with tf.variable_scope('Branch_0'): 94 | tower_conv = slim.conv2d(net, n, 3, stride=2, padding='VALID', 95 | scope='Conv2d_1a_3x3') 96 | with tf.variable_scope('Branch_1'): 97 | tower_conv1_0 = slim.conv2d(net, k, 1, scope='Conv2d_0a_1x1') 98 | tower_conv1_1 = slim.conv2d(tower_conv1_0, l, 3, 99 | scope='Conv2d_0b_3x3') 100 | tower_conv1_2 = slim.conv2d(tower_conv1_1, m, 3, 101 | stride=2, padding='VALID', 102 | scope='Conv2d_1a_3x3') 103 | with tf.variable_scope('Branch_2'): 104 | tower_pool = slim.max_pool2d(net, 3, stride=2, padding='VALID', 105 | scope='MaxPool_1a_3x3') 106 | net = tf.concat([tower_conv, tower_conv1_2, tower_pool], 3) 107 | return net 108 | 109 | def reduction_b(net): 110 | with tf.variable_scope('Branch_0'): 111 | tower_conv = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') 112 | tower_conv_1 = slim.conv2d(tower_conv, 384, 3, stride=2, 113 | padding='VALID', scope='Conv2d_1a_3x3') 114 | with tf.variable_scope('Branch_1'): 115 | tower_conv1 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') 116 | tower_conv1_1 = slim.conv2d(tower_conv1, 256, 3, stride=2, 117 | padding='VALID', scope='Conv2d_1a_3x3') 118 | with tf.variable_scope('Branch_2'): 119 | tower_conv2 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') 120 | tower_conv2_1 = slim.conv2d(tower_conv2, 256, 3, 121 | scope='Conv2d_0b_3x3') 122 | tower_conv2_2 = slim.conv2d(tower_conv2_1, 256, 3, stride=2, 123 | padding='VALID', scope='Conv2d_1a_3x3') 124 | with tf.variable_scope('Branch_3'): 125 | tower_pool = slim.max_pool2d(net, 3, stride=2, padding='VALID', 126 | scope='MaxPool_1a_3x3') 127 | net = tf.concat([tower_conv_1, tower_conv1_1, 128 | tower_conv2_2, tower_pool], 3) 129 | return net 130 | 131 | def inference(images, keep_probability, phase_train=True, 132 | bottleneck_layer_size=128, weight_decay=0.0, reuse=None): 133 | batch_norm_params = { 134 | # Decay for the moving averages. 135 | 'decay': 0.995, 136 | # epsilon to prevent 0s in variance. 137 | 'epsilon': 0.001, 138 | # force in-place updates of mean and variance estimates 139 | 'updates_collections': None, 140 | # Moving averages ends up in the trainable variables collection 141 | 'variables_collections': [ tf.GraphKeys.TRAINABLE_VARIABLES ], 142 | } 143 | 144 | with slim.arg_scope([slim.conv2d, slim.fully_connected], 145 | weights_initializer=slim.initializers.xavier_initializer(), 146 | weights_regularizer=slim.l2_regularizer(weight_decay), 147 | normalizer_fn=slim.batch_norm, 148 | normalizer_params=batch_norm_params): 149 | return inception_resnet_v1(images, is_training=phase_train, 150 | dropout_keep_prob=keep_probability, bottleneck_layer_size=bottleneck_layer_size, reuse=reuse) 151 | 152 | 153 | def inception_resnet_v1(inputs, is_training=True, 154 | dropout_keep_prob=0.8, 155 | bottleneck_layer_size=128, 156 | reuse=None, 157 | scope='InceptionResnetV1'): 158 | """Creates the Inception Resnet V1 model. 159 | Args: 160 | inputs: a 4-D tensor of size [batch_size, height, width, 3]. 161 | num_classes: number of predicted classes. 162 | is_training: whether is training or not. 163 | dropout_keep_prob: float, the fraction to keep before final layer. 164 | reuse: whether or not the network and its variables should be reused. To be 165 | able to reuse 'scope' must be given. 166 | scope: Optional variable_scope. 167 | Returns: 168 | logits: the logits outputs of the model. 169 | end_points: the set of end_points from the inception model. 170 | """ 171 | end_points = {} 172 | 173 | with tf.variable_scope(scope, 'InceptionResnetV1', [inputs], reuse=reuse): 174 | with slim.arg_scope([slim.batch_norm, slim.dropout], 175 | is_training=is_training): 176 | with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], 177 | stride=1, padding='SAME'): 178 | 179 | # 149 x 149 x 32 180 | net = slim.conv2d(inputs, 32, 3, stride=2, padding='VALID', 181 | scope='Conv2d_1a_3x3') 182 | end_points['Conv2d_1a_3x3'] = net 183 | # 147 x 147 x 32 184 | net = slim.conv2d(net, 32, 3, padding='VALID', 185 | scope='Conv2d_2a_3x3') 186 | end_points['Conv2d_2a_3x3'] = net 187 | # 147 x 147 x 64 188 | net = slim.conv2d(net, 64, 3, scope='Conv2d_2b_3x3') 189 | end_points['Conv2d_2b_3x3'] = net 190 | # 73 x 73 x 64 191 | net = slim.max_pool2d(net, 3, stride=2, padding='VALID', 192 | scope='MaxPool_3a_3x3') 193 | end_points['MaxPool_3a_3x3'] = net 194 | # 73 x 73 x 80 195 | net = slim.conv2d(net, 80, 1, padding='VALID', 196 | scope='Conv2d_3b_1x1') 197 | end_points['Conv2d_3b_1x1'] = net 198 | # 71 x 71 x 192 199 | net = slim.conv2d(net, 192, 3, padding='VALID', 200 | scope='Conv2d_4a_3x3') 201 | end_points['Conv2d_4a_3x3'] = net 202 | # 35 x 35 x 256 203 | net = slim.conv2d(net, 256, 3, stride=2, padding='VALID', 204 | scope='Conv2d_4b_3x3') 205 | end_points['Conv2d_4b_3x3'] = net 206 | 207 | # 5 x Inception-resnet-A 208 | net = slim.repeat(net, 5, block35, scale=0.17) 209 | end_points['Mixed_5a'] = net 210 | 211 | # Reduction-A 212 | with tf.variable_scope('Mixed_6a'): 213 | net = reduction_a(net, 192, 192, 256, 384) 214 | end_points['Mixed_6a'] = net 215 | 216 | # 10 x Inception-Resnet-B 217 | net = slim.repeat(net, 10, block17, scale=0.10) 218 | end_points['Mixed_6b'] = net 219 | 220 | # Reduction-B 221 | with tf.variable_scope('Mixed_7a'): 222 | net = reduction_b(net) 223 | end_points['Mixed_7a'] = net 224 | 225 | # 5 x Inception-Resnet-C 226 | net = slim.repeat(net, 5, block8, scale=0.20) 227 | end_points['Mixed_8a'] = net 228 | 229 | net = block8(net, activation_fn=None) 230 | end_points['Mixed_8b'] = net 231 | 232 | with tf.variable_scope('Logits'): 233 | end_points['PrePool'] = net 234 | #pylint: disable=no-member 235 | net = slim.avg_pool2d(net, net.get_shape()[1:3], padding='VALID', 236 | scope='AvgPool_1a_8x8') 237 | net = slim.flatten(net) 238 | 239 | net = slim.dropout(net, dropout_keep_prob, is_training=is_training, 240 | scope='Dropout') 241 | 242 | end_points['PreLogitsFlatten'] = net 243 | 244 | net = slim.fully_connected(net, bottleneck_layer_size, activation_fn=None, 245 | scope='Bottleneck', reuse=False) 246 | 247 | return net, end_points 248 | -------------------------------------------------------------------------------- /input/000002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000002.jpg -------------------------------------------------------------------------------- /input/000002.txt: -------------------------------------------------------------------------------- 1 | 142.84 207.18 2 | 222.02 203.9 3 | 159.24 253.57 4 | 146.59 290.93 5 | 227.52 284.74 6 | -------------------------------------------------------------------------------- /input/000006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000006.jpg -------------------------------------------------------------------------------- /input/000006.txt: -------------------------------------------------------------------------------- 1 | 199.93 158.28 2 | 255.34 166.54 3 | 236.08 198.92 4 | 198.83 229.24 5 | 245.23 234.52 6 | -------------------------------------------------------------------------------- /input/000007.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000007.jpg -------------------------------------------------------------------------------- /input/000007.txt: -------------------------------------------------------------------------------- 1 | 129.36 198.28 2 | 204.47 191.47 3 | 164.42 240.51 4 | 140.74 277.77 5 | 205.4 270.9 6 | -------------------------------------------------------------------------------- /input/000031.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000031.jpg -------------------------------------------------------------------------------- /input/000031.txt: -------------------------------------------------------------------------------- 1 | 151.23 240.71 2 | 274.05 235.52 3 | 217.37 305.99 4 | 158.03 346.06 5 | 272.17 341.09 6 | -------------------------------------------------------------------------------- /input/000033.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000033.jpg -------------------------------------------------------------------------------- /input/000033.txt: -------------------------------------------------------------------------------- 1 | 119.09 94.291 2 | 158.31 96.472 3 | 136.76 121.4 4 | 119.33 134.49 5 | 154.66 136.68 6 | -------------------------------------------------------------------------------- /input/000037.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000037.jpg -------------------------------------------------------------------------------- /input/000037.txt: -------------------------------------------------------------------------------- 1 | 147.37 159.39 2 | 196.94 163.26 3 | 190.68 194.36 4 | 153.72 228.44 5 | 193.94 229.7 6 | -------------------------------------------------------------------------------- /input/000050.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000050.jpg -------------------------------------------------------------------------------- /input/000050.txt: -------------------------------------------------------------------------------- 1 | 150.4 94.799 2 | 205.14 102.07 3 | 179.54 131.16 4 | 144.45 147.42 5 | 193.39 154.14 6 | -------------------------------------------------------------------------------- /input/000055.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000055.jpg -------------------------------------------------------------------------------- /input/000055.txt: -------------------------------------------------------------------------------- 1 | 114.26 193.42 2 | 205.8 190.27 3 | 154.15 244.02 4 | 124.69 295.22 5 | 200.88 292.69 6 | -------------------------------------------------------------------------------- /input/000114.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000114.jpg -------------------------------------------------------------------------------- /input/000114.txt: -------------------------------------------------------------------------------- 1 | 217.52 152.95 2 | 281.48 147.14 3 | 253.02 196.03 4 | 225.79 221.6 5 | 288.25 214.44 6 | -------------------------------------------------------------------------------- /input/000125.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000125.jpg -------------------------------------------------------------------------------- /input/000125.txt: -------------------------------------------------------------------------------- 1 | 90.928 99.858 2 | 146.87 100.33 3 | 114.22 130.36 4 | 91.579 153.32 5 | 143.63 153.56 6 | -------------------------------------------------------------------------------- /input/000126.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/000126.jpg -------------------------------------------------------------------------------- /input/000126.txt: -------------------------------------------------------------------------------- 1 | 307.56 166.54 2 | 387.06 159.62 3 | 335.52 222.26 4 | 319.3 248.85 5 | 397.71 239.14 6 | -------------------------------------------------------------------------------- /input/015259.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/015259.jpg -------------------------------------------------------------------------------- /input/015259.txt: -------------------------------------------------------------------------------- 1 | 226.38 193.65 2 | 319.12 208.97 3 | 279.99 245.88 4 | 213.79 290.55 5 | 303.03 302.1 6 | -------------------------------------------------------------------------------- /input/015270.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/015270.jpg -------------------------------------------------------------------------------- /input/015270.txt: -------------------------------------------------------------------------------- 1 | 208.4 410.08 2 | 364.41 388.68 3 | 291.6 503.57 4 | 244.82 572.86 5 | 383.18 553.49 6 | -------------------------------------------------------------------------------- /input/015309.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/015309.jpg -------------------------------------------------------------------------------- /input/015309.txt: -------------------------------------------------------------------------------- 1 | 284.61 496.57 2 | 562.77 550.78 3 | 395.85 712.84 4 | 238.92 786.8 5 | 495.61 827.22 6 | -------------------------------------------------------------------------------- /input/015310.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/015310.jpg -------------------------------------------------------------------------------- /input/015310.txt: -------------------------------------------------------------------------------- 1 | 153.95 153.43 2 | 211.13 161.54 3 | 197.28 190.26 4 | 150.82 215.98 5 | 202.32 223.12 6 | -------------------------------------------------------------------------------- /input/015316.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/015316.jpg -------------------------------------------------------------------------------- /input/015316.txt: -------------------------------------------------------------------------------- 1 | 481.31 396.88 2 | 667.75 392.43 3 | 557.81 440.55 4 | 490.44 586.28 5 | 640.56 583.2 6 | -------------------------------------------------------------------------------- /input/015384.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/015384.jpg -------------------------------------------------------------------------------- /input/015384.txt: -------------------------------------------------------------------------------- 1 | 191.79 143.97 2 | 271.86 151.23 3 | 191.25 210.29 4 | 187.82 257.12 5 | 258.82 261.96 6 | -------------------------------------------------------------------------------- /input/vd006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd006.png -------------------------------------------------------------------------------- /input/vd006.txt: -------------------------------------------------------------------------------- 1 | 123.12 117.58 2 | 176.59 122.09 3 | 126.99 144.68 4 | 117.61 183.43 5 | 163.94 186.41 6 | -------------------------------------------------------------------------------- /input/vd025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd025.png -------------------------------------------------------------------------------- /input/vd025.txt: -------------------------------------------------------------------------------- 1 | 180.12 116.13 2 | 263.18 98.397 3 | 230.48 154.72 4 | 201.37 199.01 5 | 279.18 182.56 6 | -------------------------------------------------------------------------------- /input/vd026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd026.png -------------------------------------------------------------------------------- /input/vd026.txt: -------------------------------------------------------------------------------- 1 | 171.27 263.54 2 | 286.58 263.88 3 | 203.35 333.02 4 | 170.6 389.42 5 | 281.73 386.84 6 | -------------------------------------------------------------------------------- /input/vd034.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd034.png -------------------------------------------------------------------------------- /input/vd034.txt: -------------------------------------------------------------------------------- 1 | 136.01 167.83 2 | 195.25 151.71 3 | 152.89 191.45 4 | 149.85 235.5 5 | 201.16 222.8 6 | -------------------------------------------------------------------------------- /input/vd051.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd051.png -------------------------------------------------------------------------------- /input/vd051.txt: -------------------------------------------------------------------------------- 1 | 161.92 292.04 2 | 254.21 283.81 3 | 212.75 342.06 4 | 170.78 387.28 5 | 254.6 379.82 6 | -------------------------------------------------------------------------------- /input/vd070.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd070.png -------------------------------------------------------------------------------- /input/vd070.txt: -------------------------------------------------------------------------------- 1 | 276.53 290.35 2 | 383.38 294.75 3 | 314.48 354.66 4 | 275.08 407.72 5 | 364.94 411.48 6 | -------------------------------------------------------------------------------- /input/vd092.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd092.png -------------------------------------------------------------------------------- /input/vd092.txt: -------------------------------------------------------------------------------- 1 | 108.59 149.07 2 | 157.35 143.85 3 | 134.4 173.2 4 | 117.88 200.79 5 | 159.56 196.36 6 | -------------------------------------------------------------------------------- /input/vd102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Deep3DFaceReconstruction/1935ea92af72a72c4cf7b8a677a822afd8554b51/input/vd102.png -------------------------------------------------------------------------------- /input/vd102.txt: -------------------------------------------------------------------------------- 1 | 121.62 225.96 2 | 186.73 223.07 3 | 162.99 269.82 4 | 132.12 302.62 5 | 186.42 299.21 6 | -------------------------------------------------------------------------------- /losses.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from scipy.io import loadmat,savemat 3 | ############################################################################################### 4 | # Define losses for training 5 | ############################################################################################### 6 | 7 | # photometric loss 8 | # input_imgs and render_imgs are [batchsize,h,w,3] BGR images 9 | # img_mask are [batchsize,h,w,1] attention masks 10 | def Photo_loss(input_imgs,render_imgs,img_mask): 11 | 12 | input_imgs = tf.cast(input_imgs,tf.float32) 13 | 14 | # img_mask = tf.squeeze(img_mask,3) 15 | img_mask = tf.stop_gradient(img_mask[:,:,:,0]) 16 | 17 | # photo loss with skin attention 18 | photo_loss = tf.sqrt(tf.reduce_sum(tf.square(input_imgs - render_imgs),axis = 3))*img_mask/255 19 | photo_loss = tf.reduce_sum(photo_loss) / tf.maximum(tf.reduce_sum(img_mask),1.0) 20 | 21 | return photo_loss 22 | 23 | # perceptual loss 24 | # id_feature and id_label are [batchsize, c] identity features for reconstruction images and input images 25 | def Perceptual_loss(id_feature,id_label): 26 | id_feature = tf.nn.l2_normalize(id_feature, dim = 1) 27 | id_label = tf.nn.l2_normalize(id_label, dim = 1) 28 | # cosine similarity 29 | sim = tf.reduce_sum(id_feature*id_label,1) 30 | loss = tf.reduce_sum(tf.maximum(0.0,1.0 - sim))/tf.cast(tf.shape(id_feature)[0],tf.float32) 31 | 32 | return loss 33 | 34 | # landmark loss 35 | # landmark_p and landmark_label are [batchsize, 68, 2] landmark projections for reconstruction images and input images 36 | def Landmark_loss(landmark_p,landmark_label): 37 | 38 | # we set higher weights for landmarks around the mouth and nose regions 39 | landmark_weight = tf.concat([tf.ones([1,28]),20*tf.ones([1,3]),tf.ones([1,29]),20*tf.ones([1,8])],axis = 1) 40 | landmark_weight = tf.tile(landmark_weight,[tf.shape(landmark_p)[0],1]) 41 | 42 | landmark_loss = tf.reduce_sum(tf.reduce_sum(tf.square(landmark_p-landmark_label),2)*landmark_weight)/(68.0*tf.cast(tf.shape(landmark_p)[0],tf.float32)) 43 | 44 | return landmark_loss 45 | 46 | # coefficient regularization to ensure plausible 3d faces 47 | def Regulation_loss(id_coeff,ex_coeff,tex_coeff,opt): 48 | w_ex = opt.w_ex 49 | w_tex = opt.w_tex 50 | 51 | regulation_loss = tf.nn.l2_loss(id_coeff) + w_ex * tf.nn.l2_loss(ex_coeff) + w_tex * tf.nn.l2_loss(tex_coeff) 52 | regulation_loss = 2*regulation_loss/ tf.cast(tf.shape(id_coeff)[0],tf.float32) 53 | 54 | return regulation_loss 55 | 56 | # albedo regularization to ensure an uniform skin albedo 57 | def Reflectance_loss(face_texture,facemodel): 58 | skin_mask = facemodel.skin_mask 59 | skin_mask = tf.reshape(skin_mask,[1,tf.shape(skin_mask)[0],1]) 60 | 61 | texture_mean = tf.reduce_sum(face_texture*skin_mask,1)/tf.reduce_sum(skin_mask) 62 | texture_mean = tf.expand_dims(texture_mean,1) 63 | 64 | # minimize texture variance for pre-defined skin region 65 | reflectance_loss = tf.reduce_sum(tf.square((face_texture - texture_mean)*skin_mask/255.0))/(tf.cast(tf.shape(face_texture)[0],tf.float32)*tf.reduce_sum(skin_mask)) 66 | 67 | return reflectance_loss 68 | 69 | # gamma regularization to ensure a nearly-monochromatic light 70 | def Gamma_loss(gamma): 71 | gamma = tf.reshape(gamma,[-1,3,9]) 72 | gamma_mean = tf.reduce_mean(gamma,1, keep_dims = True) 73 | 74 | gamma_loss = tf.reduce_mean(tf.square(gamma - gamma_mean)) 75 | 76 | return gamma_loss -------------------------------------------------------------------------------- /networks.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.contrib.slim.nets import resnet_v1 3 | slim = tf.contrib.slim 4 | from inception_resnet_v1 import inception_resnet_v1 5 | ############################################################################################### 6 | #Define R-Net and Perceptual-Net for 3D face reconstruction 7 | ############################################################################################### 8 | 9 | def R_Net(inputs,is_training=True): 10 | #input: [Batchsize,H,W,C], 0-255, BGR image 11 | inputs = tf.cast(inputs,tf.float32) 12 | # standard ResNet50 backbone (without the last classfication FC layer) 13 | with slim.arg_scope(resnet_v1.resnet_arg_scope()): 14 | net,end_points = resnet_v1.resnet_v1_50(inputs,is_training = is_training ,reuse = tf.AUTO_REUSE) 15 | 16 | # Modified FC layer with 257 channels for reconstruction coefficients 17 | net_id = slim.conv2d(net, 80, [1, 1], 18 | activation_fn=None, 19 | normalizer_fn=None, 20 | weights_initializer = tf.zeros_initializer(), 21 | scope='fc-id') 22 | net_ex = slim.conv2d(net, 64, [1, 1], 23 | activation_fn=None, 24 | normalizer_fn=None, 25 | weights_initializer = tf.zeros_initializer(), 26 | scope='fc-ex') 27 | net_tex = slim.conv2d(net, 80, [1, 1], 28 | activation_fn=None, 29 | normalizer_fn=None, 30 | weights_initializer = tf.zeros_initializer(), 31 | scope='fc-tex') 32 | net_angles = slim.conv2d(net, 3, [1, 1], 33 | activation_fn=None, 34 | normalizer_fn=None, 35 | weights_initializer = tf.zeros_initializer(), 36 | scope='fc-angles') 37 | net_gamma = slim.conv2d(net, 27, [1, 1], 38 | activation_fn=None, 39 | normalizer_fn=None, 40 | weights_initializer = tf.zeros_initializer(), 41 | scope='fc-gamma') 42 | net_t_xy = slim.conv2d(net, 2, [1, 1], 43 | activation_fn=None, 44 | normalizer_fn=None, 45 | weights_initializer = tf.zeros_initializer(), 46 | scope='fc-XY') 47 | net_t_z = slim.conv2d(net, 1, [1, 1], 48 | activation_fn=None, 49 | normalizer_fn=None, 50 | weights_initializer = tf.zeros_initializer(), 51 | scope='fc-Z') 52 | 53 | net_id = tf.squeeze(net_id, [1,2], name='fc-id/squeezed') 54 | net_ex = tf.squeeze(net_ex, [1,2], name='fc-ex/squeezed') 55 | net_tex = tf.squeeze(net_tex, [1,2],name='fc-tex/squeezed') 56 | net_angles = tf.squeeze(net_angles,[1,2], name='fc-angles/squeezed') 57 | net_gamma = tf.squeeze(net_gamma,[1,2], name='fc-gamma/squeezed') 58 | net_t_xy = tf.squeeze(net_t_xy,[1,2], name='fc-XY/squeezed') 59 | net_t_z = tf.squeeze(net_t_z,[1,2], name='fc-Z/squeezed') 60 | 61 | net_ = tf.concat([net_id,net_ex,net_tex,net_angles,net_gamma,net_t_xy,net_t_z], axis = 1) 62 | 63 | return net_ 64 | 65 | 66 | def Perceptual_Net(input_imgs): 67 | #input_imgs: [Batchsize,H,W,C], 0-255, BGR image 68 | 69 | input_imgs = tf.reshape(input_imgs,[-1,224,224,3]) 70 | input_imgs = tf.cast(input_imgs,tf.float32) 71 | input_imgs = tf.clip_by_value(input_imgs,0,255) 72 | input_imgs = (input_imgs - 127.5)/128.0 73 | 74 | #standard face-net backbone 75 | batch_norm_params = { 76 | 'decay': 0.995, 77 | 'epsilon': 0.001, 78 | 'updates_collections': None} 79 | 80 | with slim.arg_scope([slim.conv2d, slim.fully_connected],weights_initializer=slim.initializers.xavier_initializer(), 81 | weights_regularizer=slim.l2_regularizer(0.0), 82 | normalizer_fn=slim.batch_norm, 83 | normalizer_params=batch_norm_params): 84 | feature_128,_ = inception_resnet_v1(input_imgs, bottleneck_layer_size=128, is_training=False, reuse=tf.AUTO_REUSE) 85 | 86 | # output the last FC layer feature(before classification) as identity feature 87 | return feature_128 -------------------------------------------------------------------------------- /options.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | import os 4 | 5 | # training options 6 | 7 | class Option(): 8 | def __init__(self,model_name=None,is_train=True): 9 | #-------------------------------------------------------------------------------------- 10 | self.is_train = is_train 11 | self.model_dir = 'result' 12 | if model_name is None: 13 | self.model_name = 'model_test' 14 | else: 15 | self.model_name = model_name 16 | self.data_path = ['./processed_data'] 17 | self.val_data_path = ['./processed_data'] 18 | 19 | self.model_save_path = os.path.join(self.model_dir,self.model_name) 20 | if self.is_train: 21 | if not os.path.exists(self.model_save_path): 22 | os.makedirs(self.model_save_path) 23 | 24 | self.summary_dir = os.path.join(self.model_save_path,'summary') 25 | 26 | self.train_summary_path = os.path.join(self.summary_dir, 'train') 27 | self.val_summary_path = os.path.join(self.summary_dir, 'val') 28 | #--------------------------------------------------------------------------------------- 29 | # visible gpu settings 30 | self.config = tf.ConfigProto() 31 | self.config.gpu_options.visible_device_list = '0' 32 | self.use_pb = True 33 | #--------------------------------------------------------------------------------------- 34 | # training parameters 35 | 36 | self.w_photo = 1.92 37 | self.w_lm = 1.6e-3 38 | self.w_id = 0.2 39 | 40 | self.w_reg = 3.0e-4 41 | self.w_ref = 5.0 42 | 43 | self.w_gamma = 10.0 44 | 45 | self.w_ex = 0.8 46 | self.w_tex = 1.7e-2 47 | 48 | self.batch_size = 16 49 | self.boundaries = [100000] 50 | lr = [1e-4,2e-5] 51 | self.global_step = tf.Variable(0,name='global_step',trainable = False) 52 | self.lr = tf.train.piecewise_constant(self.global_step,self.boundaries,lr) 53 | self.augment = True 54 | self.train_maxiter = 200000 55 | self.train_summary_iter = 50 56 | self.image_summary_iter = 200 57 | self.val_summary_iter = 1000 58 | self.save_iter = 10000 59 | #--------------------------------------------------------------------------------------- 60 | # initial weights for resnet and facenet 61 | self.R_net_weights = os.path.join('./weights/resnet','resnet_v1_50.ckpt') 62 | self.Perceptual_net_weights = './weights/id_net/model-20170512-110547.ckpt-250000' 63 | self.pretrain_weights = os.path.join('train/model_test','iter_100000.ckpt') 64 | -------------------------------------------------------------------------------- /preprocess_img.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.io import loadmat,savemat 3 | from PIL import Image 4 | from skin import skinmask 5 | import argparse 6 | from utils import * 7 | import os 8 | import glob 9 | import tensorflow as tf 10 | 11 | os.environ['CUDA_VISIBLE_DEVICES'] = '0' 12 | 13 | #calculating least square problem 14 | def POS(xp,x): 15 | npts = xp.shape[1] 16 | 17 | A = np.zeros([2*npts,8]) 18 | 19 | A[0:2*npts-1:2,0:3] = x.transpose() 20 | A[0:2*npts-1:2,3] = 1 21 | 22 | A[1:2*npts:2,4:7] = x.transpose() 23 | A[1:2*npts:2,7] = 1; 24 | 25 | b = np.reshape(xp.transpose(),[2*npts,1]) 26 | 27 | k,_,_,_ = np.linalg.lstsq(A,b) 28 | 29 | R1 = k[0:3] 30 | R2 = k[4:7] 31 | sTx = k[3] 32 | sTy = k[7] 33 | s = (np.linalg.norm(R1) + np.linalg.norm(R2))/2 34 | t = np.stack([sTx,sTy],axis = 0) 35 | 36 | return t,s 37 | 38 | # resize and crop images 39 | def resize_n_crop_img(img,lm,t,s,target_size = 224.): 40 | w0,h0 = img.size 41 | w = (w0/s*102).astype(np.int32) 42 | h = (h0/s*102).astype(np.int32) 43 | img = img.resize((w,h),resample = Image.BICUBIC) 44 | 45 | left = (w/2 - target_size/2 + float((t[0] - w0/2)*102/s)).astype(np.int32) 46 | right = left + target_size 47 | up = (h/2 - target_size/2 + float((h0/2 - t[1])*102/s)).astype(np.int32) 48 | below = up + target_size 49 | 50 | img = img.crop((left,up,right,below)) 51 | img = np.array(img) 52 | img = img[:,:,::-1] #RGBtoBGR 53 | img = np.expand_dims(img,0) 54 | lm = np.stack([lm[:,0] - t[0] + w0/2,lm[:,1] - t[1] + h0/2],axis = 1)/s*102 55 | lm = lm - np.reshape(np.array([(w/2 - target_size/2),(h/2-target_size/2)]),[1,2]) 56 | 57 | return img,lm 58 | 59 | 60 | # resize and crop input images before sending to the R-Net 61 | def align_img(img,lm,lm3D): 62 | 63 | w0,h0 = img.size 64 | 65 | # change from image plane coordinates to 3D sapce coordinates(X-Y plane) 66 | lm = np.stack([lm[:,0],h0 - 1 - lm[:,1]], axis = 1) 67 | 68 | # calculate translation and scale factors using 5 facial landmarks and standard landmarks of a 3D face 69 | t,s = POS(lm.transpose(),lm3D.transpose()) 70 | 71 | # processing the image 72 | img_new,lm_new = resize_n_crop_img(img,lm,t,s) 73 | lm_new = np.stack([lm_new[:,0],223 - lm_new[:,1]], axis = 1) 74 | trans_params = np.array([w0,h0,102.0/s,t[0],t[1]]) 75 | 76 | return img_new,lm_new,trans_params 77 | 78 | # detect 68 face landmarks for aligned images 79 | def get_68landmark(img,detector,sess): 80 | 81 | input_img = detector.get_tensor_by_name('input_imgs:0') 82 | lm = detector.get_tensor_by_name('landmark:0') 83 | 84 | landmark = sess.run(lm,feed_dict={input_img:img}) 85 | landmark = np.reshape(landmark,[68,2]) 86 | landmark = np.stack([landmark[:,1],223-landmark[:,0]],axis=1) 87 | 88 | return landmark 89 | 90 | # get skin attention mask for aligned images 91 | def get_skinmask(img): 92 | 93 | img = np.squeeze(img,0) 94 | skin_img = skinmask(img) 95 | return skin_img 96 | 97 | def parse_args(): 98 | desc = "Data preprocessing for Deep3DRecon." 99 | parser = argparse.ArgumentParser(description=desc) 100 | 101 | parser.add_argument('--img_path', type=str, default='./input', help='original images folder') 102 | parser.add_argument('--save_path', type=str, default='./processed_data', help='custom path to save proccessed images and labels') 103 | 104 | 105 | return parser.parse_args() 106 | 107 | # training data pre-processing 108 | def preprocessing(): 109 | 110 | args = parse_args() 111 | image_path = args.img_path 112 | save_path = args.save_path 113 | if not os.path.isdir(save_path): 114 | os.makedirs(save_path) 115 | if not os.path.isdir(os.path.join(save_path,'lm')): 116 | os.makedirs(os.path.join(save_path,'lm')) 117 | if not os.path.isdir(os.path.join(save_path,'lm_bin')): 118 | os.makedirs(os.path.join(save_path,'lm_bin')) 119 | if not os.path.isdir(os.path.join(save_path,'mask')): 120 | os.makedirs(os.path.join(save_path,'mask')) 121 | 122 | img_list = sorted(glob.glob(image_path + '/' + '*.png')) 123 | img_list += sorted(glob.glob(image_path + '/' + '*.jpg')) 124 | 125 | lm3D = load_lm3d() 126 | 127 | with tf.Graph().as_default() as graph, tf.device('/gpu:0'): 128 | lm_detector = load_graph(os.path.join('network','landmark68_detector.pb')) 129 | tf.import_graph_def(lm_detector,name='') 130 | sess = tf.InteractiveSession() 131 | 132 | for file in img_list: 133 | 134 | print(file) 135 | name = file.split('/')[-1].replace('.png','').replace('.jpg','') 136 | img,lm5p = load_img(file,file.replace('png','txt').replace('jpg','txt')) 137 | img_align,_,_ = align_img(img,lm5p,lm3D) # [1,224,224,3] BGR image 138 | 139 | lm68p = get_68landmark(img_align,graph,sess) 140 | lm68p = lm68p.astype(np.float64) 141 | skin_mask = get_skinmask(img_align) 142 | 143 | Image.fromarray(img_align.squeeze(0)[:,:,::-1].astype(np.uint8),'RGB').save(os.path.join(save_path,name+'.png')) 144 | Image.fromarray(skin_mask.astype(np.uint8)).save(os.path.join(save_path,'mask',name+'.png')) 145 | 146 | np.savetxt(os.path.join(save_path,'lm',name+'.txt'),lm68p) 147 | lm_bin = np.reshape(lm68p,[-1]) 148 | lm_bin.tofile(os.path.join(save_path,'lm_bin',name+'.bin')) 149 | 150 | if __name__ == '__main__': 151 | preprocessing() -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Accurate 3D Face Reconstruction with Weakly-Supervised Learning: From Single Image to Image Set ## 2 | 3 |
4 |
5 |
39 |
40 |
45 |
46 |
51 |
52 |
215 |
216 |