kopia lustrzana https://github.com/OpenDroneMap/ODM
rodzic
6c64d1172c
commit
80cbc2f03b
|
@ -6,12 +6,9 @@ SuperBuild/install
|
|||
SuperBuild/src
|
||||
build
|
||||
opensfm
|
||||
pmvs
|
||||
odm_orthophoto
|
||||
odm_texturing
|
||||
odm_meshing
|
||||
odm_georeferencing
|
||||
images_resize
|
||||
.git
|
||||
|
||||
|
||||
|
|
12
README.md
12
README.md
|
@ -105,7 +105,7 @@ or
|
|||
|
||||
python run.py --rerun-from odm_meshing project-name
|
||||
|
||||
The options for rerunning are: 'resize', 'opensfm', 'slam', 'cmvs', 'pmvs', 'odm_meshing', 'mvs_texturing', 'odm_georeferencing', 'odm_orthophoto'
|
||||
The options for rerunning are: 'resize', 'opensfm', 'slam', 'smvs', 'odm_meshing', 'mvs_texturing', 'odm_georeferencing', 'odm_orthophoto'
|
||||
|
||||
### View Results
|
||||
|
||||
|
@ -151,7 +151,7 @@ You can also view the orthophoto GeoTIFF in [QGIS](http://www.qgis.org/) or othe
|
|||
|
||||
## Build and Run Using Docker
|
||||
|
||||
(Instructions below apply to Ubuntu 14.04, but the Docker image workflow
|
||||
(Instructions below apply to Ubuntu 14.04, but the Docker image workflow
|
||||
has equivalent procedures for Mac OS X and Windows. See [docs.docker.com](https://docs.docker.com/))
|
||||
|
||||
OpenDroneMap is Dockerized, meaning you can use containerization to build and run it without tampering with the configuration of libraries and packages already
|
||||
|
@ -177,7 +177,7 @@ If you want to build your own Docker image from sources, type:
|
|||
Using this method, the containerized ODM will process the images in the OpenDroneMap/images directory and output results
|
||||
to the OpenDroneMap/odm_orthophoto and OpenDroneMap/odm_texturing directories as described in the [Viewing Results](https://github.com/OpenDroneMap/OpenDroneMap/wiki/Output-and-Results) section.
|
||||
If you want to view other results outside the Docker image simply add which directories you're interested in to the run command in the same pattern
|
||||
established above. For example, if you're interested in the dense cloud results generated by PMVS and in the orthophoto,
|
||||
established above. For example, if you're interested in the dense cloud results generated by OpenSfM and in the orthophoto,
|
||||
simply use the following `docker run` command after building the image:
|
||||
|
||||
docker run -it --rm \
|
||||
|
@ -195,7 +195,7 @@ If you want to get all intermediate outputs, run the following command:
|
|||
-v "$(pwd)/odm_orthophoto:/code/odm_orthophoto" \
|
||||
-v "$(pwd)/odm_texturing:/code/odm_texturing" \
|
||||
-v "$(pwd)/opensfm:/code/opensfm" \
|
||||
-v "$(pwd)/pmvs:/code/pmvs" \
|
||||
-v "$(pwd)/smvs:/code/smvs" \
|
||||
opendronemap/opendronemap
|
||||
|
||||
To pass in custom parameters to the run.py script, simply pass it as arguments to the `docker run` command. For example:
|
||||
|
@ -222,7 +222,7 @@ When building your own Docker image, if image size is of importance to you, you
|
|||
This will clean up intermediate steps in the Docker build process, resulting in a significantly smaller image (about half the size).
|
||||
|
||||
Experimental flags need to be enabled in Docker to use the ```--squash``` flag. To enable this, insert the following into the file ```/etc/docker/daemon.json```:
|
||||
|
||||
|
||||
{
|
||||
"experimental": true
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ Coming soon...
|
|||
|
||||
## Documentation:
|
||||
|
||||
For documentation, everything is being moved to [http://docs.opendronemap.org/](http://docs.opendronemap.org/) but you can also take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki). Check those places first if you are having problems. There's also help at [community forum](http://community.opendronemap.org/), and if you still need help and think you've found a bug or need an enhancement, look through the issue queue or create one.
|
||||
For documentation, everything is being moved to [http://docs.opendronemap.org/](http://docs.opendronemap.org/) but you can also take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki). Check those places first if you are having problems. There's also help at [community forum](http://community.opendronemap.org/), and if you still need help and think you've found a bug or need an enhancement, look through the issue queue or create one.
|
||||
|
||||
## Developers
|
||||
|
||||
|
|
|
@ -114,14 +114,12 @@ SETUP_EXTERNAL_PROJECT(Hexer 1.4 ON)
|
|||
# ---------------------------------------------------------------------------------------------
|
||||
# Open Geometric Vision (OpenGV)
|
||||
# Open Structure from Motion (OpenSfM)
|
||||
# Clustering Views for Multi-view Stereo (CMVS)
|
||||
# Catkin
|
||||
# Ecto
|
||||
#
|
||||
|
||||
set(custom_libs OpenGV
|
||||
OpenSfM
|
||||
CMVS
|
||||
Catkin
|
||||
Ecto
|
||||
LASzip
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
set(_proj_name cmvs)
|
||||
set(_SB_BINARY_DIR "${SB_BINARY_DIR}/${_proj_name}")
|
||||
|
||||
ExternalProject_Add(${_proj_name}
|
||||
PREFIX ${_SB_BINARY_DIR}
|
||||
TMP_DIR ${_SB_BINARY_DIR}/tmp
|
||||
STAMP_DIR ${_SB_BINARY_DIR}/stamp
|
||||
#--Download step--------------
|
||||
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}/${_proj_name}
|
||||
URL https://github.com/edgarriba/CMVS-PMVS/archive/master.zip
|
||||
URL_MD5 dbb1493f49ca099b4208381bd20d1435
|
||||
#--Update/Patch step----------
|
||||
UPDATE_COMMAND ""
|
||||
#--Configure step-------------
|
||||
SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name}
|
||||
CONFIGURE_COMMAND cmake <SOURCE_DIR>/program
|
||||
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${SB_INSTALL_DIR}/bin
|
||||
-DCMAKE_INSTALL_PREFIX:PATH=${SB_INSTALL_DIR}
|
||||
#--Build step-----------------
|
||||
BINARY_DIR ${_SB_BINARY_DIR}
|
||||
#--Install step---------------
|
||||
INSTALL_DIR ${SB_INSTALL_DIR}
|
||||
#--Output logging-------------
|
||||
LOG_DOWNLOAD OFF
|
||||
LOG_CONFIGURE OFF
|
||||
LOG_BUILD OFF
|
||||
)
|
||||
|
|
@ -10,8 +10,6 @@ Licensing for portions of OpenDroneMap are as follows:
|
|||
* libcv - BSD - http://opencv.org/license.html
|
||||
* libcvaux - BSD - http://opencv.org/license.html
|
||||
* bundler - GPLv3 - http://www.gnu.org/copyleft/gpl.html
|
||||
* cmvs - GPLv3 - http://www.gnu.org/copyleft/gpl.html
|
||||
* pmvs2 - GPLv3 - http://www.gnu.org/copyleft/gpl.html
|
||||
* parallel - GPLv3 - http://www.gnu.org/copyleft/gpl.html
|
||||
* PoissonRecon - BSD - http://www.cs.jhu.edu/~misha/Code/PoissonRecon/license.txt
|
||||
* vlfeat - BSD - http://www.vlfeat.org/license.html
|
||||
|
|
|
@ -7,7 +7,7 @@ from appsettings import SettingsParser
|
|||
import sys
|
||||
|
||||
# parse arguments
|
||||
processopts = ['dataset', 'opensfm', 'slam', 'cmvs', 'pmvs', 'smvs',
|
||||
processopts = ['dataset', 'opensfm', 'slam', 'smvs',
|
||||
'odm_meshing', 'odm_25dmeshing', 'mvs_texturing', 'odm_georeferencing',
|
||||
'odm_dem', 'odm_orthophoto']
|
||||
|
||||
|
@ -241,63 +241,6 @@ def config():
|
|||
'with --smvs-enable-shading when you have simple JPGs with '
|
||||
'SRGB gamma correction. 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')
|
||||
|
||||
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'))
|
||||
|
||||
parser.add_argument('--pmvs-csize',
|
||||
metavar='<positive integer>',
|
||||
default=2,
|
||||
type=int,
|
||||
help='Cell size controls the density of reconstructions'
|
||||
'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'))
|
||||
|
||||
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')
|
||||
|
||||
parser.add_argument('--pmvs-min-images',
|
||||
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'))
|
||||
|
||||
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'))
|
||||
|
||||
parser.add_argument('--mesh-size',
|
||||
metavar='<positive integer>',
|
||||
default=100000,
|
||||
|
@ -612,11 +555,6 @@ def config():
|
|||
log.ODM_INFO('Fast orthophoto is turned on, automatically setting --use-25dmesh')
|
||||
args.use_25dmesh = True
|
||||
|
||||
# Cannot use pmvs
|
||||
if args.use_pmvs:
|
||||
log.ODM_INFO('Fast orthophoto is turned on, cannot use pmvs (removing --use-pmvs)')
|
||||
args.use_pmvs = False
|
||||
|
||||
if args.dtm and args.pc_classify == 'none':
|
||||
log.ODM_INFO("DTM is turned on, automatically turning on point cloud classification")
|
||||
args.pc_classify = "smrf"
|
||||
|
|
|
@ -23,11 +23,6 @@ ccd_widths_path = os.path.join(opensfm_path, 'opensfm/data/sensor_data.json')
|
|||
# define orb_slam2 path
|
||||
orb_slam2_path = os.path.join(superbuild_path, "src/orb_slam2")
|
||||
|
||||
# define pmvs path
|
||||
cmvs_path = os.path.join(superbuild_path, "install/bin/cmvs")
|
||||
cmvs_opts_path = os.path.join(superbuild_path, "install/bin/genOption")
|
||||
pmvs2_path = os.path.join(superbuild_path, "install/bin/pmvs2")
|
||||
|
||||
# define smvs join_paths
|
||||
makescene_path = os.path.join(superbuild_path, 'src', 'elibs', 'mve', 'apps', 'makescene', 'makescene') #TODO: don't install in source
|
||||
smvs_path = os.path.join(superbuild_path, 'src', 'elibs', 'smvs', 'app', 'smvsrecon')
|
||||
|
|
|
@ -8,8 +8,6 @@ from scripts.opensfm import opensfm
|
|||
|
||||
# Define pipeline tasks
|
||||
tasks_dict = {'1': 'opensfm',
|
||||
'2': 'cmvs',
|
||||
'3': 'pmvs',
|
||||
'4': 'odm_meshing',
|
||||
'5': 'mvs_texturing',
|
||||
'6': 'odm_georeferencing',
|
||||
|
@ -52,7 +50,7 @@ class ODMTaskManager(object):
|
|||
'args': _odm_app.args,
|
||||
'photos': _odm_app.photos}
|
||||
|
||||
elif task_name in ['cmvs', 'pmvs', 'odm_meshing', 'mvs_texturing', 'odm_georeferencing', 'odm_orthophoto', 'zip_results']:
|
||||
elif task_name in [ 'odm_meshing', 'mvs_texturing', 'odm_georeferencing', 'odm_orthophoto', 'zip_results']:
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
|
|
@ -274,14 +274,8 @@ class ODM_GeoRef(object):
|
|||
with open(json_file, 'w') as f:
|
||||
f.write(pipeline)
|
||||
|
||||
<<<<<<< HEAD
|
||||
# call pdal
|
||||
system.run('{bin}/pdal pipeline -i {json} --readers.ply.filename={f_in} '
|
||||
'--writers.las.filename={f_out}'.format(**kwargs))
|
||||
=======
|
||||
# call pdal
|
||||
system.run('{bin}/pdal pipeline -i {json} --readers.ply.filename={f_in}'.format(**kwargs))
|
||||
>>>>>>> 4a00c4d8411befc0c1b01a36f713e61633f002b3
|
||||
|
||||
def utm_to_latlon(self, _file, _photo, idx):
|
||||
|
||||
|
@ -421,7 +415,6 @@ class ODM_Tree(object):
|
|||
self.dataset_raw = io.join_paths(self.root_path, 'images')
|
||||
self.opensfm = io.join_paths(self.root_path, 'opensfm')
|
||||
self.smvs = io.join_paths(self.root_path, 'smvs')
|
||||
self.pmvs = io.join_paths(self.root_path, 'pmvs')
|
||||
self.odm_meshing = io.join_paths(self.root_path, 'odm_meshing')
|
||||
self.odm_texturing = io.join_paths(self.root_path, 'odm_texturing')
|
||||
self.odm_25dtexturing = io.join_paths(self.root_path, 'odm_texturing_25d')
|
||||
|
@ -446,13 +439,6 @@ class ODM_Tree(object):
|
|||
self.opensfm_model = io.join_paths(self.opensfm, 'depthmaps/merged.ply')
|
||||
self.opensfm_transformation = io.join_paths(self.opensfm, 'geocoords_transformation.txt')
|
||||
|
||||
# pmvs
|
||||
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')
|
||||
|
||||
# smvs
|
||||
self.smvs_model = io.join_paths(self.smvs, 'smvs_dense_point_cloud.ply')
|
||||
self.mve_path = io.join_paths(self.opensfm, 'mve')
|
||||
|
|
2
run.py
2
run.py
|
@ -33,7 +33,7 @@ if __name__ == '__main__':
|
|||
+ args.project_path + "/odm_orthophoto "
|
||||
+ args.project_path + "/odm_texturing "
|
||||
+ args.project_path + "/opensfm "
|
||||
+ args.project_path + "/pmvs")
|
||||
+ args.project_path + "/smvs")
|
||||
|
||||
# create an instance of my App BlackBox
|
||||
# internally configure all tasks
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
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):
|
||||
|
||||
# Benchmarking
|
||||
start_time = system.now_raw()
|
||||
|
||||
log.ODM_INFO('Running ODM 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') or \
|
||||
(args.rerun_all) or \
|
||||
(args.rerun_from is not None and
|
||||
'cmvs' in args.rerun_from)
|
||||
|
||||
if not io.file_exists(tree.pmvs_bundle) or rerun_cell:
|
||||
log.ODM_DEBUG('Writing 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)
|
||||
|
||||
outputs.reconstruction = inputs.reconstruction
|
||||
|
||||
if args.time:
|
||||
system.benchmark(start_time, tree.benchmarking, 'CMVS')
|
||||
|
||||
log.ODM_INFO('Running ODM CMVS Cell - Finished')
|
||||
return ecto.OK if args.end_with != 'cmvs' else ecto.QUIT
|
|
@ -5,8 +5,6 @@ from opendm import io
|
|||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
import pmvs2nvmcams
|
||||
|
||||
class ODMMvsTexCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("data_term", 'Data term: [area, gmi] default: gmi', "gmi")
|
||||
|
@ -38,7 +36,7 @@ class ODMMvsTexCell(ecto.Cell):
|
|||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_texturing)
|
||||
if args.use_25dmesh: system.mkdir_p(tree.odm_25dtexturing)
|
||||
if args.use_25dmesh: system.mkdir_p(tree.odm_25dtexturing)
|
||||
|
||||
# check if we rerun cell or not
|
||||
rerun_cell = (args.rerun is not None and
|
||||
|
@ -62,11 +60,11 @@ class ODMMvsTexCell(ecto.Cell):
|
|||
'model': tree.odm_25dmesh,
|
||||
|
||||
# We always skip the visibility test when using the 2.5D mesh
|
||||
# because many faces end up being narrow, and almost perpendicular
|
||||
# because many faces end up being narrow, and almost perpendicular
|
||||
# to the ground plane. The visibility test improperly classifies
|
||||
# them as "not seen" since the test is done on a single triangle vertex,
|
||||
# and while one vertex might be occluded, the other two might not.
|
||||
'force_skip_vis_test': True
|
||||
'force_skip_vis_test': True
|
||||
}]
|
||||
|
||||
for r in runs:
|
||||
|
@ -82,7 +80,7 @@ class ODMMvsTexCell(ecto.Cell):
|
|||
skipLocalSeamLeveling = ""
|
||||
skipHoleFilling = ""
|
||||
keepUnseenFaces = ""
|
||||
|
||||
|
||||
if (self.params.skip_vis_test or r['force_skip_vis_test']):
|
||||
skipGeometricVisibilityTest = "--skip_geometric_visibility_test"
|
||||
if (self.params.skip_glob_seam_leveling):
|
||||
|
@ -98,8 +96,6 @@ class ODMMvsTexCell(ecto.Cell):
|
|||
kwargs = {
|
||||
'bin': context.mvstex_path,
|
||||
'out_dir': io.join_paths(r['out_dir'], "odm_textured_model"),
|
||||
'pmvs_folder': tree.pmvs_rec_path,
|
||||
'nvm_file': io.join_paths(tree.pmvs_rec_path, "nvmCams.nvm"),
|
||||
'model': r['model'],
|
||||
'dataTerm': self.params.data_term,
|
||||
'outlierRemovalType': self.params.outlier_rem_type,
|
||||
|
@ -111,16 +107,8 @@ class ODMMvsTexCell(ecto.Cell):
|
|||
'toneMapping': self.params.tone_mapping
|
||||
}
|
||||
|
||||
if not args.use_pmvs:
|
||||
kwargs['nvm_file'] = io.join_paths(tree.opensfm,
|
||||
"reconstruction.nvm")
|
||||
else:
|
||||
log.ODM_DEBUG('Generating .nvm file from pmvs output: %s'
|
||||
% '{nvm_file}'.format(**kwargs))
|
||||
|
||||
# Create .nvm camera file.
|
||||
pmvs2nvmcams.run('{pmvs_folder}'.format(**kwargs),
|
||||
'{nvm_file}'.format(**kwargs))
|
||||
kwargs['nvm_file'] = io.join_paths(tree.opensfm,
|
||||
"reconstruction.nvm")
|
||||
|
||||
# Make sure tmp directory is empty
|
||||
mvs_tmp_dir = os.path.join(r['out_dir'], 'tmp')
|
||||
|
|
|
@ -10,8 +10,6 @@ from dataset import ODMLoadDatasetCell
|
|||
from run_opensfm import ODMOpenSfMCell
|
||||
from smvs import ODMSmvsCell
|
||||
from odm_slam import ODMSlamCell
|
||||
from pmvs import ODMPmvsCell
|
||||
from cmvs import ODMCmvsCell
|
||||
from odm_meshing import ODMeshingCell
|
||||
from mvstex import ODMMvsTexCell
|
||||
from odm_georeferencing import ODMGeoreferencingCell
|
||||
|
@ -51,13 +49,6 @@ class ODMApp(ecto.BlackBox):
|
|||
fixed_camera_params=p.args.use_fixed_camera_params,
|
||||
hybrid_bundle_adjustment=p.args.use_hybrid_bundle_adjustment),
|
||||
'slam': ODMSlamCell(),
|
||||
'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_min_images,
|
||||
cores=p.args.pmvs_num_cores),
|
||||
'smvs': ODMSmvsCell(alpha=p.args.smvs_alpha,
|
||||
scale=p.args.smvs_scale,
|
||||
threads=p.args.max_concurrency,
|
||||
|
@ -135,7 +126,7 @@ class ODMApp(ecto.BlackBox):
|
|||
self.args[:] >> self.smvs['args'],
|
||||
self.opensfm['reconstruction'] >> self.smvs['reconstruction']]
|
||||
|
||||
# create odm mesh from pmvs point cloud
|
||||
# create odm mesh from smvs point cloud
|
||||
connections += [self.tree[:] >> self.meshing['tree'],
|
||||
self.args[:] >> self.meshing['args'],
|
||||
self.smvs['reconstruction'] >> self.meshing['reconstruction']]
|
||||
|
@ -169,20 +160,14 @@ class ODMApp(ecto.BlackBox):
|
|||
connections += [self.tree[:] >> self.slam['tree'],
|
||||
self.args[:] >> self.slam['args']]
|
||||
|
||||
# run cmvs
|
||||
connections += [self.tree[:] >> self.cmvs['tree'],
|
||||
self.args[:] >> self.cmvs['args'],
|
||||
self.slam['reconstruction'] >> self.cmvs['reconstruction']]
|
||||
|
||||
# run pmvs
|
||||
connections += [self.tree[:] >> self.pmvs['tree'],
|
||||
self.args[:] >> self.pmvs['args'],
|
||||
self.cmvs['reconstruction'] >> self.pmvs['reconstruction']]
|
||||
connections += [self.tree[:] >> self.smvs['tree'],
|
||||
self.args[:] >> self.smvs['args'],
|
||||
self.slam['reconstruction'] >> self.smvs['reconstruction']]
|
||||
|
||||
# create odm mesh
|
||||
connections += [self.tree[:] >> self.meshing['tree'],
|
||||
self.args[:] >> self.meshing['args'],
|
||||
self.pmvs['reconstruction'] >> self.meshing['reconstruction']]
|
||||
self.smvs['reconstruction'] >> self.meshing['reconstruction']]
|
||||
|
||||
# create odm texture
|
||||
connections += [self.tree[:] >> self.texturing['tree'],
|
||||
|
|
|
@ -92,13 +92,12 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
'verbose': verbose
|
||||
|
||||
}
|
||||
if not args.use_pmvs:
|
||||
if args.fast_orthophoto:
|
||||
kwargs['pc'] = os.path.join(tree.opensfm, 'reconstruction.ply')
|
||||
else:
|
||||
kwargs['pc'] = tree.opensfm_model
|
||||
|
||||
if args.fast_orthophoto:
|
||||
kwargs['pc'] = os.path.join(tree.opensfm, 'reconstruction.ply')
|
||||
else:
|
||||
kwargs['pc'] = tree.pmvs_model
|
||||
kwargs['pc'] = tree.opensfm_model
|
||||
|
||||
|
||||
# Check to see if the GCP file exists
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ class ODMSlamCell(ecto.Cell):
|
|||
|
||||
# create working directories
|
||||
system.mkdir_p(tree.opensfm)
|
||||
system.mkdir_p(tree.pmvs)
|
||||
|
||||
vocabulary = os.path.join(context.orb_slam2_path,
|
||||
'Vocabulary/ORBvoc.txt')
|
||||
|
@ -96,16 +95,5 @@ class ODMSlamCell(ecto.Cell):
|
|||
'Found a valid Bundler file in: {}'.format(
|
||||
tree.opensfm_reconstruction))
|
||||
|
||||
# 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={} {}/bin/export_pmvs {} --output {}'.format(
|
||||
context.pyopencv_path, context.opensfm_path, tree.opensfm,
|
||||
tree.pmvs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid CMVS file in: {}'.format(
|
||||
tree.pmvs_visdat))
|
||||
|
||||
log.ODM_INFO('Running OMD Slam Cell - Finished')
|
||||
return ecto.OK if args.end_with != 'odm_slam' else ecto.QUIT
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
import ecto
|
||||
|
||||
from opendm import io
|
||||
from opendm import log
|
||||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
|
||||
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", "list of ODMReconstructions", [])
|
||||
outputs.declare("reconstruction", "list of ODMReconstructions", [])
|
||||
|
||||
def process(self, inputs, outputs):
|
||||
|
||||
# Benchmarking
|
||||
start_time = system.now_raw()
|
||||
|
||||
log.ODM_INFO('Running OMD PMVS 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 == 'pmvs') or \
|
||||
(args.rerun_all) or \
|
||||
(args.rerun_from is not None and
|
||||
'pmvs' in args.rerun_from)
|
||||
|
||||
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/ option-0000' %
|
||||
(context.pmvs2_path, tree.pmvs_rec_path))
|
||||
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid PMVS file in %s' % tree.pmvs_model)
|
||||
|
||||
outputs.reconstruction = inputs.reconstruction
|
||||
|
||||
if args.time:
|
||||
system.benchmark(start_time, tree.benchmarking, 'PMVS')
|
||||
|
||||
log.ODM_INFO('Running ODM PMVS Cell - Finished')
|
||||
return ecto.OK if args.end_with != 'pmvs' else ecto.QUIT
|
|
@ -1,144 +0,0 @@
|
|||
import os
|
||||
import numpy as np
|
||||
|
||||
from opendm import log
|
||||
|
||||
# Go from QR-factorizatoin to corresponding RQ-factorization.
|
||||
def rq(A):
|
||||
Q,R = np.linalg.qr(np.flipud(A).T)
|
||||
R = np.flipud(R.T)
|
||||
Q = Q.T
|
||||
return R[:,::-1],Q[::-1,:]
|
||||
|
||||
# Create a unit quaternion from rotation matrix.
|
||||
def rot2quat(R):
|
||||
|
||||
# Float epsilon (use square root to be well with the stable region).
|
||||
eps = np.sqrt(np.finfo(float).eps)
|
||||
|
||||
# If the determinant is not 1, it's not a rotation matrix
|
||||
if np.abs(np.linalg.det(R) - 1.0) > eps:
|
||||
log.ODM_ERROR('Matrix passed to rot2quat was not a rotation matrix, det != 1.0')
|
||||
|
||||
tr = np.trace(R)
|
||||
|
||||
quat = np.zeros((1,4))
|
||||
|
||||
# Is trace big enough be computationally stable?
|
||||
if tr > eps:
|
||||
S = 0.5 / np.sqrt(tr + 1.0)
|
||||
quat[0,0] = 0.25 / S
|
||||
quat[0,1] = (R[2,1] - R[1,2]) * S
|
||||
quat[0,2] = (R[0,2] - R[2,0]) * S
|
||||
quat[0,3] = (R[1,0] - R[0,1]) * S
|
||||
else: # It's not, use the largest diagonal.
|
||||
if R[0,0] > R[1,1] and R[0,0] > R[2,2]:
|
||||
S = np.sqrt(1.0 + R[0,0] - R[1,1] - R[2,2]) * 2.0
|
||||
quat[0,0] = (R[2,1] - R[1,2]) / S
|
||||
quat[0,1] = 0.25 * S
|
||||
quat[0,2] = (R[0,1] + R[1,0]) / S
|
||||
quat[0,3] = (R[0,2] + R[2,0]) / S
|
||||
elif R[1,1] > R[2,2]:
|
||||
S = np.sqrt(1.0 - R[0,0] + R[1,1] - R[2,2]) * 2.0
|
||||
quat[0,0] = (R[0,2] - R[2,0]) / S
|
||||
quat[0,1] = (R[0,1] + R[1,0]) / S
|
||||
quat[0,2] = 0.25 * S
|
||||
quat[0,3] = (R[1,2] + R[2,1]) / S
|
||||
else:
|
||||
S = np.sqrt(1.0 - R[0,0] - R[1,1] + R[2,2]) * 2.0
|
||||
quat[0,0] = (R[1,0] - R[0,1]) / S
|
||||
quat[0,1] = (R[0,2] + R[2,0]) / S
|
||||
quat[0,2] = (R[1,2] + R[2,1]) / S
|
||||
quat[0,3] = 0.25 * S
|
||||
|
||||
return quat
|
||||
|
||||
# Decompose a projection matrix into parts
|
||||
# (Intrinsic projection, Rotation, Camera position)
|
||||
def decomposeProjection(projectionMatrix):
|
||||
|
||||
# Check input:
|
||||
if projectionMatrix.shape != (3,4):
|
||||
log.ODM_ERROR('Unable to decompose projection matrix, shape != (3,4)')
|
||||
|
||||
RQ = rq(projectionMatrix[:,:3])
|
||||
|
||||
# Fix sign, since we know K is upper triangular and has a positive diagonal.
|
||||
signMat = np.diag(np.diag(np.sign(RQ[0])))
|
||||
K = signMat*RQ[0]
|
||||
R = signMat*RQ[1]
|
||||
|
||||
# Calculate camera position from translation vector.
|
||||
t = np.linalg.inv(-1.0*projectionMatrix[:,:3])*projectionMatrix[:,3]
|
||||
|
||||
return K, R, t
|
||||
|
||||
# Parses pvms contour file.
|
||||
def parseContourFile(filePath):
|
||||
|
||||
with open(filePath, 'r') as contourFile:
|
||||
if (contourFile.readline().strip() != "CONTOUR"):
|
||||
return np.array([])
|
||||
else:
|
||||
pMatData = np.loadtxt(contourFile, float, '#', None, None, 0)
|
||||
if pMatData.shape == (3,4):
|
||||
return pMatData
|
||||
return np.array([])
|
||||
|
||||
|
||||
|
||||
# Creates a .nvm camera file in the pmvs folder.
|
||||
def run(pmvsFolder, outputFile):
|
||||
|
||||
projectionFolder = pmvsFolder + "/txt"
|
||||
imageFolder = pmvsFolder + "/visualize"
|
||||
|
||||
pMatrices = []
|
||||
imageFileNames = []
|
||||
|
||||
# for all files in the visualize folder:
|
||||
for imageFileName in os.listdir(imageFolder):
|
||||
fileNameNoExt = os.path.splitext(imageFileName)[0]
|
||||
|
||||
# look for corresponding projection matrix txt file
|
||||
projectionFilePath = os.path.join(projectionFolder, fileNameNoExt)
|
||||
projectionFilePath += ".txt"
|
||||
if os.path.isfile(projectionFilePath):
|
||||
pMatData = parseContourFile(projectionFilePath)
|
||||
if pMatData.size == 0:
|
||||
log.ODM_WARNING('Unable to parse contour file, skipping: %s'
|
||||
% projectionFilePath)
|
||||
else:
|
||||
pMatrices.append(np.matrix(pMatData))
|
||||
imageFileNames.append(imageFileName)
|
||||
|
||||
|
||||
# Decompose projection matrices
|
||||
focals = []
|
||||
rotations = []
|
||||
translations = []
|
||||
for projection in pMatrices:
|
||||
KRt = decomposeProjection(projection)
|
||||
focals.append(KRt[0][0,0])
|
||||
rotations.append(rot2quat(KRt[1]))
|
||||
translations.append(KRt[2])
|
||||
|
||||
# Create .nvm file
|
||||
with open (outputFile, 'w') as nvmFile:
|
||||
nvmFile.write("NVM_V3\n\n")
|
||||
nvmFile.write('%d' % len(rotations) + "\n")
|
||||
|
||||
for idx, imageFileName in enumerate(imageFileNames):
|
||||
nvmFile.write(os.path.join("visualize", imageFileName))
|
||||
nvmFile.write(" " + '%f' % focals[idx])
|
||||
nvmFile.write(" " + '%f' % rotations[idx][0,0] +
|
||||
" " + '%f' % rotations[idx][0,1] +
|
||||
" " + '%f' % rotations[idx][0,2] +
|
||||
" " + '%f' % rotations[idx][0,3])
|
||||
nvmFile.write(" " + '%f' % translations[idx][0] +
|
||||
" " + '%f' % translations[idx][1] +
|
||||
" " + '%f' % translations[idx][2])
|
||||
nvmFile.write(" 0 0\n")
|
||||
nvmFile.write("0\n\n")
|
||||
nvmFile.write("0\n\n")
|
||||
nvmFile.write("0")
|
|
@ -42,7 +42,6 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
|
||||
# create working directories
|
||||
system.mkdir_p(tree.opensfm)
|
||||
system.mkdir_p(tree.pmvs)
|
||||
|
||||
# check if we rerun cell or not
|
||||
rerun_cell = (args.rerun is not None and
|
||||
|
@ -169,15 +168,6 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
log.ODM_WARNING('Found a valid Bundler file in: %s' %
|
||||
tree.opensfm_reconstruction)
|
||||
|
||||
# if args.use_pmvs:
|
||||
# # 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))
|
||||
# else:
|
||||
# log.ODM_WARNING('Found a valid CMVS file in: %s' % tree.pmvs_visdat)
|
||||
|
||||
if reconstruction.georef:
|
||||
system.run('PYTHONPATH=%s %s/bin/opensfm export_geocoords %s --transformation --proj \'%s\'' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm, reconstruction.georef.projection.srs))
|
||||
|
|
|
@ -66,7 +66,7 @@ class ODMSmvsCell(ecto.Cell):
|
|||
"-a%s" % self.params.alpha,
|
||||
"-s%s" % self.params.scale,
|
||||
"-o%s" % self.params.output_scale,
|
||||
"--debug-lvl=%s" % '1' if self.params.verbose else '0',
|
||||
"--debug-lvl=%s" % ('1' if self.params.verbose else '0'),
|
||||
"%s" % '-S' if self.params.shading else '',
|
||||
"%s" % '-g' if self.params.gamma_srgb and self.params.shading else '',
|
||||
"--force" if rerun_cell else ''
|
||||
|
|
Ładowanie…
Reference in New Issue