Skip to content

Commit 615a0ca

Browse files
authored
Merge pull request #113 from serengil/feat-task-1011-target-size
target size arg added into extract faces
2 parents 24f57ec + 3ae87e7 commit 615a0ca

File tree

3 files changed

+84
-4
lines changed

3 files changed

+84
-4
lines changed

retinaface/RetinaFace.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
import warnings
33
import logging
4-
from typing import Union, Any, Optional, Dict
4+
from typing import Union, Any, Optional, Dict, Tuple, List
55

66
# this has to be set before importing tf
77
os.environ["TF_USE_LEGACY_KERAS"] = "1"
@@ -55,6 +55,7 @@ def build_model() -> Any:
5555
input_signature=(tf.TensorSpec(shape=[None, None, None, 3], dtype=np.float32),),
5656
)
5757

58+
# pylint: disable=possibly-used-before-assignment
5859
return model
5960

6061

@@ -220,7 +221,9 @@ def extract_faces(
220221
align: bool = True,
221222
allow_upscaling: bool = True,
222223
expand_face_area: int = 0,
223-
) -> list:
224+
target_size: Optional[Tuple[int, int]] = None,
225+
min_max_norm: bool = True,
226+
) -> List[np.ndarray]:
224227
"""
225228
Extract detected and aligned faces
226229
Args:
@@ -230,6 +233,13 @@ def extract_faces(
230233
align (bool): enable or disable alignment
231234
allow_upscaling (bool): allowing up-scaling
232235
expand_face_area (int): expand detected facial area with a percentage
236+
target_size (optional tuple): resize the image by padding it with black pixels
237+
to fit the specified dimensions. default is None
238+
min_max_norm (bool): set this to True if you want to normalize image in [0, 1].
239+
this is only running when target_size is not none.
240+
for instance, matplotlib expects inputs in this scale. (default is True)
241+
Returns:
242+
result (List[np.ndarray]): list of extracted faces
233243
"""
234244
resp = []
235245

@@ -289,6 +299,14 @@ def extract_faces(
289299
int(rotated_y1) : int(rotated_y2), int(rotated_x1) : int(rotated_x2)
290300
]
291301

292-
resp.append(facial_img[:, :, ::-1])
302+
if target_size is not None:
303+
facial_img = postprocess.resize_image(
304+
img=facial_img, target_size=target_size, min_max_norm=min_max_norm
305+
)
306+
307+
# to rgb
308+
facial_img = facial_img[:, :, ::-1]
309+
310+
resp.append(facial_img)
293311

294312
return resp

retinaface/commons/postprocess.py

+53-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
# built-in dependencies
12
import math
23
from typing import Union, Tuple
4+
5+
# 3rd party dependencies
36
import numpy as np
47
from PIL import Image
5-
8+
import cv2
69

710
# pylint: disable=unused-argument
811

@@ -143,6 +146,55 @@ def rotate_facial_area(
143146
return (x1, y1, x2, y2)
144147

145148

149+
def resize_image(
150+
img: np.ndarray, target_size: Tuple[int, int], min_max_norm: bool = True
151+
) -> np.ndarray:
152+
"""
153+
Resize an image to expected size of a ml model with adding black pixels.
154+
Ref: github.com/serengil/deepface/blob/master/deepface/modules/preprocessing.py
155+
Args:
156+
img (np.ndarray): pre-loaded image as numpy array
157+
target_size (tuple): input shape of ml model
158+
min_max_norm (bool): set this to True if you want to normalize image in [0, 1].
159+
this is only running when target_size is not none.
160+
for instance, matplotlib expects inputs in this scale. (default is True)
161+
Returns:
162+
img (np.ndarray): resized input image
163+
"""
164+
factor_0 = target_size[0] / img.shape[0]
165+
factor_1 = target_size[1] / img.shape[1]
166+
factor = min(factor_0, factor_1)
167+
168+
dsize = (
169+
int(img.shape[1] * factor),
170+
int(img.shape[0] * factor),
171+
)
172+
img = cv2.resize(img, dsize)
173+
174+
diff_0 = target_size[0] - img.shape[0]
175+
diff_1 = target_size[1] - img.shape[1]
176+
177+
# Put the base image in the middle of the padded image
178+
img = np.pad(
179+
img,
180+
(
181+
(diff_0 // 2, diff_0 - diff_0 // 2),
182+
(diff_1 // 2, diff_1 - diff_1 // 2),
183+
(0, 0),
184+
),
185+
"constant",
186+
)
187+
188+
# double check: if target image is not still the same size with target.
189+
if img.shape[0:2] != target_size:
190+
img = cv2.resize(img, target_size)
191+
192+
if min_max_norm is True and img.max() > 1:
193+
img = (img.astype(np.float32) / 255.0).astype(np.float32)
194+
195+
return img
196+
197+
146198
def bbox_pred(boxes, box_deltas):
147199
"""
148200
This function is copied from the following code snippet:

tests/test_actions.py

+10
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,13 @@ def test_different_expanding_ratios():
107107
plt.imshow(face)
108108
plt.axis("off")
109109
plt.show()
110+
111+
112+
def test_resize():
113+
faces = RetinaFace.extract_faces(img_path="tests/dataset/img11.jpg", target_size=(224, 224))
114+
for face in faces:
115+
assert face.shape == (224, 224, 3)
116+
if do_plotting is True:
117+
plt.imshow(face)
118+
plt.show()
119+
logger.info("✅ resize test done")

0 commit comments

Comments
 (0)