Upload directory
Browse files
aligners/differentiable_face_aligner/aligner_helper.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import numpy as np
|
| 3 |
+
import cv2
|
| 4 |
+
from skimage import transform as trans
|
| 5 |
+
import cv2
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def split_network_output(align_out):
|
| 9 |
+
anchor_bbox_pred, anchor_cls_pred, anchor_ldmk_pred, merged, _ = align_out
|
| 10 |
+
bbox, cls, ldmk = torch.split(merged, [4, 2, 10], dim=1)
|
| 11 |
+
return ldmk, bbox, cls
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def get_cv2_affine_from_landmark(ldmks, reference_ldmk, image_width, image_height, ):
|
| 15 |
+
assert ldmks.ndim == 2 # batchdim
|
| 16 |
+
assert ldmks.shape[1] == 10
|
| 17 |
+
assert isinstance(ldmks, torch.Tensor)
|
| 18 |
+
|
| 19 |
+
assert reference_ldmk.ndim == 2
|
| 20 |
+
assert reference_ldmk.shape[0] == 5
|
| 21 |
+
assert reference_ldmk.shape[1] == 2
|
| 22 |
+
assert isinstance(reference_ldmk, np.ndarray)
|
| 23 |
+
|
| 24 |
+
to_img_size = np.array([[[image_width, image_height]]])
|
| 25 |
+
ldmks = ldmks.view(ldmks.shape[0], 5, 2).detach().cpu().numpy()
|
| 26 |
+
ldmks = ldmks * to_img_size
|
| 27 |
+
transforms = []
|
| 28 |
+
for ldmk in ldmks:
|
| 29 |
+
tform = trans.SimilarityTransform()
|
| 30 |
+
tform.estimate(ldmk, reference_ldmk)
|
| 31 |
+
M = tform.params[0:2, :]
|
| 32 |
+
transforms.append(M)
|
| 33 |
+
transforms = np.stack(transforms, axis=0)
|
| 34 |
+
return transforms
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def cv2_param_to_torch_theta(cv2_tfms, image_width, image_height, output_width, output_height):
|
| 38 |
+
# https://github.com/wuneng/WarpAffine2GridSample
|
| 39 |
+
"""4.Affine Transformation Matrix to theta"""
|
| 40 |
+
assert cv2_tfms.ndim == 3 # N, 2, 3
|
| 41 |
+
assert cv2_tfms.shape[1] == 2
|
| 42 |
+
assert cv2_tfms.shape[2] == 3
|
| 43 |
+
|
| 44 |
+
srcs = np.array([[0, 0], [0, 1], [1, 1]], dtype=np.float32)
|
| 45 |
+
srcs = np.expand_dims(srcs, axis=0).repeat(cv2_tfms.shape[0], axis=0)
|
| 46 |
+
dsts = np.matmul(srcs, cv2_tfms[:, :, :2].transpose(0, 2, 1)) + cv2_tfms[:, :, 2:3].transpose(0, 2, 1)
|
| 47 |
+
|
| 48 |
+
# normalize to [-1, 1]
|
| 49 |
+
srcs = srcs / np.array([[[image_width, image_height]]]) * 2 - 1
|
| 50 |
+
dsts = dsts / np.array([[[output_width, output_height]]]) * 2 - 1
|
| 51 |
+
|
| 52 |
+
thetas = []
|
| 53 |
+
for src, dst in zip(srcs, dsts):
|
| 54 |
+
theta = trans.estimate_transform("affine", src=dst, dst=src).params[:2]
|
| 55 |
+
thetas.append(theta)
|
| 56 |
+
thetas = np.stack(thetas, axis=0)
|
| 57 |
+
thetas = torch.from_numpy(thetas).float()
|
| 58 |
+
return thetas
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def adjust_ldmks(ldmks, thetas):
|
| 62 |
+
inv_thetas = inv_matrix(thetas).to(ldmks.device).float()
|
| 63 |
+
_ldmks = torch.cat([ldmks, torch.ones((ldmks.shape[0], 5, 1)).to(ldmks.device)], dim=2)
|
| 64 |
+
ldmk_aligned = (((_ldmks) * 2 - 1) @ inv_thetas.permute(0,2,1)) / 2 + 0.5
|
| 65 |
+
return ldmk_aligned
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def inv_matrix(theta):
|
| 69 |
+
# torch batched version
|
| 70 |
+
assert theta.ndim == 3
|
| 71 |
+
a, b, t1 = theta[:, 0,0], theta[:, 0,1], theta[:, 0,2]
|
| 72 |
+
c, d, t2 = theta[:, 1,0], theta[:, 1,1], theta[:, 1,2]
|
| 73 |
+
det = a * d - b * c
|
| 74 |
+
inv_det = 1.0 / det
|
| 75 |
+
inv_mat = torch.stack([
|
| 76 |
+
torch.stack([d * inv_det, -b * inv_det, (b * t2 - d * t1) * inv_det], dim=1),
|
| 77 |
+
torch.stack([-c * inv_det, a * inv_det, (c * t1 - a * t2) * inv_det], dim=1)
|
| 78 |
+
], dim=1)
|
| 79 |
+
return inv_mat
|
| 80 |
+
|
| 81 |
+
def reference_landmark():
|
| 82 |
+
return np.array([[38.29459953, 51.69630051],
|
| 83 |
+
[73.53179932, 51.50139999],
|
| 84 |
+
[56.02519989, 71.73660278],
|
| 85 |
+
[41.54930115, 92.3655014],
|
| 86 |
+
[70.72990036, 92.20410156]])
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def draw_ldmk(img, ldmk):
|
| 90 |
+
if ldmk is None:
|
| 91 |
+
return img
|
| 92 |
+
colors = [(0, 255, 0), (255, 0, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255), (255, 0, 255)]
|
| 93 |
+
img = img.copy()
|
| 94 |
+
for i in range(5):
|
| 95 |
+
color = colors[i]
|
| 96 |
+
cv2.circle(img, (int(ldmk[i*2] * img.shape[1]), int(ldmk[i*2+1] * img.shape[0])), 1, color, 4)
|
| 97 |
+
return img
|