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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -58
app.py CHANGED
@@ -27,34 +27,45 @@ logging.basicConfig(
27
  format='%(asctime)s - %(levelname)s - %(message)s'
28
  )
29
 
 
 
 
 
 
 
 
 
 
 
30
  class FaceAnalysisApp:
31
  def __init__(self):
32
  self.app = Flask(__name__, static_folder='static')
33
  self.setup_app()
34
-
 
 
 
 
35
  def setup_app(self):
 
36
  # Configuration de base
37
- self.app.config['UPLOAD_FOLDER'] = 'static/uploads'
38
- self.app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
39
- self.app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}
40
- self.app.config['SECRET_KEY'] = os.urandom(24)
 
 
41
 
42
- # Initialisation des composants
43
  CORS(self.app)
44
  self.limiter = Limiter(
45
  self.app,
46
  key_func=get_remote_address,
47
  default_limits=["200 per day", "50 per hour"]
48
  )
49
-
50
- # File d'attente pour le traitement asynchrone
51
- self.task_queue = queue.Queue()
52
- self.start_worker_thread()
53
-
54
- # Cache pour les résultats
55
- self.results_cache = {}
56
-
57
  def start_worker_thread(self):
 
58
  def worker():
59
  while True:
60
  task = self.task_queue.get()
@@ -64,21 +75,12 @@ class FaceAnalysisApp:
64
  task()
65
  except Exception as e:
66
  logging.error(f"Error in worker thread: {str(e)}")
67
- self.task_queue.task_done()
68
-
 
69
  self.worker_thread = threading.Thread(target=worker, daemon=True)
70
  self.worker_thread.start()
71
 
72
- def timing_decorator(self, f):
73
- @wraps(f)
74
- def wrap(*args, **kwargs):
75
- start = time.time()
76
- result = f(*args, **kwargs)
77
- end = time.time()
78
- logging.info(f'{f.__name__} took {end-start:.2f} seconds to execute')
79
- return result
80
- return wrap
81
-
82
  def validate_image(self, image_stream):
83
  """Valide et optimise l'image"""
84
  try:
@@ -103,7 +105,7 @@ class FaceAnalysisApp:
103
  raise ValueError("Invalid image format")
104
 
105
  def process_face_detection(self, image_path):
106
- """Détection de visage avec mise en cache"""
107
  image_hash = hashlib.md5(open(image_path, 'rb').read()).hexdigest()
108
 
109
  if image_hash in self.results_cache:
@@ -123,14 +125,15 @@ class FaceAnalysisApp:
123
 
124
  @timing_decorator
125
  def verify_faces(self, image1_path, image2_path):
126
- """Comparaison des visages avec vérification approfondie"""
127
  try:
128
- # Vérification initiale de la présence de visages
129
  face1 = cv2.imread(image1_path)
130
  face2 = cv2.imread(image2_path)
131
  if face1 is None or face2 is None:
132
  raise ValueError("Unable to read one or both images")
133
 
 
134
  result = DeepFace.verify(
135
  img1_path=image1_path,
136
  img2_path=image2_path,
@@ -139,9 +142,11 @@ class FaceAnalysisApp:
139
  )
140
 
141
  # Enrichissement des résultats
142
- result['timestamp'] = datetime.now().isoformat()
143
- result['confidence_score'] = 1 - result.get('distance', 0)
144
- result['processing_time'] = time.time()
 
 
145
 
146
  return result
147
  except Exception as e:
@@ -149,6 +154,8 @@ class FaceAnalysisApp:
149
  raise
150
 
151
  def setup_routes(self):
 
 
152
  @self.app.route('/')
153
  def index():
154
  return render_template('index.html')
@@ -157,6 +164,7 @@ class FaceAnalysisApp:
157
  @self.limiter.limit("10 per minute")
158
  def verify_faces_endpoint():
159
  try:
 
160
  if 'image1' not in request.files or 'image2' not in request.files:
161
  return jsonify({'error': 'Two images are required'}), 400
162
 
@@ -170,35 +178,33 @@ class FaceAnalysisApp:
170
  except ValueError as e:
171
  return jsonify({'error': str(e)}), 400
172
 
173
- # Création des fichiers temporaires
174
  with tempfile.TemporaryDirectory() as temp_dir:
175
- image1_path = os.path.join(temp_dir, secure_filename(image1.filename))
176
- image2_path = os.path.join(temp_dir, secure_filename(image2.filename))
177
-
178
- # Sauvegarde des images optimisées
179
- with open(image1_path, 'wb') as f:
180
- f.write(image1_stream.getvalue())
181
- with open(image2_path, 'wb') as f:
182
- f.write(image2_stream.getvalue())
183
-
184
- # Analyse des visages
185
- result = self.verify_faces(image1_path, image2_path)
186
-
187
- # Sauvegarde permanente si nécessaire
188
  if result['verified']:
189
  permanent_dir = os.path.join(self.app.static_folder, 'verified_faces')
190
  os.makedirs(permanent_dir, exist_ok=True)
191
 
192
- # Génération de noms uniques
193
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
194
- image1_name = f"face1_{timestamp}_{uuid.uuid4().hex[:8]}.jpg"
195
- image2_name = f"face2_{timestamp}_{uuid.uuid4().hex[:8]}.jpg"
196
-
197
- shutil.copy2(image1_path, os.path.join(permanent_dir, image1_name))
198
- shutil.copy2(image2_path, os.path.join(permanent_dir, image2_name))
199
 
200
- result['image1_url'] = f'/static/verified_faces/{image1_name}'
201
- result['image2_url'] = f'/static/verified_faces/{image2_name}'
202
 
203
  return jsonify(result)
204
 
@@ -221,7 +227,7 @@ class FaceAnalysisApp:
221
  except ValueError as e:
222
  return jsonify({'error': str(e)}), 400
223
 
224
- # Traitement asynchrone
225
  result_queue = queue.Queue()
226
 
227
  def process_task():
@@ -238,9 +244,10 @@ class FaceAnalysisApp:
238
  except:
239
  pass
240
 
 
241
  self.task_queue.put(process_task)
242
 
243
- # Attente du résultat avec timeout
244
  try:
245
  status, result = result_queue.get(timeout=30)
246
  if status == 'error':
@@ -262,7 +269,7 @@ class FaceAnalysisApp:
262
  return jsonify({'error': 'Rate limit exceeded'}), 429
263
 
264
  def run(self, host='0.0.0.0', port=5000, debug=False):
265
- self.setup_routes()
266
  self.app.run(host=host, port=port, debug=debug)
267
 
268
  if __name__ == '__main__':
 
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()
 
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:
 
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:
 
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,
 
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:
 
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')
 
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
 
 
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
 
 
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():
 
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':
 
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__':