From f31355e3ce07565cb7295e90b1db2f5662921f5c Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 28 Oct 2020 14:13:34 -0400 Subject: [PATCH] Remove MVE, handle image masks --- SuperBuild/CMakeLists.txt | 13 ----- opendm/context.py | 6 --- opendm/types.py | 5 -- run.py | 2 +- stages/dataset.py | 18 ++++++- stages/mve.py | 102 -------------------------------------- 6 files changed, 18 insertions(+), 128 deletions(-) delete mode 100644 stages/mve.py diff --git a/SuperBuild/CMakeLists.txt b/SuperBuild/CMakeLists.txt index c20e7674..da10a6eb 100644 --- a/SuperBuild/CMakeLists.txt +++ b/SuperBuild/CMakeLists.txt @@ -120,19 +120,6 @@ endforeach() include(ProcessorCount) ProcessorCount(nproc) -## Add mve Build - -externalproject_add(mve - GIT_REPOSITORY https://github.com/OpenDroneMap/mve.git - GIT_TAG 210 - UPDATE_COMMAND "" - SOURCE_DIR ${SB_SOURCE_DIR}/elibs/mve - CONFIGURE_COMMAND "" - BUILD_IN_SOURCE 1 - BUILD_COMMAND make -j${nproc} - INSTALL_COMMAND "" -) - externalproject_add(poissonrecon GIT_REPOSITORY https://github.com/mkazhdan/PoissonRecon.git GIT_TAG ce5005ae3094d902d551a65a8b3131e06f45e7cf diff --git a/opendm/context.py b/opendm/context.py index 3fde1c4d..a82b65d4 100644 --- a/opendm/context.py +++ b/opendm/context.py @@ -27,12 +27,6 @@ opensfm_path = os.path.join(superbuild_path, "src/opensfm") # define orb_slam2 path orb_slam2_path = os.path.join(superbuild_path, "src/orb_slam2") -# define mve join_paths -makescene_path = os.path.join(superbuild_path, 'src', 'elibs', 'mve', 'apps', 'makescene', 'makescene') #TODO: don't install in source -dmrecon_path = os.path.join(superbuild_path, 'src', 'elibs', 'mve', 'apps', 'dmrecon', 'dmrecon') -scene2pset_path = os.path.join(superbuild_path, 'src', 'elibs', 'mve', 'apps', 'scene2pset', 'scene2pset') -meshclean_path = os.path.join(superbuild_path, 'src', 'elibs', 'mve', 'apps', 'meshclean', 'meshclean') - poisson_recon_path = os.path.join(superbuild_path, 'src', 'PoissonRecon', 'Bin', 'Linux', 'PoissonRecon') dem2mesh_path = os.path.join(superbuild_path, 'src', 'dem2mesh', 'dem2mesh') dem2points_path = os.path.join(superbuild_path, 'src', 'dem2points', 'dem2points') diff --git a/opendm/types.py b/opendm/types.py index c6707f8e..21f7ad4d 100644 --- a/opendm/types.py +++ b/opendm/types.py @@ -213,7 +213,6 @@ class ODM_Tree(object): # whole reconstruction process. self.dataset_raw = io.join_paths(self.root_path, 'images') self.opensfm = io.join_paths(self.root_path, 'opensfm') - self.mve = io.join_paths(self.root_path, 'mve') self.openmvs = io.join_paths(self.opensfm, 'undistorted', 'openmvs') self.odm_meshing = io.join_paths(self.root_path, 'odm_meshing') self.odm_texturing = io.join_paths(self.root_path, 'odm_texturing') @@ -241,10 +240,6 @@ class ODM_Tree(object): self.opensfm_model = io.join_paths(self.opensfm, 'undistorted/depthmaps/merged.ply') self.opensfm_transformation = io.join_paths(self.opensfm, 'geocoords_transformation.txt') - # mve - self.mve_model = io.join_paths(self.mve, 'mve_dense_point_cloud.ply') - self.mve_views = io.join_paths(self.mve, 'views') - # OpenMVS self.openmvs_model = io.join_paths(self.openmvs, 'scene_dense.ply') diff --git a/run.py b/run.py index 9048a72c..47b3dd0c 100755 --- a/run.py +++ b/run.py @@ -60,7 +60,7 @@ if __name__ == '__main__': quote(os.path.join(args.project_path, "opensfm")), quote(os.path.join(args.project_path, "odm_filterpoints")), quote(os.path.join(args.project_path, "odm_texturing_25d")), - quote(os.path.join(args.project_path, "mve")), + quote(os.path.join(args.project_path, "openmvs")), quote(os.path.join(args.project_path, "entwine_pointcloud")), quote(os.path.join(args.project_path, "submodels")), ])) diff --git a/stages/dataset.py b/stages/dataset.py index 0c3ed086..20f21038 100644 --- a/stages/dataset.py +++ b/stages/dataset.py @@ -110,7 +110,7 @@ class ODMLoadDatasetStage(types.ODM_Stage): p.set_mask(find_mask(f, masks)) photos += [p] dataset_list.write(photos[-1].filename + '\n') - + # Check if a geo file is available if tree.odm_geo_file is not None and os.path.exists(tree.odm_geo_file): log.ODM_INFO("Found image geolocation file") @@ -134,6 +134,22 @@ class ODMLoadDatasetStage(types.ODM_Stage): log.ODM_INFO('Found %s usable images' % len(photos)) + # TODO: add support for masks in OpenMVS + has_mask = False + for p in photos: + if p.mask is not None: + has_mask = True + break + + if has_mask and not args.use_opensfm_dense: + log.ODM_WARNING("Image masks found, will use OpenSfM for dense reconstruction") + args.use_opensfm_dense = True + + # Remove OpenMVS from pipeline. Yep. + opensfm_stage = self.next_stage.next_stage.next_stage + opensfm_stage.next_stage = opensfm_stage.next_stage.next_stage + opensfm_stage.next_stage.prev_stage = opensfm_stage + # Create reconstruction object reconstruction = types.ODM_Reconstruction(photos) diff --git a/stages/mve.py b/stages/mve.py deleted file mode 100644 index d775cd45..00000000 --- a/stages/mve.py +++ /dev/null @@ -1,102 +0,0 @@ -import shutil, os, glob, math - -from opendm import log -from opendm import io -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): - # get inputs - tree = outputs['tree'] - reconstruction = outputs['reconstruction'] - photos = reconstruction.photos - - if not photos: - log.ODM_ERROR('Not enough photos in photos array to start MVE') - exit(1) - - # check if reconstruction was done before - if not io.file_exists(tree.mve_model) or self.rerun(): - # mve makescene wants the output directory - # to not exists before executing it (otherwise it - # will prompt the user for confirmation) - if io.dir_exists(tree.mve): - shutil.rmtree(tree.mve) - - # run mve makescene - if not io.dir_exists(tree.mve_views): - nvm_file = tree.opensfm_reconstruction_nvm - if reconstruction.multi_camera: - # Reconstruct only the primary band - primary = reconstruction.multi_camera[0] - nvm_file = os.path.join(tree.opensfm, "undistorted", "reconstruction_%s.nvm" % primary['name'].lower()) - - system.run('%s "%s" "%s"' % (context.makescene_path, nvm_file, tree.mve), env_vars={'OMP_NUM_THREADS': args.max_concurrency}) - - self.update_progress(10) - - # Compute mve output scale based on depthmap_resolution - max_pixels = args.depthmap_resolution * args.depthmap_resolution - if outputs['undist_image_max_size'] * outputs['undist_image_max_size'] <= max_pixels: - mve_output_scale = 0 - else: - ratio = float(outputs['undist_image_max_size'] * outputs['undist_image_max_size']) / float(max_pixels) - mve_output_scale = int(math.ceil(math.log(ratio) / math.log(4.0))) - - dmrecon_config = [ - "-s%s" % mve_output_scale, - "--progress=fancy", - "--local-neighbors=2", - # "--filter-width=3", - ] - - # Run MVE's dmrecon - log.ODM_INFO("Running dense reconstruction. This might take a while.") - - # TODO: find out why MVE is crashing at random - # MVE *seems* to have a race condition, triggered randomly, regardless of dataset - # https://gist.github.com/pierotofy/6c9ce93194ba510b61e42e3698cfbb89 - # Temporary workaround is to retry the reconstruction until we get it right - # (up to a certain number of retries). - retry_count = 1 - while retry_count < 10: - try: - system.run('%s %s "%s"' % (context.dmrecon_path, ' '.join(dmrecon_config), tree.mve), env_vars={'OMP_NUM_THREADS': args.max_concurrency}) - break - except Exception as e: - if str(e) == "Child returned 134" or str(e) == "Child returned 1": - retry_count += 1 - log.ODM_WARNING("Caught error code, retrying attempt #%s" % retry_count) - else: - raise e - - self.update_progress(90) - - scene2pset_config = [ - "-F%s" % mve_output_scale, - '-mmask' - ] - - # run scene2pset - system.run('%s %s "%s" "%s"' % (context.scene2pset_path, ' '.join(scene2pset_config), tree.mve, tree.mve_model), env_vars={'OMP_NUM_THREADS': args.max_concurrency}) - - # run cleanmesh (filter points by MVE confidence threshold) - if args.mve_confidence > 0: - mve_filtered_model = io.related_file_path(tree.mve_model, postfix=".filtered") - system.run('%s -t%s --no-clean --component-size=0 "%s" "%s"' % (context.meshclean_path, min(1.0, args.mve_confidence), tree.mve_model, mve_filtered_model), env_vars={'OMP_NUM_THREADS': args.max_concurrency}) - - if io.file_exists(mve_filtered_model): - os.remove(tree.mve_model) - os.rename(mve_filtered_model, tree.mve_model) - else: - log.ODM_WARNING("Couldn't filter MVE model (%s does not exist)." % mve_filtered_model) - - if args.optimize_disk_space: - shutil.rmtree(tree.mve_views) - else: - log.ODM_WARNING('Found a valid MVE reconstruction file in: %s' % - tree.mve_model)