Docfile commited on
Commit
f0f505e
·
verified ·
1 Parent(s): fafff62

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -261
app.py CHANGED
@@ -1,277 +1,104 @@
1
- from flask import Flask, render_template, request, jsonify, send_from_directory
2
- from flask_cors import CORS
3
- from flask_limiter import Limiter
4
- from flask_limiter.util import get_remote_address
5
  from deepface import DeepFace
6
- from werkzeug.utils import secure_filename
7
  import os
8
  import tempfile
9
  import shutil
10
- import uuid
11
- import logging
12
- import time
13
- from datetime import datetime
14
- from functools import wraps
15
- import numpy as np
16
- import cv2
17
- from PIL import Image
18
- import io
19
- import threading
20
- import queue
21
- import hashlib
22
 
23
- # Configuration du logging
24
- logging.basicConfig(
25
- filename='app.log',
26
- level=logging.INFO,
27
- format='%(asctime)s - %(levelname)s - %(message)s'
28
- )
29
-
30
- def timing_decorator(func):
31
- @wraps(func)
32
- def wrapper(*args, **kwargs):
33
- start = time.time()
34
- result = func(*args, **kwargs)
35
- end = time.time()
36
- logging.info(f'{func.__name__} took {end-start:.2f} seconds to execute')
37
- return result
38
- return wrapper
39
-
40
- class FaceAnalysisApp:
41
- def __init__(self):
42
- self.app = Flask(__name__, static_folder='static')
43
- self.setup_app()
44
- self.results_cache = {}
45
- self.task_queue = queue.Queue()
46
- self.setup_routes()
47
- self.start_worker_thread()
48
-
49
- def setup_app(self):
50
- """Configure l'application Flask"""
51
- # Configuration de base
52
- self.app.config.update(
53
- UPLOAD_FOLDER='static/uploads',
54
- MAX_CONTENT_LENGTH=16 * 1024 * 1024,
55
- ALLOWED_EXTENSIONS={'png', 'jpg', 'jpeg', 'gif'},
56
- SECRET_KEY=os.urandom(24)
57
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- # Initialisation CORS et Limiter
60
- CORS(self.app)
61
- self.limiter = Limiter(
62
- self.app,
63
- key_func=get_remote_address,
64
- default_limits=["200 per day", "50 per hour"]
65
- )
66
-
67
- def start_worker_thread(self):
68
- """Démarre le thread de traitement en arrière-plan"""
69
- def worker():
70
- while True:
71
- task = self.task_queue.get()
72
- if task is None:
73
- break
74
- try:
75
- task()
76
- except Exception as e:
77
- logging.error(f"Error in worker thread: {str(e)}")
78
- finally:
79
- self.task_queue.task_done()
80
-
81
- self.worker_thread = threading.Thread(target=worker, daemon=True)
82
- self.worker_thread.start()
83
-
84
- def validate_image(self, image_stream):
85
- """Valide et optimise l'image"""
86
- try:
87
- img = Image.open(image_stream)
88
-
89
- # Vérification des dimensions
90
- if img.size[0] > 2000 or img.size[1] > 2000:
91
- img.thumbnail((2000, 2000), Image.LANCZOS)
92
-
93
- # Conversion en RGB si nécessaire
94
- if img.mode not in ('RGB', 'L'):
95
- img = img.convert('RGB')
96
-
97
- # Optimisation
98
- output = io.BytesIO()
99
- img.save(output, format='JPEG', quality=85, optimize=True)
100
- output.seek(0)
101
-
102
- return output
103
- except Exception as e:
104
- logging.error(f"Image validation error: {str(e)}")
105
- raise ValueError("Invalid image format")
106
-
107
- def process_face_detection(self, image_path):
108
- """Détecte les visages avec mise en cache"""
109
- image_hash = hashlib.md5(open(image_path, 'rb').read()).hexdigest()
110
 
111
- if image_hash in self.results_cache:
112
- return self.results_cache[image_hash]
113
-
 
 
114
  try:
115
- result = DeepFace.analyze(
116
- img_path=image_path,
117
- actions=['age', 'gender', 'race', 'emotion'],
118
- enforce_detection=True
119
- )
120
- self.results_cache[image_hash] = result
121
- return result
122
- except Exception as e:
123
- logging.error(f"Face detection error: {str(e)}")
124
- raise
125
-
126
- @timing_decorator
127
- def verify_faces(self, image1_path, image2_path):
128
- """Compare deux visages"""
129
- try:
130
- # Vérification des images
131
- face1 = cv2.imread(image1_path)
132
- face2 = cv2.imread(image2_path)
133
- if face1 is None or face2 is None:
134
- raise ValueError("Unable to read one or both images")
135
-
136
- # Comparaison des visages
137
  result = DeepFace.verify(
138
- img1_path=image1_path,
139
- img2_path=image2_path,
140
- enforce_detection=True,
141
- model_name="VGG-Face"
 
 
142
  )
143
-
144
- # Enrichissement des résultats
145
- result.update({
146
- 'timestamp': datetime.now().isoformat(),
147
- 'confidence_score': 1 - result.get('distance', 0),
148
- 'processing_time': time.time()
149
- })
150
-
151
- return result
152
- except Exception as e:
153
- logging.error(f"Face verification error: {str(e)}")
154
- raise
155
 
156
- def setup_routes(self):
157
- """Configure les routes de l'application"""
158
-
159
- @self.app.route('/')
160
- def index():
161
- return render_template('index.html')
162
-
163
- @self.app.route('/verify', methods=['POST'])
164
- @self.limiter.limit("10 per minute")
165
- def verify_faces_endpoint():
166
- try:
167
- # Vérification des fichiers
168
- if 'image1' not in request.files or 'image2' not in request.files:
169
- return jsonify({'error': 'Two images are required'}), 400
170
-
171
- image1 = request.files['image1']
172
- image2 = request.files['image2']
173
-
174
- # Validation des images
175
- try:
176
- image1_stream = self.validate_image(image1)
177
- image2_stream = self.validate_image(image2)
178
- except ValueError as e:
179
- return jsonify({'error': str(e)}), 400
180
-
181
- # Traitement des images
182
- with tempfile.TemporaryDirectory() as temp_dir:
183
- # Sauvegarde temporaire
184
- paths = []
185
- for img, stream in [(image1, image1_stream), (image2, image2_stream)]:
186
- path = os.path.join(temp_dir, secure_filename(img.filename))
187
- with open(path, 'wb') as f:
188
- f.write(stream.getvalue())
189
- paths.append(path)
190
-
191
- # Vérification des visages
192
- result = self.verify_faces(paths[0], paths[1])
193
-
194
- # Sauvegarde des résultats positifs
195
- if result['verified']:
196
- permanent_dir = os.path.join(self.app.static_folder, 'verified_faces')
197
- os.makedirs(permanent_dir, exist_ok=True)
198
-
199
- saved_paths = []
200
- for i, path in enumerate(paths, 1):
201
- name = f"face{i}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}.jpg"
202
- dest = os.path.join(permanent_dir, name)
203
- shutil.copy2(path, dest)
204
- saved_paths.append(f'/static/verified_faces/{name}')
205
-
206
- result['image1_url'] = saved_paths[0]
207
- result['image2_url'] = saved_paths[1]
208
-
209
- return jsonify(result)
210
 
211
- except Exception as e:
212
- logging.error(f"Verification endpoint error: {str(e)}")
213
- return jsonify({'error': 'An internal error occurred'}), 500
214
 
215
- @self.app.route('/analyze', methods=['POST'])
216
- @self.limiter.limit("20 per minute")
217
- def analyze_face_endpoint():
218
- try:
219
- if 'image' not in request.files:
220
- return jsonify({'error': 'No image provided'}), 400
221
-
222
- image = request.files['image']
223
-
224
- # Validation de l'image
225
- try:
226
- image_stream = self.validate_image(image)
227
- except ValueError as e:
228
- return jsonify({'error': str(e)}), 400
229
-
230
- # File d'attente pour les résultats
231
- result_queue = queue.Queue()
232
-
233
- def process_task():
234
- try:
235
- with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as temp_file:
236
- temp_file.write(image_stream.getvalue())
237
- result = self.process_face_detection(temp_file.name)
238
- result_queue.put(('success', result))
239
- except Exception as e:
240
- result_queue.put(('error', str(e)))
241
- finally:
242
- try:
243
- os.unlink(temp_file.name)
244
- except:
245
- pass
246
-
247
- # Ajout de la tâche à la file d'attente
248
- self.task_queue.put(process_task)
249
-
250
- # Attente du résultat
251
- try:
252
- status, result = result_queue.get(timeout=30)
253
- if status == 'error':
254
- return jsonify({'error': result}), 500
255
- return jsonify(result)
256
- except queue.Empty:
257
- return jsonify({'error': 'Processing timeout'}), 408
258
-
259
- except Exception as e:
260
- logging.error(f"Analysis endpoint error: {str(e)}")
261
- return jsonify({'error': 'An internal error occurred'}), 500
262
-
263
- @self.app.errorhandler(413)
264
- def request_entity_too_large(error):
265
- return jsonify({'error': 'File too large'}), 413
266
-
267
- @self.app.errorhandler(429)
268
- def ratelimit_handler(e):
269
- return jsonify({'error': 'Rate limit exceeded'}), 429
270
 
271
- def run(self, host='0.0.0.0', port=5000, debug=False):
272
- """Démarre l'application Flask"""
273
- self.app.run(host=host, port=port, debug=debug)
274
 
275
  if __name__ == '__main__':
276
- app = FaceAnalysisApp()
277
- app.run(debug=True)
 
 
1
+ from flask import Flask, render_template, request, jsonify
 
 
 
2
  from deepface import DeepFace
 
3
  import os
4
  import tempfile
5
  import shutil
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ app = Flask(__name__)
8
+
9
+ # Configuration pour l'upload des images (facultatif, pour stocker les images temporairement)
10
+ UPLOAD_FOLDER = 'uploads'
11
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
12
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
13
+
14
+ def allowed_file(filename):
15
+ return '.' in filename and \
16
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
17
+
18
+ def process_image(image_path):
19
+ """
20
+ Traite une image avec DeepFace (détection, alignement, etc.).
21
+ Vous pouvez personnaliser cette fonction en fonction de vos besoins.
22
+ """
23
+ try:
24
+ # Exemple d'extraction des visages avec alignement et détection
25
+ faces = DeepFace.extract_faces(img_path=image_path, detector_backend='retinaface', align=True)
26
+ if len(faces) > 0 :
27
+ return faces[0]['facial_area']
28
+ else:
29
+ return None
30
+
31
+ except Exception as e:
32
+ print(f"Erreur lors du traitement de l'image : {e}")
33
+ return None
34
+
35
+ @app.route('/')
36
+ def index():
37
+ return render_template('index.html')
38
+
39
+ @app.route('/compare', methods=['POST'])
40
+ def compare():
41
+
42
+ # Gestion des fichiers uploadés
43
+ if 'file1' not in request.files or 'file2' not in request.files:
44
+ return jsonify({'error': 'Aucun fichier sélectionné'}), 400
45
+
46
+ file1 = request.files['file1']
47
+ file2 = request.files['file2']
48
+
49
+ if file1.filename == '' or file2.filename == '':
50
+ return jsonify({'error': 'Aucun fichier sélectionné'}), 400
51
+
52
+ if file1 and allowed_file(file1.filename) and file2 and allowed_file(file2.filename):
53
+ # Créer un dossier temporaire pour stocker les images
54
+ temp_dir = tempfile.mkdtemp(prefix="face_compare_", dir=app.config['UPLOAD_FOLDER'])
55
 
56
+ # Enregistre les fichiers dans le dossier temporaire
57
+ file1_path = os.path.join(temp_dir, file1.filename)
58
+ file2_path = os.path.join(temp_dir, file2.filename)
59
+ file1.save(file1_path)
60
+ file2.save(file2_path)
61
+
62
+ # Traitement des images (vous pouvez personnaliser cette partie)
63
+ processed_img1 = process_image(file1_path)
64
+ processed_img2 = process_image(file2_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ if processed_img1 is None or processed_img2 is None:
67
+ # Supprimer le dossier temporaire et son contenu
68
+ shutil.rmtree(temp_dir)
69
+ return jsonify({'error': 'Aucun visage détecté dans une ou plusieurs images'}), 400
70
+
71
  try:
72
+ # Comparaison des visages avec DeepFace
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  result = DeepFace.verify(
74
+ img1_path=file1_path,
75
+ img2_path=file2_path,
76
+ model_name="VGG-Face",
77
+ detector_backend="retinaface",
78
+ distance_metric="cosine",
79
+ enforce_detection=False
80
  )
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ # Formater la réponse
83
+ response_data = {
84
+ 'verified': result['verified'],
85
+ 'similarity': round((1 - result['distance']) * 100, 1) if result['verified'] else round((1 - result['distance']) * 100, 1)
86
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
+ # Supprimer le dossier temporaire et son contenu
89
+ shutil.rmtree(temp_dir)
90
+ return jsonify(response_data)
91
 
92
+ except Exception as e:
93
+ # Supprimer le dossier temporaire et son contenu
94
+ shutil.rmtree(temp_dir)
95
+ print(f"Erreur lors de la comparaison des visages : {e}")
96
+ return jsonify({'error': str(e)}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ else:
99
+ return jsonify({'error': 'Type de fichier non autorisé'}), 400
 
100
 
101
  if __name__ == '__main__':
102
+ # Créer le dossier d'upload si nécessaire
103
+ os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
104
+ app.run(debug=True) # Mettre debug=False en production