cyberai-1 commited on
Commit
de08434
·
1 Parent(s): 0053205

Reject uncertain traffic sign predictions

Browse files
Files changed (1) hide show
  1. app.py +44 -4
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
- #image = image.resize((30, 30))
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
- predictions = model.predict(prepare_image(path), verbose=0)[0]
255
- class_index = int(np.argmax(predictions))
256
- confidence = float(np.max(predictions))
 
 
 
 
 
 
 
 
 
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