File size: 2,963 Bytes
eebd638
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Evaluate a trained checkpoint on the validation or test split.

Usage::

    python scripts/evaluate.py \\
        --checkpoint checkpoints/best.pt \\
        --prototypes checkpoints/prototypes.pt \\
        --split val
"""

from __future__ import annotations

import argparse
import logging
import sys
from pathlib import Path

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

import torch
from omegaconf import OmegaConf
from torch.utils.data import DataLoader

from data.dataset import ConstructionChangeDataset
from evaluation.metrics import SegmentationEvaluator
from models.detector import ConstructionChangeDetector

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger(__name__)


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(description="Evaluate construction change detector.")
    p.add_argument("--checkpoint",  required=True)
    p.add_argument("--prototypes",  required=True)
    p.add_argument("--config",      default="configs/base.yaml")
    p.add_argument("--split",       default="val", choices=["train", "val"])
    p.add_argument("--threshold",   type=float, default=0.5)
    return p.parse_args()


def main() -> None:
    args   = parse_args()
    cfg    = OmegaConf.load(args.config)
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # Load model
    model = ConstructionChangeDetector(k_prototypes=cfg.model.k_prototypes)
    model.load_sam2(cfg.model.sam2_hf_id)
    state = torch.load(args.checkpoint, map_location=device)
    model.load_trainable_state_dict(state)
    model.to(device).eval()

    # Load prototypes
    proto_ckpt = torch.load(args.prototypes, map_location=device)
    prototypes = proto_ckpt["prototypes"].to(device)

    # Dataset
    dataset = ConstructionChangeDataset(
        tile_dir=cfg.data.tile_dir,
        image_size=cfg.data.image_size,
        augment=False,
        min_fg_pixels=cfg.data.min_fg_pixels,
        split=args.split,
        val_split=cfg.training.val_split,
        seed=cfg.training.seed,
    )
    loader = DataLoader(
        dataset,
        batch_size=cfg.training.batch_size,
        shuffle=False,
        num_workers=cfg.data.num_workers,
    )

    evaluator = SegmentationEvaluator(threshold=args.threshold)

    with torch.no_grad():
        for batch in loader:
            tile_2022 = batch["tile_2022"].to(device)
            tile_2024 = batch["tile_2024"].to(device)
            mask_2022 = batch["mask_2022"].to(device)
            mask_2024 = batch["mask_2024"].to(device)

            out = model(tile_2022, tile_2024, mask_2022, prototypes)
            evaluator.update(out["prob_map"].cpu(), mask_2024.cpu())

    metrics = evaluator.compute()
    logger.info("=== Evaluation results (%s split) ===", args.split)
    for k, v in metrics.items():
        logger.info("  %-12s %.4f", k, v)


if __name__ == "__main__":
    main()