File size: 4,002 Bytes
d70361b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""Export AdaptFormer probability maps for the v3 F1≈0.69 val sweep.

The F1=0.69 figure comes from ``runs/v3_baseline_analysis/analysis.json``
(val sweep @ thr=0.25 → F1=0.6900; best @ thr=0.35 → F1=0.6917).

Writes:
  runs/f1_069_inputs/prob_maps/<pair_id>_prob.png
  runs/f1_069_inputs/gt_masks/<pair_id>.png  (copies)
  runs/f1_069_inputs/summary.json
"""
from __future__ import annotations

import json
import shutil
import sys
from pathlib import Path

import numpy as np
from PIL import Image

ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))

OUT = ROOT / "runs" / "f1_069_inputs"
CKPT = ROOT / "models" / "adaptformer_delhi" / "v3_frozen"


def main() -> int:
    from app.evaluation.delhi_eval import _load_label, _load_rgb
    from app.model_inference import predict_change_mask

    man = json.loads((ROOT / "data/delhi_cd/val/manifest.json").read_text(encoding="utf-8"))
    pairs = man["pairs"]
    prob_dir = OUT / "prob_maps"
    gt_dir = OUT / "gt_masks"
    prob_dir.mkdir(parents=True, exist_ok=True)
    gt_dir.mkdir(parents=True, exist_ok=True)

    # Exact row from analysis.json where val_f1 == 0.69
    metrics_at_025 = {
        "threshold": 0.25,
        "val_f1": 0.69,
        "val_precision": 0.7021,
        "val_recall": 0.712,
        "val_iou": 0.5287,
        "source": "runs/v3_baseline_analysis/analysis.json → val_sweep",
    }
    best = {
        "threshold": 0.35,
        "val_f1": 0.6917,
        "val_precision": 0.7324,
        "val_recall": 0.6857,
        "val_iou": 0.5317,
        "source": "runs/v3_baseline_analysis/recommended_threshold.json",
    }

    exported = []
    for row in pairs:
        pid = row["pair_id"]
        before = _load_rgb(ROOT / row["before_path"])
        after = _load_rgb(ROOT / row["after_path"])
        gt_src = ROOT / row["gt_mask"]
        gt = _load_label(gt_src)
        _mask, score = predict_change_mask(before, after, threshold=0.25)
        if score is None:
            print(f"FAIL {pid}: no score")
            continue
        if score.shape[:2] != gt.shape[:2]:
            score = np.array(
                Image.fromarray((score * 255).astype(np.uint8)).resize(
                    (gt.shape[1], gt.shape[0]), Image.BILINEAR
                )
            ).astype(np.float32) / 255.0
        prob_u8 = np.clip(score * 255.0, 0, 255).astype(np.uint8)
        prob_path = prob_dir / f"{pid}_prob.png"
        Image.fromarray(prob_u8).save(prob_path)
        # also save a thumbnail like DETECTION_SAVE_PROB_MAP does
        thumb = Image.fromarray(prob_u8)
        thumb.thumbnail((1024, 1024), Image.Resampling.LANCZOS)
        thumb.save(prob_dir / f"{pid}_prob_thumb.png")
        gt_dst = gt_dir / f"{pid}.png"
        shutil.copy2(gt_src, gt_dst)
        exported.append(
            {
                "pair_id": pid,
                "prob_map": str(prob_path.relative_to(ROOT)),
                "gt_mask": str(gt_dst.relative_to(ROOT)),
                "gt_source": row["gt_mask"],
                "score_mean": round(float(score.mean()), 4),
                "score_p99": round(float(np.percentile(score, 99)), 4),
            }
        )
        print(f"OK {pid} -> {prob_path.name}")

    summary = {
        "model": str(CKPT.relative_to(ROOT)),
        "f1_069_operating_point": metrics_at_025,
        "best_val_nearby": best,
        "precision": metrics_at_025["val_precision"],
        "recall": metrics_at_025["val_recall"],
        "f1": metrics_at_025["val_f1"],
        "val_pairs": exported,
        "gt_split": "data/delhi_cd/val (4 pairs)",
        "gt_label_dir": "docs/delhi_eval/labels/",
    }
    (OUT / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
    print("Wrote", OUT / "summary.json")
    print(
        f"Precision={metrics_at_025['val_precision']}  "
        f"Recall={metrics_at_025['val_recall']}  F1={metrics_at_025['val_f1']}"
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())