SMRF vs PMF for Dense Urban LiDAR

Verdict: For dense urban LiDAR, reach for filters.smrf first — its slope-scaled threshold carves around building walls with fewer edge artefacts — and fall back to filters.pmf only when you need direct control of the window schedule to reject broad flat structures like bridge decks and parking-garage roofs.

# Context and Motivation

This comparison is part of SMRF Ground Classification in PDAL and puts that filter head to head with its main alternative, PMF Ground Classification, on the terrain type that punishes ground filters hardest: the dense city. Both are morphological classifiers, both write ASPRS Classification code 2, and both are one PDAL stage away from a digital terrain model. The question this page answers is not which is better in the abstract — it is which one to declare in your pipeline when the scene is wall-to-wall buildings, elevated roadways, and hard breaklines.

Urban environments violate the gentle assumptions that ground filters were originally built around. Terrain is interrupted by sheer vertical walls, plazas sit flush with adjacent roofs, and elevated highways float slabs of near-flat surface right at nuisance heights above the true ground. A filter that reconstructs a smooth minimum surface can be fooled into treating a bridge deck as terrain, or into shaving a ground strip off the base of every tower. SMRF and PMF fail in slightly different ways here, and knowing those failure modes is worth more than any single “best” default.

SMRF vs PMF on Urban Structures Two panels. The left panel labelled SMRF shows a building and an elevated highway with a clean ground cut around the vertical building wall. The right panel labelled PMF shows the same scene where the building edge leaves a small skirt of misclassified ground and the bridge deck needs an explicit window limit to reject. Both write Classification code 2 for accepted ground. filters.smrf filters.pmf building highway deck ground (code 2) clean wall cut building highway deck edge skirt without tuned window reject via max_window_size

# The Two Filters at a Glance

SMRF reconstructs a minimum-elevation surface and applies a progressive morphological opening whose window grows automatically, using a scalar that scales the elevation tolerance by local slope. PMF — the Progressive Morphological Filter — instead steps a structuring element through an explicit schedule bounded by max_window_size, raising an elevation-difference threshold at each step governed by initial_distance, slope, and max_distance. The practical difference is one of control: SMRF hides its window growth behind two tuning knobs, while PMF exposes the schedule so you can dictate exactly how far it reaches and how much vertical difference it tolerates at each stage. The PMF classification walk-through covers that schedule in depth.

# Parameter Equivalence

Concept filters.smrf filters.pmf Notes
Raster resolution cell cell Same meaning; metres per grid cell
Reach limit window (grows internally) max_window_size PMF caps the schedule explicitly
Slope tolerance slope slope Comparable but not numerically identical
Vertical tolerance threshold × scalar initial_distancemax_distance SMRF scales by slope; PMF ramps per step
Output label Classification = 2 Classification = 2 Both follow the ASPRS standard
Return selection returns returns Identical syntax on both stages

Because the vertical-tolerance models differ, do not expect copying slope from one filter to the other to reproduce a result. Treat each filter’s parameters as its own dialect even though the vocabulary overlaps.

# When Each Filter Wins in the City

Buildings and vertical walls — advantage SMRF. The slope-scaled elevation threshold is well suited to the abrupt rise at a building base. SMRF flags the wall as an object edge and drops the ground cleanly at the footprint boundary, leaving little of the skirt of misclassified ground that PMF can produce when its window schedule is not tuned to the block size. For downtown cores dominated by large footprints, SMRF is the lower-risk starting point.

Elevated highways and bridge decks — advantage PMF (with care). A bridge deck is a broad, flat surface sitting a few metres above true ground — exactly the shape a minimum-surface filter is tempted to accept as terrain. PMF’s explicit max_window_size and distance ramp give you a direct lever to reject a deck of known width, whereas SMRF’s automatic window growth is harder to aim at a specific structure. Neither filter rejects decks automatically, so inspect elevated-roadway areas by hand regardless.

Hard breaklines and plazas — roughly even. Sharp terrain discontinuities such as retaining walls, stair landings, and sunken plazas challenge both filters. SMRF tends to smooth across them slightly; PMF can step over them if the schedule is coarse. Tune cell down toward 0.5 m in these zones for either filter before deciding one is failing.

# Runnable Comparison Script

The script below runs both filters on the same urban tile and reports the ground count and fraction each produces, so you can compare them empirically instead of guessing. It shares the reader and outlier pre-clean so only the classifier differs.

python
#!/usr/bin/env python3
"""
compare_smrf_pmf.py — Classify the same urban tile with SMRF and PMF and
report the ground counts side by side.

Usage:
    python compare_smrf_pmf.py urban_tile.laz
"""

import json
import sys

import numpy as np
import pdal


def _ground_stats(input_path: str, classifier_stage: dict) -> dict:
    pipeline = {
        "pipeline": [
            {"type": "readers.las", "filename": input_path},
            {
                "type": "filters.outlier",
                "method": "statistical",
                "mean_k": 12,
                "multiplier": 2.5,
            },
            classifier_stage,
        ]
    }
    p = pdal.Pipeline(json.dumps(pipeline))
    total = p.execute()
    arr = p.arrays[0]
    ground = int(np.count_nonzero(arr["Classification"] == 2))
    return {"total": total, "ground": ground, "fraction": ground / total}


def compare(input_path: str) -> None:
    smrf_stage = {
        "type": "filters.smrf",
        "cell": 1.0,
        "window": 40.0,
        "slope": 0.2,
        "scalar": 1.25,
        "threshold": 0.5,
        "ignore": "Classification[7:7]",
    }
    pmf_stage = {
        "type": "filters.pmf",
        "cell": 1.0,
        "max_window_size": 40.0,
        "slope": 0.2,
        "initial_distance": 0.5,
        "max_distance": 3.0,
        "ignore": "Classification[7:7]",
    }

    smrf = _ground_stats(input_path, smrf_stage)
    pmf = _ground_stats(input_path, pmf_stage)

    print(f"{'filter':<8}{'total':>12}{'ground':>12}{'fraction':>10}")
    for name, s in (("SMRF", smrf), ("PMF", pmf)):
        print(f"{name:<8}{s['total']:>12,}{s['ground']:>12,}{s['fraction']:>9.1%}")

    delta = smrf["ground"] - pmf["ground"]
    print(
        f"\nSMRF classified {delta:+,} more ground points than PMF "
        f"({(smrf['fraction'] - pmf['fraction']) * 100:+.1f} pp)."
    )


def main() -> None:
    if len(sys.argv) != 2:
        print("Usage: python compare_smrf_pmf.py <urban_tile.laz>")
        sys.exit(1)
    compare(sys.argv[1])


if __name__ == "__main__":
    main()

A large gap between the two ground counts is itself diagnostic: if SMRF classifies far more ground, PMF’s window schedule is probably too aggressive and is discarding valid terrain near structures; if PMF classifies far more, SMRF’s threshold may be admitting rooftops or decks. Run the tile through both, then eyeball the disagreement zones in a viewer.

# Verification

Whichever filter you pick, validate the same way you would any ground pass — the checks are covered fully in the parent SMRF Ground Classification guide. In dense urban tiles the ground fraction runs lower than open terrain, often 15–35 %, because buildings occupy so much of the footprint. Focus verification on the structure edges:

python
import numpy as np, pdal, json

def urban_ground_fraction(path: str, stage: dict) -> float:
    p = pdal.Pipeline(json.dumps({"pipeline": [path, stage]}))
    p.execute()
    arr = p.arrays[0]
    return float(np.count_nonzero(arr["Classification"] == 2) / len(arr))

Rasterize the ground class and overlay it on a building-footprint layer: a correct result shows ground stopping crisply at each footprint edge with no interior roof pixels leaking into the terrain surface.

# Gotchas and Edge Cases

Identical parameters, different outcomes. Because SMRF scales its threshold by slope while PMF ramps a distance schedule, feeding both the same slope and expecting matching ground counts is a mistake. Tune each filter on its own terms.

Bridge decks slip through both. A deck at a modest height above ground is the classic false positive for either filter. Confirm bridge and overpass areas manually, and if they persist as ground, add a targeted height-above-ground filter downstream rather than over-tightening the classifier globally.

Underground and sunken features. Sunken plazas, subway entrances, and depressed roadways sit below the surrounding grade and can be dropped by either filter’s minimum-surface logic. Lower cell and inspect these areas; they often need manual reclassification.

Threading dominates runtime, not filter choice. Both stages honour OMP_NUM_THREADS, and on a dense city tile the point count and cell size drive the clock far more than the algorithm. Set threads to your physical core count before benchmarking one filter against the other, as the SMRF performance notes describe.

# Frequently Asked Questions

Is SMRF or PMF better for classifying ground around buildings?

SMRF usually handles large building footprints more gracefully because its slope-scaled elevation threshold cuts cleanly around vertical walls, whereas PMF can leave a skirt of misclassified ground at building edges unless its window schedule is tuned. In dense downtown blocks SMRF is the safer default.

How do SMRF and PMF differ in handling elevated highways and bridges?

Both can misclassify a low bridge deck as ground because it resembles a broad flat surface near terrain height. PMF’s explicit maximum window and height thresholds give more direct control to reject bridge decks, while SMRF relies on its scalar and elevation threshold. Neither is automatic; verify bridge areas manually.

Do SMRF and PMF parameters map onto each other?

Partly. Both share window, slope, and cell concepts, and both write ASPRS Classification code 2. PMF adds max_window_size and initial_distance where SMRF uses a growing window plus scalar. The slope parameter is comparable, but identical numbers will not produce identical results.

Which filter is faster on dense urban tiles?

Runtimes are broadly comparable and dominated by point count and cell size rather than the choice of filter. SMRF’s morphological openings and PMF’s window iterations both parallelise with OMP_NUM_THREADS, so tune cell size and threading before worrying about which algorithm is intrinsically quicker.