This is the official repository of our paper:
Estimating Individual Tree Height and Species from UAV Imagery
Authors: Jannik Endres, Etienne Laliberté, David Rolnick, Arthur Ouaknine
TL;DR: BIRCH-Trees is the first benchmark for joint individual tree height estimation and species identification from UAV images. Our model, DINOvTree, leverages a VFM to extract features and predicts the height and species of the center tree in the input image with two separate heads.
- 26/03/2026: Our paper is available on arXiv.
A shared VFM (blue) extracts features from an RGB image. In the height estimation head (orange), a learnable height query cross-attends to adapted patch tokens to predict
This project requires Python 3.11. We use Hydra for configuration management and Weights & Biases for comprehensive experiment tracking and visualization.
First, clone the repository and navigate into it:
git clone https://github.com/RolnickLab/DINOvTree.git
cd DINOvTreeWe use uv for package management. You can install uv like this:
pip install pipx
pipx ensurepath
pipx install uvNext, create the virtual environment and install the dependencies:
uv venv -p 3.11
source .venv/bin/activate
uv sync
uv pip install -e ./external/facebookresearch_dinov3
uv pip install -e ./external/hugobaudchon_geodatasetIf you plan to extend the code, install pre-commit to ensure consistent formatting:
uv run pre-commit installAlternative: Standard pip installation
If you prefer not to use uv, you can create a standard virtual environment and install dependencies via pip:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e ./external/facebookresearch_dinov3
pip install -e ./external/hugobaudchon_geodataset
# Optional: pre-commit installation
pre-commit installDownload the BIRCH-Trees benchmark and store it at a location of your choice, e.g: ./birch-trees.
Note: If you choose a location other than ./birch-trees, you will need to update the paths in the configuration files located in ./configs/dataset/.
Once downloaded, unzip the datasets:
unzip "./birch-trees/datasets/*.zip" -d ./birch-trees/datasets/
rm -r ./birch-trees/datasets/*.zipRequest access to the DINOv3 weights here. Once you have your links, create a file named dinov3_urls.json in the DINOv3 core directory (src/models/dinov3_model/core/) and map the model versions to your specific URLs.
For example:
{
"dinov3_vitb16": "YOUR_PERSONAL_URL_HERE",
"dinov3_vitl16": "YOUR_PERSONAL_URL_HERE"
}Note: dinov3_urls.json is ignored by Git, so your private links will not be accidentally committed.
We provide checkpoints for DINOvTree-B trained on the Quebec Trees, BCI, or Quebec Plantations dataset. You can download the checkpoints like this:
mkdir -p ./checkpoints && \
wget -O ./checkpoints/dinovtreeb_quebectrees.pth "https://huggingface.co/jannikend/dinovtree/resolve/main/checkpoints/dinovtreeb_quebectrees.pth?download=true" && \
wget -O ./checkpoints/dinovtreeb_bci.pth "https://huggingface.co/jannikend/dinovtree/resolve/main/checkpoints/dinovtreeb_bci.pth?download=true" && \
wget -O ./checkpoints/dinovtreeb_quebecplantations.pth "https://huggingface.co/jannikend/dinovtree/resolve/main/checkpoints/dinovtreeb_quebecplantations.pth?download=true"We trained all models on a single NVIDIA H100 GPU (80GB).
You can train DINOvTree-B on the Quebec Trees dataset as follows:
python scripts/train.py experiment=dinovtree_ch_qtYou can find all configuration files in the configs/experiment/ directory. To train on different datasets or use other models, override the experiment flag:
DINOvTree Models (Classification + Height):
experiment=dinovtree_ch_bci(DINOvTree-B on BCI)experiment=dinovtree_ch_qp(DINOvTree-B on Quebec Plantations)experiment=dinovtreel_ch_qt(DINOvTree-L on Quebec Trees)
Baselines:
experiment=dinov3_c_qt(DINOv3 for Classification on Quebec Trees)experiment=resnet_c_qt(ResNet50 for Classification on Quebec Trees)experiment=maskrcnn_h_qt(Mask R-CNN for Height on Quebec Trees)
Note: The allometric equations baseline does not contain trainable parameters and cannot be trained.
You can evaluate DINOvTree-B by passing the respective experiment config and the path to your downloaded checkpoint.
Quebec Trees:
python scripts/evaluate.py experiment=dinovtree_ch_qt ckpt_path=checkpoints/dinovtreeb_quebectrees.pthBCI:
python scripts/evaluate.py experiment=dinovtree_ch_bci ckpt_path=checkpoints/dinovtreeb_bci.pthQuebec Plantations:
python scripts/evaluate.py experiment=dinovtree_ch_qp ckpt_path=checkpoints/dinovtreeb_quebecplantations.pthHere is a high-level overview of our main directories:
configs/: Hydra configuration files for our models, datasets, and experiments.scripts/: Entry points for running training (train.py) and evaluation (evaluate.py).src/: Main Python package containing the core DINOvTree architecture, baseline implementations, and utilities.external/: Third-party code utilized in this project (DINOv3, GeoDataset).
Click to expand full Repository Structure
.
├── configs # Hydra configuration files.
│ ├── config.yaml # Root/default Hydra config.
│ ├── dataset # Dataset-specific config group.
│ │ ├── bci.yaml # BCI dataset config.
│ │ ├── quebec_plantations.yaml # Quebec Plantations dataset config.
│ │ └── quebec_trees.yaml # Quebec Trees dataset config.
│ ├── experiment # Experiment configs.
│ │ ├── allometric_h_qt.yaml # Allometric equations for height on Quebec Trees.
│ │ ├── dinov3_c_qt.yaml # DINOv3 for classification on Quebec Trees.
│ │ ├── dinovtree_ch_bci.yaml # DINOvTree (classification + height) on BCI.
│ │ ├── dinovtree_ch_qp.yaml # DINOvTree (classification + height) on Quebec Plantations.
│ │ ├── dinovtree_ch_qt.yaml # DINOvTree (classification + height) on Quebec Trees.
│ │ ├── dinovtreel_ch_qt.yaml # DINOvTree-L (classification + height) on Quebec Trees.
│ │ ├── maskrcnn_h_qt.yaml # Mask R-CNN for height on Quebec Trees.
│ │ └── resnet_c_qt.yaml # ResNet50 baseline for classification on Quebec Trees.
│ └── model # Model configs.
│ ├── allometric.yaml # Allometric model config.
│ ├── dinov3.yaml # DINOv3 model config.
│ ├── dinovtree.yaml # DINOvTree model config.
│ ├── maskrcnn.yaml # Mask R-CNN model config.
│ └── resnet.yaml # ResNet model config.
├── docs # Project documentation assets.
│ ├── figure_method.png # Method overview figure.
│ └── figure_teaser.png # Teaser figure shown in abstract section.
├── external # External code.
│ ├── facebookresearch_dinov3 # DINOv3 code.
│ └── hugobaudchon_geodataset # GeoDataset code.
├── LICENSE # Repository license.
├── pyproject.toml # Project metadata and tooling configuration.
├── README.md # Project README file (this file).
├── requirements.txt # pip dependency list.
├── scripts # Entry-point scripts for training/evaluation.
│ ├── evaluate.py # Evaluation script.
│ └── train.py # Training script.
├── src # Main Python package source code.
│ ├── models # Model implementations.
│ │ ├── allometric_model # Allometric model package.
│ │ │ ├── allometric_model.py # High-level allometric model wrapper.
│ │ │ └── core # Core allometric computations.
│ │ │ └── allometric.py # Allometric equation implementation.
│ │ ├── base_model # Shared model base class.
│ │ │ └── base_model.py # Base model class.
│ │ ├── dinov3_model # DINOv3 model package.
│ │ │ ├── core # Core DINOv3 model code.
│ │ │ │ └── dinov3.py # DINOv3 backbone loading/inference logic.
│ │ │ └── dinov3_model.py # High-level DINOv3 model wrapper.
│ │ ├── dinovtree_model # DINOvTree model package.
│ │ │ ├── core # Core DINOvTree modules and heads.
│ │ │ │ ├── dinovtree.py # Main DINOvTree architecture.
│ │ │ │ ├── modules.py # Reusable neural network modules.
│ │ │ │ └── task_heads.py # Height and species prediction heads.
│ │ │ └── dinovtree_model.py # High-level DINOvTree model wrapper.
│ │ ├── maskrcnn_model # Mask R-CNN model package.
│ │ │ ├── core # Core Mask R-CNN code.
│ │ │ │ └── maskrcnn.py # Mask R-CNN implementation/wrapper.
│ │ │ └── maskrcnn_model.py # High-level Mask R-CNN model wrapper.
│ │ └── resnet_model # ResNet model package.
│ │ ├── core # Core ResNet code.
│ │ │ └── resnet.py # ResNet model code.
│ │ └── resnet_model.py # High-level ResNet model wrapper.
│ └── utils # Shared utilities across models/scripts.
│ ├── augmentor.py # Data augmentation utilities.
│ ├── head.py # Shared prediction head building blocks.
│ ├── losses.py # Loss functions.
│ ├── metrics.py # Evaluation metrics.
│ ├── scheduler.py # Learning rate scheduler utilities.
│ └── utils.py # Generic utility helpers.
└── uv.lock # Lockfile for uv-managed dependencies.
We thank the authors of DINOv3, GeoDataset, and DETR for releasing their code.
Our code is licensed under the Apache License 2.0. The code in the directories external/facebookresearch_dinov3 and external/hugobaudchon_geodataset is adapted from Meta and Hugo Baudchon. They are released under the DINOv3 License and Apache License 2.0, respectively.
If you find our work useful, please consider citing our paper:
@article{endres2026treeheightspecies,
title = {Estimating Individual Tree Height and Species from UAV Imagery},
author = {Endres, Jannik and Lalibert{\'e}, Etienne and Rolnick, David and Ouaknine, Arthur},
journal = {arxiv:2603.23669 [cs.CV]},
year = {2026}
}
