From 0080422a8258b99f8214dd43d8cf6af720df2596 Mon Sep 17 00:00:00 2001 From: Sam <78538841+spwoodcock@users.noreply.github.com> Date: Wed, 8 Oct 2025 06:10:25 +0800 Subject: [PATCH] feat: add flag --merge-skip-blending to skip expensive blending in splitmerge (#1934) --- contrib/mergepreview/mergepreview.py | 2 +- opendm/config.py | 12 ++++++++++++ opendm/orthophoto.py | 11 ++++++++++- stages/splitmerge.py | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/contrib/mergepreview/mergepreview.py b/contrib/mergepreview/mergepreview.py index 1e05e98c..1be75630 100644 --- a/contrib/mergepreview/mergepreview.py +++ b/contrib/mergepreview/mergepreview.py @@ -116,7 +116,7 @@ if len(all_orthos_and_ortho_cuts) > 1: 'BIGTIFF': 'IF_SAFER', 'BLOCKXSIZE': 512, 'BLOCKYSIZE': 512 - }) + }, args.merge_skip_blending) log.ODM_INFO("Wrote %s" % output_file) diff --git a/opendm/config.py b/opendm/config.py index c8353e58..04965bab 100755 --- a/opendm/config.py +++ b/opendm/config.py @@ -828,6 +828,18 @@ def config(argv=None, parser=None): 'Options: %(choices)s. Default: ' '%(default)s')) + # TODO ideally the blending should be optimised so we don't need this param to skip it, e.g. + # 1. Only blend in a buffer zone around cutline edges (e.g., 100-200 pixels) + # 2. Skip blending for interior regions that are far from edges + # 3. This would require detecting which blocks intersect cutline boundaries + # We have block-based processing already in place in orthophoto.py, so need logic to determine + # if a block needs blending based on its proximity to cutlines + parser.add_argument('--merge-skip-blending', + action=StoreTrue, + nargs=0, + default=False, + help='During the orthophoto merging, skip expensive blending operation: %(default)s') + parser.add_argument('--force-gps', action=StoreTrue, nargs=0, diff --git a/opendm/orthophoto.py b/opendm/orthophoto.py index b78a4f5c..27f42b81 100644 --- a/opendm/orthophoto.py +++ b/opendm/orthophoto.py @@ -265,7 +265,7 @@ def feather_raster(input_raster, output_raster, blend_distance=20): return output_raster -def merge(input_ortho_and_ortho_cuts, output_orthophoto, orthophoto_vars={}): +def merge(input_ortho_and_ortho_cuts, output_orthophoto, orthophoto_vars={}, merge_skip_blending=False): """ Based on https://github.com/mapbox/rio-merge-rgba/ Merge orthophotos around cutlines using a blend buffer. @@ -334,6 +334,10 @@ def merge(input_ortho_and_ortho_cuts, output_orthophoto, orthophoto_vars={}): profile["bigtiff"] = orthophoto_vars.get('BIGTIFF', 'IF_SAFER') profile.update() + # Log here to avoid logging in each block processed + if merge_skip_blending: + log.ODM_INFO("Skipping second and third pass orthophoto blending, as --merge-skip-blending passed") + # create destination file with rasterio.open(output_orthophoto, "w", **profile) as dstrast: dstrast.colorinterp = colorinterp @@ -372,6 +376,11 @@ def merge(input_ortho_and_ortho_cuts, output_orthophoto, orthophoto_vars={}): if np.count_nonzero(dstarr[-1]) == blocksize: break + # Skip expensive blending operations if flag passed + if merge_skip_blending: + dstrast.write(dstarr, window=dst_window) + continue + # Second pass, write all feathered rasters # blending the edges for src, _ in sources: diff --git a/stages/splitmerge.py b/stages/splitmerge.py index 62d12b93..af39404c 100644 --- a/stages/splitmerge.py +++ b/stages/splitmerge.py @@ -262,7 +262,7 @@ class ODMMergeStage(types.ODM_Stage): os.remove(tree.odm_orthophoto_tif) orthophoto_vars = orthophoto.get_orthophoto_vars(args) - orthophoto.merge(all_orthos_and_ortho_cuts, tree.odm_orthophoto_tif, orthophoto_vars) + orthophoto.merge(all_orthos_and_ortho_cuts, tree.odm_orthophoto_tif, orthophoto_vars, args.merge_skip_blending) orthophoto.post_orthophoto_steps(args, merged_bounds_file, tree.odm_orthophoto_tif, tree.orthophoto_tiles, args.orthophoto_resolution, reconstruction, tree, False) elif len(all_orthos_and_ortho_cuts) == 1: