File size: 2,396 Bytes
d1ac326
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Curated before/after pair registry for the ``/curated`` demo mode (PRD §10.1).

A curated *pair* is two co-registered RGB tiles of the same place at two times. On the real Space
these are LEVIR-CD test samples (plus a couple of well-known change events); the registry reads a
``manifest.json`` describing each pair and serves the images from disk. ``scripts/gen_sample_pairs``
can synthesize placeholder pairs so the pipeline is demonstrable before the real tiles are staged.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from PIL import Image


class CuratedRegistry:
    """Reads ``<data_dir>/manifest.json`` -> pairs, each with ``before.png`` / ``after.png``."""

    def __init__(self, data_dir: str | Path) -> None:
        self.data_dir = Path(data_dir)
        self.pairs: dict[str, dict[str, Any]] = {}
        self.reload()

    def reload(self) -> None:
        self.pairs.clear()
        manifest = self.data_dir / "manifest.json"
        if not manifest.exists():
            return
        for entry in json.loads(manifest.read_text()).get("pairs", []):
            pid = str(entry["id"])
            if (self.data_dir / pid / "before.png").exists():
                self.pairs[pid] = entry

    def list(self) -> list[dict[str, Any]]:
        out = []
        for pid, entry in self.pairs.items():
            before = self.data_dir / pid / "before.png"
            with Image.open(before) as im:
                w, h = im.size
            out.append(
                {
                    "id": pid,
                    "title": entry.get("title", pid),
                    "description": entry.get("description", ""),
                    "source": entry.get("source", ""),
                    "width": w,
                    "height": h,
                }
            )
        return out

    def image_path(self, pair_id: str, which: str) -> Path:
        if pair_id not in self.pairs:
            raise KeyError(pair_id)
        if which not in ("before", "after"):
            raise ValueError(which)
        return self.data_dir / pair_id / f"{which}.png"

    def get_pair(self, pair_id: str) -> tuple[Image.Image, Image.Image]:
        before = Image.open(self.image_path(pair_id, "before")).convert("RGB")
        after = Image.open(self.image_path(pair_id, "after")).convert("RGB")
        return before, after