PSRGAN v1.0
Browse files- README.md +1 -1
- degradation.py +90 -0
README.md
CHANGED
|
@@ -109,7 +109,7 @@ The model expects Sentinel-2 L2A RGB (B04, B03, B02) after a per-image 1 to 99%
|
|
| 109 |
| `psrgan_x4_swag.safetensors` | SWAG mean and variance (32 snapshots) |
|
| 110 |
| `config.json`, `psrgan.py` | model hyperparameters and code |
|
| 111 |
| `eval/metrics.json` | every number on this page |
|
| 112 |
-
| `
|
| 113 |
|
| 114 |
# Evaluation
|
| 115 |
|
|
|
|
| 109 |
| `psrgan_x4_swag.safetensors` | SWAG mean and variance (32 snapshots) |
|
| 110 |
| `config.json`, `psrgan.py` | model hyperparameters and code |
|
| 111 |
| `eval/metrics.json` | every number on this page |
|
| 112 |
+
| `degradation.py` | randomized degradation that made the LR images of the dataset |
|
| 113 |
|
| 114 |
# Evaluation
|
| 115 |
|
degradation.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Randomized degradation that made the LR images of the PSRGAN Sentinel-2 dataset.
|
| 2 |
+
|
| 3 |
+
Each HR image gets one random blur (Gaussian, horizontal or vertical motion, defocus)
|
| 4 |
+
and is then downsampled 4x with a random interpolation (bicubic, bilinear, area).
|
| 5 |
+
|
| 6 |
+
python degradation.py hr_dir lr_dir --seed 0
|
| 7 |
+
|
| 8 |
+
Writes one LR PNG per HR image and degradations.csv with the drawn parameters.
|
| 9 |
+
"""
|
| 10 |
+
import argparse
|
| 11 |
+
import csv
|
| 12 |
+
import random
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
|
| 15 |
+
import cv2
|
| 16 |
+
import numpy as np
|
| 17 |
+
|
| 18 |
+
INTERP_NAMES = {cv2.INTER_CUBIC: "bicubic", cv2.INTER_LINEAR: "bilinear", cv2.INTER_AREA: "area"}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def degrade(hr, scale=4, rng=random):
|
| 22 |
+
"""hr: (H, W, 3) float32 in [0, 1]. Returns the LR image and the drawn parameters."""
|
| 23 |
+
h, w = hr.shape[:2]
|
| 24 |
+
filter_type = rng.choice(["gaussian", "motion", "defocus"])
|
| 25 |
+
sigma = 0.0
|
| 26 |
+
|
| 27 |
+
if filter_type == "gaussian":
|
| 28 |
+
# Isotropic Gaussian blur
|
| 29 |
+
k = rng.choice([3, 5, 7])
|
| 30 |
+
sigma = rng.uniform(0.5, 1.5)
|
| 31 |
+
blurred = cv2.GaussianBlur(hr, (k, k), sigmaX=sigma)
|
| 32 |
+
|
| 33 |
+
elif filter_type == "motion":
|
| 34 |
+
# Line kernel, horizontal or vertical
|
| 35 |
+
k = rng.choice([5, 7, 9])
|
| 36 |
+
kernel = np.zeros((k, k), np.float32)
|
| 37 |
+
if rng.random() < 0.5:
|
| 38 |
+
kernel[k // 2, :] = 1.0
|
| 39 |
+
filter_type = "motion_h"
|
| 40 |
+
else:
|
| 41 |
+
kernel[:, k // 2] = 1.0
|
| 42 |
+
filter_type = "motion_v"
|
| 43 |
+
blurred = cv2.filter2D(hr, -1, kernel / k)
|
| 44 |
+
|
| 45 |
+
else:
|
| 46 |
+
# Defocus as a disk kernel
|
| 47 |
+
k = rng.choice([5, 7, 9])
|
| 48 |
+
c = k // 2
|
| 49 |
+
yy, xx = np.mgrid[:k, :k]
|
| 50 |
+
kernel = ((yy - c) ** 2 + (xx - c) ** 2 <= c ** 2).astype(np.float32)
|
| 51 |
+
blurred = cv2.filter2D(hr, -1, kernel / kernel.sum())
|
| 52 |
+
|
| 53 |
+
interpolation = rng.choice([cv2.INTER_CUBIC, cv2.INTER_LINEAR, cv2.INTER_AREA])
|
| 54 |
+
lr = cv2.resize(blurred, (w // scale, h // scale), interpolation=interpolation)
|
| 55 |
+
return lr, {"blur": filter_type, "blur_kernel": k, "blur_sigma": sigma,
|
| 56 |
+
"interpolation": INTERP_NAMES[interpolation]}
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def main():
|
| 60 |
+
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
| 61 |
+
p.add_argument("hr_dir")
|
| 62 |
+
p.add_argument("lr_dir")
|
| 63 |
+
p.add_argument("--scale", type=int, default=4)
|
| 64 |
+
p.add_argument("--seed", type=int, default=None)
|
| 65 |
+
args = p.parse_args()
|
| 66 |
+
|
| 67 |
+
rng = random.Random(args.seed)
|
| 68 |
+
out = Path(args.lr_dir)
|
| 69 |
+
out.mkdir(parents=True, exist_ok=True)
|
| 70 |
+
rows = []
|
| 71 |
+
for path in sorted(Path(args.hr_dir).iterdir()):
|
| 72 |
+
img = cv2.imread(str(path), cv2.IMREAD_COLOR)
|
| 73 |
+
if img is None:
|
| 74 |
+
continue
|
| 75 |
+
# BGR stays BGR, the filters act per channel
|
| 76 |
+
hr = img.astype(np.float32) / 255
|
| 77 |
+
lr, params = degrade(hr, args.scale, rng)
|
| 78 |
+
lr = np.round(np.clip(lr, 0, 1) * 255).astype(np.uint8)
|
| 79 |
+
cv2.imwrite(str(out / f"{path.stem}.png"), lr)
|
| 80 |
+
rows.append({"file": path.name, **params})
|
| 81 |
+
|
| 82 |
+
with open(out / "degradations.csv", "w", newline="") as f:
|
| 83 |
+
w = csv.DictWriter(f, fieldnames=["file", "blur", "blur_kernel", "blur_sigma", "interpolation"])
|
| 84 |
+
w.writeheader()
|
| 85 |
+
w.writerows(rows)
|
| 86 |
+
print(f"{len(rows)} LR images in {out}")
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
if __name__ == "__main__":
|
| 90 |
+
main()
|