Scrappy-Doo commited on
Commit
be3fe01
·
verified ·
1 Parent(s): fbce62d

Upload 2 files

Browse files
facelib/detection/yolov5face/__init__.py ADDED
File without changes
facelib/detection/yolov5face/face_detector.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import copy
3
+ import re
4
+ import torch
5
+ import numpy as np
6
+
7
+ from pathlib import Path
8
+ from facelib.detection.yolov5face.models.yolo import Model
9
+ from facelib.detection.yolov5face.utils.datasets import letterbox
10
+ from facelib.detection.yolov5face.utils.general import (
11
+ check_img_size,
12
+ non_max_suppression_face,
13
+ scale_coords,
14
+ scale_coords_landmarks,
15
+ )
16
+
17
+ # IS_HIGH_VERSION = tuple(map(int, torch.__version__.split('+')[0].split('.')[:2])) >= (1, 9)
18
+ IS_HIGH_VERSION = [int(m) for m in list(re.findall(r"^([0-9]+)\.([0-9]+)\.([0-9]+)([^0-9][a-zA-Z0-9]*)?(\+git.*)?$",\
19
+ torch.__version__)[0][:3])] >= [1, 9, 0]
20
+
21
+
22
+ def isListempty(inList):
23
+ if isinstance(inList, list): # Is a list
24
+ return all(map(isListempty, inList))
25
+ return False # Not a list
26
+
27
+ class YoloDetector:
28
+ def __init__(
29
+ self,
30
+ config_name,
31
+ min_face=10,
32
+ target_size=None,
33
+ device='cuda',
34
+ ):
35
+ """
36
+ config_name: name of .yaml config with network configuration from models/ folder.
37
+ min_face : minimal face size in pixels.
38
+ target_size : target size of smaller image axis (choose lower for faster work). e.g. 480, 720, 1080.
39
+ None for original resolution.
40
+ """
41
+ self._class_path = Path(__file__).parent.absolute()
42
+ self.target_size = target_size
43
+ self.min_face = min_face
44
+ self.detector = Model(cfg=config_name)
45
+ self.device = device
46
+
47
+
48
+ def _preprocess(self, imgs):
49
+ """
50
+ Preprocessing image before passing through the network. Resize and conversion to torch tensor.
51
+ """
52
+ pp_imgs = []
53
+ for img in imgs:
54
+ h0, w0 = img.shape[:2] # orig hw
55
+ if self.target_size:
56
+ r = self.target_size / min(h0, w0) # resize image to img_size
57
+ if r < 1:
58
+ img = cv2.resize(img, (int(w0 * r), int(h0 * r)), interpolation=cv2.INTER_LINEAR)
59
+
60
+ imgsz = check_img_size(max(img.shape[:2]), s=self.detector.stride.max()) # check img_size
61
+ img = letterbox(img, new_shape=imgsz)[0]
62
+ pp_imgs.append(img)
63
+ pp_imgs = np.array(pp_imgs)
64
+ pp_imgs = pp_imgs.transpose(0, 3, 1, 2)
65
+ pp_imgs = torch.from_numpy(pp_imgs).to(self.device)
66
+ pp_imgs = pp_imgs.float() # uint8 to fp16/32
67
+ return pp_imgs / 255.0 # 0 - 255 to 0.0 - 1.0
68
+
69
+ def _postprocess(self, imgs, origimgs, pred, conf_thres, iou_thres):
70
+ """
71
+ Postprocessing of raw pytorch model output.
72
+ Returns:
73
+ bboxes: list of arrays with 4 coordinates of bounding boxes with format x1,y1,x2,y2.
74
+ points: list of arrays with coordinates of 5 facial keypoints (eyes, nose, lips corners).
75
+ """
76
+ bboxes = [[] for _ in range(len(origimgs))]
77
+ landmarks = [[] for _ in range(len(origimgs))]
78
+
79
+ pred = non_max_suppression_face(pred, conf_thres, iou_thres)
80
+
81
+ for image_id, origimg in enumerate(origimgs):
82
+ img_shape = origimg.shape
83
+ image_height, image_width = img_shape[:2]
84
+ gn = torch.tensor(img_shape)[[1, 0, 1, 0]] # normalization gain whwh
85
+ gn_lks = torch.tensor(img_shape)[[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]] # normalization gain landmarks
86
+ det = pred[image_id].cpu()
87
+ scale_coords(imgs[image_id].shape[1:], det[:, :4], img_shape).round()
88
+ scale_coords_landmarks(imgs[image_id].shape[1:], det[:, 5:15], img_shape).round()
89
+
90
+ for j in range(det.size()[0]):
91
+ box = (det[j, :4].view(1, 4) / gn).view(-1).tolist()
92
+ box = list(
93
+ map(int, [box[0] * image_width, box[1] * image_height, box[2] * image_width, box[3] * image_height])
94
+ )
95
+ if box[3] - box[1] < self.min_face:
96
+ continue
97
+ lm = (det[j, 5:15].view(1, 10) / gn_lks).view(-1).tolist()
98
+ lm = list(map(int, [i * image_width if j % 2 == 0 else i * image_height for j, i in enumerate(lm)]))
99
+ lm = [lm[i : i + 2] for i in range(0, len(lm), 2)]
100
+ bboxes[image_id].append(box)
101
+ landmarks[image_id].append(lm)
102
+ return bboxes, landmarks
103
+
104
+ def detect_faces(self, imgs, conf_thres=0.7, iou_thres=0.5):
105
+ """
106
+ Get bbox coordinates and keypoints of faces on original image.
107
+ Params:
108
+ imgs: image or list of images to detect faces on with BGR order (convert to RGB order for inference)
109
+ conf_thres: confidence threshold for each prediction
110
+ iou_thres: threshold for NMS (filter of intersecting bboxes)
111
+ Returns:
112
+ bboxes: list of arrays with 4 coordinates of bounding boxes with format x1,y1,x2,y2.
113
+ points: list of arrays with coordinates of 5 facial keypoints (eyes, nose, lips corners).
114
+ """
115
+ # Pass input images through face detector
116
+ images = imgs if isinstance(imgs, list) else [imgs]
117
+ images = [cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for img in images]
118
+ origimgs = copy.deepcopy(images)
119
+
120
+ images = self._preprocess(images)
121
+
122
+ if IS_HIGH_VERSION:
123
+ with torch.inference_mode(): # for pytorch>=1.9
124
+ pred = self.detector(images)[0]
125
+ else:
126
+ with torch.no_grad(): # for pytorch<1.9
127
+ pred = self.detector(images)[0]
128
+
129
+ bboxes, points = self._postprocess(images, origimgs, pred, conf_thres, iou_thres)
130
+
131
+ # return bboxes, points
132
+ if not isListempty(points):
133
+ bboxes = np.array(bboxes).reshape(-1,4)
134
+ points = np.array(points).reshape(-1,10)
135
+ padding = bboxes[:,0].reshape(-1,1)
136
+ return np.concatenate((bboxes, padding, points), axis=1)
137
+ else:
138
+ return None
139
+
140
+ def __call__(self, *args):
141
+ return self.predict(*args)