From 6085acd0adc22449756c0e55e15f371d77888504 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Mon, 1 Mar 2021 20:34:40 +0000 Subject: [PATCH 01/11] Update OpenMVS to latest --- SuperBuild/cmake/External-OpenMVS.cmake | 2 +- SuperBuild/cmake/External-OpenSfM.cmake | 2 +- VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SuperBuild/cmake/External-OpenMVS.cmake b/SuperBuild/cmake/External-OpenMVS.cmake index 42453df4..3cf5c7be 100644 --- a/SuperBuild/cmake/External-OpenMVS.cmake +++ b/SuperBuild/cmake/External-OpenMVS.cmake @@ -20,7 +20,7 @@ ExternalProject_Add(${_proj_name} #--Download step-------------- DOWNLOAD_DIR ${SB_DOWNLOAD_DIR} GIT_REPOSITORY https://github.com/OpenDroneMap/openMVS - GIT_TAG 240 + GIT_TAG 244 #--Update/Patch step---------- UPDATE_COMMAND "" #--Configure step------------- diff --git a/SuperBuild/cmake/External-OpenSfM.cmake b/SuperBuild/cmake/External-OpenSfM.cmake index 85a59abb..7f593b74 100644 --- a/SuperBuild/cmake/External-OpenSfM.cmake +++ b/SuperBuild/cmake/External-OpenSfM.cmake @@ -9,7 +9,7 @@ ExternalProject_Add(${_proj_name} #--Download step-------------- DOWNLOAD_DIR ${SB_DOWNLOAD_DIR} GIT_REPOSITORY https://github.com/OpenDroneMap/OpenSfM/ - GIT_TAG 242 + GIT_TAG 244 #--Update/Patch step---------- UPDATE_COMMAND git submodule update --init --recursive #--Configure step------------- diff --git a/VERSION b/VERSION index 79a61441..59aa62c1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.4 +2.4.5 From e39afc8b5b64bc51cb39791b97ec43d4496cb91b Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 3 Mar 2021 11:12:39 -0500 Subject: [PATCH 02/11] Credits --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index fca7dea7..38663a99 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,20 @@ If you have questions, join the developer's chat at https://community.opendronem 2. Submit a pull request with detailed changes and test results 3. Have fun! +### Credits + +ODM makes use of [several libraries]((https://github.com/OpenDroneMap/ODM/blob/master/snap/snapcraft.yaml#L36) and other awesome open source projects to perform its tasks. Among them we'd like to highlight: + + - [OpenSfM](https://github.com/mapillary/OpenSfM) + - [OpenMVS](https://github.com/cdcseacave/openMVS/) + - [PDAL](https://github.com/PDAL/PDAL) + - [Entwine](https://pdal.io/) + - [MVS Texturing](https://github.com/nmoehrle/mvs-texturing) + - [GRASS GIS](https://grass.osgeo.org/) + - [GDAL](https://gdal.org/) + - [PoissonRecon](https://github.com/mkazhdan/PoissonRecon) + + ### Citation > *OpenDroneMap Authors* ODM - A command line toolkit to generate maps, point clouds, 3D models and DEMs from drone, balloon or kite images. **OpenDroneMap/ODM GitHub Page** 2020; [https://github.com/OpenDroneMap/ODM](https://github.com/OpenDroneMap/ODM) From a722ee69e130fc1498070a3cc3fc3990185f8e52 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 3 Mar 2021 11:13:28 -0500 Subject: [PATCH 03/11] Missed parenthesis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38663a99..a9c8adb1 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ If you have questions, join the developer's chat at https://community.opendronem ### Credits -ODM makes use of [several libraries]((https://github.com/OpenDroneMap/ODM/blob/master/snap/snapcraft.yaml#L36) and other awesome open source projects to perform its tasks. Among them we'd like to highlight: +ODM makes use of [several libraries](https://github.com/OpenDroneMap/ODM/blob/master/snap/snapcraft.yaml#L36) and other awesome open source projects to perform its tasks. Among them we'd like to highlight: - [OpenSfM](https://github.com/mapillary/OpenSfM) - [OpenMVS](https://github.com/cdcseacave/openMVS/) From a27f9b564d1d2af314887863e9c22690de348fc0 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 3 Mar 2021 11:31:07 -0500 Subject: [PATCH 04/11] Fix entwine link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a9c8adb1..c9736bf6 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,7 @@ ODM makes use of [several libraries](https://github.com/OpenDroneMap/ODM/blob/ma - [OpenSfM](https://github.com/mapillary/OpenSfM) - [OpenMVS](https://github.com/cdcseacave/openMVS/) - [PDAL](https://github.com/PDAL/PDAL) - - [Entwine](https://pdal.io/) + - [Entwine](https://entwine.io/) - [MVS Texturing](https://github.com/nmoehrle/mvs-texturing) - [GRASS GIS](https://grass.osgeo.org/) - [GDAL](https://gdal.org/) From 79f9f415de831d2e1834399a7a711303c721ee83 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 3 Mar 2021 15:05:09 -0500 Subject: [PATCH 05/11] Always split dense reconstruction into scenes to keep memory usage in check --- opendm/config.py | 4 +-- stages/openmvs.py | 87 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/opendm/config.py b/opendm/config.py index d4b860d2..0ddcf525 100755 --- a/opendm/config.py +++ b/opendm/config.py @@ -353,8 +353,8 @@ def config(argv=None, parser=None): metavar='', action=StoreValue, type=float, - default=0, - help='Filters the point cloud by keeping only a single point around a radius N (in meters). This can be useful to limit the output resolution of the point cloud. Set to 0 to disable sampling. ' + default=0.01, + help='Filters the point cloud by keeping only a single point around a radius N (in meters). This can be useful to limit the output resolution of the point cloud and remove duplicate points. Set to 0 to disable sampling. ' 'Default: %(default)s') parser.add_argument('--smrf-scalar', diff --git a/stages/openmvs.py b/stages/openmvs.py index 0edb5d35..ad442a4f 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -9,6 +9,7 @@ from opendm import types from opendm.utils import get_depthmap_resolution from opendm.osfm import OSFMContext from opendm.multispectral import get_primary_band_name +from opendm.point_cloud import fast_merge_ply class ODMOpenMVSStage(types.ODM_Stage): def process(self, args, outputs): @@ -43,6 +44,9 @@ class ODMOpenMVSStage(types.ODM_Stage): else: resolution_level = int(round(math.log(outputs['undist_image_max_size'] / float(depthmap_resolution)) / math.log(2))) + log.ODM_INFO("Running dense reconstruction. This might take a while.") + + log.ODM_INFO("Estimating depthmaps") config = [ " --resolution-level %s" % int(resolution_level), "--min-resolution %s" % depthmap_resolution, @@ -51,31 +55,80 @@ class ODMOpenMVSStage(types.ODM_Stage): "--number-views-fuse 2", '-w "%s"' % depthmaps_dir, "-v 0", + "--fusion-mode 1" ] - - log.ODM_INFO("Running dense reconstruction. This might take a while.") - - # TODO: add support for image masks - system.run('%s "%s" %s' % (context.omvs_densify_path, os.path.join(tree.openmvs, 'scene.mvs'), ' '.join(config))) self.update_progress(85) - # Filter points - scene_dense = os.path.join(tree.openmvs, 'scene_dense.mvs') - if os.path.exists(scene_dense): - config = [ - "--filter-point-cloud -1", - '-i "%s"' % scene_dense, - "-v 0" - ] - system.run('%s %s' % (context.omvs_densify_path, ' '.join(config))) - else: - log.ODM_WARNING("Cannot find scene_dense.mvs, dense reconstruction probably failed. Exiting...") + log.ODM_INFO("Computing sub-scenes") + config = [ + "--sub-scene-area 660000", # TODO: param tweak? + "--max-threads %s" % args.max_concurrency, + '-w "%s"' % depthmaps_dir, + "-v 0", + ] + system.run('%s "%s" %s' % (context.omvs_densify_path, + os.path.join(tree.openmvs, 'scene.mvs'), + ' '.join(config))) + + scene_files = glob.glob(os.path.join(tree.openmvs, "scene_[0-9][0-9][0-9][0-9].mvs")) + if len(scene_files) == 0: + log.ODM_ERROR("No OpenMVS scenes found. This could be a bug, or the reconstruction could not be processed.") exit(1) + log.ODM_INFO("Fusing depthmaps for %s scenes" % len(scene_files)) + densify_ini_file = os.path.join(tree.openmvs, 'Densify.ini') + with open(densify_ini_file, 'w+') as f: + f.write("Optimize = 0\n") # Disable depth-maps re-filtering + + files_to_remove = [] + scene_ply_files = [] + + for sf in scene_files: + p, ext = os.path.splitext(sf) + scene_ply = p + "_dense_dense_filtered.ply" + scene_dense_mvs = p + "_dense.mvs" + + files_to_remove += [scene_ply, sf, scene_dense_mvs] + scene_ply_files.append(scene_ply) + + if not io.file_exists(scene_ply) or self.rerun(): + # Fuse + config = [ + '--dense-config-file "%s"' % densify_ini_file, + '--number-views-fuse 2', + '--max-threads %s' % args.max_concurrency, + '-w "%s"' % depthmaps_dir, + '-v 0', + ] + system.run('%s "%s" %s' % (context.omvs_densify_path, sf, ' '.join(config))) + + # Filter + system.run('%s "%s" --filter-point-cloud -1 -v 0' % (context.omvs_densify_path, scene_dense_mvs)) + + if not io.file_exists(scene_ply): + scene_ply_files.pop() + log.ODM_WARNING("Could not compute PLY for subscene %s" % sf) + else: + log.ODM_WARNING("Found existing dense scene file %s" % scene_ply) + + # Merge + log.ODM_INFO("Merging %s scene files" % len(scene_ply_files)) + if len(scene_ply_files) == 0: + log.ODM_ERROR("Could not compute dense point cloud (no PLY files available).") + if len(scene_ply_files) == 1: + # Simply rename + os.rename(scene_ply_files[0], tree.openmvs_model) + log.ODM_INFO("%s --> %s"% (scene_ply_files[0], tree.openmvs_model)) + else: + # Merge + fast_merge_ply(scene_ply_files, tree.openmvs_model) + + # TODO: add support for image masks + self.update_progress(95) if args.optimize_disk_space: @@ -84,7 +137,7 @@ class ODMOpenMVSStage(types.ODM_Stage): os.path.join(tree.openmvs, 'scene_dense_dense_filtered.mvs'), octx.path("undistorted", "tracks.csv"), octx.path("undistorted", "reconstruction.json") - ] + ] + files_to_remove for f in files: if os.path.exists(f): os.remove(f) From 42979c7e64ad36e474287f541f432c9cfe8dad65 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 3 Mar 2021 15:33:18 -0500 Subject: [PATCH 06/11] Better re-run logic --- stages/openmvs.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/stages/openmvs.py b/stages/openmvs.py index ad442a4f..5e61e929 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -24,17 +24,26 @@ class ODMOpenMVSStage(types.ODM_Stage): # check if reconstruction was done before if not io.file_exists(tree.openmvs_model) or self.rerun(): - if io.dir_exists(tree.openmvs): - shutil.rmtree(tree.openmvs) + if self.rerun() and io.file_exists(tree.openmvs_model): + os.remove(tree.openmvs_model) + + openmvs_scene_file = os.path.join(tree.openmvs, 'scene.mvs') # export reconstruction from opensfm - octx = OSFMContext(tree.opensfm) - cmd = 'export_openmvs' - octx.run(cmd) + if not io.file_exists(openmvs_scene_file) or self.rerun(): + octx = OSFMContext(tree.opensfm) + cmd = 'export_openmvs' + octx.run(cmd) + else: + log.ODM_WARNING("Found existing %s" % openmvs_scene_file) self.update_progress(10) depthmaps_dir = os.path.join(tree.openmvs, "depthmaps") + + if io.dir_exists(depthmaps_dir) and self.rerun(): + shutil.rmtree(depthmaps_dir) + if not io.dir_exists(depthmaps_dir): os.mkdir(depthmaps_dir) @@ -58,11 +67,23 @@ class ODMOpenMVSStage(types.ODM_Stage): "--fusion-mode 1" ] system.run('%s "%s" %s' % (context.omvs_densify_path, - os.path.join(tree.openmvs, 'scene.mvs'), + openmvs_scene_file, ' '.join(config))) self.update_progress(85) + if self.rerun(): + scene_files = glob.glob(os.path.join(tree.openmvs, "scene_[0-9][0-9][0-9][0-9].mvs")) + check_files = [] + for sf in scene_files: + p, _ = os.path.splitext(sf) + scene_ply = p + "_dense_dense_filtered.ply" + scene_dense_mvs = p + "_dense.mvs" + check_files += [sf, scene_ply, scene_dense_mvs] + for f in check_files: + if os.path.exists(f): + os.remove(f) + log.ODM_INFO("Computing sub-scenes") config = [ "--sub-scene-area 660000", # TODO: param tweak? @@ -71,7 +92,7 @@ class ODMOpenMVSStage(types.ODM_Stage): "-v 0", ] system.run('%s "%s" %s' % (context.omvs_densify_path, - os.path.join(tree.openmvs, 'scene.mvs'), + openmvs_scene_file, ' '.join(config))) scene_files = glob.glob(os.path.join(tree.openmvs, "scene_[0-9][0-9][0-9][0-9].mvs")) @@ -88,7 +109,7 @@ class ODMOpenMVSStage(types.ODM_Stage): scene_ply_files = [] for sf in scene_files: - p, ext = os.path.splitext(sf) + p, _ = os.path.splitext(sf) scene_ply = p + "_dense_dense_filtered.ply" scene_dense_mvs = p + "_dense.mvs" From 804cdb196b8665c2cb1e111ff007af3beea1ece6 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Thu, 4 Mar 2021 02:23:50 +0000 Subject: [PATCH 07/11] Simplify rerun logic, fix densification --- stages/openmvs.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/stages/openmvs.py b/stages/openmvs.py index 5e61e929..77c8a2f2 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -24,12 +24,11 @@ class ODMOpenMVSStage(types.ODM_Stage): # check if reconstruction was done before if not io.file_exists(tree.openmvs_model) or self.rerun(): - if self.rerun() and io.file_exists(tree.openmvs_model): - os.remove(tree.openmvs_model) - - openmvs_scene_file = os.path.join(tree.openmvs, 'scene.mvs') + if io.dir_exists(tree.openmvs): + shutil.rmtree(tree.openmvs) # export reconstruction from opensfm + openmvs_scene_file = os.path.join(tree.openmvs, "scene.mvs") if not io.file_exists(openmvs_scene_file) or self.rerun(): octx = OSFMContext(tree.opensfm) cmd = 'export_openmvs' @@ -56,6 +55,11 @@ class ODMOpenMVSStage(types.ODM_Stage): log.ODM_INFO("Running dense reconstruction. This might take a while.") log.ODM_INFO("Estimating depthmaps") + + densify_ini_file = os.path.join(tree.openmvs, 'config.ini') + with open(densify_ini_file, 'w+') as f: + f.write("Optimize = 0\n") # Disable depth-maps re-filtering + config = [ " --resolution-level %s" % int(resolution_level), "--min-resolution %s" % depthmap_resolution, @@ -72,18 +76,6 @@ class ODMOpenMVSStage(types.ODM_Stage): self.update_progress(85) - if self.rerun(): - scene_files = glob.glob(os.path.join(tree.openmvs, "scene_[0-9][0-9][0-9][0-9].mvs")) - check_files = [] - for sf in scene_files: - p, _ = os.path.splitext(sf) - scene_ply = p + "_dense_dense_filtered.ply" - scene_dense_mvs = p + "_dense.mvs" - check_files += [sf, scene_ply, scene_dense_mvs] - for f in check_files: - if os.path.exists(f): - os.remove(f) - log.ODM_INFO("Computing sub-scenes") config = [ "--sub-scene-area 660000", # TODO: param tweak? @@ -101,9 +93,6 @@ class ODMOpenMVSStage(types.ODM_Stage): exit(1) log.ODM_INFO("Fusing depthmaps for %s scenes" % len(scene_files)) - densify_ini_file = os.path.join(tree.openmvs, 'Densify.ini') - with open(densify_ini_file, 'w+') as f: - f.write("Optimize = 0\n") # Disable depth-maps re-filtering files_to_remove = [] scene_ply_files = [] @@ -119,6 +108,9 @@ class ODMOpenMVSStage(types.ODM_Stage): if not io.file_exists(scene_ply) or self.rerun(): # Fuse config = [ + '--resolution-level %s' % int(resolution_level), + '--min-resolution %s' % depthmap_resolution, + '--max-resolution %s' % int(outputs['undist_image_max_size']), '--dense-config-file "%s"' % densify_ini_file, '--number-views-fuse 2', '--max-threads %s' % args.max_concurrency, From f049525b6eff00134b869df3a5183329e66afe90 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Thu, 4 Mar 2021 03:09:12 +0000 Subject: [PATCH 08/11] Tweak sub-scene-area --- stages/openmvs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stages/openmvs.py b/stages/openmvs.py index 77c8a2f2..6987cc2c 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -78,7 +78,7 @@ class ODMOpenMVSStage(types.ODM_Stage): log.ODM_INFO("Computing sub-scenes") config = [ - "--sub-scene-area 660000", # TODO: param tweak? + "--sub-scene-area 10000", "--max-threads %s" % args.max_concurrency, '-w "%s"' % depthmaps_dir, "-v 0", From 6c71b0732f80453d261210814e64ef18109b0247 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Thu, 4 Mar 2021 10:26:05 -0500 Subject: [PATCH 09/11] Tweak sub-scene-area param, better rerun logic --- stages/openmvs.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stages/openmvs.py b/stages/openmvs.py index 6987cc2c..aeba637c 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -24,8 +24,9 @@ class ODMOpenMVSStage(types.ODM_Stage): # check if reconstruction was done before if not io.file_exists(tree.openmvs_model) or self.rerun(): - if io.dir_exists(tree.openmvs): - shutil.rmtree(tree.openmvs) + if self.rerun(): + if io.dir_exists(tree.openmvs): + shutil.rmtree(tree.openmvs) # export reconstruction from opensfm openmvs_scene_file = os.path.join(tree.openmvs, "scene.mvs") @@ -78,7 +79,7 @@ class ODMOpenMVSStage(types.ODM_Stage): log.ODM_INFO("Computing sub-scenes") config = [ - "--sub-scene-area 10000", + "--sub-scene-area 660000", "--max-threads %s" % args.max_concurrency, '-w "%s"' % depthmaps_dir, "-v 0", From 330a09f133c045d17bedca01ad5be31f225a7d2f Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Thu, 4 Mar 2021 18:27:11 +0000 Subject: [PATCH 10/11] Do not require sampling --- opendm/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendm/config.py b/opendm/config.py index 0ddcf525..28dd5c37 100755 --- a/opendm/config.py +++ b/opendm/config.py @@ -353,7 +353,7 @@ def config(argv=None, parser=None): metavar='', action=StoreValue, type=float, - default=0.01, + default=0, help='Filters the point cloud by keeping only a single point around a radius N (in meters). This can be useful to limit the output resolution of the point cloud and remove duplicate points. Set to 0 to disable sampling. ' 'Default: %(default)s') From 26e333318cfd57471fecc0f6681f457d0faa28c6 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Fri, 5 Mar 2021 09:22:27 -0500 Subject: [PATCH 11/11] Add --pc-tile flag --- opendm/config.py | 7 +++ stages/openmvs.py | 137 ++++++++++++++++++++++++++-------------------- 2 files changed, 86 insertions(+), 58 deletions(-) diff --git a/opendm/config.py b/opendm/config.py index 28dd5c37..016f8b2f 100755 --- a/opendm/config.py +++ b/opendm/config.py @@ -357,6 +357,13 @@ def config(argv=None, parser=None): help='Filters the point cloud by keeping only a single point around a radius N (in meters). This can be useful to limit the output resolution of the point cloud and remove duplicate points. Set to 0 to disable sampling. ' 'Default: %(default)s') + parser.add_argument('--pc-tile', + action=StoreTrue, + nargs=0, + default=False, + help='Reduce the memory usage needed for depthmap fusion by splitting large scenes into tiles. Turn this on if your machine doesn\'t have much RAM and/or you\'ve set --pc-quality to high or ultra. Experimental. ' + 'Default: %(default)s') + parser.add_argument('--smrf-scalar', metavar='', action=StoreValue, diff --git a/stages/openmvs.py b/stages/openmvs.py index aeba637c..4ea1bd58 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -68,78 +68,99 @@ class ODMOpenMVSStage(types.ODM_Stage): "--max-threads %s" % args.max_concurrency, "--number-views-fuse 2", '-w "%s"' % depthmaps_dir, - "-v 0", - "--fusion-mode 1" + "-v 0" ] + + if args.pc_tile: + config.append("--fusion-mode 1") + system.run('%s "%s" %s' % (context.omvs_densify_path, openmvs_scene_file, ' '.join(config))) self.update_progress(85) - - log.ODM_INFO("Computing sub-scenes") - config = [ - "--sub-scene-area 660000", - "--max-threads %s" % args.max_concurrency, - '-w "%s"' % depthmaps_dir, - "-v 0", - ] - system.run('%s "%s" %s' % (context.omvs_densify_path, - openmvs_scene_file, - ' '.join(config))) - - scene_files = glob.glob(os.path.join(tree.openmvs, "scene_[0-9][0-9][0-9][0-9].mvs")) - if len(scene_files) == 0: - log.ODM_ERROR("No OpenMVS scenes found. This could be a bug, or the reconstruction could not be processed.") - exit(1) - - log.ODM_INFO("Fusing depthmaps for %s scenes" % len(scene_files)) - files_to_remove = [] - scene_ply_files = [] - for sf in scene_files: - p, _ = os.path.splitext(sf) - scene_ply = p + "_dense_dense_filtered.ply" - scene_dense_mvs = p + "_dense.mvs" + if args.pc_tile: + log.ODM_INFO("Computing sub-scenes") + config = [ + "--sub-scene-area 660000", + "--max-threads %s" % args.max_concurrency, + '-w "%s"' % depthmaps_dir, + "-v 0", + ] + system.run('%s "%s" %s' % (context.omvs_densify_path, + openmvs_scene_file, + ' '.join(config))) + + scene_files = glob.glob(os.path.join(tree.openmvs, "scene_[0-9][0-9][0-9][0-9].mvs")) + if len(scene_files) == 0: + log.ODM_ERROR("No OpenMVS scenes found. This could be a bug, or the reconstruction could not be processed.") + exit(1) - files_to_remove += [scene_ply, sf, scene_dense_mvs] - scene_ply_files.append(scene_ply) + log.ODM_INFO("Fusing depthmaps for %s scenes" % len(scene_files)) + + scene_ply_files = [] - if not io.file_exists(scene_ply) or self.rerun(): - # Fuse - config = [ - '--resolution-level %s' % int(resolution_level), - '--min-resolution %s' % depthmap_resolution, - '--max-resolution %s' % int(outputs['undist_image_max_size']), - '--dense-config-file "%s"' % densify_ini_file, - '--number-views-fuse 2', - '--max-threads %s' % args.max_concurrency, - '-w "%s"' % depthmaps_dir, - '-v 0', - ] - system.run('%s "%s" %s' % (context.omvs_densify_path, sf, ' '.join(config))) + for sf in scene_files: + p, _ = os.path.splitext(sf) + scene_ply = p + "_dense_dense_filtered.ply" + scene_dense_mvs = p + "_dense.mvs" - # Filter - system.run('%s "%s" --filter-point-cloud -1 -v 0' % (context.omvs_densify_path, scene_dense_mvs)) + files_to_remove += [scene_ply, sf, scene_dense_mvs] + scene_ply_files.append(scene_ply) - if not io.file_exists(scene_ply): - scene_ply_files.pop() - log.ODM_WARNING("Could not compute PLY for subscene %s" % sf) - else: - log.ODM_WARNING("Found existing dense scene file %s" % scene_ply) + if not io.file_exists(scene_ply) or self.rerun(): + # Fuse + config = [ + '--resolution-level %s' % int(resolution_level), + '--min-resolution %s' % depthmap_resolution, + '--max-resolution %s' % int(outputs['undist_image_max_size']), + '--dense-config-file "%s"' % densify_ini_file, + '--number-views-fuse 2', + '--max-threads %s' % args.max_concurrency, + '-w "%s"' % depthmaps_dir, + '-v 0', + ] + + try: + system.run('%s "%s" %s' % (context.omvs_densify_path, sf, ' '.join(config))) + + # Filter + system.run('%s "%s" --filter-point-cloud -1 -v 0' % (context.omvs_densify_path, scene_dense_mvs)) + except: + log.ODM_WARNING("Sub-scene %s could not be reconstructed, skipping..." % sf) + + if not io.file_exists(scene_ply): + scene_ply_files.pop() + log.ODM_WARNING("Could not compute PLY for subscene %s" % sf) + else: + log.ODM_WARNING("Found existing dense scene file %s" % scene_ply) - # Merge - log.ODM_INFO("Merging %s scene files" % len(scene_ply_files)) - if len(scene_ply_files) == 0: - log.ODM_ERROR("Could not compute dense point cloud (no PLY files available).") - if len(scene_ply_files) == 1: - # Simply rename - os.rename(scene_ply_files[0], tree.openmvs_model) - log.ODM_INFO("%s --> %s"% (scene_ply_files[0], tree.openmvs_model)) - else: # Merge - fast_merge_ply(scene_ply_files, tree.openmvs_model) + log.ODM_INFO("Merging %s scene files" % len(scene_ply_files)) + if len(scene_ply_files) == 0: + log.ODM_ERROR("Could not compute dense point cloud (no PLY files available).") + if len(scene_ply_files) == 1: + # Simply rename + os.rename(scene_ply_files[0], tree.openmvs_model) + log.ODM_INFO("%s --> %s"% (scene_ply_files[0], tree.openmvs_model)) + else: + # Merge + fast_merge_ply(scene_ply_files, tree.openmvs_model) + else: + # Filter all at once + scene_dense = os.path.join(tree.openmvs, 'scene_dense.mvs') + if os.path.exists(scene_dense): + config = [ + "--filter-point-cloud -1", + '-i "%s"' % scene_dense, + "-v 0" + ] + system.run('%s %s' % (context.omvs_densify_path, ' '.join(config))) + else: + log.ODM_WARNING("Cannot find scene_dense.mvs, dense reconstruction probably failed. Exiting...") + exit(1) # TODO: add support for image masks