File size: 5,527 Bytes
387a20d
 
 
 
 
 
 
7a2d30b
387a20d
 
 
7a2d30b
387a20d
7a2d30b
387a20d
7a2d30b
 
387a20d
 
 
 
 
 
 
 
 
 
7a2d30b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387a20d
 
 
7a2d30b
 
 
 
 
 
 
 
 
 
387a20d
 
 
 
7a2d30b
 
 
 
387a20d
 
 
7a2d30b
 
 
 
 
 
387a20d
7a2d30b
 
 
 
 
 
 
 
 
 
 
 
 
 
387a20d
7a2d30b
 
 
 
 
 
 
 
387a20d
 
 
 
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
import argparse
from pathlib import Path

import numpy as np
import yaml


def real_images(paths, size, scale_factor):
    import tifffile

    if not paths:
        raise FileNotFoundError("No TIFF files supplied")
    images = []
    scale_factors = []
    for path in paths:
        image = tifffile.imread(path)
        original_dtype = image.dtype
        if image.ndim != 3:
            raise ValueError(f"Expected a 13-band TIFF, got {image.shape} from {path}")
        if image.shape[0] == 13:
            image = image.transpose(1, 2, 0)
        if image.shape[-1] != 13:
            raise ValueError(f"Expected 13 Sentinel-2 bands, got {image.shape} from {path}")
        image = np.delete(image, 10, axis=-1)  # Exclude Sentinel-2 B10 as in the paper.
        y = np.linspace(0, image.shape[0] - 1, size).round().astype(int)
        x = np.linspace(0, image.shape[1] - 1, size).round().astype(int)
        image = image[y][:, x]
        factor = scale_factor
        if factor is None:
            minimum = float(np.nanmin(image))
            maximum = float(np.nanmax(image))
            already_normalized = minimum >= 0.0 and maximum <= 1.0
            factor = 10000.0 if not already_normalized and (
                np.issubdtype(original_dtype, np.integer) or maximum > 1.0
            ) else 1.0
        images.append(np.clip(image.astype(np.float32) / factor, 0, 1).transpose(2, 0, 1))
        scale_factors.append(factor)
    return np.asarray(images, dtype=np.float32), np.asarray(scale_factors, dtype=np.float32)


def synthetic_images(count, size, seed):
    rng = np.random.default_rng(seed)
    y, x = np.mgrid[0:size, 0:size].astype(np.float32) / max(size - 1, 1)
    images = []
    for index in range(count):
        phase = rng.uniform(0, 2 * np.pi)
        bands = []
        for band in range(12):
            pattern = 0.45 + 0.22 * np.sin((band + 1) * x + phase)
            pattern += 0.18 * np.cos((band / 3 + 1) * y - phase)
            pattern += rng.normal(0, 0.025, (size, size))
            bands.append(np.clip(pattern, 0, 1))
        images.append(bands)
    return np.asarray(images, dtype=np.float32)


def save_npz(output, images, source, protocol, normalization, scale_factors, stage):
    band_order = np.asarray(["B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B8A", "B9", "B11", "B12"])
    output.parent.mkdir(parents=True, exist_ok=True)
    np.savez_compressed(output, images=images, data_source=np.asarray(source),
                        protocol=np.asarray(protocol), band_order=band_order,
                        normalization=np.asarray(normalization), scale_factors=scale_factors,
                        stage=np.asarray(stage))
    print(f"saved: {output} shape={images.shape} stage={stage} data_source={source}")


def main():
    parser = argparse.ArgumentParser(description="Generate compact 12-band spectral data")
    parser.add_argument("--config", default="conf/config.yaml")
    parser.add_argument("--real-dir", help="Convert official 13-band Sentinel-2 TIFF files")
    parser.add_argument("--scale-factor", default="auto",
                        help="TIFF divisor, or 'auto' (10000 for integer/range > 1; otherwise 1)")
    parser.add_argument("--stage", choices=("stage1", "stage2"), default="stage2",
                        help="Target stage for real TIFF conversion")
    args = parser.parse_args()
    with open(args.config, encoding="utf-8") as handle:
        config = yaml.safe_load(handle)
    if args.scale_factor == "auto":
        scale_factor = None
    else:
        scale_factor = float(args.scale_factor)
        if not np.isfinite(scale_factor) or scale_factor <= 0:
            raise ValueError("--scale-factor must be a positive finite number or 'auto'")
    if args.real_dir:
        stage = next(item for item in config["stages"] if item["name"] == args.stage)
        count = stage["train_samples"] + (config["data"]["test_samples"] if args.stage == "stage2" else 0)
        paths = sorted(Path(args.real_dir).rglob("*.tif"))
        if len(paths) < count:
            raise ValueError(f"Need at least {count} TIFF files, found {len(paths)}")
        images, scale_factors = real_images(paths[:count], stage["image_size"], scale_factor)
        normalization = "divide_by_scale_factor_then_clip_0_1"
        save_npz(Path(stage["train_path"]), images[:stage["train_samples"]], "real",
                 config["data"]["protocol"], normalization,
                 scale_factors[:stage["train_samples"]], args.stage)
        if args.stage == "stage2":
            save_npz(Path(config["data"]["test_path"]), images[stage["train_samples"]:], "real",
                     config["data"]["protocol"], normalization,
                     scale_factors[stage["train_samples"]:], "stage2")
    else:
        for index, stage in enumerate(config["stages"]):
            images = synthetic_images(stage["train_samples"], stage["image_size"], config["runtime"]["seed"] + index)
            save_npz(Path(stage["train_path"]), images, "synthetic", config["data"]["protocol"],
                     "already_0_1", np.ones(len(images), np.float32), stage["name"])
        test_size = config["stages"][-1]["image_size"]
        images = synthetic_images(config["data"]["test_samples"], test_size, config["runtime"]["seed"] + 2)
        save_npz(Path(config["data"]["test_path"]), images, "synthetic", config["data"]["protocol"],
                 "already_0_1", np.ones(len(images), np.float32), "stage2")


if __name__ == "__main__":
    main()