Code for Country-wide, high-resolution monitoring of forest browning with Sentinel-2 (ISPRS Congress 2026)
👥 Authors: Samantha Biegel, David Brüggemann, Francesco Grossi, Michele Volpi, Konrad Schindler, Benjamin Stocker
🌐 Website: samanthabiegel.github.io/forestbrowning
📄 Paper: arXiv 2604.02074
🗺️ Demo: forest-monitoring.org
This project generates Switzerland-wide, 10 m resolution NDVI anomaly maps from Sentinel-2 imagery to monitor forest browning events (drought stress, beetle outbreaks, storm damage, fire, and clear-cuts). A neural network learns the expected seasonal vegetation cycle per pixel; deviations from this expectation are flagged as anomalies.
s2-forest-browning-monitoring/
├── src/forest_browning/
│ ├── config.py # path constants (configured via env var)
│ ├── dataset.py # dataset utilities
│ ├── mlp.py # autoencoder architecture
│ ├── train.py # model training
│ ├── inference.py # anomaly score generation
│ ├── shuffle_train_data.py # pre-shuffle training data
│ ├── rechunk_output.py # reformat output for spatial access
│ └── data_processing/ # 12-step dataset creation pipeline
├── notebooks/
│ └── plot_results.ipynb # reproduces all figures → figs/
├── data/
│ └── event_polygons/ # labelled disturbance event polygons
├── checkpoints/
│ └── encoder.pt # pre-trained model checkpoint
├── tests/
├── pyproject.toml
├── uv.lock
└── README.md
- Python 3.12 or later
- uv
- GDAL CLI tools (
gdalwarp,gdaldem,gdal_calc.py) - TauDEM (
d8flowdir,aread8) with MPI
Ubuntu/Debian:
sudo apt install gdal-binmacOS:
brew install gdalVerify:
gdalwarp --versionStep 6 of the data pipeline uses TauDEM for hydrological feature computation. TauDEM is a compiled binary and requires an MPI implementation.
Ubuntu/Debian:
sudo apt install libgdal-dev libopenmpi-dev cmake git
git clone https://github.com/dtarb/TauDEM.git
cmake -S TauDEM/src -B TauDEM/build && cmake --build TauDEM/build -j4
sudo cmake --install TauDEM/buildmacOS:
brew install open-mpi cmake
git clone https://github.com/dtarb/TauDEM.git
cmake -S TauDEM/src -B TauDEM/build && cmake --build TauDEM/build -j4
sudo cmake --install TauDEM/buildVerify:
which d8flowdir && mpiexec --versiongit clone git@github.com:SamanthaBiegel/s2-forest-browning-monitoring.git
cd s2-forest-browning-monitoring
uv sync --group dev
source .venv/bin/activateInstall the pre-commit hooks:
pre-commit installBefore running any pipeline step, set the environment variable FOREST_BROWNING_DATA_DIR
to point to your local data storage:
export FOREST_BROWNING_DATA_DIR=/your/local/data/dirNote: two external datasets must be downloaded manually and placed in FOREST_BROWNING_DATA_DIR:
- Tree species map: request access via https://www.envidat.ch/#/metadata/tree-species-map-of-switzerland and place the obtained
.tifraster inFOREST_BROWNING_DATA_DIR. - Habitat map: download from https://www.envidat.ch/#/metadata/the-habitat-map-of-switzerland-v1-1 and place
habitatmap_v1_1_20241025.tifinFOREST_BROWNING_DATA_DIR.
The dataset building pipeline consists of 12 steps orchestrated by a runner module. The individual step scripts are in src/forest_browning/data_processing/ with digit prefixes (e.g., 1_extract_swisstopo_dataset.py); the runner invokes them sequentially and exits if any step fails.
Activate the virtual environment first:
source .venv/bin/activatepython -m forest_browning.data_processing.pipelineTo run one step in isolation:
python src/forest_browning/data_processing/1_extract_swisstopo_dataset.pyThe pipeline produces two Zarr datasets:
| File | Chunking | Use case |
|---|---|---|
ndvi_dataset_temporal.zarr |
(num_forest_pixels, num_timesteps) |
Training and inference |
ndvi_dataset_spatial.zarr |
(num_timesteps, num_forest_pixels) |
Fast per-day map retrieval |
A forest mask (forest_mask.npy) maps between the 1-D pixel index used in the datasets and the original 2-D spatial grid.
Activate the virtual environment first:
source .venv/bin/activateStep 1 – Pre-shuffle the training dataset:
python -m forest_browning.shuffle_train_data \
--input_zarr /path/to/ndvi_dataset_temporal.zarr \
--output_zarr /path/to/ndvi_dataset_filtered_shuffled.zarrStep 2 – Train the neural network:
python -m forest_browning.train \
--data_path /path/to/ndvi_dataset_filtered_shuffled.zarr \
--output_dir /path/to/work/dirStep 3 – Run inference to generate anomaly scores:
This step will add the anomaly scores as an additional layer to the supplied zarr file. Adapt --encoder_path path if you want to run a model trained in step 2.
python -m forest_browning.inference \
--encoder_path checkpoints/encoder.pt \
--data_path /path/to/ndvi_dataset_temporal.zarr \
--output_path /path/to/ndvi_dataset_temporal.zarrStep 4 – Rechunk output for spatial access:
python -m forest_browning.rechunk_output \
--source_zarr /path/to/ndvi_dataset_temporal.zarr \
--target_zarr /path/to/ndvi_dataset_spatial.zarrOpen and run notebooks/plot_results.ipynb. Figures are saved to figs/.
If you use this code or data in your research, please cite:
@proceedings{biegel2025forestbrowning,
title = {Country-wide, high-resolution monitoring of forest browning with {Sentinel-2}},
author = {Biegel, Samantha and Br{\"u}ggemann, David and Grossi, Francesco and Volpi, Michele and Schindler, Konrad and Stocker, Benjamin},
booktitle = {ISPRS Annals of the Photogrammetry, Remote Sensing and Spatial Information Sciences},
year = {2026},
}This project is licensed under the terms of the LICENSE file.
