joshiayush commited on
Commit
64f11f5
·
1 Parent(s): 143c297

Refactor `Aligner` to support multiple cropped images at once

Browse files
face_detection/helper.py CHANGED
@@ -17,7 +17,7 @@ def get_crops(img,objs_found,aligner=None,resize:tuple=None):
17
 
18
  crop=img[ymin:ymax,xmin:xmax]
19
  if aligner is not None:
20
- crop=aligner.align_image(crop)
21
  if crop is None: continue
22
  if resize is not None:
23
  crop=square_maker(crop)
 
17
 
18
  crop=img[ymin:ymax,xmin:xmax]
19
  if aligner is not None:
20
+ crop=aligner.align((crop,))
21
  if crop is None: continue
22
  if resize is not None:
23
  crop=square_maker(crop)
face_recognition/aligner.py CHANGED
@@ -9,72 +9,105 @@ import shutil
9
 
10
 
11
 
12
- class aligner:
13
- def __init__(self,min_aligner_confidence=0.5):
14
- mp_face_mesh = mp.solutions.face_mesh
15
-
16
- self.face_mesh_images = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1,
17
- min_detection_confidence=min_aligner_confidence)
18
-
19
- mp_drawing = mp.solutions.drawing_utils
20
- mp_drawing_styles = mp.solutions.drawing_styles
21
- LEFT_EYE_INDEXES = list(set(itertools.chain(*mp_face_mesh.FACEMESH_LEFT_EYE)))
22
- RIGHT_EYE_INDEXES = list(set(itertools.chain(*mp_face_mesh.FACEMESH_RIGHT_EYE)))
23
-
24
- self.LEFT_EYE_INDEX=LEFT_EYE_INDEXES[7] # eye point index
25
- self.RIGHT_EYE_INDEX=RIGHT_EYE_INDEXES[4] # eye point index
26
-
27
- def align_image(self,img):
28
-
29
- # start work
30
- face_mesh_results = self.face_mesh_images.process(img)
31
- if face_mesh_results.multi_face_landmarks!=None:
32
- face_landmarks=face_mesh_results.multi_face_landmarks[0]
33
-
34
- h,w,_=img.shape
35
-
36
- points=[]
37
-
38
-
39
- x_coord=int(np.clip(face_landmarks.landmark[self.LEFT_EYE_INDEX].x*w,0,w))
40
- y_coord=int(np.clip(face_landmarks.landmark[self.LEFT_EYE_INDEX].y*h,0,h))
41
- points.append((x_coord,y_coord))
42
-
43
-
44
- x_coord=int(np.clip(face_landmarks.landmark[self.RIGHT_EYE_INDEX].x*w,0,w))
45
- y_coord=int(np.clip(face_landmarks.landmark[self.RIGHT_EYE_INDEX].y*h,0,h))
46
- points.append((x_coord,y_coord))
47
-
48
- p0=np.array(points[0],dtype='float64')
49
- p1=np.array(points[1],dtype='float64')
50
-
51
-
52
- h=abs(p0[1]-p1[1])
53
- w=abs(p0[0]-p1[0])
54
-
55
- theta=np.arctan(h/w)
56
-
57
- angle=(theta * 180) / np.pi
58
-
59
- def get_direction(p0,p1):
60
- if p0[0]<p1[0]:
61
- if p0[1]<p1[1]:
62
- direction=1
63
- else:
64
- direction=-1
65
- else:
66
- if p1[1]<p0[1]:
67
- direction=1
68
- else:
69
- direction=-1
70
- return direction
71
-
72
- direction=get_direction(p0,p1)
73
- angle=direction*angle
74
- # print("rotated anticlockwise by :",angle,"angle")
75
- new_img = PIL.Image.fromarray(img)
76
- new_img = new_img.rotate(angle)
77
-
78
- return np.array(new_img)
79
- else:
80
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
 
11
 
12
+ class Aligner(mp.solutions.face_mesh.FaceMesh):
13
+ """Inherits from MediaPipe Face Mesh."""
14
+
15
+ def __init__(
16
+ self,
17
+ static_image_mode: bool = True,
18
+ max_num_faces: int = 1,
19
+ refine_landmarks: bool = False,
20
+ min_detection_confidence: float = 0.5,
21
+ min_tracking_confidence: float = 0.5
22
+ ):
23
+ """Initializes a Image Aligner object.
24
+
25
+ Unlike MediaPipe Face Mesh we set `static_image_mode` to `True` as we only
26
+ require image manipulation.
27
+
28
+ Args:
29
+ static_image_mode: Whether to treat the input images as a batch of static
30
+ and possibly unrelated images, or a video stream.
31
+ max_num_faces: Maximum number of faces to detect.
32
+ refine_landmarks: Whether to further refine the landmark coordinates
33
+ around the eyes and lips, and output additional landmarks around the
34
+ irises. Default to False.
35
+ min_detection_confidence: Minimum confidence value ([0.0, 1.0]) for face
36
+ detection to be considered successful.
37
+ min_tracking_confidence: Minimum confidence value ([0.0, 1.0]) for the
38
+ face landmarks to be considered tracked successfully.
39
+ """
40
+ super().__init__(
41
+ static_image_mode=static_image_mode,
42
+ max_num_faces=max_num_faces,
43
+ refine_landmarks=refine_landmarks,
44
+ min_detection_confidence=min_detection_confidence,
45
+ min_tracking_confidence=min_tracking_confidence
46
+ )
47
+
48
+ self._left_eye_idx = list(
49
+ set(itertools.chain(*mp.solutions.face_mesh.FACEMESH_LEFT_EYE))
50
+ )[7]
51
+ self._right_eye_idx = list(
52
+ set(itertools.chain(*mp.solutions.face_mesh.FACEMESH_RIGHT_EYE))
53
+ )[4]
54
+
55
+ def _aligner(self, /, img: np.ndarray) -> np.ndarray:
56
+ """Private helper function to align the given image parallel to the x-axis.
57
+
58
+ This function creates a line between the left and right eye points and tries
59
+ to align that line parallel to the x-axis, thus aligning the complete image.
60
+
61
+ Args:
62
+ img: Image to align parallel to the x-axis.
63
+ """
64
+ fm = self.process(img)
65
+ if fm is None:
66
+ return None
67
+
68
+ points = []
69
+ h, w, _ = img.shape
70
+
71
+ face_landmarks = fm.multi_face_landmarks[0]
72
+
73
+ le_x_coord = int(
74
+ np.clip(face_landmarks.landmark[self._left_eye_idx].x * w, 0, w)
75
+ )
76
+ le_y_coord = int(
77
+ np.clip(face_landmarks.landmark[self._left_eye_idx].y * h, 0, h)
78
+ )
79
+ p0 = np.array((le_x_coord, le_y_coord), dtype=np.float64)
80
+
81
+ re_x_coord = int(
82
+ np.clip(face_landmarks.landmark[self._right_eye_idx].x * w, 0, w)
83
+ )
84
+ re_y_coord = int(
85
+ np.clip(face_landmarks.landmark[self._right_eye_idx].y * h, 0, h)
86
+ )
87
+ p1 = np.array((re_x_coord, re_y_coord), dtype=np.float64)
88
+
89
+ h = abs(p0[1] - p1[1])
90
+ w = abs(p0[0] - p1[0])
91
+
92
+ # Get the angle between the x-axis and the line joining the eye points.
93
+ theta = np.arctan(h / w)
94
+
95
+ angle = (theta * 180) / np.pi
96
+
97
+ if p0[0] < p1[0]:
98
+ direction = 1 if p0[1] < p1[1] else -1
99
+ else:
100
+ direction = 1 if p1[1] < p0[1] else -1
101
+
102
+ angle *= direction
103
+
104
+ img = PIL.fromarray(img)
105
+ return np.array(img.rotate(angle))
106
+
107
+ def align(self, /, imgs: tuple[np.ndarray]) -> list[np.ndarray]:
108
+ """Aligns the given set of images parallel to the x-axis on the image plane.
109
+
110
+ Args:
111
+ imgs: Images to align parallel to the x-axis on the image place.
112
+ """
113
+ return [self._aligner(img) for img in imgs]
face_recognition/inference.py CHANGED
@@ -139,7 +139,7 @@ class face_recognition:
139
 
140
  crop_img=img[ymin:ymax,xmin:xmax]
141
  crop_img=cv2.resize(crop_img,[self.model_config.input_size,self.model_config.input_size])
142
- crop_img=self.aligner.align_image(crop_img)
143
 
144
 
145
  if crop_img is not None:
 
139
 
140
  crop_img=img[ymin:ymax,xmin:xmax]
141
  crop_img=cv2.resize(crop_img,[self.model_config.input_size,self.model_config.input_size])
142
+ crop_img=self.aligner.align((crop_img,))
143
 
144
 
145
  if crop_img is not None: