Detects elephants in nadir aerial photography, trained on the
Aerial Elephant Dataset (AED).
Single class (0: elephant).
Point AP @ 2.25 m = 0.9002 on 439 held-out full-resolution test images
(2,970 annotated elephants). Best F1 0.8896 at confidence 0.50
(precision 0.9168 / recall 0.8640).
Full fine-tune (all 902/902 layers transferred from COCO)
Parameters
9,948,638 (9.95 M) · 19.5 MB checkpoint
Input
1024 × 1024 tiles, via SAHI over full-resolution photos
Classes
1 (0: elephant)
Head
end2end: True — YOLO26 is NMS-free (reg_max: 1)
The -p2 and -p6 variants were tried and rejected: they ship as
architecture-only YAML, so .load('yolo26s.pt') transfers only 360/902 layers
and leaves the neck and heads training from scratch. On 17k tiles that is a bad
trade for the extra small-object resolution.
⚠️ Read this before you run it
Source images are ~5472×3648 and this model was trained on 1024 px tiles.
Calling model.predict() on a full-resolution photo yields recall of 0.1%.
You must use tiled inference (SAHI).
Working example below.
Usage
pip install ultralytics sahi huggingface_hub
python
1from huggingface_hub import hf_hub_download
2from sahi import AutoDetectionModel
3from sahi.predict import get_sliced_prediction
45weights = hf_hub_download("iamhelitha/elefind-yolo26s","elefind_yolo26s.pt")67model = AutoDetectionModel.from_pretrained(8 model_type="ultralytics",9 model_path=weights,10 confidence_threshold=0.30,11 device="cuda:0",# "cpu" works, ~14 s per 20 MP image12)1314result = get_sliced_prediction(15"your_aerial_photo.jpg",16 model,17 slice_height=1024,18 slice_width=1024,19 overlap_height_ratio=0.30,20 overlap_width_ratio=0.30,21# Pass these EXPLICITLY. SAHI defaults to GREEDYNMM/IOS, which merges22# cross-tile duplicates instead of deleting them. Because this model emits23# altitude-scaled boxes, IOS (intersection-over-smaller) over-merges animals24# standing close together in herds and costs 0.047 point AP.25 postprocess_type="NMS",26 postprocess_match_metric="IOU",27 postprocess_match_threshold=0.40,28)2930for obj in result.object_prediction_list:31print(obj.bbox.to_xyxy(), obj.score.value)
Recommended settings:slice=1024, overlap=0.30, conf=0.30, iou=0.40.
For maximum F1 use conf=0.50.
Latency (~20 MP image, RTX 3060): GPU ≈ 3.2 s, CPU ≈ 14.4 s. This is fixed by
tiling geometry, not by how many elephants are present — an empty image costs the
same as one with 62 animals.
The thing you need to understand about the metrics
AED ships point annotations only — image, x, y. There is no ground-truth
bounding box anywhere in the dataset. Every box used to train or score any model
on AED is invented, which means IoU-based mAP measures the assumed animal size
at least as much as it measures the detector.
Same detections, three scoring rules
The bars above are the same detections from two models, graded three ways.
Point matching calls them tied. The box metrics disagree with each other by
0.31 and then 0.74 — driven entirely by whether you assume an elephant is 3.5 m
or 4.5 m across. Nobody measured that number.
So the primary metric here is point-based: a detection is correct if its
centre falls within a tolerance expressed in metres of an annotated point,
converted to pixels per image using that image's Ground Sampling Distance. No
invented box, GSD-invariant, and it matches AED's own benchmark.
Evaluation results
Protocol
Test set
439 full-resolution AED test images (438 with annotations)
Ground truth
2,970 annotated elephant points
Inference
SAHI, 1024 px tiles, 0.30 overlap, match threshold 0.40
101-point rectangle interpolation (precision = 0 beyond max recall)
Primary metric
point-distance match, tolerance in metres, converted per image via GSD
Hardware
RTX 3060 12 GB, ~3.2 s per 20 MP image
The test split is fully held out — 438 source photos from AED's
test_images, never seen in training or validation, and disjoint at
source-photo level from both.
1python evaluation/evaluate.py \2 --model elefind_yolo26s=elefind_yolo26s.pt \3 --aed-root /path/to/AED --split test
Primary — point-based
A detection counts as correct when its centre lies within the tolerance, in
metres, of an annotated elephant. 4.5 m is one nadir-visible body length, so
2.25 m is half a body length — the headline figure.
Tolerance
AP
Max recall
Best F1
@ conf
Precision
Recall
1.00 m (quarter body)
0.8445
0.8892
0.8563
0.50
0.8825
0.8316
2.25 m (half body)
0.9002
0.9276
0.8896
0.50
0.9168
0.8640
4.50 m (one body)
0.9094
0.9343
0.8930
0.50
0.9203
0.8673
At the 2.25 m operating point: 2,566 TP · 233 FP · 404 FN out of 2,970.
The metric is stable across tolerance — 0.845 → 0.909 for a 4.5× loosening —
which says detections land close to the animal rather than merely nearby.
Postprocess sensitivity
Same detections, different cross-tile merge rule. SAHI defaults to the second
row; use the first.
Postprocess
Point AP @ 2.25 m
Max recall
Best F1
@ conf
Precision
Recall
NMS / IOU (recommended)
0.9002
0.9276
0.8896
0.50
0.9168
0.8640
GREEDYNMM / IOS (SAHI default)
0.8532
0.8761
0.8752
0.45
0.9202
0.8343
IOS is intersection-over-smaller, so a small box inside a larger one always
scores 1.0. Because this model emits altitude-scaled boxes, that over-merges
distinct animals standing close together in herds — costing 0.047 AP and 0.05
recall.
Secondary — box mAP, stated with its convention
Postprocess
GT convention
mAP@0.5
mAP@0.5:0.95
NMS / IOU
4.5 m / GSD
0.8584
0.4651
NMS / IOU
3.5 m / GSD
0.6414
0.1589
GREEDYNMM / IOS
4.5 m / GSD
0.7381
0.3674
GREEDYNMM / IOS
3.5 m / GSD
0.4311
0.0989
Quote mAP@0.5, not mAP@0.5:0.95 — with synthesised ground truth the tight-IoU
thresholds are scoring the size assumption, not the detector.
Baseline comparison
A YOLOv11s trained on a fixed 60 px box convention, run through the identical
pipeline on the identical images (yolo11s_baseline.pt, included here):
This model
Fixed-box baseline
Point AP @ 2.25 m, NMS/IOU
0.9002
0.8962
Point AP @ 2.25 m, GREEDYNMM/IOS
0.8532
0.8724
Best F1
0.8896 @ 0.50
0.8854 @ 0.45
Box mAP@0.5 (GT 4.5 m)
0.8584
0.1221
Predicted box width p10/median/p90
36 / 89 / 118 px
54 / 57 / 63 px
Required width p10/median/p90
37 / 80 / 113 px
37 / 80 / 113 px
These two models are statistically indistinguishable at finding elephants —
and the ranking flips with the postprocess setting, so no detection win should be
claimed for either. The real, large difference is localisation geometry: the
baseline emits one box size across a 10.5× GSD range, this model tracks altitude.
Validation during training (tiles)
Precision 0.8896 / Recall 0.8472 / mAP50 0.8883 / mAP50-95 0.5304 at
epoch 99 of 100. The validation split is 4,026 tiles from 325 held-out source
photos in AED's training set, disjoint at photo level. All validation losses were
still falling at epoch 100 and patience 25 never triggered — the run was not
overfitting and had not converged.
Graphs
PR curve
Confidence sweep
Box size tracking
Box mAP conventions
box_size_tracking.png is the most diagnostic one: each dot is a test image,
plotting the box width the model predicted against the width the altitude calls
for. This model tracks the diagonal across the whole range.
17,385 tiles (5,795 positive + 11,590 background) from 1,304 photos
Val set
4,026 tiles from 325 photos
Labels are squares synthesised from AED points at 4.5 m / GSD — the
nadir-visible longest dimension. (3.5 m, used by earlier work on this dataset,
is an elephant's shoulder height, which is not what a top-down camera sees.)
degrees=0.0 is deliberate. The boxes are fixed-size squares. Rotation
augmentation makes Ultralytics recompute the axis-aligned box of the rotated
box, inflating it by up to √2 and corrupting the size convention. flipud=0.5is enabled — from directly overhead, "up" is arbitrary.
Splits are disjoint at source-photo level, decided before tiling. Tiles
overlap by 20%, so splitting tiles instead would put near-duplicate crops in
both train and validation.
Files
File
elefind_yolo26s.pt
the model — use this
yolo11s_baseline.pt
fixed-box YOLOv11s, so the comparison above is reproducible
assets/
evaluation graphs
metrics/corrected_metrics.{json,csv}
every number above, machine-readable
Limitations and intended use
Intended for aerial wildlife survey research and conservation monitoring on
nadir imagery of comparable resolution and terrain.
Known limitations:
One class. It detects elephants. It has never seen another species and
will not distinguish one; expect false positives on large-bodied fauna.
Altitude range. Trained on GSD 0.024–0.130 m/px (roughly 320–1,712 m AGL).
Outside that, behaviour is untested.
Domain shift is baked into the benchmark. AED's test sorties flew a median
745 m against 1,084 m for training, so test elephants are ~1.45× larger in
pixels than anything in training. The reported numbers already include that
penalty.
Boxes are synthetic. Predicted box extents approximate 4.5 m / GSD. Do not
treat them as measurements of an animal.
Habitat. AED is savanna/bushland. Forest canopy, snow, and dense wetland
are out of distribution.
Not for enforcement or targeting. This is a survey and monitoring tool.
Counts carry real error (recall 0.864 at best F1) and should not be treated as
a census without human verification.
Licence
AGPL-3.0. This is inherited, not chosen lightly: the weights derive from
Ultralytics pretrained models and the inference path imports Ultralytics, which
is AGPL-3.0. If you distribute a modified version or run one as a network
service, you must publish your source.
The Aerial Elephant Dataset is not redistributed here. Obtain it from
Zenodo under its own terms.
1@inproceedings{naude2019aerial,
2 title = {The Aerial Elephant Dataset: A New Public Benchmark
3 for Aerial Object Detection},
4 author = {Naud{\'e}, Johannes J. and Joubert, Deon},
5 booktitle = {IEEE/CVF Conference on Computer Vision and Pattern
6 Recognition Workshops (CVPRW)},
7 year = {2019}
8}