-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathfaces.py
147 lines (123 loc) · 5.45 KB
/
faces.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env python
#
# This file is part of the Emotions project. The complete source code is
# available at https://github.com/luigivieira/emotions.
#
# Copyright (c) 2016-2017, Luiz Carlos Vieira (http://www.luiz.vieira.nom.br)
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import numpy as np
import dlib
import cv2
from data import FaceData
#=============================================
class FaceDetector:
"""
Implements the detector of faces (with their landmarks) in images.
"""
_detector = None
"""
Instance of the dlib's object used to detect faces in images, shared by all
instances of this class.
"""
_predictor = None
"""
Instance of the dlib's object used to predict the positions of facial
landmarks in images, shared by all instances of this class.
"""
#---------------------------------------------
def detect(self, image, downSampleRatio = None):
"""
Tries to automatically detect a face in the given image.
This method uses the face detector/predictor from the dlib package (with
its default face model) to detect a face region and 68 facial landmarks.
Even though dlib is able to detect more than one face in the image, for
the current purposes of the fsdk project only a single face is needed.
Hence, only the biggest face detected (estimated from the region size)
is considered.
Parameters
------
image: numpy.array
Image data where to search for the face.
downSampleRatio: float
Returns
------
result: bool
Indication on the success or failure of the facial detection.
face: FaceData
Instance of the FaceData class with the region and landmarks of the
detected face, or None if no face was detected.
"""
#####################
# Setup the detector
#####################
# Initialize the static detector and predictor if this is first use
if FaceDetector._detector is None or FaceDetector._predictor is None:
FaceDetector._detector = dlib.get_frontal_face_detector()
faceModel = os.path.abspath('{}/models/face_model.dat' \
.format(os.path.dirname(__file__)))
FaceDetector._predictor = dlib.shape_predictor(faceModel)
#####################
# Performance cues
#####################
# If requested, scale down the original image in order to improve
# performance in the initial face detection
if downSampleRatio is not None:
detImage = cv2.resize(image, (0, 0), fx=1.0 / downSampleRatio,
fy=1.0 / downSampleRatio)
else:
detImage = image
#####################
# Face detection
#####################
# Detect faces in the image
detectedFaces = FaceDetector._detector(detImage, 1)
if len(detectedFaces) == 0:
return False, None
# No matter how many faces have been found, consider only the first one
region = detectedFaces[0]
# If downscaling was requested, scale back the detected region so the
# landmarks can be proper located on the image in full resolution
if downSampleRatio is not None:
region = dlib.rectangle(region.left() * downSampleRatio,
region.top() * downSampleRatio,
region.right() * downSampleRatio,
region.bottom() * downSampleRatio)
# Fit the shape model over the face region to predict the positions of
# its facial landmarks
faceShape = FaceDetector._predictor(image, region)
#####################
# Return data
#####################
face = FaceData()
# Update the object data with the predicted landmark positions and
# their bounding box (with a small margin of 10 pixels)
face.landmarks = np.array([[p.x, p.y] for p in faceShape.parts()])
margin = 10
x, y, w, h = cv2.boundingRect(face.landmarks)
face.region = (
max(x - margin, 0),
max(y - margin, 0),
min(x + w + margin, image.shape[1] - 1),
min(y + h + margin, image.shape[0] - 1)
)
return True, face