coderuday21 commited on
Commit
2d5ff7e
·
1 Parent(s): ba711fa

AI rework: percentile+blur threshold + less aggressive cleanup; Hybrid lower threshold

Browse files
Files changed (1) hide show
  1. app/detection_engine.py +37 -14
app/detection_engine.py CHANGED
@@ -415,25 +415,43 @@ def ai_deep_learning_method(img1, img2, sensitivity=0.5):
415
  for ch, w in zip(channels, weights):
416
  fused += w * ch.astype(np.float64)
417
 
418
- fused = fused / (fused.max() + 1e-8)
419
- fused_uint8 = (fused * 255).astype(np.uint8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
 
421
- # Robust thresholding: handles low-contrast and noisy scenes better than Otsu-only.
422
- change_mask, used_thr, otsu_val, noise_floor = _adaptive_binary_threshold(
423
- fused_uint8, min_floor=25, sensitivity=sensitivity
424
- )
425
 
426
- change_mask = _clean_mask(change_mask)
427
 
428
- # Bilateral filter preserves sharp change boundaries while smoothing noise
429
  change_mask = cv2.bilateralFilter(change_mask, 9, 75, 75)
430
  _, change_mask = cv2.threshold(change_mask, 127, 255, cv2.THRESH_BINARY)
431
 
432
  debug = {
433
  "method": "AI-Based Deep Learning",
434
- "threshold_used": int(used_thr),
435
- "otsu": float(otsu_val),
436
- "noise_floor": float(noise_floor),
 
 
 
437
  "sensitivity": float(sensitivity),
438
  }
439
  return change_mask, debug
@@ -455,10 +473,15 @@ def hybrid_method(img1, img2, sensitivity=0.5):
455
  0.5 * ai_mask.astype(np.float32)
456
  )
457
 
458
- # Use a higher threshold: a pixel must be flagged by multiple methods
459
- base_thr = 140
 
 
 
 
 
460
  sens = float(np.clip(sensitivity, 0.0, 1.0))
461
- hybrid_thr = int(np.clip(base_thr + int((0.5 - sens) * 24), 90, 180))
462
  _, final_mask = cv2.threshold(combined.astype(np.uint8), hybrid_thr, 255, cv2.THRESH_BINARY)
463
  final_mask = _clean_mask(final_mask)
464
  debug = {
 
415
  for ch, w in zip(channels, weights):
416
  fused += w * ch.astype(np.float64)
417
 
418
+ # Percentile normalization: max-normalization can make the distribution too peaky,
419
+ # causing an overly strict threshold on some scenes.
420
+ p995 = float(np.quantile(fused, 0.995))
421
+ if p995 <= 1e-8:
422
+ p995 = float(fused.max() + 1e-8)
423
+ fused_norm = np.clip(fused / (p995 + 1e-8), 0.0, 1.0)
424
+
425
+ # Gamma < 1 boosts mid-range responses (useful for subtle changes).
426
+ gamma = 0.85
427
+ fused_norm = np.power(fused_norm, gamma)
428
+
429
+ # Smooth before thresholding so genuine change forms connected regions
430
+ # (prevents _clean_mask from deleting thin speckle artifacts).
431
+ fused_smooth = cv2.GaussianBlur(fused_norm.astype(np.float32), (7, 7), 0)
432
+
433
+ # Sensitivity -> lower percentile => more detections.
434
+ sens = float(np.clip(sensitivity, 0.0, 1.0))
435
+ q = 0.958 - (sens - 0.5) * 0.04
436
+ q = float(np.clip(q, 0.90, 0.98))
437
 
438
+ thr_score = float(np.quantile(fused_smooth, q))
439
+ change_mask = (fused_smooth >= thr_score).astype(np.uint8) * 255
 
 
440
 
441
+ change_mask = _clean_mask(change_mask, sensitivity=sens)
442
 
443
+ # Bilateral filter preserves sharp boundaries while smoothing noise
444
  change_mask = cv2.bilateralFilter(change_mask, 9, 75, 75)
445
  _, change_mask = cv2.threshold(change_mask, 127, 255, cv2.THRESH_BINARY)
446
 
447
  debug = {
448
  "method": "AI-Based Deep Learning",
449
+ "threshold_used": int(thr_score * 255),
450
+ "threshold_percentile_q": q,
451
+ "threshold_score": thr_score,
452
+ "fused_p95": float(np.quantile(fused_smooth, 0.95)),
453
+ "fused_p99": float(np.quantile(fused_smooth, 0.99)),
454
+ "fused_mean": float(np.mean(fused_smooth)),
455
  "sensitivity": float(sensitivity),
456
  }
457
  return change_mask, debug
 
473
  0.5 * ai_mask.astype(np.float32)
474
  )
475
 
476
+ # Combined mask values:
477
+ # - diff only: 0.2*255 ≈ 51
478
+ # - feature only: 0.3*255 ≈ 76
479
+ # - ai only: 0.5*255 ≈ 127
480
+ # Original threshold (140) effectively removed "ai-only" pixels.
481
+ # Lower the threshold so AI (one of the key signals) can contribute.
482
+ base_thr = 105
483
  sens = float(np.clip(sensitivity, 0.0, 1.0))
484
+ hybrid_thr = int(np.clip(base_thr + int((0.5 - sens) * 30), 70, 160))
485
  _, final_mask = cv2.threshold(combined.astype(np.uint8), hybrid_thr, 255, cv2.THRESH_BINARY)
486
  final_mask = _clean_mask(final_mask)
487
  debug = {