kopia lustrzana https://github.com/OpenDroneMap/ODM
Refactor python code to comply with PEP8
rodzic
70675e7b6c
commit
0515166832
|
@ -6,13 +6,14 @@ processopts = ['resize', 'opensfm', 'cmvs', 'pmvs',
|
|||
'odm_meshing', 'odm_texturing', 'odm_georeferencing',
|
||||
'odm_orthophoto']
|
||||
|
||||
|
||||
class RerunFrom(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, processopts[processopts.index(values):])
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description='OpenDroneMap')
|
||||
parser.add_argument('--project-path',
|
||||
parser.add_argument('--project-path',
|
||||
metavar='<string>',
|
||||
help='Path to the project to process')
|
||||
|
||||
|
@ -56,7 +57,7 @@ parser.add_argument('--force-focal',
|
|||
metavar='<positive float>',
|
||||
type=float,
|
||||
help=('Override the focal length information for the '
|
||||
'images'))
|
||||
'images'))
|
||||
|
||||
parser.add_argument('--force-ccd',
|
||||
metavar='<positive float>',
|
||||
|
@ -76,160 +77,160 @@ parser.add_argument('--matcher-threshold',
|
|||
default=2.0,
|
||||
type=float,
|
||||
help=('Ignore matched keypoints if the two images share '
|
||||
'less than <float> percent of keypoints. Default:'
|
||||
' $(default)s'))
|
||||
'less than <float> percent of keypoints. Default:'
|
||||
' $(default)s'))
|
||||
|
||||
parser.add_argument('--matcher-ratio',
|
||||
metavar='<float>',
|
||||
default=0.6,
|
||||
type=float,
|
||||
help=('Ratio of the distance to the next best matched '
|
||||
'keypoint. Default: %(default)s'))
|
||||
'keypoint. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--matcher-neighbors',
|
||||
type=int,
|
||||
metavar='<integer>',
|
||||
default=8,
|
||||
help='Number of nearest images to pre-match based on GPS '
|
||||
'exif data. Set to 0 to skip pre-matching. '
|
||||
'Neighbors works together with Distance parameter, '
|
||||
'set both to 0 to not use pre-matching. OpenSFM '
|
||||
'uses both parameters at the same time, Bundler '
|
||||
'uses only one which has value, prefering the '
|
||||
'Neighbors parameter. Default: %(default)s')
|
||||
'exif data. Set to 0 to skip pre-matching. '
|
||||
'Neighbors works together with Distance parameter, '
|
||||
'set both to 0 to not use pre-matching. OpenSFM '
|
||||
'uses both parameters at the same time, Bundler '
|
||||
'uses only one which has value, prefering the '
|
||||
'Neighbors parameter. Default: %(default)s')
|
||||
|
||||
parser.add_argument('--matcher-distance',
|
||||
metavar='<integer>',
|
||||
default=0,
|
||||
type=int,
|
||||
help='Distance threshold in meters to find pre-matching '
|
||||
'images based on GPS exif data. Set to 0 to skip '
|
||||
'pre-matching. Default: %(default)s')
|
||||
'images based on GPS exif data. Set to 0 to skip '
|
||||
'pre-matching. Default: %(default)s')
|
||||
|
||||
parser.add_argument('--cmvs-maxImages',
|
||||
metavar='<integer>',
|
||||
default=500,
|
||||
type=int,
|
||||
help='The maximum number of images per cluster. '
|
||||
'Default: %(default)s')
|
||||
'Default: %(default)s')
|
||||
|
||||
parser.add_argument('--pmvs-level',
|
||||
metavar='<positive integer>',
|
||||
default=1,
|
||||
type=int,
|
||||
help=('The level in the image pyramid that is used '
|
||||
'for the computation. see '
|
||||
'http://www.di.ens.fr/pmvs/documentation.html for '
|
||||
'more pmvs documentation. Default: %(default)s'))
|
||||
'for the computation. see '
|
||||
'http://www.di.ens.fr/pmvs/documentation.html for '
|
||||
'more pmvs documentation. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--pmvs-csize',
|
||||
metavar='< positive integer>',
|
||||
default=2,
|
||||
type=int,
|
||||
help='Cell size controls the density of reconstructions'
|
||||
'Default: %(default)s')
|
||||
'Default: %(default)s')
|
||||
|
||||
parser.add_argument('--pmvs-threshold',
|
||||
metavar='<float: -1.0 <= x <= 1.0>',
|
||||
default=0.7,
|
||||
type=float,
|
||||
help=('A patch reconstruction is accepted as a success '
|
||||
'and kept if its associated photometric consistency '
|
||||
'measure is above this threshold. Default: %(default)s'))
|
||||
'and kept if its associated photometric consistency '
|
||||
'measure is above this threshold. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--pmvs-wsize',
|
||||
metavar='<positive integer>',
|
||||
default=7,
|
||||
type=int,
|
||||
help='pmvs samples wsize x wsize pixel colors from '
|
||||
'each image to compute photometric consistency '
|
||||
'score. For example, when wsize=7, 7x7=49 pixel '
|
||||
'colors are sampled in each image. Increasing the '
|
||||
'value leads to more stable reconstructions, but '
|
||||
'the program becomes slower. Default: %(default)s')
|
||||
'each image to compute photometric consistency '
|
||||
'score. For example, when wsize=7, 7x7=49 pixel '
|
||||
'colors are sampled in each image. Increasing the '
|
||||
'value leads to more stable reconstructions, but '
|
||||
'the program becomes slower. Default: %(default)s')
|
||||
|
||||
parser.add_argument('--pmvs-minImageNum',
|
||||
metavar='<positive integer>',
|
||||
default=3,
|
||||
type=int,
|
||||
help=('Each 3D point must be visible in at least '
|
||||
'minImageNum images for being reconstructed. 3 is '
|
||||
'suggested in general. Default: %(default)s'))
|
||||
'minImageNum images for being reconstructed. 3 is '
|
||||
'suggested in general. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--pmvs-num-cores',
|
||||
metavar='<positive integer>',
|
||||
default=context.num_cores,
|
||||
type=int,
|
||||
help=('The maximum number of cores to use in dense '
|
||||
'reconstruction. Default: %(default)s'))
|
||||
'reconstruction. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--odm_meshing-maxVertexCount',
|
||||
metavar='<positive integer>',
|
||||
default=100000,
|
||||
type=int,
|
||||
help=('The maximum vertex count of the output mesh '
|
||||
'Default: %(default)s'))
|
||||
'Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--odm_meshing-octreeDepth',
|
||||
metavar='<positive integer>',
|
||||
default=9,
|
||||
type=int,
|
||||
help=('Oct-tree depth used in the mesh reconstruction, '
|
||||
'increase to get more vertices, recommended '
|
||||
'values are 8-12. Default: %(default)s'))
|
||||
'increase to get more vertices, recommended '
|
||||
'values are 8-12. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--odm_meshing-samplesPerNode',
|
||||
metavar='<float >= 1.0>',
|
||||
default=1.0,
|
||||
type=float,
|
||||
help=('Number of points per octree node, recommended '
|
||||
'and default value: %(default)s'))
|
||||
'and default value: %(default)s'))
|
||||
|
||||
parser.add_argument('--odm_meshing-solverDivide',
|
||||
metavar='<positive integer>',
|
||||
default=9,
|
||||
type=int,
|
||||
help=('Oct-tree depth at which the Laplacian equation '
|
||||
'is solved in the surface reconstruction step. '
|
||||
'Increasing this value increases computation '
|
||||
'times slightly but helps reduce memory usage. '
|
||||
'Default: %(default)s'))
|
||||
'is solved in the surface reconstruction step. '
|
||||
'Increasing this value increases computation '
|
||||
'times slightly but helps reduce memory usage. '
|
||||
'Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--odm_texturing-textureResolution',
|
||||
metavar='<positive integer>',
|
||||
default=4096,
|
||||
type=int,
|
||||
help=('The resolution of the output textures. Must be '
|
||||
'greater than textureWithSize. Default: %(default)s'))
|
||||
'greater than textureWithSize. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--odm_texturing-textureWithSize',
|
||||
metavar='<positive integer>',
|
||||
default=3600,
|
||||
type=int,
|
||||
help=('The resolution to rescale the images performing '
|
||||
'the texturing. Default: %(default)s'))
|
||||
'the texturing. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--odm_georeferencing-gcpFile',
|
||||
metavar='<path string>',
|
||||
default='gcp_list.txt',
|
||||
help=('path to the file containing the ground control '
|
||||
'points used for georeferencing. Default: '
|
||||
'%(default)s. The file needs to '
|
||||
'be on the following line format: \neasting '
|
||||
'northing height pixelrow pixelcol imagename'))
|
||||
'points used for georeferencing. Default: '
|
||||
'%(default)s. The file needs to '
|
||||
'be on the following line format: \neasting '
|
||||
'northing height pixelrow pixelcol imagename'))
|
||||
|
||||
parser.add_argument('--odm_georeferencing-useGcp',
|
||||
action = 'store_true',
|
||||
default = False,
|
||||
help = 'Enabling GCPs from the file above. The GCP file '
|
||||
'is not used by default.')
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Enabling GCPs from the file above. The GCP file '
|
||||
'is not used by default.')
|
||||
|
||||
parser.add_argument('--odm_orthophoto-resolution',
|
||||
metavar='<float > 0.0>',
|
||||
default=20.0,
|
||||
type=float,
|
||||
help=('Orthophoto ground resolution in pixels/meter'
|
||||
'Default: %(default)s'))
|
||||
'Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--zip-results',
|
||||
action='store_true',
|
||||
|
@ -240,6 +241,6 @@ parser.add_argument('--time',
|
|||
action='store_true',
|
||||
default=False,
|
||||
help='Generates a benchmark file with runtime info\n'
|
||||
'Default: %(default)s')
|
||||
'Default: %(default)s')
|
||||
|
||||
args = vars(parser.parse_args())
|
||||
|
|
11
opendm/io.py
11
opendm/io.py
|
@ -1,25 +1,32 @@
|
|||
import os
|
||||
|
||||
|
||||
def get_files_list(path_dir):
|
||||
return os.listdir(path_dir)
|
||||
|
||||
|
||||
def absolute_path_file(path_file):
|
||||
return os.path.abspath(path_file)
|
||||
|
||||
|
||||
def extract_file_from_path_file(path_file):
|
||||
path, file = os.path.split(path_file)
|
||||
return file
|
||||
|
||||
|
||||
def extract_path_from_file(file):
|
||||
path_file = os.path.abspath(os.path.dirname(file))
|
||||
path, file = os.path.split(path_file)
|
||||
return path
|
||||
|
||||
|
||||
def join_paths(path1, path2):
|
||||
return os.path.join(path1, path2)
|
||||
|
||||
|
||||
def file_exists(path_file):
|
||||
return os.path.isfile(path_file)
|
||||
return os.path.isfile(path_file)
|
||||
|
||||
|
||||
def dir_exists(dirname):
|
||||
return os.path.isdir(dirname)
|
||||
return os.path.isdir(dirname)
|
||||
|
|
|
@ -5,14 +5,18 @@ WARNING = '\033[93m'
|
|||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
|
||||
|
||||
def ODM_INFO(str):
|
||||
print OKBLUE + '[INFO] ' + str + ENDC
|
||||
print OKBLUE + '[INFO] ' + str + ENDC
|
||||
|
||||
|
||||
def ODM_WARNING(str):
|
||||
print WARNING + '[WARNING] ' + str + ENDC
|
||||
print WARNING + '[WARNING] ' + str + ENDC
|
||||
|
||||
|
||||
def ODM_ERROR(str):
|
||||
print FAIL + '[ERROR] ' + str + ENDC
|
||||
|
||||
print FAIL + '[ERROR] ' + str + ENDC
|
||||
|
||||
|
||||
def ODM_DEBUG(str):
|
||||
print OKGREEN + '[DEBUG] ' + str + ENDC
|
||||
print OKGREEN + '[DEBUG] ' + str + ENDC
|
||||
|
|
|
@ -8,11 +8,13 @@ import subprocess
|
|||
from opendm import context
|
||||
from opendm import log
|
||||
|
||||
|
||||
def get_ccd_widths():
|
||||
"""Return the CCD Width of the camera listed in the JSON defs file."""
|
||||
with open(context.ccd_widths_path) as jsonFile:
|
||||
return json.load(jsonFile)
|
||||
|
||||
|
||||
def run(cmd):
|
||||
"""Run a system command"""
|
||||
log.ODM_DEBUG('running %s' % cmd)
|
||||
|
@ -23,10 +25,12 @@ def run(cmd):
|
|||
sys.exit("\nquitting cause: \n\t" + cmd + "\nreturned with code " +
|
||||
str(returnCode) + ".\n")
|
||||
|
||||
|
||||
def now():
|
||||
"""Return the current time"""
|
||||
return datetime.datetime.now().strftime('%a %b %d %H:%M:%S %Z %Y')
|
||||
|
||||
|
||||
def run_and_return(cmdSrc, cmdDest=None):
|
||||
"""Run a system command and return the output"""
|
||||
process = subprocess.Popen(cmdSrc, stdout=subprocess.PIPE, shell=True)
|
||||
|
@ -35,8 +39,8 @@ def run_and_return(cmdSrc, cmdDest=None):
|
|||
|
||||
|
||||
def mkdir_p(path):
|
||||
'''Make a directory including parent directories.
|
||||
'''
|
||||
"""Make a directory including parent directories.
|
||||
"""
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except os.error as exc:
|
||||
|
@ -50,26 +54,3 @@ def calculate_EPSG(utmZone, south):
|
|||
return 32700 + utmZone
|
||||
else:
|
||||
return 32600 + utmZone
|
||||
|
||||
|
||||
def parse_coordinate_system():
|
||||
"""Write attributes to jobOptions from coord file"""
|
||||
if os.path.isfile(jobOptions['jobDir'] +
|
||||
'/odm_georeferencing/coordFile.txt'):
|
||||
with open(jobOptions['jobDir'] + '/odm_georeferencing/coordFile.txt') as f:
|
||||
for lineNumber, line in enumerate(f):
|
||||
if lineNumber == 0:
|
||||
tokens = line.split(' ')
|
||||
if len(tokens) == 3:
|
||||
utmZoneString = tokens[2][0:len(tokens[2])-2].strip()
|
||||
utmSouthBool = (tokens[2][len(tokens[2])-2].strip() == 'S')
|
||||
jobOptions['csString'] = '+datum=WGS84 +proj=utm +zone=' \
|
||||
+ utmZoneString + (' +south' if utmSouthBool else '')
|
||||
jobOptions['epsg'] = calculate_EPSG(int(utmZoneString), utmSouthBool)
|
||||
elif lineNumber == 1:
|
||||
tokens = line.split(' ')
|
||||
if len(tokens) == 2:
|
||||
jobOptions['utmEastOffset'] = int(tokens[0].strip())
|
||||
jobOptions['utmNorthOffset'] = int(tokens[1].strip())
|
||||
else:
|
||||
break
|
||||
|
|
229
opendm/tasks.py
229
opendm/tasks.py
|
@ -8,143 +8,144 @@ from scripts.resize import resize
|
|||
from scripts.opensfm import opensfm
|
||||
|
||||
# Define pipeline tasks
|
||||
tasks_dict = { '1': 'resize',
|
||||
'2': 'opensfm',
|
||||
'3': 'cmvs',
|
||||
'4': 'pmvs',
|
||||
'5': 'odm_meshing',
|
||||
'6': 'odm_texturing',
|
||||
'7': 'odm_georeferencing',
|
||||
'8': 'odm_orthophoto',
|
||||
'9': 'zip_results' }
|
||||
tasks_dict = {'1': 'resize',
|
||||
'2': 'opensfm',
|
||||
'3': 'cmvs',
|
||||
'4': 'pmvs',
|
||||
'5': 'odm_meshing',
|
||||
'6': 'odm_texturing',
|
||||
'7': 'odm_georeferencing',
|
||||
'8': 'odm_orthophoto',
|
||||
'9': 'zip_results'}
|
||||
|
||||
|
||||
class ODMTaskManager(object):
|
||||
"""docstring for ODMTaskManager"""
|
||||
def __init__(self, odm_app):
|
||||
self.odm_app = odm_app
|
||||
self.initial_task_id = 0
|
||||
self.current_task_id = 0
|
||||
self.final_task_id = len(tasks_dict)
|
||||
self.tasks = self.init_tasks(tasks_dict, self.odm_app)
|
||||
"""docstring for ODMTaskManager"""
|
||||
|
||||
def init_tasks(self, _tasks_dict, _odm_app):
|
||||
# dict to store tasks objects
|
||||
tasks = {}
|
||||
# loop over tasks dict
|
||||
for key, in _tasks_dict:
|
||||
# instantiate and append ODMTask
|
||||
task_name = _tasks_dict[key]
|
||||
tasks[key] = ODMTask(key, task_name)
|
||||
def __init__(self, odm_app):
|
||||
self.odm_app = odm_app
|
||||
self.initial_task_id = 0
|
||||
self.current_task_id = 0
|
||||
self.final_task_id = len(tasks_dict)
|
||||
self.tasks = self.init_tasks(tasks_dict, self.odm_app)
|
||||
|
||||
# setup tasks
|
||||
if task_name == 'resize':
|
||||
# setup this task
|
||||
command = resize
|
||||
inputs = { 'project_path': _odm_app.project_path,
|
||||
'args': _odm_app.args,
|
||||
'photos': _odm_app.photos }
|
||||
def init_tasks(self, _tasks_dict, _odm_app):
|
||||
# dict to store tasks objects
|
||||
tasks = {}
|
||||
# loop over tasks dict
|
||||
for key, in _tasks_dict:
|
||||
# instantiate and append ODMTask
|
||||
task_name = _tasks_dict[key]
|
||||
tasks[key] = ODMTask(key, task_name)
|
||||
|
||||
elif task_name == 'opensfm':
|
||||
# setup this task
|
||||
command = opensfm
|
||||
inputs = { 'project_path': _odm_app.project_path,
|
||||
'args': _odm_app.args,
|
||||
'photos': _odm_app.photos }
|
||||
# setup tasks
|
||||
if task_name == 'resize':
|
||||
# setup this task
|
||||
command = resize
|
||||
inputs = {'project_path': _odm_app.project_path,
|
||||
'args': _odm_app.args,
|
||||
'photos': _odm_app.photos}
|
||||
|
||||
elif task_name == 'cmvs':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
elif task_name == 'opensfm':
|
||||
# setup this task
|
||||
command = opensfm
|
||||
inputs = {'project_path': _odm_app.project_path,
|
||||
'args': _odm_app.args,
|
||||
'photos': _odm_app.photos}
|
||||
|
||||
elif task_name == 'pmvs':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
elif task_name == 'cmvs':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
||||
elif task_name == 'odm_meshing':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
elif task_name == 'pmvs':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
||||
elif task_name == 'odm_texturing':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
elif task_name == 'odm_meshing':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
||||
elif task_name == 'odm_georeferencing':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
elif task_name == 'odm_texturing':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
||||
elif task_name == 'odm_orthophoto':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
elif task_name == 'odm_georeferencing':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
||||
elif task_name == 'zip_results':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
elif task_name == 'odm_orthophoto':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
||||
else:
|
||||
log.ODM_ERROR('task_name %s is not valid' % task_name)
|
||||
elif task_name == 'zip_results':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
||||
# setup task configuration
|
||||
task = tasks[key]
|
||||
task.command = command
|
||||
task.inputs = inputs
|
||||
else:
|
||||
log.ODM_ERROR('task_name %s is not valid' % task_name)
|
||||
|
||||
return tasks
|
||||
# setup task configuration
|
||||
task = tasks[key]
|
||||
task.command = command
|
||||
task.inputs = inputs
|
||||
|
||||
def run_tasks(self):
|
||||
return tasks
|
||||
|
||||
#curr_task = self.tasks['resize']
|
||||
def run_tasks(self):
|
||||
|
||||
#self.tasks['resize']
|
||||
# curr_task = self.tasks['resize']
|
||||
|
||||
for id in range(self.initial_task_id, self.final_task_id + 1):
|
||||
# catch task with current id
|
||||
task = self.tasks[str(id)]
|
||||
# update task tracking
|
||||
log.ODM_INFO('Running task %s: %s' % (task.id, task.name))
|
||||
self.current_task_id = task.id
|
||||
# run task
|
||||
task.state = task.run()
|
||||
if task.state == 2:
|
||||
log.ODM_INFO('Succeeded task %s: %s - %s' % (task.id, task.name, system.now()))
|
||||
else:
|
||||
log.ODM_ERROR('Aborted task %s: %s' % (task.id, task.name))
|
||||
# self.tasks['resize']
|
||||
|
||||
for id in range(self.initial_task_id, self.final_task_id + 1):
|
||||
# catch task with current id
|
||||
task = self.tasks[str(id)]
|
||||
# update task tracking
|
||||
log.ODM_INFO('Running task %s: %s' % (task.id, task.name))
|
||||
self.current_task_id = task.id
|
||||
# run task
|
||||
task.state = task.run()
|
||||
if task.state == 2:
|
||||
log.ODM_INFO('Succeeded task %s: %s - %s' % (task.id, task.name, system.now()))
|
||||
else:
|
||||
log.ODM_ERROR('Aborted task %s: %s' % (task.id, task.name))
|
||||
|
||||
|
||||
class ODMTask(object):
|
||||
"""docstring for ODMTask"""
|
||||
def __init__(self, id, name):
|
||||
# task definition
|
||||
self.id = id
|
||||
self.name = name
|
||||
# task i/o
|
||||
self.command = None
|
||||
self.inputs = {}
|
||||
# Current task state (0:waiting, 1:running, 2:succeded: 3:failed)
|
||||
# By default we set a task in waiting state
|
||||
self.state = 0
|
||||
"""docstring for ODMTask"""
|
||||
|
||||
# Launch task
|
||||
def run(self):
|
||||
# while doing something
|
||||
self.state = 1
|
||||
return self.launch_command()
|
||||
def __init__(self, id, name):
|
||||
# task definition
|
||||
self.id = id
|
||||
self.name = name
|
||||
# task i/o
|
||||
self.command = None
|
||||
self.inputs = {}
|
||||
# Current task state (0:waiting, 1:running, 2:succeded: 3:failed)
|
||||
# By default we set a task in waiting state
|
||||
self.state = 0
|
||||
|
||||
def launch_command(self):
|
||||
if self.command is None:
|
||||
log.ODM_ERROR('Call method for task %s not defined' % self.name)
|
||||
return 3 # failed
|
||||
# run conmmand
|
||||
try:
|
||||
succeed = self.command(**self.inputs)
|
||||
return 2 if succeed else 3 # 2:succeed, 3:failed
|
||||
except Exception, e:
|
||||
log.ODM_ERROR(str(e))
|
||||
return 3 # failed
|
||||
# Launch task
|
||||
def run(self):
|
||||
# while doing something
|
||||
self.state = 1
|
||||
return self.launch_command()
|
||||
|
||||
def launch_command(self):
|
||||
if self.command is None:
|
||||
log.ODM_ERROR('Call method for task %s not defined' % self.name)
|
||||
return 3 # failed
|
||||
# run conmmand
|
||||
try:
|
||||
succeed = self.command(**self.inputs)
|
||||
return 2 if succeed else 3 # 2:succeed, 3:failed
|
||||
except Exception, e:
|
||||
log.ODM_ERROR(str(e))
|
||||
return 3 # failed
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import os
|
||||
import cv2
|
||||
import pyexiv2
|
||||
import subprocess
|
||||
import re
|
||||
from fractions import Fraction
|
||||
|
||||
|
@ -10,9 +8,11 @@ import io
|
|||
import system
|
||||
import context
|
||||
|
||||
|
||||
class ODM_Photo:
|
||||
""" ODMPhoto - a class for ODMPhotos
|
||||
"""
|
||||
|
||||
def __init__(self, path_file, force_focal, force_ccd):
|
||||
# general purpose
|
||||
self.path_file = path_file
|
||||
|
@ -28,12 +28,12 @@ class ODM_Photo:
|
|||
self.camera_model = None
|
||||
# parse values from metadata
|
||||
self.parse_pyexiv2_values(self.path_file, force_focal, force_ccd)
|
||||
# compute focal lenght into pixels
|
||||
# compute focal length into pixels
|
||||
self.update_focal()
|
||||
|
||||
# print log message
|
||||
log.ODM_DEBUG('Loaded %s | dimensions: %s x %s | focal: %s | ccd: %s' % \
|
||||
(self.filename, self.width, self.height, self.focal_length, self.ccd_width))
|
||||
log.ODM_DEBUG('Loaded %s | dimensions: %s x %s | focal: %s | ccd: %s' %
|
||||
(self.filename, self.width, self.height, self.focal_length, self.ccd_width))
|
||||
|
||||
def update_focal(self):
|
||||
# compute focal length in pixels
|
||||
|
@ -59,12 +59,15 @@ class ODM_Photo:
|
|||
try:
|
||||
val = metadata[key].value
|
||||
# parse tag names
|
||||
if key == 'Exif.Image.Make': self.camera_make = val
|
||||
elif key == 'Exif.Image.Model': self.camera_model = val
|
||||
elif key == 'Exif.Photo.FocalLength': self.focal_length = float(val)
|
||||
if key == 'Exif.Image.Make':
|
||||
self.camera_make = val
|
||||
elif key == 'Exif.Image.Model':
|
||||
self.camera_model = val
|
||||
elif key == 'Exif.Photo.FocalLength':
|
||||
self.focal_length = float(val)
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
|
||||
# needed to do that since sometimes metadata contains wrong data
|
||||
img = cv2.imread(_path_file)
|
||||
self.width = img.shape[1]
|
||||
|
@ -81,7 +84,8 @@ class ODM_Photo:
|
|||
# search ccd by camera model
|
||||
key = [x for x in ccd_widths.keys() if self.camera_model in x]
|
||||
# convert to float if found
|
||||
if key: self.ccd_width = float(ccd_widths[key[0]])
|
||||
if key:
|
||||
self.ccd_width = float(ccd_widths[key[0]])
|
||||
# else:
|
||||
# log.ODM_ERROR('Could not find ccd_width in file')
|
||||
|
||||
|
@ -89,6 +93,7 @@ class ODM_Photo:
|
|||
# TODO: finish this class
|
||||
class ODM_Reconstruction(object):
|
||||
"""docstring for ODMReconstruction"""
|
||||
|
||||
def __init__(self, arg):
|
||||
super(ODMReconstruction, self).__init__()
|
||||
self.arg = arg
|
||||
|
@ -96,6 +101,7 @@ class ODM_Reconstruction(object):
|
|||
|
||||
class ODM_GCPoint(object):
|
||||
"""docstring for ODMPoint"""
|
||||
|
||||
def __init__(self, x, y, z):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
@ -104,6 +110,7 @@ class ODM_GCPoint(object):
|
|||
|
||||
class ODM_GeoRef(object):
|
||||
"""docstring for ODMUtmZone"""
|
||||
|
||||
def __init__(self):
|
||||
self.datum = 'WGS84'
|
||||
self.epsg = None
|
||||
|
@ -129,13 +136,13 @@ class ODM_GeoRef(object):
|
|||
log.ODM_ERROR('Empty EPSG: Could not convert to LAS')
|
||||
return
|
||||
|
||||
kwargs = { 'bin': context.pdal_path,
|
||||
'f_in': _file,
|
||||
'f_out': _file + '.las',
|
||||
'east': self.utm_east_offset,
|
||||
'north': self.utm_north_offset,
|
||||
'epsg': self.epsg,
|
||||
'xml': pdalXML}
|
||||
kwargs = {'bin': context.pdal_path,
|
||||
'f_in': _file,
|
||||
'f_out': _file + '.las',
|
||||
'east': self.utm_east_offset,
|
||||
'north': self.utm_north_offset,
|
||||
'epsg': self.epsg,
|
||||
'xml': pdalXML}
|
||||
|
||||
# call txt2las
|
||||
# system.run('{bin}/txt2las -i {f_in} -o {f_out} -skip 30 -parse xyzRGBssss ' \
|
||||
|
@ -143,7 +150,7 @@ class ODM_GeoRef(object):
|
|||
# '-translate_xyz 0 -epsg {epsg}'.format(**kwargs))
|
||||
#
|
||||
# create pipeline file transform.xml to enable transformation
|
||||
pipelineXml = '<?xml version=\"1.0\" encoding=\"utf-8\"?>'
|
||||
pipelineXml = '<?xml version=\"1.0\" encoding=\"utf-8\"?>'
|
||||
pipelineXml += '<Pipeline version=\"1.0\">'
|
||||
pipelineXml += ' <Writer type=\"writers.las\">'
|
||||
pipelineXml += ' <Option name=\"filename\">'
|
||||
|
@ -176,16 +183,16 @@ class ODM_GeoRef(object):
|
|||
|
||||
gcp = self.gcps[idx]
|
||||
|
||||
kwargs = { 'datum': self.datum,
|
||||
'zone': self.utm_zone,
|
||||
'file': _file,
|
||||
'x': gcp.x + self.utm_east_offset,
|
||||
'y': gcp.y + self.utm_north_offset,
|
||||
'z': gcp.z }
|
||||
kwargs = {'datum': self.datum,
|
||||
'zone': self.utm_zone,
|
||||
'file': _file,
|
||||
'x': gcp.x + self.utm_east_offset,
|
||||
'y': gcp.y + self.utm_north_offset,
|
||||
'z': gcp.z}
|
||||
|
||||
latlon = system.run_and_return('echo {x} {y} | cs2cs +proj=utm '
|
||||
'+datum={datum} +ellps={datum} +zone={zone} +units=m +to '
|
||||
'+proj=latlon +ellps={datum}'.format(**kwargs)).split()
|
||||
'+datum={datum} +ellps={datum} +zone={zone} +units=m +to '
|
||||
'+proj=latlon +ellps={datum}'.format(**kwargs)).split()
|
||||
|
||||
# Example: 83d18'16.285"W
|
||||
# Example: 41d2'11.789"N
|
||||
|
@ -198,7 +205,7 @@ class ODM_GeoRef(object):
|
|||
alt_str = ''
|
||||
else:
|
||||
log.ODM_ERROR('Something went wrong %s' % latlon)
|
||||
|
||||
|
||||
tokens = re.split("[d '\"]+", lon_str)
|
||||
if len(tokens) >= 4:
|
||||
lon_deg, lon_min, lon_sec = tokens[:3]
|
||||
|
@ -226,7 +233,7 @@ class ODM_GeoRef(object):
|
|||
metadata = pyexiv2.ImageMetadata(_photo.path_file)
|
||||
metadata.read()
|
||||
|
||||
## set values
|
||||
# set values
|
||||
|
||||
# GPS latitude
|
||||
key = 'Exif.GPSInfo.GPSLatitude'
|
||||
|
@ -256,10 +263,9 @@ class ODM_GeoRef(object):
|
|||
key = 'Exif.GPSInfo.GPSAltitudeRef'
|
||||
metadata[key] = pyexiv2.ExifTag(key, '0')
|
||||
|
||||
## write values
|
||||
# write values
|
||||
metadata.write()
|
||||
|
||||
|
||||
def parse_coordinate_system(self, _file):
|
||||
"""Write attributes to jobOptions from coord file"""
|
||||
# check for coordinate file existence
|
||||
|
@ -273,8 +279,8 @@ class ODM_GeoRef(object):
|
|||
# 'WGS84 UTM 17N'
|
||||
line = f.readline().split(' ')
|
||||
self.datum = line[0]
|
||||
self.utm_pole = line[2][len(line)-1]
|
||||
self.utm_zone = int(line[2][:len(line)-1])
|
||||
self.utm_pole = line[2][len(line) - 1]
|
||||
self.utm_zone = int(line[2][:len(line) - 1])
|
||||
# extract east and west offsets from second line.
|
||||
# We will assume the following format:
|
||||
# '440143 4588391'
|
||||
|
@ -293,10 +299,10 @@ class ODM_GeoRef(object):
|
|||
|
||||
class ODM_Tree(object):
|
||||
def __init__(self, root_path):
|
||||
### root path to the project
|
||||
# root path to the project
|
||||
self.root_path = io.absolute_path_file(root_path)
|
||||
|
||||
### modules paths
|
||||
# modules paths
|
||||
|
||||
# here are defined where all modules should be located in
|
||||
# order to keep track all files al directories during the
|
||||
|
@ -311,8 +317,8 @@ class ODM_Tree(object):
|
|||
self.odm_orthophoto = io.join_paths(self.root_path, 'odm_orthophoto')
|
||||
self.odm_pdal = io.join_paths(self.root_path, 'pdal')
|
||||
|
||||
### important files paths
|
||||
|
||||
# important files paths
|
||||
|
||||
# opensfm
|
||||
self.opensfm_bundle = io.join_paths(self.opensfm, 'bundle_r000.out')
|
||||
self.opensfm_bundle_list = io.join_paths(self.opensfm, 'list_r000.out')
|
||||
|
@ -325,11 +331,11 @@ class ODM_Tree(object):
|
|||
self.pmvs_visdat = io.join_paths(self.pmvs_rec_path, 'vis.dat')
|
||||
self.pmvs_options = io.join_paths(self.pmvs_rec_path, 'pmvs_options.txt')
|
||||
self.pmvs_model = io.join_paths(self.pmvs_rec_path, 'models/option-0000.ply')
|
||||
|
||||
|
||||
# odm_meshing
|
||||
self.odm_mesh = io.join_paths(self.odm_meshing, 'odm_mesh.ply')
|
||||
self.odm_meshing_log = io.join_paths(self.odm_meshing, 'odm_meshing_log.txt')
|
||||
|
||||
|
||||
# odm_texturing
|
||||
self.odm_textured_model_obj = io.join_paths(
|
||||
self.odm_texturing, 'odm_textured_model.obj')
|
||||
|
@ -368,4 +374,3 @@ class ODM_Tree(object):
|
|||
self.odm_orthophoto_corners = io.join_paths(self.odm_orthophoto, 'odm_orthphoto_corners.txt')
|
||||
self.odm_orthophoto_log = io.join_paths(self.odm_orthophoto, 'odm_orthophoto_log.txt')
|
||||
self.odm_orthophoto_tif_log = io.join_paths(self.odm_orthophoto, 'gdal_translate_log.txt')
|
||||
|
||||
|
|
36
run.py
36
run.py
|
@ -9,28 +9,30 @@ import ecto
|
|||
|
||||
from scripts.odm_app import ODMApp
|
||||
|
||||
|
||||
def usage():
|
||||
log.ODM_ERROR('USAGE: %s --project-path [project_path]' % sys.argv[0])
|
||||
log.ODM_ERROR('OpenDroneMap app finished - %s' % system.now())
|
||||
sys.exit(0)
|
||||
|
||||
log.ODM_ERROR('USAGE: %s --project-path [project_path]' % sys.argv[0])
|
||||
log.ODM_ERROR('OpenDroneMap app finished - %s' % system.now())
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
log.ODM_INFO('Initializing OpenDroneMap app - %s' % system.now())
|
||||
log.ODM_INFO('Initializing OpenDroneMap app - %s' % system.now())
|
||||
|
||||
# Force to provide the images path
|
||||
if config.args.get('project_path') is None:
|
||||
usage()
|
||||
# Force to provide the images path
|
||||
if config.args.get('project_path') is None:
|
||||
usage()
|
||||
|
||||
# create an instance of my App BlackBox
|
||||
# internally configure all tasks
|
||||
app = ODMApp(args=config.args)
|
||||
# create an instance of my App BlackBox
|
||||
# internally configure all tasks
|
||||
app = ODMApp(args=config.args)
|
||||
|
||||
# create a plasm that only contains the BlackBox
|
||||
plasm = ecto.Plasm()
|
||||
plasm.insert(app)
|
||||
# create a plasm that only contains the BlackBox
|
||||
plasm = ecto.Plasm()
|
||||
plasm.insert(app)
|
||||
|
||||
# execute the plasm
|
||||
plasm.execute(niter=1)
|
||||
# execute the plasm
|
||||
plasm.execute(niter=1)
|
||||
|
||||
log.ODM_INFO('OpenDroneMap app finished - %s' % system.now())
|
||||
log.ODM_INFO('OpenDroneMap app finished - %s' % system.now())
|
||||
|
|
|
@ -5,6 +5,7 @@ from opendm import log
|
|||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
|
||||
class ODMCmvsCell(ecto.Cell):
|
||||
|
||||
def declare_params(self, params):
|
||||
|
@ -53,7 +54,7 @@ class ODMCmvsCell(ecto.Cell):
|
|||
system.run('{bin} {prefix}/ {max_images} {cores}'.format(**kwargs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid CMVS file in: %s' %
|
||||
(tree.pmvs_bundle))
|
||||
tree.pmvs_bundle)
|
||||
|
||||
log.ODM_INFO('Running OMD CMVS Cell - Finished')
|
||||
return ecto.OK if args['end_with'] != 'cmvs' else ecto.QUIT
|
||||
|
|
|
@ -6,13 +6,14 @@ from opendm import io
|
|||
from opendm import types
|
||||
from opendm import log
|
||||
|
||||
|
||||
class ODMLoadDatasetCell(ecto.Cell):
|
||||
|
||||
def declare_params(self, params):
|
||||
params.declare("force_focal", 'Override the focal length information for the '
|
||||
'images', None)
|
||||
'images', None)
|
||||
params.declare("force_ccd", 'Override the ccd widht information for the '
|
||||
'images', None)
|
||||
'images', None)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -65,4 +66,4 @@ class ODMLoadDatasetCell(ecto.Cell):
|
|||
outputs.photos = photos
|
||||
|
||||
log.ODM_INFO('Running ODM Load Dataset Cell - Finished')
|
||||
return ecto.OK
|
||||
return ecto.OK
|
||||
|
|
|
@ -14,11 +14,14 @@ from odm_texturing import ODMTexturingCell
|
|||
from odm_georeferencing import ODMGeoreferencingCell
|
||||
from odm_orthophoto import ODMOrthoPhotoCell
|
||||
|
||||
|
||||
class ODMApp(ecto.BlackBox):
|
||||
''' ODMApp - a class for ODM Activities
|
||||
'''
|
||||
"""ODMApp - a class for ODM Activities
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
ecto.BlackBox.__init__(self, *args, **kwargs)
|
||||
self.tree = None
|
||||
|
||||
@staticmethod
|
||||
def declare_direct_params(p):
|
||||
|
@ -30,38 +33,38 @@ class ODMApp(ecto.BlackBox):
|
|||
Implement the virtual function from the base class
|
||||
Only cells from which something is forwarded have to be declared
|
||||
"""
|
||||
cells = { 'args': ecto.Constant(value=p.args),
|
||||
'dataset': ODMLoadDatasetCell(force_focal=p.args['force_focal'],
|
||||
force_ccd=p.args['force_ccd']),
|
||||
'resize': ODMResizeCell(resize_to=p.args['resize_to']),
|
||||
'opensfm': ODMOpenSfMCell(use_exif_size=False,
|
||||
feature_process_size=p.args['resize_to'],
|
||||
feature_min_frames=p.args['min_num_features'],
|
||||
processes=context.num_cores,
|
||||
matching_gps_neighbors=p.args['matcher_neighbors'],
|
||||
matching_gps_distance=p.args['matcher_distance']),
|
||||
'cmvs': ODMCmvsCell(max_images=p.args['cmvs_maxImages']),
|
||||
'pmvs': ODMPmvsCell(level=p.args['pmvs_level'],
|
||||
csize=p.args['pmvs_csize'],
|
||||
thresh=p.args['pmvs_threshold'],
|
||||
wsize=p.args['pmvs_wsize'],
|
||||
min_imgs=p.args['pmvs_minImageNum'],
|
||||
cores=p.args['pmvs_num_cores']),
|
||||
'meshing': ODMeshingCell(max_vertex=p.args['odm_meshing_maxVertexCount'],
|
||||
oct_tree=p.args['odm_meshing_octreeDepth'],
|
||||
samples=p.args['odm_meshing_samplesPerNode'],
|
||||
solver=p.args['odm_meshing_solverDivide']),
|
||||
'texturing': ODMTexturingCell(resize=p.args['resize_to'],
|
||||
resolution=p.args['odm_texturing_textureResolution'],
|
||||
size=p.args['odm_texturing_textureWithSize']),
|
||||
'georeferencing': ODMGeoreferencingCell(img_size=p.args['resize_to'],
|
||||
gcp_file=p.args['odm_georeferencing_gcpFile'],
|
||||
use_gcp=p.args['odm_georeferencing_useGcp']),
|
||||
'orthophoto': ODMOrthoPhotoCell(resolution=p.args['odm_orthophoto_resolution'])
|
||||
cells = {'args': ecto.Constant(value=p.args),
|
||||
'dataset': ODMLoadDatasetCell(force_focal=p.args['force_focal'],
|
||||
force_ccd=p.args['force_ccd']),
|
||||
'resize': ODMResizeCell(resize_to=p.args['resize_to']),
|
||||
'opensfm': ODMOpenSfMCell(use_exif_size=False,
|
||||
feature_process_size=p.args['resize_to'],
|
||||
feature_min_frames=p.args['min_num_features'],
|
||||
processes=context.num_cores,
|
||||
matching_gps_neighbors=p.args['matcher_neighbors'],
|
||||
matching_gps_distance=p.args['matcher_distance']),
|
||||
'cmvs': ODMCmvsCell(max_images=p.args['cmvs_maxImages']),
|
||||
'pmvs': ODMPmvsCell(level=p.args['pmvs_level'],
|
||||
csize=p.args['pmvs_csize'],
|
||||
thresh=p.args['pmvs_threshold'],
|
||||
wsize=p.args['pmvs_wsize'],
|
||||
min_imgs=p.args['pmvs_minImageNum'],
|
||||
cores=p.args['pmvs_num_cores']),
|
||||
'meshing': ODMeshingCell(max_vertex=p.args['odm_meshing_maxVertexCount'],
|
||||
oct_tree=p.args['odm_meshing_octreeDepth'],
|
||||
samples=p.args['odm_meshing_samplesPerNode'],
|
||||
solver=p.args['odm_meshing_solverDivide']),
|
||||
'texturing': ODMTexturingCell(resize=p.args['resize_to'],
|
||||
resolution=p.args['odm_texturing_textureResolution'],
|
||||
size=p.args['odm_texturing_textureWithSize']),
|
||||
'georeferencing': ODMGeoreferencingCell(img_size=p.args['resize_to'],
|
||||
gcp_file=p.args['odm_georeferencing_gcpFile'],
|
||||
use_gcp=p.args['odm_georeferencing_useGcp']),
|
||||
'orthophoto': ODMOrthoPhotoCell(resolution=p.args['odm_orthophoto_resolution'])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return cells
|
||||
return cells
|
||||
|
||||
def configure(self, p, _i, _o):
|
||||
tree = types.ODM_Tree(p.args['project_path'])
|
||||
|
@ -69,54 +72,55 @@ class ODMApp(ecto.BlackBox):
|
|||
|
||||
def connections(self, _p):
|
||||
# define initial task
|
||||
initial_task = _p.args['start_with']
|
||||
initial_task_id = config.processopts.index(initial_task)
|
||||
# TODO: What is this?
|
||||
# initial_task = _p.args['start_with']
|
||||
# initial_task_id = config.processopts.index(initial_task)
|
||||
|
||||
## define the connections like you would for the plasm
|
||||
connections = []
|
||||
# define the connections like you would for the plasm
|
||||
# connections = []
|
||||
|
||||
## load the dataset
|
||||
connections = [ self.tree[:] >> self.dataset['tree'] ]
|
||||
# load the dataset
|
||||
connections = [self.tree[:] >> self.dataset['tree']]
|
||||
|
||||
# run resize cell
|
||||
connections += [ self.tree[:] >> self.resize['tree'],
|
||||
self.args[:] >> self.resize['args'],
|
||||
self.dataset['photos'] >> self.resize['photos'] ]
|
||||
connections += [self.tree[:] >> self.resize['tree'],
|
||||
self.args[:] >> self.resize['args'],
|
||||
self.dataset['photos'] >> self.resize['photos']]
|
||||
|
||||
# run opensfm with images from load dataset
|
||||
connections += [ self.tree[:] >> self.opensfm['tree'],
|
||||
self.args[:] >> self.opensfm['args'],
|
||||
self.resize['photos'] >> self.opensfm['photos'] ]
|
||||
|
||||
# run cmvs
|
||||
connections += [ self.tree[:] >> self.cmvs['tree'],
|
||||
self.args[:] >> self.cmvs['args'],
|
||||
self.opensfm['reconstruction'] >> self.cmvs['reconstruction'] ]
|
||||
|
||||
# run pmvs
|
||||
connections += [ self.tree[:] >> self.pmvs['tree'],
|
||||
self.args[:] >> self.pmvs['args'],
|
||||
self.cmvs['reconstruction'] >> self.pmvs['reconstruction'] ]
|
||||
|
||||
# create odm mesh
|
||||
connections += [ self.tree[:] >> self.meshing['tree'],
|
||||
self.args[:] >> self.meshing['args'],
|
||||
self.pmvs['reconstruction'] >> self.meshing['reconstruction'] ]
|
||||
|
||||
# create odm texture
|
||||
connections += [ self.tree[:] >> self.texturing['tree'],
|
||||
self.args[:] >> self.texturing['args'],
|
||||
self.meshing['reconstruction'] >> self.texturing['reconstruction'] ]
|
||||
|
||||
# create odm georeference
|
||||
connections += [ self.tree[:] >> self.georeferencing['tree'],
|
||||
self.args[:] >> self.georeferencing['args'],
|
||||
self.dataset['photos'] >> self.georeferencing['photos'],
|
||||
self.texturing['reconstruction'] >> self.georeferencing['reconstruction'] ]
|
||||
connections += [self.tree[:] >> self.opensfm['tree'],
|
||||
self.args[:] >> self.opensfm['args'],
|
||||
self.resize['photos'] >> self.opensfm['photos']]
|
||||
|
||||
## create odm orthophoto
|
||||
connections += [ self.tree[:] >> self.orthophoto['tree'],
|
||||
self.args[:] >> self.orthophoto['args'],
|
||||
self.georeferencing['reconstruction'] >> self.orthophoto['reconstruction'] ]
|
||||
# run cmvs
|
||||
connections += [self.tree[:] >> self.cmvs['tree'],
|
||||
self.args[:] >> self.cmvs['args'],
|
||||
self.opensfm['reconstruction'] >> self.cmvs['reconstruction']]
|
||||
|
||||
# run pmvs
|
||||
connections += [self.tree[:] >> self.pmvs['tree'],
|
||||
self.args[:] >> self.pmvs['args'],
|
||||
self.cmvs['reconstruction'] >> self.pmvs['reconstruction']]
|
||||
|
||||
# create odm mesh
|
||||
connections += [self.tree[:] >> self.meshing['tree'],
|
||||
self.args[:] >> self.meshing['args'],
|
||||
self.pmvs['reconstruction'] >> self.meshing['reconstruction']]
|
||||
|
||||
# create odm texture
|
||||
connections += [self.tree[:] >> self.texturing['tree'],
|
||||
self.args[:] >> self.texturing['args'],
|
||||
self.meshing['reconstruction'] >> self.texturing['reconstruction']]
|
||||
|
||||
# create odm georeference
|
||||
connections += [self.tree[:] >> self.georeferencing['tree'],
|
||||
self.args[:] >> self.georeferencing['args'],
|
||||
self.dataset['photos'] >> self.georeferencing['photos'],
|
||||
self.texturing['reconstruction'] >> self.georeferencing['reconstruction']]
|
||||
|
||||
# create odm orthophoto
|
||||
connections += [self.tree[:] >> self.orthophoto['tree'],
|
||||
self.args[:] >> self.orthophoto['args'],
|
||||
self.georeferencing['reconstruction'] >> self.orthophoto['reconstruction']]
|
||||
|
||||
return connections
|
||||
|
|
|
@ -5,20 +5,20 @@ from opendm import io
|
|||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
class ODMeshingCell(ecto.Cell):
|
||||
|
||||
class ODMeshingCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("max_vertex", 'The maximum vertex count of the output '
|
||||
'mesh', 100000)
|
||||
'mesh', 100000)
|
||||
params.declare("oct_tree", 'Oct-tree depth used in the mesh reconstruction, '
|
||||
'increase to get more vertices, recommended '
|
||||
'values are 8-12', 9)
|
||||
'increase to get more vertices, recommended '
|
||||
'values are 8-12', 9)
|
||||
params.declare("samples", 'Number of points per octree node, recommended '
|
||||
'value: 1.0', 1)
|
||||
'value: 1.0', 1)
|
||||
params.declare("solver", 'Oct-tree depth at which the Laplacian equation '
|
||||
'is solved in the surface reconstruction step. '
|
||||
'Increasing this value increases computation '
|
||||
'times slightly but helps reduce memory usage.', 9)
|
||||
'is solved in the surface reconstruction step. '
|
||||
'Increasing this value increases computation '
|
||||
'times slightly but helps reduce memory usage.', 9)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -27,7 +27,7 @@ class ODMeshingCell(ecto.Cell):
|
|||
outputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
|
||||
|
||||
def process(self, inputs, outputs):
|
||||
|
||||
|
||||
log.ODM_INFO('Running OMD Meshing Cell')
|
||||
|
||||
# get inputs
|
||||
|
@ -55,17 +55,17 @@ class ODMeshingCell(ecto.Cell):
|
|||
'max_vertex': self.params.max_vertex,
|
||||
'oct_tree': self.params.oct_tree,
|
||||
'samples': self.params.samples,
|
||||
'solver':self.params.solver
|
||||
'solver': self.params.solver
|
||||
}
|
||||
|
||||
# run meshing binary
|
||||
system.run('{bin}/odm_meshing -inputFile {infile} ' \
|
||||
'-outputFile {outfile} -logFile {log} ' \
|
||||
'-maxVertexCount {max_vertex} -octreeDepth {oct_tree} ' \
|
||||
'-samplesPerNode {samples} -solverDivide {solver}'.format(**kwargs))
|
||||
system.run('{bin}/odm_meshing -inputFile {infile} '
|
||||
'-outputFile {outfile} -logFile {log} '
|
||||
'-maxVertexCount {max_vertex} -octreeDepth {oct_tree} '
|
||||
'-samplesPerNode {samples} -solverDivide {solver}'.format(**kwargs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid ODM Mesh file in: %s' %
|
||||
(tree.odm_mesh))
|
||||
|
||||
log.ODM_WARNING('Found a valid ODM Mesh file in: %s' %
|
||||
tree.odm_mesh)
|
||||
|
||||
log.ODM_INFO('Running OMD Meshing Cell - Finished')
|
||||
return ecto.OK if args['end_with'] != 'odm_meshing' else ecto.QUIT
|
||||
return ecto.OK if args['end_with'] != 'odm_meshing' else ecto.QUIT
|
||||
|
|
|
@ -47,17 +47,17 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
|||
}
|
||||
|
||||
# run odm_orthophoto
|
||||
system.run('{bin}/odm_orthophoto -inputFile {model_geo} ' \
|
||||
'-logFile {log} -outputFile {ortho} -resolution {res} ' \
|
||||
'-outputCornerFile {corners}'.format(**kwargs))
|
||||
system.run('{bin}/odm_orthophoto -inputFile {model_geo} '
|
||||
'-logFile {log} -outputFile {ortho} -resolution {res} '
|
||||
'-outputCornerFile {corners}'.format(**kwargs))
|
||||
|
||||
# Create georeferenced GeoTiff
|
||||
geoTiffCreated = False
|
||||
geotiffcreated = False
|
||||
georef = types.ODM_GeoRef()
|
||||
# creates the coord refs # TODO I don't want to have to do this twice- after odm_georef
|
||||
georef.parse_coordinate_system(tree.odm_georeferencing_coords)
|
||||
|
||||
if (georef.epsg and georef.utm_east_offset and georef.utm_north_offset):
|
||||
if georef.epsg and georef.utm_east_offset and georef.utm_north_offset:
|
||||
ulx = uly = lrx = lry = 0.0
|
||||
with open(tree.odm_orthophoto_corners) as f:
|
||||
for lineNumber, line in enumerate(f):
|
||||
|
@ -87,8 +87,8 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
|||
|
||||
system.run('gdal_translate -a_ullr {ulx} {uly} {lrx} {lry} '
|
||||
'-a_srs \"EPSG:{epsg}\" {png} {tiff} > {log}'.format(**kwargs))
|
||||
geoTiffCreated = True
|
||||
if not geoTiffCreated:
|
||||
geotiffcreated = True
|
||||
if not geotiffcreated:
|
||||
log.ODM_WARNING('No geo-referenced orthophoto created due '
|
||||
'to missing geo-referencing or corner coordinates.')
|
||||
|
||||
|
@ -97,54 +97,3 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
|||
|
||||
log.ODM_INFO('Running OMD OrthoPhoto Cell - Finished')
|
||||
return ecto.OK if args['end_with'] != 'odm_orthophoto' else ecto.QUIT
|
||||
|
||||
|
||||
def odm_orthophoto():
|
||||
"""Run odm_orthophoto"""
|
||||
print "\n - running orthophoto generation - " + system.now()
|
||||
|
||||
os.chdir(jobOptions["jobDir"])
|
||||
try:
|
||||
os.mkdir(jobOptions["jobDir"] + "/odm_orthophoto")
|
||||
except:
|
||||
pass
|
||||
|
||||
run("\"" + BIN_PATH + "/odm_orthophoto\" -inputFile " + jobOptions["jobDir"] + \
|
||||
"-results/odm_texturing/odm_textured_model_geo.obj -logFile " + jobOptions["jobDir"] \
|
||||
+ "/odm_orthophoto/odm_orthophoto_log.txt -outputFile " + jobOptions["jobDir"] \
|
||||
+ "-results/odm_orthphoto.png -resolution 20.0 -outputCornerFile " + jobOptions["jobDir"] \
|
||||
+ "/odm_orthphoto_corners.txt")
|
||||
|
||||
if "csString" not in jobOptions:
|
||||
parse_coordinate_system() # Writes the coord string to the jobOptions object
|
||||
|
||||
geoTiffCreated = False
|
||||
if ("csString" in jobOptions and
|
||||
"utmEastOffset" in jobOptions and "utmNorthOffset" in jobOptions):
|
||||
ulx = uly = lrx = lry = 0.0
|
||||
with open(jobOptions["jobDir"] +
|
||||
"/odm_orthphoto_corners.txt") as f: # Open tree.odm_orthophoto_corners
|
||||
for lineNumber, line in enumerate(f):
|
||||
if lineNumber == 0:
|
||||
tokens = line.split(' ')
|
||||
if len(tokens) == 4:
|
||||
ulx = float(tokens[0]) + \
|
||||
float(jobOptions["utmEastOffset"])
|
||||
lry = float(tokens[1]) + \
|
||||
float(jobOptions["utmNorthOffset"])
|
||||
lrx = float(tokens[2]) + \
|
||||
float(jobOptions["utmEastOffset"])
|
||||
uly = float(tokens[3]) + \
|
||||
float(jobOptions["utmNorthOffset"])
|
||||
|
||||
print(" Creating GeoTIFF...")
|
||||
sys.stdout.write(" ")
|
||||
run("gdal_translate -a_ullr " + str(ulx) + " " + str(uly) + " " +
|
||||
str(lrx) + " " + str(lry) + " -a_srs \"" + jobOptions["csString"] +
|
||||
"\" " + jobOptions["jobDir"] + "-results/odm_orthphoto.png " +
|
||||
jobOptions["jobDir"] + "-results/odm_orthphoto.tif")
|
||||
geoTiffCreated = True
|
||||
|
||||
if not geoTiffCreated:
|
||||
|
||||
print " Warning: No geo-referenced orthophoto created due to missing geo-referencing or corner coordinates."
|
|
@ -5,14 +5,14 @@ from opendm import io
|
|||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
class ODMTexturingCell(ecto.Cell):
|
||||
|
||||
class ODMTexturingCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("resize", 'resizes images by the largest side', 2400)
|
||||
params.declare("resolution", 'The resolution of the output textures. Must be '
|
||||
'greater than textureWithSize.', 4096)
|
||||
'greater than textureWithSize.', 4096)
|
||||
params.declare("size", 'The resolution to rescale the images performing '
|
||||
'the texturing.', 3600)
|
||||
'the texturing.', 3600)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -21,7 +21,7 @@ class ODMTexturingCell(ecto.Cell):
|
|||
outputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
|
||||
|
||||
def process(self, inputs, outputs):
|
||||
|
||||
|
||||
log.ODM_INFO('Running OMD Texturing Cell')
|
||||
|
||||
# get inputs
|
||||
|
@ -39,8 +39,8 @@ class ODMTexturingCell(ecto.Cell):
|
|||
'odm_texturing' in args['rerun_from'])
|
||||
|
||||
if not io.file_exists(tree.odm_textured_model_obj) or rerun_cell:
|
||||
log.ODM_DEBUG('Writting ODM Textured file in: %s' \
|
||||
% tree.odm_textured_model_obj)
|
||||
log.ODM_DEBUG('Writing ODM Textured file in: %s'
|
||||
% tree.odm_textured_model_obj)
|
||||
|
||||
# odm_texturing definitions
|
||||
kwargs = {
|
||||
|
@ -57,14 +57,14 @@ class ODMTexturingCell(ecto.Cell):
|
|||
}
|
||||
|
||||
# run texturing binary
|
||||
system.run('{bin}/odm_texturing -bundleFile {bundle} ' \
|
||||
'-imagesPath {imgs_path} -imagesListPath {imgs_list} ' \
|
||||
'-inputModelPath {model} -outputFolder {out_dir}/ ' \
|
||||
'-textureResolution {resolution} -bundleResizedTo {resize} ' \
|
||||
'-textureWithSize {size} -logFile {log}'.format(**kwargs))
|
||||
system.run('{bin}/odm_texturing -bundleFile {bundle} '
|
||||
'-imagesPath {imgs_path} -imagesListPath {imgs_list} '
|
||||
'-inputModelPath {model} -outputFolder {out_dir}/ '
|
||||
'-textureResolution {resolution} -bundleResizedTo {resize} '
|
||||
'-textureWithSize {size} -logFile {log}'.format(**kwargs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid ODM Texture file in: %s' \
|
||||
% tree.odm_textured_model_obj)
|
||||
|
||||
log.ODM_WARNING('Found a valid ODM Texture file in: %s'
|
||||
% tree.odm_textured_model_obj)
|
||||
|
||||
log.ODM_INFO('Running OMD Texturing Cell - Finished')
|
||||
return ecto.OK if args['end_with'] != 'odm_texturing' else ecto.QUIT
|
||||
return ecto.OK if args['end_with'] != 'odm_texturing' else ecto.QUIT
|
||||
|
|
|
@ -5,6 +5,7 @@ from opendm import io
|
|||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
|
||||
class ODMOpenSfMCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("use_exif_size", "The application arguments.", False)
|
||||
|
@ -44,8 +45,7 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
(args['rerun_from'] is not None and
|
||||
'opensfm' in args['rerun_from'])
|
||||
|
||||
|
||||
### check if reconstruction was done before
|
||||
# check if reconstruction was done before
|
||||
|
||||
if not io.file_exists(tree.opensfm_reconstruction) or rerun_cell:
|
||||
# create file list
|
||||
|
@ -63,7 +63,7 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
"matching_gps_neighbors: %s" % self.params.matching_gps_neighbors
|
||||
]
|
||||
|
||||
if args['matcher_distance']>0:
|
||||
if args['matcher_distance'] > 0:
|
||||
config.append("matching_gps_distance: %s" % self.params.matching_gps_distance)
|
||||
|
||||
# write config file
|
||||
|
@ -72,30 +72,28 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
fout.write("\n".join(config))
|
||||
|
||||
# run OpenSfM reconstruction
|
||||
system.run('PYTHONPATH=%s %s/bin/run_all %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
system.run('PYTHONPATH=%s %s/bin/run_all %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid OpenSfm file in: %s' %
|
||||
(tree.opensfm_reconstruction))
|
||||
log.ODM_WARNING('Found a valid OpenSfm file in: %s' %
|
||||
tree.opensfm_reconstruction)
|
||||
|
||||
|
||||
### check if reconstruction was exported to bundler before
|
||||
# check if reconstruction was exported to bundler before
|
||||
|
||||
if not io.file_exists(tree.opensfm_bundle_list) or rerun_cell:
|
||||
# convert back to bundler's format
|
||||
system.run('PYTHONPATH=%s %s/bin/export_bundler %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid Bundler file in: %s' %
|
||||
(tree.opensfm_reconstruction))
|
||||
log.ODM_WARNING('Found a valid Bundler file in: %s' %
|
||||
tree.opensfm_reconstruction)
|
||||
|
||||
|
||||
### check if reconstruction was exported to pmvs before
|
||||
# check if reconstruction was exported to pmvs before
|
||||
|
||||
if not io.file_exists(tree.pmvs_visdat) or rerun_cell:
|
||||
# run PMVS converter
|
||||
system.run('PYTHONPATH=%s %s/bin/export_pmvs %s --output %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm, tree.pmvs))
|
||||
system.run('PYTHONPATH=%s %s/bin/export_pmvs %s --output %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm, tree.pmvs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid CMVS file in: %s' % tree.pmvs_visdat)
|
||||
|
||||
|
|
|
@ -5,26 +5,26 @@ from opendm import log
|
|||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
class ODMPmvsCell(ecto.Cell):
|
||||
|
||||
class ODMPmvsCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("level", 'The level in the image pyramid that is used '
|
||||
'for the computation', 1)
|
||||
params.declare("csize", 'Cell size controls the density of reconstructions', 2)
|
||||
params.declare("thresh", 'A patch reconstruction is accepted as a success '
|
||||
'and kept, if its associcated photometric consistency '
|
||||
'measure is above this threshold.', 0.7)
|
||||
'and kept, if its associcated photometric consistency '
|
||||
'measure is above this threshold.', 0.7)
|
||||
params.declare("wsize", 'pmvs samples wsize x wsize pixel colors from '
|
||||
'each image to compute photometric consistency '
|
||||
'score. For example, when wsize=7, 7x7=49 pixel '
|
||||
'colors are sampled in each image. Increasing the '
|
||||
'value leads to more stable reconstructions, but '
|
||||
'the program becomes slower.', 7)
|
||||
'each image to compute photometric consistency '
|
||||
'score. For example, when wsize=7, 7x7=49 pixel '
|
||||
'colors are sampled in each image. Increasing the '
|
||||
'value leads to more stable reconstructions, but '
|
||||
'the program becomes slower.', 7)
|
||||
params.declare("min_imgs", 'Each 3D point must be visible in at least '
|
||||
'minImageNum images for being reconstructed. 3 is '
|
||||
'suggested in general.', 3)
|
||||
'minImageNum images for being reconstructed. 3 is '
|
||||
'suggested in general.', 3)
|
||||
params.declare("cores", 'The maximum number of cores to use in dense '
|
||||
' reconstruction.', context.num_cores)
|
||||
' reconstruction.', context.num_cores)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -33,7 +33,7 @@ class ODMPmvsCell(ecto.Cell):
|
|||
outputs.declare("reconstruction", "list of ODMReconstructions", [])
|
||||
|
||||
def process(self, inputs, outputs):
|
||||
|
||||
|
||||
log.ODM_INFO('Running OMD PMVS Cell')
|
||||
|
||||
# get inputs
|
||||
|
@ -62,12 +62,12 @@ class ODMPmvsCell(ecto.Cell):
|
|||
}
|
||||
|
||||
# generate pmvs2 options
|
||||
system.run('{bin} {prefix}/ {level} {csize} {thresh} {wsize} ' \
|
||||
system.run('{bin} {prefix}/ {level} {csize} {thresh} {wsize} '
|
||||
'{min_imgs} {cores}'.format(**kwargs))
|
||||
|
||||
# run pmvs2
|
||||
system.run('%s %s/ option-0000' % \
|
||||
(context.pmvs2_path, tree.pmvs_rec_path))
|
||||
system.run('%s %s/ option-0000' %
|
||||
(context.pmvs2_path, tree.pmvs_rec_path))
|
||||
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid PMVS file in %s' % tree.pmvs_model)
|
||||
|
|
|
@ -7,6 +7,7 @@ from opendm import system
|
|||
from opendm import io
|
||||
from opendm import types
|
||||
|
||||
|
||||
class ODMResizeCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("resize_to", "resizes images by the largest side", 2400)
|
||||
|
@ -29,7 +30,7 @@ class ODMResizeCell(ecto.Cell):
|
|||
if not photos:
|
||||
log.ODM_ERROR('Not enough photos in photos to resize')
|
||||
return ecto.QUIT
|
||||
|
||||
|
||||
# create working directory
|
||||
system.mkdir_p(tree.dataset_resize)
|
||||
|
||||
|
@ -78,17 +79,17 @@ class ODMResizeCell(ecto.Cell):
|
|||
photo.update_focal()
|
||||
|
||||
# log message
|
||||
log.ODM_DEBUG('Resized %s | dimensions: %s' % \
|
||||
(photo.filename, img_r.shape))
|
||||
log.ODM_DEBUG('Resized %s | dimensions: %s' %
|
||||
(photo.filename, img_r.shape))
|
||||
else:
|
||||
# log message
|
||||
log.ODM_WARNING('Already resized %s | dimensions: %s x %s' % \
|
||||
(photo.filename, photo.width, photo.height))
|
||||
log.ODM_WARNING('Already resized %s | dimensions: %s x %s' %
|
||||
(photo.filename, photo.width, photo.height))
|
||||
|
||||
log.ODM_INFO('Resized %s images' % len(photos))
|
||||
|
||||
|
||||
# append photos to cell output
|
||||
self.outputs.photos = photos
|
||||
|
||||
log.ODM_INFO('Running ODM Resize Cell - Finished')
|
||||
return ecto.OK if args['end_with'] != 'resize' else ecto.QUIT
|
||||
return ecto.OK if args['end_with'] != 'resize' else ecto.QUIT
|
||||
|
|
Ładowanie…
Reference in New Issue