Generate a Canopy Mask from DSM and DTM in QGIS

QGIStutorialcanopydrone mappingaerial surveytree crownprecision agriculturePython
Generate a Canopy Mask from DSM and DTM in QGIS

A canopy mask is a binary raster where each pixel is either 1 (canopy — tree or structure above ground level) or 0 (ground). It’s the foundation for any analysis that should ignore bare soil, such as NDVI statistics per plot. This script generates one from your DSM and DTM in a single run.

What it does

The generate_canopy_mask.py script subtracts the DTM (bare earth elevation) from the DSM (surface elevation including trees and structures). Any pixel where DSM − DTM > 0.1 m is classified as canopy (value = 1); everything else is ground (value = 0).

The 0.1 m threshold filters out sub-10 cm noise and rounding errors in the DTM, which would otherwise produce false canopy classifications on flat ground.

Output: canopy_mask.tif saved in the same directory as the DSM, loaded into your QGIS project.

What you need

  • QGIS 3.28 or newer
  • numpy, rasterio (bundled with QGIS)
  • A DSM raster with “_dsm” in the layer name, checked in the Layers panel
  • A DTM raster with “_dtm” in the layer name, checked in the Layers panel
  • The generate_canopy_mask.py script from the Vegetation & Field Analysis Pack

Steps

  1. Load your DSM and DTM rasters into QGIS. Make sure their layer names contain _dsm and _dtm respectively.
  2. Check (tick) both layers in the Layers panel.
  3. Open Plugins → Python Console → Show Editor.
  4. Paste the script and click Run.

The canopy mask is saved alongside the DSM and added to your project automatically. Before — buffered crown polygons over the orthomosaic, no mask yet:

Buffered crown polygons over a coconut plantation orthomosaic, before the canopy mask is generated

After — the binary canopy mask, solid black where canopy was detected:

Binary canopy mask over tree crown polygons on a coconut plantation — solid black where canopy was detected, circles marking the crown boundaries

This is one step in a complete per-tree health workflow that uses this same canopy mask to compute canopy-only NDVI per tree.

Using the canopy mask

Once generated, the canopy_mask layer is what canopy_ndvi_median_calc.py looks for by name when computing per-plot NDVI statistics. Keep the layer name as canopy_mask — the NDVI script matches it exactly.

You can also use the canopy mask with raster_mask_generator.py to isolate canopy-only pixels from any other raster.

Adjusting the threshold

The 0.1 m threshold is hardcoded in the script. To change it, open the script and find:

canopy = (chm > 0.1).astype(np.uint8)

Replace 0.1 with your desired minimum height above ground to be classified as canopy.

Get the script

generate_canopy_mask.py is part of the Vegetation & Field Analysis Pack — six vegetation analysis scripts for QGIS at $40 USD.