You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

180 lines
5.6 KiB

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
"""
This script helps with OpenMVS scalable pipeline.
Starting from a SfM solution stored into a MVS project accompanied by the undistorted images,
the fist step is to compute all depth maps without fusion:
DensifyPointCloud scene.mvs --fusion-mode 1
Next split the scene in sub-scenes using the area parameter, which is related to the inverse of GSD;
it is a bit non intuitive, but normally it should remain constant for a desired memory limit;
for example you can use the bellow value to limit memory usage to ~16GB:
DensifyPointCloud scene.mvs --sub-scene-area 660000
disable depth-maps re-filtering by creating a file Densify.ini with just this line:
Optimize = 0
and call fusion on each of the sub-scenes like:
DensifyPointCloud scene_0000.mvs --dense-config-file Densify.ini
............
DensifyPointCloud scene_000n.mvs --dense-config-file Densify.ini
This script helps to automate the process of calling DensifyPointCloud/ReconstructMesh on all sub-scenes.
usage: MvsScalablePipeline.py openMVS_module input_scene <options>
ex: MvsScalablePipeline.py DensifyPointCloud scene_XXXX.mvs --number-views-fuse 2
"""
import os
import subprocess
import sys
import argparse
import glob
DEBUG = False
if sys.platform.startswith('win'):
PATH_DELIM = ';'
FOLDER_DELIM = '\\'
else:
PATH_DELIM = ':'
FOLDER_DELIM = '/'
def whereis(afile):
"""
return directory in which afile is, None if not found. Look in PATH
"""
if sys.platform.startswith('win'):
cmd = "where"
else:
cmd = "which"
try:
ret = subprocess.run([cmd, afile], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True)
return os.path.split(ret.stdout.decode())[0]
except subprocess.CalledProcessError:
return None
def find(afile):
"""
As whereis look only for executable on linux, this find look for all file type
"""
for d in os.environ['PATH'].split(PATH_DELIM):
if os.path.isfile(os.path.join(d, afile)):
return d
return None
# Try to find openMVS binaries in PATH
OPENMVS_BIN = whereis("ReconstructMesh")
# Ask user for openMVS directory if not found
if not OPENMVS_BIN:
OPENMVS_BIN = input("openMVS binary folder?\n")
# HELPERS for terminal colors
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
NO_EFFECT, BOLD, UNDERLINE, BLINK, INVERSE, HIDDEN = (0, 1, 4, 5, 7, 8)
# from Python cookbook, #475186
def has_colours(stream):
'''
Return stream colours capability
'''
if not hasattr(stream, "isatty"):
return False
if not stream.isatty():
return False # auto color only on TTYs
try:
import curses
curses.setupterm()
return curses.tigetnum("colors") > 2
except Exception:
# guess false in case of error
return False
HAS_COLOURS = has_colours(sys.stdout)
def printout(text, colour=WHITE, background=BLACK, effect=NO_EFFECT):
"""
print() with colour
"""
if HAS_COLOURS:
seq = "\x1b[%d;%d;%dm" % (effect, 30+colour, 40+background) + text + "\x1b[0m"
sys.stdout.write(seq+'\r\n')
else:
sys.stdout.write(text+'\r\n')
# store config and data in
class ConfContainer:
"""
Container for all the config variables
"""
def __init__(self):
pass
CONF = ConfContainer()
# ARGS
PARSER = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
description="Scalable MVS reconstruction with these steps: \r\n" +
"MvsScalablePipeline.py openMVS_module input_scene <options>\r\n"
)
PARSER.add_argument('openMVS_module',
help="the OpenMVS module to use: DensifyPointCloud, ReconstructMesh, etc.")
PARSER.add_argument('input_scene',
help="the scene name reg to process: scene_XXXX.mvs")
PARSER.add_argument('passthrough', nargs=argparse.REMAINDER, help="Option to be passed to command lines")
PARSER.parse_args(namespace=CONF) # store args in the ConfContainer
suffix = os.path.basename(CONF.input_scene).replace('scene_XXXX','')
CONF.input_scene = CONF.input_scene.replace('_dense','').replace('_mesh','').replace('_refine','').replace('_texture','')
# Absolute path for input directory
if len(CONF.input_scene) < 10 or CONF.input_scene[-9:] != '_XXXX.mvs':
sys.exit("%s: invalid scene name" % CONF.input_scene)
match CONF.openMVS_module:
case 'ReconstructMesh':
moduleSuffix = '_mesh.mvs'
case 'RefineMesh':
moduleSuffix = '_refine.mvs'
case 'TextureMesh':
moduleSuffix = '_texture.mvs'
case _:
moduleSuffix = '_dense.mvs'
printout("# Module {} start #".format(CONF.openMVS_module), colour=RED, effect=BOLD)
for scene_name in glob.glob(os.path.abspath(os.path.join(os.path.dirname(CONF.input_scene), 'scene_[0-9][0-9][0-9][0-9]'+suffix))):
if os.path.exists(os.path.splitext(scene_name)[0] + moduleSuffix) == False:
printout("# Process: %s" % os.path.basename(scene_name), colour=GREEN, effect=NO_EFFECT)
# create a commandline for the current step
cmdline = [os.path.join(OPENMVS_BIN, CONF.openMVS_module), scene_name] + CONF.passthrough
print('Cmd: ' + ' '.join(cmdline))
if not DEBUG:
# Launch the current step
try:
pStep = subprocess.Popen(cmdline)
pStep.wait()
if pStep.returncode != 0:
printout("# Warning: step failed", colour=RED, effect=BOLD)
except KeyboardInterrupt:
sys.exit('\r\nProcess canceled by user, all files remains')
else:
print('\t'.join(cmdline))
printout("# Module {} end #".format(CONF.openMVS_module), colour=RED, effect=BOLD)