Crestline - skyline & inner ridgeline detection for mountain photos
In french : S.D.E.C.E. - Système de Détection de l’Emplacement par Comparaison des Escarpements - nom pris au hasard
Draws the skyline and the inner ridgelines (a nearer ridge silhouetted against farther terrain) on any mountain photo.
Method
- Labels come from 3D terrain, not from hand drawing. We took 6,286 photos from smapshot, whose camera poses were aligned by volunteers on a 3D model of Switzerland.
- From each pose we ray-traced a depth map in the Swiss elevation model (swissALTI3D 2 m, plus swissALTIRegio far away, with Earth curvature and refraction).
- Skyline = where rays stop hitting terrain. Inner ridgelines = sharp depth jumps (near terrain occluding terrain ≥ 25 % and ≥ 50 m farther).
- Each pose was refined by a small rotation (≤ 0.6°) to snap terrain lines onto image edges. Photos whose rendered skyline did not match the photo were discarded.
- Ignored during training:
- trees, buildings and people;
- clouds hiding the terrain;
- ridges with no visible edge in the photo.
- Model: U-Net with a ConvNeXt-Base encoder (ImageNet-22k), 3 outputs (sky, skyline, inner ridges), trained 30 epochs on one RTX 5090 (~25 min).
Results
Per-column skyline error between the predicted and true sky boundary, in pixels at ~1024 px width.
- CH1 (203 photos of the Swiss Alps, skylines corrected by hand, ETH Zurich) is fully independent of our pipeline. It is the reference.
- smapshot-test (588 photos) comes from geographic areas never seen in training (10 km cells), with labels from our pipeline.
| Model | CH1 mean error | CH1 ≤ 2 px | smapshot-test mean error | smapshot-test ≤ 2 px | Inner-ridge F1 |
|---|---|---|---|---|---|
| Crestline (this model) | 1.1 px | 91.2 % | 2.2 px | 82.0 % | 0.35 |
| SegFormer-B5 (ADE20K, sky class) | 2.7 px | 77.6 % | 8.0 px | 69.3 % | — |
| Mask2Former Swin-L (ADE20K, sky class) | 2.6 px | 68.2 % | 4.1 px | 66.8 % | — |
| Same pipeline trained on GeoPose3K (ConvNeXt-Large) | 4.7 px | 74.5 % | 11.4 px | 64.2 % | 0.08 |
Public segmenters are already good on clear photos (median 1-2 px). They fail on clouds, snow against a white sky, haze and backlight. That is where this model gains most: its mean error is about 2.4× lower than theirs on CH1. No public model detects inner ridgelines; this is also where Crestline has the most room to improve.
Why not GeoPose3K?
We first trained on GeoPose3K (40 GB, 3,111 photos rendered from a DEM). Its labels are not precise enough:
- the DEM is 24-30 m and most poses are semi-automatic, so rendered ridges are often off by tens of pixels, especially on terrain closer than ~3 km. A 30 m DEM cell seen from 500 m is already ~3.4°;
- the same network trained on it is clearly worse (table above), and nearly blind to inner ridges (F1 0.08).
Swapping the label source (hand-aligned poses, 2 m DEM) mattered far more than model size: a ConvNeXt-Large brought no gain.
Usage
from PIL import Image
import crestline # crestline.py from this repository
model, config = crestline.load("LPN64/crestline-convnext-base")
image, prob = crestline.predict(model, Image.open("photo.jpg")) # prob: sky, skyline, inner ridges
skyline_rows, ridges = crestline.lines(prob, config["ridge_threshold"]) # 0.4
crestline.overlay(image, skyline_rows, ridges).save("lines.jpg")
More examples (holiday photos, Tour des Fiz, Haute-Savoie)
Limitations
- Trained on Swiss Alps photos (1900s to 2020s, many black-and-white). Expect lower quality on very different terrain.
- Inner ridgelines are recall-limited (F1 ≈ 0.35 on smapshot-test): faint ridges in haze are often missed.
- Summits hidden in clouds: the skyline follows the visible boundary, which may be the cloud.
License and attribution
Weights released under CC BY-SA 4.0, because most training photos are CC BY-SA 4.0.
Training data licences, stated plainly. The 6,286 training photos are:
| Licence | Photos | Share |
|---|---|---|
| CC BY-SA 4.0 (ETH-Bibliothek Zürich, Bildarchiv) | 3,672 | 58 % |
| swisstopo ("Quelle: swisstopo") | 1,666 | 27 % |
| No explicit licence in the source metadata (ETH-Bibliothek Zürich, Bildarchiv) | 918 | 15 % |
| Public domain | 30 | < 1 % |
The 918 photos without an explicit licence come from the same public archive. ETH-Bibliothek publishes its images under CC BY-SA 4.0 or as public domain, but the licence field of these particular records was empty. No photo is redistributed in this repository, only the trained weights. If you are a rights holder and object to this use, please open a discussion on this repository and we will retrain without the concerned images.
Attribution:
- Labels derived from swissALTI3D / swissALTIRegio © swisstopo.
- Photos: ETH-Bibliothek Zürich, Bildarchiv and swisstopo.
- Camera poses: smapshot (HEIG-VD, ETH-Bibliothek, swisstopo) and its volunteer contributors.
- Evaluation: CH1 (Baatz, Saurer, Köser, Pollefeys, ETH Zurich). Evaluation only, no image redistributed.
- The holiday photos in this card belong to the repository author.
- Downloads last month
- 14
Model tree for LPN64/crestline-convnext-base
Base model
timm/convnext_base.fb_in22k







