XXV ISPRS CONGRESS 2026 | NTUA Remote Sensing Lab
Tilemachos Moumouris, Vasileios Tsironis, Athena Psalta, Konstantinos Karantzalos
National Technical University of Athens, Remote Sensing Lab
This repository contains the code for precise temporal localization of mowing events in dense HLS satellite time series. Rather than classifying how many mowing events occur per season (frequency detection), we recover the exact Day of Year (DOY) of each event — a finer-grained task that is directly actionable for CAP compliance verification.
We evaluate the Prithvi-EO-2.0 family of foundation models (Tiny, 100M, 300M, 600M) as frozen encoders paired with a lightweight MLP localization head, and compare against a 3D CNN baseline. A sliding window strategy processes the full growing season with tunable temporal resolution.
Key results:
- 3D CNN baseline achieves the highest detection F1 of 83.63%
- Prithvi-EO-2.0 600M achieves the best temporal precision: MTE = 0.83 timesteps (~7 days), a 33% reduction over the CNN baseline
- A consistent detection–localization trade-off emerges across all architectures
We contribute 451 newly annotated grassland parcels in Central Thessaly, Greece, each labeled with per-event DOY mowing timestamps. This extends the benchmark from our previous work beyond frequency classification to precise per-event temporal localization — the first publicly available resource of this kind for the Mediterranean region.
Data source: NASA Harmonized Landsat Sentinel-2 (HLS), 6 spectral bands (Blue, Green, Red, NIR, SWIR-1, SWIR-2), 27 timesteps at 30m resolution covering April–November 2021, max 20% cloud cover.
| Events per parcel | Count | % |
|---|---|---|
| 1 | 27 | 6.0% |
| 2 | 58 | 12.9% |
| 3 | 155 | 34.4% |
| 4 | 169 | 37.5% |
| 5 | 42 | 9.3% |
| Total | 451 |
├── train.py # Training entry point
├── train_configs.py # All hyperparameters
├── requirements.txt
├── Dockerfile
│
├── Data/
│ └── Pytorch_Dataset.py # MowingParcelDataset, collate_fn
│
├── Preprocess/
│ └── preprocessing.py # HLSPreprocessor (normalization + interpolation)
│
├── Model/
│ ├── model.py # PrithviEncoder + MowingLocalizationModel
│ ├── heads.py # Pluggable classification heads (MLP)
│ ├── loss.py # FocalLoss
│ └── trainer_scaffold.py # Training loop, checkpointing, early stopping
│
├── Evaluation/
│ └── metrics.py # Tolerant F1, MTE
│
└── dataset_extracted_full/
Architecture. A frozen Prithvi-EO-2.0 encoder processes HLS time series via a sliding window of size W and stride S, producing per-timestep feature vectors (B, T, D). A lightweight MLP head maps these to per-timestep binary logits (B, T).
Sliding window. Window start indices are generated as {0, S, 2S, …} with a final window at T−W to guarantee full coverage. Features from overlapping windows are averaged. Window sizes W=3 and W=5 were evaluated.
Training. Only the MLP head is trained; the encoder is kept frozen throughout. Focal loss (α=0.75, γ=2.0) handles class imbalance. Early stopping is monitored on validation tolerant-F1.
Evaluation metrics:
- F1 (tolerant): a prediction counts as TP if it falls within ±tolerance timesteps of a ground-truth event
- MTE (Mean Temporal Error): mean absolute timestep offset between predicted and ground-truth event positions — the primary cross-model localization metric
| Model | F1 (%) | Window | Tolerance | MTE |
|---|---|---|---|---|
| 3D CNN (baseline) | 79.37 | 3 | 1 | 1.60 |
| 3D CNN (baseline) | 83.63 | 3 | 2 | 1.24 |
| Prithvi-EO-2.0 Tiny | 71.08 | 3 | 1 | 2.27 |
| Prithvi-EO-2.0 100M | 77.15 | 3 | 1 | 0.98 |
| Prithvi-EO-2.0 300M | 71.56 | 3 | 1 | 0.86 |
| Prithvi-EO-2.0 600M | 75.00 | 3 | 1 | 0.83 |
Note: models at different tolerances should not be compared on F1 alone. Use MTE for cross-condition localization comparison.
git clone https://github.com/rslab-ntua/mowing-event-localization.git
cd mowing-event-localization
pip install -r requirements.txtOr with Docker:
docker build -t mowing-localization .
docker run --gpus all mowing-localizationTerraTorch (required for Prithvi-EO-2.0) will automatically download pretrained weights on first run.
All hyperparameters are controlled via train_configs.py. To train:
python train.pyKey config options:
# Model
"head_type": "mlp" # classification head
"num_frames": 5 # sliding window size W
"window_stride": 1 # stride S
"freeze_encoder": True # keep Prithvi weights frozen
# Training
"learning_rate": 1e-3
"max_epochs": 100
"patience": 20
"use_focal_loss": True
# Evaluation
"pred_threshold": 0.5
"tolerance_timesteps": 3 # ±S for tolerant F1Training progress and metrics are logged to MLflow. View runs with:
mlflow uiCheckpoints are saved to checkpoints/best.pth and checkpoints/latest.pth.
Citation will be added after official publication.
@Article{isprs-archives-XLVIII-M-7-2025-43-2025,
AUTHOR = {Moumouris, T. and Tsironis, V. and Psalta, A. and Karantzalos, K.},
TITLE = {Large Scale Mowing Event Detection on Dense Time Series Data Using Deep Learning Methods and Knowledge Distillation},
JOURNAL = {The International Archives of the Photogrammetry, Remote Sensing and Spatial Information Sciences},
VOLUME = {XLVIII-M-7-2025},
YEAR = {2025},
PAGES = {43--48},
URL = {https://isprs-archives.copernicus.org/articles/XLVIII-M-7-2025/43/2025/},
DOI = {10.5194/isprs-archives-XLVIII-M-7-2025-43-2025}
}This project is released under the GNU General Public License v3.0. The dataset is released for research use only.