Spaces:
Sleeping
Sleeping
Commit ·
105a05d
1
Parent(s): 2d5ff7e
AI/Hybrid overhaul: bidirectional AI fusion, stronger hybrid sensitivity, keep large unclassified regions
Browse files- app/detection_engine.py +37 -15
app/detection_engine.py
CHANGED
|
@@ -348,15 +348,8 @@ def feature_based_method(img1, img2, num_clusters=4, sensitivity=0.5):
|
|
| 348 |
return change_mask
|
| 349 |
|
| 350 |
|
| 351 |
-
def
|
| 352 |
-
"""
|
| 353 |
-
Advanced multi-signal fusion:
|
| 354 |
-
- Multi-scale color difference (LAB)
|
| 355 |
-
- Structural dissimilarity (SSIM)
|
| 356 |
-
- Texture change (LBP)
|
| 357 |
-
- Edge change (Canny)
|
| 358 |
-
All fused with learned weights and adaptive thresholding.
|
| 359 |
-
"""
|
| 360 |
if img1.shape != img2.shape:
|
| 361 |
img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]))
|
| 362 |
|
|
@@ -445,7 +438,7 @@ def ai_deep_learning_method(img1, img2, sensitivity=0.5):
|
|
| 445 |
_, change_mask = cv2.threshold(change_mask, 127, 255, cv2.THRESH_BINARY)
|
| 446 |
|
| 447 |
debug = {
|
| 448 |
-
"method": "AI-
|
| 449 |
"threshold_used": int(thr_score * 255),
|
| 450 |
"threshold_percentile_q": q,
|
| 451 |
"threshold_score": thr_score,
|
|
@@ -457,6 +450,30 @@ def ai_deep_learning_method(img1, img2, sensitivity=0.5):
|
|
| 457 |
return change_mask, debug
|
| 458 |
|
| 459 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
def hybrid_method(img1, img2, sensitivity=0.5):
|
| 461 |
"""Hybrid: weighted fusion of all methods with confidence-based merging."""
|
| 462 |
if img1.shape != img2.shape:
|
|
@@ -477,11 +494,10 @@ def hybrid_method(img1, img2, sensitivity=0.5):
|
|
| 477 |
# - diff only: 0.2*255 ≈ 51
|
| 478 |
# - feature only: 0.3*255 ≈ 76
|
| 479 |
# - ai only: 0.5*255 ≈ 127
|
| 480 |
-
#
|
| 481 |
-
|
| 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) *
|
| 485 |
_, final_mask = cv2.threshold(combined.astype(np.uint8), hybrid_thr, 255, cv2.THRESH_BINARY)
|
| 486 |
final_mask = _clean_mask(final_mask)
|
| 487 |
debug = {
|
|
@@ -1603,7 +1619,13 @@ def analyze_change_regions(change_mask, image, min_area=400, use_ensemble=True,
|
|
| 1603 |
object_type, confidence = classify_object_type(image, (x, y, w, h))
|
| 1604 |
|
| 1605 |
if object_type is None:
|
| 1606 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1607 |
|
| 1608 |
region_id += 1
|
| 1609 |
region = {
|
|
|
|
| 348 |
return change_mask
|
| 349 |
|
| 350 |
|
| 351 |
+
def _ai_fusion_core(img1, img2, sensitivity=0.5):
|
| 352 |
+
"""One-direction AI fusion core. Returns (mask, debug)."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
if img1.shape != img2.shape:
|
| 354 |
img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]))
|
| 355 |
|
|
|
|
| 438 |
_, change_mask = cv2.threshold(change_mask, 127, 255, cv2.THRESH_BINARY)
|
| 439 |
|
| 440 |
debug = {
|
| 441 |
+
"method": "AI-Core",
|
| 442 |
"threshold_used": int(thr_score * 255),
|
| 443 |
"threshold_percentile_q": q,
|
| 444 |
"threshold_score": thr_score,
|
|
|
|
| 450 |
return change_mask, debug
|
| 451 |
|
| 452 |
|
| 453 |
+
def ai_deep_learning_method(img1, img2, sensitivity=0.5):
|
| 454 |
+
"""
|
| 455 |
+
Bidirectional AI fusion for robustness:
|
| 456 |
+
- run core on (before, after) and (after, before)
|
| 457 |
+
- combine with OR then clean
|
| 458 |
+
This improves stability for asymmetric scenes / normalization drift.
|
| 459 |
+
"""
|
| 460 |
+
fwd_mask, fwd_debug = _ai_fusion_core(img1, img2, sensitivity=sensitivity)
|
| 461 |
+
rev_mask, rev_debug = _ai_fusion_core(img2, img1, sensitivity=sensitivity)
|
| 462 |
+
|
| 463 |
+
combined = cv2.bitwise_or(fwd_mask, rev_mask)
|
| 464 |
+
combined = _clean_mask(combined, sensitivity=sensitivity)
|
| 465 |
+
|
| 466 |
+
debug = {
|
| 467 |
+
"method": "AI-Based Deep Learning",
|
| 468 |
+
"threshold_used": fwd_debug.get("threshold_used"),
|
| 469 |
+
"bidirectional": True,
|
| 470 |
+
"forward": fwd_debug,
|
| 471 |
+
"reverse": rev_debug,
|
| 472 |
+
"sensitivity": float(sensitivity),
|
| 473 |
+
}
|
| 474 |
+
return combined, debug
|
| 475 |
+
|
| 476 |
+
|
| 477 |
def hybrid_method(img1, img2, sensitivity=0.5):
|
| 478 |
"""Hybrid: weighted fusion of all methods with confidence-based merging."""
|
| 479 |
if img1.shape != img2.shape:
|
|
|
|
| 494 |
# - diff only: 0.2*255 ≈ 51
|
| 495 |
# - feature only: 0.3*255 ≈ 76
|
| 496 |
# - ai only: 0.5*255 ≈ 127
|
| 497 |
+
# Keep threshold low enough that ai-only regions can pass.
|
| 498 |
+
base_thr = 98
|
|
|
|
| 499 |
sens = float(np.clip(sensitivity, 0.0, 1.0))
|
| 500 |
+
hybrid_thr = int(np.clip(base_thr + int((0.5 - sens) * 36), 60, 150))
|
| 501 |
_, final_mask = cv2.threshold(combined.astype(np.uint8), hybrid_thr, 255, cv2.THRESH_BINARY)
|
| 502 |
final_mask = _clean_mask(final_mask)
|
| 503 |
debug = {
|
|
|
|
| 1619 |
object_type, confidence = classify_object_type(image, (x, y, w, h))
|
| 1620 |
|
| 1621 |
if object_type is None:
|
| 1622 |
+
# Do not silently drop large coherent regions; keep them as generic
|
| 1623 |
+
# ground-change candidates so key changes are still surfaced.
|
| 1624 |
+
if raw_area >= max(min_area * 2, 800) and fill_ratio >= 0.18:
|
| 1625 |
+
object_type = "Unclassified Ground Change"
|
| 1626 |
+
confidence = max(0.2, min(0.5, fill_ratio))
|
| 1627 |
+
else:
|
| 1628 |
+
continue
|
| 1629 |
|
| 1630 |
region_id += 1
|
| 1631 |
region = {
|