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

Refactor `Aligner` to support multiple cropped images at once

Browse files
app/__init__.py CHANGED
@@ -4,19 +4,19 @@ from datetime import timedelta
4
  from face_detection import inference as fd
5
  from face_detection.helper import get_crops as fd_get_crops
6
  from face_recognition import inference as fr
7
- from face_recognition.aligner import aligner
8
  from face_recognition import helper as fr_helper
9
 
10
 
11
 
12
- # face_detector=fd.face_detection("face_detection/Models/v1")
13
  # face_detector=fd.face_detection("face_detection/Models/mobilenet")
14
- face_detector=fd.face_detection("face_detection/Models/BestMap")
15
  face_detector.square_preprocessing=fd.square_pad()
16
  # face_recognizer=fr.face_recognition("face_recognition/Models/v1")
17
  # face_recognizer=fr.face_recognition("face_recognition/Models/mobilenet_basic_lfw")
18
  face_recognizer=fr.face_recognition("face_recognition/Models/keras_mobilenet_emore_adamw")
19
- aligner_obj=aligner()
20
 
21
  # image_size=544
22
  # p_thres=0.7
 
4
  from face_detection import inference as fd
5
  from face_detection.helper import get_crops as fd_get_crops
6
  from face_recognition import inference as fr
7
+ from face_recognition.aligner import Aligner
8
  from face_recognition import helper as fr_helper
9
 
10
 
11
 
12
+ face_detector=fd.face_detection("face_detection/Models/v1")
13
  # face_detector=fd.face_detection("face_detection/Models/mobilenet")
14
+ # face_detector=fd.face_detection("face_detection/Models/BestMap")
15
  face_detector.square_preprocessing=fd.square_pad()
16
  # face_recognizer=fr.face_recognition("face_recognition/Models/v1")
17
  # face_recognizer=fr.face_recognition("face_recognition/Models/mobilenet_basic_lfw")
18
  face_recognizer=fr.face_recognition("face_recognition/Models/keras_mobilenet_emore_adamw")
19
+ aligner_obj=Aligner()
20
 
21
  # image_size=544
22
  # p_thres=0.7
app/demo/routes.py CHANGED
@@ -42,7 +42,7 @@ def load_settings(func):
42
  # we will set image_size inside routes
43
 
44
  # set face aligner settings
45
- aligner_obj.face_mesh_images.min_detection_confidence=session["demo"]['settings']['a_thres']
46
 
47
  # set face recognizer settings
48
  face_recognizer.thres=session["demo"]['settings']['d_thres']
 
42
  # we will set image_size inside routes
43
 
44
  # set face aligner settings
45
+ aligner_obj.min_detection_confidence=session["demo"]['settings']['a_thres']
46
 
47
  # set face recognizer settings
48
  face_recognizer.thres=session["demo"]['settings']['d_thres']
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,))[0]
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.Image.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
@@ -14,7 +14,7 @@ import importlib
14
 
15
 
16
  from face_recognition import config
17
- from face_recognition.aligner import aligner
18
 
19
 
20
 
@@ -25,7 +25,7 @@ class face_recognition:
25
  self.model_config= importlib.import_module(config_file_path)
26
  # print(self.model_config)
27
  self.thres=thres if thres is not None else self.model_config.d_thres
28
- self.aligner=aligner(min_aligner_confidence) if min_aligner_confidence is not None else aligner(config.min_aligner_confidence)
29
  self.feature_extractor=tf.keras.models.load_model(model_path+"/model.h5",compile=False)
30
 
31
 
@@ -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:
 
14
 
15
 
16
  from face_recognition import config
17
+ from face_recognition.aligner import Aligner
18
 
19
 
20
 
 
25
  self.model_config= importlib.import_module(config_file_path)
26
  # print(self.model_config)
27
  self.thres=thres if thres is not None else self.model_config.d_thres
28
+ self.aligner=Aligner(min_aligner_confidence) if min_aligner_confidence is not None else Aligner(config.min_aligner_confidence)
29
  self.feature_extractor=tf.keras.models.load_model(model_path+"/model.h5",compile=False)
30
 
31
 
 
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: