Spaces:
Sleeping
Sleeping
cyberai-1 commited on
Commit ·
1d60d0d
1
Parent(s): b921d86
Update App.py
Browse files
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", "
|
| 29 |
MC_DROPOUT_PASSES = int(os.environ.get("MC_DROPOUT_PASSES", "1"))
|
| 30 |
-
UNKNOWN_CONFIDENCE_THRESHOLD = float(os.environ.get("UNKNOWN_CONFIDENCE_THRESHOLD", "0.
|
| 31 |
-
UNKNOWN_MARGIN_THRESHOLD = float(os.environ.get("UNKNOWN_MARGIN_THRESHOLD", "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
|
| 288 |
-
class_index
|
| 289 |
-
confidence
|
| 290 |
second_confidence = float(probabilities[int(ranked_indices[1])]) if len(ranked_indices) > 1 else 0.0
|
| 291 |
-
margin
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|