EO-VAE
February 15, 2026 · View on GitHub
Earth Observation Variational AutoEncoder (EO-VAE) repository. This project focuses on training Variational AutoEncoders for remote sensing data and leveraging the latent space for downstream tasks like super-resolution.
Table of Contents
- Installation
- Stage 1: Weight Distillation
- Stage 2: VAE Finetuning
- Stage 3: Super-Resolution Training
- Evaluation
- Project Structure
Installation
Install Dependencies
Install the package and its dependencies in editable mode:
cd /path/to/eo-vae
pip install -e .
Using a pretrained checkpoint (Inference)
Use the built-in config-driven loader to avoid manual Encoder/Decoder construction.
import torch
from eo_vae.models.new_autoencoder import EOFluxVAE
model = EOFluxVAE.from_pretrained(
repo_id="nilsleh/eo-vae",
ckpt_filename="eo-vae.ckpt",
config_filename="model_config.yaml",
device="cpu",
)
# Run reconstruction / latent extraction
x = torch.randn(1, 3, 256, 256)
wvs = torch.tensor([0.665, 0.56, 0.49], dtype=torch.float32) # example for S2RGB
with torch.no_grad():
recon = model.reconstruct(x, wvs) # [B, 3, 256, 256]
z = model.encode_spatial_normalized(x, wvs) # [B, 32, 32, 32] for 256x256 input
These are the wavelengths used across modalities:
WAVELENGTHS = {
'S2RGB': [0.665, 0.56, 0.49],
'S1RTC': [5.4, 5.6],
'S2L2A': [
0.443, 0.490, 0.560, 0.665, 0.705, 0.740,
0.783, 0.842, 0.865, 1.610, 2.190, 0.945,
],
'S2L1C': [
0.443, 0.490, 0.560, 0.665, 0.705, 0.740,
0.783, 0.842, 0.865, 0.945, 1.375, 1.610, 2.190,
],
}
Data Requirements
The pipeline expects the following data structure:
/path/to/data/
├── terramesh/ # For weight distillation & VAE finetuning
│ ├── S2L2A/
│ ├── S1RTC/
│ └── ...
└── sen2naip/ # For super-resolution
├── train/
├── val/
└── test/
Update the data paths in the relevant config files before running.
Stage 1: Weight Distillation
Purpose: Distill weights from a pretrained Flux VAE into the RGB channels of the dynamic convolution layers, preparing them for multi-modality finetuning.
Prerequisites
- Pre-trained Flux2 AutoEncoder checkpoint (e.g.,
ae.safetensors) - TerraMesh dataset (S2L2A modality)
Configuration
The distillation uses the config at configs/weight_distill.yaml. Review and update:
experiment:
exp_dir: "results/exps/eo-vae/all" # Output directory for checkpoints
datamodule:
data_path: "/mnt/SSD2/nils/datasets/terramesh" # Update to your data path
batch_size: 16
num_workers: 4
Run
python weight_distill_train.py \
--config configs/weight_distill.yaml \
--teacher-ckpt /path/to/flux_ae.safetensors
Optional Arguments:
--max-steps 5000- Maximum training steps (default from config)--lr 1e-4- Learning rate--val-every 500- Validation frequency--log-every 50- Logging frequency
Output
A checkpoint saving distilled weights will be created in:
results/exps/eo-vae/all/eo-vae-distill-2-layers_MM-DD-YYYY_HH-MM-SS-ffffff/
This checkpoint path will be used as --ckpt in Stage 2.
Stage 2: VAE Finetuning
Purpose: Finetune the weight-distilled VAE on multi-modal TerraMesh data (S2L2A, S1RTC, etc.).
Prerequisites
- Distilled checkpoint from Stage 1
- TerraMesh dataset with multiple modalities
Configuration
The finetuning uses the config at configs/eo-vae.yaml. Key settings:
experiment:
exp_dir: "results/exps/eo-vae/full"
model:
_target_: eo_vae.models.new_autoencoder.EOFluxVAE
base_lr: 1e-4
final_lr: 2e-5
loss_fn:
_target_: eo_vae.models.modules.consistency_loss.EOConsistencyLoss
pixel_weight: 1.0
msssim_weight: 1.0 # MS-SSIM loss starts after 2000 steps
datamodule:
data_path: "path to terramesh data" # Update to your path
modalities: ["S2L2A"] # Can add more: ["S2L2A", "S1RTC"]
batch_size: 16
train_collate_mode: "random" # Randomly select modality per batch
val_collate_mode: "S2L2A" # Validation on S2L2A
trainer:
max_epochs: 100 # Adjust based on your needs
Run
python train.py \
--config configs/eo-vae.yaml \
--ckpt /path/to/distilled_checkpoint.pt
This will:
- Load the distilled VAE weights
- Train on the specified modalities with consistency and MS-SSIM losses
- Save checkpoints periodically
- Log progress to Weights & Biases (if configured)
Output
This checkpoint path will be used for latent encoding and super-resolution.
Stage 3: Super-Resolution Training
Purpose: Train a diffusion-based super-resolution model in the latent space of the frozen VAE.
Prerequisites
- Finetuned VAE checkpoint from Stage 2
- Sen2NAIP dataset
Step 3.1: Encode Latents
First, encode the entire dataset to latent space using the frozen VAE to speed up training:
python encode_latents.py \
--sen2naip_root /path/to/sen2naip \
--config configs/eo-vae.yaml \
--ckpt /path/to/vae_checkpoint.pt \
--output_root /path/to/latent_output \
--use_spatial_norm
Arguments:
--sen2naip_root- Path to Sen2NAIP dataset--config- VAE config file--ckpt- Trained VAE checkpoint--output_root- Directory to save encoded latents and statistics--use_spatial_norm- Apply spatial normalization to latents
Output:
- Latents saved as
.npyfiles for train/val/test splits - Statistics file:
latent_stats.json(containing mean and std for normalization)
Step 3.2: Configure Super-Resolution Training
Update the super-resolution config at configs_superres/eo_vae_latent.yaml:
experiment:
exp_dir: "results/exps/eo-vae/sr"
trainer:
max_epochs: 750
accelerator: "gpu"
devices: [0] # GPU device ID
lightning_module:
base_lr: 1e-4
final_lr: 1e-5
warmup_epochs: 10
datamodule:
latent_root: /path/to/latent_output # From Step 3.1
batch_size: 4
num_workers: 4
Step 3.3: Train Super-Resolution
python train_super_res.py --config configs_superres/eo_vae_latent.yaml
Optional Debug Mode (CPU, no logging):
python train_super_res.py --config configs_superres/eo_vae_latent.yaml --debug
Evaluation
VAE Reconstruction Evaluation
Visual Evaluation
Visualize VAE reconstructions:
python visual_eval.py \
--config configs/eo-vae.yaml \
--ckpt /path/to/vae_checkpoint.pt \
--modality S2L2A \
--output_dir results/vae_visuals
This generates visualizations comparing input and reconstructed images.
Metric Evaluation
Evaluate VAE on reconstruction metrics (MSE, SSIM):
python eval_metric_super_res.py \
--config_vae configs/eo-vae.yaml \
--ckpt_vae /path/to/vae_checkpoint.pt
Outputs metrics to console and results/sr_metrics.json.
Super-Resolution Evaluation
Metric-Based Evaluation
Evaluate super-resolution performance on test set:
python eval_metric_super_res.py \
--config configs_superres/eo_vae_latent.yaml \
--ckpt /path/to/sr_checkpoint.pt \
--split test
Metrics computed:
- PSNR (Peak Signal-to-Noise Ratio)
- SSIM (Structural Similarity Index)
- MSE (Mean Squared Error)
- SAM (Spectral Angle Mapper) - for multi-spectral data
Output saved to results/sr_metrics.json.
Visual Evaluation
Generate visual comparisons:
python eval_viz_tokenizer.py \
--config configs_superres/eo_vae_latent.yaml \
--ckpt /path/to/sr_checkpoint.pt \
--output_dir results/sr_visuals \
--num_samples 50
Generates comparison plots:
- Low-resolution input
- Super-resolved output
- High-resolution ground truth
- Difference maps
Tokenizer/Model Comparison
Compare against TerraMind tokenizer:
python evaluate_metrics_tokenizer.py \
--config configs/eo-vae.yaml \
--ckpt /path/to/vae_checkpoint.pt \
--modality S2L2A \
--tm_model_name terramind_v1_tokenizer_s2l2a
Outputs comparison table of reconstruction metrics.
Project Structure
.
├── configs/ # VAE training configs
│ ├── eo-vae.yaml # Main VAE finetuning config
│ ├── weight_distill.yaml # Weight distillation config
│ └── ...
│
├── configs_superres/ # Super-resolution configs
│ ├── eo_vae_latent.yaml # Main SR config
│ └── ...
│
├── eo_vae/
│ ├── models/
│ │ ├── autoencoder.py # VAE encoder/decoder
│ │ ├── autoencoder_flux.py # Flux-based VAE
│ │ ├── new_autoencoder.py # Multi-modal VAE (EOFluxVAE)
│ │ ├── super_res.py # Diffusion SR model
│ │ └── modules/
│ │ ├── consistency_loss.py # Multi-modal consistency loss
│ │ ├── dynamic_conv.py # Dynamic convolution layers
│ │ ├── loss_functions.py # Training losses
│ │ └── ...
│ │
│ ├── datasets/
│ │ ├── terramesh_datamodule.py # TerraMesh data loader
│ │ ├── sen2naip.py # Sen2NAIP data loader
│ │ └── ...
│ │
│ └── utils/ # Training utilities
│ ├── callbacks.py # Lightning callbacks
│ ├── image_logger.py # VAE image logging
│ └── super_res_image_logger.py # SR image logging
│
├── train.py # VAE finetuning script
├── train_super_res.py # SR training script
├── weight_distill_train.py # Weight distillation script
├── encode_latents.py # Latent encoding for SR
│
├── visual_eval.py # VAE visual evaluation
├── eval_metric_super_res.py # SR metric evaluation
├── evaluate_metrics_tokenizer.py # Model comparison
└── eval_viz_tokenizer.py # Visual comparisons
License
Apache-2.0 License
Citation
If you use this code, please cite:
@article{eo-vae,
title={EO-VAE: Towards A Multi-sensor Tokenizer for Earth Observation Data},
author={Lehmann, Nils and Wang, Yi and Xiong, Zhitong and Zhu, Xiaoxiang},
journal={arXiv preprint arXiv:2602.12177},
year={2026}
}