2019-04-22 19:14:39 +00:00
|
|
|
import shutil, os, glob, math
|
2018-06-27 18:32:49 +00:00
|
|
|
|
|
|
|
from opendm import log
|
|
|
|
from opendm import io
|
|
|
|
from opendm import system
|
|
|
|
from opendm import context
|
2019-03-06 00:03:04 +00:00
|
|
|
from opendm import point_cloud
|
2019-04-22 19:14:39 +00:00
|
|
|
from opendm import types
|
2019-05-06 15:35:23 +00:00
|
|
|
from opendm.osfm import OSFMContext
|
2018-06-27 18:32:49 +00:00
|
|
|
|
2019-04-22 19:14:39 +00:00
|
|
|
class ODMMveStage(types.ODM_Stage):
|
|
|
|
def process(self, args, outputs):
|
2018-06-27 18:32:49 +00:00
|
|
|
# get inputs
|
2019-04-22 19:14:39 +00:00
|
|
|
tree = outputs['tree']
|
|
|
|
reconstruction = outputs['reconstruction']
|
2018-06-27 18:32:49 +00:00
|
|
|
photos = reconstruction.photos
|
|
|
|
|
|
|
|
if not photos:
|
2018-12-02 17:45:26 +00:00
|
|
|
log.ODM_ERROR('Not enough photos in photos array to start MVE')
|
2019-04-22 19:14:39 +00:00
|
|
|
exit(1)
|
2018-06-27 18:32:49 +00:00
|
|
|
|
|
|
|
# check if reconstruction was done before
|
2019-04-22 19:14:39 +00:00
|
|
|
if not io.file_exists(tree.mve_model) or self.rerun():
|
2018-07-02 19:21:30 +00:00
|
|
|
# mve makescene wants the output directory
|
|
|
|
# to not exists before executing it (otherwise it
|
|
|
|
# will prompt the user for confirmation)
|
2018-12-02 17:45:26 +00:00
|
|
|
if io.dir_exists(tree.mve):
|
|
|
|
shutil.rmtree(tree.mve)
|
2018-07-02 19:21:30 +00:00
|
|
|
|
2018-06-30 23:36:53 +00:00
|
|
|
# run mve makescene
|
2018-07-01 22:49:53 +00:00
|
|
|
if not io.dir_exists(tree.mve_views):
|
2020-01-23 19:28:27 +00:00
|
|
|
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})
|
2018-06-30 23:36:53 +00:00
|
|
|
|
2019-05-15 22:01:46 +00:00
|
|
|
self.update_progress(10)
|
|
|
|
|
2019-04-03 20:23:21 +00:00
|
|
|
# Compute mve output scale based on depthmap_resolution
|
2019-04-10 14:41:35 +00:00
|
|
|
max_pixels = args.depthmap_resolution * args.depthmap_resolution
|
2019-08-15 21:01:11 +00:00
|
|
|
if outputs['undist_image_max_size'] * outputs['undist_image_max_size'] <= max_pixels:
|
2019-04-10 14:41:35 +00:00
|
|
|
mve_output_scale = 0
|
|
|
|
else:
|
2019-08-15 21:01:11 +00:00
|
|
|
ratio = float(outputs['undist_image_max_size'] * outputs['undist_image_max_size']) / float(max_pixels)
|
2019-04-10 14:41:35 +00:00
|
|
|
mve_output_scale = int(math.ceil(math.log(ratio) / math.log(4.0)))
|
2019-04-03 20:23:21 +00:00
|
|
|
|
2019-04-03 18:47:06 +00:00
|
|
|
dmrecon_config = [
|
2019-04-03 20:23:21 +00:00
|
|
|
"-s%s" % mve_output_scale,
|
2020-02-02 15:45:41 +00:00
|
|
|
"--progress=fancy",
|
2019-04-10 14:41:35 +00:00
|
|
|
"--local-neighbors=2",
|
2019-06-20 15:20:41 +00:00
|
|
|
# "--filter-width=3",
|
2018-06-27 18:32:49 +00:00
|
|
|
]
|
|
|
|
|
2019-04-03 18:47:06 +00:00
|
|
|
# Run MVE's dmrecon
|
2020-02-02 15:45:41 +00:00
|
|
|
log.ODM_INFO("Running dense reconstruction. This might take a while.")
|
2019-05-04 23:09:48 +00:00
|
|
|
|
2019-05-21 16:38:30 +00:00
|
|
|
# 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:
|
2019-07-10 17:37:55 +00:00
|
|
|
system.run('%s %s "%s"' % (context.dmrecon_path, ' '.join(dmrecon_config), tree.mve), env_vars={'OMP_NUM_THREADS': args.max_concurrency})
|
2019-05-21 16:38:30 +00:00
|
|
|
break
|
|
|
|
except Exception as e:
|
2019-05-27 20:29:30 +00:00
|
|
|
if str(e) == "Child returned 134" or str(e) == "Child returned 1":
|
2019-05-21 16:38:30 +00:00
|
|
|
retry_count += 1
|
|
|
|
log.ODM_WARNING("Caught error code, retrying attempt #%s" % retry_count)
|
|
|
|
else:
|
|
|
|
raise e
|
|
|
|
|
2019-05-15 22:01:46 +00:00
|
|
|
self.update_progress(90)
|
2019-02-19 20:08:38 +00:00
|
|
|
|
2019-04-03 18:47:06 +00:00
|
|
|
scene2pset_config = [
|
2019-04-03 20:23:21 +00:00
|
|
|
"-F%s" % mve_output_scale
|
2019-04-03 18:47:06 +00:00
|
|
|
]
|
2019-02-19 20:08:38 +00:00
|
|
|
|
2019-03-20 20:55:35 +00:00
|
|
|
# run scene2pset
|
2019-04-12 17:58:25 +00:00
|
|
|
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})
|
2019-06-20 15:20:41 +00:00
|
|
|
|
|
|
|
# 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)
|
2020-03-30 14:32:21 +00:00
|
|
|
|
|
|
|
if args.optimize_disk_space:
|
|
|
|
shutil.rmtree(tree.mve_views)
|
2018-06-27 18:32:49 +00:00
|
|
|
else:
|
2018-12-02 17:45:26 +00:00
|
|
|
log.ODM_WARNING('Found a valid MVE reconstruction file in: %s' %
|
|
|
|
tree.mve_model)
|