def process(self, args, outputs): tree = outputs['tree'] if outputs['large']: if not os.path.exists(tree.submodels_path): log.ODM_ERROR( "We reached the merge stage, but %s folder does not exist. Something must have gone wrong at an earlier stage. Check the log and fix possible problem before restarting?" % tree.submodels_path) exit(1) # Merge point clouds if args.merge in ['all', 'pointcloud']: if not io.file_exists(tree.odm_georeferencing_model_laz) or self.rerun(): all_point_clouds = get_submodel_paths(tree.submodels_path, "odm_georeferencing", "odm_georeferenced_model.laz") try: point_cloud.merge(all_point_clouds, tree.odm_georeferencing_model_laz) point_cloud.post_point_cloud_steps(args, tree) except Exception as e: log.ODM_WARNING("Could not merge point cloud: %s (skipping)" % str(e)) else: log.ODM_WARNING("Found merged point cloud in %s" % tree.odm_georeferencing_model_laz) self.update_progress(25) # Merge crop bounds merged_bounds_file = os.path.join(tree.odm_georeferencing, 'odm_georeferenced_model.bounds.gpkg') if not io.file_exists(merged_bounds_file) or self.rerun(): all_bounds = get_submodel_paths(tree.submodels_path, 'odm_georeferencing', 'odm_georeferenced_model.bounds.gpkg') log.ODM_INFO("Merging all crop bounds: %s" % all_bounds) if len(all_bounds) > 0: # Calculate a new crop area # based on the convex hull of all crop areas of all submodels # (without a buffer, otherwise we are double-cropping) Cropper.merge_bounds(all_bounds, merged_bounds_file, 0) else: log.ODM_WARNING("No bounds found for any submodel.") # Merge orthophotos if args.merge in ['all', 'orthophoto']: if not io.dir_exists(tree.odm_orthophoto): system.mkdir_p(tree.odm_orthophoto) if not io.file_exists(tree.odm_orthophoto_tif) or self.rerun(): all_orthos_and_ortho_cuts = get_all_submodel_paths(tree.submodels_path, os.path.join("odm_orthophoto", "odm_orthophoto_feathered.tif"), os.path.join("odm_orthophoto", "odm_orthophoto_cut.tif"), ) if len(all_orthos_and_ortho_cuts) > 1: log.ODM_INFO( "Found %s submodels with valid orthophotos and cutlines" % len(all_orthos_and_ortho_cuts)) # TODO: histogram matching via rasterio # currently parts have different color tones if io.file_exists(tree.odm_orthophoto_tif): 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.post_orthophoto_steps(args, merged_bounds_file, tree.odm_orthophoto_tif, tree.orthophoto_tiles) elif len(all_orthos_and_ortho_cuts) == 1: # Simply copy log.ODM_WARNING("A single orthophoto/cutline pair was found between all submodels.") shutil.copyfile(all_orthos_and_ortho_cuts[0][0], tree.odm_orthophoto_tif) else: log.ODM_WARNING( "No orthophoto/cutline pairs were found in any of the submodels. No orthophoto will be generated.") else: log.ODM_WARNING("Found merged orthophoto in %s" % tree.odm_orthophoto_tif) self.update_progress(75) # Merge DEMs def merge_dems(dem_filename, human_name): if not io.dir_exists(tree.path('odm_dem')): system.mkdir_p(tree.path('odm_dem')) dem_file = tree.path("odm_dem", dem_filename) if not io.file_exists(dem_file) or self.rerun(): all_dems = get_submodel_paths(tree.submodels_path, "odm_dem", dem_filename) log.ODM_INFO("Merging %ss" % human_name) # Merge dem_vars = utils.get_dem_vars(args) eu_map_source = None # Default # Use DSM's euclidean map for DTMs # (requires the DSM to be computed) if human_name == "DTM": eu_map_source = "dsm" euclidean_merge_dems(all_dems, dem_file, dem_vars, euclidean_map_source=eu_map_source) if io.file_exists(dem_file): # Crop if args.crop > 0: Cropper.crop(merged_bounds_file, dem_file, dem_vars, keep_original=not args.optimize_disk_space) log.ODM_INFO("Created %s" % dem_file) if args.tiles: generate_dem_tiles(dem_file, tree.path("%s_tiles" % human_name.lower()), args.max_concurrency) else: log.ODM_WARNING("Cannot merge %s, %s was not created" % (human_name, dem_file)) else: log.ODM_WARNING("Found merged %s in %s" % (human_name, dem_filename)) if args.merge in ['all', 'dem'] and args.dsm: merge_dems("dsm.tif", "DSM") if args.merge in ['all', 'dem'] and args.dtm: merge_dems("dtm.tif", "DTM") self.update_progress(95) # Merge reports if not io.dir_exists(tree.odm_report): system.mkdir_p(tree.odm_report) geojson_shots = tree.path(tree.odm_report, "shots.geojson") if not io.file_exists(geojson_shots) or self.rerun(): geojson_shots_files = get_submodel_paths(tree.submodels_path, "odm_report", "shots.geojson") log.ODM_INFO("Merging %s shots.geojson files" % len(geojson_shots_files)) merge_geojson_shots(geojson_shots_files, geojson_shots) else: log.ODM_WARNING("Found merged shots.geojson in %s" % tree.odm_report) # Stop the pipeline short! We're done. self.next_stage = None else: log.ODM_INFO("Normal dataset, nothing to merge.") self.progress = 0.0
def process(self, args, outputs): tree = outputs['tree'] reconstruction = outputs['reconstruction'] # Export GCP information if available gcp_export_file = tree.path("odm_georeferencing", "ground_control_points.gpkg") gcp_gml_export_file = tree.path("odm_georeferencing", "ground_control_points.gml") gcp_geojson_export_file = tree.path("odm_georeferencing", "ground_control_points.geojson") if reconstruction.has_gcp() and (not io.file_exists(gcp_export_file) or self.rerun()): octx = OSFMContext(tree.opensfm) gcps = octx.ground_control_points(reconstruction.georef.proj4()) if len(gcps): gcp_schema = { 'geometry': 'Point', 'properties': OrderedDict([ ('id', 'str'), ('observations_count', 'int'), ('observations_list', 'str'), ('error_x', 'float'), ('error_y', 'float'), ('error_z', 'float'), ]) } # Write GeoPackage with fiona.open(gcp_export_file, 'w', driver="GPKG", crs=fiona.crs.from_string(reconstruction.georef.proj4()), schema=gcp_schema) as f: for gcp in gcps: f.write({ 'geometry': { 'type': 'Point', 'coordinates': gcp['coordinates'], }, 'properties': OrderedDict([ ('id', gcp['id']), ('observations_count', len(gcp['observations'])), ('observations_list', ",".join([obs['shot_id'] for obs in gcp['observations']])), ('error_x', gcp['error'][0]), ('error_y', gcp['error'][1]), ('error_z', gcp['error'][2]), ]) }) # Write GML try: system.run('ogr2ogr -of GML "{}" "{}"'.format(gcp_gml_export_file, gcp_export_file)) except Exception as e: log.ODM_WARNING("Cannot generate ground control points GML file: %s" % str(e)) # Write GeoJSON geojson = { 'type': 'FeatureCollection', 'features': [] } from_srs = CRS.from_proj4(reconstruction.georef.proj4()) to_srs = CRS.from_epsg(4326) transformer = location.transformer(from_srs, to_srs) for gcp in gcps: properties = gcp.copy() del properties['coordinates'] geojson['features'].append({ 'type': 'Feature', 'geometry': { 'type': 'Point', 'coordinates': transformer.TransformPoint(*gcp['coordinates']), }, 'properties': properties }) with open(gcp_geojson_export_file, 'w') as f: f.write(json.dumps(geojson, indent=4)) else: log.ODM_WARNING("GCPs could not be loaded for writing to %s" % gcp_export_file) if not io.file_exists(tree.odm_georeferencing_model_laz) or self.rerun(): cmd = ('pdal translate -i "%s" -o \"%s\"' % (tree.filtered_point_cloud, tree.odm_georeferencing_model_laz)) stages = ["ferry"] params = [ '--filters.ferry.dimensions="views => UserData"', '--writers.las.compression="lazip"', ] if reconstruction.is_georeferenced(): log.ODM_INFO("Georeferencing point cloud") stages.append("transformation") params += [ '--filters.transformation.matrix="1 0 0 %s 0 1 0 %s 0 0 1 0 0 0 0 1"' % reconstruction.georef.utm_offset(), '--writers.las.offset_x=%s' % reconstruction.georef.utm_east_offset, '--writers.las.offset_y=%s' % reconstruction.georef.utm_north_offset, '--writers.las.scale_x=0.001', '--writers.las.scale_y=0.001', '--writers.las.scale_z=0.001', '--writers.las.offset_z=0', '--writers.las.a_srs="%s"' % reconstruction.georef.proj4() ] if reconstruction.has_gcp() and io.file_exists(gcp_gml_export_file): log.ODM_INFO("Embedding GCP info in point cloud") params += [ '--writers.las.vlrs="{\\\"filename\\\": \\\"%s\\\", \\\"user_id\\\": \\\"ODM_GCP\\\", \\\"description\\\": \\\"Ground Control Points (GML)\\\"}"' % gcp_gml_export_file.replace(os.sep, "/") ] system.run(cmd + ' ' + ' '.join(stages) + ' ' + ' '.join(params)) self.update_progress(50) 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') if args.fast_orthophoto: decimation_step = 4 else: decimation_step = 40 # More aggressive decimation for large datasets if not args.fast_orthophoto: decimation_step *= int(len(reconstruction.photos) / 1000) + 1 decimation_step = min(decimation_step, 95) try: cropper.create_bounds_gpkg(tree.odm_georeferencing_model_laz, args.crop, decimation_step=decimation_step) except: log.ODM_WARNING("Cannot calculate crop bounds! We will skip cropping") args.crop = 0 if 'boundary' in outputs and args.crop == 0: log.ODM_INFO("Using boundary JSON as cropping area") bounds_base, _ = os.path.splitext(tree.odm_georeferencing_model_laz) bounds_json = bounds_base + ".bounds.geojson" bounds_gpkg = bounds_base + ".bounds.gpkg" export_to_bounds_files(outputs['boundary'], reconstruction.get_proj_srs(), bounds_json, bounds_gpkg) else: log.ODM_INFO("Converting point cloud (non-georeferenced)") system.run(cmd + ' ' + ' '.join(stages) + ' ' + ' '.join(params)) point_cloud.post_point_cloud_steps(args, tree, self.rerun()) else: log.ODM_WARNING('Found a valid georeferenced model in: %s' % tree.odm_georeferencing_model_laz) if args.optimize_disk_space and io.file_exists(tree.odm_georeferencing_model_laz) and io.file_exists(tree.filtered_point_cloud): os.remove(tree.filtered_point_cloud)
def process(self, args, outputs): tree = outputs['tree'] reconstruction = outputs['reconstruction'] if not io.file_exists( tree.odm_georeferencing_model_laz) or self.rerun(): cmd = ( 'pdal translate -i "%s" -o \"%s\"' % (tree.filtered_point_cloud, tree.odm_georeferencing_model_laz)) stages = ["ferry"] params = [ '--filters.ferry.dimensions="views => UserData"', '--writers.las.compression="lazip"', ] if reconstruction.is_georeferenced(): log.ODM_INFO("Georeferencing point cloud") stages.append("transformation") params += [ '--filters.transformation.matrix="1 0 0 %s 0 1 0 %s 0 0 1 0 0 0 0 1"' % reconstruction.georef.utm_offset(), '--writers.las.offset_x=%s' % reconstruction.georef.utm_east_offset, '--writers.las.offset_y=%s' % reconstruction.georef.utm_north_offset, '--writers.las.offset_z=0', '--writers.las.a_srs="%s"' % reconstruction.georef.proj4() ] system.run(cmd + ' ' + ' '.join(stages) + ' ' + ' '.join(params)) self.update_progress(50) 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') if args.fast_orthophoto: decimation_step = 10 else: decimation_step = 40 # More aggressive decimation for large datasets if not args.fast_orthophoto: decimation_step *= int( len(reconstruction.photos) / 1000) + 1 decimation_step = min(decimation_step, 95) try: cropper.create_bounds_gpkg( tree.odm_georeferencing_model_laz, args.crop, decimation_step=decimation_step) except: log.ODM_WARNING( "Cannot calculate crop bounds! We will skip cropping" ) args.crop = 0 else: log.ODM_INFO("Converting point cloud (non-georeferenced)") system.run(cmd + ' ' + ' '.join(stages) + ' ' + ' '.join(params)) point_cloud.post_point_cloud_steps(args, tree, self.rerun()) else: log.ODM_WARNING('Found a valid georeferenced model in: %s' % tree.odm_georeferencing_model_laz) if args.optimize_disk_space and io.file_exists( tree.odm_georeferencing_model_laz) and io.file_exists( tree.filtered_point_cloud): os.remove(tree.filtered_point_cloud)
def process(self, args, outputs): tree = outputs['tree'] reconstruction = outputs['reconstruction'] doPointCloudGeo = True transformPointCloud = True verbose = '-verbose' if self.params.get('verbose') else '' class nonloc: runs = [] def add_run(primary=True, band=None): subdir = "" if not primary and band is not None: subdir = band # 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! if not args.use_3dmesh: nonloc.runs += [{ 'georeferencing_dir': os.path.join(tree.odm_25dgeoreferencing, subdir), 'texturing_dir': os.path.join(tree.odm_25dtexturing, subdir), }] if not args.skip_3dmodel and (primary or args.use_3dmesh): nonloc.runs += [{ 'georeferencing_dir': tree.odm_georeferencing, 'texturing_dir': os.path.join(tree.odm_texturing, subdir), }] if reconstruction.multi_camera: for band in reconstruction.multi_camera: primary = band['name'] == get_primary_band_name( reconstruction.multi_camera, args.primary_band) add_run(primary, band['name'].lower()) else: add_run() progress_per_run = 100.0 / len(nonloc.runs) progress = 0.0 for r in nonloc.runs: if not io.dir_exists(r['georeferencing_dir']): system.mkdir_p(r['georeferencing_dir']) odm_georeferencing_model_obj_geo = os.path.join( r['texturing_dir'], tree.odm_georeferencing_model_obj_geo) odm_georeferencing_model_obj = os.path.join( r['texturing_dir'], tree.odm_textured_model_obj) 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 \ 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, 'imgs': tree.dataset_raw, 'imgs_list': tree.opensfm_bundle_list, 'model': odm_georeferencing_model_obj, '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, 'verbose': verbose } if transformPointCloud: kwargs[ 'pc_params'] = '-inputPointCloudFile {input_pc_file} -outputPointCloudFile {output_pc_file}'.format( **kwargs) if reconstruction.is_georeferenced(): kwargs[ 'pc_params'] += ' -outputPointCloudSrs %s' % pipes.quote( reconstruction.georef.proj4()) else: log.ODM_WARNING( 'NO SRS: The output point cloud will not have a SRS.' ) else: kwargs['pc_params'] = '' if 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} ' '-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: reconstruction.georef.extract_offsets( odm_georeferencing_model_txt_geo_file) point_cloud.post_point_cloud_steps(args, tree) 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') if args.fast_orthophoto: decimation_step = 10 else: decimation_step = 40 # More aggressive decimation for large datasets if not args.fast_orthophoto: decimation_step *= int( len(reconstruction.photos) / 1000) + 1 decimation_step = min(decimation_step, 95) try: cropper.create_bounds_gpkg( tree.odm_georeferencing_model_laz, args.crop, decimation_step=decimation_step) except: log.ODM_WARNING( "Cannot calculate crop bounds! We will skip cropping" ) args.crop = 0 # 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' % tree.odm_georeferencing_model_laz) if args.optimize_disk_space and io.file_exists( tree.odm_georeferencing_model_laz) and io.file_exists( tree.filtered_point_cloud): os.remove(tree.filtered_point_cloud) progress += progress_per_run self.update_progress(progress)
def process(args, tree, reconstruction, current_path): odm_orthophoto = io.join_paths(current_path, 'orthophoto') odm_orthophoto_path = odm_orthophoto odm_orthophoto_render = io.join_paths(odm_orthophoto_path, 'odm_orthophoto_render.tif') odm_orthophoto_tif = io.join_paths(odm_orthophoto_path, 'odm_orthophoto.tif') odm_orthophoto_corners = io.join_paths(odm_orthophoto_path, 'odm_orthophoto_corners.tif') odm_orthophoto_log = io.join_paths(odm_orthophoto_path, 'odm_orthophoto_log.tif') odm_orthophoto_tif_log = io.join_paths(odm_orthophoto_path, 'gdal_translate_log.txt') odm_25dgeoreferencing = io.join_paths(current_path, 'odm_georeferencing') odm_georeferencing = io.join_paths(current_path, 'odm_georeferencing') odm_georeferencing_coords = io.join_paths( odm_georeferencing, 'coords.txt') odm_georeferencing_gcp = io.find('gcp_list.txt', current_path) odm_georeferencing_gcp_utm = io.join_paths(odm_georeferencing, 'gcp_list_utm.txt') odm_georeferencing_utm_log = io.join_paths( odm_georeferencing, 'odm_georeferencing_utm_log.txt') odm_georeferencing_log = 'odm_georeferencing_log.txt' odm_georeferencing_transform_file = 'odm_georeferencing_transform.txt' odm_georeferencing_proj = 'proj.txt' odm_georeferencing_model_txt_geo = 'odm_georeferencing_model_geo.txt' odm_georeferencing_model_obj_geo = 'odm_textured_model_geo.obj' odm_georeferencing_xyz_file = io.join_paths( odm_georeferencing, 'odm_georeferenced_model.csv') odm_georeferencing_las_json = io.join_paths( odm_georeferencing, 'las.json') odm_georeferencing_model_laz = io.join_paths( odm_georeferencing, 'odm_georeferenced_model.laz') odm_georeferencing_model_las = io.join_paths( odm_georeferencing, 'odm_georeferenced_model.las') odm_georeferencing_dem = io.join_paths( odm_georeferencing, 'odm_georeferencing_model_dem.tif') opensfm_reconstruction = io.join_paths(current_path,'reconstruction.json') odm_texturing = io.join_paths(current_path,'mvs') odm_textured_model_obj = io.join_paths(odm_texturing, 'odm_textured_model.obj') images_dir = io.join_paths(current_path, 'images') tree = tree opensfm_bundle = os.path.join(current_path, 'opensfm', 'bundle_r000.out') opensfm_bundle_list = os.path.join(current_path, 'opensfm', 'list_r000.out') opensfm_transformation = os.path.join(current_path, 'opensfm', 'geocoords_transformation.txt') filtered_point_cloud = os.path.join(current_path, 'filterpoints', 'point_cloud.ply') doPointCloudGeo = True transformPointCloud = True verbose ='' class nonloc: runs = [] def add_run(primary=True, band=None): subdir = "" if not primary and band is not None: subdir = band # 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! b = True if b: nonloc.runs += [{ 'georeferencing_dir': os.path.join(odm_georeferencing, subdir), 'texturing_dir': os.path.join(odm_texturing, subdir), }] if not args.skip_3dmodel and (primary or args.use_3dmesh): nonloc.runs += [{ 'georeferencing_dir': odm_georeferencing, 'texturing_dir': os.path.join(odm_texturing, subdir), }] if reconstruction.multi_camera: for band in reconstruction.multi_camera: primary = band == reconstruction.multi_camera[0] add_run(primary, band['name'].lower()) else: add_run() #progress_per_run = 100.0 / len(nonloc.runs) #progress = 0.0 for r in nonloc.runs: if not io.dir_exists(r['georeferencing_dir']): system.mkdir_p(r['georeferencing_dir']) odm_georeferencing_model_obj_geo = os.path.join(r['texturing_dir'], odm_georeferencing_model_obj_geo) odm_georeferencing_model_obj = os.path.join(r['texturing_dir'], odm_textured_model_obj) odm_georeferencing_log = os.path.join(r['georeferencing_dir'], odm_georeferencing_log) odm_georeferencing_transform_file = os.path.join(r['georeferencing_dir'], odm_georeferencing_transform_file) odm_georeferencing_model_txt_geo_file = os.path.join(r['georeferencing_dir'], odm_georeferencing_model_txt_geo) pio = True if pio: #if not io.file_exists(odm_georeferencing_model_obj_geo) or \ #not io.file_exists(odm_georeferencing_model_laz) # odm_georeference definitions kwargs = { 'bin': context.odm_modules_path, 'input_pc_file': filtered_point_cloud, 'bundle': opensfm_bundle, 'imgs': images_dir, 'imgs_list': opensfm_bundle_list, 'model': odm_georeferencing_model_obj, 'log': odm_georeferencing_log, 'input_trans_file': opensfm_transformation, 'transform_file': odm_georeferencing_transform_file, 'coords': odm_georeferencing_coords, 'output_pc_file': odm_georeferencing_model_laz, 'geo_sys': odm_georeferencing_model_txt_geo_file, 'model_geo': odm_georeferencing_model_obj_geo, 'verbose': verbose } if transformPointCloud: kwargs['pc_params'] = '-inputPointCloudFile {input_pc_file} -outputPointCloudFile {output_pc_file}'.format(**kwargs) if reconstruction.is_georeferenced(): kwargs['pc_params'] += ' -outputPointCloudSrs %s' % pipes.quote(reconstruction.georef.proj4()) else: log.ODM_WARNING('NO SRS: The output point cloud will not have a SRS.') else: kwargs['pc_params'] = '' if io.file_exists(opensfm_transformation) and io.file_exists(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(odm_georeferencing_coords): print('running georeferencing') 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} ' '-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: reconstruction.georef.extract_offsets(odm_georeferencing_model_txt_geo_file) point_cloud.post_point_cloud_steps(args, tree) if args.crop > 0: log.ODM_INFO("Calculating cropping area and generating bounds shapefile from point cloud") cropper = Cropper(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(odm_georeferencing_model_laz, args.crop, decimation_step=decimation_step) # 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' % odm_georeferencing_model_laz) if args.optimize_disk_space and io.file_exists(odm_georeferencing_model_laz) and io.file_exists(filtered_point_cloud): os.remove(filtered_point_cloud)