cyberai-1 commited on
Commit
1d60d0d
·
1 Parent(s): b921d86

Update App.py

Browse files
Files changed (1) hide show
  1. app.py +26 -17
app.py CHANGED
@@ -25,11 +25,11 @@ UPLOAD_DIR = BASE_DIR / "static" / "uploads"
25
  DATABASE_PATH = BASE_DIR / "instance" / "traffic_signs.sqlite3"
26
  MODEL_PATH = BASE_DIR / "traffic_classifier.h5"
27
  ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "webp"}
28
- NORMALIZE_INPUT = os.environ.get("NORMALIZE_INPUT", "false").lower() in {"1", "true", "yes"}
29
  MC_DROPOUT_PASSES = int(os.environ.get("MC_DROPOUT_PASSES", "1"))
30
- UNKNOWN_CONFIDENCE_THRESHOLD = float(os.environ.get("UNKNOWN_CONFIDENCE_THRESHOLD", "0.999"))
31
- UNKNOWN_MARGIN_THRESHOLD = float(os.environ.get("UNKNOWN_MARGIN_THRESHOLD", "0.0"))
32
- UNKNOWN_VOTE_THRESHOLD = float(os.environ.get("UNKNOWN_VOTE_THRESHOLD", "0.0"))
33
  URL_CONTENT_TYPES = {
34
  "image/jpeg": "jpg",
35
  "image/png": "png",
@@ -282,21 +282,29 @@ def predict_probabilities(batch):
282
 
283
  def predict_sign(path):
284
  if model is None:
285
- return "Model unavailable", 0.0
 
286
  probabilities, vote_agreement = predict_probabilities(prepare_image(path))
287
- ranked_indices = np.argsort(probabilities)[::-1]
288
- class_index = int(ranked_indices[0])
289
- confidence = float(probabilities[class_index])
290
  second_confidence = float(probabilities[int(ranked_indices[1])]) if len(ranked_indices) > 1 else 0.0
291
- margin = confidence - second_confidence
292
- if (
293
- confidence < UNKNOWN_CONFIDENCE_THRESHOLD
294
- or margin < UNKNOWN_MARGIN_THRESHOLD
295
- or vote_agreement < UNKNOWN_VOTE_THRESHOLD
296
- ):
297
- return "Unknown traffic sign", confidence
 
 
 
 
 
 
 
298
  label = TRAFFIC_SIGN_CLASSES[class_index] if class_index < len(TRAFFIC_SIGN_CLASSES) else f"Class {class_index}"
299
- return label, confidence
300
 
301
 
302
  def format_confidence(confidence):
@@ -416,7 +424,7 @@ def predict():
416
 
417
 
418
  def create_prediction(save_path, image_path):
419
- label, confidence = predict_sign(save_path)
420
  with get_db() as conn:
421
  cursor = conn.execute(
422
  """
@@ -438,6 +446,7 @@ def create_prediction(save_path, image_path):
438
  "image_path": image_path,
439
  "predicted_class": label,
440
  "confidence": confidence,
 
441
  }
442
 
443
 
 
25
  DATABASE_PATH = BASE_DIR / "instance" / "traffic_signs.sqlite3"
26
  MODEL_PATH = BASE_DIR / "traffic_classifier.h5"
27
  ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "webp"}
28
+ NORMALIZE_INPUT = os.environ.get("NORMALIZE_INPUT", "true").lower() in {"1", "true", "yes"} # le modèle a été entraîné sur des pixels /255
29
  MC_DROPOUT_PASSES = int(os.environ.get("MC_DROPOUT_PASSES", "1"))
30
+ UNKNOWN_CONFIDENCE_THRESHOLD = float(os.environ.get("UNKNOWN_CONFIDENCE_THRESHOLD", "0.85")) # en dessous → "Unknown"
31
+ UNKNOWN_MARGIN_THRESHOLD = float(os.environ.get("UNKNOWN_MARGIN_THRESHOLD", "0.10")) # écart min entre 1er et 2e choix
32
+ UNKNOWN_VOTE_THRESHOLD = float(os.environ.get("UNKNOWN_VOTE_THRESHOLD", "0.0")) # accord MC-Dropout (0 = désactivé)
33
  URL_CONTENT_TYPES = {
34
  "image/jpeg": "jpg",
35
  "image/png": "png",
 
282
 
283
  def predict_sign(path):
284
  if model is None:
285
+ return "Model unavailable", 0.0, False
286
+
287
  probabilities, vote_agreement = predict_probabilities(prepare_image(path))
288
+ ranked_indices = np.argsort(probabilities)[::-1]
289
+ class_index = int(ranked_indices[0])
290
+ confidence = float(probabilities[class_index])
291
  second_confidence = float(probabilities[int(ranked_indices[1])]) if len(ranked_indices) > 1 else 0.0
292
+ margin = confidence - second_confidence
293
+
294
+ # ── Rejet : image inconnue ou hors-distribution ───────────────────────────
295
+ # Le softmax force toujours une prédiction ; ces trois critères détectent
296
+ # les cas où le modèle n'est pas suffisamment sûr de lui.
297
+ is_unknown = (
298
+ confidence < UNKNOWN_CONFIDENCE_THRESHOLD # confiance globale trop faible
299
+ or margin < UNKNOWN_MARGIN_THRESHOLD # trop proche du 2e candidat
300
+ or vote_agreement < UNKNOWN_VOTE_THRESHOLD # MC-Dropout désaccord
301
+ )
302
+
303
+ if is_unknown:
304
+ return "Unknown traffic sign", confidence, True
305
+
306
  label = TRAFFIC_SIGN_CLASSES[class_index] if class_index < len(TRAFFIC_SIGN_CLASSES) else f"Class {class_index}"
307
+ return label, confidence, False
308
 
309
 
310
  def format_confidence(confidence):
 
424
 
425
 
426
  def create_prediction(save_path, image_path):
427
+ label, confidence, is_unknown = predict_sign(save_path)
428
  with get_db() as conn:
429
  cursor = conn.execute(
430
  """
 
446
  "image_path": image_path,
447
  "predicted_class": label,
448
  "confidence": confidence,
449
+ "is_unknown": is_unknown, # utilisé dans le template pour afficher l'avertissement
450
  }
451
 
452