feat: add --compact-overviews config param for slim orthophoto overviews

pull/1935/head
spwoodcock 2025-10-06 14:01:21 +01:00
rodzic 29800652c6
commit 7f3a40c45f
3 zmienionych plików z 65 dodań i 32 usunięć

Wyświetl plik

@ -5,7 +5,7 @@ from opendm.concurrency import get_max_memory
from opendm import io
from opendm import log
def convert_to_cogeo(src_path, blocksize=256, max_workers=1, compression="DEFLATE"):
def convert_to_cogeo(src_path, blocksize=256, max_workers=1, compression="DEFLATE", compact_overviews=False):
"""
Guarantee that the .tif passed as an argument is a Cloud Optimized GeoTIFF (cogeo)
The file is destructively converted into a cogeo.
@ -15,39 +15,49 @@ def convert_to_cogeo(src_path, blocksize=256, max_workers=1, compression="DEFLAT
"""
if not os.path.isfile(src_path):
logger.warning("Cannot convert to cogeo: %s (file does not exist)" % src_path)
log.ODM_WARNING("Cannot convert to cogeo: %s (file does not exist)" % src_path)
return False
log.ODM_INFO("Optimizing %s as Cloud Optimized GeoTIFF" % src_path)
tmpfile = io.related_file_path(src_path, postfix='_cogeo')
swapfile = io.related_file_path(src_path, postfix='_cogeo_swap')
kwargs = {
'threads': max_workers if max_workers else 'ALL_CPUS',
'blocksize': blocksize,
'max_memory': get_max_memory(),
'src_path': src_path,
'tmpfile': tmpfile,
'compress': compression,
'predictor': '2' if compression in ['LZW', 'DEFLATE'] else '1',
}
# Configuration params
threads = max_workers if max_workers else 'ALL_CPUS'
predictor = '2' if compression in ['LZW', 'DEFLATE'] else '1'
max_memory = get_max_memory()
# Build gdal_translate command
cmd = [
'gdal_translate',
'-of', 'COG',
'-co', f'NUM_THREADS={threads}',
'-co', f'BLOCKSIZE={blocksize}',
'-co', f'COMPRESS={compression}',
'-co', f'PREDICTOR={predictor}',
'-co', 'BIGTIFF=IF_SAFER',
'-co', 'RESAMPLING=NEAREST',
]
if compact_overviews:
cmd.extend([
'-co', 'PHOTOMETRIC_OVERVIEW=YCBCR',
'-co', 'INTERLEAVE_OVERVIEW=PIXEL',
])
cmd.extend([
'--config', 'GDAL_CACHEMAX', f'{max_memory}%',
'--config', 'GDAL_NUM_THREADS', str(threads),
src_path,
tmpfile,
])
try:
system.run("gdal_translate "
"-of COG "
"-co NUM_THREADS={threads} "
"-co BLOCKSIZE={blocksize} "
"-co COMPRESS={compress} "
"-co PREDICTOR={predictor} "
"-co BIGTIFF=IF_SAFER "
"-co RESAMPLING=NEAREST "
"--config GDAL_CACHEMAX {max_memory}% "
"--config GDAL_NUM_THREADS {threads} "
"\"{src_path}\" \"{tmpfile}\" ".format(**kwargs))
system.run(' '.join(cmd))
except Exception as e:
log.ODM_WARNING("Cannot create Cloud Optimized GeoTIFF: %s" % str(e))
return False
if os.path.isfile(tmpfile):
shutil.move(src_path, swapfile) # Move to swap location
@ -57,6 +67,7 @@ def convert_to_cogeo(src_path, blocksize=256, max_workers=1, compression="DEFLAT
except IOError as e:
log.ODM_WARNING("Cannot move %s to %s: %s" % (tmpfile, src_path, str(e)))
shutil.move(swapfile, src_path) # Attempt to restore
return False
if os.path.isfile(swapfile):
os.remove(swapfile)

Wyświetl plik

@ -740,6 +740,12 @@ def config(argv=None, parser=None):
default=False,
help='Build orthophoto overviews for faster display in programs such as QGIS. Default: %(default)s')
parser.add_argument('--compact-overviews',
action=StoreTrue,
nargs=0,
default=False,
help='Use addtional compression for orthophoto overviews, to improve viewing performance. Default: %(default)s')
parser.add_argument('--cog',
action=StoreTrue,
nargs=0,

Wyświetl plik

@ -29,15 +29,31 @@ def get_orthophoto_vars(args):
'NUM_THREADS': args.max_concurrency
}
def build_overviews(orthophoto_file):
def build_overviews(orthophoto_file, compact_overviews=False):
"""
Build overviews for an orthophoto file using gdaladdo
:param orthophoto_file: path to orthophoto
:param compact_overviews: whether to use compact overview settings
"""
log.ODM_INFO("Building Overviews")
kwargs = {'orthophoto': orthophoto_file}
# Run gdaladdo
system.run('gdaladdo -r average '
'--config BIGTIFF_OVERVIEW IF_SAFER '
'--config COMPRESS_OVERVIEW JPEG '
'{orthophoto} 2 4 8 16'.format(**kwargs))
# Build gdaladdo command
cmd = [
'gdaladdo',
'-r', 'average',
'--config', 'BIGTIFF_OVERVIEW', 'IF_SAFER',
'--config', 'COMPRESS_OVERVIEW', 'JPG',
]
if compact_overviews:
cmd.extend([
'--config', 'PHOTOMETRIC_OVERVIEW', 'YCBCR',
'--config', 'INTERLEAVE_OVERVIEW', 'PIXEL',
])
cmd.extend([orthophoto_file, '2', '4', '8', '16'])
system.run(' '.join(cmd))
def generate_png(orthophoto_file, output_file=None, outsize=None):
if output_file is None:
@ -172,7 +188,7 @@ def post_orthophoto_steps(args, bounds_file_path, orthophoto_file, orthophoto_ti
Cropper.crop(bounds_file_path, orthophoto_file, get_orthophoto_vars(args), keep_original=not args.optimize_disk_space, warp_options=['-dstalpha'])
if args.build_overviews and not args.cog:
build_overviews(orthophoto_file)
build_overviews(orthophoto_file, compact_overviews=args.compact_overviews)
if args.orthophoto_png:
generate_png(orthophoto_file)
@ -186,7 +202,7 @@ def post_orthophoto_steps(args, bounds_file_path, orthophoto_file, orthophoto_ti
generate_orthophoto_tiles(orthophoto_file, orthophoto_tiles_dir, args.max_concurrency, resolution)
if args.cog:
convert_to_cogeo(orthophoto_file, max_workers=args.max_concurrency, compression=args.orthophoto_compression)
convert_to_cogeo(orthophoto_file, max_workers=args.max_concurrency, compression=args.orthophoto_compression, compact_overviews=args.compact_overviews)
generate_extent_polygon(orthophoto_file)
generate_tfw(orthophoto_file)