Mike0021's picture
init: terrain diffusion demo Space
0edffc2 verified
Raw
History Blame Contribute Delete
6.16 kB
import os
import random
import json
import numpy as np
from pyfastnoiselite.pyfastnoiselite import FastNoiseLite, NoiseType, FractalType
import torch
from terrain_diffusion.inference.perlin_transform import build_quantiles, transform_perlin
STATS_CACHE_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "data", "global", "synthetic_map_stats.json")
def _load_stats_cache():
if not os.path.exists(STATS_CACHE_PATH):
return None
try:
with open(STATS_CACHE_PATH, "r", encoding="utf-8") as cache_file:
data = json.load(cache_file)
noise_quantile_tables = data["noise_quantile_tables"]
data_quantile_tables = data["data_quantile_tables"]
stats = {
"a_temp_std": float(data["a_temp_std"]),
"b_temp_std": float(data["b_temp_std"]),
"temp_std_p1": float(data["temp_std_p1"]),
"temp_std_p99": float(data["temp_std_p99"]),
}
for index, quantile_table in enumerate(noise_quantile_tables):
stats[f"noise_quantiles_{index}"] = np.asarray(quantile_table, dtype=np.float64)
for index, quantile_table in enumerate(data_quantile_tables):
stats[f"base_image_quantiles_{index}"] = np.asarray(quantile_table, dtype=np.float64)
print("Synthetic map stats cache hit.")
return stats
except Exception:
print("Synthetic map stats cache unreadable. Recomputing.")
return None
def make_synthetic_map_factory(frequency_mult=[1.0, 1.0, 1.0, 1.0, 1.0], seed=None, drop_water_pct=0.0):
actual_seeds = [((seed or random.randint(0, 2**30)) + i + 1) & 0x7FFFFFFF for i in range(5)]
stats = _load_stats_cache()
if stats is None:
raise RuntimeError(
f"Synthetic map stats not found at {STATS_CACHE_PATH}. "
"This file must be pre-computed and included in the Space."
)
a_temp_std = float(stats['a_temp_std'])
b_temp_std = float(stats['b_temp_std'])
temp_std_p1 = float(stats['temp_std_p1'])
temp_std_p99 = float(stats['temp_std_p99'])
def build_synthetic_map(frequency, octaves, lacunarity, gain, seed, noise_quantiles, base_image_quantiles):
noise = FastNoiseLite(seed=seed)
noise.noise_type = NoiseType.NoiseType_Perlin
noise.frequency = frequency
noise.fractal_type = FractalType.FractalType_FBm
noise.fractal_octaves = octaves
noise.fractal_lacunarity = lacunarity
noise.fractal_gain = gain
transform_fn = lambda x: transform_perlin(x, noise_quantiles, base_image_quantiles)
return noise, transform_fn
def sample_synthetic_map(noise, transform_fn, i1, j1, i2, j2):
x = np.arange(i1, i2, dtype=np.float32)
y = np.arange(j1, j2, dtype=np.float32)
xx, yy = np.meshgrid(x, y)
Xs = xx.flatten()
Ys = yy.flatten()
coords = np.array([Xs, Ys], dtype=np.float32)
noise_values = noise.gen_from_coords(coords)
transformed_values = transform_fn(noise_values)
return transformed_values.reshape(i2 - i1, j2 - j1)
map_configs = [
(0.05 * frequency_mult[0], 4, 2.0, 0.5),
(0.05 * frequency_mult[1], 2, 2.0, 0.5),
(0.05 * frequency_mult[2], 4, 2.0, 0.5),
(0.05 * frequency_mult[3], 4, 2.0, 0.5),
(0.05 * frequency_mult[4], 4, 2.0, 0.5),
]
synthetic_params = [
build_synthetic_map(*cfg, actual_seeds[i], stats[f'noise_quantiles_{i}'], stats[f'base_image_quantiles_{i}'])
for i, cfg in enumerate(map_configs)
]
synthetic_elev_params, synthetic_temp_params, synthetic_temp_std_params, synthetic_precip_params, synthetic_precip_std_params = synthetic_params
def finalize_synthetic_map(raw_map):
synthetic_elev = np.asarray(raw_map[0], dtype=np.float32)
synthetic_temp = np.asarray(raw_map[1], dtype=np.float32)
synthetic_temp_std = np.asarray(raw_map[2], dtype=np.float32)
synthetic_precip = np.asarray(raw_map[3], dtype=np.float32)
synthetic_precip_std = np.asarray(raw_map[4], dtype=np.float32)
lapse_rate = (-6.5 + 0.0015 * synthetic_precip).clip(-9.8, -4.0) / 1000
synthetic_temp = synthetic_temp + lapse_rate * np.maximum(0, synthetic_elev)
synthetic_temp = np.clip(synthetic_temp, -10, 40)
synthetic_temp = np.where(synthetic_temp > 20, synthetic_temp, (synthetic_temp - 20) * 1.25 + 20)
t = (synthetic_temp_std - temp_std_p1) / (temp_std_p99 - temp_std_p1)
baseline = np.maximum(temp_std_p1, -(a_temp_std * synthetic_temp + b_temp_std))
synthetic_temp_std = t * (temp_std_p99 - baseline) + baseline
synthetic_temp_std = synthetic_temp_std + (a_temp_std * synthetic_temp + b_temp_std)
synthetic_temp_std = np.maximum(synthetic_temp_std, 20)
synthetic_precip_std = synthetic_precip_std * np.maximum(0, (185 - 0.04111 * synthetic_precip) / 185)
return np.stack([synthetic_elev, synthetic_temp, synthetic_temp_std, synthetic_precip, synthetic_precip_std], axis=0)
def sample_raw_synthetic_map(i1, j1, i2, j2):
synthetic_elev = sample_synthetic_map(*synthetic_elev_params, i1, j1, i2, j2)
synthetic_temp = sample_synthetic_map(*synthetic_temp_params, i1, j1, i2, j2)
synthetic_temp_std = sample_synthetic_map(*synthetic_temp_std_params, i1, j1, i2, j2)
synthetic_precip = sample_synthetic_map(*synthetic_precip_params, i1, j1, i2, j2)
synthetic_precip_std = sample_synthetic_map(*synthetic_precip_std_params, i1, j1, i2, j2)
return np.stack([synthetic_elev, synthetic_temp, synthetic_temp_std, synthetic_precip, synthetic_precip_std], axis=0)
def sample_full_synthetic_map(i1, j1, i2, j2):
synthetic_map = finalize_synthetic_map(sample_raw_synthetic_map(i1, j1, i2, j2))
synthetic_map[0] = np.sign(synthetic_map[0]) * np.sqrt(np.abs(synthetic_map[0]))
return torch.from_numpy(synthetic_map).float()
sample_full_synthetic_map.sample_raw = sample_raw_synthetic_map
sample_full_synthetic_map.finalize = finalize_synthetic_map
return sample_full_synthetic_map