Remove PMVS module

Former-commit-id: eda8beaefe
pull/1161/head
Dakota Benjamin 2018-07-01 17:28:55 -04:00
rodzic 6c64d1172c
commit 80cbc2f03b
19 zmienionych plików z 26 dodań i 490 usunięć

Wyświetl plik

@ -6,12 +6,9 @@ SuperBuild/install
SuperBuild/src SuperBuild/src
build build
opensfm opensfm
pmvs
odm_orthophoto odm_orthophoto
odm_texturing odm_texturing
odm_meshing odm_meshing
odm_georeferencing odm_georeferencing
images_resize images_resize
.git .git

Wyświetl plik

@ -105,7 +105,7 @@ or
python run.py --rerun-from odm_meshing project-name 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 ### 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 ## 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/)) 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 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 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. 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 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: simply use the following `docker run` command after building the image:
docker run -it --rm \ 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_orthophoto:/code/odm_orthophoto" \
-v "$(pwd)/odm_texturing:/code/odm_texturing" \ -v "$(pwd)/odm_texturing:/code/odm_texturing" \
-v "$(pwd)/opensfm:/code/opensfm" \ -v "$(pwd)/opensfm:/code/opensfm" \
-v "$(pwd)/pmvs:/code/pmvs" \ -v "$(pwd)/smvs:/code/smvs" \
opendronemap/opendronemap opendronemap/opendronemap
To pass in custom parameters to the run.py script, simply pass it as arguments to the `docker run` command. For example: 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). 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 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 "experimental": true
} }
@ -244,7 +244,7 @@ Coming soon...
## Documentation: ## 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 ## Developers

Wyświetl plik

@ -114,14 +114,12 @@ SETUP_EXTERNAL_PROJECT(Hexer 1.4 ON)
# --------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------
# Open Geometric Vision (OpenGV) # Open Geometric Vision (OpenGV)
# Open Structure from Motion (OpenSfM) # Open Structure from Motion (OpenSfM)
# Clustering Views for Multi-view Stereo (CMVS)
# Catkin # Catkin
# Ecto # Ecto
# #
set(custom_libs OpenGV set(custom_libs OpenGV
OpenSfM OpenSfM
CMVS
Catkin Catkin
Ecto Ecto
LASzip LASzip

Wyświetl plik

@ -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
)

Wyświetl plik

@ -10,8 +10,6 @@ Licensing for portions of OpenDroneMap are as follows:
* libcv - BSD - http://opencv.org/license.html * libcv - BSD - http://opencv.org/license.html
* libcvaux - BSD - http://opencv.org/license.html * libcvaux - BSD - http://opencv.org/license.html
* bundler - GPLv3 - http://www.gnu.org/copyleft/gpl.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 * parallel - GPLv3 - http://www.gnu.org/copyleft/gpl.html
* PoissonRecon - BSD - http://www.cs.jhu.edu/~misha/Code/PoissonRecon/license.txt * PoissonRecon - BSD - http://www.cs.jhu.edu/~misha/Code/PoissonRecon/license.txt
* vlfeat - BSD - http://www.vlfeat.org/license.html * vlfeat - BSD - http://www.vlfeat.org/license.html

Wyświetl plik

@ -7,7 +7,7 @@ from appsettings import SettingsParser
import sys import sys
# parse arguments # parse arguments
processopts = ['dataset', 'opensfm', 'slam', 'cmvs', 'pmvs', 'smvs', processopts = ['dataset', 'opensfm', 'slam', 'smvs',
'odm_meshing', 'odm_25dmeshing', 'mvs_texturing', 'odm_georeferencing', 'odm_meshing', 'odm_25dmeshing', 'mvs_texturing', 'odm_georeferencing',
'odm_dem', 'odm_orthophoto'] 'odm_dem', 'odm_orthophoto']
@ -241,63 +241,6 @@ def config():
'with --smvs-enable-shading when you have simple JPGs with ' 'with --smvs-enable-shading when you have simple JPGs with '
'SRGB gamma correction. Default: %(default)s') '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', parser.add_argument('--mesh-size',
metavar='<positive integer>', metavar='<positive integer>',
default=100000, default=100000,
@ -612,11 +555,6 @@ def config():
log.ODM_INFO('Fast orthophoto is turned on, automatically setting --use-25dmesh') log.ODM_INFO('Fast orthophoto is turned on, automatically setting --use-25dmesh')
args.use_25dmesh = True 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': if args.dtm and args.pc_classify == 'none':
log.ODM_INFO("DTM is turned on, automatically turning on point cloud classification") log.ODM_INFO("DTM is turned on, automatically turning on point cloud classification")
args.pc_classify = "smrf" args.pc_classify = "smrf"

Wyświetl plik

@ -23,11 +23,6 @@ ccd_widths_path = os.path.join(opensfm_path, 'opensfm/data/sensor_data.json')
# define orb_slam2 path # define orb_slam2 path
orb_slam2_path = os.path.join(superbuild_path, "src/orb_slam2") 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 # define smvs join_paths
makescene_path = os.path.join(superbuild_path, 'src', 'elibs', 'mve', 'apps', 'makescene', 'makescene') #TODO: don't install in source 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') smvs_path = os.path.join(superbuild_path, 'src', 'elibs', 'smvs', 'app', 'smvsrecon')

Wyświetl plik

@ -8,8 +8,6 @@ from scripts.opensfm import opensfm
# Define pipeline tasks # Define pipeline tasks
tasks_dict = {'1': 'opensfm', tasks_dict = {'1': 'opensfm',
'2': 'cmvs',
'3': 'pmvs',
'4': 'odm_meshing', '4': 'odm_meshing',
'5': 'mvs_texturing', '5': 'mvs_texturing',
'6': 'odm_georeferencing', '6': 'odm_georeferencing',
@ -52,7 +50,7 @@ class ODMTaskManager(object):
'args': _odm_app.args, 'args': _odm_app.args,
'photos': _odm_app.photos} '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 # setup this task
command = None command = None
inputs = {} inputs = {}

Wyświetl plik

@ -274,14 +274,8 @@ class ODM_GeoRef(object):
with open(json_file, 'w') as f: with open(json_file, 'w') as f:
f.write(pipeline) f.write(pipeline)
<<<<<<< HEAD
# call pdal # 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)) system.run('{bin}/pdal pipeline -i {json} --readers.ply.filename={f_in}'.format(**kwargs))
>>>>>>> 4a00c4d8411befc0c1b01a36f713e61633f002b3
def utm_to_latlon(self, _file, _photo, idx): 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.dataset_raw = io.join_paths(self.root_path, 'images')
self.opensfm = io.join_paths(self.root_path, 'opensfm') self.opensfm = io.join_paths(self.root_path, 'opensfm')
self.smvs = io.join_paths(self.root_path, 'smvs') 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_meshing = io.join_paths(self.root_path, 'odm_meshing')
self.odm_texturing = io.join_paths(self.root_path, 'odm_texturing') self.odm_texturing = io.join_paths(self.root_path, 'odm_texturing')
self.odm_25dtexturing = io.join_paths(self.root_path, 'odm_texturing_25d') 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_model = io.join_paths(self.opensfm, 'depthmaps/merged.ply')
self.opensfm_transformation = io.join_paths(self.opensfm, 'geocoords_transformation.txt') 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 # smvs
self.smvs_model = io.join_paths(self.smvs, 'smvs_dense_point_cloud.ply') self.smvs_model = io.join_paths(self.smvs, 'smvs_dense_point_cloud.ply')
self.mve_path = io.join_paths(self.opensfm, 'mve') self.mve_path = io.join_paths(self.opensfm, 'mve')

2
run.py
Wyświetl plik

@ -33,7 +33,7 @@ if __name__ == '__main__':
+ args.project_path + "/odm_orthophoto " + args.project_path + "/odm_orthophoto "
+ args.project_path + "/odm_texturing " + args.project_path + "/odm_texturing "
+ args.project_path + "/opensfm " + args.project_path + "/opensfm "
+ args.project_path + "/pmvs") + args.project_path + "/smvs")
# create an instance of my App BlackBox # create an instance of my App BlackBox
# internally configure all tasks # internally configure all tasks

Wyświetl plik

@ -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

Wyświetl plik

@ -5,8 +5,6 @@ from opendm import io
from opendm import system from opendm import system
from opendm import context from opendm import context
import pmvs2nvmcams
class ODMMvsTexCell(ecto.Cell): class ODMMvsTexCell(ecto.Cell):
def declare_params(self, params): def declare_params(self, params):
params.declare("data_term", 'Data term: [area, gmi] default: gmi', "gmi") 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 # define paths and create working directories
system.mkdir_p(tree.odm_texturing) 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 # check if we rerun cell or not
rerun_cell = (args.rerun is not None and rerun_cell = (args.rerun is not None and
@ -62,11 +60,11 @@ class ODMMvsTexCell(ecto.Cell):
'model': tree.odm_25dmesh, 'model': tree.odm_25dmesh,
# We always skip the visibility test when using the 2.5D mesh # 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 # to the ground plane. The visibility test improperly classifies
# them as "not seen" since the test is done on a single triangle vertex, # 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. # 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: for r in runs:
@ -82,7 +80,7 @@ class ODMMvsTexCell(ecto.Cell):
skipLocalSeamLeveling = "" skipLocalSeamLeveling = ""
skipHoleFilling = "" skipHoleFilling = ""
keepUnseenFaces = "" keepUnseenFaces = ""
if (self.params.skip_vis_test or r['force_skip_vis_test']): if (self.params.skip_vis_test or r['force_skip_vis_test']):
skipGeometricVisibilityTest = "--skip_geometric_visibility_test" skipGeometricVisibilityTest = "--skip_geometric_visibility_test"
if (self.params.skip_glob_seam_leveling): if (self.params.skip_glob_seam_leveling):
@ -98,8 +96,6 @@ class ODMMvsTexCell(ecto.Cell):
kwargs = { kwargs = {
'bin': context.mvstex_path, 'bin': context.mvstex_path,
'out_dir': io.join_paths(r['out_dir'], "odm_textured_model"), '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'], 'model': r['model'],
'dataTerm': self.params.data_term, 'dataTerm': self.params.data_term,
'outlierRemovalType': self.params.outlier_rem_type, 'outlierRemovalType': self.params.outlier_rem_type,
@ -111,16 +107,8 @@ class ODMMvsTexCell(ecto.Cell):
'toneMapping': self.params.tone_mapping 'toneMapping': self.params.tone_mapping
} }
if not args.use_pmvs: kwargs['nvm_file'] = io.join_paths(tree.opensfm,
kwargs['nvm_file'] = io.join_paths(tree.opensfm, "reconstruction.nvm")
"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))
# Make sure tmp directory is empty # Make sure tmp directory is empty
mvs_tmp_dir = os.path.join(r['out_dir'], 'tmp') mvs_tmp_dir = os.path.join(r['out_dir'], 'tmp')

Wyświetl plik

@ -10,8 +10,6 @@ from dataset import ODMLoadDatasetCell
from run_opensfm import ODMOpenSfMCell from run_opensfm import ODMOpenSfMCell
from smvs import ODMSmvsCell from smvs import ODMSmvsCell
from odm_slam import ODMSlamCell from odm_slam import ODMSlamCell
from pmvs import ODMPmvsCell
from cmvs import ODMCmvsCell
from odm_meshing import ODMeshingCell from odm_meshing import ODMeshingCell
from mvstex import ODMMvsTexCell from mvstex import ODMMvsTexCell
from odm_georeferencing import ODMGeoreferencingCell from odm_georeferencing import ODMGeoreferencingCell
@ -51,13 +49,6 @@ class ODMApp(ecto.BlackBox):
fixed_camera_params=p.args.use_fixed_camera_params, fixed_camera_params=p.args.use_fixed_camera_params,
hybrid_bundle_adjustment=p.args.use_hybrid_bundle_adjustment), hybrid_bundle_adjustment=p.args.use_hybrid_bundle_adjustment),
'slam': ODMSlamCell(), '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, 'smvs': ODMSmvsCell(alpha=p.args.smvs_alpha,
scale=p.args.smvs_scale, scale=p.args.smvs_scale,
threads=p.args.max_concurrency, threads=p.args.max_concurrency,
@ -135,7 +126,7 @@ class ODMApp(ecto.BlackBox):
self.args[:] >> self.smvs['args'], self.args[:] >> self.smvs['args'],
self.opensfm['reconstruction'] >> self.smvs['reconstruction']] 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'], connections += [self.tree[:] >> self.meshing['tree'],
self.args[:] >> self.meshing['args'], self.args[:] >> self.meshing['args'],
self.smvs['reconstruction'] >> self.meshing['reconstruction']] self.smvs['reconstruction'] >> self.meshing['reconstruction']]
@ -169,20 +160,14 @@ class ODMApp(ecto.BlackBox):
connections += [self.tree[:] >> self.slam['tree'], connections += [self.tree[:] >> self.slam['tree'],
self.args[:] >> self.slam['args']] self.args[:] >> self.slam['args']]
# run cmvs connections += [self.tree[:] >> self.smvs['tree'],
connections += [self.tree[:] >> self.cmvs['tree'], self.args[:] >> self.smvs['args'],
self.args[:] >> self.cmvs['args'], self.slam['reconstruction'] >> self.smvs['reconstruction']]
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']]
# create odm mesh # create odm mesh
connections += [self.tree[:] >> self.meshing['tree'], connections += [self.tree[:] >> self.meshing['tree'],
self.args[:] >> self.meshing['args'], self.args[:] >> self.meshing['args'],
self.pmvs['reconstruction'] >> self.meshing['reconstruction']] self.smvs['reconstruction'] >> self.meshing['reconstruction']]
# create odm texture # create odm texture
connections += [self.tree[:] >> self.texturing['tree'], connections += [self.tree[:] >> self.texturing['tree'],

Wyświetl plik

@ -92,13 +92,12 @@ class ODMGeoreferencingCell(ecto.Cell):
'verbose': verbose 'verbose': verbose
} }
if not args.use_pmvs:
if args.fast_orthophoto: if args.fast_orthophoto:
kwargs['pc'] = os.path.join(tree.opensfm, 'reconstruction.ply') kwargs['pc'] = os.path.join(tree.opensfm, 'reconstruction.ply')
else:
kwargs['pc'] = tree.opensfm_model
else: else:
kwargs['pc'] = tree.pmvs_model kwargs['pc'] = tree.opensfm_model
# Check to see if the GCP file exists # Check to see if the GCP file exists

Wyświetl plik

@ -39,7 +39,6 @@ class ODMSlamCell(ecto.Cell):
# create working directories # create working directories
system.mkdir_p(tree.opensfm) system.mkdir_p(tree.opensfm)
system.mkdir_p(tree.pmvs)
vocabulary = os.path.join(context.orb_slam2_path, vocabulary = os.path.join(context.orb_slam2_path,
'Vocabulary/ORBvoc.txt') 'Vocabulary/ORBvoc.txt')
@ -96,16 +95,5 @@ class ODMSlamCell(ecto.Cell):
'Found a valid Bundler file in: {}'.format( 'Found a valid Bundler file in: {}'.format(
tree.opensfm_reconstruction)) 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') log.ODM_INFO('Running OMD Slam Cell - Finished')
return ecto.OK if args.end_with != 'odm_slam' else ecto.QUIT return ecto.OK if args.end_with != 'odm_slam' else ecto.QUIT

Wyświetl plik

@ -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

Wyświetl plik

@ -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")

Wyświetl plik

@ -42,7 +42,6 @@ class ODMOpenSfMCell(ecto.Cell):
# create working directories # create working directories
system.mkdir_p(tree.opensfm) system.mkdir_p(tree.opensfm)
system.mkdir_p(tree.pmvs)
# check if we rerun cell or not # check if we rerun cell or not
rerun_cell = (args.rerun is not None and 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' % log.ODM_WARNING('Found a valid Bundler file in: %s' %
tree.opensfm_reconstruction) 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: if reconstruction.georef:
system.run('PYTHONPATH=%s %s/bin/opensfm export_geocoords %s --transformation --proj \'%s\'' % 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)) (context.pyopencv_path, context.opensfm_path, tree.opensfm, reconstruction.georef.projection.srs))

Wyświetl plik

@ -66,7 +66,7 @@ class ODMSmvsCell(ecto.Cell):
"-a%s" % self.params.alpha, "-a%s" % self.params.alpha,
"-s%s" % self.params.scale, "-s%s" % self.params.scale,
"-o%s" % self.params.output_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" % '-S' if self.params.shading else '',
"%s" % '-g' if self.params.gamma_srgb and self.params.shading else '', "%s" % '-g' if self.params.gamma_srgb and self.params.shading else '',
"--force" if rerun_cell else '' "--force" if rerun_cell else ''