-
Notifications
You must be signed in to change notification settings - Fork 5
/
server.py
262 lines (206 loc) · 9.12 KB
/
server.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
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
import datetime
import os
import json
import time
from threading import Thread
from flask import Flask, request, jsonify, Response, send_from_directory
from flask_cors import CORS
from flask_restx import Api, Resource
from werkzeug.middleware.proxy_fix import ProxyFix
from src import *
from src.classifier import FacerecClassifier
from src.connectors import antract_connector as antract
from src.utils import utils, uri_utils
from bson.json_util import dumps
TRAINING_IMG = 'data/training_img_aligned/'
IMG_DIR = os.path.join(os.getcwd(), TRAINING_IMG)
VIDEO_DIR = os.path.join(os.getcwd(), 'video')
os.makedirs('database', exist_ok=True)
database.init()
flask_app = Flask(__name__)
flask_app.wsgi_app = ProxyFix(flask_app.wsgi_app, x_proto=1, x_port=1, x_for=1, x_host=1, x_prefix=1)
api = Api(app=flask_app,
version="0.1.0",
default='facerec',
title="Face Recognition Api",
description="Recognise celebrities on videos.", )
CORS(flask_app)
PROJECTS = [p for p in os.listdir(TRAINING_IMG) if os.path.isdir(os.path.join(TRAINING_IMG, p))]
project_param = {'description': 'The project context of the call', 'enum': PROJECTS, 'required': True}
def now():
return datetime.datetime.now().isoformat()
@api.route('/projects')
@api.doc(description="Get list of active projects.")
class Projects(Resource):
def get(self):
return jsonify(PROJECTS)
@api.route('/training-set')
@api.doc(description="Get list of training images with classes.",
params={'project': project_param})
class TrainingSet(Resource):
def get(self):
dataset = request.args.get('project', 'general')
folder = os.path.join(TRAINING_IMG, dataset)
labels, paths = utils.fetch_dataset(folder)
results = {}
for path, c in zip(paths, labels):
path = path.replace(TRAINING_IMG, 'training_img_aligned/')
if c not in results:
results[c] = {
'class': c,
'path': [path]
}
else:
results[c]['path'].append(path)
return jsonify(list(results.values()))
# http://127.0.0.1:5000/crawler?project=antract&q=Charles De Gaulle;Vincent Auriol;Pierre Mendès France;Georges Bidault;Guy Mollet;François Mitterrand;Georges Pompidou;Elisabeth II;Konrad Adenauer;Dwight Eisenhower;Nikita Khrouchtchev;Viatcheslav Molotov;Ahmed Ben Bella
# http://127.0.0.1:5000/crawler?project=memad&q=Annastiina Heikkilä;Frans Timmermans;Manfred Weber;Markus Preiss;Ska Keller;Emilie Tran Nguyen;Jan Zahradil;Margrethe Vestager;Nico Cué;Laura Huhtasaari;Asseri Kinnunen
@api.route('/crawler')
@api.doc(
description="Search faces of people in the web to be added to the dataset.",
params={
'q': {
'required': True,
'description': 'The name of the person, or multiple individuals separated by a semicolon, '
'like in "Tom Hanks;Monica Bellucci"'},
'project': project_param
})
class Crawler(Resource):
def get(self):
start_time = time.time()
q = request.args.get('q')
if q is None:
raise ValueError('Missing required parameter: q')
project = request.args.get('project', default='general')
for keyword in q.split(';'):
crawler.main(keyword, max_num=50, project=project)
return jsonify({
'task': 'crawl',
'time': now(),
'execution_time': (time.time() - start_time),
'status': 'ok'
})
@api.route('/train/<string:project>')
@api.doc(description="Trigger the training of the model")
class Training(Resource):
def get(self, project):
start_time = time.time()
classifier.main(classifier='SVM', project=project, discard_disabled="true")
return jsonify({
'task': 'train',
'time': now(),
'execution_time': (time.time() - start_time),
'status': 'ok'
})
# http://127.0.0.1:5000/track?speedup=25&video=video/yle_a-studio_8a3a9588e0f58e1e40bfd30198274cb0ce27984e.mp4
# http://127.0.0.1:5000/track?format=ttl&video=http://data.memad.eu/yle/a-studio/8a3a9588e0f58e1e40bfd30198274cb0ce27984e
@api.route('/track')
@api.doc(description="Extract from the video all the continuous positions of the people in the dataset",
params={
'video': {'required': True, 'description': 'URI of the video to be analysed'},
'project': project_param,
'speedup': {'default': 25, 'type': int,
'description': 'Number of frame to wait between two iterations of the algorithm'},
'no_cache': {'type': bool, 'default': False,
'description': 'Set it if you want to recompute the annotations'},
'format': {'default': 'json', 'enum': ['json', 'ttl'], 'description': 'Set the output format'},
})
class Track(Resource):
def get(self):
video_id = request.args.get('video').strip()
project = request.args.get('project').strip()
speedup = request.args.get('speedup', type=int, default=25)
no_cache = 'no_cache' in request.args.to_dict() and request.args.get('no_cache') != 'false'
video = None
locator = video_id
if not no_cache:
video = database.get_all_about(video_id, project)
if video:
locator = video['locator']
need_run = not video or 'tracks' not in video and video.get('status') != 'RUNNING'
if not video or need_run:
if video_id.startswith('http'): # it is a uri!
locator, video = uri_utils.uri2video(video_id)
video_id = video['locator']
elif not os.path.isfile(video_id):
raise FileNotFoundError('video not found: %s' % video_id)
else:
video = {'locator': video_id}
database.save_metadata(video)
if need_run:
database.clean_analysis(video_id, project)
database.save_status(video_id, project, 'RUNNING')
video['status'] = 'RUNNING'
Thread(target=run_tracker, args=(locator, speedup, video_id, project)).start()
elif 'tracks' in video and len(video['tracks']) > 0:
raw_tracks = clusterize.from_dict(video['tracks'])
video['tracks'] = clusterize.main(raw_tracks, confidence_threshold=0, merge_cluster=True)
assigned_tracks = [t['merged_tracks'] for t in video['tracks']]
if 'feat_clusters' in video:
video['feat_clusters'] = clusterize.unknown_clusterise(video['feat_clusters'], assigned_tracks,
raw_tracks)
if '_id' in video:
del video['_id'] # the database id should not appear on the output
fmt = request.args.get('format')
if fmt == 'ttl':
return Response(semantifier.semantify(video), mimetype='text/turtle')
return jsonify(video)
def run_tracker(video_path, speedup, video, project):
try:
return tracker.main(video_path, project=project, video_speedup=speedup, export_frames=True, video_id=video)
except RuntimeError:
database.save_status(video, project, 'ERROR')
@flask_app.route('/get_locator')
def send_video():
path = request.args.get('video')
if path.startswith('http'):
video_path, info = uri_utils.uri2video(path)
return video_path
else:
return send_from_directory(VIDEO_DIR, path, as_attachment=True)
@flask_app.route('/get_metadata')
def get_metadata():
path = request.args.get('video')
if path.startswith('http://www.ina.fr/'):
return jsonify(antract.get_metadata_for(path)[0])
else:
return None
@flask_app.route('/appearance/<string:person>')
def get_appearances(person):
project = request.args.get('project')
return jsonify(json.loads(dumps(database.get_video_with(person, project))))
@flask_app.route('/training_img_aligned/<path:subpath>')
def send_img(subpath=None):
dirname = os.path.dirname(subpath)
filename = os.path.basename(subpath)
return send_from_directory(os.path.join(IMG_DIR, dirname), filename, as_attachment=True)
@api.route('/disabled/<string:project>')
class Disabled(Resource):
def get(self, project):
DISABLED_FILE = os.path.join(TRAINING_IMG, project, 'disabled.txt')
if not os.path.isfile(DISABLED_FILE):
# automatic disable
return jsonify(classifier.get_outlier_list(project))
with open(DISABLED_FILE) as f:
dis = f.read().split('\n')
return jsonify(dis)
def post(self, project):
data = request.json
DISABLED_FILE = os.path.join(TRAINING_IMG, project, 'disabled.txt')
with open(DISABLED_FILE, 'w') as f:
for x in data:
f.write(x)
f.write('\n')
f.close()
return 'ok'
@api.errorhandler(ValueError)
def handle_invalid_usage(error):
response = jsonify({
'status': 'error',
'error': str(error),
'time': now()
})
response.status_code = 422
return response
if __name__ == '__main__':
flask_app.run()