Spaces:
Running
Running
| """Compare run 47 vs GT vs native for missed-change analysis.""" | |
| from __future__ import annotations | |
| import json | |
| import sqlite3 | |
| from collections import Counter | |
| from pathlib import Path | |
| import numpy as np | |
| from PIL import Image | |
| from scipy import ndimage | |
| ROOT = Path(__file__).resolve().parent.parent | |
| def main() -> None: | |
| c = sqlite3.connect(ROOT / "data/satellite_app.db") | |
| r = c.execute( | |
| "SELECT regions_json,change_percentage,regions_count,before_full_path,after_full_path,overlay_path " | |
| "FROM detection_runs WHERE id=47" | |
| ).fetchone() | |
| regs = json.loads(r[0]) | |
| print("run47 change%", r[1], "n", r[2]) | |
| print(Counter(x.get("objectType") for x in regs)) | |
| for x in regs: | |
| print( | |
| f" #{x['id']} {x.get('objectType')} conf={float(x.get('confidence') or 0):.2f} " | |
| f"area={x['area']} bbox={x['bbox']}" | |
| ) | |
| gt_path = ROOT / "docs/delhi_eval/labels/dda_grid54_h43x2e1.png" | |
| g = np.array(Image.open(gt_path).convert("L")) > 127 | |
| print("GT shape", g.shape, "change%", round(g.mean() * 100, 4), "px", int(g.sum())) | |
| lab, n = ndimage.label(g) | |
| print("GT components", n) | |
| before = np.array(Image.open(ROOT / "data" / r[3]).convert("RGB")) | |
| # overlay-sized working image | |
| bh, bw = before.shape[:2] | |
| gt_r = np.array(Image.fromarray(g.astype(np.uint8) * 255).resize((bw, bh), Image.NEAREST)) > 127 | |
| # Approximate pred mask from region bboxes (coarse) — better: recover from overlay tint | |
| overlay = np.array(Image.open(ROOT / "data" / r[5]).convert("RGB")) | |
| after = np.array(Image.open(ROOT / "data" / r[4]).convert("RGB")) | |
| # Tint recovery: changed pixels pushed toward red | |
| diff = overlay.astype(np.float32) - after.astype(np.float32) | |
| pred = (diff[:, :, 0] > 8) & (diff[:, :, 0] > diff[:, :, 1] + 3) | |
| # Dilate slightly | |
| pred = ndimage.binary_dilation(pred, iterations=1) | |
| print("pred_from_overlay change%", round(pred.mean() * 100, 4), "px", int(pred.sum())) | |
| inter = (gt_r & pred).sum() | |
| gt_px = max(int(gt_r.sum()), 1) | |
| pred_px = max(int(pred.sum()), 1) | |
| print("GT recall vs overlay tint", round(inter / gt_px, 4), "precision", round(inter / pred_px, 4)) | |
| # Per GT component: covered? | |
| glab, gn = ndimage.label(gt_r) | |
| missed = [] | |
| for i in range(1, gn + 1): | |
| comp = glab == i | |
| area = int(comp.sum()) | |
| cov = float((comp & pred).sum() / max(area, 1)) | |
| ys, xs = np.where(comp) | |
| bbox = (int(xs.min()), int(ys.min()), int(xs.max() - xs.min() + 1), int(ys.max() - ys.min() + 1)) | |
| status = "HIT" if cov >= 0.15 else "MISS" | |
| print(f" GT#{i} area={area} cov={cov:.2f} {status} bbox={bbox}") | |
| if status == "MISS": | |
| missed.append((i, area, bbox, cov)) | |
| # Save miss crops for inspection | |
| out = ROOT / "runs/missed_gt_review" | |
| out.mkdir(parents=True, exist_ok=True) | |
| for i, area, bbox, cov in missed: | |
| x0, y0, bw, bh = bbox | |
| pad = 40 | |
| xa, ya = max(0, x0 - pad), max(0, y0 - pad) | |
| xb, yb = min(before.shape[1], x0 + bw + pad), min(before.shape[0], y0 + bh + pad) | |
| panel = np.concatenate([before[ya:yb, xa:xb], after[ya:yb, xa:xb]], axis=1) | |
| Image.fromarray(panel).save(out / f"miss_gt{i}_a{area}.png") | |
| print("missed", len(missed), "->", out) | |
| ns = ROOT / "runs/native_dda/20260721_131449/native/summary.json" | |
| if ns.exists(): | |
| s = json.loads(ns.read_text(encoding="utf-8")) | |
| print("native change%", s["stats"].get("change_percentage"), "regions", s.get("n_regions")) | |
| if __name__ == "__main__": | |
| main() | |