File size: 11,615 Bytes
4eacf82
90dd565
4eacf82
 
 
90dd565
 
 
 
 
 
 
 
4eacf82
bbee304
c52e903
4eacf82
 
90dd565
4eacf82
11dd06e
 
8750883
90dd565
 
 
26fa092
90dd565
 
bbee304
 
26fa092
4eacf82
 
90dd565
702c6ed
 
6c596a6
90dd565
 
6c596a6
 
 
90dd565
6c596a6
702c6ed
90dd565
 
 
6c596a6
90dd565
 
 
 
 
 
 
702c6ed
90dd565
 
 
299c982
90dd565
702c6ed
 
 
 
90dd565
 
 
 
 
 
 
c0d891a
 
 
90dd565
 
 
c0d891a
 
 
 
90dd565
 
 
 
 
 
26fa092
90dd565
 
 
6c596a6
702c6ed
 
90dd565
 
 
 
 
 
 
 
 
702c6ed
 
 
90dd565
bb04c88
90dd565
c52e903
8750883
11dd06e
 
baf5f42
90dd565
baf5f42
 
 
 
 
 
 
 
 
90dd565
baf5f42
90dd565
baf5f42
90dd565
 
 
26fa092
90dd565
 
 
 
 
 
 
 
 
 
 
baf5f42
 
90dd565
baf5f42
26fa092
90dd565
baf5f42
90dd565
 
 
26fa092
90dd565
 
 
 
 
 
 
 
 
 
 
baf5f42
 
90dd565
baf5f42
26fa092
90dd565
baf5f42
90dd565
 
 
26fa092
90dd565
 
 
 
 
 
 
 
 
 
 
baf5f42
 
90dd565
 
baf5f42
90dd565
 
baf5f42
90dd565
 
 
26fa092
90dd565
 
 
26fa092
90dd565
 
26fa092
90dd565
 
 
 
 
baf5f42
90dd565
 
 
 
 
 
 
 
26fa092
90dd565
 
26fa092
90dd565
 
26fa092
90dd565
 
 
 
 
 
baf5f42
bb04c88
baf5f42
 
 
 
90dd565
baf5f42
 
c52e903
baf5f42
 
c52e903
 
baf5f42
 
 
 
 
 
bb04c88
90dd565
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
---
license: apache-2.0
pipeline_tag: image-segmentation
tags:
- pytorch
- pytorch lightning
- self-supervised learning
- masked autoencoders
- transformers
- remote sensing
- earth observation
- multimodal
- multitemporal
library_name: pytorch
datasets:
- ignf/flair-hub
---

## Download

⚖️ [**Model weights**](./MAESTRO_FLAIR-HUB_base/checkpoints/pretrain-epoch=99.ckpt) <br>
⚙️ [**Model configuration**](./MAESTRO_FLAIR-HUB_base/.hydra/config_resolved.yaml) <br>
📂 [**Dataset splits**](https://huggingface.co/IGNF/MAESTRO_FLAIR-HUB_base/tree/main/dataset_splits) <br>

## Abstract

**MAESTRO** is a tailored adaptation of the Masked Autoencoder (MAE) that effectively orchestrates the use of multimodal, multitemporal, and multispectral Earth Observation (EO) data. Evaluated on four EO datasets, MAESTRO sets a new state-of-the-art on tasks that strongly rely on multitemporal dynamics, while remaining competitive on tasks dominated by a single monotemporal modality.

MAESTRO's contributions are as follows:
- **Extensive benchmarking of multimodal and multitemporal SSL:** Impact evaluation of various fusion strategies for multimodal and multitemporal SSL.
- **Patch-group-wise normalization:** Novel normalization scheme that normalizes reconstruction targets patch-wise within groups of highly correlated spectral bands.
- **MAESTRO:** Novel adaptation of the MAE that combines optimized fusion strategies with patch-group-wise normalization.

<div style="position: relative; text-align: center;">
    <img src="./media/Maestro_Overview.png" style="width: 100%; display: block; margin: 0 auto;"/>
</div>


📃 **Paper:** https://arxiv.org/abs/2508.10894 <br>
💻 **Code repository:** https://github.com/IGNF/MAESTRO <br>



## Pre-training


This model is pre-trained on [FLAIR-HUB](https://huggingface.co/datasets/IGNF/FLAIR-HUB).

FLAIR-HUB contains 241,100 tiles of size 102.4 × 102.4 m, covering a total area of 2,528 km² across France.

We retain six distinct modalities:
  - Aerial imagery RGB + NIR (0.2 m resolution)
  - DEM/DSM imagery (0.2 m resolution)
  - SPOT 6–7 imagery
  - Sentinel-1 time series in ascending orbit
  - Sentinel-1 time series in descending orbit
  - Sentinel-2 time series

Below is the reconstruction loss during pre-training on the combined training and validation ensembles, using patch-group-wise normalization and modality-weighted averaging proportional to token counts.

<div style="position: relative; text-align: center;">
    <img src="./media/train_rec_loss_maeflair.png" style="width: 100%; display: block; margin: 0 auto;"/>
</div>

<hr>


## Fine-tuning

For optimal fine-tuning results with this model:
- Ensure that patch sizes and channels match between pre-training and fine-tuning for each modality:
  - Modality "aerial":
    - Patch size: 16
    - Channels: NIR, RED, GREEN, BLUE
  - Modality "dem":
    - Patch size: 32
    - Channels: DEM, DSM
  - Modality "spot":
    - Patch size: 16
    - Channels: RED, GREEN, BLUE
  - Modality "s1_asc":
    - Patch size: 2
    - Channels: VV, VH
  - Modality "s1_des":
    - Patch size: 2
    - Channels: VV, VH
  - Modality "s2":
    - Patch size: 2
    - Channels: B02, B03, B04, B05, B06, B07, B08, B8A, B11, B12
- Use fixed cross-dataset grids for positional encodings proportional to ground sampling distance: `grid_pos_enc` ≈ 1.6 * `crop_meters`

Note that modality names must match between pre-training and fine-tuning. 

Below are cross-dataset evaluation results obtained with these guidelines on TreeSatAI-TS and PASTIS-HD.

<p align="center">

| Model              | Pre-training dataset | TreeSatAI-TS | PASTIS-HD | 
|--------------------|-----------------------|--------------|-----------|
| MAESTRO (ours)     | FLAIR-HUB             | **79.6**     | **68.0**  |
| DINO-v2            | LVD-142M              | 76.7         | 64.4      |
| DINO-v2 sat.       | Maxar Vivid2          | 76.3         | 64.0      |
| DOFA               | DOFA MM               | 76.0         | 62.9      |
| CROMA              | SSL4EO                | 70.5         | 65.0      |
| Prithvi-EO-2.0     | HLS                   | 75.6         | 66.2      |
| SatMAE             | fMoW RGB+S            | 76.9         | 66.6      |
</p>


## 🚀 Getting started

Prerequisites: 
- Clone MAESTRO's [code repository](https://github.com/ignf/maestro)
- Fetch [Dataset splits](https://huggingface.co/IGNF/MAESTRO_FLAIR-HUB_base/tree/main/dataset_splits) and move them to each dataset directory
- Fetch [model weights](./MAESTRO_FLAIR-HUB_base/checkpoints/pretrain-epoch=99.ckpt) and move them into `/path/to/experiments/MAESTRO_FLAIR-HUB_base/checkpoints/`
- Fetch [model configuration](./MAESTRO_FLAIR-HUB_base/.hydra/config_resolved.yaml) and move it into `/path/to/experiments/MAESTRO_FLAIR-HUB_base/.hydra/`

The module is setup with [Poetry](https://python-poetry.org/).

```bash
# 1. Change directory
cd MAESTRO

# 2. Install dependencies with Poetry
poetry install
```

Pre-training on FLAIR-HUB is performed using:
```bash
# batch size 9 on 4 nodes with 4 GPUs per node
poetry run python main.py \
  model.model=mae model.model_size=medium \
  model.fusion_mode=group model.inter_depth=3 \
  opt_pretrain.epochs=100 opt_probe.epochs=0 opt_finetune.epochs=0 \
  opt_pretrain.batch_size=9 trainer.num_nodes=4 \
  datasets.name_dataset=flair \
  datasets.flair.filter_inputs=[aerial,dem,spot,s2,s1_asc,s1_des] \
  datasets.flair.crop_meters=102.4 datasets.flair.grid_pos_enc=160 \
  datasets.flair.aerial.image_size=512 datasets.flair.aerial.patch_size.mae=16 \
  datasets.flair.dem.image_size=512 datasets.flair.dem.patch_size.mae=32 \
  datasets.flair.spot.image_size=128 datasets.flair.spot.patch_size.mae=16 datasets.flair.spot.bands=3 \
  datasets.flair.s2.image_size=10 datasets.flair.s2.patch_size.mae=2 \
  datasets.flair.s1_asc.image_size=10 datasets.flair.s1_asc.patch_size.mae=2 \
  datasets.flair.s1_des.image_size=10 datasets.flair.s1_des.patch_size.mae=2 \
  datasets.root_dir=/path/to/dataset/dir datasets.flair.csv_dir=/path/to/dataset/dir/FLAIR-HUB datasets.flair.rel_dir=FLAIR-HUB \
  run.exp_dir=/path/to/experiments/dir run.exp_name=MAESTRO_FLAIR-HUB_base \
```

Fine-tuning on TreeSatAI-TS:
```bash
# batch size 24 on 1 node with 4 GPUs per node
# load pre-trained model "MAESTRO_FLAIR-HUB_base"
poetry run python main.py \
  model.model=mae model.model_size=medium \
  model.fusion_mode=group model.inter_depth=3 \
  opt_pretrain.epochs=0 opt_probe.epochs=10 opt_finetune.epochs=50 \
  opt_probe.batch_size=24 opt_finetune.batch_size=24 trainer.num_nodes=1 \
  opt_finetune.monitor=treesat_mlc_thresh/weighted_f1_val \
  datasets.name_dataset=treesatai_ts \
  datasets.treesatai_ts.filter_inputs=[aerial,s2,s1_asc,s1_des] \
  datasets.treesatai_ts.crop_meters=60 datasets.treesatai_ts.grid_pos_enc=96 \
  datasets.treesatai_ts.aerial.image_size=240 datasets.treesatai_ts.aerial.patch_size.mae=16 \
  datasets.treesatai_ts.s2.image_size=6 datasets.treesatai_ts.s2.patch_size.mae=2 \
  datasets.treesatai_ts.s1_asc.image_size=6 datasets.treesatai_ts.s1_asc.patch_size.mae=2 \
  datasets.treesatai_ts.s1_des.image_size=6 datasets.treesatai_ts.s1_des.patch_size.mae=2 \
  datasets.root_dir=/path/to/dataset/dir datasets.treesatai_ts.rel_dir=TreeSatAI-TS \
  run.exp_dir=/path/to/experiments/dir run.exp_name=MAESTRO_FLAIR-HUB-x-TSAI-TS_base \
  run.load_name=MAESTRO_FLAIR-HUB_base
```

Fine-tuning on PASTIS-HD:
```bash
# batch size 12 on 1 node with 4 GPUs per node
# load pre-trained model "MAESTRO_FLAIR-HUB_base"
poetry run python main.py \
  model.model=mae model.model_size=medium \
  model.fusion_mode=group model.inter_depth=3 \
  opt_pretrain.epochs=0 opt_probe.epochs=10 opt_finetune.epochs=50 \
  opt_probe.batch_size=12 opt_finetune.batch_size=12 trainer.num_nodes=1 \
  opt_finetune.monitor=pastis_seg/average_iou_val \
  datasets.name_dataset=pastis_hd \
  datasets.pastis_hd.filter_inputs=[spot,s2,s1_asc,s1_des] \
  datasets.pastis_hd.crop_meters=160 datasets.pastis_hd.grid_pos_enc=256 datasets.pastis_hd.repeats=8 \
  datasets.pastis_hd.spot.image_size=160 datasets.pastis_hd.spot.patch_size.mae=16 \
  datasets.pastis_hd.s2.image_size=16 datasets.pastis_hd.s2.patch_size.mae=2 \
  datasets.pastis_hd.s1_asc.image_size=16 datasets.pastis_hd.s1_asc.patch_size.mae=2 \
  datasets.pastis_hd.s1_des.image_size=16 datasets.pastis_hd.s1_des.patch_size.mae=2 \
  datasets.root_dir=/path/to/dataset/dir datasets.pastis_hd.rel_dir=PASTIS-HD \
  run.exp_dir=/path/to/experiments/dir run.exp_name=MAESTRO_FLAIR-HUB-x-PASTIS-HD_base \
  run.load_name=MAESTRO_FLAIR-HUB_base
```


Fine-tuning on FLAIR#2:
```bash
# batch size 6 on 2 nodes with 4 GPUs per node
# load pre-trained model "MAESTRO_FLAIR-HUB_base"
poetry run python main.py \
  model.model=mae model.model_size=medium \
  model.fusion_mode=group model.inter_depth=3 \
  opt_pretrain.epochs=0 opt_probe.epochs=15 opt_finetune.epochs=100 \
  opt_probe.batch_size=6 opt_finetune.batch_size=6 trainer.num_nodes=2 \
  opt_finetune.monitor=cosia/average_iou_val \
  datasets.name_dataset=flair \
  datasets.flair.version=flair2 \
  datasets.flair.filter_inputs=[aerial,dem,s2] \
  datasets.flair.crop_meters=102.4 datasets.flair.grid_pos_enc=160 \
  datasets.flair.aerial.image_size=512 datasets.flair.aerial.patch_size.mae=16 \
  datasets.flair.dem.image_size=512 datasets.flair.dem.patch_size.mae=32 \
  datasets.flair.s2.image_size=10 datasets.flair.s2.patch_size.mae=2 \
  datasets.root_dir=/path/to/dataset/dir datasets.flair.csv_dir=/path/to/dataset/dir/FLAIR-HUB datasets.flair.rel_dir=FLAIR-HUB \
  run.exp_dir=/path/to/experiments/dir run.exp_name=MAESTRO_FLAIR-HUB-x-FLAIR2_base \
  run.load_name=MAESTRO_FLAIR-HUB_base
```

Fine-tuning on FLAIR-HUB:
```bash
# batch size 6 on 4 nodes with 4 GPUs per node
# load pre-trained model "MAESTRO_FLAIR-HUB_base"
poetry run python main.py \
  model.model=mae model.model_size=medium \
  model.fusion_mode=group model.inter_depth=3 \
  opt_pretrain.epochs=0 opt_probe.epochs=15 opt_finetune.epochs=100 \
  opt_probe.batch_size=6 opt_finetune.batch_size=6 trainer.num_nodes=4 \
  opt_finetune.monitor=cosia/average_iou_val \
  datasets.name_dataset=flair \
  datasets.flair.filter_inputs=[aerial,dem,s2,s1_asc,s1_des] \
  datasets.flair.crop_meters=102.4 datasets.flair.grid_pos_enc=160 \
  datasets.flair.aerial.image_size=512 datasets.flair.aerial.patch_size.mae=16 \
  datasets.flair.dem.image_size=512 datasets.flair.dem.patch_size.mae=32 \
  datasets.flair.s2.image_size=10 datasets.flair.s2.patch_size.mae=2 \
  datasets.flair.s1_asc.image_size=10 datasets.flair.s1_asc.patch_size.mae=2 \
  datasets.flair.s1_des.image_size=10 datasets.flair.s1_des.patch_size.mae=2 \
  datasets.root_dir=/path/to/dataset/dir datasets.flair.csv_dir=/path/to/dataset/dir/FLAIR-HUB datasets.flair.rel_dir=FLAIR-HUB \
  run.exp_dir=/path/to/experiments/dir run.exp_name=MAESTRO_FLAIR-HUB-x-FLAIR-HUB_base \
  run.load_name=MAESTRO_FLAIR-HUB_base
```

<hr>

## Reference

If you use this model, please cite:

```bibtex
@inproceedings{labatie2026maestro,
  title={MAESTRO: Masked AutoEncoders for Multimodal, Multitemporal, and Multispectral Earth Observation Data},
  author={Labatie, Antoine and Vaccaro, Michael and Lardiere, Nina and Garioud, Anatol and Gonthier, Nicolas},
  booktitle={Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (WACV)},
  year={2026}
}
```

<hr>

## Acknowledgement

The experiments in the paper were conducted using HPC/AI resources from GENCI-IDRIS (allocations A0181013803, A0161013803, AD010114597R1, and AD011014690R1).