fast-face-python 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,202 @@
1
+ import cv2
2
+ import numpy as np
3
+
4
+ from ..schema import ProviderType
5
+ from .base import BaseFaceModel
6
+
7
+
8
+ class YuNet(BaseFaceModel):
9
+ OUTPUT_NAMES = [
10
+ "cls_8",
11
+ "cls_16",
12
+ "cls_32",
13
+ "obj_8",
14
+ "obj_16",
15
+ "obj_32",
16
+ "bbox_8",
17
+ "bbox_16",
18
+ "bbox_32",
19
+ "kps_8",
20
+ "kps_16",
21
+ "kps_32",
22
+ ]
23
+ STRIDES = (8, 16, 32)
24
+ DIVISOR = 32
25
+
26
+ def __init__(
27
+ self,
28
+ model_path: str,
29
+ input_size: tuple = (320, 320),
30
+ conf_threshold: float = 0.6,
31
+ nms_threshold: float = 0.4,
32
+ top_k: int = 5000,
33
+ keep_top_k: int = 1000,
34
+ providers: list[ProviderType] | None = None,
35
+ **kwargs,
36
+ ):
37
+ if providers is None:
38
+ providers = ["CPUExecutionProvider"]
39
+ super().__init__(
40
+ model_path=model_path,
41
+ conf_threshold=conf_threshold,
42
+ nms_threshold=nms_threshold,
43
+ top_k=top_k,
44
+ keep_top_k=keep_top_k,
45
+ providers=providers,
46
+ sess_options=kwargs.get("sess_options"),
47
+ )
48
+ self._input_w, self._input_h = input_size
49
+ self._update_pad_size()
50
+
51
+ def _update_pad_size(self):
52
+ self._pad_w = ((self._input_w - 1) // self.DIVISOR + 1) * self.DIVISOR
53
+ self._pad_h = ((self._input_h - 1) // self.DIVISOR + 1) * self.DIVISOR
54
+
55
+ def set_input_size(self, input_size: tuple[int, int]):
56
+ self._input_w, self._input_h = input_size
57
+ self._update_pad_size()
58
+
59
+ def preprocess(self, imgs: list[np.ndarray]) -> np.ndarray:
60
+ preprocessed_imgs = []
61
+ for img in imgs:
62
+ h, w = img.shape[:2]
63
+ scale = min(self._input_w / w, self._input_h / h)
64
+ new_w = int(w * scale)
65
+ new_h = int(h * scale)
66
+
67
+ resized = cv2.resize(img, (new_w, new_h))
68
+
69
+ padded = np.zeros((self._pad_h, self._pad_w, 3), dtype=np.uint8)
70
+ padded[:new_h, :new_w, :] = resized
71
+
72
+ preprocessed_imgs.append(padded)
73
+
74
+ batch_imgs = np.stack(preprocessed_imgs, axis=0)
75
+ batch_imgs = np.transpose(batch_imgs, (0, 3, 1, 2)).astype(np.float32)
76
+ return batch_imgs
77
+
78
+ def _generate_priors(self, h: int, w: int) -> np.ndarray:
79
+ priors = []
80
+ for stride in self.STRIDES:
81
+ feat_w = int(np.ceil(w / stride))
82
+ feat_h = int(np.ceil(h / stride))
83
+ for y in range(feat_h):
84
+ for x in range(feat_w):
85
+ cx = x * stride
86
+ cy = y * stride
87
+ priors.append([cx, cy, stride, stride])
88
+ return np.array(priors, dtype=np.float32)
89
+
90
+ def post_process(
91
+ self,
92
+ outputs: list[np.ndarray],
93
+ original_shapes: list[tuple[int, int]],
94
+ preprocessed_shape: tuple[int, int],
95
+ ) -> list[np.ndarray]:
96
+ batch_size = outputs[0].shape[0]
97
+ h, w = preprocessed_shape
98
+ priors = self._generate_priors(h, w)
99
+
100
+ cls_scores = []
101
+ obj_scores = []
102
+ bboxes = []
103
+ kpss = []
104
+
105
+ for i in range(3):
106
+ cls_out = outputs[i]
107
+ if len(cls_out.shape) == 3 and cls_out.shape[-1] == 2:
108
+ cls_out = cls_out[:, :, 1:2]
109
+ elif len(cls_out.shape) > 3:
110
+ cls_out = cls_out.reshape((batch_size, cls_out.shape[1], -1)).transpose(
111
+ (0, 2, 1)
112
+ )
113
+ if cls_out.shape[-1] == 2:
114
+ cls_out = cls_out[:, :, 1:2]
115
+ cls_scores.append(cls_out)
116
+
117
+ obj_out = outputs[i + 3]
118
+ if len(obj_out.shape) > 3:
119
+ obj_out = obj_out.reshape((batch_size, 1, -1)).transpose((0, 2, 1))
120
+ obj_scores.append(obj_out)
121
+
122
+ bbox_out = outputs[i + 6]
123
+ if len(bbox_out.shape) > 3:
124
+ bbox_out = bbox_out.reshape((batch_size, 4, -1)).transpose((0, 2, 1))
125
+ bboxes.append(bbox_out)
126
+
127
+ kps_out = outputs[i + 9]
128
+ if len(kps_out.shape) > 3:
129
+ kps_out = kps_out.reshape((batch_size, 10, -1)).transpose((0, 2, 1))
130
+ kpss.append(kps_out)
131
+
132
+ cls_scores = np.concatenate(cls_scores, axis=1)
133
+ obj_scores = np.concatenate(obj_scores, axis=1)
134
+ bboxes = np.concatenate(bboxes, axis=1)
135
+ kpss = np.concatenate(kpss, axis=1)
136
+
137
+ scores = cls_scores * obj_scores
138
+
139
+ results = []
140
+ for i in range(batch_size):
141
+ orig_h, orig_w = original_shapes[i][:2]
142
+ scale_factor = min(self._input_w / orig_w, self._input_h / orig_h)
143
+
144
+ batch_bbox = bboxes[i]
145
+ cx = batch_bbox[:, 0:1] * priors[:, 2:3] + priors[:, 0:1]
146
+ cy = batch_bbox[:, 1:2] * priors[:, 3:4] + priors[:, 1:2]
147
+ w_box = np.exp(batch_bbox[:, 2:3]) * priors[:, 2:3]
148
+ h_box = np.exp(batch_bbox[:, 3:4]) * priors[:, 3:4]
149
+
150
+ x1 = cx - w_box / 2.0
151
+ y1 = cy - h_box / 2.0
152
+ x2 = cx + w_box / 2.0
153
+ y2 = cy + h_box / 2.0
154
+
155
+ batch_kps = kpss[i]
156
+ decoded_kps = np.zeros_like(batch_kps)
157
+ for k in range(5):
158
+ decoded_kps[:, k * 2 : k * 2 + 1] = (
159
+ batch_kps[:, k * 2 : k * 2 + 1] * priors[:, 2:3] + priors[:, 0:1]
160
+ )
161
+ decoded_kps[:, k * 2 + 1 : k * 2 + 2] = (
162
+ batch_kps[:, k * 2 + 1 : k * 2 + 2] * priors[:, 3:4]
163
+ + priors[:, 1:2]
164
+ )
165
+
166
+ x1 /= scale_factor
167
+ y1 /= scale_factor
168
+ x2 /= scale_factor
169
+ y2 /= scale_factor
170
+ decoded_kps /= scale_factor
171
+
172
+ batch_scores = scores[i].flatten()
173
+
174
+ inds = np.where(batch_scores > self.conf_threshold)[0]
175
+
176
+ if len(inds) == 0:
177
+ results.append(np.empty((0, 15), dtype=np.float32))
178
+ continue
179
+
180
+ x1 = x1[inds]
181
+ y1 = y1[inds]
182
+ x2 = x2[inds]
183
+ y2 = y2[inds]
184
+ filtered_kps = decoded_kps[inds]
185
+ filtered_scores = batch_scores[inds]
186
+
187
+ order = filtered_scores.argsort()[::-1][: self.top_k]
188
+
189
+ x1 = x1[order]
190
+ y1 = y1[order]
191
+ x2 = x2[order]
192
+ y2 = y2[order]
193
+ filtered_kps = filtered_kps[order]
194
+ filtered_scores = filtered_scores[order]
195
+
196
+ dets = np.hstack(
197
+ (x1, y1, x2, y2, filtered_scores[:, np.newaxis], filtered_kps)
198
+ ).astype(np.float32, copy=False)
199
+
200
+ results.append(dets)
201
+
202
+ return results
fast_face/schema.py ADDED
@@ -0,0 +1,13 @@
1
+ from typing import Any, Union
2
+
3
+ MODEL_FILENAMES = {
4
+ "YUNET": "yunet.onnx",
5
+ "RETINAFACE_MOBILENET": "retinaface_mobilenet.onnx",
6
+ "RETINAFACE_RESNET50": "retinaface_resnet50.onnx",
7
+ # "SCRFD": "scrfd.onnx",
8
+ "ADAFACE_IR101": "adaface_ir101.onnx",
9
+ "ADAFACE_IR50": "adaface_ir50.onnx",
10
+ "ADAFACE_IR18": "adaface_ir18.onnx",
11
+ }
12
+
13
+ ProviderType = Union[str, tuple[str, dict[str, Any]]]