Skip to content

Commit 3a38aab

Browse files
authored
Merge pull request #85 from serengil/feat-task-2502-align-first-by-default
align first by default
2 parents 57a0d91 + 7908b50 commit 3a38aab

File tree

4 files changed

+45
-47
lines changed

4 files changed

+45
-47
lines changed

.github/pull_request_template.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Tickets
2+
3+
https://github.com/serengil/retinaface/issues/XXX
4+
5+
### What has been done
6+
7+
With this PR, ...
8+
9+
## How to test
10+
11+
```shell
12+
make lint && make test
13+
```

retinaface/RetinaFace.py

+22-20
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ def extract_faces(
212212
align: bool = True,
213213
allow_upscaling: bool = True,
214214
expand_face_area: int = 0,
215-
align_first: bool = False,
216215
) -> list:
217216
"""
218217
Extract detected and aligned faces
@@ -223,8 +222,6 @@ def extract_faces(
223222
align (bool): enable or disable alignment
224223
allow_upscaling (bool): allowing up-scaling
225224
expand_face_area (int): expand detected facial area with a percentage
226-
align_first (bool): set this True to align first and detect second
227-
this can be applied only if input image has just one face
228225
"""
229226
resp = []
230227

@@ -238,12 +235,6 @@ def extract_faces(
238235
img_path=img, threshold=threshold, model=model, allow_upscaling=allow_upscaling
239236
)
240237

241-
if align_first is True and len(obj) > 1:
242-
logger.warn(
243-
f"Even though align_first is set to True, there are {len(obj)} faces in input image."
244-
"Align first functionality can be applied only if there is single face in the input"
245-
)
246-
247238
if not isinstance(obj, dict):
248239
return resp
249240

@@ -263,10 +254,7 @@ def extract_faces(
263254
x2 = min(img.shape[1], w + int((w * expand_face_area) / 100)) # expand right
264255
y2 = min(img.shape[0], h + int((h * expand_face_area) / 100)) # expand bottom
265256

266-
if align_first is False or (align_first is True and len(obj) > 1):
267-
facial_img = img[y1:y2, x1:x2]
268-
else:
269-
facial_img = img.copy()
257+
facial_img = img[y1:y2, x1:x2]
270258

271259
if align is True:
272260
landmarks = identity["landmarks"]
@@ -275,19 +263,33 @@ def extract_faces(
275263
nose = landmarks["nose"]
276264
# mouth_right = landmarks["mouth_right"]
277265
# mouth_left = landmarks["mouth_left"]
266+
267+
# notice that left eye of one is seen on the right from your perspective
278268
facial_img, rotate_angle, rotate_direction = postprocess.alignment_procedure(
279-
facial_img, right_eye, left_eye, nose
269+
img=img, left_eye=right_eye, right_eye=left_eye, nose=nose
280270
)
281271

282-
if align_first is True and len(obj) == 1:
283-
facial_area = postprocess.rotate_facial_area(
272+
# find new facial area coordinates after alignment
273+
projected_facial_area = postprocess.rotate_facial_area(
284274
facial_area, rotate_angle, rotate_direction, img.shape
285275
)
286276
# Expand the facial area to be extracted and stay within img.shape limits
287-
x1 = max(0, facial_area[0] - int((facial_area[2] * expand_face_area) / 100))
288-
y1 = max(0, facial_area[1] - int((facial_area[3] * expand_face_area) / 100))
289-
x2 = min(img.shape[1], facial_area[2] + int((facial_area[2] * expand_face_area) / 100))
290-
y2 = min(img.shape[0], facial_area[3] + int((facial_area[3] * expand_face_area) / 100))
277+
x1 = max(
278+
0,
279+
projected_facial_area[0] - int((projected_facial_area[2] * expand_face_area) / 100),
280+
)
281+
y1 = max(
282+
0,
283+
projected_facial_area[1] - int((projected_facial_area[3] * expand_face_area) / 100),
284+
)
285+
x2 = min(
286+
img.shape[1],
287+
projected_facial_area[2] + int((projected_facial_area[2] * expand_face_area) / 100),
288+
)
289+
y2 = min(
290+
img.shape[0],
291+
projected_facial_area[3] + int((projected_facial_area[3] * expand_face_area) / 100),
292+
)
291293
facial_img = facial_img[y1:y2, x1:x2]
292294

293295
resp.append(facial_img[:, :, ::-1])

tests/test_actions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_alignment_for_clock_way():
7575

7676

7777
def do_alignment_checks(img: np.ndarray, expected_faces: int) -> None:
78-
faces = RetinaFace.extract_faces(img_path=img, align=True, expand_face_area=100)
78+
faces = RetinaFace.extract_faces(img_path=img, align=True, expand_face_area=10)
7979

8080
# it has one clear face
8181
assert len(faces) == expected_faces

tests/test_align_first.py

+9-26
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,15 @@
66

77
THRESHOLD = 1000
88

9-
10-
def test_detect_first():
11-
"""
12-
Test the default behavior. Detect first and align second causes
13-
so many black pixels
14-
"""
15-
faces = RetinaFace.extract_faces(img_path="tests/dataset/img11.jpg")
16-
num_black_pixels = np.sum(np.all(faces[0] == 0, axis=2))
17-
assert num_black_pixels > THRESHOLD
18-
logger.info("✅ Disabled align_first test for single face photo done")
9+
VISUAL_TEST = False
1910

2011

2112
def test_align_first():
2213
"""
2314
Test align first behavior. Align first and detect second do not cause
2415
so many black pixels in contrast to default behavior
2516
"""
26-
faces = RetinaFace.extract_faces(img_path="tests/dataset/img11.jpg", align_first=True)
17+
faces = RetinaFace.extract_faces(img_path="tests/dataset/img11.jpg")
2718
num_black_pixels = np.sum(np.all(faces[0] == 0, axis=2))
2819
assert num_black_pixels < THRESHOLD
2920
logger.info("✅ Enabled align_first test for single face photo done")
@@ -34,22 +25,14 @@ def test_align_first_for_group_photo():
3425
Align first will not work if the given image has many faces and
3526
it will cause so many black pixels
3627
"""
37-
faces = RetinaFace.extract_faces(img_path="tests/dataset/couple.jpg", align_first=True)
38-
for face in faces:
39-
num_black_pixels = np.sum(np.all(face == 0, axis=2))
40-
assert num_black_pixels > THRESHOLD
41-
42-
logger.info("✅ Enabled align_first test for group photo done")
43-
44-
45-
def test_default_behavior_for_group_photo():
46-
"""
47-
Align first will not work in the default behaviour and
48-
it will cause so many black pixels
49-
"""
5028
faces = RetinaFace.extract_faces(img_path="tests/dataset/couple.jpg")
5129
for face in faces:
5230
num_black_pixels = np.sum(np.all(face == 0, axis=2))
53-
assert num_black_pixels > THRESHOLD
31+
assert num_black_pixels < THRESHOLD
32+
if VISUAL_TEST is True:
33+
import matplotlib.pyplot as plt
5434

55-
logger.info("✅ Disabled align_first test for group photo done")
35+
plt.imshow(face)
36+
plt.show()
37+
38+
logger.info("✅ Enabled align_first test for group photo done")

0 commit comments

Comments
 (0)