kopia lustrzana https://github.com/OpenDroneMap/ODM
				
				
				
			
							rodzic
							
								
									3ef7a3a8bd
								
							
						
					
					
						commit
						dd16a8913c
					
				|  | @ -123,62 +123,55 @@ class OSFMContext: | |||
|         else: | ||||
|             log.ODM_WARNING("%s already exists, not rerunning OpenSfM setup" % list_path) | ||||
| 
 | ||||
| 
 | ||||
|     def feature_matching(self, rerun=False): | ||||
|         if not io.file_exists(self.feature_matching_done_file()) or rerun: | ||||
|             # TODO: put extract metadata into its own function | ||||
|     def extract_metadata(self, rerun=False): | ||||
|         metadata_dir = self.path("exif") | ||||
|         if not io.dir_exists(metadata_dir) or rerun: | ||||
|             self.run('extract_metadata') | ||||
| 
 | ||||
|             # TODO: distributed workflow should do these two steps independently | ||||
|     def feature_matching(self, rerun=False): | ||||
|         features_dir = self.path("features") | ||||
|         matches_dir = self.path("matches") | ||||
|          | ||||
|         if not io.dir_exists(features_dir) or rerun: | ||||
|             self.run('detect_features') | ||||
|             self.run('match_features') | ||||
| 
 | ||||
|             self.mark_feature_matching_done() | ||||
|         else: | ||||
|             log.ODM_WARNING('Found a feature matching done progress file in: %s' % self.feature_matching_done_file()) | ||||
|             log.ODM_WARNING('Detect features already done: %s exists' % features_dir) | ||||
| 
 | ||||
|     def feature_matching_done_file(self): | ||||
|         return io.join_paths(self.opensfm_project_path, 'matching_done.txt') | ||||
|         if not io.dir_exists(matches_dir) or rerun: | ||||
|             self.run('match_features') | ||||
|         else: | ||||
|             log.ODM_WARNING('Match features already done: %s exists' % matches_dir) | ||||
| 
 | ||||
|     def mark_feature_matching_done(self): | ||||
|         with open(self.feature_matching_done_file(), 'w') as fout: | ||||
|             fout.write("Matching done!\n") | ||||
|          | ||||
|     def path(self, *paths): | ||||
|         return os.path.join(self.opensfm_project_path, *paths) | ||||
| 
 | ||||
|     def set_image_list_absolute(self): | ||||
|     def save_absolute_image_list_to(self, file): | ||||
|         """ | ||||
|         Checks the image_list.txt file and makes sure that all paths | ||||
|         Writes a copy of the image_list.txt file and makes sure that all paths | ||||
|         written in it are absolute paths and not relative paths. | ||||
|         If there are relative paths, they are changed to absolute paths. | ||||
|         """ | ||||
|         image_list_file = self.path("image_list.txt") | ||||
|         tmp_list_file = self.path("image_list.txt.tmp") | ||||
| 
 | ||||
|         if io.file_exists(image_list_file): | ||||
|             changed = False | ||||
| 
 | ||||
|             with open(image_list_file, 'r') as f: | ||||
|                 content = f.read() | ||||
|              | ||||
|             lines = [] | ||||
|             for line in map(str.strip, content.split('\n')): | ||||
|                 if line and not line.startswith("/"): | ||||
|                     changed = True | ||||
|                     line = os.path.abspath(os.path.join(self.opensfm_project_path, line)) | ||||
|                 lines.append(line) | ||||
| 
 | ||||
|             if changed: | ||||
|                 with open(tmp_list_file, 'w') as f: | ||||
|                     f.write("\n".join(lines)) | ||||
|             with open(file, 'w') as f: | ||||
|                 f.write("\n".join(lines)) | ||||
| 
 | ||||
|                 os.remove(image_list_file) | ||||
|                 os.rename(tmp_list_file, image_list_file) | ||||
| 
 | ||||
|                 log.ODM_DEBUG("%s now contains absolute paths" % image_list_file) | ||||
|             log.ODM_DEBUG("Wrote %s with absolute paths" % file) | ||||
|         else: | ||||
|             log.ODM_WARNING("No %s found, cannot check for absolute paths." % image_list_file) | ||||
|             log.ODM_WARNING("No %s found, cannot create %s" % (image_list_file, file)) | ||||
|      | ||||
|     def name(self): | ||||
|         return os.path.basename(os.path.abspath(self.path(".."))) | ||||
| 
 | ||||
| def get_submodel_argv(args, submodels_path, submodel_name): | ||||
|     """ | ||||
|  |  | |||
|  | @ -0,0 +1,17 @@ | |||
| from opendm import log | ||||
| 
 | ||||
| class HybridDistributedExecutor: | ||||
|     def __init__(self, nodeUrl): | ||||
|         self.nodeUrl = nodeUrl | ||||
|         log.ODM_INFO("Initializing hybrid distributed executor with cluster node: %s" % nodeUrl) | ||||
| 
 | ||||
|     def set_projects(self, paths): | ||||
|         self.project_paths = paths | ||||
| 
 | ||||
|     def run_reconstruct(self): | ||||
|         print(self.project_paths) | ||||
|         exit(1) | ||||
| 
 | ||||
|     def run_toolchain(self): | ||||
|         pass | ||||
|      | ||||
|  | @ -6,6 +6,7 @@ from opendm import system | |||
| from opendm import context | ||||
| from opendm import point_cloud | ||||
| from opendm import types | ||||
| from opendm.osfm import OSFMContext | ||||
| 
 | ||||
| class ODMMveStage(types.ODM_Stage): | ||||
|     def process(self, args, outputs): | ||||
|  | @ -28,7 +29,9 @@ class ODMMveStage(types.ODM_Stage): | |||
|             if not io.file_exists(tree.mve_bundle): | ||||
|                 system.mkdir_p(tree.mve_path) | ||||
|                 system.mkdir_p(io.join_paths(tree.mve_path, 'bundle')) | ||||
|                 io.copy(tree.opensfm_image_list, tree.mve_image_list) | ||||
| 
 | ||||
|                 octx = OSFMContext(tree.opensfm) | ||||
|                 octx.save_absolute_image_list_to(tree.mve_image_list) | ||||
|                 io.copy(tree.opensfm_bundle, tree.mve_bundle) | ||||
| 
 | ||||
|             # mve makescene wants the output directory | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ class ODMOpenSfMStage(types.ODM_Stage): | |||
| 
 | ||||
|         octx = OSFMContext(tree.opensfm) | ||||
|         octx.setup(args, tree.dataset_raw, photos, gcp_path=tree.odm_georeferencing_gcp, rerun=self.rerun()) | ||||
|         octx.extract_metadata(self.rerun()) | ||||
|         octx.feature_matching(self.rerun()) | ||||
|         octx.reconstruct(self.rerun()) | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ from opendm.dem.merge import euclidean_merge_dems | |||
| from opensfm.large import metadataset | ||||
| from opendm.cropper import Cropper | ||||
| from opendm.concurrency import get_max_memory | ||||
| from opendm.remote import HybridDistributedExecutor | ||||
| from pipes import quote | ||||
| 
 | ||||
| class ODMSplitStage(types.ODM_Stage): | ||||
|  | @ -23,6 +24,9 @@ class ODMSplitStage(types.ODM_Stage): | |||
|         outputs['large'] = len(photos) > args.split | ||||
| 
 | ||||
|         if outputs['large']: | ||||
|             # If we have a cluster address, we'll use a distributed workflow | ||||
|             local_workflow = not bool(args.sm_cluster) | ||||
| 
 | ||||
|             octx = OSFMContext(tree.opensfm) | ||||
|             split_done_file = octx.path("split_done.txt") | ||||
| 
 | ||||
|  | @ -38,8 +42,10 @@ class ODMSplitStage(types.ODM_Stage): | |||
|                 ] | ||||
| 
 | ||||
|                 octx.setup(args, tree.dataset_raw, photos, gcp_path=tree.odm_georeferencing_gcp, append_config=config, rerun=self.rerun()) | ||||
|                 octx.extract_metadata(self.rerun()) | ||||
| 
 | ||||
|                 octx.feature_matching(self.rerun()) | ||||
|                 if local_workflow: | ||||
|                     octx.feature_matching(self.rerun()) | ||||
| 
 | ||||
|                 # Create submodels | ||||
|                 if not io.dir_exists(tree.submodels_path) or self.rerun(): | ||||
|  | @ -57,34 +63,32 @@ class ODMSplitStage(types.ODM_Stage): | |||
| 
 | ||||
|                 gcp_file = GCPFile(tree.odm_georeferencing_gcp) | ||||
| 
 | ||||
|                 # Make sure the image list file has absolute paths | ||||
|                 for sp in submodel_paths: | ||||
|                     sp_octx = OSFMContext(sp) | ||||
|                     sp_octx.set_image_list_absolute() | ||||
| 
 | ||||
|                     # Copy filtered GCP file if needed | ||||
|                     # One in OpenSfM's directory, one in the submodel project directory | ||||
|                     if gcp_file.exists(): | ||||
|                         submodel_gcp_file = os.path.abspath(sp_octx.path("..", "gcp_list.txt")) | ||||
|                         submodel_images_dir = os.path.abspath(sp_octx.path("..", "images")) | ||||
|                         submodel_name = os.path.basename(os.path.abspath(sp_octx.path(".."))) | ||||
| 
 | ||||
|                         if gcp_file.make_filtered_copy(submodel_gcp_file, submodel_images_dir): | ||||
|                             log.ODM_DEBUG("Copied filtered GCP file to %s" % submodel_gcp_file) | ||||
|                             io.copy(submodel_gcp_file, os.path.abspath(sp_octx.path("gcp_list.txt"))) | ||||
|                         else: | ||||
|                             log.ODM_DEBUG("No GCP will be copied for %s, not enough images in the submodel are referenced by the GCP" % submodel_name) | ||||
|                             log.ODM_DEBUG("No GCP will be copied for %s, not enough images in the submodel are referenced by the GCP" % sp_octx.name()) | ||||
|                          | ||||
|                 # Reconstruct each submodel | ||||
|                 log.ODM_INFO("Dataset has been split into %s submodels. Reconstructing each submodel..." % len(submodel_paths)) | ||||
| 
 | ||||
|                 # TODO: on a network workflow we probably stop here | ||||
|                 # and let NodeODM take over | ||||
|                 # exit(0) | ||||
| 
 | ||||
|                 for sp in submodel_paths: | ||||
|                     log.ODM_INFO("Reconstructing %s" % sp) | ||||
|                     OSFMContext(sp).reconstruct(self.rerun()) | ||||
|                 if local_workflow: | ||||
|                     for sp in submodel_paths: | ||||
|                         log.ODM_INFO("Reconstructing %s" % sp) | ||||
|                         OSFMContext(sp).reconstruct(self.rerun()) | ||||
|                 else: | ||||
|                     de = HybridDistributedExecutor(args.sm_cluster) | ||||
|                     de.set_projects([os.path.abspath(os.path.join(p, "..")) for p in submodel_paths]) | ||||
|                     de.run_reconstruct() | ||||
| 
 | ||||
|                 # Align | ||||
|                 alignment_file = octx.path('alignment_done.txt') | ||||
|  | @ -97,19 +101,11 @@ class ODMSplitStage(types.ODM_Stage): | |||
|                 else: | ||||
|                     log.ODM_WARNING('Found a alignment matching done progress file in: %s' % alignment_file) | ||||
| 
 | ||||
|                 # Dense reconstruction for each submodel | ||||
|                 # Aligned reconstruction is in reconstruction.aligned.json | ||||
|                 # We need to rename it to reconstruction.json | ||||
|                 remove_paths = [] | ||||
|                 for sp in submodel_paths: | ||||
| 
 | ||||
|                     # TODO: network workflow | ||||
|                      | ||||
|                     # We have already done matching | ||||
|                     sp_octx = OSFMContext(sp) | ||||
|                     sp_octx.mark_feature_matching_done() | ||||
| 
 | ||||
|                     submodel_name = os.path.basename(os.path.abspath(sp_octx.path(".."))) | ||||
| 
 | ||||
|                     # Aligned reconstruction is in reconstruction.aligned.json | ||||
|                     # We need to rename it to reconstruction.json | ||||
| 
 | ||||
|                     aligned_recon = sp_octx.path('reconstruction.aligned.json') | ||||
|                     main_recon = sp_octx.path('reconstruction.json') | ||||
|  | @ -117,7 +113,8 @@ class ODMSplitStage(types.ODM_Stage): | |||
|                     if not io.file_exists(aligned_recon): | ||||
|                         log.ODM_WARNING("Submodel %s does not have an aligned reconstruction (%s). " | ||||
|                                         "This could mean that the submodel could not be reconstructed " | ||||
|                                         " (are there enough features to reconstruct it?). Skipping." % (submodel_name, aligned_recon)) | ||||
|                                         " (are there enough features to reconstruct it?). Skipping." % (sp_octx.name(), aligned_recon)) | ||||
|                         remove_paths.append(sp) | ||||
|                         continue | ||||
| 
 | ||||
|                     if io.file_exists(main_recon): | ||||
|  | @ -126,14 +123,25 @@ class ODMSplitStage(types.ODM_Stage): | |||
|                     shutil.move(aligned_recon, main_recon) | ||||
|                     log.ODM_DEBUG("%s is now %s" % (aligned_recon, main_recon)) | ||||
| 
 | ||||
|                     log.ODM_INFO("========================") | ||||
|                     log.ODM_INFO("Processing %s" % submodel_name) | ||||
|                     log.ODM_INFO("========================") | ||||
|                 # Remove invalid submodels | ||||
|                 submodel_paths = [p for p in submodel_paths if not p in remove_paths] | ||||
| 
 | ||||
|                     argv = get_submodel_argv(args, tree.submodels_path, submodel_name) | ||||
|                 # Run ODM toolchain for each submodel | ||||
|                 if local_workflow: | ||||
|                     for sp in submodel_paths: | ||||
|                         sp_octx = OSFMContext(sp) | ||||
| 
 | ||||
|                     # Re-run the ODM toolchain on the submodel | ||||
|                     system.run(" ".join(map(quote, argv)), env_vars=os.environ.copy()) | ||||
|                         log.ODM_INFO("========================") | ||||
|                         log.ODM_INFO("Processing %s" % sp_octx.name()) | ||||
|                         log.ODM_INFO("========================") | ||||
| 
 | ||||
|                         argv = get_submodel_argv(args, tree.submodels_path, sp_octx.name()) | ||||
| 
 | ||||
|                         # Re-run the ODM toolchain on the submodel | ||||
|                         system.run(" ".join(map(quote, argv)), env_vars=os.environ.copy()) | ||||
|                 else: | ||||
|                     de.set_projects([os.path.abspath(os.path.join(p, "..")) for p in submodel_paths]) | ||||
|                     de.run_toolchain() | ||||
| 
 | ||||
|                 with open(split_done_file, 'w') as fout:  | ||||
|                     fout.write("Split done!\n") | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Piero Toffanin
						Piero Toffanin