forked from cleardusk/3DDFA_V2
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathTDDFA.py
executable file
·126 lines (100 loc) · 4.18 KB
/
TDDFA.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# coding: utf-8
__author__ = 'cleardusk'
import os.path as osp
import time
import numpy as np
import cv2
import torch
from torchvision.transforms import Compose
import torch.backends.cudnn as cudnn
import models
from utils.io import _load
from utils.functions import get_suffix, parse_roi_box_from_landmark, crop_img, \
draw_landmarks, parse_roi_box_from_bbox
from utils.tddfa_util import load_model, ToTensorGjz, NormalizeGjz, recon_dense, recon_sparse
make_abs_path = lambda fn: osp.join(osp.dirname(osp.realpath(__file__)), fn)
class TDDFA(object):
"""TDDFA: named Three-D Dense Face Alignment (TDDFA)"""
def __init__(self, **kvs):
torch.set_grad_enabled(False)
# config
self.gpu_mode = kvs.get('gpu_mode', False)
self.gpu_id = kvs.get('gpu_id', 0)
self.size = kvs.get('size', 120)
param_mean_std_fp = kvs.get(
'param_mean_std_fp', make_abs_path(f'configs/param_mean_std_62d_{self.size}x{self.size}.pkl')
)
# load model, 62 = 12(pose) + 40(shape) +10(expression)
model = getattr(models, kvs.get('arch'))(
num_classes=kvs.get('num_params', 62),
widen_factor=kvs.get('widen_factor', 1),
size=self.size,
mode=kvs.get('mode', 'small')
)
model = load_model(model, kvs.get('checkpoint_fp'))
if self.gpu_mode:
cudnn.benchmark = True
model = model.cuda(device=self.gpu_id)
self.model = model
self.model.eval() # eval mode, fix BN
# data normalization
transform_normalize = NormalizeGjz(mean=127.5, std=128)
transform_to_tensor = ToTensorGjz()
transform = Compose([transform_to_tensor, transform_normalize])
self.transform = transform
# params normalization config
r = _load(param_mean_std_fp)
self.param_mean = r.get('mean')
self.param_std = r.get('std')
# print('param_mean and param_srd', self.param_mean, self.param_std)
def __call__(self, img_ori, objs, **kvs):
"""The main call of TDDFA, given image and box / landmark, return 3DMM params and roi_box
:param img_ori: the input image
:param objs: the list of box or landmarks
:param kvs: options
:return: param list and roi_box list
"""
# Crop image, forward to get the param
param_lst = []
roi_box_lst = []
crop_policy = kvs.get('crop_policy', 'box')
for obj in objs:
if crop_policy == 'box':
# by face box
roi_box = parse_roi_box_from_bbox(obj)
elif crop_policy == 'landmark':
# by landmarks
roi_box = parse_roi_box_from_landmark(obj)
else:
raise Exception(f'Unknown crop policy {crop_policy}')
roi_box_lst.append(roi_box)
img = crop_img(img_ori, roi_box)
img = cv2.resize(img, dsize=(self.size, self.size), interpolation=cv2.INTER_LINEAR)
inp = self.transform(img).unsqueeze(0)
if self.gpu_mode:
inp = inp.cuda(device=self.gpu_id)
if kvs.get('timer_flag', False):
end = time.time()
param = self.model(inp)
elapse = f'Inference: {(time.time() - end) * 1000:.1f}ms'
print(elapse)
else:
param = self.model(inp)
param = param.squeeze().cpu().numpy().flatten().astype(np.float32)
param = param * self.param_std + self.param_mean # re-scale
# print('output', param)
param_lst.append(param)
return param_lst, roi_box_lst
def recon_vers(self, param_lst, roi_box_lst, **kvs):
dense_flag = kvs.get('dense_flag', False)
size = self.size
ver_lst = []
for param, roi_box in zip(param_lst, roi_box_lst):
if dense_flag:
# pts_dense = predict_dense(param, roi_box)
pts3d = recon_dense(param, roi_box, size)
else:
# pts68 = predict_68pts(param, roi_box)
pts3d = recon_sparse(param, roi_box, size) # 68 pts
ver_lst.append(pts3d)
return ver_lst