Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hackassin
GitHub Repository: hackassin/learnopencv
Path: blob/master/Face-Recognition-with-ArcFace/align/detector.py
3142 views
1
import numpy as np
2
import torch
3
from torch.autograd import Variable
4
from align.get_nets import PNet, RNet, ONet
5
from align.box_utils import nms, calibrate_box, get_image_boxes, convert_to_square
6
from align.first_stage import run_first_stage
7
8
9
def detect_faces(image, min_face_size = 20.0,
10
thresholds=[0.6, 0.7, 0.8],
11
nms_thresholds=[0.7, 0.7, 0.7]):
12
"""
13
Arguments:
14
image: an instance of PIL.Image.
15
min_face_size: a float number.
16
thresholds: a list of length 3.
17
nms_thresholds: a list of length 3.
18
19
Returns:
20
two float numpy arrays of shapes [n_boxes, 4] and [n_boxes, 10],
21
bounding boxes and facial landmarks.
22
"""
23
24
# LOAD MODELS
25
pnet = PNet()
26
rnet = RNet()
27
onet = ONet()
28
onet.eval()
29
30
# BUILD AN IMAGE PYRAMID
31
width, height = image.size
32
min_length = min(height, width)
33
34
min_detection_size = 12
35
factor = 0.707 # sqrt(0.5)
36
37
# scales for scaling the image
38
scales = []
39
40
# scales the image so that
41
# minimum size that we can detect equals to
42
# minimum face size that we want to detect
43
m = min_detection_size/min_face_size
44
min_length *= m
45
46
factor_count = 0
47
while min_length > min_detection_size:
48
scales.append(m*factor**factor_count)
49
min_length *= factor
50
factor_count += 1
51
52
# STAGE 1
53
54
# it will be returned
55
bounding_boxes = []
56
57
# run P-Net on different scales
58
for s in scales:
59
boxes = run_first_stage(image, pnet, scale = s, threshold = thresholds[0])
60
bounding_boxes.append(boxes)
61
62
# collect boxes (and offsets, and scores) from different scales
63
bounding_boxes = [i for i in bounding_boxes if i is not None]
64
bounding_boxes = np.vstack(bounding_boxes)
65
66
keep = nms(bounding_boxes[:, 0:5], nms_thresholds[0])
67
bounding_boxes = bounding_boxes[keep]
68
69
# use offsets predicted by pnet to transform bounding boxes
70
bounding_boxes = calibrate_box(bounding_boxes[:, 0:5], bounding_boxes[:, 5:])
71
# shape [n_boxes, 5]
72
73
bounding_boxes = convert_to_square(bounding_boxes)
74
bounding_boxes[:, 0:4] = np.round(bounding_boxes[:, 0:4])
75
76
# STAGE 2
77
78
img_boxes = get_image_boxes(bounding_boxes, image, size = 24)
79
img_boxes = Variable(torch.FloatTensor(img_boxes), volatile = True)
80
output = rnet(img_boxes)
81
offsets = output[0].data.numpy() # shape [n_boxes, 4]
82
probs = output[1].data.numpy() # shape [n_boxes, 2]
83
84
keep = np.where(probs[:, 1] > thresholds[1])[0]
85
bounding_boxes = bounding_boxes[keep]
86
bounding_boxes[:, 4] = probs[keep, 1].reshape((-1, ))
87
offsets = offsets[keep]
88
89
keep = nms(bounding_boxes, nms_thresholds[1])
90
bounding_boxes = bounding_boxes[keep]
91
bounding_boxes = calibrate_box(bounding_boxes, offsets[keep])
92
bounding_boxes = convert_to_square(bounding_boxes)
93
bounding_boxes[:, 0:4] = np.round(bounding_boxes[:, 0:4])
94
95
# STAGE 3
96
97
img_boxes = get_image_boxes(bounding_boxes, image, size = 48)
98
if len(img_boxes) == 0:
99
return [], []
100
img_boxes = Variable(torch.FloatTensor(img_boxes), volatile = True)
101
output = onet(img_boxes)
102
landmarks = output[0].data.numpy() # shape [n_boxes, 10]
103
offsets = output[1].data.numpy() # shape [n_boxes, 4]
104
probs = output[2].data.numpy() # shape [n_boxes, 2]
105
106
keep = np.where(probs[:, 1] > thresholds[2])[0]
107
bounding_boxes = bounding_boxes[keep]
108
bounding_boxes[:, 4] = probs[keep, 1].reshape((-1, ))
109
offsets = offsets[keep]
110
landmarks = landmarks[keep]
111
112
# compute landmark points
113
width = bounding_boxes[:, 2] - bounding_boxes[:, 0] + 1.0
114
height = bounding_boxes[:, 3] - bounding_boxes[:, 1] + 1.0
115
xmin, ymin = bounding_boxes[:, 0], bounding_boxes[:, 1]
116
landmarks[:, 0:5] = np.expand_dims(xmin, 1) + np.expand_dims(width, 1)*landmarks[:, 0:5]
117
landmarks[:, 5:10] = np.expand_dims(ymin, 1) + np.expand_dims(height, 1)*landmarks[:, 5:10]
118
119
bounding_boxes = calibrate_box(bounding_boxes, offsets)
120
keep = nms(bounding_boxes, nms_thresholds[2], mode = 'min')
121
bounding_boxes = bounding_boxes[keep]
122
landmarks = landmarks[keep]
123
124
return bounding_boxes, landmarks
125
126