Spaces:
Running
Running
add utils
Browse files- utils/common.py +44 -0
- utils/face_align.py +103 -0
- utils/transform.py +116 -0
utils/common.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from numpy.linalg import norm as l2norm
|
| 2 |
+
|
| 3 |
+
class Face(dict):
|
| 4 |
+
|
| 5 |
+
def __init__(self, d=None, **kwargs):
|
| 6 |
+
super().__init__()
|
| 7 |
+
if d is None:
|
| 8 |
+
d = {}
|
| 9 |
+
if kwargs:
|
| 10 |
+
d.update(**kwargs)
|
| 11 |
+
for k, v in d.items():
|
| 12 |
+
setattr(self, k, v)
|
| 13 |
+
|
| 14 |
+
def __setattr__(self, name, value):
|
| 15 |
+
if isinstance(value, (list, tuple)):
|
| 16 |
+
value = [self.__class__(x)
|
| 17 |
+
if isinstance(x, dict) else x for x in value]
|
| 18 |
+
elif isinstance(value, dict) and not isinstance(value, self.__class__):
|
| 19 |
+
value = self.__class__(value)
|
| 20 |
+
super(Face, self).__setattr__(name, value)
|
| 21 |
+
super(Face, self).__setitem__(name, value)
|
| 22 |
+
|
| 23 |
+
__setitem__ = __setattr__
|
| 24 |
+
|
| 25 |
+
def __getattr__(self, name):
|
| 26 |
+
return None
|
| 27 |
+
|
| 28 |
+
@property
|
| 29 |
+
def embedding_norm(self):
|
| 30 |
+
if self.embedding is None:
|
| 31 |
+
return None
|
| 32 |
+
return l2norm(self.embedding)
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def normed_embedding(self):
|
| 36 |
+
if self.embedding is None:
|
| 37 |
+
return None
|
| 38 |
+
return self.embedding / self.embedding_norm
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def sex(self):
|
| 42 |
+
if self.gender is None:
|
| 43 |
+
return None
|
| 44 |
+
return 'M' if self.gender==1 else 'F'
|
utils/face_align.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
from skimage import transform as trans
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
arcface_dst = np.array(
|
| 7 |
+
[[38.2946, 51.6963], [73.5318, 51.5014], [56.0252, 71.7366],
|
| 8 |
+
[41.5493, 92.3655], [70.7299, 92.2041]],
|
| 9 |
+
dtype=np.float32)
|
| 10 |
+
|
| 11 |
+
def estimate_norm(lmk, image_size=112,mode='arcface'):
|
| 12 |
+
assert lmk.shape == (5, 2)
|
| 13 |
+
assert image_size%112==0 or image_size%128==0
|
| 14 |
+
if image_size%112==0:
|
| 15 |
+
ratio = float(image_size)/112.0
|
| 16 |
+
diff_x = 0
|
| 17 |
+
else:
|
| 18 |
+
ratio = float(image_size)/128.0
|
| 19 |
+
diff_x = 8.0*ratio
|
| 20 |
+
dst = arcface_dst * ratio
|
| 21 |
+
dst[:,0] += diff_x
|
| 22 |
+
tform = trans.SimilarityTransform()
|
| 23 |
+
tform.estimate(lmk, dst)
|
| 24 |
+
M = tform.params[0:2, :]
|
| 25 |
+
return M
|
| 26 |
+
|
| 27 |
+
def norm_crop(img, landmark, image_size=112, mode='arcface'):
|
| 28 |
+
M = estimate_norm(landmark, image_size, mode)
|
| 29 |
+
warped = cv2.warpAffine(img, M, (image_size, image_size), borderValue=0.0)
|
| 30 |
+
return warped
|
| 31 |
+
|
| 32 |
+
def norm_crop2(img, landmark, image_size=112, mode='arcface'):
|
| 33 |
+
M = estimate_norm(landmark, image_size, mode)
|
| 34 |
+
warped = cv2.warpAffine(img, M, (image_size, image_size), borderValue=0.0)
|
| 35 |
+
return warped, M
|
| 36 |
+
|
| 37 |
+
def square_crop(im, S):
|
| 38 |
+
if im.shape[0] > im.shape[1]:
|
| 39 |
+
height = S
|
| 40 |
+
width = int(float(im.shape[1]) / im.shape[0] * S)
|
| 41 |
+
scale = float(S) / im.shape[0]
|
| 42 |
+
else:
|
| 43 |
+
width = S
|
| 44 |
+
height = int(float(im.shape[0]) / im.shape[1] * S)
|
| 45 |
+
scale = float(S) / im.shape[1]
|
| 46 |
+
resized_im = cv2.resize(im, (width, height))
|
| 47 |
+
det_im = np.zeros((S, S, 3), dtype=np.uint8)
|
| 48 |
+
det_im[:resized_im.shape[0], :resized_im.shape[1], :] = resized_im
|
| 49 |
+
return det_im, scale
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def transform(data, center, output_size, scale, rotation):
|
| 53 |
+
scale_ratio = scale
|
| 54 |
+
rot = float(rotation) * np.pi / 180.0
|
| 55 |
+
#translation = (output_size/2-center[0]*scale_ratio, output_size/2-center[1]*scale_ratio)
|
| 56 |
+
t1 = trans.SimilarityTransform(scale=scale_ratio)
|
| 57 |
+
cx = center[0] * scale_ratio
|
| 58 |
+
cy = center[1] * scale_ratio
|
| 59 |
+
t2 = trans.SimilarityTransform(translation=(-1 * cx, -1 * cy))
|
| 60 |
+
t3 = trans.SimilarityTransform(rotation=rot)
|
| 61 |
+
t4 = trans.SimilarityTransform(translation=(output_size / 2,
|
| 62 |
+
output_size / 2))
|
| 63 |
+
t = t1 + t2 + t3 + t4
|
| 64 |
+
M = t.params[0:2]
|
| 65 |
+
cropped = cv2.warpAffine(data,
|
| 66 |
+
M, (output_size, output_size),
|
| 67 |
+
borderValue=0.0)
|
| 68 |
+
return cropped, M
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def trans_points2d(pts, M):
|
| 72 |
+
new_pts = np.zeros(shape=pts.shape, dtype=np.float32)
|
| 73 |
+
for i in range(pts.shape[0]):
|
| 74 |
+
pt = pts[i]
|
| 75 |
+
new_pt = np.array([pt[0], pt[1], 1.], dtype=np.float32)
|
| 76 |
+
new_pt = np.dot(M, new_pt)
|
| 77 |
+
#print('new_pt', new_pt.shape, new_pt)
|
| 78 |
+
new_pts[i] = new_pt[0:2]
|
| 79 |
+
|
| 80 |
+
return new_pts
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def trans_points3d(pts, M):
|
| 84 |
+
scale = np.sqrt(M[0][0] * M[0][0] + M[0][1] * M[0][1])
|
| 85 |
+
#print(scale)
|
| 86 |
+
new_pts = np.zeros(shape=pts.shape, dtype=np.float32)
|
| 87 |
+
for i in range(pts.shape[0]):
|
| 88 |
+
pt = pts[i]
|
| 89 |
+
new_pt = np.array([pt[0], pt[1], 1.], dtype=np.float32)
|
| 90 |
+
new_pt = np.dot(M, new_pt)
|
| 91 |
+
#print('new_pt', new_pt.shape, new_pt)
|
| 92 |
+
new_pts[i][0:2] = new_pt[0:2]
|
| 93 |
+
new_pts[i][2] = pts[i][2] * scale
|
| 94 |
+
|
| 95 |
+
return new_pts
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def trans_points(pts, M):
|
| 99 |
+
if pts.shape[1] == 2:
|
| 100 |
+
return trans_points2d(pts, M)
|
| 101 |
+
else:
|
| 102 |
+
return trans_points3d(pts, M)
|
| 103 |
+
|
utils/transform.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import math
|
| 3 |
+
import numpy as np
|
| 4 |
+
from skimage import transform as trans
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def transform(data, center, output_size, scale, rotation):
|
| 8 |
+
scale_ratio = scale
|
| 9 |
+
rot = float(rotation) * np.pi / 180.0
|
| 10 |
+
#translation = (output_size/2-center[0]*scale_ratio, output_size/2-center[1]*scale_ratio)
|
| 11 |
+
t1 = trans.SimilarityTransform(scale=scale_ratio)
|
| 12 |
+
cx = center[0] * scale_ratio
|
| 13 |
+
cy = center[1] * scale_ratio
|
| 14 |
+
t2 = trans.SimilarityTransform(translation=(-1 * cx, -1 * cy))
|
| 15 |
+
t3 = trans.SimilarityTransform(rotation=rot)
|
| 16 |
+
t4 = trans.SimilarityTransform(translation=(output_size / 2,
|
| 17 |
+
output_size / 2))
|
| 18 |
+
t = t1 + t2 + t3 + t4
|
| 19 |
+
M = t.params[0:2]
|
| 20 |
+
cropped = cv2.warpAffine(data,
|
| 21 |
+
M, (output_size, output_size),
|
| 22 |
+
borderValue=0.0)
|
| 23 |
+
return cropped, M
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def trans_points2d(pts, M):
|
| 27 |
+
new_pts = np.zeros(shape=pts.shape, dtype=np.float32)
|
| 28 |
+
for i in range(pts.shape[0]):
|
| 29 |
+
pt = pts[i]
|
| 30 |
+
new_pt = np.array([pt[0], pt[1], 1.], dtype=np.float32)
|
| 31 |
+
new_pt = np.dot(M, new_pt)
|
| 32 |
+
#print('new_pt', new_pt.shape, new_pt)
|
| 33 |
+
new_pts[i] = new_pt[0:2]
|
| 34 |
+
|
| 35 |
+
return new_pts
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def trans_points3d(pts, M):
|
| 39 |
+
scale = np.sqrt(M[0][0] * M[0][0] + M[0][1] * M[0][1])
|
| 40 |
+
#print(scale)
|
| 41 |
+
new_pts = np.zeros(shape=pts.shape, dtype=np.float32)
|
| 42 |
+
for i in range(pts.shape[0]):
|
| 43 |
+
pt = pts[i]
|
| 44 |
+
new_pt = np.array([pt[0], pt[1], 1.], dtype=np.float32)
|
| 45 |
+
new_pt = np.dot(M, new_pt)
|
| 46 |
+
#print('new_pt', new_pt.shape, new_pt)
|
| 47 |
+
new_pts[i][0:2] = new_pt[0:2]
|
| 48 |
+
new_pts[i][2] = pts[i][2] * scale
|
| 49 |
+
|
| 50 |
+
return new_pts
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def trans_points(pts, M):
|
| 54 |
+
if pts.shape[1] == 2:
|
| 55 |
+
return trans_points2d(pts, M)
|
| 56 |
+
else:
|
| 57 |
+
return trans_points3d(pts, M)
|
| 58 |
+
|
| 59 |
+
def estimate_affine_matrix_3d23d(X, Y):
|
| 60 |
+
''' Using least-squares solution
|
| 61 |
+
Args:
|
| 62 |
+
X: [n, 3]. 3d points(fixed)
|
| 63 |
+
Y: [n, 3]. corresponding 3d points(moving). Y = PX
|
| 64 |
+
Returns:
|
| 65 |
+
P_Affine: (3, 4). Affine camera matrix (the third row is [0, 0, 0, 1]).
|
| 66 |
+
'''
|
| 67 |
+
X_homo = np.hstack((X, np.ones([X.shape[0],1]))) #n x 4
|
| 68 |
+
P = np.linalg.lstsq(X_homo, Y)[0].T # Affine matrix. 3 x 4
|
| 69 |
+
return P
|
| 70 |
+
|
| 71 |
+
def P2sRt(P):
|
| 72 |
+
''' decompositing camera matrix P
|
| 73 |
+
Args:
|
| 74 |
+
P: (3, 4). Affine Camera Matrix.
|
| 75 |
+
Returns:
|
| 76 |
+
s: scale factor.
|
| 77 |
+
R: (3, 3). rotation matrix.
|
| 78 |
+
t: (3,). translation.
|
| 79 |
+
'''
|
| 80 |
+
t = P[:, 3]
|
| 81 |
+
R1 = P[0:1, :3]
|
| 82 |
+
R2 = P[1:2, :3]
|
| 83 |
+
s = (np.linalg.norm(R1) + np.linalg.norm(R2))/2.0
|
| 84 |
+
r1 = R1/np.linalg.norm(R1)
|
| 85 |
+
r2 = R2/np.linalg.norm(R2)
|
| 86 |
+
r3 = np.cross(r1, r2)
|
| 87 |
+
|
| 88 |
+
R = np.concatenate((r1, r2, r3), 0)
|
| 89 |
+
return s, R, t
|
| 90 |
+
|
| 91 |
+
def matrix2angle(R):
|
| 92 |
+
''' get three Euler angles from Rotation Matrix
|
| 93 |
+
Args:
|
| 94 |
+
R: (3,3). rotation matrix
|
| 95 |
+
Returns:
|
| 96 |
+
x: pitch
|
| 97 |
+
y: yaw
|
| 98 |
+
z: roll
|
| 99 |
+
'''
|
| 100 |
+
sy = math.sqrt(R[0,0] * R[0,0] + R[1,0] * R[1,0])
|
| 101 |
+
|
| 102 |
+
singular = sy < 1e-6
|
| 103 |
+
|
| 104 |
+
if not singular :
|
| 105 |
+
x = math.atan2(R[2,1] , R[2,2])
|
| 106 |
+
y = math.atan2(-R[2,0], sy)
|
| 107 |
+
z = math.atan2(R[1,0], R[0,0])
|
| 108 |
+
else :
|
| 109 |
+
x = math.atan2(-R[1,2], R[1,1])
|
| 110 |
+
y = math.atan2(-R[2,0], sy)
|
| 111 |
+
z = 0
|
| 112 |
+
|
| 113 |
+
# rx, ry, rz = np.rad2deg(x), np.rad2deg(y), np.rad2deg(z)
|
| 114 |
+
rx, ry, rz = x*180/np.pi, y*180/np.pi, z*180/np.pi
|
| 115 |
+
return rx, ry, rz
|
| 116 |
+
|