OpenDroneMap-ODM/stages/odm_georeferencing.py

161 wiersze
8.3 KiB
Python
Czysty Zwykły widok Historia

2016-12-14 18:36:23 +00:00
import os
2018-06-04 01:01:04 +00:00
import struct
import pipes
2015-12-01 16:25:14 +00:00
from opendm import io
from opendm import log
2015-12-10 17:17:39 +00:00
from opendm import types
2015-12-01 16:25:14 +00:00
from opendm import system
from opendm import context
2018-01-04 17:08:59 +00:00
from opendm.cropper import Cropper
from opendm import point_cloud
2015-12-01 16:25:14 +00:00
2016-02-25 20:02:48 +00:00
2019-04-22 19:14:39 +00:00
class ODMGeoreferencingStage(types.ODM_Stage):
def process(self, args, outputs):
tree = outputs['tree']
reconstruction = outputs['reconstruction']
2015-12-01 16:25:14 +00:00
gcpfile = tree.odm_georeferencing_gcp
doPointCloudGeo = True
transformPointCloud = True
2019-04-22 19:14:39 +00:00
verbose = '-verbose' if self.params.get('verbose') else ''
geo_ref = reconstruction.georef
2015-12-01 16:25:14 +00:00
runs = [{
2017-04-05 18:27:52 +00:00
'georeferencing_dir': tree.odm_georeferencing,
'texturing_dir': tree.odm_texturing,
'model': os.path.join(tree.odm_texturing, tree.odm_textured_model_obj)
}]
if args.skip_3dmodel:
runs = []
if not args.use_3dmesh:
2019-02-15 17:15:01 +00:00
# Make sure 2.5D mesh is georeferenced before the 3D mesh
# Because it will be used to calculate a transform
# for the point cloud. If we use the 3D model transform,
# DEMs and orthophoto might not align!
runs.insert(0, {
2017-04-05 18:27:52 +00:00
'georeferencing_dir': tree.odm_25dgeoreferencing,
'texturing_dir': tree.odm_25dtexturing,
'model': os.path.join(tree.odm_25dtexturing, tree.odm_textured_model_obj)
2019-02-15 17:15:01 +00:00
})
for r in runs:
2017-04-05 18:27:52 +00:00
odm_georeferencing_model_obj_geo = os.path.join(r['texturing_dir'], tree.odm_georeferencing_model_obj_geo)
odm_georeferencing_log = os.path.join(r['georeferencing_dir'], tree.odm_georeferencing_log)
odm_georeferencing_transform_file = os.path.join(r['georeferencing_dir'], tree.odm_georeferencing_transform_file)
odm_georeferencing_model_txt_geo_file = os.path.join(r['georeferencing_dir'], tree.odm_georeferencing_model_txt_geo)
if not io.file_exists(odm_georeferencing_model_obj_geo) or \
2019-04-22 19:14:39 +00:00
not io.file_exists(tree.odm_georeferencing_model_laz) or self.rerun():
# odm_georeference definitions
kwargs = {
'bin': context.odm_modules_path,
'input_pc_file': tree.filtered_point_cloud,
'bundle': tree.opensfm_bundle,
2017-08-24 19:19:51 +00:00
'imgs': tree.dataset_raw,
'imgs_list': tree.opensfm_bundle_list,
'model': r['model'],
'log': odm_georeferencing_log,
'input_trans_file': tree.opensfm_transformation,
'transform_file': odm_georeferencing_transform_file,
'coords': tree.odm_georeferencing_coords,
'output_pc_file': tree.odm_georeferencing_model_laz,
'geo_sys': odm_georeferencing_model_txt_geo_file,
'model_geo': odm_georeferencing_model_obj_geo,
'gcp': gcpfile,
'verbose': verbose
}
2018-07-01 21:28:55 +00:00
if transformPointCloud:
kwargs['pc_params'] = '-inputPointCloudFile {input_pc_file} -outputPointCloudFile {output_pc_file}'.format(**kwargs)
2019-01-26 04:40:30 +00:00
if geo_ref and geo_ref.projection and geo_ref.projection.srs:
kwargs['pc_params'] += ' -outputPointCloudSrs %s' % pipes.quote(geo_ref.projection.srs)
else:
log.ODM_WARNING('NO SRS: The output point cloud will not have a SRS.')
2018-07-04 19:33:11 +00:00
else:
kwargs['pc_params'] = ''
# Check to see if the GCP file exists
2019-04-22 19:14:39 +00:00
if not self.params.get('use_exif') and (self.params.get('gcp_file') or tree.odm_georeferencing_gcp):
log.ODM_INFO('Found %s' % gcpfile)
try:
system.run('{bin}/odm_georef -bundleFile {bundle} -imagesPath {imgs} -imagesListPath {imgs_list} '
'-inputFile {model} -outputFile {model_geo} '
'{pc_params} {verbose} '
'-logFile {log} -outputTransformFile {transform_file} -georefFileOutputPath {geo_sys} -gcpFile {gcp} '
'-outputCoordFile {coords}'.format(**kwargs))
except Exception:
log.ODM_EXCEPTION('Georeferencing failed. ')
2019-04-22 19:14:39 +00:00
exit(1)
elif io.file_exists(tree.opensfm_transformation) and io.file_exists(tree.odm_georeferencing_coords):
log.ODM_INFO('Running georeferencing with OpenSfM transformation matrix')
system.run('{bin}/odm_georef -bundleFile {bundle} -inputTransformFile {input_trans_file} -inputCoordFile {coords} '
'-inputFile {model} -outputFile {model_geo} '
'{pc_params} {verbose} '
'-logFile {log} -outputTransformFile {transform_file} -georefFileOutputPath {geo_sys}'.format(**kwargs))
elif io.file_exists(tree.odm_georeferencing_coords):
log.ODM_INFO('Running georeferencing with generated coords file.')
system.run('{bin}/odm_georef -bundleFile {bundle} -inputCoordFile {coords} '
'-inputFile {model} -outputFile {model_geo} '
'{pc_params} {verbose} '
2017-06-12 11:57:37 +00:00
'-logFile {log} -outputTransformFile {transform_file} -georefFileOutputPath {geo_sys}'.format(**kwargs))
else:
log.ODM_WARNING('Georeferencing failed. Make sure your '
'photos have geotags in the EXIF or you have '
'provided a GCP file. ')
doPointCloudGeo = False # skip the rest of the georeferencing
if doPointCloudGeo:
# update images metadata
geo_ref.extract_offsets(odm_georeferencing_model_txt_geo_file)
reconstruction.georef = geo_ref
# XYZ point cloud output
2018-06-04 01:01:04 +00:00
if args.pc_csv:
log.ODM_INFO("Creating geo-referenced CSV file (XYZ format)")
system.run("pdal translate -i \"{}\" "
"-o \"{}\" "
"--writers.text.format=csv "
"--writers.text.order=\"X,Y,Z\" "
"--writers.text.keep_unspecified=false ".format(
tree.odm_georeferencing_model_laz,
tree.odm_georeferencing_xyz_file))
2019-02-22 20:28:37 +00:00
# LAS point cloud output
if args.pc_las:
log.ODM_INFO("Creating geo-referenced LAS file")
system.run("pdal translate -i \"{}\" "
"-o \"{}\" ".format(
tree.odm_georeferencing_model_laz,
tree.odm_georeferencing_model_las))
2018-01-04 17:08:59 +00:00
if args.crop > 0:
log.ODM_INFO("Calculating cropping area and generating bounds shapefile from point cloud")
cropper = Cropper(tree.odm_georeferencing, 'odm_georeferenced_model')
decimation_step = 40 if args.fast_orthophoto or args.use_opensfm_dense else 90
# More aggressive decimation for large datasets
if not args.fast_orthophoto:
decimation_step *= int(len(reconstruction.photos) / 1000) + 1
cropper.create_bounds_gpkg(tree.odm_georeferencing_model_laz, args.crop,
decimation_step=decimation_step)
2018-01-04 17:08:59 +00:00
# Do not execute a second time, since
# We might be doing georeferencing for
# multiple models (3D, 2.5D, ...)
doPointCloudGeo = False
transformPointCloud = False
else:
log.ODM_WARNING('Found a valid georeferenced model in: %s'
2018-12-26 18:37:31 +00:00
% tree.odm_georeferencing_model_laz)