diff --git a/SuperBuild/CMakeLists.txt b/SuperBuild/CMakeLists.txt index b3899d3c..40c85dcd 100644 --- a/SuperBuild/CMakeLists.txt +++ b/SuperBuild/CMakeLists.txt @@ -137,6 +137,7 @@ set(custom_libs OpenSfM MvsTexturing OpenMVS FPCFilter + PyPopsift ) # Build entwine only on Linux diff --git a/SuperBuild/cmake/External-FPCFilter.cmake b/SuperBuild/cmake/External-FPCFilter.cmake new file mode 100644 index 00000000..78f5c398 --- /dev/null +++ b/SuperBuild/cmake/External-FPCFilter.cmake @@ -0,0 +1,27 @@ +set(_proj_name fpcfilter) +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} + GIT_REPOSITORY https://github.com/OpenDroneMap/FPCFilter + GIT_TAG main + #--Update/Patch step---------- + UPDATE_COMMAND "" + #--Configure step------------- + SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name} + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -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 +) diff --git a/SuperBuild/cmake/External-PyPopsift.cmake b/SuperBuild/cmake/External-PyPopsift.cmake new file mode 100644 index 00000000..91d82f64 --- /dev/null +++ b/SuperBuild/cmake/External-PyPopsift.cmake @@ -0,0 +1,37 @@ +set(_SB_BINARY_DIR "${SB_BINARY_DIR}/pypopsift") + +# Pypopsift +find_package(CUDA 7.0) + +if(CUDA_FOUND) + ExternalProject_Add(pypopsift + DEPENDS opensfm + PREFIX ${_SB_BINARY_DIR} + TMP_DIR ${_SB_BINARY_DIR}/tmp + STAMP_DIR ${_SB_BINARY_DIR}/stamp + #--Download step-------------- + DOWNLOAD_DIR ${SB_DOWNLOAD_DIR} + GIT_REPOSITORY https://github.com/uav4geo/pypopsift + GIT_TAG 270 + #--Update/Patch step---------- + UPDATE_COMMAND "" + #--Configure step------------- + SOURCE_DIR ${SB_SOURCE_DIR}/pypopsift + CMAKE_ARGS + -DOUTPUT_DIR=${SB_INSTALL_DIR}/bin/opensfm/opensfm + -DCMAKE_INSTALL_PREFIX=${SB_INSTALL_DIR} + ${GPU_CMAKE_ARGS} + ${WIN32_CMAKE_ARGS} + ${ARM64_CMAKE_ARGS} + #--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 + ) +else() + message(WARNING "Could not find CUDA >= 7.0") +endif() \ No newline at end of file diff --git a/configure.sh b/configure.sh index 91d50b57..dc730102 100755 --- a/configure.sh +++ b/configure.sh @@ -130,7 +130,7 @@ installreqs() { set -e pip install --ignore-installed -r requirements.txt if [ ! -z "$GPU_INSTALL" ]; then - pip install --ignore-installed -r requirements.gpu.txt + # Nothing, add extra GPU-related build instructions here fi set +e } diff --git a/gpu.Dockerfile b/gpu.Dockerfile index ae959c89..c3c8f07c 100644 --- a/gpu.Dockerfile +++ b/gpu.Dockerfile @@ -37,9 +37,6 @@ COPY --from=builder /code /code # Copy the Python libraries installed via pip from the builder COPY --from=builder /usr/local /usr/local -# Install OpenCL Drivers -RUN apt update && apt install -y nvidia-opencl-icd-340 intel-opencl-icd - # Install shared libraries that we depend on via APT, but *not* # the -dev packages to save space! # Also run a smoke test diff --git a/opendm/config.py b/opendm/config.py index 11ef8189..64235308 100755 --- a/opendm/config.py +++ b/opendm/config.py @@ -162,9 +162,7 @@ def config(argv=None, parser=None): default=8, type=int, help='Number of nearest images to pre-match based on GPS ' - 'exif data. Set to 0 to skip pre-matching. ' - 'Neighbors works together with Distance parameter, ' - 'set both to 0 to not use pre-matching. Default: %(default)s') + 'exif data. Set to 0 to skip pre-matching. Default: %(default)s') parser.add_argument('--use-fixed-camera-params', action=StoreTrue, @@ -236,7 +234,7 @@ def config(argv=None, parser=None): action=StoreValue, default='incremental', choices=['incremental', 'triangulation'], - help=('Choose the structure from motion algorithm. If camera positions and angles are available, triangulation is significantly faster for aerial datasets. ' + help=('Choose the structure from motion algorithm. For aerial datasets, if camera GPS positions and angles are available, triangulation is faster. ' 'Can be one of: %(choices)s. Default: ' '%(default)s')) @@ -770,9 +768,9 @@ def config(argv=None, parser=None): if args.fast_orthophoto: log.ODM_INFO('Fast orthophoto is turned on, automatically setting --skip-3dmodel') args.skip_3dmodel = True - if not 'sfm_algorithm_is_set' in args: - log.ODM_INFO('Fast orthophoto is turned on, automatically setting --sfm-algorithm to triangulation') - args.sfm_algorithm = 'triangulation' + # if not 'sfm_algorithm_is_set' in args: + # log.ODM_INFO('Fast orthophoto is turned on, automatically setting --sfm-algorithm to triangulation') + # args.sfm_algorithm = 'triangulation' if args.pc_rectify and not args.pc_classify: log.ODM_INFO("Ground rectify is turned on, automatically turning on point cloud classification") diff --git a/opendm/gpu.py b/opendm/gpu.py index 53f7d1a9..670c0884 100644 --- a/opendm/gpu.py +++ b/opendm/gpu.py @@ -1,5 +1,6 @@ import os import sys +import shutil from opendm import log from repoze.lru import lru_cache @@ -7,38 +8,29 @@ def gpu_disabled_by_user(): return bool(os.environ.get('ODM_NO_GPU')) @lru_cache(maxsize=None) -def has_gpus(): +def has_popsift(): + try: + from opensfm import pypopsift + return True + except: + return False + +@lru_cache(maxsize=None) +def has_gpu(): if gpu_disabled_by_user(): log.ODM_INFO("Disabling GPU features (ODM_NO_GPU is set)") return False - try: - import pyopencl - except: - return False - - try: - platforms = pyopencl.get_platforms() - for p in platforms: - log.ODM_INFO("Found GPU device: %s" % p.name) - - return len(platforms) > 0 - except Exception as e: - return False - -@lru_cache(maxsize=None) -def windows_no_cuda(): - """ - Check if CUDA lib is available on Windows - Returns true if OS is windows and CUDA is not found. - """ if sys.platform == 'win32': nvcuda_path = os.path.join(os.environ.get('SYSTEMROOT'), 'system32', 'nvcuda.dll') if os.path.isfile(nvcuda_path): - return False + return True else: log.ODM_INFO("No CUDA drivers detected, using CPU") - return True + return False else: - return False - + if shutil.which('nvidia-smi') is not None: + return True + else: + log.ODM_INFO("nvidia-smi not found in PATH, using CPU") + return False diff --git a/opendm/osfm.py b/opendm/osfm.py index 5907e27a..8f99428b 100644 --- a/opendm/osfm.py +++ b/opendm/osfm.py @@ -21,7 +21,7 @@ from opensfm.actions import undistort from opensfm.dataset import DataSet from opensfm import report from opendm.multispectral import get_photos_by_band -from opendm.gpu import has_gpus +from opendm.gpu import has_popsift, has_gpu from opensfm import multiview, exif from opensfm.actions.export_geocoords import _transform @@ -164,6 +164,7 @@ class OSFMContext: "feature_min_frames: %s" % args.min_num_features, "processes: %s" % args.max_concurrency, "matching_gps_neighbors: %s" % args.matcher_neighbors, + "matching_gps_distance: 0", "matching_graph_rounds: 50", "optimize_camera_parameters: %s" % ('no' if args.use_fixed_camera_params or args.cameras else 'yes'), "reconstruction_algorithm: %s" % (args.sfm_algorithm), @@ -200,9 +201,8 @@ class OSFMContext: config.append("matcher_type: %s" % osfm_matchers[matcher_type]) # GPU acceleration? - if has_gpus() and feature_type == "SIFT" and (not 'min_num_features_is_set' in args): + if has_gpu() and has_popsift() and feature_type == "SIFT": log.ODM_INFO("Using GPU for extracting SIFT features") - log.ODM_INFO("--min-num-features will be ignored") feature_type = "SIFT_GPU" config.append("feature_type: %s" % feature_type) diff --git a/requirements.gpu.txt b/requirements.gpu.txt deleted file mode 100644 index 9b8e92e5..00000000 --- a/requirements.gpu.txt +++ /dev/null @@ -1,2 +0,0 @@ -silx>=0.12.0 -pyopencl==2021.1.1 \ No newline at end of file diff --git a/stages/dataset.py b/stages/dataset.py index 6b168948..8000c61d 100644 --- a/stages/dataset.py +++ b/stages/dataset.py @@ -173,3 +173,11 @@ class ODMLoadDatasetStage(types.ODM_Stage): else: args.boundary = None log.ODM_WARNING("Reconstruction is not georeferenced, but boundary file provided (will ignore boundary file)") + + # If sfm-algorithm is triangulation, check if photos have OPK + if args.sfm_algorithm == 'triangulation': + for p in photos: + if not p.has_opk(): + log.ODM_WARNING("No omega/phi/kappa angles found in input photos (%s), switching sfm-algorithm to incremental" % p.filename) + args.sfm_algorithm = 'incremental' + break diff --git a/stages/openmvs.py b/stages/openmvs.py index 0fa6886c..179a74ce 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -6,7 +6,7 @@ from opendm import system from opendm import context from opendm import point_cloud from opendm import types -from opendm.gpu import gpu_disabled_by_user, windows_no_cuda +from opendm.gpu import has_gpu from opendm.utils import get_depthmap_resolution from opendm.osfm import OSFMContext from opendm.multispectral import get_primary_band_name @@ -73,7 +73,7 @@ class ODMOpenMVSStage(types.ODM_Stage): gpu_config = [] - if gpu_disabled_by_user() or windows_no_cuda(): + if not has_gpu(): gpu_config.append("--cuda-device -1") if args.pc_tile: