From 3b2a60737ca75eb5b3b770e8aa3d074d6a695dca Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Tue, 9 Feb 2021 12:14:05 -0500 Subject: [PATCH 1/8] Add gpu dockerfile --- SuperBuild/cmake/External-OpenSfM.cmake | 2 +- gpu.Dockerfile | 46 +++++++++++++++++++++++++ opendm/gpu.py | 15 ++++++++ opendm/osfm.py | 10 +++++- requirements.txt | 2 ++ start-dev-env.sh | 10 +++++- 6 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 gpu.Dockerfile create mode 100644 opendm/gpu.py diff --git a/SuperBuild/cmake/External-OpenSfM.cmake b/SuperBuild/cmake/External-OpenSfM.cmake index 1ccc5ed0..058caef5 100644 --- a/SuperBuild/cmake/External-OpenSfM.cmake +++ b/SuperBuild/cmake/External-OpenSfM.cmake @@ -9,7 +9,7 @@ ExternalProject_Add(${_proj_name} #--Download step-------------- DOWNLOAD_DIR ${SB_DOWNLOAD_DIR} GIT_REPOSITORY https://github.com/OpenDroneMap/OpenSfM/ - GIT_TAG 241 + GIT_TAG gpu #--Update/Patch step---------- UPDATE_COMMAND git submodule update --init --recursive #--Configure step------------- diff --git a/gpu.Dockerfile b/gpu.Dockerfile new file mode 100644 index 00000000..752bdad0 --- /dev/null +++ b/gpu.Dockerfile @@ -0,0 +1,46 @@ +FROM nvidia/cuda:11.2.0-runtime-ubuntu20.04 AS builder + +# Env variables +ENV DEBIAN_FRONTEND=noninteractive \ + PYTHONPATH="$PYTHONPATH:/code/SuperBuild/install/lib/python3.8/dist-packages:/code/SuperBuild/src/opensfm" \ + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/code/SuperBuild/install/lib" + +# Prepare directories +WORKDIR /code + +# Copy everything +COPY . ./ + +# Run the build +RUN bash configure.sh install + +# Clean Superbuild +RUN bash configure.sh clean + +### END Builder + +### Use a second image for the final asset to reduce the number and +# size of the layers. +FROM nvidia/cuda:11.2.0-runtime-ubuntu20.04 + +# Env variables +ENV DEBIAN_FRONTEND=noninteractive \ + PYTHONPATH="$PYTHONPATH:/code/SuperBuild/install/lib/python3.8/dist-packages:/code/SuperBuild/src/opensfm" \ + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/code/SuperBuild/install/lib" + +WORKDIR /code + +# Copy everything we built from the builder +COPY --from=builder /code /code + +# Copy the Python libraries installed via pip from the builder +COPY --from=builder /usr/local /usr/local + +# Install shared libraries that we depend on via APT, but *not* +# the -dev packages to save space! +RUN bash configure.sh installruntimedepsonly \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Entry point +ENTRYPOINT ["python3", "/code/run.py"] diff --git a/opendm/gpu.py b/opendm/gpu.py new file mode 100644 index 00000000..173010c2 --- /dev/null +++ b/opendm/gpu.py @@ -0,0 +1,15 @@ +import os, subprocess +from opendm import log +from repoze.lru import lru_cache + +@lru_cache(maxsize=None) +def has_gpus(): + import pyopencl + + try: + platforms = pyopencl.get_platforms() + print(platforms) + exit(1) + except Exception as e: + print(e) + return False diff --git a/opendm/osfm.py b/opendm/osfm.py index a3a48342..5abebfc0 100644 --- a/opendm/osfm.py +++ b/opendm/osfm.py @@ -17,7 +17,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 class OSFMContext: def __init__(self, opensfm_project_path): @@ -214,6 +214,14 @@ class OSFMContext: log.ODM_WARNING("Using BOW matching, will use HAHOG feature type, not SIFT") feature_type = "HAHOG" + # GPU acceleration? + if has_gpus() and feature_type == "SIFT": + log.ODM_INFO("Using GPU for extracting SIFT features") + feature_type = "SIFT_GPU" + + # TODO: REMOVE + config.append("matcher_type: SIFT_GPU") + config.append("feature_type: %s" % feature_type) if has_alt: diff --git a/requirements.txt b/requirements.txt index 8c852e51..783f4d04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,3 +27,5 @@ scikit-image==0.17.2 scipy==1.5.4 xmltodict==0.12.0 fpdf2==2.2.0rc2 +silx>=0.12.0 +pyopencl==2021.1.1 \ No newline at end of file diff --git a/start-dev-env.sh b/start-dev-env.sh index 323b9fe5..ff832d6e 100755 --- a/start-dev-env.sh +++ b/start-dev-env.sh @@ -90,6 +90,7 @@ fi export PORT="${PORT:=3000}" export QTC="${QTC:=NO}" export IMAGE="${IMAGE:=opendronemap/nodeodm}" +export GPU="${GPU:=NO}" if [ -z "$DATA" ]; then echo "Usage: DATA=/path/to/datasets [VARS] $0" @@ -98,6 +99,7 @@ if [ -z "$DATA" ]; then echo " DATA Path to directory that contains datasets for testing. The directory will be mounted in /datasets. If you don't have any, simply set it to a folder outside the ODM repository." echo " PORT Port to expose for NodeODM (default: $PORT)" echo " IMAGE Docker image to use (default: $IMAGE)" + echo " GPU Enable GPU support (default: $GPU)" echo " QTC When set to YES, installs QT Creator for C++ development (default: $QTC)" exit 1 fi @@ -108,6 +110,7 @@ echo "Datasets path: $DATA" echo "Expose port: $PORT" echo "QT Creator: $QTC" echo "Image: $IMAGE" +echo "GPU: $GPU" if [ ! -e "$HOME"/.odm-dev-home ]; then mkdir -p "$HOME"/.odm-dev-home @@ -116,6 +119,11 @@ fi USER_ID=$(id -u) GROUP_ID=$(id -g) USER=$(id -un) +GPU_FLAG="" +if [[ "$GPU" != "NO" ]]; then + GPU_FLAG="--gpus all" +fi + xhost + || true -docker run -ti --entrypoint bash --name odmdev -v $(pwd):/code -v "$DATA":/datasets -p $PORT:3000 --privileged -e DISPLAY -e LANG=C.UTF-8 -e LC_ALL=C.UTF-8 -v="/tmp/.X11-unix:/tmp/.X11-unix:rw" -v="$HOME/.odm-dev-home:/home/$USER" $IMAGE -c "/code/start-dev-env.sh --setup $USER $USER_ID $GROUP_ID $QTC" +docker run -ti --entrypoint bash --name odmdev -v $(pwd):/code -v "$DATA":/datasets -p $PORT:3000 $GPU_FLAG --privileged -e DISPLAY -e LANG=C.UTF-8 -e LC_ALL=C.UTF-8 -v="/tmp/.X11-unix:/tmp/.X11-unix:rw" -v="$HOME/.odm-dev-home:/home/$USER" $IMAGE -c "/code/start-dev-env.sh --setup $USER $USER_ID $GROUP_ID $QTC" exit 0 From 1f2d93fd0da775cbf9b3a3a6e85ca96f3223bdb9 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Wed, 10 Feb 2021 14:00:43 -0500 Subject: [PATCH 2/8] Dockerfile gpu changes --- gpu.Dockerfile | 3 +++ opendm/gpu.py | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/gpu.Dockerfile b/gpu.Dockerfile index 752bdad0..bd1069a2 100644 --- a/gpu.Dockerfile +++ b/gpu.Dockerfile @@ -36,6 +36,9 @@ 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 install nvidia-opencl-icd-340 intel-opencl-icd + # Install shared libraries that we depend on via APT, but *not* # the -dev packages to save space! RUN bash configure.sh installruntimedepsonly \ diff --git a/opendm/gpu.py b/opendm/gpu.py index 173010c2..df288f9b 100644 --- a/opendm/gpu.py +++ b/opendm/gpu.py @@ -1,4 +1,3 @@ -import os, subprocess from opendm import log from repoze.lru import lru_cache @@ -8,8 +7,9 @@ def has_gpus(): try: platforms = pyopencl.get_platforms() - print(platforms) - exit(1) + for p in platforms: + log.ODM_INFO("Found GPU device: %s" % p.name) + + return len(platforms) > 0 except Exception as e: - print(e) return False From 5e3737a3f5c263304dddd7b52c9806607a96cf01 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Fri, 5 Mar 2021 10:44:08 -0500 Subject: [PATCH 3/8] GitHub GPU workflow, use SIFT_GPU when GPUs are available --- .github/workflows/publish-docker-gpu.yaml | 32 +++++++++++++++++++++++ configure.sh | 3 +++ gpu.Dockerfile | 2 +- opendm/gpu.py | 6 ++++- opendm/osfm.py | 4 +-- requirements.gpu.txt | 2 ++ requirements.txt | 2 -- 7 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/publish-docker-gpu.yaml create mode 100644 requirements.gpu.txt diff --git a/.github/workflows/publish-docker-gpu.yaml b/.github/workflows/publish-docker-gpu.yaml new file mode 100644 index 00000000..f0c07dcb --- /dev/null +++ b/.github/workflows/publish-docker-gpu.yaml @@ -0,0 +1,32 @@ +name: Publish Docker GPU Images + +on: + push: + branches: + - master + tags: + - v* + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push Docker image + id: docker_build + uses: docker/build-push-action@v2 + with: + file: ./gpu.Dockerfile + platforms: linux/amd64 + push: true + tags: opendronemap/odm:gpu diff --git a/configure.sh b/configure.sh index e5158848..4b3ff049 100755 --- a/configure.sh +++ b/configure.sh @@ -120,6 +120,9 @@ install() { pip install --ignore-installed -r requirements.txt + if [ ! -z "$GPU_INSTALL" ]; then + pip install --ignore-installed -r requirements.gpu.txt + fi if [ ! -z "$PORTABLE_INSTALL" ]; then echo "Replacing g++ and gcc with our scripts for portability..." diff --git a/gpu.Dockerfile b/gpu.Dockerfile index bd1069a2..63cedef0 100644 --- a/gpu.Dockerfile +++ b/gpu.Dockerfile @@ -12,7 +12,7 @@ WORKDIR /code COPY . ./ # Run the build -RUN bash configure.sh install +RUN PORTABLE_INSTALL=YES GPU_INSTALL=YES bash configure.sh install # Clean Superbuild RUN bash configure.sh clean diff --git a/opendm/gpu.py b/opendm/gpu.py index df288f9b..4284705d 100644 --- a/opendm/gpu.py +++ b/opendm/gpu.py @@ -3,7 +3,11 @@ from repoze.lru import lru_cache @lru_cache(maxsize=None) def has_gpus(): - import pyopencl + try: + import pyopencl + except: + log.ODM_INFO("PyOpenCL is missing (not a GPU build)") + return False try: platforms = pyopencl.get_platforms() diff --git a/opendm/osfm.py b/opendm/osfm.py index 5abebfc0..0d4fb751 100644 --- a/opendm/osfm.py +++ b/opendm/osfm.py @@ -217,10 +217,8 @@ class OSFMContext: # GPU acceleration? if has_gpus() 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" - - # TODO: REMOVE - config.append("matcher_type: SIFT_GPU") config.append("feature_type: %s" % feature_type) diff --git a/requirements.gpu.txt b/requirements.gpu.txt new file mode 100644 index 00000000..9b8e92e5 --- /dev/null +++ b/requirements.gpu.txt @@ -0,0 +1,2 @@ +silx>=0.12.0 +pyopencl==2021.1.1 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 783f4d04..8c852e51 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,5 +27,3 @@ scikit-image==0.17.2 scipy==1.5.4 xmltodict==0.12.0 fpdf2==2.2.0rc2 -silx>=0.12.0 -pyopencl==2021.1.1 \ No newline at end of file From 0049e87c48637c936ae43b2ee6887f6925b5b345 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Fri, 5 Mar 2021 11:03:19 -0500 Subject: [PATCH 4/8] Fix version file --- VERSION | 4 ---- 1 file changed, 4 deletions(-) diff --git a/VERSION b/VERSION index fdf19d76..7bf4b6a8 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1 @@ -<<<<<<< HEAD 2.4.6 -======= -2.4.5 ->>>>>>> origin/master From eb193465e697160b7f436ae4b161d7b38f329974 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Fri, 5 Mar 2021 20:41:46 +0000 Subject: [PATCH 5/8] Remove new line --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7bf4b6a8..62e64205 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.6 +2.4.6 \ No newline at end of file From 56162ec4a89665f49ae454d6bcf6d4fa7fd0f92e Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Mon, 15 Mar 2021 15:35:42 +0000 Subject: [PATCH 6/8] Update README --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index c9736bf6..6d0955f4 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,46 @@ snap run opendronemap Snap packages will be kept up-to-date automatically, so you don't need to update ODM manually. +## GPU Acceleration + +ODM has support for doing SIFT feature extraction on a GPU, which is about 2x faster CPU on a consumer laptop. To use this feature, you need to use the `opendronemap/odm:gpu` docker image instead of `opendronemap/odm` and you need to pass the `--gpus all` flag to docker: + +``` +docker run -ti --rm -v c:/Users/youruser/datasets:/datasets --gpus all opendronemap/odm:gpu --project-path /datasets project +``` + +When you run ODM, if the GPU is recognized, in the first few lines of the output log you should notice: + +``` +[INFO] Writing exif overrides +[INFO] Maximum photo dimensions: 4000px +[INFO] Found GPU device: Intel(R) OpenCL HD Graphics +[INFO] Using GPU for extracting SIFT features +``` + +The implementation is OpenCL-based, so the acceleration should work with most graphics card (not just NVIDIA). + +If you have an NVIDIA card, you can test that docker is recognizing the GPU by running: + +``` +docker run --rm --gpus all nvidia/cuda:10.0-base nvidia-smi +``` + +If you see an output that looks like this: + +``` +Fri Jul 24 18:51:55 2020 ++-----------------------------------------------------------------------------+ +| NVIDIA-SMI 440.82 Driver Version: 440.82 CUDA Version: 10.2 | +|-------------------------------+----------------------+----------------------+ +| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | +| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | +``` + +You're in good shape! + +See https://github.com/NVIDIA/nvidia-docker and https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#docker for information on docker/NVIDIA setup. + ## WSL or WSL2 Install Note: This requires that you have installed WSL already by following [the instructions on Microsoft's Website](https://docs.microsoft.com/en-us/windows/wsl/install-win10). From 409c70f03e10f5b6f2725bd23cff9f6e9eacd6c9 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Mon, 15 Mar 2021 16:00:21 +0000 Subject: [PATCH 7/8] Fix GPU dockerfile --- gpu.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpu.Dockerfile b/gpu.Dockerfile index 63cedef0..732427e3 100644 --- a/gpu.Dockerfile +++ b/gpu.Dockerfile @@ -37,7 +37,7 @@ COPY --from=builder /code /code COPY --from=builder /usr/local /usr/local # Install OpenCL Drivers -RUN apt install nvidia-opencl-icd-340 intel-opencl-icd +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! From 874479231efa3ba705535427766d71f2eb869054 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Mon, 15 Mar 2021 12:34:04 -0400 Subject: [PATCH 8/8] Bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 62e64205..e30309f7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.6 \ No newline at end of file +2.4.7