added tree structure to organize file paths

pull/249/head
edgarriba 2015-12-10 11:01:41 +00:00
rodzic 23eb0acc7f
commit c65d2b51b3
12 zmienionych plików z 454 dodań i 241 usunięć

Wyświetl plik

@ -1,7 +1,7 @@
import argparse
# parse arguments
processopts = ['resize', 'opensfm', 'pmvs',
processopts = ['resize', 'opensfm', 'cmvs', 'pmvs',
'odm_meshing', 'odm_texturing', 'odm_georeferencing',
'odm_orthophoto']
@ -28,7 +28,7 @@ parser.add_argument('--end-with', '-e',
choices=processopts,
help=('Can be one of:' + ' | '.join(processopts)))
parser.add_argument('--run-only',
parser.add_argument('--rerun', '-r'
metavar='<string>',
choices=processopts,
help=('Can be one of:' + ' | '.join(processopts)))
@ -143,6 +143,13 @@ parser.add_argument('--pmvs-minImageNum',
'minImageNum images for being reconstructed. 3 is '
'suggested in general.'))
parser.add_argument('--pmvs-num-cores',
metavar='<positive integer>',
default=1,
type=int,
help=('The maximum number of cores to use in dense '
'reconstruction.'))
parser.add_argument('--odm_meshing-maxVertexCount',
metavar='<positive integer>',
default=100000,
@ -211,10 +218,4 @@ parser.add_argument('--zip-results',
default=False,
help='compress the results using gunzip')
parser.add_argument('--use-opensfm',
type=bool,
default=True,
help='use OpenSfM instead of Bundler to find the camera positions '
'(replaces getKeypoints, match and bundler steps)')
args = vars(parser.parse_args())

Wyświetl plik

@ -51,7 +51,7 @@ class ODMPhoto:
metadata.read()
# loop over image tags
for key in metadata:
# try/catch tag value due to weird bug in pyexiv2
# try/catch tag value due to weird bug in pyexiv2
# ValueError: invalid literal for int() with base 10: ''
try:
val = metadata[key].value
@ -84,7 +84,7 @@ class ODMPhoto:
# TODO: finish this class
class ODMReconstruction(object):
class ODMTree(object):
def __init__(self, root_path):
### root path to the project
self.root_path = io.absolute_path_file(root_path)
@ -95,7 +95,7 @@ class ODMReconstruction(object):
# order to keep track all files al directories during the
# whole reconstruction process.
self.dataset_raw = io.join_paths(self.root_path, 'images')
self.dataset_resized = io.join_paths(self.root_path, 'images_resized')
self.dataset_resize = io.join_paths(self.root_path, 'images_resize')
self.opensfm = io.join_paths(self.root_path, 'opensfm')
self.pmvs = io.join_paths(self.root_path, 'pmvs')
self.odm_meshing = io.join_paths(self.root_path, 'odm_meshing')
@ -110,19 +110,48 @@ class ODMReconstruction(object):
self.opensfm_bundle_list = io.join_paths(self.opensfm, 'list_r000.out')
self.opensfm_image_list = io.join_paths(self.opensfm, 'image_list.txt')
self.opensfm_reconstruction = io.join_paths(self.opensfm, 'reconstruction.json')
# pmvs
self.pmvs_options = io.join_paths(self.pmvs, 'recon0/pmvs_options.txt')
self.pmvs_model = io.join_paths(self.pmvs, 'models/pmvs_options.txt.ply')
self.pmvs_rec_path = io.join_paths(self.pmvs, 'recon0')
self.pmvs_bundle = io.join_paths(self.pmvs_rec_path, 'bundle.rd.out')
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.mesh = io.join_paths(self.odm_meshing, 'odm_mesh.ply')
# odm_texturing && odm_georeferencing
self.textured_model = io.join_paths(self.odm_texturing, 'odm_textured_model_geo.ply')
self.textured_model_obj = io.join_paths(self.odm_texturing, 'odm_textured_model.obj')
self.textured_model_mtl = io.join_paths(self.odm_texturing, 'odm_textured_model.mtl')
self.textured_model_obj_geo = io.join_paths(self.odm_texturing, 'odm_textured_model_geo.obj')
self.textured_model_mtl_geo = io.join_paths(self.odm_texturing, 'odm_textured_model_geo.mtl')
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')
self.odm_textured_model_mtl = io.join_paths(
self.odm_texturing, 'odm_textured_model.mtl')
self.odm_textured_model_txt_geo = io.join_paths(
self.odm_texturing, 'odm_textured_model_geo.txt')
self.odm_textured_model_ply_geo = io.join_paths(
self.odm_texturing, 'odm_textured_model_geo.ply')
self.odm_textured_model_obj_geo = io.join_paths(
self.odm_texturing, 'odm_textured_model_geo.obj')
self.odm_textured_model_mtl_geo = io.join_paths(
self.odm_texturing, 'odm_textured_model_geo.mtl')
self.odm_texuring_log = io.join_paths(
self.odm_texturing, 'odm_texturing_log.txt')
# odm_georeferencing
self.odm_georeferencing_coords = io.join_paths(
self.odm_georeferencing, 'coords.txt')
self.odm_georeferencing_gcp = io.join_paths(
self.odm_georeferencing, 'gcp_list.txt')
self.odm_georeferencing_utm_log = io.join_paths(
self.odm_georeferencing, 'odm_georeferencing_utm_log.txt')
self.odm_georeferencing_log = io.join_paths(
self.odm_georeferencing, 'odm_georeferencing_log.txt')
# odm_orthophoto
self.orthophoto = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.png')
self.odm_orthophoto_file = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.png')
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')

56
scripts/cmvs.py 100644
Wyświetl plik

@ -0,0 +1,56 @@
import ecto
from opendm import io
from opendm import log
from opendm import system
from opendm import context
class ODMCmvsCell(ecto.Cell):
def declare_params(self, params):
params.declare("max_images", 'The maximum number of images '
'per cluster', 500)
params.declare("cores", 'The maximum number of cores to use '
'in dense reconstruction.', context.num_cores)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "Struct with paths", [])
inputs.declare("reconstruction", "list of ODMReconstructions", [])
outputs.declare("reconstruction", "list of ODMReconstructions", [])
def process(self, inputs, outputs):
log.ODM_INFO('Running OMD CMVS Cell')
# get inputs
args = self.inputs.args
tree = self.inputs.tree
# check if we rerun cell or not
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'cmvs'
if not io.file_exists(tree.pmvs_bundle) or rerun_cell:
log.ODM_DEBUG('Writting CMVS vis in: %s' % tree.pmvs_bundle)
# copy bundle file to pmvs dir
from shutil import copyfile
copyfile(tree.opensfm_bundle,
tree.pmvs_bundle)
kwargs = {
'bin': context.cmvs_path,
'prefix': self.inputs.tree.pmvs_rec_path,
'max_images': self.params.max_images,
'cores': self.params.cores
}
# run cmvs
system.run('{bin} {prefix}/ {max_images} {cores}'.format(**kwargs))
else:
log.ODM_WARNING('Found a valid CMVS file in: %s' %
(tree.pmvs_bundle))
log.ODM_INFO('Running OMD CMVS Cell - Finished')
return ecto.OK if args['end_with'] != 'cmvs' else ecto.QUIT

Wyświetl plik

@ -12,8 +12,9 @@ class ODMLoadDatasetCell(ecto.Cell):
pass
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
outputs.declare("photos", "Clusters output. list of ODMPhoto's", [])
outputs.declare("photos", "list of ODMPhoto's", [])
def process(self, inputs, outputs):
# check if the extension is sopported
@ -23,17 +24,19 @@ class ODMLoadDatasetCell(ecto.Cell):
log.ODM_INFO('Running ODM Load Dataset Cell')
# get parameters
# get inputs
args = self.inputs.args
project_path = io.absolute_path_file(args['project_path'])
images_dir = io.join_paths(project_path, 'images_resize')
tree = self.inputs.tree
# set images directory
images_dir = tree.dataset_resize
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'resize'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'resize'
if not io.dir_exists(images_dir) or rerun_cell:
images_dir = io.join_paths(project_path, 'images')
images_dir = tree.dataset_raw
if not io.dir_exists(images_dir):
log.ODM_ERROR("You must put your pictures into an <images> directory")
return ecto.QUIT
@ -44,7 +47,6 @@ class ODMLoadDatasetCell(ecto.Cell):
files = io.get_files_list(images_dir)
# filter images for its extension type
# by now only 'jpg' and 'jpeg are supported
files = [f for f in files if supported_extension(f)]
if files:

Wyświetl plik

@ -1,11 +1,14 @@
import ecto
from opendm import context
from opendm import types
from opendm import config
from dataset import ODMLoadDatasetCell
from resize import ODMResizeCell
from opensfm import ODMOpenSfMCell
from pmvs import ODMPmvsCell
from cmvs import ODMCmvsCell
from odm_meshing import ODMeshingCell
from odm_texturing import ODMTexturingCell
from odm_georeferencing import ODMGeoreferencingCell
@ -23,67 +26,127 @@ class ODMApp(ecto.BlackBox):
@staticmethod
def declare_cells(p):
print p.args
"""
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),
'load_dataset': ODMLoadDatasetCell(),
'dataset': ODMLoadDatasetCell(),
'resize': ODMResizeCell(),
'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_k']),
'pmvs': ODMPmvsCell(),
'meshing': ODMeshingCell(),
'texturing': ODMTexturingCell(),
'georeferencing': ODMGeoreferencingCell(),
'orthophoto': ODMOrthoPhotoCell()
'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.ODMTree(p.args['project_path'])
self.tree = ecto.Constant(value=tree)
def connections(self, _p):
# define initial and final tasks
# TODO: not sure how to manage that
# define initial task
initial_task = _p.args['start_with']
final_task = _p.args['end_with']
run_only = _p.args['run_only']
initial_task_id = config.processopts.index(initial_task)
# define the connections like you would for the plasm
print _p.args['rerun']
## define the connections like you would for the plasm
connections = []
# load the dataset
connections = [ self.args[:] >> self.load_dataset['args'] ]
## load the dataset
connections = [ self.tree[:] >> self.dataset['tree'],
self.args[:] >> self.dataset['args'] ]
# resize images
connections += [ self.args[:] >> self.resize['args'],
self.load_dataset['photos'] >> self.resize['photos'] ]
## check we want to start with resize task
if initial_task_id <= config.processopts.index('resize'):
# run opensfm
connections += [ self.args[:] >> self.opensfm['args'],
self.load_dataset['photos'] >> self.opensfm['photos'] ]
# resize images
connections += [ self.tree[:] >> self.resize['tree'],
self.args[:] >> self.resize['args'],
self.dataset['photos'] >> self.resize['photos'] ]
# forward opensfm variables
connections += [ self.tree[:] >> self.opensfm['tree'],
self.args[:] >> self.opensfm['args'] ]
# run cmvs
connections += [ self.args[:] >> self.pmvs['args'],
self.opensfm['reconstruction_path'] >>
self.pmvs['reconstruction_path'] ]
## check we want to start with opensfm task
if initial_task_id <= config.processopts.index('opensfm'):
# run opensfm with images from load dataset
connections += [ self.dataset['photos'] >> self.opensfm['photos'] ]
else:
# run opensfm with images from resize
connections += [ self.resize['photos'] >> self.opensfm['photos'] ]
# create odm mesh
connections += [ self.args[:] >> self.meshing['args'],
self.pmvs['model_path'] >> self.meshing['model_path'] ]
## check we want to start with cmvs task
if initial_task_id <= config.processopts.index('cmvs'):
# create odm texture
connections += [ self.args[:] >> self.texturing['args'],
self.meshing['mesh_path'] >> self.texturing['model_path'] ]
# run cmvs
connections += [ self.tree[:] >> self.cmvs['tree'],
self.args[:] >> self.cmvs['args'],
self.opensfm['reconstruction'] >> self.cmvs['reconstruction'] ]
## check we want to start with cmvs task
if initial_task_id <= config.processopts.index('pmvs'):
# create odm georeference
connections += [ self.args[:] >> self.georeferencing['args'] ]
# run pmvs
connections += [ self.tree[:] >> self.pmvs['tree'],
self.args[:] >> self.pmvs['args'],
self.cmvs['reconstruction'] >> self.pmvs['reconstruction'] ]
# create odm orthophoto
connections += [ self.args[:] >> self.orthophoto['args'] ]
## check we want to start with odm_meshing task
if initial_task_id <= config.processopts.index('odm_meshing'):
# create odm mesh
connections += [ self.tree[:] >> self.meshing['tree'],
self.args[:] >> self.meshing['args'],
self.pmvs['reconstruction'] >> self.meshing['reconstruction'] ]
## check we want to start with odm_texturing task
if initial_task_id <= config.processopts.index('odm_texturing'):
# create odm texture
connections += [ self.tree[:] >> self.texturing['tree'],
self.args[:] >> self.texturing['args'],
self.meshing['reconstruction'] >> self.texturing['reconstruction'] ]
## check we want to start with odm_georeferencing task
if initial_task_id <= config.processopts.index('odm_georeferencing'):
# 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'] ]
## check we want to start with odm_orthophoto task
if initial_task_id <= config.processopts.index('odm_orthophoto'):
## create odm orthophoto
connections += [ self.tree[:] >> self.orthophoto['tree'],
self.args[:] >> self.orthophoto['args'],
self.georeferencing['reconstruction'] >> self.orthophoto['reconstruction'] ]
return connections

Wyświetl plik

@ -6,10 +6,20 @@ from opendm import system
from opendm import context
class ODMGeoreferencingCell(ecto.Cell):
def declare_params(self, params):
params.declare("gcp_file", 'path to the file containing the ground control '
'points used for georeferencing.The file needs to '
'be on the following line format: \neasting '
'northing height pixelrow pixelcol imagename', 'gcp_list.txt')
params.declare("use_gcp", 'set to true for enabling GCPs from the file above', False)
params.declare("img_size", 'image size used in calibration', 2400)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
inputs.declare("texture_path", "The application arguments.", {})
inputs.declare("photos", "list of ODMPhoto's", [])
inputs.declare("reconstruction", "list of ODMReconstructions", [])
outputs.declare("reconstruction", "list of ODMReconstructions", [])
def process(self, inputs, outputs):
@ -17,80 +27,82 @@ class ODMGeoreferencingCell(ecto.Cell):
# get inputs
args = self.inputs.args
project_path = io.absolute_path_file(args['project_path'])
tree = self.inputs.tree
# define paths and create working directories
odm_texturing = io.join_paths(project_path, 'odm_texturing')
odm_georeferencing = io.join_paths(project_path, 'odm_georeferencing')
system.mkdir_p(odm_georeferencing)
images_path = io.join_paths(project_path, 'images_resize')
images_list_file = io.join_paths(project_path, 'opensfm/list_r000.out')
# define gcp file path, we'll assume that's placed in the project root
gcp_file = io.join_paths(project_path, args['odm_georeferencing_gcpFile'])
coords_file = io.join_paths(odm_georeferencing, 'coords.txt')
system.mkdir_p(tree.odm_georeferencing)
# in case a gcp file it's not provided, let's try to generate it using
# images metadata. Internally calls jhead.
if not args['odm_georeferencing_useGcp'] and not io.file_exists(coords_file):
if not self.params.use_gcp and \
not io.file_exists(tree.odm_georeferencing_coords):
log.ODM_WARNING('Warning: No coordinates file. ' \
'Generating coordinates file in: %s' % coords_file)
'Generating coordinates file in: %s' % tree.odm_georeferencing_coords)
try:
log_file = io.join_paths(odm_georeferencing, 'odm_texturing_utm_log.txt')
# odm_georeference definitions
kwargs = {
'bin': context.odm_modules_path,
'imgs': tree.dataset_resize,
'imgs_list': tree.opensfm_bundle_list,
'coords': tree.odm_georeferencing_coords,
'log': tree.odm_georeferencing_utm_log
}
# run UTM extraction binary
system.run('{bin}/odm_extract_utm -imagesPath {imgs}/ ' \
'-imageListFile {imgs_list} -outputCoordFile {coords} ' \
'-logFile {log}'.format(**kwargs))
system.run('%s/odm_extract_utm -imagesPath %s/ ' \
'-imageListFile %s -outputCoordFile %s ' \
'-logFile %s' % \
(context.odm_modules_path, images_path, \
images_list_file, coords_file, log_file))
except Exception, e:
log.ODM_ERROR('Could not generate GCP file from images metadata.' \
'Consider rerunning with argument --odm_georeferencing-useGcp'\
' and provide a proper GCP file')
log.ODM_ERROR(str(e))
log.ODM_ERROR('Could not generate GCP file from images metadata.' \
'Consider rerunning with argument --odm_georeferencing-useGcp' \
' and provide a proper GCP file')
log.ODM_ERROR(e)
return ecto.QUIT
elif io.file_exists(coords_file):
log.ODM_WARNING('Found a valid coordinates file in: %s' % coords_file)
# define odm georeferencing outputs
# for convenience we'll put all data into odm_texturing
model_geo = io.join_paths(odm_texturing, 'odm_textured_model_geo.obj')
pointcloud_geo = io.join_paths(odm_texturing, 'odm_textured_model_geo.ply')
system_geo = io.join_paths(odm_texturing, 'odm_textured_model_geo.txt')
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'odm_georeferencing'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'odm_georeferencing'
if not io.file_exists(model_geo) or \
not io.file_exists(pointcloud_geo) or rerun_cell:
if not io.file_exists(tree.odm_textured_model_obj_geo) or \
not io.file_exists(tree.odm_textured_model_ply_geo) or rerun_cell:
# odm_georeference definitions
kwargs = {
'bin': context.odm_modules_path,
'bundle': io.join_paths(project_path,'opensfm/bundle_r000.out'),
'gcp': io.join_paths(project_path, args['odm_georeferencing_gcpFile']),
'imgs': io.join_paths(project_path, 'images_resize'),
'imgs_list': io.join_paths(project_path, 'opensfm/list_r000.out'),
'size': str(args['resize_to']),
'model': io.join_paths(project_path, 'odm_texturing/odm_textured_model.obj'),
'pc': io.join_paths(project_path, 'pmvs/recon0/models/option-0000.ply'),
'log': io.join_paths(odm_georeferencing, 'odm_texturing_log.txt'),
'coords': io.join_paths(odm_georeferencing, 'coords.txt'),
'pc_geo': pointcloud_geo,
'geo_sys': system_geo,
'model_geo': model_geo,
'bundle': tree.opensfm_bundle,
'imgs': tree.dataset_resize,
'imgs_list': tree.opensfm_bundle_list,
'model': tree.odm_textured_model_obj,
'pc': tree.pmvs_model,
'log': tree.odm_georeferencing_log,
'coords': tree.odm_georeferencing_coords,
'pc_geo': tree.odm_textured_model_ply_geo,
'geo_sys': tree.odm_textured_model_txt_geo,
'model_geo': tree.odm_textured_model_obj_geo,
'size': self.params.img_size,
'gcp': io.join_paths(tree.root_path, self.params.gcp_file),
}
# run odm_georeference
system.run('{bin}/odm_georef -bundleFile {bundle} -inputCoordFile {coords} ' \
'-bundleResizedTo {size} -inputFile {model} -outputFile {model_geo} ' \
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} ' \
'-logFile {log} -georefFileOutputPath {geo_sys}'.format(**kwargs))
if self.params.use_gcp and \
io.file_exists(tree.odm_georeferencing_coords):
system.run('{bin}/odm_georef -bundleFile {bundle} -inputCoordFile {coords} ' \
'-bundleResizedTo {size} -inputFile {model} -outputFile {model_geo} ' \
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} ' \
'-logFile {log} -georefFileOutputPath {geo_sys} -gcpFile {gcp} ' \
'-outputCoordFile {coords}'.format(**kwargs))
else:
system.run('{bin}/odm_georef -bundleFile {bundle} -inputCoordFile {coords} ' \
'-inputFile {model} -outputFile {model_geo} ' \
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} ' \
'-logFile {log} -georefFileOutputPath {geo_sys}'.format(**kwargs))
else:
log.ODM_WARNING('Found a valid georeferenced model in: %s' % pointcloud_geo)
log.ODM_WARNING('Found a valid georeferenced model in: %s' \
% tree.odm_textured_model_ply_geo)
log.ODM_INFO('Running OMD Georeferencing Cell - Finished')

Wyświetl plik

@ -5,12 +5,26 @@ 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)
params.declare("oct_tree", 'Oct-tree depth used in the mesh reconstruction, '
'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)
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)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
inputs.declare("model_path", "Clusters output. list of reconstructions", [])
outputs.declare("mesh_path", "Clusters output. list of reconstructions", [])
inputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
outputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
def process(self, inputs, outputs):
@ -18,36 +32,37 @@ class ODMeshingCell(ecto.Cell):
# get inputs
args = self.inputs.args
model_path = self.inputs.model_path
project_path = io.absolute_path_file(args['project_path'])
tree = self.inputs.tree
# define paths and create working directories
odm_meshing = io.join_paths(project_path, 'odm_meshing')
system.mkdir_p(odm_meshing)
output_file = io.join_paths(odm_meshing, 'odm_mesh.ply')
log_file = io.join_paths(odm_meshing, 'odm_meshing_log.txt')
self.outputs.mesh_path = output_file
system.mkdir_p(tree.odm_meshing)
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'odm_meshing'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'odm_meshing'
if not io.file_exists(output_file) or rerun_cell:
log.ODM_DEBUG('Writting odm mesh file in: %s' % output_file)
if not io.file_exists(tree.odm_mesh) or rerun_cell:
log.ODM_DEBUG('Writting ODM Mesh file in: %s' % tree.odm_mesh)
kwargs = {
'bin': context.odm_modules_path,
'infile': tree.pmvs_model,
'outfile': tree.odm_mesh,
'log': tree.odm_meshing_log,
'max_vertex': self.params.max_vertex,
'oct_tree': self.params.oct_tree,
'samples': self.params.samples,
'solver':self.params.solver
}
# run meshing binary
system.run('%s/odm_meshing -inputFile %s -outputFile %s ' \
'-logFile %s -maxVertexCount %s -octreeDepth %s ' \
'-samplesPerNode %s -solverDivide %s' % \
(context.odm_modules_path, model_path, output_file, log_file, \
str(args['odm_meshing_maxVertexCount']), \
str(args['odm_meshing_octreeDepth']), \
str(args['odm_meshing_samplesPerNode']), \
str(args['odm_meshing_solverDivide'])))
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' %
(output_file))
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

Wyświetl plik

@ -6,9 +6,13 @@ from opendm import system
from opendm import context
class ODMOrthoPhotoCell(ecto.Cell):
def declare_params(self, params):
params.declare("resolution", 'Orthophoto ground resolution in pixels/meter', 20)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
inputs.declare("reconstruction", "list of ODMReconstructions", [])
def process(self, inputs, outputs):
@ -16,33 +20,32 @@ class ODMOrthoPhotoCell(ecto.Cell):
# get inputs
args = self.inputs.args
project_path = io.absolute_path_file(args['project_path'])
tree = self.inputs.tree
# define paths and create working directories
odm_texturing = io.join_paths(project_path, 'odm_texturing')
odm_orthophoto = io.join_paths(project_path, 'odm_orthophoto')
system.mkdir_p(odm_orthophoto)
# odm_georeference definitions
kwargs = {
'bin': context.odm_modules_path,
'model_geo': io.join_paths(odm_texturing, 'odm_textured_model_geo.obj'),
'log': io.join_paths(odm_orthophoto, 'odm_orthophoto_log.txt'),
'ortho': io.join_paths(odm_orthophoto, 'odm_orthphoto.png'),
'corners': io.join_paths(odm_orthophoto, 'odm_orthphoto_corners.txt'),
'res': str(20.0)
}
system.mkdir_p(tree.odm_orthophoto)
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'odm_orthophoto'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'odm_orthophoto'
if not io.file_exists(tree.odm_orthophoto_file) or rerun_cell:
# odm_georeference definitions
kwargs = {
'bin': context.odm_modules_path,
'model_geo': tree.odm_textured_model_obj_geo,
'log': tree.odm_orthophoto_log,
'ortho': tree.odm_orthophoto_file,
'corners': tree.odm_orthophoto_corners,
'res': self.params.resolution
}
if not io.file_exists(kwargs['ortho']) or rerun_cell:
# run odm_georeference
system.run('{bin}/odm_orthophoto -inputFile {model_geo} -logFile {log} ' \
'-outputFile {ortho} -resolution {res} -outputCornerFile {corners}'.format(**kwargs))
else:
log.ODM_WARNING('Found a valid orthophoto in: %s' % kwargs['ortho'])
log.ODM_WARNING('Found a valid orthophoto in: %s' % tree.odm_orthophoto_file)
log.ODM_INFO('Running OMD OrthoPhoto Cell - Finished')
return ecto.OK if args['end_with'] != 'odm_orthophoto' else ecto.QUIT

Wyświetl plik

@ -7,10 +7,18 @@ from opendm import context
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)
params.declare("size", 'The resolution to rescale the images performing '
'the texturing.', 3600)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
inputs.declare("model_path", "Clusters output. list of reconstructions", [])
outputs.declare("texture_path", "Clusters output. list of reconstructions", [])
inputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
outputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
def process(self, inputs, outputs):
@ -18,47 +26,42 @@ class ODMTexturingCell(ecto.Cell):
# get inputs
args = self.inputs.args
input_model_path = self.inputs.model_path
project_path = io.absolute_path_file(args['project_path'])
tree = self.inputs.tree
# define paths and create working directories
odm_meshing = io.join_paths(project_path, 'odm_meshing')
odm_texturing = io.join_paths(project_path, 'odm_texturing')
system.mkdir_p(odm_texturing)
output_file = io.join_paths(odm_texturing, 'odm_textured_model.obj')
system.mkdir_p(tree.odm_texturing)
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'odm_texturing'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'odm_texturing'
if not io.file_exists(output_file) or rerun_cell:
log.ODM_DEBUG('Writting odm textured file in: %s' % output_file)
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)
# odm_texturing definitions
kwargs = {
'bin': context.odm_modules_path,
'out_dir': odm_texturing,
'bundle': io.join_paths(project_path,'opensfm/bundle_r000.out'),
'imgs_path': io.join_paths(project_path, 'images_resize'),
'imgs_list': io.join_paths(project_path, 'opensfm/list_r000.out'),
'model': io.join_paths(odm_meshing, 'odm_mesh.ply'),
'log': io.join_paths(odm_texturing, 'odm_texturing_log.txt'),
'bsize': str(args['resize_to']),
'res': str(args['odm_texturing_textureResolution']),
'wsize': str(args['odm_texturing_textureWithSize'])
'out_dir': tree.odm_texturing,
'bundle': tree.opensfm_bundle,
'imgs_path': tree.dataset_resize,
'imgs_list': tree.opensfm_bundle_list,
'model': tree.odm_mesh,
'log': tree.odm_texuring_log,
'resize': self.params.resize,
'resolution': self.params.resolution,
'size': self.params.size
}
# run texturing binary
system.run('{bin}/odm_texturing -bundleFile {bundle} ' \
'-imagesPath {imgs_path} -imagesListPath {imgs_list} ' \
'-inputModelPath {model} -outputFolder {out_dir}/ ' \
'-textureResolution {res} -bundleResizedTo {bsize} ' \
'-textureWithSize {wsize} -logFile {log}'.format(**kwargs))
'-textureResolution {resolution} -bundleResizedTo {resize} ' \
'-textureWithSize {size} -logFile {log}'.format(**kwargs))
else:
log.ODM_WARNING('Found a valid odm texture file in: %s' % output_file)
self.outputs.texture_path = output_file
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

Wyświetl plik

@ -6,6 +6,8 @@ from opendm import system
from opendm import context
class ODMOpenSfMCell(ecto.Cell):
def declare_params(self, params):
pass
def declare_params(self, params):
params.declare("use_exif_size", "The application arguments.", False)
@ -15,40 +17,38 @@ class ODMOpenSfMCell(ecto.Cell):
params.declare("matching_gps_neighbors", "The application arguments.", 0)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
inputs.declare("photos", "Clusters output. list of ODMPhoto's", [])
outputs.declare("reconstructions", "Clusters output. list of reconstructions", [])
outputs.declare("reconstruction_path", "The directory to the images to load.", "")
inputs.declare("photos", "list of ODMPhoto's", [])
outputs.declare("reconstruction", "list of ODMReconstructions", [])
def process(self, inputs, outputs):
log.ODM_INFO('Running OMD OpenSfm Cell')
# get inputs
tree = self.inputs.tree
args = self.inputs.args
photos = self.inputs.photos
project_path = io.absolute_path_file(args['project_path'])
if not photos:
log.ODM_ERROR('Not enough photos in photos array to start OpenSfm')
return ecto.QUIT
# create working directories
opensfm_path = io.join_paths(project_path, 'opensfm')
pmvs_path = io.join_paths(project_path, 'pmvs')
system.mkdir_p(opensfm_path)
system.mkdir_p(pmvs_path)
# create working directories
system.mkdir_p(tree.opensfm)
system.mkdir_p(tree.pmvs)
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'opensfm'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'opensfm'
### check if reconstruction was done before
reconstruction_file = io.join_paths(opensfm_path, 'reconstruction.json')
if not io.file_exists(reconstruction_file) or rerun_cell:
if not io.file_exists(tree.opensfm_reconstruction) or rerun_cell:
# create file list
list_path = io.join_paths(opensfm_path, 'image_list.txt')
list_path = io.join_paths(tree.opensfm, 'image_list.txt')
with open(list_path, 'w') as fout:
for photo in photos:
fout.write('%s\n' % photo.path_file)
@ -63,40 +63,37 @@ class ODMOpenSfMCell(ecto.Cell):
]
# write config file
config_filename = io.join_paths(opensfm_path, 'config.yaml')
config_filename = io.join_paths(tree.opensfm, 'config.yaml')
with open(config_filename, 'w') as fout:
fout.write("\n".join(config))
# run OpenSfM reconstruction
system.run('PYTHONPATH=%s %s/bin/run_all %s' %
(context.pyopencv_path, context.opensfm_path, opensfm_path))
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
else:
log.ODM_WARNING('Found a valid reconstruction file in: %s' %
(reconstruction_file))
log.ODM_WARNING('Found a valid OpenSfm file in: %s' %
(tree.opensfm_reconstruction))
### check if reconstruction was exported to bundler before
bundler_file = io.join_paths(opensfm_path, 'bundle_r000.out')
if not io.file_exists(bundler_file) or rerun_cell:
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, opensfm_path))
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
else:
log.ODM_WARNING('Found a valid bundle file in: %s' %
(reconstruction_file))
log.ODM_WARNING('Found a valid Bundler file in: %s' %
(tree.opensfm_reconstruction))
### check if reconstruction was exported to pmvs before
pmvs_file = io.join_paths(pmvs_path, 'recon0/pmvs_options.txt')
if not io.file_exists(pmvs_file) or rerun_cell:
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, opensfm_path, pmvs_path))
(context.pyopencv_path, context.opensfm_path, tree.opensfm, tree.pmvs))
else:
log.ODM_WARNING('Found a valid PMVS file in: %s' % pmvs_file)
# append biggest reconstruction path to output
self.outputs.reconstruction_path = io.join_paths(pmvs_path, 'recon0')
log.ODM_WARNING('Found a valid CMVS file in: %s' % tree.pmvs_visdat)
log.ODM_INFO('Running OMD OpenSfm Cell - Finished')
return ecto.OK if args['end_with'] != 'opensfm' else ecto.QUIT

Wyświetl plik

@ -5,12 +5,32 @@ 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)
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)
params.declare("min_imgs", 'Each 3D point must be visible in at least '
'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)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
inputs.declare("reconstruction_path", "Clusters output. list of reconstructions", [])
outputs.declare("model_path", "Clusters output. list of reconstructions", [])
inputs.declare("reconstruction", "list of ODMReconstructions", [])
outputs.declare("reconstruction", "list of ODMReconstructions", [])
def process(self, inputs, outputs):
@ -18,24 +38,36 @@ class ODMPmvsCell(ecto.Cell):
# get inputs
args = self.inputs.args
rec_path = self.inputs.reconstruction_path
# the path to create the model
model_path = io.join_paths(rec_path, 'models/pmvs_options.txt.ply')
# attach created model to output
self.outputs.model_path = model_path
tree = self.inputs.tree
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'pmvs'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'pmvs'
if not io.file_exists(model_path) or rerun_cell:
log.ODM_DEBUG('Creating dense pointcloud in: %s' % model_path)
if not io.file_exists(tree.pmvs_model) or rerun_cell:
log.ODM_DEBUG('Creating dense pointcloud in: %s' % tree.pmvs_model)
kwargs = {
'bin': context.cmvs_opts_path,
'prefix': tree.pmvs_rec_path,
'level': self.params.level,
'csize': self.params.csize,
'thresh': self.params.thresh,
'wsize': self.params.wsize,
'min_imgs': self.params.min_imgs,
'cores': self.params.cores
}
# generate pmvs2 options
system.run('{bin} {prefix}/ {level} {csize} {thresh} {wsize} ' \
'{min_imgs} {cores}'.format(**kwargs))
# run pmvs2
system.run('%s %s/ pmvs_options.txt ' % (context.pmvs2_path, 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' % model_path)
log.ODM_WARNING('Found a valid PMVS file in %s' % tree.pmvs_model)
log.ODM_INFO('Running OMD PMVS Cell - Finished')
return ecto.OK if args['end_with'] != 'pmvs' else ecto.QUIT

Wyświetl plik

@ -12,6 +12,7 @@ class ODMResizeCell(ecto.Cell):
pass
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", [])
inputs.declare("photos", "Clusters inputs. list of ODMPhoto's", [])
outputs.declare("photos", "Clusters output. list of ODMPhoto's", [])
@ -22,29 +23,28 @@ class ODMResizeCell(ecto.Cell):
# get inputs
args = self.inputs.args
tree = self.inputs.tree
photos = self.inputs.photos
project_path = io.absolute_path_file(args['project_path'])
if not photos:
log.ODM_ERROR('Not enough photos in photos to resize')
return ecto.QUIT
# create working directory
resizing_dir = io.join_paths(project_path, 'images_resize')
system.mkdir_p(resizing_dir)
system.mkdir_p(tree.dataset_resize)
log.ODM_DEBUG('Resizing dataset to: %s' % resizing_dir)
log.ODM_DEBUG('Resizing dataset to: %s' % tree.dataset_resize)
# check if we rerun cell or not
rerun_cell = args['run_only'] is not None \
and args['run_only'] == 'resize'
rerun_cell = args['rerun'] is not None \
and args['rerun'] == 'resize'
# loop over photos
for photo in photos:
# TODO(edgar): check if resize is needed, else copy img.
# Try to avoid oversampling!
new_path_file = io.join_paths(resizing_dir, photo.filename)
new_path_file = io.join_paths(tree.dataset_resize, photo.filename)
if not io.file_exists(new_path_file) or rerun_cell:
# open and resize image with opencv