Spaces:
Sleeping
Sleeping
cyberai-1 commited on
Commit ·
de08434
1
Parent(s): 0053205
Reject uncertain traffic sign predictions
Browse files
app.py
CHANGED
|
@@ -26,6 +26,10 @@ 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 |
URL_CONTENT_TYPES = {
|
| 30 |
"image/jpeg": "jpg",
|
| 31 |
"image/png": "png",
|
|
@@ -241,19 +245,55 @@ def save_uploaded_image(file):
|
|
| 241 |
|
| 242 |
def prepare_image(path):
|
| 243 |
image = Image.open(path).convert("RGB")
|
| 244 |
-
|
| 245 |
array = np.asarray(image, dtype=np.float32)
|
| 246 |
if NORMALIZE_INPUT:
|
| 247 |
array = array / 255.0
|
| 248 |
return np.expand_dims(array, axis=0)
|
| 249 |
|
| 250 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
def predict_sign(path):
|
| 252 |
if model is None:
|
| 253 |
return "Model unavailable", 0.0
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
label = TRAFFIC_SIGN_CLASSES[class_index] if class_index < len(TRAFFIC_SIGN_CLASSES) else f"Class {class_index}"
|
| 258 |
return label, confidence
|
| 259 |
|
|
|
|
| 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", "12"))
|
| 30 |
+
UNKNOWN_CONFIDENCE_THRESHOLD = float(os.environ.get("UNKNOWN_CONFIDENCE_THRESHOLD", "0.50"))
|
| 31 |
+
UNKNOWN_MARGIN_THRESHOLD = float(os.environ.get("UNKNOWN_MARGIN_THRESHOLD", "0.10"))
|
| 32 |
+
UNKNOWN_VOTE_THRESHOLD = float(os.environ.get("UNKNOWN_VOTE_THRESHOLD", "0.60"))
|
| 33 |
URL_CONTENT_TYPES = {
|
| 34 |
"image/jpeg": "jpg",
|
| 35 |
"image/png": "png",
|
|
|
|
| 245 |
|
| 246 |
def prepare_image(path):
|
| 247 |
image = Image.open(path).convert("RGB")
|
| 248 |
+
image = image.resize((30, 30))
|
| 249 |
array = np.asarray(image, dtype=np.float32)
|
| 250 |
if NORMALIZE_INPUT:
|
| 251 |
array = array / 255.0
|
| 252 |
return np.expand_dims(array, axis=0)
|
| 253 |
|
| 254 |
|
| 255 |
+
def normalize_model_output(predictions):
|
| 256 |
+
predictions = np.asarray(predictions, dtype=np.float64)
|
| 257 |
+
total = float(np.sum(predictions))
|
| 258 |
+
if np.any(predictions < 0) or not np.isclose(total, 1.0, atol=1e-3):
|
| 259 |
+
shifted = predictions - np.max(predictions)
|
| 260 |
+
exp_values = np.exp(shifted)
|
| 261 |
+
return exp_values / np.sum(exp_values)
|
| 262 |
+
return predictions
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
def predict_probabilities(batch):
|
| 266 |
+
if MC_DROPOUT_PASSES <= 1:
|
| 267 |
+
return normalize_model_output(model.predict(batch, verbose=0)[0]), 1.0
|
| 268 |
+
|
| 269 |
+
samples = []
|
| 270 |
+
votes = []
|
| 271 |
+
for _ in range(MC_DROPOUT_PASSES):
|
| 272 |
+
probabilities = normalize_model_output(model(batch, training=True).numpy()[0])
|
| 273 |
+
samples.append(probabilities)
|
| 274 |
+
votes.append(int(np.argmax(probabilities)))
|
| 275 |
+
|
| 276 |
+
mean_probabilities = np.mean(np.asarray(samples), axis=0)
|
| 277 |
+
vote_counts = np.bincount(votes, minlength=len(mean_probabilities))
|
| 278 |
+
vote_agreement = float(np.max(vote_counts) / MC_DROPOUT_PASSES)
|
| 279 |
+
return mean_probabilities, vote_agreement
|
| 280 |
+
|
| 281 |
+
|
| 282 |
def predict_sign(path):
|
| 283 |
if model is None:
|
| 284 |
return "Model unavailable", 0.0
|
| 285 |
+
probabilities, vote_agreement = predict_probabilities(prepare_image(path))
|
| 286 |
+
ranked_indices = np.argsort(probabilities)[::-1]
|
| 287 |
+
class_index = int(ranked_indices[0])
|
| 288 |
+
confidence = float(probabilities[class_index])
|
| 289 |
+
second_confidence = float(probabilities[int(ranked_indices[1])]) if len(ranked_indices) > 1 else 0.0
|
| 290 |
+
margin = confidence - second_confidence
|
| 291 |
+
if (
|
| 292 |
+
confidence < UNKNOWN_CONFIDENCE_THRESHOLD
|
| 293 |
+
or margin < UNKNOWN_MARGIN_THRESHOLD
|
| 294 |
+
or vote_agreement < UNKNOWN_VOTE_THRESHOLD
|
| 295 |
+
):
|
| 296 |
+
return "Unknown traffic sign", confidence
|
| 297 |
label = TRAFFIC_SIGN_CLASSES[class_index] if class_index < len(TRAFFIC_SIGN_CLASSES) else f"Class {class_index}"
|
| 298 |
return label, confidence
|
| 299 |
|