dsm2dtm — extract bare-earth terrain models from surface models, in pure Python.
A robust open-source library for generating Digital Terrain Models from Digital Surface Models. No SAGA, no GDAL CLI — just NumPy, SciPy and Rasterio. Ships as a Python package, a CLI, and a QGIS plugin.
Fig. 01 From left: the aerial image, the input digital surface model (note the brown artefacts — forests and buildings), and the derived digital terrain model showing only the bare earth.
The problem
Many open elevation datasets ship only as digital surface models — the top of every tree, roof and crane included. Hydrology, terrain analysis and earthworks all want the bare ground underneath.
Commercial DTM derivation tools exist but cost thousands of euros per seat, and the open alternatives lean on heavy external binaries like SAGA or the GDAL CLI — fine on a workstation, painful inside a containerised pipeline. I wanted something free, scriptable, and pip-installable: a researcher in a remote field office should be able to run it on a laptop, and a backend service should be able to depend on it without dragging in a system-level GIS stack.
How it works
Under the hood dsm2dtm is an optimised implementation of the Progressive Morphological Filter (PMF) paired with a surface-refinement pass. The intuition: man-made structures and tall vegetation present as objects sitting on top of the ground, so an iterative morphological opening with growing kernels can "shave them off" while leaving the terrain intact.
-
01
Resolution adaptation
Parameters scale automatically to the input pixel size. High-resolution inputs (>0.5m) are downsampled for stability and speed, then upsampled back at the end.
-
02
Slope estimation
Local terrain slopes are computed and used to adapt filtering thresholds — steep mountains and flat polders shouldn't be filtered with the same kernel.
-
03
Progressive morphological filter
An iterative morphological opening (erosion → dilation) with progressively larger window sizes removes anything sticking up above the surrounding ground.
-
04
Refinement & gap filling
A smoothing pass compares the rough estimate with the original surface to recover detail and reject spikes; remaining nodata holes are filled by inverse-distance weighting or nearest-neighbour.
Using it
One install, three surfaces. Pick the one that fits.
Python library
$ pip install dsm2dtm
# High-level API from dsm2dtm import generate_dtm, save_dtm dtm_array, profile = generate_dtm("dsm.tif") save_dtm(dtm_array, profile, "dtm.tif")
Command line
$ dsm2dtm --dsm dsm.tif --out_dir output/
The CLI exposes a slope parameter and a kernel radius for object removal; sensible defaults work for most aerial datasets.
QGIS plugin
For analysts who live in QGIS rather than a terminal, dsm2dtm also ships as a plugin available in the official QGIS Plugin Repository. Search for DSM to DTM under Plugins → Manage and Install Plugins… — no Python environment required.
plugins.qgis.org/plugins/dsm2dtm ↗Distribution
Packaging matters as much as the algorithm does. dsm2dtm is available on PyPI, conda-forge, and the QGIS Plugin Repository — so it meets users where they already work, whether that's a Jupyter notebook, a Snakemake pipeline, or a QGIS dock panel. Cross-platform CI (Linux, macOS, Windows) keeps the wheels honest.
pip install dsm2dtm
conda install -c conda-forge dsm2dtm
Plugins → DSM to DTM
FOSS4G Europe
I presented dsm2dtm at FOSS4G Europe 2024 in Tartu, Estonia — walking through the algorithm, real-world results, and the trade-offs of morphological filtering versus point-cloud methods.
The takeaway from the room: there's a meaningful gap between "expensive commercial pipelines" and "research code that's hard to run" — small, well-packaged open-source tools matter for accessibility in geospatial work.