kopia lustrzana https://github.com/OpenDroneMap/ODM
151 wiersze
6.9 KiB
Python
151 wiersze
6.9 KiB
Python
import os
|
|
|
|
from opendm import io
|
|
from opendm import log
|
|
from opendm import system
|
|
from opendm import context
|
|
from opendm import types
|
|
from opendm import gsd
|
|
from opendm import orthophoto
|
|
from opendm.concurrency import get_max_memory_mb
|
|
from opendm.cutline import compute_cutline
|
|
from opendm.utils import double_quote
|
|
from opendm import pseudogeo
|
|
from opendm.multispectral import get_primary_band_name
|
|
|
|
|
|
class ODMOrthoPhotoStage(types.ODM_Stage):
|
|
def process(self, args, outputs):
|
|
tree = outputs['tree']
|
|
reconstruction = outputs['reconstruction']
|
|
|
|
# define paths and create working directories
|
|
system.mkdir_p(tree.odm_orthophoto)
|
|
|
|
if args.skip_orthophoto:
|
|
log.ODM_WARNING("--skip-orthophoto is set, no orthophoto will be generated")
|
|
return
|
|
|
|
if not io.file_exists(tree.odm_orthophoto_tif) or self.rerun():
|
|
|
|
resolution = gsd.cap_resolution(args.orthophoto_resolution, tree.opensfm_reconstruction,
|
|
ignore_gsd=args.ignore_gsd,
|
|
ignore_resolution=(not reconstruction.is_georeferenced()) and args.ignore_gsd,
|
|
has_gcp=reconstruction.has_gcp())
|
|
|
|
# odm_orthophoto definitions
|
|
kwargs = {
|
|
'odm_ortho_bin': context.odm_orthophoto_path,
|
|
'log': tree.odm_orthophoto_log,
|
|
'ortho': tree.odm_orthophoto_render,
|
|
'corners': tree.odm_orthophoto_corners,
|
|
'res': 1.0 / (resolution/100.0),
|
|
'bands': '',
|
|
'depth_idx': '',
|
|
'inpaint': '',
|
|
'utm_offsets': '',
|
|
'a_srs': '',
|
|
'vars': '',
|
|
'gdal_configs': '--config GDAL_CACHEMAX %s' % (get_max_memory_mb() * 1024 * 1024)
|
|
}
|
|
|
|
models = []
|
|
|
|
if args.use_3dmesh:
|
|
base_dir = tree.odm_texturing
|
|
else:
|
|
base_dir = tree.odm_25dtexturing
|
|
|
|
model_file = tree.odm_textured_model_obj
|
|
|
|
if reconstruction.multi_camera:
|
|
for band in reconstruction.multi_camera:
|
|
primary = band['name'] == get_primary_band_name(reconstruction.multi_camera, args.primary_band)
|
|
subdir = ""
|
|
if not primary:
|
|
subdir = band['name'].lower()
|
|
models.append(os.path.join(base_dir, subdir, model_file))
|
|
kwargs['bands'] = '-bands %s' % (','.join([double_quote(b['name']) for b in reconstruction.multi_camera]))
|
|
|
|
# If a RGB band is present,
|
|
# use bit depth of the first non-RGB band
|
|
depth_idx = None
|
|
all_bands = [b['name'].lower() for b in reconstruction.multi_camera]
|
|
for b in ['rgb', 'redgreenblue']:
|
|
if b in all_bands:
|
|
for idx in range(len(all_bands)):
|
|
if all_bands[idx] != b:
|
|
depth_idx = idx
|
|
break
|
|
break
|
|
|
|
if depth_idx is not None:
|
|
kwargs['depth_idx'] = '-outputDepthIdx %s' % depth_idx
|
|
else:
|
|
models.append(os.path.join(base_dir, model_file))
|
|
|
|
# Perform edge inpainting on georeferenced RGB datasets
|
|
if reconstruction.is_georeferenced():
|
|
kwargs['inpaint'] = "-inpaintThreshold 1.0"
|
|
|
|
# Thermal dataset with single band
|
|
if reconstruction.photos[0].band_name.upper() == "LWIR":
|
|
kwargs['bands'] = '-bands lwir'
|
|
|
|
kwargs['models'] = ','.join(map(double_quote, models))
|
|
|
|
if reconstruction.is_georeferenced():
|
|
orthophoto_vars = orthophoto.get_orthophoto_vars(args)
|
|
kwargs['utm_offsets'] = "-utm_north_offset %s -utm_east_offset %s" % (reconstruction.georef.utm_north_offset, reconstruction.georef.utm_east_offset)
|
|
kwargs['a_srs'] = "-a_srs \"%s\"" % reconstruction.georef.proj4()
|
|
kwargs['vars'] = ' '.join(['-co %s=%s' % (k, orthophoto_vars[k]) for k in orthophoto_vars])
|
|
kwargs['ortho'] = tree.odm_orthophoto_tif # Render directly to final file
|
|
|
|
# run odm_orthophoto
|
|
log.ODM_INFO('Creating GeoTIFF')
|
|
system.run('"{odm_ortho_bin}" -inputFiles {models} '
|
|
'-logFile "{log}" -outputFile "{ortho}" -resolution {res} -verbose '
|
|
'-outputCornerFile "{corners}" {bands} {depth_idx} {inpaint} '
|
|
'{utm_offsets} {a_srs} {vars} {gdal_configs} '.format(**kwargs), env_vars={'OMP_NUM_THREADS': args.max_concurrency})
|
|
|
|
# Create georeferenced GeoTiff
|
|
if reconstruction.is_georeferenced():
|
|
bounds_file_path = os.path.join(tree.odm_georeferencing, 'odm_georeferenced_model.bounds.gpkg')
|
|
|
|
# Cutline computation, before cropping
|
|
# We want to use the full orthophoto, not the cropped one.
|
|
if args.orthophoto_cutline:
|
|
cutline_file = os.path.join(tree.odm_orthophoto, "cutline.gpkg")
|
|
|
|
compute_cutline(tree.odm_orthophoto_tif,
|
|
bounds_file_path,
|
|
cutline_file,
|
|
args.max_concurrency,
|
|
scale=0.25)
|
|
|
|
orthophoto.compute_mask_raster(tree.odm_orthophoto_tif, cutline_file,
|
|
os.path.join(tree.odm_orthophoto, "odm_orthophoto_cut.tif"),
|
|
blend_distance=20, only_max_coords_feature=True)
|
|
|
|
orthophoto.post_orthophoto_steps(args, bounds_file_path, tree.odm_orthophoto_tif, tree.orthophoto_tiles, resolution)
|
|
|
|
# Generate feathered orthophoto also
|
|
if args.orthophoto_cutline:
|
|
orthophoto.feather_raster(tree.odm_orthophoto_tif,
|
|
os.path.join(tree.odm_orthophoto, "odm_orthophoto_feathered.tif"),
|
|
blend_distance=20
|
|
)
|
|
|
|
else:
|
|
if io.file_exists(tree.odm_orthophoto_render):
|
|
pseudogeo.add_pseudo_georeferencing(tree.odm_orthophoto_render)
|
|
log.ODM_INFO("Renaming %s --> %s" % (tree.odm_orthophoto_render, tree.odm_orthophoto_tif))
|
|
os.replace(tree.odm_orthophoto_render, tree.odm_orthophoto_tif)
|
|
else:
|
|
log.ODM_WARNING("Could not generate an orthophoto (it did not render)")
|
|
else:
|
|
log.ODM_WARNING('Found a valid orthophoto in: %s' % tree.odm_orthophoto_tif)
|
|
|
|
if io.file_exists(tree.odm_orthophoto_render):
|
|
os.remove(tree.odm_orthophoto_render)
|