diff --git a/SuperBuild/cmake/External-OpenSfM.cmake b/SuperBuild/cmake/External-OpenSfM.cmake index fefd20cf..049322a7 100644 --- a/SuperBuild/cmake/External-OpenSfM.cmake +++ b/SuperBuild/cmake/External-OpenSfM.cmake @@ -8,15 +8,17 @@ ExternalProject_Add(${_proj_name} STAMP_DIR ${_SB_BINARY_DIR}/stamp #--Download step-------------- DOWNLOAD_DIR ${SB_DOWNLOAD_DIR} - URL https://github.com/mapillary/OpenSfM/archive/a4b07056aec1184692c1432fbdd1074710aec32b.zip - URL_MD5 42B2B1994C3309BBF4525C8CC1F6F741 + URL https://github.com/mapillary/OpenSfM/archive/odm-3.zip + URL_MD5 a0853c6fe6d8193f7006e17dd896154e #--Update/Patch step---------- UPDATE_COMMAND "" #--Configure step------------- SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name} CONFIGURE_COMMAND cmake /${_proj_name}/src - -DCERES_ROOT_DIR=${SB_INSTALL_DIR} + -DCERES_ROOT_DIR=${SB_INSTALL_DIR} -DOpenCV_DIR=${SB_INSTALL_DIR}/share/OpenCV + -DOPENSFM_BUILD_TESTS=off + #--Build step----------------- BINARY_DIR ${_SB_BINARY_DIR} #--Install step--------------- diff --git a/configure.sh b/configure.sh index dbebd753..b2049791 100755 --- a/configure.sh +++ b/configure.sh @@ -1,5 +1,10 @@ #!/bin/bash +## Set up library paths +RUNPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export PYTHONPATH=$RUNPATH/SuperBuild/install/lib/python2.7/dist-packages:$RUNPATH/SuperBuild/src/opensfm:$PYTHONPATH +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$RUNPATH/SuperBuild/install/lib + ## Before installing echo "Updating the system" sudo apt-get update @@ -53,7 +58,8 @@ sudo apt-get install -y -qq python-networkx \ libboost-regex-dev \ libboost-python-dev \ libboost-date-time-dev \ - libboost-thread-dev + libboost-thread-dev \ + python-pyproj sudo pip install -U PyYAML \ exifread \ diff --git a/opendm/config.py b/opendm/config.py index 11153e06..e8d84589 100644 --- a/opendm/config.py +++ b/opendm/config.py @@ -110,6 +110,12 @@ def config(): 'images based on GPS exif data. Set to 0 to skip ' 'pre-matching. Default: %(default)s') + parser.add_argument('--use-opensfm-pointcloud', + action='store_true', + default=False, + help='Use OpenSfM to compute the point cloud instead ' + 'of PMVS') + parser.add_argument('--cmvs-maxImages', metavar='', default=500, diff --git a/opendm/types.py b/opendm/types.py index 581d5753..edf524c3 100644 --- a/opendm/types.py +++ b/opendm/types.py @@ -360,6 +360,7 @@ class ODM_Tree(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') + self.opensfm_model = io.join_paths(self.opensfm, 'depthmaps/merged.ply') # pmvs self.pmvs_rec_path = io.join_paths(self.pmvs, 'recon0') diff --git a/scripts/mvstex.py b/scripts/mvstex.py index dcc58625..2d9f4df0 100644 --- a/scripts/mvstex.py +++ b/scripts/mvstex.py @@ -84,14 +84,18 @@ class ODMMvsTexCell(ecto.Cell): 'skipHoleFilling': skipHoleFilling, 'keepUnseenFaces': keepUnseenFaces } - - 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)) - + + if args.use_opensfm_pointcloud: + 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)) + # run texturing binary system.run('{bin} {nvm_file} {model} {out_dir} ' '-d {dataTerm} -o {outlierRemovalType} ' diff --git a/scripts/odm_app.py b/scripts/odm_app.py index 008049a9..730165d9 100644 --- a/scripts/odm_app.py +++ b/scripts/odm_app.py @@ -110,20 +110,26 @@ class ODMApp(ecto.BlackBox): self.args[:] >> self.opensfm['args'], self.resize['photos'] >> self.opensfm['photos']] - # run cmvs - connections += [self.tree[:] >> self.cmvs['tree'], - self.args[:] >> self.cmvs['args'], - self.opensfm['reconstruction'] >> self.cmvs['reconstruction']] + if _p.args.use_opensfm_pointcloud: + # create odm mesh from opensfm point cloud + connections += [self.tree[:] >> self.meshing['tree'], + self.args[:] >> self.meshing['args'], + self.opensfm['reconstruction'] >> self.meshing['reconstruction']] + else: + # run cmvs + connections += [self.tree[:] >> self.cmvs['tree'], + self.args[:] >> self.cmvs['args'], + self.opensfm['reconstruction'] >> self.cmvs['reconstruction']] - # run pmvs - connections += [self.tree[:] >> self.pmvs['tree'], - self.args[:] >> self.pmvs['args'], - self.cmvs['reconstruction'] >> self.pmvs['reconstruction']] + # run pmvs + connections += [self.tree[:] >> self.pmvs['tree'], + self.args[:] >> self.pmvs['args'], + self.cmvs['reconstruction'] >> self.pmvs['reconstruction']] - # create odm mesh - connections += [self.tree[:] >> self.meshing['tree'], - self.args[:] >> self.meshing['args'], - self.pmvs['reconstruction'] >> self.meshing['reconstruction']] + # create odm mesh from pmvs point cloud + connections += [self.tree[:] >> self.meshing['tree'], + self.args[:] >> self.meshing['args'], + self.pmvs['reconstruction'] >> self.meshing['reconstruction']] # create odm texture connections += [self.tree[:] >> self.texturing['tree'], diff --git a/scripts/odm_georeferencing.py b/scripts/odm_georeferencing.py index 516069ac..dea64e6f 100644 --- a/scripts/odm_georeferencing.py +++ b/scripts/odm_georeferencing.py @@ -85,7 +85,6 @@ class ODMGeoreferencingCell(ecto.Cell): '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_georeferencing_model_ply_geo, @@ -95,6 +94,10 @@ class ODMGeoreferencingCell(ecto.Cell): 'gcp': gcpfile, } + if args.use_opensfm_pointcloud: + kwargs['pc'] = tree.opensfm_model + else: + kwargs['pc'] = tree.pmvs_model if self.params.use_gcp and \ io.file_exists(gcpfile): diff --git a/scripts/odm_meshing.py b/scripts/odm_meshing.py index dc8f1973..1db4dab8 100644 --- a/scripts/odm_meshing.py +++ b/scripts/odm_meshing.py @@ -52,7 +52,6 @@ class ODMeshingCell(ecto.Cell): 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, @@ -60,6 +59,10 @@ class ODMeshingCell(ecto.Cell): 'samples': self.params.samples, 'solver': self.params.solver } + if args.use_opensfm_pointcloud: + kwargs['infile'] = tree.opensfm_model + else: + kwargs['infile'] = tree.pmvs_model # run meshing binary system.run('{bin}/odm_meshing -inputFile {infile} ' diff --git a/scripts/opensfm.py b/scripts/opensfm.py index 21d80221..4f99f539 100644 --- a/scripts/opensfm.py +++ b/scripts/opensfm.py @@ -48,9 +48,13 @@ class ODMOpenSfMCell(ecto.Cell): (args.rerun_from is not None and 'opensfm' in args.rerun_from) - # check if reconstruction was done before + if args.use_opensfm_pointcloud: + output_file = tree.opensfm_model + else: + output_file = tree.opensfm_reconstruction - if not io.file_exists(tree.opensfm_reconstruction) or rerun_cell: + # check if reconstruction was done before + if not io.file_exists(output_file) or rerun_cell: # create file list list_path = io.join_paths(tree.opensfm, 'image_list.txt') with open(list_path, 'w') as fout: @@ -77,12 +81,18 @@ class ODMOpenSfMCell(ecto.Cell): # run OpenSfM reconstruction system.run('PYTHONPATH=%s %s/bin/run_all %s' % (context.pyopencv_path, context.opensfm_path, tree.opensfm)) + if args.use_opensfm_pointcloud: + system.run('PYTHONPATH=%s %s/bin/opensfm export_visualsfm %s' % + (context.pyopencv_path, context.opensfm_path, tree.opensfm)) + system.run('PYTHONPATH=%s %s/bin/opensfm undistort %s' % + (context.pyopencv_path, context.opensfm_path, tree.opensfm)) + system.run('PYTHONPATH=%s %s/bin/opensfm compute_depthmaps %s' % + (context.pyopencv_path, context.opensfm_path, tree.opensfm)) else: log.ODM_WARNING('Found a valid OpenSfM file in: %s' % tree.opensfm_reconstruction) # check if reconstruction was exported to bundler before - if not io.file_exists(tree.opensfm_bundle_list) or rerun_cell: # convert back to bundler's format system.run('PYTHONPATH=%s %s/bin/export_bundler %s' % @@ -91,17 +101,17 @@ class ODMOpenSfMCell(ecto.Cell): log.ODM_WARNING('Found a valid Bundler file in: %s' % tree.opensfm_reconstruction) - # check if reconstruction was exported to pmvs before + if not args.use_opensfm_pointcloud: + # 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 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 args.time: - system.benchmark(start_time, tree.benchmarking, 'OpenSfM') + if args.time: + system.benchmark(start_time, tree.benchmarking, 'OpenSfM') log.ODM_INFO('Running ODM OpenSfM Cell - Finished') return ecto.OK if args.end_with != 'opensfm' else ecto.QUIT