-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
284 lines (239 loc) · 8.47 KB
/
Copy pathmain.py
File metadata and controls
284 lines (239 loc) · 8.47 KB
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
import os
import cv2
import sys
import json
import time
import flask
import mtcnn
import base64
import logging
import pyfiglet
import argparse
import warnings
import numpy as np
import tensorflow as tf
from PIL import Image
from utils import dbase
from utils import config
from flask import Flask, request
from flask_cors import CORS, cross_origin
from utils.imgProcessor import ImgProcessor
from utils.recognitionEngine import RecognitionEngine
# disable warnings
if not sys.warnoptions:
warnings.simplefilter("ignore")
# Create the application instance
app = Flask('FR APP')
app.config['CORS_HEADERS'] = 'Content-Type'
app.config['UPLOAD_EXTENSIONS'] = ['.jpg', '.png']
cors = CORS(app)
# initialize empty lists
ids, descriptors, persons_ids = [], [], []
def shutdown_server():
"""
shutdown_server
The actual function for turning off the Flask server.
"""
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
@app.route('/shutdown', methods=['POST'])
def shutdown() -> dict:
"""
shutdown
The endpoint for turning off the Flask server.
:return: dict
"""
shutdown_server()
return {'status': 'Server shut down.'}
# Create a URL route in our application for "/@app.route('/healtcheck')"
@app.route('/healtcheck')
def healtcheck() -> dict:
"""
healtcheck
The function for performing the healtcheck of the whole system.
:return: dict
"""
return {
'DB_status': 'Active' if db is not None else 'Inactive',
'Flask_version': flask.__version__,
'Opencv_version': cv2.__version__,
'Tensorflow_version': tf.__version__,
'GPU_available': 'Yes' if gpus else 'No'
}
'''
# Create a URL route in our application for "/@app.route('/detect')"
# The purpose of this endpoint is to extract faces from the providied imagage
@app.route('/detect', methods=["POST"])
def detect() -> dict:
"""
detect
The function for performing detection based on configuration parameters.
Crops all faces from the image and returns the list containing all of them in numpy array format.
:param img: numpy.array()
:return: dict
"""
pil_image = Image.open(request.files['image']).convert('RGB')
img = np.array(pil_image)
faces = imgProcessor.detect(img)
faces_b64 = []
for face in faces:
# convert image to bytes (base64 encoding)
faces_b64.append(str(base64.b64encode(face)))
return {
'Status': 'SUCCESS',
# Serialization
'faces': json.dumps(faces_b64)
}
# Create a URL route in our application for "/@app.route('/isalive')"
# The purpose of this endpoint is to classify if the provided face is real or fake
@app.route('/isalive', methods=["POST"])
def is_alive() -> dict:
"""
isAlive
Performs the prediction if the face is real or fake.
:return: dict
"""
pil_image = Image.open(request.files['image']).convert('RGB')
img = np.array(pil_image)
return imgProcessor.is_alive(img)
'''
# Create a URL route in our application for "/@app.route('/isalive')"
# The purpose of this endpoint is to classify if the provided face is real or fake
@app.route('/identification', methods=["POST"])
@cross_origin()
def predict_rest() -> dict:
"""
predict_rest
The actual function for performing the recognition.
Extract all faces from the image, check if faces are real,
extract face descriptors and search the database using HNSW
and angular distance metric.
:return: dict
"""
pil_image = Image.open(request.files['image'])
img = np.float32(pil_image)
# check image size (important for gpus with less vram)
if img.shape[0]*img.shape[1] > 1920*1080:
return{
'status': 'ERROR',
'response': 'Inappropriate image size (use smaller images).'
}
# detect faces
faces = imgProcessor.detect(img)
response = []
global ids, descriptors, persons_ids
start = time.time()
if persons_ids:
for face in faces:
# encode face
descriptor = imgProcessor.encode(face)
# find personid in the database
person_id = recEngine.identification(descriptor, persons_ids)
if person_id['personid'] != 'Not recognized':
# find name in the database
person = dbase.find_person_by_id(db, person_id['personid'])
response.append(person)
else:
response.append(person_id)
else:
return {
'status': 'ERROR',
'response': 'Empty database'
}
logging.info('Identification time: ' + str(time.time()-start))
return {
'status': 'SUCCESS',
'response': response
}
# Create a URL route in our application for "/@app.route('/encode')"
# The purpose of this endpoint is to encode the face
@app.route('/encodeAndInsert', methods=["POST"])
def encode_and_insert() -> dict:
"""
encodeAndInsert
The actual function for adding a new person into database.
After person is added successfully, make base is performed.
:return: dict
"""
name = request.form.get('name')
uploaded_files = request.files.getlist("images")
embeds = []
if not uploaded_files:
return {"status": "ERROR"}
for image in uploaded_files:
pil_image = Image.open(image)
img = np.float32(pil_image)
# check image size (important for gpus with less vram)
if img.shape[0] * img.shape[1] > 1920 * 1080:
return {
'status': 'ERROR',
'response': 'Inappropriate image size (use smaller images).'
}
# detect face
faces = imgProcessor.detect(img)
if len(faces) != 1:
return {
'status': 'ERROR',
'response': 'Images must contain only one face. Please try again.'
}
embeds.append(imgProcessor.encode(np.array(faces[0])))
_ = dbase.receive_descriptors(db, db_conn, name, embeds)
global ids, descriptors, persons_ids
ids, descriptors, persons_ids = dbase.read_descriptors(db)
return recEngine.make_base(descriptors)
if __name__ == '__main__':
# remove old log file
os.remove("FRAPP.log")
# set environment variables
os.environ['FLASK_ENV'] = 'development'
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
# turn off tensorflow logger
logger = tf.get_logger()
logger.disabled = True
logger.setLevel(logging.FATAL)
# open file for logging
logging.basicConfig(filename='FRAPP.log', level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s')
# allow GPU memory grow
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
try:
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
except RuntimeError as e:
logging.info(str(e))
print(e)
logging.info('Allow GPU memory grow successful.')
# parse arguments
parser = argparse.ArgumentParser(description='Process some arguments.')
parser.add_argument('--cdp', type=str, help='the path to config file')
logging.info('Parsing arguments successful.')
args = parser.parse_args()
config_path = args.cdp
cfg = config.readConfig(config_path)
db_conn, db = dbase.db_connect(cfg["host"], cfg["port"], cfg["name"], cfg["user"], cfg["password"])
# initialize image processor (used for detection, anti-spoofing and vector extraction)
imgProcessor = ImgProcessor(cfg)
# initialize recognition engine
recEngine = RecognitionEngine(cfg['threshold'])
logging.info('All models initialized successfully.')
try:
# read ids, descriptors and person_ids from database
ids, descriptors, persons_ids = dbase.read_descriptors(db)
logging.info('Read descriptors successful.')
recEngine.make_base(np.array(descriptors))
logging.info('Make base successful.')
except:
logging.info('The database is empty.')
# Run the flask rest api
# This can be updated to use multiple threads or processors
# In addition, some type of queue should be used
# print starting text
ascii_banner = pyfiglet.figlet_format("F R A P P", font="slant")
print(ascii_banner)
logging.info('FR APP IS RUNNING.')
logging.info('---------------' * 4)
# threaded=False, processes=3
app.run(debug=True, host='127.0.0.1', port=5000)