Skip to content

Commit b10c119

Browse files
authored
Make sure detected face locations don't cross outside image bounds (ageitgey#15)
1 parent 3976fae commit b10c119

File tree

4 files changed

+27
-2
lines changed

4 files changed

+27
-2
lines changed

face_recognition/api.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
print("pip install git+https://github.com/ageitgey/face_recognition_models")
1313
quit()
1414

15+
face_detector = dlib.get_frontal_face_detector()
16+
1517
predictor_model = face_recognition_models.pose_predictor_model_location()
1618
pose_predictor = dlib.shape_predictor(predictor_model)
1719

@@ -39,6 +41,17 @@ def _css_to_rect(css):
3941
return dlib.rectangle(css[3], css[0], css[1], css[2])
4042

4143

44+
def _trim_css_to_bounds(css, image_shape):
45+
"""
46+
Make sure a tuple in (top, right, bottom, left) order is within the bounds of the image.
47+
48+
:param css: plain tuple representation of the rect in (top, right, bottom, left) order
49+
:param image_shape: numpy shape of the image array
50+
:return: a trimmed plain tuple representation of the rect in (top, right, bottom, left) order
51+
"""
52+
return max(css[0], 0), min(css[1], image_shape[1]), min(css[2], image_shape[0]), max(css[3], 0)
53+
54+
4255
def _face_distance(faces, face_to_compare):
4356
"""
4457
Given a list of face encodings, compared them to a known face encoding and get a euclidean distance
@@ -70,7 +83,6 @@ def _raw_face_locations(img, number_of_times_to_upsample=1):
7083
:param number_of_times_to_upsample: How many times to upsample the image looking for faces. Higher numbers find smaller faces.
7184
:return: A list of dlib 'rect' objects of found face locations
7285
"""
73-
face_detector = dlib.get_frontal_face_detector()
7486
return face_detector(img, number_of_times_to_upsample)
7587

7688

@@ -82,7 +94,7 @@ def face_locations(img, number_of_times_to_upsample=1):
8294
:param number_of_times_to_upsample: How many times to upsample the image looking for faces. Higher numbers find smaller faces.
8395
:return: A list of tuples of found face locations in css (top, right, bottom, left) order
8496
"""
85-
return [_rect_to_css(face) for face in _raw_face_locations(img, number_of_times_to_upsample)]
97+
return [_trim_css_to_bounds(_rect_to_css(face), img.shape) for face in _raw_face_locations(img, number_of_times_to_upsample)]
8698

8799

88100
def _raw_face_landmarks(face_image, face_locations=None):

tests/test_face_recognition.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ def test_face_locations(self):
5050
self.assertEqual(len(detected_faces), 1)
5151
self.assertEqual(detected_faces[0], (142, 617, 409, 349))
5252

53+
def test_partial_face_locations(self):
54+
img = api.load_image_file(os.path.join(os.path.dirname(__file__), 'test_images', 'obama_partial_face.jpg'))
55+
detected_faces = api.face_locations(img)
56+
57+
self.assertEqual(len(detected_faces), 1)
58+
self.assertEqual(detected_faces[0], (142, 191, 365, 0))
59+
60+
img = api.load_image_file(os.path.join(os.path.dirname(__file__), 'test_images', 'obama_partial_face2.jpg'))
61+
detected_faces = api.face_locations(img)
62+
63+
self.assertEqual(len(detected_faces), 1)
64+
self.assertEqual(detected_faces[0], (142, 551, 409, 349))
65+
5366
def test_raw_face_landmarks(self):
5467
img = api.load_image_file(os.path.join(os.path.dirname(__file__), 'test_images', 'obama.jpg'))
5568
face_landmarks = api._raw_face_landmarks(img)
413 KB
Loading
467 KB
Loading

0 commit comments

Comments
 (0)