r.dem.coregister
Co-register post-event DSM to reference DEM using PGCPs
r.dem.coregister [-v] dem=name reference=name pgcp=name output=name [method=string] [buffer=float] [min_points=integer] [stable_mask=name] [bias_output=name] [transform_output=name] [apply_transform=name] [--overwrite] [--verbose] [--quiet] [--qq] [--ui]
Example:
r.dem.coregister dem=name reference=name pgcp=name output=name
grass.tools.Tools.r_dem_coregister(dem, reference, pgcp, output, method="pgcp_vertical", buffer=2.0, min_points=30, stable_mask=None, bias_output=None, transform_output=None, apply_transform=None, flags=None, overwrite=None, verbose=None, quiet=None, superquiet=None)
Example:
tools = Tools()
tools.r_dem_coregister(dem="name", reference="name", pgcp="name", output="name")
This grass.tools API is experimental in version 8.5 and expected to be stable in version 8.6.
grass.script.run_command("r.dem.coregister", dem, reference, pgcp, output, method="pgcp_vertical", buffer=2.0, min_points=30, stable_mask=None, bias_output=None, transform_output=None, apply_transform=None, flags=None, overwrite=None, verbose=None, quiet=None, superquiet=None)
Example:
gs.run_command("r.dem.coregister", dem="name", reference="name", pgcp="name", output="name")
Parameters
dem=name [required]
Post-event DSM to co-register
reference=name [required]
Reference DEM (e.g., pre-event LiDAR DTM/DSM)
pgcp=name [required]
Pseudo ground control points (PGCPs) (e.g. roads, buildings, fire hydrants) vector for PGCP extraction
Or data source for direct OGR access
output=name [required]
Co-registered DSM output name
method=string
Co-registration method
Allowed values: pgcp_vertical, nk, nk_icp, icp
Default: pgcp_vertical
buffer=float
Buffer distance (m) around PGCP features for residual sampling
Default: 2.0
min_points=integer
Minimum number of PGCPs required to proceed
Default: 30
stable_mask=name
Stable-terrain mask (1=stable); required by method nk and nk_icp
bias_output=name
CSV file path for per-PGCP residual statistics
transform_output=name
Write the solved transform (PGCP, N&K, ICP components) to a file
apply_transform=name
Replay a saved transform onto dem (shared horizontal, per-surface vertical) instead of solving
-v
Verbose: write per-PGCP residuals to bias_output CSV
--overwrite
Allow output files to overwrite existing files
--help
Print usage summary
--verbose
Verbose module output
--quiet
Quiet module output
--qq
Very quiet module output
--ui
Force launching GUI dialog
dem : str | np.ndarray, required
Post-event DSM to co-register
Used as: input, raster, name
reference : str | np.ndarray, required
Reference DEM (e.g., pre-event LiDAR DTM/DSM)
Used as: input, raster, name
pgcp : str, required
Pseudo ground control points (PGCPs) (e.g. roads, buildings, fire hydrants) vector for PGCP extraction
Or data source for direct OGR access
Used as: input, vector, name
output : str | type(np.ndarray) | type(np.array) | type(gs.array.array), required
Co-registered DSM output name
Used as: output, raster, name
method : str, optional
Co-registration method
Allowed values: pgcp_vertical, nk, nk_icp, icp
Default: pgcp_vertical
buffer : float, optional
Buffer distance (m) around PGCP features for residual sampling
Default: 2.0
min_points : int, optional
Minimum number of PGCPs required to proceed
Default: 30
stable_mask : str | np.ndarray, optional
Stable-terrain mask (1=stable); required by method nk and nk_icp
Used as: input, raster, name
bias_output : str, optional
CSV file path for per-PGCP residual statistics
Used as: output, file, name
transform_output : str, optional
Write the solved transform (PGCP, N&K, ICP components) to a file
Used as: output, file, name
apply_transform : str | io.StringIO, optional
Replay a saved transform onto dem (shared horizontal, per-surface vertical) instead of solving
Used as: input, file, name
flags : str, optional
Allowed values: v
v
Verbose: write per-PGCP residuals to bias_output CSV
overwrite : bool, optional
Allow output files to overwrite existing files
Default: None
verbose : bool, optional
Verbose module output
Default: None
quiet : bool, optional
Quiet module output
Default: None
superquiet : bool, optional
Very quiet module output
Default: None
Returns:
result : grass.tools.support.ToolResult | np.ndarray | tuple[np.ndarray] | None
If the tool produces text as standard output, a ToolResult object will be returned. Otherwise, None will be returned. If an array type (e.g., np.ndarray) is used for one of the raster outputs, the result will be an array and will have the shape corresponding to the computational region. If an array type is used for more than one raster output, the result will be a tuple of arrays.
Raises:
grass.tools.ToolError: When the tool ended with an error.
dem : str, required
Post-event DSM to co-register
Used as: input, raster, name
reference : str, required
Reference DEM (e.g., pre-event LiDAR DTM/DSM)
Used as: input, raster, name
pgcp : str, required
Pseudo ground control points (PGCPs) (e.g. roads, buildings, fire hydrants) vector for PGCP extraction
Or data source for direct OGR access
Used as: input, vector, name
output : str, required
Co-registered DSM output name
Used as: output, raster, name
method : str, optional
Co-registration method
Allowed values: pgcp_vertical, nk, nk_icp, icp
Default: pgcp_vertical
buffer : float, optional
Buffer distance (m) around PGCP features for residual sampling
Default: 2.0
min_points : int, optional
Minimum number of PGCPs required to proceed
Default: 30
stable_mask : str, optional
Stable-terrain mask (1=stable); required by method nk and nk_icp
Used as: input, raster, name
bias_output : str, optional
CSV file path for per-PGCP residual statistics
Used as: output, file, name
transform_output : str, optional
Write the solved transform (PGCP, N&K, ICP components) to a file
Used as: output, file, name
apply_transform : str, optional
Replay a saved transform onto dem (shared horizontal, per-surface vertical) instead of solving
Used as: input, file, name
flags : str, optional
Allowed values: v
v
Verbose: write per-PGCP residuals to bias_output CSV
overwrite : bool, optional
Allow output files to overwrite existing files
Default: None
verbose : bool, optional
Verbose module output
Default: None
quiet : bool, optional
Quiet module output
Default: None
superquiet : bool, optional
Very quiet module output
Default: None
DESCRIPTION
r.dem.coregister co-registers a post-event DSM to a reference DEM using pseudo ground control points (PGCPs) sampled from stable features, terrain assumed unchanged between surveys. Roads are the typical source, but any stable feature works: buildings, parking lots, or fire hydrants. It removes a robust vertical bias and can optionally chain horizontal (Nuth and Kaab) and ICP refinement.
method=pgcp_vertical (default)
The pgcp vector is buffered by buffer metres and rasterized, the
elevation residual dem - reference is sampled within that PGCP mask, and a
robust median vertical bias is estimated. The bias is removed with
output = dem - median_bias. Robust statistics (median bias, NMAD, RMSE) are
reported, and with the -v flag per-PGCP residuals are written to
bias_output as CSV.
If fewer than min_points PGCP samples are found the tool warns and proceeds.
method=nk and method=nk_icp
These chain additional alignment after the PGCP vertical step, operating on the
PGCP-corrected DSM: nk adds a horizontal and vertical Nuth and Kaab correction
(r.dem.nk), and nk_icp further adds a point-to-plane ICP refinement
(r.dem.icp). The full chain is PGCP vertical, then N&K, then ICP.
Both methods require a stable_mask raster (1=stable). The Nuth and Kaab method regresses the elevation difference against slope and aspect, so the mask must cover broad, sloped, unchanged terrain. The flat PGCP features used for the PGCP step are unsuitable here (they are filtered out by the N&K slope limits), so a separate stable mask must be supplied. The same mask is passed to r.dem.icp to restrict ICP to stable terrain.
method=icp
This chains a point-to-plane ICP refinement (r.dem.icp) directly onto the
PGCP-corrected DSM, skipping the Nuth and Kaab step. The chain is PGCP vertical,
then ICP. Unlike nk and nk_icp, stable_mask is optional for this
method: when supplied it is passed to r.dem.icp to restrict the alignment to
unchanged terrain (recommended in change-detection contexts); when omitted, ICP
aligns over all valid terrain.
Solving once and replaying onto another surface
transform_output writes the composed PGCP, N&K, and ICP transform to a file. apply_transform replays a saved transform onto another DEM, skipping the solve. This is intended for surfaces from the same acquisition (for example a DSM and a DTM): solve the alignment on the cleaner bare-earth DTM, then replay it onto the DSM so both share the same horizontal alignment. On replay the horizontal components (N&K dx, dy and ICP dx, dy, yaw) are shared, while the vertical bias is re-estimated per surface with the PGCP step, since the DSM and DTM vertical offsets differ. Replay therefore still needs pgcp but not a stable_mask.
NOTES
The PGCP approach assumes the supplied features are stable reference surfaces. When using roads, choose buffer to stay within the paved surface and avoid curbs, vegetation, and vehicles. The computational region should match the input DEM resolution.
EXAMPLES
The commands below use the example scene built in the r.dem toolset manual, which is derived from the North Carolina sample dataset. Build it there first.
Remove the vertical bias from road PGCPs alone, writing the per-PGCP residuals to CSV:
g.region raster=elev_lid792_1m
r.dem.coregister dem=dsm_offset reference=elev_lid792_1m \
pgcp=pgcp_roads output=dsm_pgcp method=pgcp_vertical \
buffer=2.0 bias_output=pgcp_residuals.csv -v
Roads are flat, so the horizontal shift contributes almost nothing to the elevation residual there and the median bias recovers the applied 1.32 m:
N samples: 9725
Median bias: 1.3150 m
NMAD: 0.1014 m
Chain the Nuth and Kääb step to remove the horizontal offset as well. This needs a stable_mask of broad sloped terrain, separate from the flat PGCP features:
r.dem.coregister dem=dsm_offset reference=elev_lid792_1m \
pgcp=pgcp_roads stable_mask=stable_terrain \
output=dsm_coreg method=nk

Figure: Stable-terrain residual before co-registration, after r.dem.nk, and
after r.dem.icp.
SEE ALSO
r.dem, r.dem.change, r.dem.icp, r.dem.nk, v.buffer, v.to.rast
AUTHORS
Corey T. White, Center for Geospatial Analytics, NC State University
SOURCE CODE
Available at: r.dem.coregister source code
(history)
Latest change: Thursday Aug 20 19:32:37 2026 in commit d08d385