From 660767730a24083d437b53103218bc6c8cd96871 Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Sun, 24 Mar 2024 21:27:37 -0400 Subject: [PATCH 01/82] python3.12 support --- .github/workflows/macos.yml | 8 +++++-- .github/workflows/ubuntu-wheel.yml | 12 ++++++++-- .github/workflows/windows.yml | 8 +++++-- CHANGELOG.md | 1 + docker/Dockerfile.wheel | 2 +- docker/README.md | 2 +- docker/docker_build.sh | 36 +++++++++++++++++++++++++++++- docker/docker_test.sh | 24 ++++++++++++++++++++ docs/arm.rst | 1 + docs/getting_started.in.rst | 5 +++++ python/README.rst | 1 + python/setup.py | 1 + util/ci_utils.sh | 6 ++--- 13 files changed, 95 insertions(+), 12 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 00bd9636130..77771a95a7a 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -127,7 +127,7 @@ jobs: fail-fast: false # https://github.community/t/how-to-conditionally-include-exclude-items-in-matrix-eg-based-on-branch/16853/6 matrix: - python_version: ['3.8', '3.9', '3.10', '3.11'] + python_version: ['3.8', '3.9', '3.10', '3.11', '3.12'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: @@ -137,6 +137,8 @@ jobs: python_version: '3.9' - is_main: false python_version: '3.10' + - is_main: false + python_version: '3.11' env: BUILD_CUDA_MODULE: OFF @@ -234,7 +236,7 @@ jobs: strategy: fail-fast: false matrix: - python_version: ['3.8', '3.9', '3.10', '3.11'] + python_version: ['3.8', '3.9', '3.10', '3.11', '3.12'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: @@ -244,6 +246,8 @@ jobs: python_version: '3.9' - is_main: false python_version: '3.10' + - is_main: false + python_version: '3.11' env: OPEN3D_ML_ROOT: ${{ github.workspace }}/Open3D-ML diff --git a/.github/workflows/ubuntu-wheel.yml b/.github/workflows/ubuntu-wheel.yml index a798762c965..e7da0ea3fa5 100644 --- a/.github/workflows/ubuntu-wheel.yml +++ b/.github/workflows/ubuntu-wheel.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - python_version: ['3.8', '3.9', '3.10', '3.11'] + python_version: ['3.8', '3.9', '3.10', '3.11', '3.12'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: @@ -40,6 +40,8 @@ jobs: python_version: '3.9' - is_main: false python_version: '3.10' + - is_main: false + python_version: '3.11' env: DEVELOPER_BUILD: ${{ github.event.inputs.developer_build || 'ON' }} PYTHON_VERSION: ${{ matrix.python_version }} @@ -64,6 +66,8 @@ jobs: docker/docker_build.sh cuda_wheel_py310_dev elif [ "${{ env.PYTHON_VERSION }}" = "3.11" ] && [ "${{ env.DEVELOPER_BUILD }}" = "ON" ]; then docker/docker_build.sh cuda_wheel_py311_dev + elif [ "${{ env.PYTHON_VERSION }}" = "3.12" ] && [ "${{ env.DEVELOPER_BUILD }}" = "ON" ]; then + docker/docker_build.sh cuda_wheel_py312_dev elif [ "${{ env.PYTHON_VERSION }}" = "3.8" ] && [ "${{ env.DEVELOPER_BUILD }}" = "OFF" ]; then docker/docker_build.sh cuda_wheel_py38 elif [ "${{ env.PYTHON_VERSION }}" = "3.9" ] && [ "${{ env.DEVELOPER_BUILD }}" = "OFF" ]; then @@ -72,6 +76,8 @@ jobs: docker/docker_build.sh cuda_wheel_py310 elif [ "${{ env.PYTHON_VERSION }}" = "3.11" ] && [ "${{ env.DEVELOPER_BUILD }}" = "OFF" ]; then docker/docker_build.sh cuda_wheel_py311 + elif [ "${{ env.PYTHON_VERSION }}" = "3.12" ] && [ "${{ env.DEVELOPER_BUILD }}" = "OFF" ]; then + docker/docker_build.sh cuda_wheel_py312 fi PIP_PKG_NAME="$(basename ${GITHUB_WORKSPACE}/open3d-[0-9]*.whl)" PIP_CPU_PKG_NAME="$(basename ${GITHUB_WORKSPACE}/open3d_cpu*.whl)" @@ -117,7 +123,7 @@ jobs: strategy: fail-fast: false matrix: - python_version: ['3.8', '3.9', '3.10', '3.11'] + python_version: ['3.8', '3.9', '3.10', '3.11', '3.12'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: @@ -127,6 +133,8 @@ jobs: python_version: '3.9' - is_main: false python_version: '3.10' + - is_main: false + python_version: '3.11' env: OPEN3D_ML_ROOT: ${{ github.workspace }}/Open3D-ML steps: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d687f6b7e33..9ca74f0f657 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -242,7 +242,7 @@ jobs: fail-fast: false # https://github.community/t/how-to-conditionally-include-exclude-items-in-matrix-eg-based-on-branch/16853/6 matrix: - python_version: ['3.8', '3.9', '3.10', '3.11'] + python_version: ['3.8', '3.9', '3.10', '3.11', '3.12'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: @@ -252,6 +252,8 @@ jobs: python_version: '3.9' - is_main: false python_version: '3.10' + - is_main: false + python_version: '3.11' steps: - name: Checkout source code @@ -347,7 +349,7 @@ jobs: strategy: fail-fast: false matrix: - python_version: ['3.8', '3.9', '3.10', '3.11'] + python_version: ['3.8', '3.9', '3.10', '3.11', '3.12'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: @@ -357,6 +359,8 @@ jobs: python_version: '3.9' - is_main: false python_version: '3.10' + - is_main: false + python_version: '3.11' steps: - name: Checkout source code diff --git a/CHANGELOG.md b/CHANGELOG.md index 3509dda95e6..b8d71ddd4f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ - Fix regression in printing cuda tensor from PR #6444 🐛 - Add Python pathlib support for file IO (PR #6619) - Fix log error message for `probability` argument validation in `PointCloud::SegmentPlane` (PR #6622) +- Python 3.12 support ## 0.13 diff --git a/docker/Dockerfile.wheel b/docker/Dockerfile.wheel index 6a7d7080aed..98ec8d52be5 100644 --- a/docker/Dockerfile.wheel +++ b/docker/Dockerfile.wheel @@ -1,5 +1,5 @@ # FROM must be called before other ARGS except for ARG BASE_IMAGE -ARG BASE_IMAGE=nvidia/cuda:11.7.1-cudnn8-devel-ubuntu18.04 +ARG BASE_IMAGE=nvidia/cuda:11.8.0-cudnn8-devel-ubuntu18.04 FROM ${BASE_IMAGE} # Customizable build arguments from cuda.yml diff --git a/docker/README.md b/docker/README.md index d14d9521c19..732eebf4bda 100644 --- a/docker/README.md +++ b/docker/README.md @@ -26,7 +26,7 @@ to install Nvidia Docker to run the CUDA container. To verify that the Nvidia Docker is working, run: ```bash -docker run --rm --gpus all nvidia/cuda:11.7-base nvidia-smi +docker run --rm --gpus all nvidia/cuda:11.8-base nvidia-smi ``` ### ARM64 Docker diff --git a/docker/docker_build.sh b/docker/docker_build.sh index 33ab6ad0115..b78d56de73d 100755 --- a/docker/docker_build.sh +++ b/docker/docker_build.sh @@ -27,20 +27,24 @@ OPTION: openblas-amd64-py39-dev : OpenBLAS AMD64 3.9 wheel, developer mode openblas-amd64-py310-dev : OpenBLAS AMD64 3.10 wheel, developer mode openblas-amd64-py311-dev : OpenBLAS AMD64 3.11 wheel, developer mode + openblas-amd64-py312-dev : OpenBLAS AMD64 3.12 wheel, developer mode openblas-amd64-py38 : OpenBLAS AMD64 3.8 wheel, release mode openblas-amd64-py39 : OpenBLAS AMD64 3.9 wheel, release mode openblas-amd64-py310 : OpenBLAS AMD64 3.10 wheel, release mode openblas-amd64-py311 : OpenBLAS AMD64 3.11 wheel, release mode + openblas-amd64-py312 : OpenBLAS AMD64 3.12 wheel, release mode # OpenBLAS ARM64 (Dockerfile.openblas) openblas-arm64-py38-dev : OpenBLAS ARM64 3.8 wheel, developer mode openblas-arm64-py39-dev : OpenBLAS ARM64 3.9 wheel, developer mode openblas-arm64-py310-dev : OpenBLAS ARM64 3.10 wheel, developer mode openblas-arm64-py311-dev : OpenBLAS ARM64 3.11 wheel, developer mode + openblas-arm64-py312-dev : OpenBLAS ARM64 3.12 wheel, developer mode openblas-arm64-py38 : OpenBLAS ARM64 3.8 wheel, release mode openblas-arm64-py39 : OpenBLAS ARM64 3.9 wheel, release mode openblas-arm64-py310 : OpenBLAS ARM64 3.10 wheel, release mode openblas-arm64-py311 : OpenBLAS ARM64 3.11 wheel, release mode + openblas-arm64-py312 : OpenBLAS ARM64 3.12 wheel, release mode # Ubuntu CPU CI (Dockerfile.ci) cpu-static : Ubuntu CPU static @@ -66,10 +70,12 @@ OPTION: cuda_wheel_py39_dev : CUDA Python 3.9 wheel, developer mode cuda_wheel_py310_dev : CUDA Python 3.10 wheel, developer mode cuda_wheel_py311_dev : CUDA Python 3.11 wheel, developer mode + cuda_wheel_py312_dev : CUDA Python 3.12 wheel, developer mode cuda_wheel_py38 : CUDA Python 3.8 wheel, release mode cuda_wheel_py39 : CUDA Python 3.9 wheel, release mode cuda_wheel_py310 : CUDA Python 3.10 wheel, release mode cuda_wheel_py311 : CUDA Python 3.11 wheel, release mode + cuda_wheel_py312 : CUDA Python 3.12 wheel, release mode " HOST_OPEN3D_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. >/dev/null 2>&1 && pwd)" @@ -78,7 +84,7 @@ HOST_OPEN3D_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. >/dev/null 2>&1 && pw CCACHE_VERSION=4.3 CMAKE_VERSION=cmake-3.20.6-linux-x86_64 CMAKE_VERSION_AARCH64=cmake-3.20.6-linux-aarch64 -CUDA_VERSION=11.7.1-cudnn8 +CUDA_VERSION=11.8.0-cudnn8 print_usage_and_exit_docker_build() { echo "$__usage_docker_build" @@ -127,6 +133,9 @@ openblas_export_env() { elif [[ "py311" =~ ^($options)$ ]]; then export PYTHON_VERSION=3.11 export DOCKER_TAG=${DOCKER_TAG}-py311 + elif [[ "py312" =~ ^($options)$ ]]; then + export PYTHON_VERSION=3.12 + export DOCKER_TAG=${DOCKER_TAG}-py312 else echo "Invalid python version." print_usage_and_exit_docker_build @@ -182,6 +191,8 @@ cuda_wheel_build() { PYTHON_VERSION=3.10 elif [[ "py311" =~ ^($options)$ ]]; then PYTHON_VERSION=3.11 + elif [[ "py312" =~ ^($options)$ ]]; then + PYTHON_VERSION=3.12 else echo "Invalid python version." print_usage_and_exit_docker_build @@ -490,6 +501,10 @@ function main() { openblas_export_env amd64 py311 dev openblas_build ;; + openblas-amd64-py312-dev) + openblas_export_env amd64 py312 dev + openblas_build + ;; openblas-amd64-py38) openblas_export_env amd64 py38 openblas_build @@ -506,6 +521,10 @@ function main() { openblas_export_env amd64 py311 openblas_build ;; + openblas-amd64-py312) + openblas_export_env amd64 py312 + openblas_build + ;; # OpenBLAS ARM64 openblas-arm64-py38-dev) @@ -524,6 +543,10 @@ function main() { openblas_export_env arm64 py311 dev openblas_build ;; + openblas-arm64-py312-dev) + openblas_export_env arm64 py312 dev + openblas_build + ;; openblas-arm64-py38) openblas_export_env arm64 py38 openblas_build @@ -540,6 +563,10 @@ function main() { openblas_export_env arm64 py311 openblas_build ;; + openblas-arm64-py312) + openblas_export_env arm64 py312 + openblas_build + ;; # CPU CI cpu-static) @@ -586,6 +613,9 @@ function main() { cuda_wheel_py311_dev) cuda_wheel_build py311 dev ;; + cuda_wheel_py312_dev) + cuda_wheel_build py312 dev + ;; cuda_wheel_py38) cuda_wheel_build py38 ;; @@ -598,6 +628,10 @@ function main() { cuda_wheel_py311) cuda_wheel_build py311 ;; + cuda_wheel_py312) + cuda_wheel_build py312 + ;; + # ML CIs 2-bionic) diff --git a/docker/docker_test.sh b/docker/docker_test.sh index 544eeb755e4..477496230d6 100755 --- a/docker/docker_test.sh +++ b/docker/docker_test.sh @@ -20,20 +20,24 @@ OPTION: openblas-amd64-py39-dev : OpenBLAS AMD64 3.9 wheel, developer mode openblas-amd64-py310-dev : OpenBLAS AMD64 3.10 wheel, developer mode openblas-amd64-py311-dev : OpenBLAS AMD64 3.11 wheel, developer mode + openblas-amd64-py312-dev : OpenBLAS AMD64 3.12 wheel, developer mode openblas-amd64-py38 : OpenBLAS AMD64 3.8 wheel, release mode openblas-amd64-py39 : OpenBLAS AMD64 3.9 wheel, release mode openblas-amd64-py310 : OpenBLAS AMD64 3.10 wheel, release mode openblas-amd64-py311 : OpenBLAS AMD64 3.11 wheel, release mode + openblas-amd64-py312 : OpenBLAS AMD64 3.12 wheel, release mode # OpenBLAS ARM64 (Dockerfile.openblas) openblas-arm64-py38-dev : OpenBLAS ARM64 3.8 wheel, developer mode openblas-arm64-py39-dev : OpenBLAS ARM64 3.9 wheel, developer mode openblas-arm64-py310-dev : OpenBLAS ARM64 3.10 wheel, developer mode openblas-arm64-py311-dev : OpenBLAS ARM64 3.11 wheel, developer mode + openblas-arm64-py312-dev : OpenBLAS ARM64 3.12 wheel, developer mode openblas-arm64-py38 : OpenBLAS ARM64 3.8 wheel, release mode openblas-arm64-py39 : OpenBLAS ARM64 3.9 wheel, release mode openblas-arm64-py310 : OpenBLAS ARM64 3.10 wheel, release mode openblas-arm64-py311 : OpenBLAS ARM64 3.11 wheel, release mode + openblas-arm64-py312 : OpenBLAS ARM64 3.12 wheel, release mode # Ubuntu CPU CI (Dockerfile.ci) cpu-static : Ubuntu CPU static @@ -230,6 +234,11 @@ openblas-amd64-py311-dev) openblas_print_env cpp_python_linking_uninstall_test ;; +openblas-amd64-py312-dev) + openblas_export_env amd64 py312 dev + openblas_print_env + cpp_python_linking_uninstall_test + ;; openblas-amd64-py38) openblas_export_env amd64 py38 openblas_print_env @@ -250,6 +259,11 @@ openblas-amd64-py311) openblas_print_env cpp_python_linking_uninstall_test ;; +openblas-amd64-py312) + openblas_export_env amd64 py312 + openblas_print_env + cpp_python_linking_uninstall_test + ;; # OpenBLAS ARM64 openblas-arm64-py38-dev) @@ -272,6 +286,11 @@ openblas-arm64-py311-dev) openblas_print_env cpp_python_linking_uninstall_test ;; +openblas-arm64-py312-dev) + openblas_export_env arm64 py312 dev + openblas_print_env + cpp_python_linking_uninstall_test + ;; openblas-arm64-py38) openblas_export_env arm64 py38 openblas_print_env @@ -292,6 +311,11 @@ openblas-arm64-py311) openblas_print_env cpp_python_linking_uninstall_test ;; +openblas-arm64-py312) + openblas_export_env arm64 py312 + openblas_print_env + cpp_python_linking_uninstall_test + ;; # CPU CI cpu-static) diff --git a/docs/arm.rst b/docs/arm.rst index e9f9c942413..93d5b47f126 100644 --- a/docs/arm.rst +++ b/docs/arm.rst @@ -71,6 +71,7 @@ commands: ./docker_build.sh openblas-arm64-py39 # Python 3.9 ./docker_build.sh openblas-arm64-py310 # Python 3.10 ./docker_build.sh openblas-arm64-py311 # Python 3.11 + ./docker_build.sh openblas-arm64-py311 # Python 3.12 After running ``docker_build.sh``, you shall see a ``.whl`` file generated the current directly on the host. Then simply install the ``.whl`` file by: diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index b9f390ea835..34f69dfdfe6 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -17,6 +17,7 @@ Supported Python versions: * 3.9 * 3.10 * 3.11 +* 3.12 Supported operating systems: @@ -74,24 +75,28 @@ version (``HEAD`` of ``main`` branch): - `Python 3.9 `__ - `Python 3.10 `__ - `Python 3.11 `__ + - `Python 3.12 `__ * - Linux (CPU) - `Python 3.8 `__ - `Python 3.9 `__ - `Python 3.10 `__ - `Python 3.11 `__ + - `Python 3.12 `__ * - MacOS - `Python 3.8 `__ - `Python 3.9 `__ - `Python 3.10 `__ - `Python 3.11 `__ + - `Python 3.12 `__ * - Windows - `Python 3.8 `__ - `Python 3.9 `__ - `Python 3.10 `__ - `Python 3.11 `__ + - `Python 3.12 `__ Please use these links from the `latest version of this page `__ only. You can also diff --git a/python/README.rst b/python/README.rst index 232c3b0ca24..bf046cc2b11 100644 --- a/python/README.rst +++ b/python/README.rst @@ -48,6 +48,7 @@ With Python versions: * 3.9 * 3.10 * 3.11 +* 3.12 Resources ====================== diff --git a/python/setup.py b/python/setup.py index 029c5717180..59aa268dccc 100644 --- a/python/setup.py +++ b/python/setup.py @@ -131,6 +131,7 @@ def finalize_options(self): "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Education", "Topic :: Multimedia :: Graphics :: 3D Modeling", "Topic :: Multimedia :: Graphics :: 3D Rendering", diff --git a/util/ci_utils.sh b/util/ci_utils.sh index 38ff654880e..3bcbfdbfd0c 100644 --- a/util/ci_utils.sh +++ b/util/ci_utils.sh @@ -25,10 +25,10 @@ LOW_MEM_USAGE=${LOW_MEM_USAGE:-OFF} # Dependency versions: # CUDA: see docker/docker_build.sh # ML -TENSORFLOW_VER="2.13.0" -TORCH_VER="2.0.1" +TENSORFLOW_VER="2.16.1" +TORCH_VER="2.2.0" TORCH_CPU_GLNX_VER="${TORCH_VER}+cpu" -TORCH_CUDA_GLNX_VER="${TORCH_VER}+cu117" # match CUDA_VERSION in docker/docker_build.sh +TORCH_CUDA_GLNX_VER="${TORCH_VER}+cu118" # match CUDA_VERSION in docker/docker_build.sh TORCH_MACOS_VER="${TORCH_VER}" TORCH_REPO_URL="https://download.pytorch.org/whl/torch/" # Python From 4ba2d39faa53b4251862a2ef5bce1a07654b0aeb Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Sun, 24 Mar 2024 21:45:01 -0400 Subject: [PATCH 02/82] tensorboard bump --- python/requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/requirements_test.txt b/python/requirements_test.txt index 4a2f418b6bb..902f88f240d 100644 --- a/python/requirements_test.txt +++ b/python/requirements_test.txt @@ -1,6 +1,6 @@ pytest==7.1.2 pytest-randomly==3.8.0 scipy==1.10.1 -tensorboard==2.13.0 +tensorboard==2.16.1gs oauthlib==3.2.2 certifi==2023.7.22 From 17baa25de34ae9888fce1324b4839f949bb45833 Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Sun, 24 Mar 2024 21:46:47 -0400 Subject: [PATCH 03/82] typo --- python/requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/requirements_test.txt b/python/requirements_test.txt index 902f88f240d..5c8186291af 100644 --- a/python/requirements_test.txt +++ b/python/requirements_test.txt @@ -1,6 +1,6 @@ pytest==7.1.2 pytest-randomly==3.8.0 scipy==1.10.1 -tensorboard==2.16.1gs +tensorboard==2.16.1s oauthlib==3.2.2 certifi==2023.7.22 From fae3dff86def8ebb005902679b5264623f28ce64 Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Mon, 25 Mar 2024 08:50:42 -0400 Subject: [PATCH 04/82] what is happening with my typo fixing --- python/requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/requirements_test.txt b/python/requirements_test.txt index 5c8186291af..72d5784c5e8 100644 --- a/python/requirements_test.txt +++ b/python/requirements_test.txt @@ -1,6 +1,6 @@ pytest==7.1.2 pytest-randomly==3.8.0 scipy==1.10.1 -tensorboard==2.16.1s +tensorboard==2.16.1 oauthlib==3.2.2 certifi==2023.7.22 From 8a2cc17766132a676f4c8418b75c9596532a4d59 Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Mon, 25 Mar 2024 09:07:27 -0400 Subject: [PATCH 05/82] bump scipy --- python/requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/requirements_test.txt b/python/requirements_test.txt index 72d5784c5e8..253ffee771c 100644 --- a/python/requirements_test.txt +++ b/python/requirements_test.txt @@ -1,6 +1,6 @@ pytest==7.1.2 pytest-randomly==3.8.0 -scipy==1.10.1 +scipy==1.11.4 tensorboard==2.16.1 oauthlib==3.2.2 certifi==2023.7.22 From 66f8800a8640790c02ea0842b4f7ca33a5894864 Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Mon, 25 Mar 2024 09:18:53 -0400 Subject: [PATCH 06/82] unrelated style check fix --- examples/python/reconstruction_system/make_fragments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/python/reconstruction_system/make_fragments.py b/examples/python/reconstruction_system/make_fragments.py index 17d875aa86c..5af9d960f4b 100644 --- a/examples/python/reconstruction_system/make_fragments.py +++ b/examples/python/reconstruction_system/make_fragments.py @@ -145,6 +145,7 @@ def make_pointcloud_for_fragment(path_dataset, color_files, depth_files, write_ascii=False, compressed=True) + def process_single_fragment(fragment_id, color_files, depth_files, n_files, n_fragments, config): if config["path_intrinsic"]: From 53036135c6390a2bae69a666aa2018876620d362 Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Mon, 25 Mar 2024 11:48:07 -0400 Subject: [PATCH 07/82] scipy 1.11.4 required for python3.12, otherwise unchanged --- python/requirements_test.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/requirements_test.txt b/python/requirements_test.txt index 253ffee771c..c41e7ce5753 100644 --- a/python/requirements_test.txt +++ b/python/requirements_test.txt @@ -1,6 +1,7 @@ pytest==7.1.2 pytest-randomly==3.8.0 -scipy==1.11.4 +scipy==1.10.1; python_version < "3.12" +scipy==1.11.4; python_version >= "3.12" tensorboard==2.16.1 oauthlib==3.2.2 certifi==2023.7.22 From e5dd29c1abf1bea734e7efd295f0e8b70fdc5ecb Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Mon, 25 Mar 2024 11:52:57 -0400 Subject: [PATCH 08/82] tensorboard 2.16.2 required for python3.12, otherwise unchanged --- python/requirements_test.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/requirements_test.txt b/python/requirements_test.txt index c41e7ce5753..7af2d222c27 100644 --- a/python/requirements_test.txt +++ b/python/requirements_test.txt @@ -2,6 +2,7 @@ pytest==7.1.2 pytest-randomly==3.8.0 scipy==1.10.1; python_version < "3.12" scipy==1.11.4; python_version >= "3.12" -tensorboard==2.16.1 +tensorboard==2.13.0; python_version < "3.12" +tensorboard==2.16.2; python_version >= "3.12" oauthlib==3.2.2 certifi==2023.7.22 From 15abf1898bb711c5ae43e2f7f27bd87f26aae5a0 Mon Sep 17 00:00:00 2001 From: Stuart Wheaton Date: Mon, 25 Mar 2024 20:44:38 -0400 Subject: [PATCH 09/82] fix improper error fstring --- python/open3d/visualization/tensorboard_plugin/summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/open3d/visualization/tensorboard_plugin/summary.py b/python/open3d/visualization/tensorboard_plugin/summary.py index 0b6e254b754..4bb35fa5da7 100644 --- a/python/open3d/visualization/tensorboard_plugin/summary.py +++ b/python/open3d/visualization/tensorboard_plugin/summary.py @@ -424,7 +424,7 @@ def get_or_check_shape(prop, tensor_tuple, exp_shape): raise ValueError( f"Property {prop} tensor should have shape[{k}]" f"={s} for all elements but is " - f"{tensor.shape[k-1] for tensor in tensor_tuple}.") + f"{[tensor.shape[k-1] for tensor in tensor_tuple]}.") return shape[:2] From 80ae047db2cda0390b088cf759c8dc0ce0a8e4bd Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:22:00 -0700 Subject: [PATCH 10/82] Misc. CI updates and fixes (#6716) * Fix RemoveUnreferencedVertices for CUDA * Style fix * Remove docker build cache for CI to avoid SYCL CI failure. * Manual CI run for Ubuntu SYCL (workflow-dispatch) * Update github actions for Node 16 -> Node 20 migration: uploaded artifacts must have unique names * Remove build folders in CI to save space. Keep bin, lib, etc. 9.6G free space available for Ubuntu wheel CI now. --- .github/workflows/clean-gcloud-profiles.yml | 4 +- .github/workflows/documentation.yml | 14 ++--- .github/workflows/macos.yml | 51 +++++++++---------- .github/workflows/style.yml | 4 +- .github/workflows/ubuntu-cuda.yml | 10 ++-- .github/workflows/ubuntu-openblas.yml | 8 +-- .github/workflows/ubuntu-sycl.yml | 8 +-- .github/workflows/ubuntu-wheel.yml | 25 ++++----- .github/workflows/ubuntu.yml | 12 ++--- .github/workflows/vtk_packages.yml | 12 ++--- .github/workflows/webrtc.yml | 16 +++--- .github/workflows/windows.yml | 45 ++++++++-------- cpp/open3d/t/geometry/TriangleMesh.cpp | 1 + .../pipelines/registration/registration.cpp | 2 +- docker/Dockerfile.ci | 5 +- docker/Dockerfile.wheel | 5 +- docker/docker_build.sh | 2 + .../reconstruction_system/make_fragments.py | 1 + 18 files changed, 118 insertions(+), 107 deletions(-) diff --git a/.github/workflows/clean-gcloud-profiles.yml b/.github/workflows/clean-gcloud-profiles.yml index 5b5398156fd..0aec813a48f 100644 --- a/.github/workflows/clean-gcloud-profiles.yml +++ b/.github/workflows/clean-gcloud-profiles.yml @@ -36,12 +36,12 @@ jobs: fail-fast: false steps: - name: GCloud CLI auth - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 85368dd8a45..b0e289e41cc 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -29,7 +29,7 @@ jobs: DEVELOPER_BUILD: ${{ github.event.inputs.developer_build || 'ON' }} steps: - name: Checkout Open3D source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Maximize build space run: | @@ -37,13 +37,13 @@ jobs: maximize_ubuntu_github_actions_build_space - name: Checkout Open3D-ML source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: isl-org/Open3D-ML path: ${{ env.OPEN3D_ML_ROOT }} - name: Setup cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: # Ref: https://github.com/apache/incubator-mxnet/pull/18459/files path: ~/.ccache @@ -56,7 +56,7 @@ jobs: restore-keys: | ${{ runner.os }}-ccache - name: Set up Python version - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.8' @@ -81,7 +81,7 @@ jobs: ccache -s - name: Upload docs - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: open3d_docs path: docs/_out/html @@ -89,13 +89,13 @@ jobs: - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 00bd9636130..16b242d081b 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -39,9 +39,9 @@ jobs: LOW_MEM_USAGE: ON steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: # Ref: https://github.com/apache/incubator-mxnet/pull/18459/files path: ~/.ccache @@ -54,7 +54,7 @@ jobs: restore-keys: | ${{ runner.os }}-ccache - name: Set up Python version - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install dependencies @@ -73,7 +73,7 @@ jobs: PATH=/usr/local/var/homebrew/linked/ccache/libexec:$PATH ccache -s ./util/run_ci.sh - DEVEL_PKG_NAME="$(basename package/open3d-devel-*.tar.xz)" + DEVEL_PKG_NAME="$(basename build/package/open3d-devel-*.tar.xz)" echo "DEVEL_PKG_NAME=$DEVEL_PKG_NAME" >> $GITHUB_ENV - name: Build Open3D viewer app if: ${{ env.BUILD_SHARED_LIBS == 'OFF' }} @@ -87,21 +87,21 @@ jobs: - name: Upload package if: ${{ env.BUILD_SHARED_LIBS == 'ON' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: open3d-devel-macosx + name: ${{ env.DEVEL_PKG_NAME }} path: build/package/${{ env.DEVEL_PKG_NAME }} if-no-files-found: error - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' && env.BUILD_SHARED_LIBS == 'ON' }} - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' && env.BUILD_SHARED_LIBS == 'ON' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} @@ -113,7 +113,7 @@ jobs: echo "Download devel package at: https://storage.googleapis.com/open3d-releases/devel/${{ env.DEVEL_PKG_NAME }}" - name: Upload Open3D viewer app - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ env.BUILD_SHARED_LIBS == 'OFF' }} with: name: open3d-app-macosx-10_15 @@ -143,16 +143,16 @@ jobs: OPEN3D_ML_ROOT: ${{ github.workspace }}/Open3D-ML steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Checkout Open3D-ML source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: isl-org/Open3D-ML path: ${{ env.OPEN3D_ML_ROOT }} - name: Setup cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: # Ref: https://github.com/apache/incubator-mxnet/pull/18459/files path: ~/.ccache @@ -166,7 +166,7 @@ jobs: ${{ runner.os }}-ccache - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} @@ -199,21 +199,21 @@ jobs: echo "PIP_PKG_NAME=$PIP_PKG_NAME" >> $GITHUB_ENV - name: Upload wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: open3d_macosx_x86_64_wheels + name: ${{ env.PIP_PKG_NAME }} path: build/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} if-no-files-found: error - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} @@ -249,23 +249,22 @@ jobs: OPEN3D_ML_ROOT: ${{ github.workspace }}/Open3D-ML steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Checkout Open3D-ML source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: isl-org/Open3D-ML path: ${{ env.OPEN3D_ML_ROOT }} - name: Download wheels - uses: actions/download-artifact@v3 - # See https://github.com/dawidd6/action-download-artifact for more - # flexible artifact download options + uses: actions/download-artifact@v4 with: - name: open3d_macosx_x86_64_wheels + pattern: open3d-*macosx*.whl + merge-multiple: true - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} @@ -290,12 +289,12 @@ jobs: needs: [build-wheel, MacOS] steps: - name: GCloud CLI auth - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 744b997e2e7..e0de61516be 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -17,9 +17,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python version - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.8' - name: Install dependencies diff --git a/.github/workflows/ubuntu-cuda.yml b/.github/workflows/ubuntu-cuda.yml index ce4528a83ca..bc7dcf9f67a 100644 --- a/.github/workflows/ubuntu-cuda.yml +++ b/.github/workflows/ubuntu-cuda.yml @@ -64,7 +64,7 @@ jobs: CCACHE_TAR_NAME : open3d-ci-${{ matrix.CI_CONFIG }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: 'false' - name: Package code @@ -74,12 +74,12 @@ jobs: tar -czvf Open3D.tar.gz Open3D ls -alh - name: GCloud CLI auth - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} @@ -150,9 +150,9 @@ jobs: - name: Upload package if: ${{ env.BUILD_PACKAGE == 'true' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: open3d-devel-linux-x86_64-cuda + name: open3d-devel-linux-x86_64-cuda-${CI_CONFIG} path: open3d-devel-linux*.tar.xz if-no-files-found: error diff --git a/.github/workflows/ubuntu-openblas.yml b/.github/workflows/ubuntu-openblas.yml index 233c32bd96f..9bbb423f457 100644 --- a/.github/workflows/ubuntu-openblas.yml +++ b/.github/workflows/ubuntu-openblas.yml @@ -24,7 +24,7 @@ jobs: fail-fast: false steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Maximize build space run: | source util/ci_utils.sh @@ -66,7 +66,7 @@ jobs: GCE_INSTANCE_PREFIX: open3d-ci-openblas-arm64-py310-dev steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Package code run: | # GITHUB_WORKSPACE: /home/runner/work/Open3D/Open3D @@ -74,12 +74,12 @@ jobs: tar -czvf Open3D.tar.gz Open3D ls -alh - name: GCloud CLI auth - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/.github/workflows/ubuntu-sycl.yml b/.github/workflows/ubuntu-sycl.yml index 1c1ac21697d..4f4c9f6d9c1 100644 --- a/.github/workflows/ubuntu-sycl.yml +++ b/.github/workflows/ubuntu-sycl.yml @@ -1,6 +1,7 @@ name: Ubuntu SYCL on: + workflow_dispatch: push: branches: - main @@ -24,7 +25,7 @@ jobs: BUILD_SHARED_LIBS: [ON, OFF] steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Maximize build space run: | source util/ci_utils.sh @@ -38,6 +39,7 @@ jobs: fi - name: Docker test run: | + du -hs $PWD if [ "${{ matrix.BUILD_SHARED_LIBS }}" = "ON" ]; then docker/docker_test.sh sycl-shared else @@ -46,13 +48,13 @@ jobs: - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/.github/workflows/ubuntu-wheel.yml b/.github/workflows/ubuntu-wheel.yml index a798762c965..906d07112f7 100644 --- a/.github/workflows/ubuntu-wheel.yml +++ b/.github/workflows/ubuntu-wheel.yml @@ -47,7 +47,7 @@ jobs: OPEN3D_CPU_RENDERING: true steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Maximize build space run: | source util/ci_utils.sh @@ -78,22 +78,22 @@ jobs: echo "PIP_PKG_NAME=$PIP_PKG_NAME" >> $GITHUB_ENV echo "PIP_CPU_PKG_NAME=$PIP_CPU_PKG_NAME" >> $GITHUB_ENV - name: Upload wheel to GitHub artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: open3d_linux_x86_64_wheels + name: ${{ env.PIP_PKG_NAME }} path: | ${{ env.PIP_PKG_NAME }} ${{ env.PIP_CPU_PKG_NAME }} if-no-files-found: error - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} @@ -131,22 +131,23 @@ jobs: OPEN3D_ML_ROOT: ${{ github.workspace }}/Open3D-ML steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Maximize build space run: | source util/ci_utils.sh maximize_ubuntu_github_actions_build_space - name: Checkout Open3D-ML source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: isl-org/Open3D-ML path: ${{ env.OPEN3D_ML_ROOT }} - name: Download wheels - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: open3d_linux_x86_64_wheels + pattern: open3d*-manylinux*.whl + merge-multiple: true - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} - name: Test Python package @@ -181,12 +182,12 @@ jobs: needs: [build-wheel] steps: - name: GCloud CLI auth - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 1a69ec4da2d..3bbdaadc1d1 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -39,7 +39,7 @@ jobs: OPEN3D_CPU_RENDERING: true steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Maximize build space run: | source util/ci_utils.sh @@ -72,27 +72,27 @@ jobs: fi - name: Upload package to GitHub artifacts if: ${{ env.BUILD_SHARED_LIBS == 'ON' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: open3d-devel-linux-x86_64 + name: open3d-devel-linux-x86_64-ML_${{ matrix.MLOPS }} path: open3d-devel-*.tar.xz if-no-files-found: error - name: Upload viewer to GitHub artifacts if: ${{ env.BUILD_SHARED_LIBS == 'OFF' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: open3d-viewer-Linux path: open3d-viewer-*-Linux.deb if-no-files-found: error - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v1' + uses: 'google-github-actions/auth@v2' with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/.github/workflows/vtk_packages.yml b/.github/workflows/vtk_packages.yml index 8ae7d812187..acd20c5260b 100644 --- a/.github/workflows/vtk_packages.yml +++ b/.github/workflows/vtk_packages.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-18.04 steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: CMake configure run: | mkdir build @@ -25,7 +25,7 @@ jobs: make -j$(nproc) cmake -E sha256sum vtk*.tar.gz > checksum_linux.txt - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: vtk_linux path: | @@ -53,7 +53,7 @@ jobs: - name: Disk space used run: Get-PSDrive - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Config # Move build directory to C: https://github.com/actions/virtual-environments/issues/1341 run: | @@ -75,7 +75,7 @@ jobs: ls cmake -E sha256sum (get-item vtk*.tar.gz).Name > checksum_win_${{matrix.configuration}}.txt - name: Upload package - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: vtk_windows_${{matrix.configuration}} path: | @@ -90,7 +90,7 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: CMake configure run: | mkdir build @@ -102,7 +102,7 @@ jobs: make -j2 cmake -E sha256sum vtk*.tar.gz > checksum_macos.txt - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: vtk_macos path: | diff --git a/.github/workflows/webrtc.yml b/.github/workflows/webrtc.yml index 4602379d00f..90c45e053e7 100644 --- a/.github/workflows/webrtc.yml +++ b/.github/workflows/webrtc.yml @@ -37,10 +37,10 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python version - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 @@ -61,9 +61,9 @@ jobs: build_webrtc - name: Upload WebRTC - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: webrtc_release + name: webrtc_release_${{ matrix.os }} path: | webrtc_*.tar.gz checksum_*.txt @@ -81,10 +81,10 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python version - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.8' @@ -167,9 +167,9 @@ jobs: cmake -E sha256sum webrtc_${env:WEBRTC_COMMIT_SHORT}_win.zip | Tee-Object -FilePath checksum_win.txt - name: Upload WebRTC - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: webrtc_release + name: webrtc_release_windows path: | ${{ env.WORK_DIR }}/webrtc_*.zip ${{ env.WORK_DIR }}/checksum_*.txt diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d687f6b7e33..11223949007 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,7 +54,7 @@ jobs: run: Get-PSDrive - name: Setup Windows SDK - uses: GuillaumeFalourd/setup-windows10-sdk-action@v1.11 + uses: GuillaumeFalourd/setup-windows10-sdk-action@v2 with: sdk-version: 19041 @@ -92,10 +92,10 @@ jobs: echo "$CUDA_PATH\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python version - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 @@ -148,9 +148,9 @@ jobs: - name: Upload Package if: ${{ matrix.BUILD_SHARED_LIBS == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: open3d-devel-windows + name: ${{ env.DEVEL_PKG_NAME }} path: ${{ env.BUILD_DIR }}/package/${{ env.DEVEL_PKG_NAME }} if-no-files-found: error @@ -166,7 +166,7 @@ jobs: - name: Upload Viewer if: ${{ matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' && matrix.CONFIG == 'Release' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: open3d-app-windows-amd64 path: C:\Program Files\Open3D\bin\Open3D @@ -174,13 +174,13 @@ jobs: - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' }} - uses: google-github-actions/auth@v1 + uses: google-github-actions/auth@v2 with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_DOCS_PROJECT }} @@ -255,15 +255,15 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Windows SDK - uses: GuillaumeFalourd/setup-windows10-sdk-action@v1.11 + uses: GuillaumeFalourd/setup-windows10-sdk-action@v2 with: sdk-version: 19041 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} @@ -307,21 +307,21 @@ jobs: echo "PIP_PKG_NAME=$PIP_PKG_NAME" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Upload wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: open3d_win_amd64_wheels + name: ${{ env.PIP_PKG_NAME }} path: ${{ env.BUILD_DIR }}/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} if-no-files-found: error - name: GCloud CLI auth if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/auth@v1 + uses: google-github-actions/auth@v2 with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_DOCS_PROJECT }} @@ -360,17 +360,16 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download wheels - uses: actions/download-artifact@v3 - # See https://github.com/dawidd6/action-download-artifact for more - # flexible artifact download options + uses: actions/download-artifact@v4 with: - name: open3d_win_amd64_wheels + pattern: open3d-*win*.whl + merge-multiple: true - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} @@ -418,12 +417,12 @@ jobs: needs: [build-wheel] steps: - name: GCloud CLI auth - uses: google-github-actions/auth@v1 + uses: google-github-actions/auth@v2 with: project_id: ${{ secrets.GCE_PROJECT }} credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v1 + uses: google-github-actions/setup-gcloud@v2 with: version: ${{ env.GCE_CLI_GHA_VERSION }} project_id: ${{ secrets.GCE_PROJECT }} diff --git a/cpp/open3d/t/geometry/TriangleMesh.cpp b/cpp/open3d/t/geometry/TriangleMesh.cpp index 7316901b473..2bf329838cc 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.cpp +++ b/cpp/open3d/t/geometry/TriangleMesh.cpp @@ -1292,6 +1292,7 @@ TriangleMesh TriangleMesh::RemoveUnreferencedVertices() { UpdateTriangleIndicesByVertexMask(tris_cpu, vertex_mask); }); + SetTriangleIndices(tris_cpu.To(GetDevice())); } // send the vertex mask to original device and apply to diff --git a/cpp/pybind/pipelines/registration/registration.cpp b/cpp/pybind/pipelines/registration/registration.cpp index f5165537fea..cddcb7e7e80 100644 --- a/cpp/pybind/pipelines/registration/registration.cpp +++ b/cpp/pybind/pipelines/registration/registration.cpp @@ -351,7 +351,7 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. "Check if two point clouds build the polygons with similar " "edge lengths. That is, checks if the lengths of any two " "arbitrary edges (line formed by two vertices) individually " - "drawn withinin source point cloud and within the target " + "drawn within the source point cloud and within the target " "point cloud with correspondences are similar. The only " "parameter similarity_threshold is a number between 0 " "(loose) and 1 (strict)"); diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index e446353744a..73dd3fe1aac 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -17,6 +17,7 @@ ARG BUILD_TENSORFLOW_OPS ARG BUILD_PYTORCH_OPS ARG PACKAGE ARG BUILD_SYCL_MODULE +ARG CI RUN if [ -z "${DEVELOPER_BUILD}" ]; then echo "Error: ARG DEVELOPER_BUILD not specified."; exit 1; fi \ && if [ -z "${CCACHE_TAR_NAME}" ]; then echo "Error: ARG CCACHE_TAR_NAME not specified."; exit 1; fi \ @@ -210,7 +211,9 @@ RUN \ && make install-pip-package -j$(nproc) \ && make install -j$(nproc) \ && if [ "${PACKAGE}" = "ON" ]; then make package; fi \ - && if [ "${PACKAGE}" = "VIEWER" ]; then make package-Open3DViewer-deb; fi + && if [ "${PACKAGE}" = "VIEWER" ]; then make package-Open3DViewer-deb; fi \ + && if [ "${CI:-}a" != "a" ]; then rm -rf _deps assimp embree ippicv mkl mkl_install webrtc; fi +# If CI is not null or unset, remove all large build folders to save disk space # Compress ccache folder, move to / directory RUN ccache -s \ diff --git a/docker/Dockerfile.wheel b/docker/Dockerfile.wheel index 6a7d7080aed..a719262ece2 100644 --- a/docker/Dockerfile.wheel +++ b/docker/Dockerfile.wheel @@ -10,6 +10,7 @@ ARG CCACHE_VERSION ARG PYTHON_VERSION ARG BUILD_TENSORFLOW_OPS ARG BUILD_PYTORCH_OPS +ARG CI # Forward all ARG to ENV # ci_utils.sh requires these environment variables @@ -130,7 +131,9 @@ WORKDIR /root/Open3D RUN export NPROC=$(nproc) \ && export BUILD_SHARED_LIBS=OFF \ && source /root/Open3D/util/ci_utils.sh \ - && build_pip_package build_azure_kinect build_jupyter + && build_pip_package build_azure_kinect build_jupyter \ + && if [ ${CI:-}a != a ]; then cd /root/Open3D/build/ && ls | grep -Ev '^lib$' | xargs rm -rf ; fi + # remove build folder (except lib) to save CI space on Github # Compress ccache folder, move to / directory RUN ccache -s \ diff --git a/docker/docker_build.sh b/docker/docker_build.sh index 33ab6ad0115..f55276baed5 100755 --- a/docker/docker_build.sh +++ b/docker/docker_build.sh @@ -207,6 +207,7 @@ cuda_wheel_build() { --build-arg PYTHON_VERSION="${PYTHON_VERSION}" \ --build-arg BUILD_TENSORFLOW_OPS="${BUILD_TENSORFLOW_OPS}" \ --build-arg BUILD_PYTORCH_OPS="${BUILD_PYTORCH_OPS}" \ + --build-arg CI="${CI:-}" \ -t open3d-ci:wheel \ -f docker/Dockerfile.wheel . popd @@ -249,6 +250,7 @@ ci_build() { --build-arg BUILD_PYTORCH_OPS="${BUILD_PYTORCH_OPS}" \ --build-arg PACKAGE="${PACKAGE}" \ --build-arg BUILD_SYCL_MODULE="${BUILD_SYCL_MODULE}" \ + --build-arg CI="${CI:-}" \ -t "${DOCKER_TAG}" \ -f docker/Dockerfile.ci . popd diff --git a/examples/python/reconstruction_system/make_fragments.py b/examples/python/reconstruction_system/make_fragments.py index 17d875aa86c..5af9d960f4b 100644 --- a/examples/python/reconstruction_system/make_fragments.py +++ b/examples/python/reconstruction_system/make_fragments.py @@ -145,6 +145,7 @@ def make_pointcloud_for_fragment(path_dataset, color_files, depth_files, write_ascii=False, compressed=True) + def process_single_fragment(fragment_id, color_files, depth_files, n_files, n_fragments, config): if config["path_intrinsic"]: From 84b8e071e23eb65a60df87b785683eea5142b415 Mon Sep 17 00:00:00 2001 From: mone27 Date: Mon, 1 Apr 2024 17:11:57 +0200 Subject: [PATCH 11/82] Fix typos in NearestNeighborSearch.h, NanoFlannIndex and NNSIndex (#6705) --- cpp/open3d/core/nns/NNSIndex.h | 6 +++--- cpp/open3d/core/nns/NanoFlannIndex.h | 6 +++--- cpp/open3d/core/nns/NearestNeighborSearch.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp/open3d/core/nns/NNSIndex.h b/cpp/open3d/core/nns/NNSIndex.h index 0e71a6d3a40..8eb8160da5d 100644 --- a/cpp/open3d/core/nns/NNSIndex.h +++ b/cpp/open3d/core/nns/NNSIndex.h @@ -51,7 +51,7 @@ class NNSIndex { /// \param knn Number of nearest neighbor to search. /// \return Pair of Tensors: (indices, distances): /// - indices: Tensor of shape {n, knn}, with dtype Int32. - /// - distainces: Tensor of shape {n, knn}, same dtype with dataset_points. + /// - distances: Tensor of shape {n, knn}, same dtype with dataset_points. virtual std::pair SearchKnn(const Tensor &query_points, int knn) const = 0; @@ -61,7 +61,7 @@ class NNSIndex { /// dtype with dataset_points. /// \param radii list of radius. Must be 1D, with shape {n, }. /// \return Tuple of Tensors: (indices, distances, num_neighbors): - /// - indicecs: Tensor of shape {total_num_neighbors,}, dtype Int32. + /// - indices: Tensor of shape {total_num_neighbors,}, dtype Int32. /// - distances: Tensor of shape {total_num_neighbors,}, same dtype with /// dataset_points. /// - num_neighbors: Tensor of shape {n,}, dtype Int32. @@ -76,7 +76,7 @@ class NNSIndex { /// dtype with dataset_points. /// \param radius Radius. /// \return Tuple of Tensors, (indices, distances, num_neighbors): - /// - indicecs: Tensor of shape {total_num_neighbors,}, dtype Int32. + /// - indices: Tensor of shape {total_num_neighbors,}, dtype Int32. /// - distances: Tensor of shape {total_num_neighbors,}, same dtype with /// dataset_points. /// - num_neighbors: Tensor of shape {n}, dtype Int32. diff --git a/cpp/open3d/core/nns/NanoFlannIndex.h b/cpp/open3d/core/nns/NanoFlannIndex.h index 32f0b48cedd..e092099d46e 100644 --- a/cpp/open3d/core/nns/NanoFlannIndex.h +++ b/cpp/open3d/core/nns/NanoFlannIndex.h @@ -54,7 +54,7 @@ class NanoFlannIndex : public NNSIndex { /// \param knn Number of nearest neighbor to search. /// \return Pair of Tensors: (indices, distances): /// - indices: Tensor of shape {n, knn}, with dtype Int32. - /// - distainces: Tensor of shape {n, knn}, same dtype with dataset_points. + /// - distances: Tensor of shape {n, knn}, same dtype with dataset_points. std::pair SearchKnn(const Tensor &query_points, int knn) const override; @@ -64,7 +64,7 @@ class NanoFlannIndex : public NNSIndex { /// dtype with dataset_points. /// \param radii list of radius. Must be 1D, with shape {n, }. /// \return Tuple of Tensors: (indices, distances, counts): - /// - indicecs: Tensor of shape {total_num_neighbors,}, dtype Int32. + /// - indices: Tensor of shape {total_num_neighbors,}, dtype Int32. /// - distances: Tensor of shape {total_num_neighbors,}, same dtype with /// dataset_points. /// - counts: Tensor of shape {n,}, dtype Int64. @@ -79,7 +79,7 @@ class NanoFlannIndex : public NNSIndex { /// dtype with dataset_points. /// \param radius Radius. /// \return Tuple of Tensors, (indices, distances, counts): - /// - indicecs: Tensor of shape {total_num_neighbors,}, dtype Int32. + /// - indices: Tensor of shape {total_num_neighbors,}, dtype Int32. /// - distances: Tensor of shape {total_num_neighbors,}, same dtype with /// dataset_points. /// - counts: Tensor of shape {n}, dtype Int64. diff --git a/cpp/open3d/core/nns/NearestNeighborSearch.h b/cpp/open3d/core/nns/NearestNeighborSearch.h index 32fa6b87785..7ce92811dd9 100644 --- a/cpp/open3d/core/nns/NearestNeighborSearch.h +++ b/cpp/open3d/core/nns/NearestNeighborSearch.h @@ -76,7 +76,7 @@ class NearestNeighborSearch { /// \param radius Radius. /// \param sort Sort the results by distance. Default is True. /// \return Tuple of Tensors, (indices, distances, num_neighbors): - /// - indicecs: Tensor of shape {total_number_of_neighbors,}, with dtype + /// - indices: Tensor of shape {total_number_of_neighbors,}, with dtype /// same as index_dtype_. /// - distances: Tensor of shape {total_number_of_neighbors,}, same dtype /// with query_points. The distances are squared L2 distances. @@ -91,7 +91,7 @@ class NearestNeighborSearch { /// \param radii Radii of query points. Each query point has one radius. /// Must be 1D, with shape {n,}. /// \return Tuple of Tensors, (indices,distances, num_neighbors): - /// - indicecs: Tensor of shape {total_number_of_neighbors,}, with dtype + /// - indices: Tensor of shape {total_number_of_neighbors,}, with dtype /// same as index_dtype_. /// - distances: Tensor of shape {total_number_of_neighbors,}, same dtype /// with query_points. The distances are squared L2 distances. @@ -108,7 +108,7 @@ class NearestNeighborSearch { /// \param max_knn Maximum number of neighbor to search per query. /// \return Tuple of Tensors, (indices, distances, counts): /// - indices: Tensor of shape {n, knn}, with dtype same as index_dtype_. - /// - distainces: Tensor of shape {n, knn}, with same dtype with + /// - distances: Tensor of shape {n, knn}, with same dtype with /// query_points. The distances are squared L2 distances. /// - counts: Counts of neighbour for each query points. [Tensor /// of shape {n}, with dtype same as index_dtype_]. From 172367ea241e36236af886e3649a09e8401c7503 Mon Sep 17 00:00:00 2001 From: Daniel Simon Date: Mon, 15 Apr 2024 16:40:21 -0700 Subject: [PATCH 12/82] Switched QHull ImGUI and GoogleTest to use FetchContent (#6645) * Switched QHull and GoogleTest to use FetchContent to populate their files at configure time. This means that the open3d_build_3rdparty_library function doesn't need to mark source files as generated which was causing them to be removed when running the clean target. * Switched ImGUI to use FetchContent instead of external project so files are provided at configure time instead of build time. * Removed vestigial ext_imgui target that used to download the imgui sources. Sources are now downloaded at configure time. --- 3rdparty/find_dependencies.cmake | 10 ---------- 3rdparty/googletest/googletest.cmake | 8 ++++---- 3rdparty/imgui/imgui.cmake | 8 ++++---- 3rdparty/qhull/qhull.cmake | 8 ++++---- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index 169bfbe1a54..d929b27728a 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -96,8 +96,6 @@ function(open3d_build_3rdparty_library name) if(arg_SOURCES) foreach(src IN LISTS arg_SOURCES) get_filename_component(abs_src "${src}" ABSOLUTE BASE_DIR "${arg_DIRECTORY}") - # Mark as generated to skip CMake's file existence checks - set_source_files_properties(${abs_src} PROPERTIES GENERATED TRUE) target_sources(${name} PRIVATE ${abs_src}) endforeach() foreach(incl IN LISTS include_dirs) @@ -1030,8 +1028,6 @@ if(NOT USE_SYSTEM_QHULLCPP) src/libqhull_r/rboxlib_r.c INCLUDE_DIRS src/ - DEPENDS - ext_qhull ) open3d_build_3rdparty_library(3rdparty_qhullcpp DIRECTORY ${QHULL_SOURCE_DIR} SOURCES @@ -1057,8 +1053,6 @@ if(NOT USE_SYSTEM_QHULLCPP) src/libqhullcpp/RoadLogEvent.cpp INCLUDE_DIRS src/ - DEPENDS - ext_qhull ) target_link_libraries(3rdparty_qhullcpp PRIVATE 3rdparty_qhull_r) list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_CUSTOM Open3D::3rdparty_qhullcpp) @@ -1158,8 +1152,6 @@ if (BUILD_UNIT_TESTS) googletest/ googlemock/include/ googlemock/ - DEPENDS - ext_googletest ) endif() endif() @@ -1190,8 +1182,6 @@ if(BUILD_GUI) imgui_tables.cpp imgui_widgets.cpp imgui.cpp - DEPENDS - ext_imgui ) list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_CUSTOM Open3D::3rdparty_imgui) else() diff --git a/3rdparty/googletest/googletest.cmake b/3rdparty/googletest/googletest.cmake index 706bf35204a..dd84c07c32c 100644 --- a/3rdparty/googletest/googletest.cmake +++ b/3rdparty/googletest/googletest.cmake @@ -1,6 +1,6 @@ -include(ExternalProject) +include(FetchContent) -ExternalProject_Add( +FetchContent_Declare( ext_googletest PREFIX googletest URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz @@ -12,5 +12,5 @@ ExternalProject_Add( INSTALL_COMMAND "" ) -ExternalProject_Get_Property(ext_googletest SOURCE_DIR) -set(GOOGLETEST_SOURCE_DIR ${SOURCE_DIR}) +FetchContent_Populate(ext_googletest) +FetchContent_GetProperties(ext_googletest SOURCE_DIR GOOGLETEST_SOURCE_DIR) diff --git a/3rdparty/imgui/imgui.cmake b/3rdparty/imgui/imgui.cmake index 752addd86a1..12ea3175736 100644 --- a/3rdparty/imgui/imgui.cmake +++ b/3rdparty/imgui/imgui.cmake @@ -1,6 +1,6 @@ -include(ExternalProject) +include(FetchContent) -ExternalProject_Add( +FetchContent_Declare( ext_imgui PREFIX imgui URL https://github.com/ocornut/imgui/archive/refs/tags/v1.88.tar.gz @@ -12,5 +12,5 @@ ExternalProject_Add( INSTALL_COMMAND "" ) -ExternalProject_Get_Property(ext_imgui SOURCE_DIR) -set(IMGUI_SOURCE_DIR ${SOURCE_DIR}) +FetchContent_Populate(ext_imgui) +FetchContent_GetProperties(ext_imgui SOURCE_DIR IMGUI_SOURCE_DIR) diff --git a/3rdparty/qhull/qhull.cmake b/3rdparty/qhull/qhull.cmake index c20c86c471f..fe15e83ca6f 100644 --- a/3rdparty/qhull/qhull.cmake +++ b/3rdparty/qhull/qhull.cmake @@ -1,6 +1,6 @@ -include(ExternalProject) +include(FetchContent) -ExternalProject_Add( +FetchContent_Declare( ext_qhull PREFIX qhull # v8.0.0+ causes seg fault @@ -14,5 +14,5 @@ ExternalProject_Add( INSTALL_COMMAND "" ) -ExternalProject_Get_Property(ext_qhull SOURCE_DIR) -set(QHULL_SOURCE_DIR ${SOURCE_DIR}) +FetchContent_Populate(ext_qhull) +FetchContent_GetProperties(ext_qhull SOURCE_DIR QHULL_SOURCE_DIR) From 15624686c4fffdde9ad7b6b102a498f593436906 Mon Sep 17 00:00:00 2001 From: Ahmed Elsayed Date: Tue, 16 Apr 2024 18:52:48 +0200 Subject: [PATCH 13/82] Update ispc binary path on Windows, update civetweb to v1.16 (#6577) --- 3rdparty/civetweb/civetweb.cmake | 4 ++-- cmake/Open3DFetchISPCCompiler.cmake | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/3rdparty/civetweb/civetweb.cmake b/3rdparty/civetweb/civetweb.cmake index dc8d556ea64..5d4749fe4e7 100644 --- a/3rdparty/civetweb/civetweb.cmake +++ b/3rdparty/civetweb/civetweb.cmake @@ -3,8 +3,8 @@ include(ExternalProject) ExternalProject_Add( ext_civetweb PREFIX civetweb - URL https://github.com/civetweb/civetweb/archive/refs/tags/v1.15.tar.gz - URL_HASH SHA256=90a533422944ab327a4fbb9969f0845d0dba05354f9cacce3a5005fa59f593b9 + URL https://github.com/civetweb/civetweb/archive/refs/tags/v1.16.tar.gz + URL_HASH SHA256=f0e471c1bf4e7804a6cfb41ea9d13e7d623b2bcc7bc1e2a4dd54951a24d60285 DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/civetweb" UPDATE_COMMAND "" CMAKE_ARGS diff --git a/cmake/Open3DFetchISPCCompiler.cmake b/cmake/Open3DFetchISPCCompiler.cmake index 3f7940c648b..1753b52325e 100644 --- a/cmake/Open3DFetchISPCCompiler.cmake +++ b/cmake/Open3DFetchISPCCompiler.cmake @@ -50,7 +50,11 @@ function(open3d_fetch_ispc_compiler) ) FetchContent_MakeAvailable(ext_ispc) + if (WIN32) + set(CMAKE_ISPC_COMPILER "${ext_ispc_SOURCE_DIR}/bin/ispc.exe" PARENT_SCOPE) + else() # Linux + set(CMAKE_ISPC_COMPILER "${ext_ispc_SOURCE_DIR}/bin/ispc" PARENT_SCOPE) + endif() - set(CMAKE_ISPC_COMPILER "${ext_ispc_SOURCE_DIR}/bin/ispc" PARENT_SCOPE) endif() endfunction() From a4a173e566e5935b89b97de610ae54622a416b55 Mon Sep 17 00:00:00 2001 From: Jan Lebert Date: Tue, 16 Apr 2024 17:40:58 -0700 Subject: [PATCH 14/82] Add macOS arm64 CI runner, fix macOS arm64 builds, fused multi-platform viewer app and python wheels (#6695) * Test macOS arm64 runner * Use matrix to run jobs for both Intel and Apple SIlicon * cleanup workflow, try to fix libomp related issue * Fuse x64 and arm64 wheels, fix wheel naming & unit tests * embree 4.3.1: fix macOS arm64 compile * Add prebuilt arm64 filament binaries * Fuse viewer app * Documentation CI fix by adding lxml[html_clean] to requirements. * Fix for uploading linux CUDA artifacts to github name clash --------- Co-authored-by: Sameer Sheorey --- .github/workflows/macos.yml | 207 +++++++++++++++++--- .github/workflows/ubuntu-cuda.yml | 2 +- 3rdparty/embree/embree.cmake | 10 +- 3rdparty/filament/filament_build.cmake | 1 + 3rdparty/filament/filament_download.cmake | 14 +- 3rdparty/librealsense/fix-macos-arm64.patch | 22 +++ 3rdparty/librealsense/librealsense.cmake | 3 + CHANGELOG.md | 1 + CMakeLists.txt | 17 +- cpp/tests/geometry/TriangleMesh.cpp | 145 +++++++++++--- docs/getting_started.in.rst | 6 +- docs/requirements.txt | 4 +- python/setup.py | 5 + python/test/ml_ops/test_radius_search.py | 4 +- util/ci_utils.sh | 10 +- 15 files changed, 373 insertions(+), 78 deletions(-) create mode 100644 3rdparty/librealsense/fix-macos-arm64.patch diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 16b242d081b..4ac23fd67a8 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -26,10 +26,13 @@ env: jobs: MacOS: - runs-on: macos-12 + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: + # macos-12 is Intel runner, macos-14 is Apple Silicon + # https://github.com/actions/runner-images + os: [macos-12, macos-14] CONFIG: [ON, OFF] env: BUILD_SHARED_LIBS: ${{ matrix.CONFIG }} @@ -47,26 +50,49 @@ jobs: path: ~/.ccache # We include the commit sha in the cache key, as new cache entries are # only created if there is no existing entry for the key yet. - key: ${{ runner.os }}-ccache-${{ github.sha }} + key: ${{ runner.os }}-${{ runner.arch }}-ccache-${{ github.sha }} # Restore any ccache cache entry, if none for - # ${{ runner.os }}-ccache-${{ github.sha }} exists. + # ${{ runner.os }}-${{ runner.arch }}-ccache-${{ github.sha }} exists. # Common prefix will be used so that ccache can be used across commits. restore-keys: | - ${{ runner.os }}-ccache + ${{ runner.os }}-${{ runner.arch }}-ccache + - name: Set up Python version uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: '3.11' + - name: Install dependencies run: | brew install ccache pkg-config - # Install libomp 11.1.0 from old brew bottle for catalina (10.15). - # Directly installing the Ruby formula will install for the current OS + + if [[ ${{ runner.arch}} == "ARM64" ]]; then + # Fix gfortran not found issue + ln -s $(which gfortran-13) /usr/local/bin/gfortran + + # Default macos-14 image Xcode (version 15.0.1) linker causes build issues. + # Newer Xcode versions work, but embree recommends Apple clang <= 14 on + # arm64 to avoid possible "EXEC_BAD_INSTRUCTION" runtime exceptions: + # https://github.com/embree/embree/releases/tag/v4.3.1 + sudo xcode-select -switch /Applications/Xcode_14.3.1.app + fi + + # Install libomp 11.1.0 from old brew bottle for x64 catalina (10.15) + # / arm64 big sur (11.0). Directly installing the Ruby formula will + # install for the current OS. # https://github.com/microsoft/LightGBM/issues/4229 - brew unlink libomp - curl -L -H "Authorization: Bearer QQ==" -o libomp-11.1.0.catalina.bottle.tar.gz \ - https://ghcr.io/v2/homebrew/core/libomp/blobs/sha256:45a5aa653bd45bd5ff5858580b1a4670c4b5a51ea29d68d45a53f72f56010e05 - brew install -f libomp-11.1.0.catalina.bottle.tar.gz + if [[ ${{ runner.arch}} == "X64" ]]; then + brew unlink libomp + # x64 catalina (10.15) bottle + export LIBOMP_BOTTLE_HASH=45a5aa653bd45bd5ff5858580b1a4670c4b5a51ea29d68d45a53f72f56010e05 + else # ARM64 + # arm64 big_sur (11.0) bottle + export LIBOMP_BOTTLE_HASH=f87f7841eb8b72650fa771af39642361aec371ea1a1f94f081ecc0e8168a0e75 + fi + curl -L -H "Authorization: Bearer QQ==" -o libomp-11.1.0.bottle.tar.gz \ + https://ghcr.io/v2/homebrew/core/libomp/blobs/sha256:$LIBOMP_BOTTLE_HASH + brew install -f libomp-11.1.0.bottle.tar.gz + ccache -M 2G # See .github/workflows/readme.md for ccache strategy. - name: Config and build run: | @@ -82,7 +108,7 @@ jobs: pushd build make -j${NPROC} Open3DViewer pushd bin - zip -rv open3d-app-macosx-10_15.zip Open3D.app + zip -rv open3d-app-macosx-10_15-${{ runner.arch}}.zip Open3D.app ccache -s - name: Upload package @@ -116,21 +142,58 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ env.BUILD_SHARED_LIBS == 'OFF' }} with: - name: open3d-app-macosx-10_15 - path: build/bin/open3d-app-macosx-10_15.zip + name: open3d-app-macosx-10_15-${{ runner.arch}} + path: build/bin/open3d-app-macosx-10_15-${{ runner.arch}}.zip + if-no-files-found: error + + fuse-viewer: + name: Fuse x64 and ARM64 viewer app + runs-on: [macos-12] + needs: [MacOS] + steps: + - name: Download viewer apps + uses: actions/download-artifact@v4 + with: + pattern: open3d-app-macosx-10_15-* + merge-multiple: true + + - name: Fuse x64 and arm64 viewer apps + run: | + unzip open3d-app-macosx-10_15-X64.zip -d x64 + unzip open3d-app-macosx-10_15-ARM64.zip -d arm64 + for i in arm64/Open3D.app/Contents/MacOS/*; do + filepath=Open3D.app/Contents/MacOS/$(basename $i) + lipo -create arm64/${filepath} x64/${filepath} -output arm64/${filepath} + done + mv arm64/Open3D.app Open3D.app + zip -rv open3d-app-macosx-10_15-universal2.zip Open3D.app + + - name: Upload Open3D viewer app + uses: actions/upload-artifact@v4 + with: + name: open3d-app-macosx-10_15-universal2 + path: open3d-app-macosx-10_15-universal2.zip if-no-files-found: error build-wheel: name: Build wheel - runs-on: macos-12 + runs-on: ${{ matrix.os }} strategy: fail-fast: false # https://github.community/t/how-to-conditionally-include-exclude-items-in-matrix-eg-based-on-branch/16853/6 matrix: + # macos-12 is Intel runner, macos-14 is Apple Silicon + # https://github.com/actions/runner-images + os: [macos-12, macos-14] python_version: ['3.8', '3.9', '3.10', '3.11'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: + # TODO: remove macos-14 excludes when https://github.com/actions/setup-python/issues/808 is fixed + - os: macos-14 + python_version: '3.8' + - os: macos-14 + python_version: '3.9' - is_main: false python_version: '3.8' - is_main: false @@ -158,12 +221,12 @@ jobs: path: ~/.ccache # We include the commit sha in the cache key, as new cache entries are # only created if there is no existing entry for the key yet. - key: ${{ runner.os }}-ccache-${{ github.sha }} + key: ${{ runner.os }}-${{ runner.arch }}-ccache-${{ github.sha }} # Restore any ccache cache entry, if none for - # ${{ runner.os }}-ccache-${{ github.sha }} exists. + # ${{ runner.os }}-${{ runner.arch }}-ccache-${{ github.sha }} exists. # Common prefix will be used so that ccache can be used across commits. restore-keys: | - ${{ runner.os }}-ccache + ${{ runner.os }}-${{ runner.arch }}-ccache - name: Set up Python uses: actions/setup-python@v5 @@ -180,12 +243,26 @@ jobs: cmake --version source util/ci_utils.sh install_python_dependencies + + # Fix macos-14 arm64 runner image issues, see comments in MacOS job. + if [[ ${{ runner.arch}} == "ARM64" ]]; then + ln -s $(which gfortran-13) /usr/local/bin/gfortran + sudo xcode-select -switch /Applications/Xcode_14.3.1.app + fi + # Install libomp 11.1.0. See comment above. + if [[ ${{ runner.arch}} == "X64" ]]; then + brew unlink libomp + # x64 catalina (10.15) bottle + export LIBOMP_BOTTLE_HASH=45a5aa653bd45bd5ff5858580b1a4670c4b5a51ea29d68d45a53f72f56010e05 + else # ARM64 + # arm64 big_sur (11.0) bottle + export LIBOMP_BOTTLE_HASH=f87f7841eb8b72650fa771af39642361aec371ea1a1f94f081ecc0e8168a0e75 + fi + curl -L -H "Authorization: Bearer QQ==" -o libomp-11.1.0.bottle.tar.gz \ + https://ghcr.io/v2/homebrew/core/libomp/blobs/sha256:$LIBOMP_BOTTLE_HASH + brew install -f libomp-11.1.0.bottle.tar.gz brew install ccache - brew unlink libomp - curl -L -H "Authorization: Bearer QQ==" -o libomp-11.1.0.catalina.bottle.tar.gz \ - https://ghcr.io/v2/homebrew/core/libomp/blobs/sha256:45a5aa653bd45bd5ff5858580b1a4670c4b5a51ea29d68d45a53f72f56010e05 - brew install -f libomp-11.1.0.catalina.bottle.tar.gz ccache -M 2G # See .github/workflows/readme.md for ccache strategy. - name: Config and build wheel @@ -227,17 +304,97 @@ jobs: gsutil cp build/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} gs://open3d-releases/python-wheels/ echo "Download pip package at: https://storage.googleapis.com/open3d-releases/python-wheels/${{ env.PIP_PKG_NAME }}" + fuse-wheel: + name: Fuse universal2 wheel + runs-on: [macos-12] + needs: [build-wheel] + strategy: + fail-fast: false + # https://github.community/t/how-to-conditionally-include-exclude-items-in-matrix-eg-based-on-branch/16853/6 + matrix: + python_version: ['3.10', '3.11'] + is_main: + - ${{ github.ref == 'refs/heads/main' }} + exclude: + - is_main: false + python_version: '3.10' + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python_version }} + - name: Download X64 wheels + uses: actions/download-artifact@v4 + with: + pattern: open3d-*macosx*_x86_64.whl + path: x64_wheels + merge-multiple: true + - name: Download ARM64 wheels + uses: actions/download-artifact@v4 + with: + pattern: open3d-*macosx*_arm64.whl + path: arm64_wheels + merge-multiple: true + - name: Fuse x64 and ARM64 wheels + env: + python_version: ${{ matrix.python_version }} + run: | + PYTAG="-cp$(echo ${{ env.python_version }} | tr -d '.')" + mkdir universal_wheels + + pip install delocate + delocate-fuse -v x64_wheels/open3d-*${PYTAG}*.whl arm64_wheels/open3d-*${PYTAG}*.whl + + # Normalize file name as delocate-fuse doesn't update it + OLD_WHL_NAME=$(basename x64_wheels/open3d-*${PYTAG}*.whl) + NEW_WHL_NAME=${OLD_WHL_NAME/x86_64/universal2} + mv x64_wheels/${OLD_WHL_NAME} universal_wheels/${NEW_WHL_NAME} + + echo "PIP_PKG_NAME=$NEW_WHL_NAME" >> $GITHUB_ENV + - name: Upload merged wheels + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PIP_PKG_NAME }} + path: universal_wheels/${{ env.PIP_PKG_NAME }} + if-no-files-found: error + + - name: GCloud CLI auth + if: ${{ github.ref == 'refs/heads/main' }} + uses: 'google-github-actions/auth@v2' + with: + project_id: ${{ secrets.GCE_PROJECT }} + credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' + - name: GCloud CLI setup + if: ${{ github.ref == 'refs/heads/main' }} + uses: google-github-actions/setup-gcloud@v2 + with: + version: ${{ env.GCE_CLI_GHA_VERSION }} + project_id: ${{ secrets.GCE_PROJECT }} + + - name: Upload wheel to GCS bucket + if: ${{ github.ref == 'refs/heads/main' }} + env: + python_version: ${{ matrix.python_version }} + run: | + gsutil cp universal_wheels/${{ env.PIP_PKG_NAME }} gs://open3d-releases/python-wheels/ + echo "Download pip package at: https://storage.googleapis.com/open3d-releases/python-wheels/${{ env.PIP_PKG_NAME }}" + test-wheel: name: Test wheel - runs-on: macos-12 + runs-on: ${{ matrix.os }} needs: [build-wheel] strategy: fail-fast: false matrix: + os: [macos-12, macos-14] python_version: ['3.8', '3.9', '3.10', '3.11'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: + - os: macos-14 + python_version: '3.8' + - os: macos-14 + python_version: '3.9' - is_main: false python_version: '3.8' - is_main: false @@ -273,7 +430,7 @@ jobs: python -V source util/ci_utils.sh pi_tag=$(python -c "import sys; print(f'cp{sys.version_info.major}{sys.version_info.minor}')") - test_wheel open3d*-"$pi_tag"-*.whl + test_wheel open3d*-"$pi_tag"-*_$(uname -m).whl - name: Run Python unit tests (benchmarks) run: | @@ -286,7 +443,7 @@ jobs: # no need to run on macOS runs-on: ubuntu-latest if: ${{ github.ref == 'refs/heads/main' }} - needs: [build-wheel, MacOS] + needs: [fuse-wheel, MacOS] steps: - name: GCloud CLI auth uses: 'google-github-actions/auth@v2' diff --git a/.github/workflows/ubuntu-cuda.yml b/.github/workflows/ubuntu-cuda.yml index bc7dcf9f67a..250be1597fb 100644 --- a/.github/workflows/ubuntu-cuda.yml +++ b/.github/workflows/ubuntu-cuda.yml @@ -152,7 +152,7 @@ jobs: if: ${{ env.BUILD_PACKAGE == 'true' }} uses: actions/upload-artifact@v4 with: - name: open3d-devel-linux-x86_64-cuda-${CI_CONFIG} + name: open3d-devel-linux-x86_64-cuda-${{ matrix.CI_CONFIG }} path: open3d-devel-linux*.tar.xz if-no-files-found: error diff --git a/3rdparty/embree/embree.cmake b/3rdparty/embree/embree.cmake index e0794b55da0..35d27f11048 100644 --- a/3rdparty/embree/embree.cmake +++ b/3rdparty/embree/embree.cmake @@ -8,13 +8,11 @@ include(ExternalProject) # select ISAs if(APPLE) if(APPLE_AARCH64) - # Turn off ISA optimizations for Apple ARM64 for now. - set(ISA_ARGS -DEMBREE_ISA_AVX=OFF - -DEMBREE_ISA_AVX2=OFF - -DEMBREE_ISA_AVX512=OFF - -DEMBREE_ISA_SSE2=OFF - -DEMBREE_ISA_SSE42=OFF + set(ISA_ARGS -DEMBREE_ISA_NEON=OFF + -DEMBREE_ISA_NEON2X=ON ) + set(ISA_LIBS embree_avx2) + set(ISA_BUILD_BYPRODUCTS "/${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}embree_avx2${CMAKE_STATIC_LIBRARY_SUFFIX}") else() # With AppleClang we can select only 1 ISA. set(ISA_ARGS -DEMBREE_ISA_AVX=OFF diff --git a/3rdparty/filament/filament_build.cmake b/3rdparty/filament/filament_build.cmake index cf2d650530e..432f55a28c0 100644 --- a/3rdparty/filament/filament_build.cmake +++ b/3rdparty/filament/filament_build.cmake @@ -72,5 +72,6 @@ ExternalProject_Add( -DFILAMENT_SUPPORTS_VULKAN=OFF -DFILAMENT_SKIP_SAMPLES=ON -DFILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB=20 # to support many small entities + -DSPIRV_WERROR=OFF BUILD_BYPRODUCTS ${lib_byproducts} ) diff --git a/3rdparty/filament/filament_download.cmake b/3rdparty/filament/filament_download.cmake index ef41dd17faf..f21cc91c31b 100644 --- a/3rdparty/filament/filament_download.cmake +++ b/3rdparty/filament/filament_download.cmake @@ -26,9 +26,17 @@ else() string(APPEND lib_dir /x86_64/md) endif() elseif(APPLE) - set(FILAMENT_URL https://github.com/google/filament/releases/download/v1.9.19/filament-v1.9.19-mac.tgz) - set(FILAMENT_SHA256 2765d0ce60647fc17d1880c4618cf7d6b5343d8be4dad87978c3917d9c723b4e) - string(APPEND lib_dir /x86_64) + if (APPLE_AARCH64) + set(FILAMENT_URL https://github.com/isl-org/open3d_downloads/releases/download/filament/filament-v1.9.19-macos_arm64.tgz) + set(FILAMENT_SHA256 3422bdff451d90144fbb69e625d8dcaeaf3222dc2c28879536067937955bc362) + string(APPEND lib_dir /arm64) + # Our arm64 builds use FILAMENT_SUPPORTS_VULKAN=OFF + list(REMOVE_ITEM filament_LIBRARIES bluevk) + else() + set(FILAMENT_URL https://github.com/google/filament/releases/download/v1.9.19/filament-v1.9.19-mac.tgz) + set(FILAMENT_SHA256 2765d0ce60647fc17d1880c4618cf7d6b5343d8be4dad87978c3917d9c723b4e) + string(APPEND lib_dir /x86_64) + endif() else() # Linux: Check glibc version and use open3d filament binary if new (Ubuntu 20.04 and similar) execute_process(COMMAND ldd --version OUTPUT_VARIABLE ldd_version) string(REGEX MATCH "([0-9]+\.)+[0-9]+" glibc_version ${ldd_version}) diff --git a/3rdparty/librealsense/fix-macos-arm64.patch b/3rdparty/librealsense/fix-macos-arm64.patch new file mode 100644 index 00000000000..0de73a574a1 --- /dev/null +++ b/3rdparty/librealsense/fix-macos-arm64.patch @@ -0,0 +1,22 @@ +From beb4c44debc8336de991c983274cad841eb5c323 Mon Sep 17 00:00:00 2001 +From: Pavol Rusnak +Date: Sun, 20 Jun 2021 12:26:58 +0200 +Subject: [PATCH] Fix build on macOS arm64 + +--- + src/proc/color-formats-converter.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/proc/color-formats-converter.cpp b/src/proc/color-formats-converter.cpp +index 564a23d9c4..6c6c8c97d8 100644 +--- a/src/proc/color-formats-converter.cpp ++++ b/src/proc/color-formats-converter.cpp +@@ -18,7 +18,7 @@ + #include // For SSSE3 intrinsics + #endif + +-#if defined (ANDROID) || (defined (__linux__) && !defined (__x86_64__)) ++#if defined (ANDROID) || (defined (__linux__) && !defined (__x86_64__)) || (defined (__APPLE__) && !defined (__x86_64__)) + + bool has_avx() { return false; } + diff --git a/3rdparty/librealsense/librealsense.cmake b/3rdparty/librealsense/librealsense.cmake index c6b4e358e32..e5caa700df7 100644 --- a/3rdparty/librealsense/librealsense.cmake +++ b/3rdparty/librealsense/librealsense.cmake @@ -17,6 +17,9 @@ ExternalProject_Add( COMMAND ${GIT_EXECUTABLE} init COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_LIST_DIR}/fix-cudacrt.patch + # Patch for macOS ARM64 support for versions < 2.50.0 + COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace + ${CMAKE_CURRENT_LIST_DIR}/fix-macos-arm64.patch CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DBUILD_SHARED_LIBS=OFF diff --git a/CHANGELOG.md b/CHANGELOG.md index 3509dda95e6..af429044ee2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ - Fix regression in printing cuda tensor from PR #6444 🐛 - Add Python pathlib support for file IO (PR #6619) - Fix log error message for `probability` argument validation in `PointCloud::SegmentPlane` (PR #6622) +- Fix macOS arm64 builds, add CI runner for macOS arm64 (PR #6695) ## 0.13 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d03dad7dc6..7abd4307b91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,11 +12,6 @@ cmake_minimum_required(VERSION 3.20) # CMake 3.20+ is required to: # - detect IntelLLVM compiler for SYCL -if (APPLE) -set (CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING - "Minimum OS X deployment version" FORCE) -endif() - # CMAKE_HOST_SYSTEM_PROCESSOR is only available after calling project(), # which depends on ${OPEN3D_VERSION}, which depends on ${DEVELOPER_BUILD}. if(UNIX AND NOT APPLE) @@ -35,6 +30,11 @@ if(APPLE) ) if(PROCESSOR_ARCH STREQUAL "arm64") set(APPLE_AARCH64 TRUE) + set (CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING + "Minimum OS X deployment version" FORCE) + else() + set (CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING + "Minimum OS X deployment version" FORCE) endif() endif() @@ -117,9 +117,12 @@ option(USE_SYSTEM_VTK "Use system pre-installed VTK" OFF option(USE_SYSTEM_ZEROMQ "Use system pre-installed ZeroMQ" OFF) if(LINUX_AARCH64 OR APPLE_AARCH64) option(BUILD_VTK_FROM_SOURCE "Build VTK from source" ON ) - option(BUILD_FILAMENT_FROM_SOURCE "Build filament from source" ON ) else() option(BUILD_VTK_FROM_SOURCE "Build VTK from source" OFF) +endif() +if(LINUX_AARCH64) + option(BUILD_FILAMENT_FROM_SOURCE "Build filament from source" ON ) +else() option(BUILD_FILAMENT_FROM_SOURCE "Build filament from source" OFF) endif() @@ -198,7 +201,7 @@ cmake_policy(GET CMP0072 CMP0072_VALUE) if ((LINUX_AARCH64 OR APPLE_AARCH64) AND BUILD_ISPC_MODULE) message(FATAL_ERROR "ISPC module is not yet supported on ARM Linux") endif() -if ((LINUX_AARCH64 OR APPLE_AARCH64) AND NOT BUILD_FILAMENT_FROM_SOURCE) +if (LINUX_AARCH64 AND NOT BUILD_FILAMENT_FROM_SOURCE) message(FATAL_ERROR "ARM CPU detected, you must set BUILD_FILAMENT_FROM_SOURCE=ON.") endif() if ((LINUX_AARCH64 OR APPLE_AARCH64) AND NOT USE_BLAS) diff --git a/cpp/tests/geometry/TriangleMesh.cpp b/cpp/tests/geometry/TriangleMesh.cpp index 559330bb2a0..ff6d75f6c8c 100644 --- a/cpp/tests/geometry/TriangleMesh.cpp +++ b/cpp/tests/geometry/TriangleMesh.cpp @@ -1843,33 +1843,121 @@ TEST(TriangleMesh, CreateFromPointCloudPoisson) { {0.742035, 0.885688, 0.458892}, {0.742035, 0.885688, 0.458892}, {0.383097, 0.761093, 0.173810}, {0.284898, 0.359292, 0.669062}}; mesh_gt.triangles_ = { - {1, 13, 0}, {0, 14, 2}, {13, 14, 0}, {1, 15, 13}, - {15, 16, 13}, {1, 3, 15}, {14, 13, 16}, {16, 17, 14}, - {2, 18, 4}, {14, 18, 2}, {4, 18, 5}, {18, 14, 17}, - {17, 19, 18}, {18, 19, 6}, {6, 5, 18}, {7, 16, 15}, - {7, 8, 16}, {8, 20, 16}, {21, 16, 20}, {17, 16, 21}, - {9, 20, 8}, {10, 20, 9}, {21, 20, 10}, {22, 17, 21}, - {19, 17, 22}, {19, 22, 11}, {11, 6, 19}, {22, 21, 10}, - {10, 12, 22}, {11, 22, 12}, {24, 0, 23}, {1, 0, 24}, - {0, 2, 25}, {25, 23, 0}, {3, 1, 24}, {24, 26, 3}, - {2, 4, 27}, {27, 25, 2}, {27, 5, 28}, {4, 5, 27}, - {28, 6, 29}, {5, 6, 28}, {8, 7, 30}, {30, 31, 8}, - {32, 8, 31}, {9, 8, 32}, {33, 9, 32}, {10, 9, 33}, - {6, 11, 34}, {34, 29, 6}, {12, 10, 33}, {33, 35, 12}, - {34, 12, 35}, {11, 12, 34}, {24, 23, 48}, {36, 23, 25}, - {36, 37, 23}, {37, 48, 23}, {38, 24, 48}, {38, 39, 24}, - {39, 26, 24}, {39, 49, 26}, {38, 48, 37}, {25, 27, 40}, - {40, 36, 25}, {27, 28, 41}, {41, 40, 27}, {28, 29, 42}, - {42, 41, 28}, {39, 30, 49}, {39, 31, 30}, {39, 50, 31}, - {39, 43, 50}, {44, 50, 43}, {32, 31, 50}, {44, 32, 50}, - {44, 45, 32}, {45, 33, 32}, {42, 34, 46}, {29, 34, 42}, - {47, 33, 45}, {35, 33, 47}, {34, 35, 47}, {47, 46, 34}, - {37, 36, 51}, {39, 38, 52}, {53, 52, 51}, {52, 37, 51}, - {52, 38, 37}, {36, 40, 54}, {54, 51, 36}, {54, 40, 41}, - {51, 54, 55}, {55, 53, 51}, {55, 41, 42}, {54, 41, 55}, - {43, 39, 52}, {56, 52, 53}, {56, 44, 52}, {44, 43, 52}, - {45, 44, 56}, {56, 55, 57}, {53, 55, 56}, {55, 42, 46}, - {46, 57, 55}, {45, 56, 57}, {57, 47, 45}, {57, 46, 47}}; + {1, 13, 0}, + {0, 14, 2}, + {13, 14, 0}, + {1, 15, 13}, + {15, 16, 13}, + {1, 3, 15}, + {14, 13, 16}, + {16, 17, 14}, + {2, 18, 4}, + {14, 18, 2}, + {4, 18, 5}, + {18, 14, 17}, + {17, 19, 18}, + {18, 19, 6}, + {6, 5, 18}, + {7, 16, 15}, + {7, 8, 16}, + {8, 20, 16}, + {21, 16, 20}, + {17, 16, 21}, + {9, 20, 8}, + {10, 20, 9}, + {21, 20, 10}, + {22, 17, 21}, + {19, 17, 22}, + {19, 22, 11}, + {11, 6, 19}, + {22, 21, 10}, + {10, 12, 22}, + {11, 22, 12}, + {24, 0, 23}, + {1, 0, 24}, + {0, 2, 25}, + {25, 23, 0}, + {3, 1, 24}, + {24, 26, 3}, + {2, 4, 27}, + {27, 25, 2}, + {27, 5, 28}, + {4, 5, 27}, + {28, 6, 29}, + {5, 6, 28}, + {8, 7, 30}, + {30, 31, 8}, + {32, 8, 31}, + {9, 8, 32}, + {33, 9, 32}, + {10, 9, 33}, + {6, 11, 34}, + {34, 29, 6}, + {12, 10, 33}, + {33, 35, 12}, + {34, 12, 35}, + {11, 12, 34}, + {24, 23, 48}, + {36, 23, 25}, + {36, 37, 23}, + {37, 48, 23}, + {38, 24, 48}, + {38, 39, 24}, + {39, 26, 24}, + {39, 49, 26}, + {38, 48, 37}, + {25, 27, 40}, + {40, 36, 25}, + {27, 28, 41}, + {41, 40, 27}, + {28, 29, 42}, + {42, 41, 28}, + {39, 30, 49}, + {39, 31, 30}, + {39, 50, 31}, + {39, 43, 50}, + {44, 50, 43}, + {32, 31, 50}, +#if defined(__APPLE__) && defined(__arm64__) + // Apple Silicon consistently triangulates the vertices differently + {44, 45, 50}, + {45, 32, 50}, +#else + {44, 32, 50}, + {44, 45, 32}, +#endif + {45, 33, 32}, + {42, 34, 46}, + {29, 34, 42}, + {47, 33, 45}, + {35, 33, 47}, + {34, 35, 47}, + {47, 46, 34}, + {37, 36, 51}, + {39, 38, 52}, + {53, 52, 51}, + {52, 37, 51}, + {52, 38, 37}, + {36, 40, 54}, + {54, 51, 36}, + {54, 40, 41}, + {51, 54, 55}, + {55, 53, 51}, + {55, 41, 42}, + {54, 41, 55}, + {43, 39, 52}, + {56, 52, 53}, + {56, 44, 52}, + {44, 43, 52}, + {45, 44, 56}, + {56, 55, 57}, + {53, 55, 56}, + {55, 42, 46}, + {46, 57, 55}, + {45, 56, 57}, + {57, 47, 45}, + {57, 46, 47} + }; std::vector densities_gt = { 0.39865168929100037, 0.32580316066741943, 0.39778709411621094, 0.2200755625963211, 0.428702175617218, 0.4288075268268585, @@ -2208,7 +2296,8 @@ TEST(TriangleMesh, CreateMeshCoordinateFrame) { indices.push_back(output_tm->triangles_[i](1, 0)); indices.push_back(output_tm->triangles_[i](2, 0)); } - unique(indices.begin(), indices.end()); + auto last = unique(indices.begin(), indices.end()); + indices.erase(last, indices.end()); sort(indices.begin(), indices.end()); auto output = output_tm->SelectByIndex(indices); diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index b9f390ea835..4d3ccc84214 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -82,9 +82,9 @@ version (``HEAD`` of ``main`` branch): - `Python 3.11 `__ * - MacOS - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ - `Python 3.11 `__ * - Windows diff --git a/docs/requirements.txt b/docs/requirements.txt index 299c15f5a84..a974fc66a1d 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -4,4 +4,6 @@ jinja2==3.1.3 m2r2==0.3.3.post2 matplotlib==3.7.3 nbsphinx==0.9.3 -sphinx==7.1.2 \ No newline at end of file +sphinx==7.1.2 +nbconvert==6.5.4 +lxml[html_clean]==5.2.1 diff --git a/python/setup.py b/python/setup.py index 029c5717180..e32f69ab615 100644 --- a/python/setup.py +++ b/python/setup.py @@ -70,6 +70,11 @@ def get_tag(self): libc.gnu_get_libc_version.restype = ctypes.c_char_p GLIBC_VER = libc.gnu_get_libc_version().decode("utf8").split(".") plat = f"manylinux_{GLIBC_VER[0]}_{GLIBC_VER[1]}{plat[5:]}" + elif plat[:6] == "macosx": + # If the Python interpreter is an universal2 app the resulting wheel is tagged as + # universal2 instead of the current architecture. This is a workaround to fix it. + plat = plat.replace("universal2", platform.machine()) + return python, abi, plat diff --git a/python/test/ml_ops/test_radius_search.py b/python/test/ml_ops/test_radius_search.py index 85b2af0ad0d..b799b2c7602 100644 --- a/python/test/ml_ops/test_radius_search.py +++ b/python/test/ml_ops/test_radius_search.py @@ -103,7 +103,7 @@ def test_radius_search(dtype, ml, num_points_queries, metric, if normalize_distances: gt_dist /= radii[i] - np.testing.assert_allclose(dist, gt_dist, rtol=1e-7, atol=1e-8) + np.testing.assert_allclose(dist, gt_dist, rtol=1e-7, atol=1e-7) @mltest.parametrize.ml_cpu_only @@ -236,4 +236,4 @@ def test_radius_search_batches(ml, batch_size): if normalize_distances: gt_dist /= radii[i] - np.testing.assert_allclose(dist, gt_dist, rtol=1e-7, atol=1e-8) + np.testing.assert_allclose(dist, gt_dist, rtol=1e-7, atol=1e-7) diff --git a/util/ci_utils.sh b/util/ci_utils.sh index 38ff654880e..aa30d2c12ad 100644 --- a/util/ci_utils.sh +++ b/util/ci_utils.sh @@ -55,8 +55,14 @@ install_python_dependencies() { TF_ARCH_DISABLE_NAME=tensorflow-cpu TORCH_GLNX="torch==$TORCH_CUDA_GLNX_VER" else - TF_ARCH_NAME=tensorflow-cpu - TF_ARCH_DISABLE_NAME=tensorflow + # tensorflow-cpu wheels for macOS arm64 are not available + if [[ "$OSTYPE" == "darwin"* ]]; then + TF_ARCH_NAME=tensorflow + TF_ARCH_DISABLE_NAME=tensorflow + else + TF_ARCH_NAME=tensorflow-cpu + TF_ARCH_DISABLE_NAME=tensorflow + fi TORCH_GLNX="torch==$TORCH_CPU_GLNX_VER" fi From 74dcbe87426b15e921bcc28825a5afc5673c9b0f Mon Sep 17 00:00:00 2001 From: Luis Alonso Murillo Rojas Date: Wed, 17 Apr 2024 14:33:21 -0600 Subject: [PATCH 15/82] Fix check in `ConvertFromPinholeCameraParameters` function from `ViewControl` (#6711) --- cpp/open3d/visualization/visualizer/ViewControl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/open3d/visualization/visualizer/ViewControl.cpp b/cpp/open3d/visualization/visualizer/ViewControl.cpp index a4dff55a64a..a4ba2aae314 100644 --- a/cpp/open3d/visualization/visualizer/ViewControl.cpp +++ b/cpp/open3d/visualization/visualizer/ViewControl.cpp @@ -182,8 +182,8 @@ bool ViewControl::ConvertFromPinholeCameraParameters( window_width_ != intrinsic.width_ || std::abs(intrinsic.intrinsic_matrix_(0, 2) - ((double)window_width_ / 2.0 - 0.5)) > threshold || - std::abs(intrinsic.intrinsic_matrix_(1, 2) = - ((double)window_height_ / 2.0 - 0.5)) > threshold)) { + std::abs(intrinsic.intrinsic_matrix_(1, 2) - + ((double)window_height_ / 2.0 - 0.5)) > threshold)) { utility::LogWarning( "[ViewControl] ConvertFromPinholeCameraParameters() failed " "because window height and width do not match."); From 5c39bc00e4c70c3c468317654d84f707900c6064 Mon Sep 17 00:00:00 2001 From: Daniel Rauch Date: Wed, 17 Apr 2024 22:35:54 +0200 Subject: [PATCH 16/82] Fixes and improvements for point selection in O3DVisualizer (fixes #6725) (#6733) - Make points removable by correctly forwarding the key modifier to the already implemented function - Fix bounding box calculation for picked point size selection (manual bbox extend calculation was wrong) - Fix crash occurring when trying to make selection and non-pickable geometries where in scene - Fix copy&paste error in help text for MACOS --- .../gui/PickPointsInteractor.cpp | 65 ++++++++++--------- .../visualizer/O3DVisualizer.cpp | 22 ++++--- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/cpp/open3d/visualization/gui/PickPointsInteractor.cpp b/cpp/open3d/visualization/gui/PickPointsInteractor.cpp index 45ca7c44c1e..63ecfba7955 100644 --- a/cpp/open3d/visualization/gui/PickPointsInteractor.cpp +++ b/cpp/open3d/visualization/gui/PickPointsInteractor.cpp @@ -155,8 +155,6 @@ void PickPointsInteractor::SetPickableGeometry( // TriangleMesh so that occluded points are not selected. points_.clear(); for (auto &pg : geometry) { - lookup_->Add(pg.name, points_.size()); - auto cloud = dynamic_cast(pg.geometry); auto tcloud = dynamic_cast(pg.tgeometry); @@ -166,6 +164,12 @@ void PickPointsInteractor::SetPickableGeometry( auto lineset = dynamic_cast(pg.geometry); auto tlineset = dynamic_cast(pg.tgeometry); + + // only add geometry with pickable points + if (cloud || tcloud || mesh || tmesh || lineset || tlineset) { + lookup_->Add(pg.name, points_.size()); + } + if (cloud) { points_.insert(points_.end(), cloud->points_.begin(), cloud->points_.end()); @@ -268,35 +272,38 @@ void PickPointsInteractor::SetOnStartedPolygonPicking( } void PickPointsInteractor::Mouse(const MouseEvent &e) { - if (e.type == MouseEvent::BUTTON_UP) { - if (e.modifiers & int(KeyModifier::ALT)) { - if (pending_.empty() || pending_.back().keymods == 0) { - pending_.push({{gui::Point(e.x, e.y)}, int(KeyModifier::ALT)}); - if (on_ui_changed_) { - on_ui_changed_({}); - } - } else { - pending_.back().polygon.push_back(gui::Point(e.x, e.y)); - if (on_started_poly_pick_) { - on_started_poly_pick_(); - } - if (on_ui_changed_) { - std::vector lines; - auto &polygon = pending_.back().polygon; - for (size_t i = 1; i < polygon.size(); ++i) { - auto &p0 = polygon[i - 1]; - auto &p1 = polygon[i]; - lines.push_back({p0.x, p0.y}); - lines.push_back({p1.x, p1.y}); - } - lines.push_back({polygon.back().x, polygon.back().y}); - lines.push_back({polygon[0].x, polygon[0].y}); - on_ui_changed_(lines); - } + if (e.type != MouseEvent::BUTTON_UP) return; + + bool polygon_picking_requested = e.modifiers & int(KeyModifier::ALT); + if (!polygon_picking_requested) { + // standard point picking + pending_.push({{gui::Point(e.x, e.y)}, e.modifiers}); + DoPick(); + } else { + // special polygon picking mode + if (pending_.empty() || pending_.back().keymods == 0) { + pending_.push({{gui::Point(e.x, e.y)}, e.modifiers}); + if (on_ui_changed_) { + on_ui_changed_({}); } } else { - pending_.push({{gui::Point(e.x, e.y)}, 0}); - DoPick(); + pending_.back().polygon.push_back(gui::Point(e.x, e.y)); + if (on_started_poly_pick_) { + on_started_poly_pick_(); + } + if (on_ui_changed_) { + std::vector lines; + auto &polygon = pending_.back().polygon; + for (size_t i = 1; i < polygon.size(); ++i) { + auto &p0 = polygon[i - 1]; + auto &p1 = polygon[i]; + lines.push_back({p0.x, p0.y}); + lines.push_back({p1.x, p1.y}); + } + lines.push_back({polygon.back().x, polygon.back().y}); + lines.push_back({polygon[0].x, polygon[0].y}); + on_ui_changed_(lines); + } } } } diff --git a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp index 3ab06cec1b9..554d38b2466 100644 --- a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp +++ b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp @@ -384,7 +384,9 @@ struct O3DVisualizer::Impl { std::vector>> &indices, int keymods) { - if ((keymods & int(KeyModifier::SHIFT)) || + bool unselect_mode_requested = + keymods & int(KeyModifier::SHIFT); + if (unselect_mode_requested || polygon_selection_unselects_) { selections_->UnselectIndices(indices); } else { @@ -488,11 +490,13 @@ struct O3DVisualizer::Impl { }); #if __APPLE__ - const char *selection_help = - "Cmd-click to select a point\nCmd-ctrl-click to polygon select"; + const char *selection_help = R"(Cmd-click to select a point +Cmd-shift-click to deselect a point +Cmd-alt-click to polygon select)"; #else - const char *selection_help = - "Ctrl-click to select a point\nCmd-alt-click to polygon select"; + const char *selection_help = R"(Ctrl-click to select a point +Ctrl-shift-click to deselect a point +Ctrl-alt-click to polygon select)"; #endif // __APPLE__ h = new Horiz(); h->AddStretch(); @@ -1545,12 +1549,10 @@ struct O3DVisualizer::Impl { OverrideMaterial(o.name, o.material, ui_state_.scene_shader); } - auto bbox = scene_->GetScene()->GetBoundingBox(); - auto xdim = bbox.max_bound_.x() - bbox.min_bound_.x(); - auto ydim = bbox.max_bound_.y() - bbox.min_bound_.z(); - auto zdim = bbox.max_bound_.z() - bbox.min_bound_.y(); + auto bbox_extend = scene_->GetScene()->GetBoundingBox().GetExtent(); auto psize = double(std::max(5, px)) * 0.000666 * - std::max(xdim, std::max(ydim, zdim)); + std::max(bbox_extend.x(), + std::max(bbox_extend.y(), bbox_extend.z())); selections_->SetPointSize(psize); scene_->SetPickablePointSize(px); From 785878f062aac4e52b791ad726504afddcfc26c2 Mon Sep 17 00:00:00 2001 From: Pier Angelo Vendrame Date: Thu, 18 Apr 2024 01:03:47 +0200 Subject: [PATCH 17/82] Simplify data_ and data_interface_ in KDTreeFlann. (#6734) KDTreeFlann::SetRawData had some confusion around data, data_, and data_interface_. As a result, the data from the parameter was copied to the member std::vector, but the interface (which takes an Eigen::Map) used the data passed as an argument to the method. As a result, the searches risked to be run on a dangling pointer, instead of the intended internal storage. However, rather than adjusting the pointer for Eigen::Map, I preferred simplifying the code, and copy the argument to an Eigen::MatrixXd. --- CHANGELOG.md | 2 ++ cpp/open3d/geometry/KDTreeFlann.cpp | 21 ++++++--------------- cpp/open3d/geometry/KDTreeFlann.h | 14 +++++--------- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af429044ee2..b68b0bdbd02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## Main + - Fix TriangleMesh::SamplePointsUniformly not sampling triangle meshes uniformly (PR #6653) - Fix tensor based TSDF integration example. - Use GLIBCXX_USE_CXX11_ABI=ON by default @@ -33,6 +34,7 @@ - Add Python pathlib support for file IO (PR #6619) - Fix log error message for `probability` argument validation in `PointCloud::SegmentPlane` (PR #6622) - Fix macOS arm64 builds, add CI runner for macOS arm64 (PR #6695) +- Fix KDTreeFlann possibly using a dangling pointer instead of internal storage and simplified its members (PR #6734) ## 0.13 diff --git a/cpp/open3d/geometry/KDTreeFlann.cpp b/cpp/open3d/geometry/KDTreeFlann.cpp index d93175a8d43..c81814a724d 100644 --- a/cpp/open3d/geometry/KDTreeFlann.cpp +++ b/cpp/open3d/geometry/KDTreeFlann.cpp @@ -97,8 +97,7 @@ int KDTreeFlann::SearchKNN(const T &query, // This is optimized code for heavily repeated search. // Other flann::Index::knnSearch() implementations lose performance due to // memory allocation/deallocation. - if (data_.empty() || dataset_size_ <= 0 || - size_t(query.rows()) != dimension_ || knn < 0) { + if (data_.size() == 0 || query.rows() != data_.rows() || knn < 0) { return -1; } indices.resize(knn); @@ -121,8 +120,7 @@ int KDTreeFlann::SearchRadius(const T &query, // Since max_nn is not given, we let flann to do its own memory management. // Other flann::Index::radiusSearch() implementations lose performance due // to memory management and CPU caching. - if (data_.empty() || dataset_size_ <= 0 || - size_t(query.rows()) != dimension_) { + if (data_.size() == 0 || query.rows() != data_.rows()) { return -1; } std::vector> indices_dists; @@ -148,8 +146,7 @@ int KDTreeFlann::SearchHybrid(const T &query, // It is also the recommended setting for search. // Other flann::Index::radiusSearch() implementations lose performance due // to memory allocation/deallocation. - if (data_.empty() || dataset_size_ <= 0 || - size_t(query.rows()) != dimension_ || max_nn < 0) { + if (data_.size() == 0 || query.rows() != data_.rows() || max_nn < 0) { return -1; } distance2.resize(max_nn); @@ -166,18 +163,12 @@ int KDTreeFlann::SearchHybrid(const T &query, } bool KDTreeFlann::SetRawData(const Eigen::Map &data) { - dimension_ = data.rows(); - dataset_size_ = data.cols(); - if (dimension_ == 0 || dataset_size_ == 0) { + if (data.size() == 0) { utility::LogWarning("[KDTreeFlann::SetRawData] Failed due to no data."); return false; } - data_.resize(dataset_size_ * dimension_); - memcpy(data_.data(), data.data(), - dataset_size_ * dimension_ * sizeof(double)); - data_interface_.reset(new Eigen::Map(data)); - nanoflann_index_.reset( - new KDTree_t(dimension_, std::cref(*data_interface_), 15)); + data_ = data; + nanoflann_index_ = std::make_unique(data_.rows(), data_, 15); nanoflann_index_->index_->buildIndex(); return true; } diff --git a/cpp/open3d/geometry/KDTreeFlann.h b/cpp/open3d/geometry/KDTreeFlann.h index 3d3ab2c5bea..cb41fccafb7 100644 --- a/cpp/open3d/geometry/KDTreeFlann.h +++ b/cpp/open3d/geometry/KDTreeFlann.h @@ -97,17 +97,13 @@ class KDTreeFlann { bool SetRawData(const Eigen::Map &data); protected: - using KDTree_t = nanoflann::KDTreeEigenMatrixAdaptor< - Eigen::Map, - -1, - nanoflann::metric_L2, - false>; + using KDTree_t = nanoflann::KDTreeEigenMatrixAdaptor; - std::vector data_; - std::unique_ptr> data_interface_; + Eigen::MatrixXd data_; std::unique_ptr nanoflann_index_; - size_t dimension_ = 0; - size_t dataset_size_ = 0; }; } // namespace geometry From 5c982c7b5edc76f899860e2594a950c5c23ec88f Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:15:13 -0700 Subject: [PATCH 18/82] Convert triangle mesh model to tmesh map (#6758) - ToString() for Material and string representation in Python. - MaterialRecord to Material conversion - allows converting triangle mesh models to a map of tmeshes, including materials. Limitation: Only one material per tmesh (same as TriangleMeshModel) - Support for emissive color reading in FileASSIMP (model) and writing for tmesh (tio FIleASSIMP) --- cpp/open3d/io/file_format/FileASSIMP.cpp | 3 + cpp/open3d/t/geometry/TriangleMesh.cpp | 25 +++++- cpp/open3d/t/geometry/TriangleMesh.h | 27 ++++++- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 10 ++- .../visualization/rendering/Material.cpp | 78 +++++++++++++++++++ cpp/open3d/visualization/rendering/Material.h | 16 ++++ cpp/pybind/t/geometry/trianglemesh.cpp | 34 ++++++-- .../visualization/rendering/material.cpp | 4 + cpp/tests/t/geometry/TriangleMesh.cpp | 1 + docs/getting_started.in.rst | 9 ++- 10 files changed, 190 insertions(+), 17 deletions(-) diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index e78d8be7718..92a2fda8077 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -422,6 +422,9 @@ bool ReadModelUsingAssimp(const std::string& filename, mat->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, o3d_mat.base_clearcoat_roughness); mat->Get(AI_MATKEY_ANISOTROPY, o3d_mat.base_anisotropy); + mat->Get(AI_MATKEY_COLOR_EMISSIVE, color); + o3d_mat.emissive_color = + Eigen::Vector4f(color.r, color.g, color.b, 1.f); aiString alpha_mode; mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alpha_mode); std::string alpha_mode_str(alpha_mode.C_Str()); diff --git a/cpp/open3d/t/geometry/TriangleMesh.cpp b/cpp/open3d/t/geometry/TriangleMesh.cpp index 2bf329838cc..43899aaf58f 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.cpp +++ b/cpp/open3d/t/geometry/TriangleMesh.cpp @@ -377,6 +377,7 @@ geometry::TriangleMesh TriangleMesh::FromLegacy( tmat.SetAnisotropy(mat.baseAnisotropy); tmat.SetBaseClearcoat(mat.baseClearCoat); tmat.SetBaseClearcoatRoughness(mat.baseClearCoatRoughness); + // no emissive_color in legacy mesh material if (mat.albedo) tmat.SetAlbedoMap(Image::FromLegacy(*mat.albedo)); if (mat.normalMap) tmat.SetNormalMap(Image::FromLegacy(*mat.normalMap)); if (mat.roughness) @@ -453,10 +454,6 @@ open3d::geometry::TriangleMesh TriangleMesh::ToLegacy() const { legacy_mat.baseColor.f4[1] = tmat.GetBaseColor().y(); legacy_mat.baseColor.f4[2] = tmat.GetBaseColor().z(); legacy_mat.baseColor.f4[3] = tmat.GetBaseColor().w(); - utility::LogWarning("{},{},{},{}", legacy_mat.baseColor.f4[0], - legacy_mat.baseColor.f4[1], - legacy_mat.baseColor.f4[2], - legacy_mat.baseColor.f4[3]); } if (tmat.HasBaseRoughness()) { legacy_mat.baseRoughness = tmat.GetBaseRoughness(); @@ -523,6 +520,26 @@ open3d::geometry::TriangleMesh TriangleMesh::ToLegacy() const { return mesh_legacy; } +std::unordered_map +TriangleMesh::FromTriangleMeshModel( + const open3d::visualization::rendering::TriangleMeshModel &model, + core::Dtype float_dtype, + core::Dtype int_dtype, + const core::Device &device) { + std::unordered_map tmeshes; + for (const auto &mobj : model.meshes_) { + auto tmesh = TriangleMesh::FromLegacy(*mobj.mesh, float_dtype, + int_dtype, device); + // material textures will be on the CPU. GPU resident texture images is + // not yet supported. See comment in Material.cpp + tmesh.SetMaterial( + visualization::rendering::Material::FromMaterialRecord( + model.materials_[mobj.material_idx])); + tmeshes.emplace(mobj.mesh_name, tmesh); + } + return tmeshes; +} + TriangleMesh TriangleMesh::To(const core::Device &device, bool copy) const { if (!copy && GetDevice() == device) { return *this; diff --git a/cpp/open3d/t/geometry/TriangleMesh.h b/cpp/open3d/t/geometry/TriangleMesh.h index 4a1f9138fd6..fc592b0a9f9 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.h +++ b/cpp/open3d/t/geometry/TriangleMesh.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include "open3d/core/Tensor.h" #include "open3d/core/TensorCheck.h" @@ -16,6 +17,7 @@ #include "open3d/t/geometry/DrawableGeometry.h" #include "open3d/t/geometry/Geometry.h" #include "open3d/t/geometry/TensorMap.h" +#include "open3d/visualization/rendering/Model.h" namespace open3d { namespace t { @@ -701,7 +703,8 @@ class TriangleMesh : public Geometry, public DrawableGeometry { /// values, e.g. vertices, normals, colors. /// \param int_dtype Int32 or Int64, used to store index values, e.g. /// triangles. - /// \param device The device where the resulting TriangleMesh resides in. + /// \param device The device where the resulting TriangleMesh resides in + /// (default CPU:0). static geometry::TriangleMesh FromLegacy( const open3d::geometry::TriangleMesh &mesh_legacy, core::Dtype float_dtype = core::Float32, @@ -711,6 +714,28 @@ class TriangleMesh : public Geometry, public DrawableGeometry { /// Convert to a legacy Open3D TriangleMesh. open3d::geometry::TriangleMesh ToLegacy() const; + /// Convert a TriangleMeshModel (e.g. as read from a file with + /// open3d::io::ReadTriangleMeshModel) to an unordered map of mesh names to + /// TriangleMeshes. Only one material is supported per mesh. Materials + /// common to multiple meshes will be dupicated. Textures (as + /// t::geometry::Image) will use shared storage. + /// \param model TriangleMeshModel to convert. + /// \param float_dtype Float32 or Float64, used to store floating point + /// values, e.g. vertices, normals, colors. + /// \param int_dtype Int32 or Int64, used to store index values, e.g. + /// triangles. + /// \param device The device where the resulting TriangleMesh resides in + /// (default CPU:0). Material textures use CPU storage - GPU resident + /// texture images are not yet supported. + /// \return unordered map of constituent mesh names to TriangleMeshes, with + /// materials. + static std::unordered_map + FromTriangleMeshModel( + const open3d::visualization::rendering::TriangleMeshModel &model, + core::Dtype float_dtype = core::Float32, + core::Dtype int_dtype = core::Int64, + const core::Device &device = core::Device("CPU:0")); + /// Compute the convex hull of the triangle mesh using qhull. /// /// This runs on the CPU. diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 5eae7a9ffdc..514d59fca1b 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -6,6 +6,7 @@ // ---------------------------------------------------------------------------- #include +#include #include #include @@ -357,12 +358,17 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, auto r = mesh.GetMaterial().GetBaseClearcoatRoughness(); ai_mat->AddProperty(&r, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); } + if (mesh.GetMaterial().HasEmissiveColor()) { + auto c = mesh.GetMaterial().GetEmissiveColor(); + auto ac = aiColor4D(c.x(), c.y(), c.z(), c.w()); + ai_mat->AddProperty(&ac, 1, AI_MATKEY_COLOR_EMISSIVE); + } // Count texture maps... // NOTE: GLTF2 expects a single combined roughness/metal map. If the // model has one we just export it, otherwise if both roughness and - // metal maps are avaialbe we combine them, otherwise if only one or the - // other is available we just export the one map. + // metal maps are available we combine them, otherwise if only one or + // the other is available we just export the one map. int n_textures = 0; if (mesh.GetMaterial().HasAlbedoMap()) ++n_textures; if (mesh.GetMaterial().HasNormalMap()) ++n_textures; diff --git a/cpp/open3d/visualization/rendering/Material.cpp b/cpp/open3d/visualization/rendering/Material.cpp index fad82623382..d264711f987 100644 --- a/cpp/open3d/visualization/rendering/Material.cpp +++ b/cpp/open3d/visualization/rendering/Material.cpp @@ -26,6 +26,7 @@ void Material::SetDefaultProperties() { SetTransmission(1.f); SetAbsorptionColor(Eigen::Vector4f(1.f, 1.f, 1.f, 1.f)); SetAbsorptionDistance(1.f); + SetEmissiveColor(Eigen::Vector4f(1.f, 1.f, 1.f, 1.f)); SetPointSize(3.f); SetLineWidth(1.f); } @@ -39,6 +40,24 @@ void Material::SetTextureMap(const std::string &key, texture_maps_[key] = image.To(core::Device("CPU:0"), true); } +std::string Material::ToString() const { + if (!IsValid()) { + return "Invalid Material\n"; + } + std::ostringstream os; + os << "Material " << material_name_ << '\n'; + for (const auto &kv : scalar_properties_) { + os << '\t' << kv.first << ": " << kv.second << '\n'; + } + for (const auto &kv : vector_properties_) { + os << '\t' << kv.first << ": " << kv.second.transpose() << '\n'; + } + for (const auto &kv : texture_maps_) { + os << '\t' << kv.first << ": " << kv.second.ToString() << '\n'; + } + return os.str(); +} + void Material::ToMaterialRecord(MaterialRecord &record) const { record.shader = GetMaterialName(); // Convert base material properties @@ -63,6 +82,9 @@ void Material::ToMaterialRecord(MaterialRecord &record) const { if (HasAnisotropy()) { record.base_anisotropy = GetAnisotropy(); } + if (HasEmissiveColor()) { + record.emissive_color = GetEmissiveColor(); + } if (HasThickness()) { record.thickness = GetThickness(); } @@ -124,6 +146,62 @@ void Material::ToMaterialRecord(MaterialRecord &record) const { } } +Material Material::FromMaterialRecord(const MaterialRecord &record) { + using t::geometry::Image; + Material tmat(record.shader); + // scalar and vector properties + tmat.SetBaseColor(record.base_color); + tmat.SetBaseMetallic(record.base_metallic); + tmat.SetBaseRoughness(record.base_roughness); + tmat.SetBaseReflectance(record.base_reflectance); + tmat.SetBaseClearcoat(record.base_clearcoat); + tmat.SetBaseClearcoatRoughness(record.base_clearcoat_roughness); + tmat.SetAnisotropy(record.base_anisotropy); + tmat.SetEmissiveColor(record.emissive_color); + // refractive materials + tmat.SetThickness(record.thickness); + tmat.SetTransmission(record.transmission); + tmat.SetAbsorptionDistance(record.absorption_distance); + // points and lines + tmat.SetPointSize(record.point_size); + tmat.SetLineWidth(record.line_width); + // maps + if (record.albedo_img) { + tmat.SetAlbedoMap(Image::FromLegacy(*record.albedo_img)); + } + if (record.normal_img) { + tmat.SetNormalMap(Image::FromLegacy(*record.normal_img)); + } + if (record.ao_img) { + tmat.SetAOMap(Image::FromLegacy(*record.ao_img)); + } + if (record.metallic_img) { + tmat.SetMetallicMap(Image::FromLegacy(*record.metallic_img)); + } + if (record.roughness_img) { + tmat.SetRoughnessMap(Image::FromLegacy(*record.roughness_img)); + } + if (record.reflectance_img) { + tmat.SetReflectanceMap(Image::FromLegacy(*record.reflectance_img)); + } + if (record.clearcoat_img) { + tmat.SetClearcoatMap(Image::FromLegacy(*record.clearcoat_img)); + } + if (record.clearcoat_roughness_img) { + tmat.SetClearcoatRoughnessMap( + Image::FromLegacy(*record.clearcoat_roughness_img)); + } + if (record.anisotropy_img) { + tmat.SetAnisotropyMap(Image::FromLegacy(*record.anisotropy_img)); + } + if (record.ao_rough_metal_img) { + tmat.SetAORoughnessMetalMap( + Image::FromLegacy(*record.ao_rough_metal_img)); + } + + return tmat; +} + } // namespace rendering } // namespace visualization } // namespace open3d diff --git a/cpp/open3d/visualization/rendering/Material.h b/cpp/open3d/visualization/rendering/Material.h index 7ee1396b281..27afcd8a69a 100644 --- a/cpp/open3d/visualization/rendering/Material.h +++ b/cpp/open3d/visualization/rendering/Material.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include "open3d/t/geometry/Image.h" @@ -34,6 +35,9 @@ class Material { Material(const Material &mat) = default; + /// Convert from MaterialRecord + static Material FromMaterialRecord(const MaterialRecord &mat); + Material &operator=(const Material &other) = default; /// Create an empty but valid material for the specified material name @@ -51,6 +55,9 @@ class Material { /// Get the name of the material. const std::string &GetMaterialName() const { return material_name_; } + /// String reprentation for printing. + std::string ToString() const; + /// Returns the texture map map const TextureMaps &GetTextureMaps() const { return texture_maps_; } @@ -249,6 +256,9 @@ class Material { float GetAbsorptionDistance() const { return GetScalarProperty("absorption_distance"); } + Eigen::Vector4f GetEmissiveColor() const { + return GetVectorProperty("emissive_color"); + } bool HasBaseColor() const { return HasVectorProperty("base_color"); } bool HasBaseMetallic() const { return HasScalarProperty("metallic"); } @@ -267,6 +277,9 @@ class Material { bool HasAbsorptionDistance() const { return HasScalarProperty("absorption_distance"); } + bool HasEmissiveColor() const { + return HasVectorProperty("emissive_color"); + } void SetBaseColor(const Eigen::Vector4f &value) { SetVectorProperty("base_color", value); @@ -295,6 +308,9 @@ class Material { void SetAbsorptionDistance(float value) { SetScalarProperty("absorption_distance", value); } + void SetEmissiveColor(const Eigen::Vector4f &value) { + SetVectorProperty("emissive_color", value); + } //////////////////////////////////////////////////////////////////////////// /// diff --git a/cpp/pybind/t/geometry/trianglemesh.cpp b/cpp/pybind/t/geometry/trianglemesh.cpp index cf245426d58..96307fddc91 100644 --- a/cpp/pybind/t/geometry/trianglemesh.cpp +++ b/cpp/pybind/t/geometry/trianglemesh.cpp @@ -238,6 +238,28 @@ The attributes of the triangle mesh have different levels:: "vertex_dtype"_a = core::Float32, "triangle_dtype"_a = core::Int64, "device"_a = core::Device("CPU:0"), "Create a TriangleMesh from a legacy Open3D TriangleMesh."); + triangle_mesh.def_static( + "from_triangle_mesh_model", &TriangleMesh::FromTriangleMeshModel, + "model"_a, "vertex_dtype"_a = core::Float32, + "triangle_dtype"_a = core::Int64, + "device"_a = core::Device("CPU:0"), + R"(Convert a TriangleMeshModel (e.g. as read from a file with +`open3d.io.read_triangle_mesh_model()`) to a dictionary of mesh names to +triangle meshes with the specified vertex and triangle dtypes and moved to the +specified device. Only a single material per mesh is supported. Materials common +to multiple meshes will be duplicated. Textures (as t.geometry.Image) will use +shared storage on the CPU (GPU resident images for textures is not yet supported). + +Returns: + Dictionary of names to triangle meshes. + +Example: + flight_helmet = o3d.data.FlightHelmetModel() + model = o3d.io.read_triangle_model(flight_helmet.path) + mesh_dict = o3d.t.geometry.TriangleMesh.from_triangle_mesh_model(model) + o3d.visualization.draw(list({"name": name, "geometry": tmesh} for + (name, tmesh) in mesh_dict.items())) + )"); // conversion triangle_mesh.def("to_legacy", &TriangleMesh::ToLegacy, "Convert to a legacy Open3D TriangleMesh."); @@ -706,7 +728,7 @@ This function always uses the CPU device. Returns: This function creates a face attribute "texture_uvs" and returns a tuple - with (max stretch, num_charts, num_partitions) storing the + with (max stretch, num_charts, num_partitions) storing the actual amount of stretch, the number of created charts, and the number of parallel partitions created. @@ -883,7 +905,7 @@ This function always uses the CPU device. "max_faces"_a, R"(Partition the mesh by recursively doing PCA. -This function creates a new face attribute with the name "partition_ids" storing +This function creates a new face attribute with the name "partition_ids" storing the partition id for each face. Args: @@ -892,7 +914,7 @@ the partition id for each face. Example: - This code partitions a mesh such that each partition contains at most 20k + This code partitions a mesh such that each partition contains at most 20k faces:: import open3d as o3d @@ -911,15 +933,15 @@ the partition id for each face. R"(Returns a new mesh with the faces selected by a boolean mask. Args: - mask (open3d.core.Tensor): A boolean mask with the shape (N) with N as the + mask (open3d.core.Tensor): A boolean mask with the shape (N) with N as the number of faces in the mesh. - + Returns: A new mesh with the selected faces. If the original mesh is empty, return an empty mesh. Example: - This code partitions the mesh using PCA and then visualized the individual + This code partitions the mesh using PCA and then visualized the individual parts:: import open3d as o3d diff --git a/cpp/pybind/visualization/rendering/material.cpp b/cpp/pybind/visualization/rendering/material.cpp index 82225c9c90a..3e109dcdbb3 100644 --- a/cpp/pybind/visualization/rendering/material.cpp +++ b/cpp/pybind/visualization/rendering/material.cpp @@ -12,6 +12,7 @@ #include "open3d/visualization/rendering/Material.h" +#include "open3d/visualization/rendering/MaterialRecord.h" #include "pybind/open3d_pybind.h" PYBIND11_MAKE_OPAQUE( @@ -41,6 +42,9 @@ void pybind_material(py::module& m) { mat.def(py::init<>()) .def(py::init(), "", "mat"_a) .def(py::init(), "", "material_name"_a) + .def(py::init(&Material::FromMaterialRecord), "material_record"_a, + "Convert from MaterialRecord.") + .def("__repr__", &Material::ToString) .def("set_default_properties", &Material::SetDefaultProperties, "Fills material with defaults for common PBR material " "properties used by Open3D") diff --git a/cpp/tests/t/geometry/TriangleMesh.cpp b/cpp/tests/t/geometry/TriangleMesh.cpp index b891d6b6986..aa0c75b8b7c 100644 --- a/cpp/tests/t/geometry/TriangleMesh.cpp +++ b/cpp/tests/t/geometry/TriangleMesh.cpp @@ -10,6 +10,7 @@ #include #include "core/CoreTest.h" +#include "open3d/core/Dtype.h" #include "open3d/core/EigenConverter.h" #include "open3d/core/TensorCheck.h" #include "tests/Tests.h" diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index 4d3ccc84214..4bdd9e87d65 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -82,10 +82,10 @@ version (``HEAD`` of ``main`` branch): - `Python 3.11 `__ * - MacOS - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 (x86_64) `__ + - `Python 3.9 (x86_64) `__ + - `Python 3.10 (x86_64+arm64) `__ + - `Python 3.11 (x86_64+arm64) `__ * - Windows - `Python 3.8 `__ @@ -172,6 +172,7 @@ available for the main supported platforms. Also, the latest development version :columns: 2 * `x86_64 `__ + * `arm64 `__ :Windows 10+: .. hlist:: From 008cfb7fb9831f84b6026617e9aa60ab814c701a Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Mon, 6 May 2024 17:26:37 -0700 Subject: [PATCH 19/82] Switch hosting of devel packages from Google cloud to Github releases (#6784) New pre-release devel-main will be continuously updated with the devel packages for the main branch. Also publish devel versions of viewer app. This is to reduce Google cloud cost. --- .github/workflows/documentation.yml | 81 +++++----- .github/workflows/macos.yml | 143 +++++------------- .github/workflows/ubuntu-cuda.yml | 11 +- .github/workflows/ubuntu-wheel.yml | 50 +----- .github/workflows/ubuntu.yml | 25 ++- .github/workflows/windows.yml | 107 +++---------- CMakeLists.txt | 3 +- .../Open3DViewer/Debian/CMakeLists.in.txt | 2 +- docs/getting_started.in.rst | 63 +++++--- 9 files changed, 161 insertions(+), 324 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index b0e289e41cc..6e4c21bc61f 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -17,9 +17,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -env: - GCE_CLI_GHA_VERSION: '416.0.0' # Fixed to avoid dependency on API changes - jobs: headless-docs: # Build headless and docs @@ -58,7 +55,7 @@ jobs: - name: Set up Python version uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - name: Install dependencies env: @@ -87,46 +84,44 @@ jobs: path: docs/_out/html if-no-files-found: error - - name: GCloud CLI auth - if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v2' - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - - name: Deploy docs + - name: Deploy docs if all artifacts available if: ${{ github.ref == 'refs/heads/main' }} + env: + GH_TOKEN: ${{ github.token }} run: | - # Compress and upload the docs, only for main branch - docs_out_dir="docs/_out" # Docs in ${docs_out_dir}/html - tar_file="${{ github.sha }}_ready.tar.gz" + tar_file="open3d-${GITHUB_SHA}-docs.tar.gz" rm -rf ${tar_file} - tar -C ${docs_out_dir} -czvf ${tar_file} html - gsutil cp ${tar_file} gs://open3d-docs/${tar_file} - echo "Docs archive uploaded to:" - echo "https://storage.googleapis.com/open3d-docs/${tar_file}" + # Docs in docs/_out/html + tar -C docs/_out -czf ${tar_file} html - - name: Check wheels and ready documentation archive - if: ${{ github.ref == 'refs/heads/main' }} - run: | - if [ $(gsutil ls gs://open3d-docs/${{ github.sha }}_ready* | wc -l)\ - -eq 4 ]; then - echo "All wheels and docs available. Making docs ready." - # Remove all marker files: Note _ at end of pattern. - gsutil rm gs://open3d-docs/${{ github.sha }}_ready_* - # Rename docs archive: - gsutil mv gs://open3d-docs/${{ github.sha }}_ready.tar.gz \ - gs://open3d-docs/${{ github.sha }}.tar.gz - # Set holds on new artifacts, release on old - gsutil retention temp release gs://open3d-releases/* - gsutil retention temp set gs://open3d-releases/python-wheels/*${GITHUB_SHA:0:7}*.whl - gsutil retention temp set gs://open3d-releases/devel/*${GITHUB_SHA:0:7}* - else - echo "All wheels / docs not available yet." - fi + echo "Waiting for other release assets..." + this_sha=$(echo ${GITHUB_SHA} | cut -c 1-6) + n_this_sha_assets=$(gh release view main-devel --json assets --jq ".assets | map(select(.name | contains(\"${this_sha}\"))) | length") + # Total assets from each main branch commmit: + # Python wheels (4x4) + Viewer (3) + C++ libs (4+2+2) = 27, + while ((n_this_sha_assets < 27)); do + sleep 60 + echo -n "." + n_this_sha_assets=$(gh release view main-devel --json assets --jq ".assets | map(select(.name | contains(\"${this_sha}\"))) | length") + done + gh release upload main-devel ${tar_file} --clobber + gh release view main-devel + + echo "\nAll assets ready. Removing release assets except from last 3 commits: ${last_shas[@]}" + release_assets=($(gh release view main-devel --json assets --jq '.assets[] | .name')) + last_shas=($(git log --pretty=format:%h --abbrev-commit -n 3)) + echo "Waiting for all assets to be ready..." + for relass in "${release_assets[@]}"; do + found=false + for last_sha in "${last_shas[@]}"; do + if [[ $relass == *${last_sha}* ]]; then + found=true + fi + done + if [ $found == false ]; then + set -x + gh release delete-asset main-devel $relass + set +x + fi + done + gh release view main-devel diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 4ac23fd67a8..20af6fdd767 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -22,7 +22,6 @@ env: # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources NPROC: 4 DEVELOPER_BUILD: ${{ github.event.inputs.developer_build || 'ON' }} - GCE_CLI_GHA_VERSION: '416.0.0' # Fixed to avoid dependency on API changes jobs: MacOS: @@ -104,11 +103,12 @@ jobs: - name: Build Open3D viewer app if: ${{ env.BUILD_SHARED_LIBS == 'OFF' }} run: | + OPEN3D_VERSION_FULL="$(grep -F OPEN3D_VERSION_FULL build/CMakeCache.txt | cut -f2 -d'=')" PATH=/usr/local/var/homebrew/linked/ccache/libexec:$PATH pushd build make -j${NPROC} Open3DViewer pushd bin - zip -rv open3d-app-macosx-10_15-${{ runner.arch}}.zip Open3D.app + zip -rv "open3d-${OPEN3D_VERSION_FULL}-app-macosx-10_15-${{ runner.arch }}.zip" Open3D.app ccache -s - name: Upload package @@ -119,31 +119,20 @@ jobs: path: build/package/${{ env.DEVEL_PKG_NAME }} if-no-files-found: error - - name: GCloud CLI auth - if: ${{ github.ref == 'refs/heads/main' && env.BUILD_SHARED_LIBS == 'ON' }} - uses: 'google-github-actions/auth@v2' - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - if: ${{ github.ref == 'refs/heads/main' && env.BUILD_SHARED_LIBS == 'ON' }} - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - - name: Upload package to GCS bucket + - name: Update package devel release if: ${{ github.ref == 'refs/heads/main' && env.BUILD_SHARED_LIBS == 'ON' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gsutil cp build/package/${{ env.DEVEL_PKG_NAME }} gs://open3d-releases/devel/ - echo "Download devel package at: https://storage.googleapis.com/open3d-releases/devel/${{ env.DEVEL_PKG_NAME }}" + gh release upload main-devel build/package/${{ env.DEVEL_PKG_NAME }} --clobber + gh release view main-devel - name: Upload Open3D viewer app uses: actions/upload-artifact@v4 if: ${{ env.BUILD_SHARED_LIBS == 'OFF' }} with: name: open3d-app-macosx-10_15-${{ runner.arch}} - path: build/bin/open3d-app-macosx-10_15-${{ runner.arch}}.zip + path: build/bin/open3d-*-app-macosx-10_15-${{ runner.arch }}.zip if-no-files-found: error fuse-viewer: @@ -151,6 +140,8 @@ jobs: runs-on: [macos-12] needs: [MacOS] steps: + - name: Checkout source code # for gh release upload + uses: actions/checkout@v4 - name: Download viewer apps uses: actions/download-artifact@v4 with: @@ -159,22 +150,35 @@ jobs: - name: Fuse x64 and arm64 viewer apps run: | - unzip open3d-app-macosx-10_15-X64.zip -d x64 - unzip open3d-app-macosx-10_15-ARM64.zip -d arm64 + unzip open3d-*-app-macosx-10_15-X64.zip -d x64 + unzip open3d-*-app-macosx-10_15-ARM64.zip -d arm64 + APP_NAME=$(ls open3d-*-app-macosx-10_15-X64.zip) + APP_NAME=${APP_NAME/-X64/-universal2} # includes version for i in arm64/Open3D.app/Contents/MacOS/*; do filepath=Open3D.app/Contents/MacOS/$(basename $i) lipo -create arm64/${filepath} x64/${filepath} -output arm64/${filepath} done mv arm64/Open3D.app Open3D.app - zip -rv open3d-app-macosx-10_15-universal2.zip Open3D.app + zip -rv "${APP_NAME}" Open3D.app - name: Upload Open3D viewer app uses: actions/upload-artifact@v4 with: name: open3d-app-macosx-10_15-universal2 - path: open3d-app-macosx-10_15-universal2.zip + path: open3d-*-app-macosx-10_15-universal2.zip if-no-files-found: error + - name: Checkout source code + uses: actions/checkout@v4 + - name: Update viewer devel release + if: ${{ github.ref == 'refs/heads/main' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release upload main-devel open3d-*-app-macosx-10_15-universal2.zip --clobber + gh release view main-devel + + build-wheel: name: Build wheel runs-on: ${{ matrix.os }} @@ -282,28 +286,6 @@ jobs: path: build/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} if-no-files-found: error - - name: GCloud CLI auth - if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v2' - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - - name: Upload wheel to GCS bucket - if: ${{ github.ref == 'refs/heads/main' }} - env: - python_version: ${{ matrix.python_version }} - run: | - PYTAG="-cp$(echo ${{ env.python_version }} | tr -d '.')" - gsutil cp build/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} gs://open3d-releases/python-wheels/ - echo "Download pip package at: https://storage.googleapis.com/open3d-releases/python-wheels/${{ env.PIP_PKG_NAME }}" - fuse-wheel: name: Fuse universal2 wheel runs-on: [macos-12] @@ -319,38 +301,41 @@ jobs: - is_main: false python_version: '3.10' steps: + - name: Checkout source code # for gh release upload + uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} + - name: Download X64 wheels uses: actions/download-artifact@v4 with: pattern: open3d-*macosx*_x86_64.whl path: x64_wheels merge-multiple: true + - name: Download ARM64 wheels uses: actions/download-artifact@v4 with: pattern: open3d-*macosx*_arm64.whl path: arm64_wheels merge-multiple: true + - name: Fuse x64 and ARM64 wheels env: python_version: ${{ matrix.python_version }} run: | PYTAG="-cp$(echo ${{ env.python_version }} | tr -d '.')" mkdir universal_wheels - pip install delocate delocate-fuse -v x64_wheels/open3d-*${PYTAG}*.whl arm64_wheels/open3d-*${PYTAG}*.whl - # Normalize file name as delocate-fuse doesn't update it OLD_WHL_NAME=$(basename x64_wheels/open3d-*${PYTAG}*.whl) NEW_WHL_NAME=${OLD_WHL_NAME/x86_64/universal2} mv x64_wheels/${OLD_WHL_NAME} universal_wheels/${NEW_WHL_NAME} - echo "PIP_PKG_NAME=$NEW_WHL_NAME" >> $GITHUB_ENV + - name: Upload merged wheels uses: actions/upload-artifact@v4 with: @@ -358,27 +343,14 @@ jobs: path: universal_wheels/${{ env.PIP_PKG_NAME }} if-no-files-found: error - - name: GCloud CLI auth - if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v2' - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - - name: Upload wheel to GCS bucket + - name: Update devel release if: ${{ github.ref == 'refs/heads/main' }} env: - python_version: ${{ matrix.python_version }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gsutil cp universal_wheels/${{ env.PIP_PKG_NAME }} gs://open3d-releases/python-wheels/ - echo "Download pip package at: https://storage.googleapis.com/open3d-releases/python-wheels/${{ env.PIP_PKG_NAME }}" - + gh release upload main-devel universal_wheels/${{ env.PIP_PKG_NAME }} --clobber + gh release view main-devel + test-wheel: name: Test wheel runs-on: ${{ matrix.os }} @@ -432,45 +404,8 @@ jobs: pi_tag=$(python -c "import sys; print(f'cp{sys.version_info.major}{sys.version_info.minor}')") test_wheel open3d*-"$pi_tag"-*_$(uname -m).whl - - name: Run Python unit tests (benchmarks) + - name: Run Python unit tests run: | source util/ci_utils.sh echo "Running Open3D python tests..." run_python_tests - - ready-docs: - name: Ready docs archive - # no need to run on macOS - runs-on: ubuntu-latest - if: ${{ github.ref == 'refs/heads/main' }} - needs: [fuse-wheel, MacOS] - steps: - - name: GCloud CLI auth - uses: 'google-github-actions/auth@v2' - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - name: Check wheels and ready documentation archive - run: | - touch marker_file - gsutil cp marker_file gs://open3d-docs/${{ github.sha }}_ready_macos - if [ $(gsutil ls gs://open3d-docs/${{ github.sha }}_ready* | wc -l)\ - -eq 4 ]; then - echo "All wheels and docs available. Making docs ready." - # Remove all marker files: Note _ at end of pattern. - gsutil rm gs://open3d-docs/${{ github.sha }}_ready_* - # Rename docs archive: - gsutil mv gs://open3d-docs/${{ github.sha }}_ready.tar.gz \ - gs://open3d-docs/${{ github.sha }}.tar.gz - # Set holds on new artifacts, release on old - gsutil retention temp release gs://open3d-releases/* - gsutil retention temp set gs://open3d-releases/python-wheels/*${GITHUB_SHA:0:7}*.whl - gsutil retention temp set gs://open3d-releases/devel/*${GITHUB_SHA:0:7}* - else - echo "All wheels / docs not available yet. Docs not ready." - fi diff --git a/.github/workflows/ubuntu-cuda.yml b/.github/workflows/ubuntu-cuda.yml index 250be1597fb..1ff1d20c75b 100644 --- a/.github/workflows/ubuntu-cuda.yml +++ b/.github/workflows/ubuntu-cuda.yml @@ -156,14 +156,13 @@ jobs: path: open3d-devel-linux*.tar.xz if-no-files-found: error - - name: Upload package to GCS bucket + - name: Update devel release if: ${{ github.ref == 'refs/heads/main' && env.BUILD_PACKAGE == 'true' }} + env: + GH_TOKEN: ${{ github.token }} run: | - gcloud compute ssh "${INSTANCE_NAME}" \ - --zone="${GCE_ZONE}" \ - --command="ls -alh \ - && gsutil cp open3d-devel-linux-*.tar.xz \ - gs://open3d-releases/devel/" + gh release upload main-devel open3d-devel-linux-*.tar.xz --clobber + gh release view main-devel - name: VM run docker run: | diff --git a/.github/workflows/ubuntu-wheel.yml b/.github/workflows/ubuntu-wheel.yml index 906d07112f7..2ff88c74b6a 100644 --- a/.github/workflows/ubuntu-wheel.yml +++ b/.github/workflows/ubuntu-wheel.yml @@ -101,14 +101,14 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} run: | gsutil cp ${GITHUB_WORKSPACE}/${{ env.CCACHE_TAR_NAME }}.tar.gz gs://open3d-ci-cache/ - - name: Upload wheel to GCS - if: ${{ github.ref == 'refs/heads/main' }} + - name: Update devel release + # if: ${{ github.ref == 'refs/heads/main' }} + env: + GH_TOKEN: ${{ github.token }} run: | - gsutil cp ${GITHUB_WORKSPACE}/${{ env.PIP_PKG_NAME }} \ - ${GITHUB_WORKSPACE}/${{ env.PIP_CPU_PKG_NAME }} gs://open3d-releases/python-wheels/ - echo "Download pip package at: - https://storage.googleapis.com/open3d-releases/python-wheels/${{ env.PIP_PKG_NAME }} - https://storage.googleapis.com/open3d-releases/python-wheels/${{ env.PIP_CPU_PKG_NAME }}" + gh release upload main-devel ${GITHUB_WORKSPACE}/${{ env.PIP_PKG_NAME }} \ + ${GITHUB_WORKSPACE}/${{ env.PIP_CPU_PKG_NAME }} --clobber + gh release view main-devel test-wheel-cpu: name: Test wheel CPU @@ -174,39 +174,3 @@ jobs: source util/ci_utils.sh echo "Running Open3D python tests (CPU wheel)..." run_python_tests - - ready-docs: - name: Ready docs archive - runs-on: ubuntu-latest - if: ${{ github.ref == 'refs/heads/main' }} - needs: [build-wheel] - steps: - - name: GCloud CLI auth - uses: 'google-github-actions/auth@v2' - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - name: Check wheels and ready documentation archive - run: | - touch marker_file - gsutil cp marker_file gs://open3d-docs/${{ github.sha }}_ready_ubuntu - if [ $(gsutil ls gs://open3d-docs/${{ github.sha }}_ready* | wc -l)\ - -eq 4 ]; then - echo "All wheels and docs available. Making docs ready." - # Remove all marker files: Note _ at end of pattern. - gsutil rm gs://open3d-docs/${{ github.sha }}_ready_* - # Rename docs archive: - gsutil mv gs://open3d-docs/${{ github.sha }}_ready.tar.gz \ - gs://open3d-docs/${{ github.sha }}.tar.gz - # Set holds on new artifacts, release on old - gsutil retention temp release gs://open3d-releases/* - gsutil retention temp set gs://open3d-releases/python-wheels/*${GITHUB_SHA:0:7}*.whl - gsutil retention temp set gs://open3d-releases/devel/*${GITHUB_SHA:0:7}* - else - echo "All wheels / docs not available yet." - fi diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3bbdaadc1d1..ba2765c82c7 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -19,7 +19,6 @@ concurrency: env: NPROC: 2 - GCE_CLI_GHA_VERSION: "416.0.0" jobs: ubuntu: @@ -84,20 +83,14 @@ jobs: name: open3d-viewer-Linux path: open3d-viewer-*-Linux.deb if-no-files-found: error - - name: GCloud CLI auth + - name: Update devel release if: ${{ github.ref == 'refs/heads/main' }} - uses: 'google-github-actions/auth@v2' - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - name: Upload package to GCS bucket - if: ${{ github.ref == 'refs/heads/main' && env.BUILD_SHARED_LIBS == 'ON' }} + env: + GH_TOKEN: ${{ github.token }} run: | - gsutil cp open3d-devel-*.tar.xz gs://open3d-releases/devel/ - echo "Download devel package at: https://storage.googleapis.com/open3d-releases/devel/${{ env.DEVEL_PKG_NAME }}" + if [ ${BUILD_SHARED_LIBS} == 'ON' ] ; then + gh release upload main-devel open3d-devel-*.tar.xz --clobber + else + gh release upload main-devel open3d-viewer-*-Linux.deb --clobber + fi + gh release view main-devel diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 11223949007..141f1917a30 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -28,7 +28,6 @@ env: BUILD_DIR: "C:\\Open3D\\build" NPROC: 2 DEVELOPER_BUILD: ${{ github.event.inputs.developer_build || 'ON' }} - GCE_CLI_GHA_VERSION: '416.0.0' # Fixed to avoid dependency on API changes jobs: windows: @@ -154,6 +153,14 @@ jobs: path: ${{ env.BUILD_DIR }}/package/${{ env.DEVEL_PKG_NAME }} if-no-files-found: error + - name: Update devel release with package + if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' }} + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload main-devel ${{ env.BUILD_DIR }}/package/${{ env.DEVEL_PKG_NAME }} --clobber + gh release view main-devel + - name: Viewer App working-directory: ${{ env.BUILD_DIR }} if: ${{ matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' && matrix.CONFIG == 'Release' }} @@ -172,29 +179,16 @@ jobs: path: C:\Program Files\Open3D\bin\Open3D if-no-files-found: error - - name: GCloud CLI auth - if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' }} - uses: google-github-actions/auth@v2 - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' }} - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_DOCS_PROJECT }} - - - name: Upload package to GCS bucket - if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' }} + - name: Update devel release with viewer + if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.BUILD_CUDA_MODULE == 'OFF' && matrix.CONFIG == 'Release' }} + env: + GH_TOKEN: ${{ github.token }} run: | - gsutil cp ${{ env.BUILD_DIR }}/package/${{ env.DEVEL_PKG_NAME }} ` - gs://open3d-releases/devel/ - if ($LastExitCode -eq 0) { - echo "Download devel package at: https://storage.googleapis.com/open3d-releases/devel/${{ env.DEVEL_PKG_NAME }}" - } else { - throw "Devel package upload failed" - } + $Env:OPEN3D_VERSION_FULL = (Select-String -Path "C:/Open3D/build/CMakeCache.txt" -Pattern "OPEN3D_VERSION_FULL").Line.Split('=')[1] + Compress-Archive -Path "C:/Program Files/Open3D/bin/Open3D" -DestinationPath ` + "open3d-$Env:OPEN3D_VERSION_FULL-app-windows-amd64.zip" + gh release upload main-devel "open3d-$Env:OPEN3D_VERSION_FULL-app-windows-amd64.zip" --clobber + gh release view main-devel - name: Run C++ unit tests if: ${{ matrix.BUILD_CUDA_MODULE == 'OFF' }} @@ -313,32 +307,13 @@ jobs: path: ${{ env.BUILD_DIR }}/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} if-no-files-found: error - - name: GCloud CLI auth - if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/auth@v2 - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - if: ${{ github.ref == 'refs/heads/main' }} - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_DOCS_PROJECT }} - - - name: Upload wheel to GCS bucket + - name: Update devel release with wheel if: ${{ github.ref == 'refs/heads/main' }} env: - python_version: ${{ matrix.python_version }} + GH_TOKEN: ${{ github.token }} run: | - $ErrorActionPreference = 'Stop' - $PYTAG="-cp$(${{ env.python_version }} -replace '\.', '')" - gsutil cp ${{ env.BUILD_DIR }}/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} gs://open3d-releases/python-wheels/ - if ($LastExitCode -eq 0) { - echo "Download pip package at: https://storage.googleapis.com/open3d-releases/python-wheels/${{ env.PIP_PKG_NAME }}" - } else { - throw "Wheel upload failed" - } + gh release upload main-devel ${{ env.BUILD_DIR }}/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} --clobber + gh release view main-devel test-wheel: name: Test wheel @@ -394,7 +369,6 @@ jobs: python -m pip install "$PIP_PKG_NAME" python -c "import open3d; print('Imported:', open3d)" python -c "import open3d; print('CUDA enabled: ', open3d.core.cuda.is_available())" - deactivate - name: Run Python unit tests @@ -406,42 +380,3 @@ jobs: echo "Testing ML and ML Ops disabled" python -m pytest python/test/ --ignore python/test/ml_ops/ deactivate - - ready-docs: - name: Ready docs archive - # no need to run on Windows - runs-on: ubuntu-latest - if: ${{ github.ref == 'refs/heads/main' }} - # temp workaround for Windows CUDA Debug CI out of space. Still update docs. - # needs: [build-wheel, windows] - needs: [build-wheel] - steps: - - name: GCloud CLI auth - uses: google-github-actions/auth@v2 - with: - project_id: ${{ secrets.GCE_PROJECT }} - credentials_json: '${{ secrets.GCE_SA_KEY_GPU_CI }}' - - name: GCloud CLI setup - uses: google-github-actions/setup-gcloud@v2 - with: - version: ${{ env.GCE_CLI_GHA_VERSION }} - project_id: ${{ secrets.GCE_PROJECT }} - - name: Check wheels and ready documentation archive - run: | - touch marker_file - gsutil cp marker_file gs://open3d-docs/${{ github.sha }}_ready_windows - if [ $(gsutil ls gs://open3d-docs/${{ github.sha }}_ready* | wc -l)\ - -eq 4 ]; then - echo "All wheels and docs available. Making docs ready." - # Remove all marker files: Note _ at end of pattern. - gsutil rm gs://open3d-docs/${{ github.sha }}_ready_* - # Rename docs archive: - gsutil mv gs://open3d-docs/${{ github.sha }}_ready.tar.gz \ - gs://open3d-docs/${{ github.sha }}.tar.gz - # Set holds on new artifacts, release on old - gsutil retention temp release gs://open3d-releases/* - gsutil retention temp set gs://open3d-releases/python-wheels/*${GITHUB_SHA:0:7}*.whl - gsutil retention temp set gs://open3d-releases/devel/*${GITHUB_SHA:0:7}* - else - echo "All wheels / docs not available yet." - fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 7abd4307b91..6e5784bb4aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,8 @@ string(CONCAT OPEN3D_VERSION ".${OPEN3D_VERSION_MINOR}" ".${OPEN3D_VERSION_PATCH}" ) -set(OPEN3D_VERSION_FULL "${OPEN3D_VERSION}${OPEN3D_VERSION_DEVHASH}") +set(OPEN3D_VERSION_FULL "${OPEN3D_VERSION}${OPEN3D_VERSION_DEVHASH}" CACHE + STRING "Open3D full version.") # Set additional info set(PROJECT_EMAIL "open3d@intel.com") set(PROJECT_DOCS "https://www.open3d.org/docs") diff --git a/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt b/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt index 9320f265d3e..b7bce43f4c6 100644 --- a/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt +++ b/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt @@ -32,7 +32,7 @@ set(CPACK_PACKAGE_NAME "open3d-viewer") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Open3D Viewer for 3D files") set(CPACK_PACKAGE_CONTACT "Open3D team <@PROJECT_EMAIL@>") set(CPACK_DEBIAN_PACKAGE_SECTION "Graphics") -set(CPACK_PACKAGE_VERSION "@OPEN3D_VERSION@") +set(CPACK_PACKAGE_VERSION "@OPEN3D_VERSION_FULL@") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc++1, libgomp1, libpng16-16, libglfw3") set(CPACK_PACKAGE_HOMEPAGE_URL "@PROJECT_HOMEPAGE_URL@") diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index 4bdd9e87d65..a2aa5a745ee 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -5,6 +5,21 @@ Getting started .. _install_open3d_python: +Viewer +====== + +Use the Open3D viewer application to visualize 3D data in various formats and +interact with it. You can download the latest stable release app from `Github +releases `__. The latest development +version (``HEAD`` of ``main`` branch) viewer app is provided here [#]_: + +* `Linux (Ubuntu 18.04+ or glibc 2.27+) `__ [#]_ +* `MacOSX v10.15+ (Intel or Apple Silicon) `__ +* `Windows 10+ (64-bit) `__ + +.. [#] Please use these links from the `latest version of this page `__ only. +.. [#] To check the `glibc` version on your system, run :code:`ldd --version`. + Python ====== @@ -70,28 +85,28 @@ version (``HEAD`` of ``main`` branch): :widths: auto * - Linux - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ * - Linux (CPU) - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ * - MacOS - - `Python 3.8 (x86_64) `__ - - `Python 3.9 (x86_64) `__ - - `Python 3.10 (x86_64+arm64) `__ - - `Python 3.11 (x86_64+arm64) `__ + - `Python 3.8 (x86_64) `__ + - `Python 3.9 (x86_64) `__ + - `Python 3.10 (x86_64+arm64) `__ + - `Python 3.11 (x86_64+arm64) `__ * - Windows - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ Please use these links from the `latest version of this page `__ only. You can also @@ -162,24 +177,24 @@ available for the main supported platforms. Also, the latest development version .. hlist:: :columns: 2 - * `x86_64 (CXX11 ABI) `__ - * `x86_64 (CXX11 ABI) with CUDA 11.x `__ - * `x86_64 (pre CXX11 ABI) `__ - * `x86_64 (pre CXX11 ABI) with CUDA 11.x `__ + * `x86_64 (CXX11 ABI) `__ + * `x86_64 (CXX11 ABI) with CUDA 11.x `__ + * `x86_64 (pre CXX11 ABI) `__ + * `x86_64 (pre CXX11 ABI) with CUDA 11.x `__ :MacOSX v10.15+: .. hlist:: :columns: 2 - * `x86_64 `__ - * `arm64 `__ + * `x86_64 `__ + * `arm64 `__ :Windows 10+: .. hlist:: :columns: 2 - * `x86_64 Release `__ - * `x86_64 Debug `__ + * `x86_64 Release `__ + * `x86_64 Debug `__ .. [#] Please use these links from the `latest version of this page `__ only. From bf48b4540583805b56a6286db13ceb30f2b0635b Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Sat, 11 May 2024 23:45:03 -0700 Subject: [PATCH 20/82] Robust image I/O (#6765) - Use file signatures (beginning bytes) instead of filename extension (png/jpg) to select decoder. jpg files with png extension and vice versa are read correctly. - Catch libjpeg errors and report as normal Open3D exceptions. Change Open3D warnings to errors (exceptions) on jpeg I/O error. - Simplify demo_scene example - Clear image on read error - Fix for github release macOS bug. --- .github/workflows/documentation.yml | 3 +- .github/workflows/macos.yml | 2 - cpp/open3d/io/ImageIO.cpp | 56 +++-- cpp/open3d/io/ImageIO.h | 9 +- cpp/open3d/io/file_format/FileJPG.cpp | 258 ++++++++++++-------- cpp/open3d/io/file_format/FilePNG.cpp | 2 + cpp/open3d/t/io/ImageIO.cpp | 66 ++--- cpp/open3d/t/io/file_format/FileJPG.cpp | 176 +++++++------ cpp/open3d/t/io/file_format/FilePNG.cpp | 4 +- examples/python/visualization/demo_scene.py | 45 +--- 10 files changed, 350 insertions(+), 271 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 6e4c21bc61f..00580af33d9 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -92,7 +92,7 @@ jobs: tar_file="open3d-${GITHUB_SHA}-docs.tar.gz" rm -rf ${tar_file} # Docs in docs/_out/html - tar -C docs/_out -czf ${tar_file} html + tar -C docs/_out -cvzf ${tar_file} html echo "Waiting for other release assets..." this_sha=$(echo ${GITHUB_SHA} | cut -c 1-6) @@ -110,7 +110,6 @@ jobs: echo "\nAll assets ready. Removing release assets except from last 3 commits: ${last_shas[@]}" release_assets=($(gh release view main-devel --json assets --jq '.assets[] | .name')) last_shas=($(git log --pretty=format:%h --abbrev-commit -n 3)) - echo "Waiting for all assets to be ready..." for relass in "${release_assets[@]}"; do found=false for last_sha in "${last_shas[@]}"; do diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 20af6fdd767..f0234f975ee 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -168,8 +168,6 @@ jobs: path: open3d-*-app-macosx-10_15-universal2.zip if-no-files-found: error - - name: Checkout source code - uses: actions/checkout@v4 - name: Update viewer devel release if: ${{ github.ref == 'refs/heads/main' }} env: diff --git a/cpp/open3d/io/ImageIO.cpp b/cpp/open3d/io/ImageIO.cpp index a4126df14e6..c00361a449a 100644 --- a/cpp/open3d/io/ImageIO.cpp +++ b/cpp/open3d/io/ImageIO.cpp @@ -7,24 +7,25 @@ #include "open3d/io/ImageIO.h" +#include +#include #include #include "open3d/utility/FileSystem.h" #include "open3d/utility/Logging.h" namespace open3d { +namespace io { namespace { -using namespace io; -static const std::unordered_map< - std::string, - std::function> - file_extension_to_image_read_function{ - {"png", ReadImageFromPNG}, - {"jpg", ReadImageFromJPG}, - {"jpeg", ReadImageFromJPG}, - }; +using signature_decoder_t = + std::pair>; +static const std::array signature_decoder_list{ + {{"\x89\x50\x4e\x47\xd\xa\x1a\xa", ReadImageFromPNG}, + {"\xFF\xD8\xFF", ReadImageFromJPG}}}; +static constexpr uint8_t MAX_SIGNATURE_LEN = 8; static const std::unordered_map< std::string, @@ -34,11 +35,8 @@ static const std::unordered_map< {"jpg", WriteImageToJPG}, {"jpeg", WriteImageToJPG}, }; - } // unnamed namespace -namespace io { - std::shared_ptr CreateImageFromFile( const std::string &filename) { auto image = std::make_shared(); @@ -47,21 +45,27 @@ std::shared_ptr CreateImageFromFile( } bool ReadImage(const std::string &filename, geometry::Image &image) { - std::string filename_ext = - utility::filesystem::GetFileExtensionInLowerCase(filename); - if (filename_ext.empty()) { - utility::LogWarning( - "Read geometry::Image failed: missing file extension."); - return false; - } - auto map_itr = file_extension_to_image_read_function.find(filename_ext); - if (map_itr == file_extension_to_image_read_function.end()) { - utility::LogWarning( - "Read geometry::Image failed: file extension {} unknown", - filename_ext); - return false; + std::string signature_buffer(MAX_SIGNATURE_LEN, 0); + std::ifstream file(filename, std::ios::binary); + file.read(&signature_buffer[0], MAX_SIGNATURE_LEN); + std::string err_msg; + if (!file) { + err_msg = "Read geometry::Image failed for file {}. I/O error."; + } else { + file.close(); + for (const auto &signature_decoder : signature_decoder_list) { + if (signature_buffer.compare(0, signature_decoder.first.size(), + signature_decoder.first) == 0) { + return signature_decoder.second(filename, image); + } + } + err_msg = + "Read geometry::Image failed for file {}. Unknown file " + "signature, only PNG and JPG are supported."; } - return map_itr->second(filename, image); + image.Clear(); + utility::LogWarning(err_msg.c_str(), filename); + return false; } bool WriteImage(const std::string &filename, diff --git a/cpp/open3d/io/ImageIO.h b/cpp/open3d/io/ImageIO.h index 3c6b62be3d8..45b0a3f71ed 100644 --- a/cpp/open3d/io/ImageIO.h +++ b/cpp/open3d/io/ImageIO.h @@ -69,11 +69,18 @@ bool WriteImageToJPG(const std::string &filename, const geometry::Image &image, int quality = kOpen3DImageIODefaultQuality); -/// The general entrance for reading an Image from memory +/// Read a PNG image from memory. +/// \param image_data_ptr the pointer to image data in memory. +/// \param image_data_size the size of image data in memory. +/// \return return true if the read function is successful, false otherwise. bool ReadPNGFromMemory(const unsigned char *image_data_ptr, size_t image_data_size, geometry::Image &image); +/// Read a JPG image from memory. +/// \param image_data_ptr the pointer to image data in memory. +/// \param image_data_size the size of image data in memory. +/// \return return true if the read function is successful, false otherwise. bool ReadJPGFromMemory(const unsigned char *image_data_ptr, size_t image_data_size, geometry::Image &image); diff --git a/cpp/open3d/io/file_format/FileJPG.cpp b/cpp/open3d/io/file_format/FileJPG.cpp index 7de155575bf..cb9cf6760e9 100644 --- a/cpp/open3d/io/file_format/FileJPG.cpp +++ b/cpp/open3d/io/file_format/FileJPG.cpp @@ -17,6 +17,23 @@ namespace open3d { namespace io { +namespace { + +/// Convert libjpeg error messages to std::runtime_error. This prevents +/// libjpeg from exit() in case of errors. +void jpeg_error_throw(j_common_ptr p_cinfo) { + if (p_cinfo->is_decompressor) + jpeg_destroy_decompress( + reinterpret_cast(p_cinfo)); + else + jpeg_destroy_compress( + reinterpret_cast(p_cinfo)); + char buffer[JMSG_LENGTH_MAX]; + (*p_cinfo->err->format_message)(p_cinfo, buffer); + throw std::runtime_error(buffer); +} + +} // namespace bool ReadImageFromJPG(const std::string &filename, geometry::Image &image) { struct jpeg_decompress_struct cinfo; @@ -27,53 +44,64 @@ bool ReadImageFromJPG(const std::string &filename, geometry::Image &image) { if ((file_in = utility::filesystem::FOpen(filename, "rb")) == NULL) { utility::LogWarning("Read JPG failed: unable to open file: {}", filename); + image.Clear(); return false; } - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, file_in); - jpeg_read_header(&cinfo, TRUE); - - // We only support two channel types: gray, and RGB. - int num_of_channels = 3; - int bytes_per_channel = 1; - switch (cinfo.jpeg_color_space) { - case JCS_RGB: - case JCS_YCbCr: - cinfo.out_color_space = JCS_RGB; - cinfo.out_color_components = 3; - num_of_channels = 3; - break; - case JCS_GRAYSCALE: - cinfo.jpeg_color_space = JCS_GRAYSCALE; - cinfo.out_color_components = 1; - num_of_channels = 1; - break; - case JCS_CMYK: - case JCS_YCCK: - default: - utility::LogWarning("Read JPG failed: color space not supported."); - jpeg_destroy_decompress(&cinfo); - fclose(file_in); - return false; - } - jpeg_start_decompress(&cinfo); - image.Prepare(cinfo.output_width, cinfo.output_height, num_of_channels, - bytes_per_channel); - int row_stride = cinfo.output_width * cinfo.output_components; - buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, - row_stride, 1); - uint8_t *pdata = image.data_.data(); - while (cinfo.output_scanline < cinfo.output_height) { - jpeg_read_scanlines(&cinfo, buffer, 1); - memcpy(pdata, buffer[0], row_stride); - pdata += row_stride; + try { + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error_throw; + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, file_in); + jpeg_read_header(&cinfo, TRUE); + + // We only support two channel types: gray, and RGB. + int num_of_channels = 3; + int bytes_per_channel = 1; + switch (cinfo.jpeg_color_space) { + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + cinfo.out_color_components = 3; + num_of_channels = 3; + break; + case JCS_GRAYSCALE: + cinfo.jpeg_color_space = JCS_GRAYSCALE; + cinfo.out_color_components = 1; + num_of_channels = 1; + break; + case JCS_CMYK: + case JCS_YCCK: + default: + utility::LogWarning( + "Read JPG failed: color space not supported."); + jpeg_destroy_decompress(&cinfo); + fclose(file_in); + image.Clear(); + return false; + } + jpeg_start_decompress(&cinfo); + image.Prepare(cinfo.output_width, cinfo.output_height, num_of_channels, + bytes_per_channel); + int row_stride = cinfo.output_width * cinfo.output_components; + buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, + row_stride, 1); + uint8_t *pdata = image.data_.data(); + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + memcpy(pdata, buffer[0], row_stride); + pdata += row_stride; + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(file_in); + return true; + } catch (const std::runtime_error &err) { + fclose(file_in); + image.Clear(); + utility::LogWarning("libjpeg error: {}", err.what()); + return false; } - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(file_in); - return true; } bool WriteImageToJPG(const std::string &filename, @@ -108,30 +136,37 @@ bool WriteImageToJPG(const std::string &filename, return false; } - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - jpeg_stdio_dest(&cinfo, file_out); - cinfo.image_width = image.width_; - cinfo.image_height = image.height_; - cinfo.input_components = image.num_of_channels_; - cinfo.in_color_space = - (cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB); - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - jpeg_start_compress(&cinfo, TRUE); - int row_stride = image.width_ * image.num_of_channels_; - const uint8_t *pdata = image.data_.data(); - std::vector buffer(row_stride); - while (cinfo.next_scanline < cinfo.image_height) { - memcpy(buffer.data(), pdata, row_stride); - row_pointer[0] = buffer.data(); - jpeg_write_scanlines(&cinfo, row_pointer, 1); - pdata += row_stride; + try { + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error_throw; + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, file_out); + cinfo.image_width = image.width_; + cinfo.image_height = image.height_; + cinfo.input_components = image.num_of_channels_; + cinfo.in_color_space = + (cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB); + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + jpeg_start_compress(&cinfo, TRUE); + int row_stride = image.width_ * image.num_of_channels_; + const uint8_t *pdata = image.data_.data(); + std::vector buffer(row_stride); + while (cinfo.next_scanline < cinfo.image_height) { + memcpy(buffer.data(), pdata, row_stride); + row_pointer[0] = buffer.data(); + jpeg_write_scanlines(&cinfo, row_pointer, 1); + pdata += row_stride; + } + jpeg_finish_compress(&cinfo); + fclose(file_out); + jpeg_destroy_compress(&cinfo); + return true; + } catch (const std::runtime_error &err) { + fclose(file_out); + utility::LogWarning(err.what()); + return false; } - jpeg_finish_compress(&cinfo); - fclose(file_out); - jpeg_destroy_compress(&cinfo); - return true; } bool ReadJPGFromMemory(const unsigned char *image_data_ptr, @@ -141,48 +176,57 @@ bool ReadJPGFromMemory(const unsigned char *image_data_ptr, struct jpeg_error_mgr jerr; JSAMPARRAY buffer; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - jpeg_mem_src(&cinfo, image_data_ptr, image_data_size); - jpeg_read_header(&cinfo, TRUE); - - // We only support two channel types: gray, and RGB. - int num_of_channels = 3; - int bytes_per_channel = 1; - switch (cinfo.jpeg_color_space) { - case JCS_RGB: - case JCS_YCbCr: - cinfo.out_color_space = JCS_RGB; - cinfo.out_color_components = 3; - num_of_channels = 3; - break; - case JCS_GRAYSCALE: - cinfo.jpeg_color_space = JCS_GRAYSCALE; - cinfo.out_color_components = 1; - num_of_channels = 1; - break; - case JCS_CMYK: - case JCS_YCCK: - default: - utility::LogWarning("Read JPG failed: color space not supported."); - jpeg_destroy_decompress(&cinfo); - return false; - } - jpeg_start_decompress(&cinfo); - image.Prepare(cinfo.output_width, cinfo.output_height, num_of_channels, - bytes_per_channel); - int row_stride = cinfo.output_width * cinfo.output_components; - buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, - row_stride, 1); - uint8_t *pdata = image.data_.data(); - while (cinfo.output_scanline < cinfo.output_height) { - jpeg_read_scanlines(&cinfo, buffer, 1); - memcpy(pdata, buffer[0], row_stride); - pdata += row_stride; + try { + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error_throw; + jpeg_create_decompress(&cinfo); + jpeg_mem_src(&cinfo, image_data_ptr, image_data_size); + jpeg_read_header(&cinfo, TRUE); + + // We only support two channel types: gray, and RGB. + int num_of_channels = 3; + int bytes_per_channel = 1; + switch (cinfo.jpeg_color_space) { + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + cinfo.out_color_components = 3; + num_of_channels = 3; + break; + case JCS_GRAYSCALE: + cinfo.jpeg_color_space = JCS_GRAYSCALE; + cinfo.out_color_components = 1; + num_of_channels = 1; + break; + case JCS_CMYK: + case JCS_YCCK: + default: + utility::LogWarning( + "Read JPG failed: color space not supported."); + jpeg_destroy_decompress(&cinfo); + image.Clear(); + return false; + } + jpeg_start_decompress(&cinfo); + image.Prepare(cinfo.output_width, cinfo.output_height, num_of_channels, + bytes_per_channel); + int row_stride = cinfo.output_width * cinfo.output_components; + buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, + row_stride, 1); + uint8_t *pdata = image.data_.data(); + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + memcpy(pdata, buffer[0], row_stride); + pdata += row_stride; + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + return true; + } catch (const std::runtime_error &err) { + image.Clear(); + utility::LogWarning(err.what()); + return false; } - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - return true; } } // namespace io diff --git a/cpp/open3d/io/file_format/FilePNG.cpp b/cpp/open3d/io/file_format/FilePNG.cpp index 70832d8ee1e..55a7e410eda 100644 --- a/cpp/open3d/io/file_format/FilePNG.cpp +++ b/cpp/open3d/io/file_format/FilePNG.cpp @@ -106,6 +106,7 @@ bool ReadPNGFromMemory(const unsigned char *image_data_ptr, if (png_image_begin_read_from_memory(&pngimage, image_data_ptr, image_data_size) == 0) { utility::LogWarning("Read PNG failed: unable to parse header."); + image.Clear(); return false; } @@ -122,6 +123,7 @@ bool ReadPNGFromMemory(const unsigned char *image_data_ptr, if (png_image_finish_read(&pngimage, NULL, image.data_.data(), 0, NULL) == 0) { utility::LogWarning("PNG error: {}", pngimage.message); + image.Clear(); return false; } return true; diff --git a/cpp/open3d/t/io/ImageIO.cpp b/cpp/open3d/t/io/ImageIO.cpp index df1ca3625ce..d172ad16cb7 100644 --- a/cpp/open3d/t/io/ImageIO.cpp +++ b/cpp/open3d/t/io/ImageIO.cpp @@ -8,7 +8,9 @@ #include "open3d/t/io/ImageIO.h" #include +#include #include +#include #include #include @@ -23,14 +25,14 @@ namespace open3d { namespace t { namespace io { -static const std::unordered_map< - std::string, - std::function> - file_extension_to_image_read_function{ - {"png", ReadImageFromPNG}, - {"jpg", ReadImageFromJPG}, - {"jpeg", ReadImageFromJPG}, - }; +namespace { +using signature_decoder_t = + std::pair>; +static const std::array signature_decoder_list{ + {{"\x89\x50\x4e\x47\xd\xa\x1a\xa", ReadImageFromPNG}, + {"\xFF\xD8\xFF", ReadImageFromJPG}}}; +static constexpr uint8_t MAX_SIGNATURE_LEN = 8; static const std::unordered_map< std::string, @@ -40,6 +42,7 @@ static const std::unordered_map< {"jpg", WriteImageToJPG}, {"jpeg", WriteImageToJPG}, }; +} // namespace std::shared_ptr CreateImageFromFile( const std::string &filename) { @@ -49,21 +52,27 @@ std::shared_ptr CreateImageFromFile( } bool ReadImage(const std::string &filename, geometry::Image &image) { - std::string filename_ext = - utility::filesystem::GetFileExtensionInLowerCase(filename); - if (filename_ext.empty()) { - utility::LogWarning( - "Read geometry::Image failed: missing file extension."); - return false; - } - auto map_itr = file_extension_to_image_read_function.find(filename_ext); - if (map_itr == file_extension_to_image_read_function.end()) { - utility::LogWarning( - "Read geometry::Image failed: file extension {} unknown", - filename_ext); - return false; + std::string signature_buffer(MAX_SIGNATURE_LEN, 0); + std::ifstream file(filename, std::ios::binary); + file.read(&signature_buffer[0], MAX_SIGNATURE_LEN); + std::string err_msg; + if (!file) { + err_msg = "Read geometry::Image failed for file {}. I/O error."; + } else { + file.close(); + for (const auto &signature_decoder : signature_decoder_list) { + if (signature_buffer.compare(0, signature_decoder.first.size(), + signature_decoder.first) == 0) { + return signature_decoder.second(filename, image); + } + } + err_msg = + "Read geometry::Image failed for file {}. Unknown file " + "signature, only PNG and JPG are supported."; } - return map_itr->second(filename, image); + image.Clear(); + utility::LogWarning(err_msg.c_str(), filename); + return false; } bool WriteImage(const std::string &filename, @@ -99,7 +108,8 @@ DepthNoiseSimulator::DepthNoiseSimulator(const std::string &noise_model_path) { for (int i = 0; i < skip_first_n_lines; ++i) { if (!(line_buffer = file.ReadLine())) { utility::LogError( - "Read depth model failed: file {} is less than {} lines.", + "Read depth model failed: file {} is less than {} " + "lines.", noise_model_path, skip_first_n_lines); } } @@ -182,11 +192,11 @@ geometry::Image DepthNoiseSimulator::Simulate(const geometry::Image &im_src, geometry::kernel::TArrayIndexer dst_indexer(im_dst_tensor, 2); geometry::kernel::TArrayIndexer model_indexer(model_, 3); - // To match the original implementation, we try to keep the same variable - // names with reference to the original code. Compared to the original - // implementation, parallelization is done in im_dst_tensor per-pixel level, - // instead of per-image level. Check out the original code at: - // http://redwood-data.org/indoor/data/simdepth.py. + // To match the original implementation, we try to keep the same + // variable names with reference to the original code. Compared to the + // original implementation, parallelization is done in im_dst_tensor + // per-pixel level, instead of per-image level. Check out the original + // code at: http://redwood-data.org/indoor/data/simdepth.py. core::ParallelFor( core::Device("CPU:0"), width * height, [&] OPEN3D_DEVICE(int workload_idx) { diff --git a/cpp/open3d/t/io/file_format/FileJPG.cpp b/cpp/open3d/t/io/file_format/FileJPG.cpp index dc83fb3ad2e..e1830f5135f 100644 --- a/cpp/open3d/t/io/file_format/FileJPG.cpp +++ b/cpp/open3d/t/io/file_format/FileJPG.cpp @@ -19,6 +19,24 @@ namespace open3d { namespace t { namespace io { +namespace { + +/// Convert libjpeg error messages to std::runtime_error. This prevents +/// libjpeg from exit() in case of errors. +void jpeg_error_throw(j_common_ptr p_cinfo) { + if (p_cinfo->is_decompressor) + jpeg_destroy_decompress( + reinterpret_cast(p_cinfo)); + else + jpeg_destroy_compress( + reinterpret_cast(p_cinfo)); + char buffer[JMSG_LENGTH_MAX]; + (*p_cinfo->err->format_message)(p_cinfo, buffer); + throw std::runtime_error(buffer); +} + +} // namespace + bool ReadImageFromJPG(const std::string &filename, geometry::Image &image) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; @@ -28,56 +46,67 @@ bool ReadImageFromJPG(const std::string &filename, geometry::Image &image) { if ((file_in = utility::filesystem::FOpen(filename, "rb")) == NULL) { utility::LogWarning("Read JPG failed: unable to open file: {}", filename); + image.Clear(); return false; } - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, file_in); - jpeg_read_header(&cinfo, TRUE); - - // We only support two channel types: gray, and RGB. - int num_of_channels = 3; - switch (cinfo.jpeg_color_space) { - case JCS_RGB: - case JCS_YCbCr: - cinfo.out_color_space = JCS_RGB; - cinfo.out_color_components = 3; - num_of_channels = 3; - break; - case JCS_GRAYSCALE: - cinfo.jpeg_color_space = JCS_GRAYSCALE; - cinfo.out_color_components = 1; - num_of_channels = 1; - break; - case JCS_CMYK: - case JCS_YCCK: - default: - utility::LogWarning("Read JPG failed: color space not supported."); - jpeg_destroy_decompress(&cinfo); - fclose(file_in); - return false; - } - jpeg_start_decompress(&cinfo); - image.Clear(); - image.Reset(cinfo.output_height, cinfo.output_width, num_of_channels, - core::UInt8, image.GetDevice()); - - int row_stride = cinfo.output_width * cinfo.output_components; - buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, - row_stride, 1); - uint8_t *pdata = static_cast(image.GetDataPtr()); - - while (cinfo.output_scanline < cinfo.output_height) { - jpeg_read_scanlines(&cinfo, buffer, 1); - core::MemoryManager::MemcpyFromHost(pdata, image.GetDevice(), buffer[0], - row_stride * 1); - pdata += row_stride; + try { + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error_throw; + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, file_in); + jpeg_read_header(&cinfo, TRUE); + + // We only support two channel types: gray, and RGB. + int num_of_channels = 3; + switch (cinfo.jpeg_color_space) { + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + cinfo.out_color_components = 3; + num_of_channels = 3; + break; + case JCS_GRAYSCALE: + cinfo.jpeg_color_space = JCS_GRAYSCALE; + cinfo.out_color_components = 1; + num_of_channels = 1; + break; + case JCS_CMYK: + case JCS_YCCK: + default: + utility::LogWarning( + "Read JPG failed: color space not supported."); + jpeg_destroy_decompress(&cinfo); + fclose(file_in); + image.Clear(); + return false; + } + jpeg_start_decompress(&cinfo); + image.Clear(); + image.Reset(cinfo.output_height, cinfo.output_width, num_of_channels, + core::UInt8, image.GetDevice()); + + int row_stride = cinfo.output_width * cinfo.output_components; + buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, + row_stride, 1); + uint8_t *pdata = static_cast(image.GetDataPtr()); + + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + core::MemoryManager::MemcpyFromHost(pdata, image.GetDevice(), + buffer[0], row_stride * 1); + pdata += row_stride; + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(file_in); + return true; + } catch (const std::runtime_error &err) { + fclose(file_in); + image.Clear(); + utility::LogWarning("libjpeg error: {}", err.what()); + return false; } - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(file_in); - return true; } bool WriteImageToJPG(const std::string &filename, @@ -112,31 +141,38 @@ bool WriteImageToJPG(const std::string &filename, return false; } - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - jpeg_stdio_dest(&cinfo, file_out); - cinfo.image_width = image.GetCols(); - cinfo.image_height = image.GetRows(); - cinfo.input_components = image.GetChannels(); - cinfo.in_color_space = - (cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB); - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - jpeg_start_compress(&cinfo, TRUE); - int row_stride = image.GetCols() * image.GetChannels(); - const uint8_t *pdata = static_cast(image.GetDataPtr()); - std::vector buffer(row_stride); - while (cinfo.next_scanline < cinfo.image_height) { - core::MemoryManager::MemcpyToHost(buffer.data(), pdata, - image.GetDevice(), row_stride * 1); - row_pointer[0] = buffer.data(); - jpeg_write_scanlines(&cinfo, row_pointer, 1); - pdata += row_stride; + try { + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error_throw; + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, file_out); + cinfo.image_width = image.GetCols(); + cinfo.image_height = image.GetRows(); + cinfo.input_components = image.GetChannels(); + cinfo.in_color_space = + (cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB); + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + jpeg_start_compress(&cinfo, TRUE); + int row_stride = image.GetCols() * image.GetChannels(); + const uint8_t *pdata = static_cast(image.GetDataPtr()); + std::vector buffer(row_stride); + while (cinfo.next_scanline < cinfo.image_height) { + core::MemoryManager::MemcpyToHost( + buffer.data(), pdata, image.GetDevice(), row_stride * 1); + row_pointer[0] = buffer.data(); + jpeg_write_scanlines(&cinfo, row_pointer, 1); + pdata += row_stride; + } + jpeg_finish_compress(&cinfo); + fclose(file_out); + jpeg_destroy_compress(&cinfo); + return true; + } catch (const std::runtime_error &err) { + fclose(file_out); + utility::LogWarning(err.what()); + return false; } - jpeg_finish_compress(&cinfo); - fclose(file_out); - jpeg_destroy_compress(&cinfo); - return true; } } // namespace io diff --git a/cpp/open3d/t/io/file_format/FilePNG.cpp b/cpp/open3d/t/io/file_format/FilePNG.cpp index 2497e2a71c8..8560bc8b431 100644 --- a/cpp/open3d/t/io/file_format/FilePNG.cpp +++ b/cpp/open3d/t/io/file_format/FilePNG.cpp @@ -41,6 +41,7 @@ bool ReadImageFromPNG(const std::string &filename, geometry::Image &image) { pngimage.version = PNG_IMAGE_VERSION; if (png_image_begin_read_from_file(&pngimage, filename.c_str()) == 0) { utility::LogWarning("Read PNG failed: unable to parse header."); + image.Clear(); return false; } @@ -64,6 +65,7 @@ bool ReadImageFromPNG(const std::string &filename, geometry::Image &image) { utility::LogWarning("Read PNG failed: unable to read file: {}", filename); utility::LogWarning("PNG error: {}", pngimage.message); + image.Clear(); return false; } return true; @@ -141,7 +143,7 @@ bool WriteImageToPNGInMemory(std::vector &buffer, buffer.resize(mem_bytes); if (png_image_write_to_memory(&pngimage, &buffer[0], &mem_bytes, 0, image.GetDataPtr(), 0, nullptr) == 0) { - utility::LogWarning("Unable to encode to encode to PNG in memory."); + utility::LogWarning("Unable to encode to PNG in memory."); return false; } return true; diff --git a/examples/python/visualization/demo_scene.py b/examples/python/visualization/demo_scene.py index cfb5d3d2393..b0662feadc7 100644 --- a/examples/python/visualization/demo_scene.py +++ b/examples/python/visualization/demo_scene.py @@ -6,33 +6,15 @@ # ---------------------------------------------------------------------------- """Demo scene demonstrating models, built-in shapes, and materials""" -import math import numpy as np -import os import open3d as o3d import open3d.visualization as vis -def convert_material_record(mat_record): - mat = vis.Material('defaultLit') - # Convert scalar parameters - mat.vector_properties['base_color'] = mat_record.base_color - mat.scalar_properties['metallic'] = mat_record.base_metallic - mat.scalar_properties['roughness'] = mat_record.base_roughness - mat.scalar_properties['reflectance'] = mat_record.base_reflectance - mat.texture_maps['albedo'] = o3d.t.geometry.Image.from_legacy( - mat_record.albedo_img) - mat.texture_maps['normal'] = o3d.t.geometry.Image.from_legacy( - mat_record.normal_img) - mat.texture_maps['ao_rough_metal'] = o3d.t.geometry.Image.from_legacy( - mat_record.ao_rough_metal_img) - return mat - - def create_scene(): - ''' - Creates the geometry and materials for the demo scene and returns a dictionary suitable for draw call - ''' + """Creates the geometry and materials for the demo scene and returns a + dictionary suitable for draw call + """ # Create some shapes for our scene a_cube = o3d.geometry.TriangleMesh.create_box(2, 4, @@ -47,7 +29,7 @@ def create_scene(): resolution=40, create_uv_map=True) a_sphere.compute_vertex_normals() - rotate_90 = o3d.geometry.get_rotation_matrix_from_xyz((-math.pi / 2, 0, 0)) + rotate_90 = o3d.geometry.get_rotation_matrix_from_xyz((-np.pi / 2, 0, 0)) a_sphere.rotate(rotate_90) a_sphere.translate((5, 2.4, 0)) a_sphere = o3d.t.geometry.TriangleMesh.from_legacy(a_sphere) @@ -68,17 +50,13 @@ def create_scene(): # Load an OBJ model for our scene helmet_data = o3d.data.FlightHelmetModel() helmet = o3d.io.read_triangle_model(helmet_data.path) - helmet_parts = [] - for m in helmet.meshes: - # m.mesh.paint_uniform_color((1.0, 0.75, 0.3)) - m.mesh.scale(10.0, (0.0, 0.0, 0.0)) - helmet_parts.append(m) + helmet_parts = o3d.t.geometry.TriangleMesh.from_triangle_mesh_model(helmet) # Create a ground plane ground_plane = o3d.geometry.TriangleMesh.create_box( 50.0, 0.1, 50.0, create_uv_map=True, map_texture_to_each_face=True) ground_plane.compute_triangle_normals() - rotate_180 = o3d.geometry.get_rotation_matrix_from_xyz((-math.pi, 0, 0)) + rotate_180 = o3d.geometry.get_rotation_matrix_from_xyz((-np.pi, 0, 0)) ground_plane.rotate(rotate_180) ground_plane.translate((-25.0, -0.1, -25.0)) ground_plane.paint_uniform_color((1, 1, 1)) @@ -157,12 +135,11 @@ def create_scene(): "geometry": a_sphere }] # Load the helmet - for part in helmet_parts: - name = part.mesh_name - tgeom = o3d.t.geometry.TriangleMesh.from_legacy(part.mesh) - tgeom.material = convert_material_record( - helmet.materials[part.material_idx]) - geoms.append({"name": name, "geometry": tgeom}) + for name, tmesh in helmet_parts.items(): + geoms.append({ + "name": name, + "geometry": tmesh.scale(10.0, (0.0, 0.0, 0.0)) + }) return geoms From a1fb32c92d11e90ce2620787f70d56e3e5acadbc Mon Sep 17 00:00:00 2001 From: Fastriver <31440714+organic-nailer@users.noreply.github.com> Date: Wed, 15 May 2024 13:15:22 +0900 Subject: [PATCH 21/82] Add Mouse Callback to VisualizerWithKeyCallback (#6760) --------- Co-authored-by: Sameer Sheorey --- .../visualizer/VisualizerWithKeyCallback.cpp | 62 +++++++++++++++++++ .../visualizer/VisualizerWithKeyCallback.h | 44 +++++++++++++ cpp/pybind/visualization/visualizer.cpp | 46 ++++++++++++-- .../customized_visualization_key_action.py | 40 +++++++++++- 4 files changed, 185 insertions(+), 7 deletions(-) diff --git a/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.cpp b/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.cpp index a4845166b8a..6fd167b73e5 100644 --- a/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.cpp +++ b/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.cpp @@ -26,6 +26,13 @@ void VisualizerWithKeyCallback::PrintVisualizerHelp() { utility::LogInfo( " The default functions of these keys will be overridden."); utility::LogInfo(""); + + std::string mouse_callbacks = (mouse_move_callback_ ? "MouseMove, " : ""); + mouse_callbacks += (mouse_scroll_callback_ ? "MouseScroll, " : ""); + mouse_callbacks += (mouse_button_callback_ ? "MouseButton, " : ""); + utility::LogInfo(" Custom mouse callbacks registered for: {}", + mouse_callbacks.substr(0, mouse_callbacks.size() - 2)); + utility::LogInfo(""); } void VisualizerWithKeyCallback::RegisterKeyCallback( @@ -38,6 +45,21 @@ void VisualizerWithKeyCallback::RegisterKeyActionCallback( key_action_to_callback_[key] = callback; } +void VisualizerWithKeyCallback::RegisterMouseMoveCallback( + std::function callback) { + mouse_move_callback_ = callback; +} + +void VisualizerWithKeyCallback::RegisterMouseScrollCallback( + std::function callback) { + mouse_scroll_callback_ = callback; +} + +void VisualizerWithKeyCallback::RegisterMouseButtonCallback( + std::function callback) { + mouse_button_callback_ = callback; +} + void VisualizerWithKeyCallback::KeyPressCallback( GLFWwindow *window, int key, int scancode, int action, int mods) { auto action_callback = key_action_to_callback_.find(key); @@ -63,6 +85,46 @@ void VisualizerWithKeyCallback::KeyPressCallback( } } +void VisualizerWithKeyCallback::MouseMoveCallback(GLFWwindow *window, + double x, + double y) { + if (mouse_move_callback_) { + if (mouse_move_callback_(this, x, y)) { + UpdateGeometry(); + } + UpdateRender(); + } else { + Visualizer::MouseMoveCallback(window, x, y); + } +} + +void VisualizerWithKeyCallback::MouseScrollCallback(GLFWwindow *window, + double x, + double y) { + if (mouse_scroll_callback_) { + if (mouse_scroll_callback_(this, x, y)) { + UpdateGeometry(); + } + UpdateRender(); + } else { + Visualizer::MouseScrollCallback(window, x, y); + } +} + +void VisualizerWithKeyCallback::MouseButtonCallback(GLFWwindow *window, + int button, + int action, + int mods) { + if (mouse_button_callback_) { + if (mouse_button_callback_(this, button, action, mods)) { + UpdateGeometry(); + } + UpdateRender(); + } else { + Visualizer::MouseButtonCallback(window, button, action, mods); + } +} + std::string VisualizerWithKeyCallback::PrintKeyToString(int key) { if (key == GLFW_KEY_SPACE) { // 32 return std::string("Space"); diff --git a/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.h b/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.h index 92274978d81..add4e265edc 100644 --- a/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.h +++ b/cpp/open3d/visualization/visualizer/VisualizerWithKeyCallback.h @@ -33,6 +33,7 @@ class VisualizerWithKeyCallback : public Visualizer { void PrintVisualizerHelp() override; void RegisterKeyCallback(int key, std::function callback); + /// Register callback function with access to GLFW key actions. /// /// \param key GLFW key value, see [GLFW key @@ -48,18 +49,61 @@ class VisualizerWithKeyCallback : public Visualizer { void RegisterKeyActionCallback( int key, std::function callback); + /// Register callback function with access to GLFW mouse actions. + /// + /// \param callback The callback function. The callback function takes + /// `Visualizer *`, and the x and y mouse position inside the window and + /// returns a boolean indicating if `UpdateGeometry()` needs to be run. See + /// [GLFW mouse + /// position](https://www.glfw.org/docs/latest/input_guide.html#input_mouse) + /// for more details. + void RegisterMouseMoveCallback( + std::function callback); + + /// Register callback function with access to GLFW mouse actions. + /// + /// \param callback The callback function. The callback function takes + /// `Visualizer *`, and the x and y scroll values and returns a boolean + /// indicating if `UpdateGeometry()` needs to be run. A normal mouse only + /// provides a y scroll value. See [GLFW mouse + /// scrolling](https://www.glfw.org/docs/latest/input_guide.html#scrolling) + /// for more details. + void RegisterMouseScrollCallback( + std::function callback); + + /// Register callback function with access to GLFW mouse actions. + /// + /// \param callback The callback function. The callback function takes + /// `Visualizer *`, `button`, `action` and `mods` as input and returns a + /// boolean indicating UpdateGeometry() needs to be run. The `action` can be + /// one of GLFW_RELEASE (0), GLFW_PRESS (1) or GLFW_REPEAT (2), see [GLFW + /// input interface](https://www.glfw.org/docs/latest/group__input.html). + /// The `mods` specifies the modifier key, see [GLFW modifier + /// key](https://www.glfw.org/docs/latest/group__mods.html). + void RegisterMouseButtonCallback( + std::function callback); + protected: void KeyPressCallback(GLFWwindow *window, int key, int scancode, int action, int mods) override; + void MouseMoveCallback(GLFWwindow *window, double x, double y) override; + void MouseScrollCallback(GLFWwindow *window, double x, double y) override; + void MouseButtonCallback(GLFWwindow *window, + int button, + int action, + int mods) override; std::string PrintKeyToString(int key); protected: std::map> key_to_callback_; std::map> key_action_to_callback_; + std::function mouse_move_callback_; + std::function mouse_scroll_callback_; + std::function mouse_button_callback_; }; } // namespace visualization diff --git a/cpp/pybind/visualization/visualizer.cpp b/cpp/pybind/visualization/visualizer.cpp index 9370612d06c..990727dd45c 100644 --- a/cpp/pybind/visualization/visualizer.cpp +++ b/cpp/pybind/visualization/visualizer.cpp @@ -169,10 +169,48 @@ void pybind_visualizer(py::module &m) { .def("register_key_action_callback", &VisualizerWithKeyCallback::RegisterKeyActionCallback, "Function to register a callback function for a key action " - "event. The callback function takes Visualizer, action and " - "mods as input and returns a boolean indicating if " - "UpdateGeometry() needs to be run.", - "key"_a, "callback_func"_a); + "event. The callback function takes `Visualizer`, `action` " + "and `mods` as input and returns a boolean indicating if " + "`UpdateGeometry()` needs to be run. The `action` can be one " + "of `GLFW_RELEASE` (0), `GLFW_PRESS` (1) or `GLFW_REPEAT` " + "(2), see `GLFW input interface " + "`__. The " + "`mods` specifies the modifier key, see `GLFW modifier key " + "`__", + "key"_a, "callback_func"_a) + + .def("register_mouse_move_callback", + &VisualizerWithKeyCallback::RegisterMouseMoveCallback, + "Function to register a callback function for a mouse move " + "event. The callback function takes Visualizer, x and y mouse " + "position inside the window as input and returns a boolean " + "indicating if UpdateGeometry() needs to be run. `GLFW mouse " + "position `__ for more details.", + "callback_func"_a) + + .def("register_mouse_scroll_callback", + &VisualizerWithKeyCallback::RegisterMouseScrollCallback, + "Function to register a callback function for a mouse scroll " + "event. The callback function takes Visualizer, x and y mouse " + "scroll offset as input and returns a boolean " + "indicating if UpdateGeometry() needs to be run. `GLFW mouse " + "scrolling `__ for more details.", + "callback_func"_a) + + .def("register_mouse_button_callback", + &VisualizerWithKeyCallback::RegisterMouseButtonCallback, + "Function to register a callback function for a mouse button " + "event. The callback function takes `Visualizer`, `button`, " + "`action` and `mods` as input and returns a boolean " + "indicating `UpdateGeometry()` needs to be run. The `action` " + "can be one of GLFW_RELEASE (0), GLFW_PRESS (1) or " + "GLFW_REPEAT (2), see `GLFW input interface " + "`__. " + "The `mods` specifies the modifier key, see `GLFW modifier " + "key `__.", + "callback_func"_a); py::class_, std::shared_ptr> diff --git a/examples/python/visualization/customized_visualization_key_action.py b/examples/python/visualization/customized_visualization_key_action.py index 9f885615e4e..c68e2f1110f 100644 --- a/examples/python/visualization/customized_visualization_key_action.py +++ b/examples/python/visualization/customized_visualization_key_action.py @@ -41,11 +41,45 @@ def animation_callback(vis): vis.run() +def custom_mouse_action(pcd): + + vis = o3d.visualization.VisualizerWithKeyCallback() + buttons = ['left', 'right', 'middle'] + actions = ['up', 'down'] + mods_name = ['shift', 'ctrl', 'alt', 'cmd'] + + def on_key_action(vis, action, mods): + print("on_key_action", action, mods) + + vis.register_key_action_callback(ord("A"), on_key_action) + + def on_mouse_move(vis, x, y): + print(f"on_mouse_move({x:.2f}, {y:.2f})") + + def on_mouse_scroll(vis, x, y): + print(f"on_mouse_scroll({x:.2f}, {y:.2f})") + + def on_mouse_button(vis, button, action, mods): + pressed_mods = " ".join( + [mods_name[i] for i in range(4) if mods & (1 << i)]) + print(f"on_mouse_button: {buttons[button]}, {actions[action]}, " + + pressed_mods) + + vis.register_mouse_move_callback(on_mouse_move) + vis.register_mouse_scroll_callback(on_mouse_scroll) + vis.register_mouse_button_callback(on_mouse_button) + + vis.create_window() + vis.add_geometry(pcd) + vis.run() + + if __name__ == "__main__": ply_data = o3d.data.PLYPointCloud() pcd = o3d.io.read_point_cloud(ply_data.path) - print( - "Customized visualization with smooth key action (without keyboard repeat delay)" - ) + print("Customized visualization with smooth key action " + "(without keyboard repeat delay). Press the space-bar.") custom_key_action_without_kb_repeat_delay(pcd) + print("Customized visualization with mouse action.") + custom_mouse_action(pcd) From 1b55f11f2895edfb6e3312de96644db30fa2b027 Mon Sep 17 00:00:00 2001 From: Nicola Loi <79461707+nicolaloi@users.noreply.github.com> Date: Fri, 17 May 2024 18:34:19 +0200 Subject: [PATCH 22/82] Fix segfault (infinite recursion) of PointCloud::DetectPlanarPatches if multiple points have same coordinates (#6794) --- CHANGELOG.md | 1 + .../PointCloudPlanarPatchDetection.cpp | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b68b0bdbd02..7d3b964d6d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Fix log error message for `probability` argument validation in `PointCloud::SegmentPlane` (PR #6622) - Fix macOS arm64 builds, add CI runner for macOS arm64 (PR #6695) - Fix KDTreeFlann possibly using a dangling pointer instead of internal storage and simplified its members (PR #6734) +- Fix segmentation fault (infinite recursion) of DetectPlanarPatches if multiple points have same coordinates (PR #6794) ## 0.13 diff --git a/cpp/open3d/geometry/PointCloudPlanarPatchDetection.cpp b/cpp/open3d/geometry/PointCloudPlanarPatchDetection.cpp index 16813bad75e..b76bc9f3fc4 100644 --- a/cpp/open3d/geometry/PointCloudPlanarPatchDetection.cpp +++ b/cpp/open3d/geometry/PointCloudPlanarPatchDetection.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -57,12 +58,18 @@ class BoundaryVolumeHierarchy { /// \brief Constructor for the root node of the octree. /// - /// \param point_cloud is the associated set of points being partitioned - BoundaryVolumeHierarchy(const PointCloud* point_cloud, - const Eigen::Vector3d& min_bound, - const Eigen::Vector3d& max_bound, - size_t min_points = 1, - double min_size = 0.0) + /// \param point_cloud is the associated set of points being partitioned. + /// \param min_bound is the minimum coordinate of the bounding volume. + /// \param max_bound is the maximum coordinate of the bounding volume. + /// \param min_points is the threshold number of points in a node to stop + /// partitioning it further. \param min_size is the threshold size of a node + /// to stop partitioning it further. + BoundaryVolumeHierarchy( + const PointCloud* point_cloud, + const Eigen::Vector3d& min_bound, + const Eigen::Vector3d& max_bound, + size_t min_points = 1, + double min_size = std::numeric_limits::epsilon()) : point_cloud_(point_cloud), min_points_(min_points), min_size_(min_size), From 69786b68707379f1113a80b0ed75dee2b1c132f2 Mon Sep 17 00:00:00 2001 From: Luis Alonso Murillo Rojas Date: Sun, 19 May 2024 15:17:21 -0600 Subject: [PATCH 23/82] Add SYCL copy support to tensor (#6764) * Add SYCL support to Tensor To method * Check for non-contiguous sycl tensors (not supported) --------- Co-authored-by: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> --- cpp/open3d/core/CMakeLists.txt | 1 + cpp/open3d/core/Device.h | 4 ++ cpp/open3d/core/Tensor.cpp | 2 +- cpp/open3d/core/kernel/UnaryEW.cpp | 16 ++++++-- cpp/open3d/core/kernel/UnaryEW.h | 4 ++ cpp/open3d/core/kernel/UnaryEWSYCL.cpp | 53 ++++++++++++++++++++++++++ cpp/tests/core/Tensor.cpp | 39 +++++++++++++------ docker/Dockerfile.ci | 2 +- 8 files changed, 103 insertions(+), 18 deletions(-) create mode 100644 cpp/open3d/core/kernel/UnaryEWSYCL.cpp diff --git a/cpp/open3d/core/CMakeLists.txt b/cpp/open3d/core/CMakeLists.txt index d42e645da39..7926d3fbcc6 100644 --- a/cpp/open3d/core/CMakeLists.txt +++ b/cpp/open3d/core/CMakeLists.txt @@ -56,6 +56,7 @@ target_sources(core PRIVATE kernel/ReductionCPU.cpp kernel/UnaryEW.cpp kernel/UnaryEWCPU.cpp + kernel/UnaryEWSYCL.cpp linalg/AddMM.cpp linalg/AddMMCPU.cpp linalg/Det.cpp diff --git a/cpp/open3d/core/Device.h b/cpp/open3d/core/Device.h index 2df1366c5b9..5b04875ed20 100644 --- a/cpp/open3d/core/Device.h +++ b/cpp/open3d/core/Device.h @@ -99,6 +99,10 @@ class IsDevice { inline bool IsCUDA() const { return GetDevice().GetType() == Device::DeviceType::CUDA; } + + inline bool IsSYCL() const { + return GetDevice().GetType() == Device::DeviceType::SYCL; + } }; } // namespace core diff --git a/cpp/open3d/core/Tensor.cpp b/cpp/open3d/core/Tensor.cpp index 7e1014bc800..8859be594e8 100644 --- a/cpp/open3d/core/Tensor.cpp +++ b/cpp/open3d/core/Tensor.cpp @@ -748,7 +748,7 @@ Tensor Tensor::Contiguous() const { std::string Tensor::ToString(bool with_suffix, const std::string& indent) const { std::ostringstream rc; - if (IsCUDA() || !IsContiguous()) { + if (IsCUDA() || IsSYCL() || !IsContiguous()) { Tensor host_contiguous_tensor = Contiguous().To(Device("CPU:0")); rc << host_contiguous_tensor.ToString(false, indent); } else { diff --git a/cpp/open3d/core/kernel/UnaryEW.cpp b/cpp/open3d/core/kernel/UnaryEW.cpp index d2cb7c89f4d..87b99a268aa 100644 --- a/cpp/open3d/core/kernel/UnaryEW.cpp +++ b/cpp/open3d/core/kernel/UnaryEW.cpp @@ -50,20 +50,28 @@ void Copy(const Tensor& src, Tensor& dst) { src.GetShape(), dst.GetShape()); } - // Disbatch to device + // Dispatch to device Device src_device = src.GetDevice(); Device dst_device = dst.GetDevice(); - if ((!src_device.IsCPU() && !src_device.IsCUDA()) || - (!dst_device.IsCPU() && !dst_device.IsCUDA())) { + if ((!src_device.IsCPU() && !src_device.IsCUDA() && !src_device.IsSYCL()) || + (!dst_device.IsCPU() && !dst_device.IsCUDA() && !dst_device.IsSYCL())) { utility::LogError("Copy: Unimplemented device"); } if (src_device.IsCPU() && dst_device.IsCPU()) { CopyCPU(src, dst); - } else { + } else if ((src_device.IsCPU() || src_device.IsCUDA()) && + (dst_device.IsCPU() || dst_device.IsCUDA())) { #ifdef BUILD_CUDA_MODULE CopyCUDA(src, dst); #else utility::LogError("Not compiled with CUDA, but CUDA device is used."); +#endif + } else if ((src_device.IsCPU() || src_device.IsSYCL()) && + (dst_device.IsCPU() || dst_device.IsSYCL())) { +#ifdef BUILD_SYCL_MODULE + CopySYCL(src, dst); +#else + utility::LogError("Not compiled with SYCL, but SYCL device is used."); #endif } } diff --git a/cpp/open3d/core/kernel/UnaryEW.h b/cpp/open3d/core/kernel/UnaryEW.h index c28c30bcc51..985eba0232a 100644 --- a/cpp/open3d/core/kernel/UnaryEW.h +++ b/cpp/open3d/core/kernel/UnaryEW.h @@ -49,6 +49,10 @@ void CopyCPU(const Tensor& src, Tensor& dst); void CopyCUDA(const Tensor& src, Tensor& dst); #endif +#ifdef BUILD_SYCL_MODULE +void CopySYCL(const Tensor& src, Tensor& dst); +#endif + } // namespace kernel } // namespace core } // namespace open3d diff --git a/cpp/open3d/core/kernel/UnaryEWSYCL.cpp b/cpp/open3d/core/kernel/UnaryEWSYCL.cpp new file mode 100644 index 00000000000..e32005c4b36 --- /dev/null +++ b/cpp/open3d/core/kernel/UnaryEWSYCL.cpp @@ -0,0 +1,53 @@ +// ---------------------------------------------------------------------------- +// - Open3D: www.open3d.org - +// ---------------------------------------------------------------------------- +// Copyright (c) 2018-2023 www.open3d.org +// SPDX-License-Identifier: MIT +// ---------------------------------------------------------------------------- + +#include +#include + +#include "open3d/core/Dtype.h" +#include "open3d/core/MemoryManager.h" +#include "open3d/core/SizeVector.h" +#include "open3d/core/Tensor.h" +#include "open3d/core/kernel/UnaryEW.h" +#include "open3d/utility/Logging.h" + +namespace open3d { +namespace core { +namespace kernel { + +void CopySYCL(const Tensor& src, Tensor& dst) { + // It has been checked that + // - at least one of src or dst is SYCL device + SizeVector shape = src.GetShape(); + Dtype src_dtype = src.GetDtype(); + Dtype dst_dtype = dst.GetDtype(); + Device dst_device = dst.GetDevice(); + Device src_device = src.GetDevice(); + + if (src_dtype != dst_dtype) { + utility::LogError( + "CopySYCL: Dtype conversion from src to dst not implemented!"); + } + if ((dst_device.IsSYCL() && !dst.IsContiguous()) || + (src_device.IsSYCL() && !src.IsContiguous())) { + utility::LogError( + "CopySYCL: NonContiguous SYCL tensor Copy not implemented!"); + } + Tensor src_conti = src.Contiguous(); // No op if already contiguous + if (dst.IsContiguous() && src.GetShape() == dst.GetShape() && + src_dtype == dst_dtype) { + MemoryManager::Memcpy(dst.GetDataPtr(), dst_device, + src_conti.GetDataPtr(), src_conti.GetDevice(), + src_dtype.ByteSize() * shape.NumElements()); + } else { + dst.CopyFrom(src_conti.To(dst_device)); + } +} + +} // namespace kernel +} // namespace core +} // namespace open3d diff --git a/cpp/tests/core/Tensor.cpp b/cpp/tests/core/Tensor.cpp index ad377274169..e4a4b14cf9d 100644 --- a/cpp/tests/core/Tensor.cpp +++ b/cpp/tests/core/Tensor.cpp @@ -30,12 +30,24 @@ INSTANTIATE_TEST_SUITE_P(Tensor, TensorPermuteDevices, testing::ValuesIn(PermuteDevices::TestCases())); +class TensorPermuteDevicesWithSYCL : public PermuteDevices {}; +INSTANTIATE_TEST_SUITE_P( + Tensor, + TensorPermuteDevicesWithSYCL, + testing::ValuesIn(PermuteDevicesWithSYCL::TestCases())); + class TensorPermuteDevicePairs : public PermuteDevicePairs {}; INSTANTIATE_TEST_SUITE_P( Tensor, TensorPermuteDevicePairs, testing::ValuesIn(TensorPermuteDevicePairs::TestCases())); +class TensorPermuteDevicePairsWithSYCL : public PermuteDevicePairsWithSYCL {}; +INSTANTIATE_TEST_SUITE_P( + Tensor, + TensorPermuteDevicePairsWithSYCL, + testing::ValuesIn(TensorPermuteDevicePairsWithSYCL::TestCases())); + class TensorPermuteSizesDefaultStridesAndDevices : public testing::TestWithParam< std::tuple, @@ -54,7 +66,7 @@ static constexpr const T &AsConst(T &t) noexcept { return t; } -TEST_P(TensorPermuteDevices, Constructor) { +TEST_P(TensorPermuteDevicesWithSYCL, Constructor) { core::Device device = GetParam(); core::Dtype dtype = core::Float32; @@ -71,7 +83,7 @@ TEST_P(TensorPermuteDevices, Constructor) { EXPECT_ANY_THROW(core::Tensor({-1, -1}, dtype, device)); } -TEST_P(TensorPermuteDevices, ConstructorBool) { +TEST_P(TensorPermuteDevicesWithSYCL, ConstructorBool) { core::Device device = GetParam(); core::SizeVector shape{2, 3}; @@ -105,7 +117,7 @@ TEST_P(TensorPermuteDevices, WithInitValue) { EXPECT_EQ(t.ToFlatVector(), vals); } -TEST_P(TensorPermuteDevices, WithInitList) { +TEST_P(TensorPermuteDevicesWithSYCL, WithInitList) { core::Device device = GetParam(); core::Tensor t; @@ -187,7 +199,7 @@ TEST_P(TensorPermuteDevices, WithInitList) { std::exception); } -TEST_P(TensorPermuteDevices, WithInitValueBool) { +TEST_P(TensorPermuteDevicesWithSYCL, WithInitValueBool) { core::Device device = GetParam(); std::vector vals{true, false, true, true, false, false}; @@ -195,7 +207,7 @@ TEST_P(TensorPermuteDevices, WithInitValueBool) { EXPECT_EQ(t.ToFlatVector(), vals); } -TEST_P(TensorPermuteDevices, WithInitValueTypeMismatch) { +TEST_P(TensorPermuteDevicesWithSYCL, WithInitValueTypeMismatch) { core::Device device = GetParam(); std::vector vals{0, 1, 2, 3, 4, 5}; @@ -203,7 +215,7 @@ TEST_P(TensorPermuteDevices, WithInitValueTypeMismatch) { std::runtime_error); } -TEST_P(TensorPermuteDevices, WithInitValueSizeMismatch) { +TEST_P(TensorPermuteDevicesWithSYCL, WithInitValueSizeMismatch) { core::Device device = GetParam(); std::vector vals{0, 1, 2, 3, 4}; @@ -298,7 +310,7 @@ TEST_P(TensorPermuteDevicePairs, IndexSetFillFancy) { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0})); } -TEST_P(TensorPermuteDevicePairs, Copy) { +TEST_P(TensorPermuteDevicePairsWithSYCL, Copy) { core::Device dst_device; core::Device src_device; std::tie(dst_device, src_device) = GetParam(); @@ -317,7 +329,7 @@ TEST_P(TensorPermuteDevicePairs, Copy) { EXPECT_EQ(dst_t.ToFlatVector(), vals); } -TEST_P(TensorPermuteDevicePairs, CopyBool) { +TEST_P(TensorPermuteDevicePairsWithSYCL, CopyBool) { core::Device dst_device; core::Device src_device; std::tie(dst_device, src_device) = GetParam(); @@ -357,12 +369,15 @@ TEST_P(TensorPermuteDevicePairs, ToDevice) { core::Device src_device; std::tie(dst_device, src_device) = GetParam(); - core::Tensor src_t = core::Tensor::Init({0, 1, 2, 3}, src_device); + core::Tensor src_t = + core::Tensor::Init({0.f, 1.f, 2.f, 3.f}, src_device); core::Tensor dst_t = src_t.To(dst_device); EXPECT_TRUE(dst_t.To(src_device).AllClose(src_t)); EXPECT_ANY_THROW(src_t.To(core::Device("CPU:1"))); + EXPECT_ANY_THROW(src_t.To(core::Device("SYCL:100"))); + EXPECT_ANY_THROW(src_t.To(core::Device("CUDA:-1"))); EXPECT_ANY_THROW(src_t.To(core::Device("CUDA:100000"))); } @@ -529,7 +544,7 @@ TEST_P(TensorPermuteDevices, Flatten) { EXPECT_ANY_THROW(src_t.Flatten(2, 1)); } -TEST_P(TensorPermuteDevices, DefaultStrides) { +TEST_P(TensorPermuteDevicesWithSYCL, DefaultStrides) { core::Device device = GetParam(); core::Tensor t0({}, core::Float32, device); @@ -663,7 +678,7 @@ TEST_P(TensorPermuteDevices, ItemAssign) { EXPECT_EQ(t[1][2][3].Item(), 101); } -TEST_P(TensorPermuteDevices, ToString) { +TEST_P(TensorPermuteDevicesWithSYCL, ToString) { using ::testing::AnyOf; core::Device device = GetParam(); core::Tensor t; @@ -738,7 +753,7 @@ TEST_P(TensorPermuteDevices, ToString) { [True False False]])"); } -TEST_P(TensorPermuteDevicePairs, CopyContiguous) { +TEST_P(TensorPermuteDevicePairsWithSYCL, CopyContiguous) { core::Device dst_device; core::Device src_device; std::tie(dst_device, src_device) = GetParam(); diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index 73dd3fe1aac..81379352f41 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -94,7 +94,7 @@ RUN conda --version \ # Activate open3d virtualenv # This works during docker build. It becomes the prefix of all RUN commands. # Ref: https://stackoverflow.com/a/60148365/1255535 -SHELL ["conda", "run", "-n", "open3d", "/bin/bash", "-c"] +SHELL ["conda", "run", "-n", "open3d", "/bin/bash", "-o", "pipefail", "-c"] # Dependencies: cmake ENV PATH=${HOME}/${CMAKE_VERSION}/bin:${PATH} From b68eae1616de77529654d038482a34bb8ecb7190 Mon Sep 17 00:00:00 2001 From: Nicola Loi <79461707+nicolaloi@users.noreply.github.com> Date: Thu, 23 May 2024 16:27:10 +0200 Subject: [PATCH 24/82] Fix ransac update of the iteration number (#6789) The update of the iteration number is forced to be non-negative (was negative if corres_inlier_ratio = 0.0) --- CHANGELOG.md | 1 + cpp/open3d/pipelines/registration/Registration.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3b964d6d0..3d9d4bbb575 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Fix log error message for `probability` argument validation in `PointCloud::SegmentPlane` (PR #6622) - Fix macOS arm64 builds, add CI runner for macOS arm64 (PR #6695) - Fix KDTreeFlann possibly using a dangling pointer instead of internal storage and simplified its members (PR #6734) +- Fix RANSAC early stop if no inliers in a specific iteration (PR #6789) - Fix segmentation fault (infinite recursion) of DetectPlanarPatches if multiple points have same coordinates (PR #6794) ## 0.13 diff --git a/cpp/open3d/pipelines/registration/Registration.cpp b/cpp/open3d/pipelines/registration/Registration.cpp index 8527d620bf0..6820d2f7c33 100644 --- a/cpp/open3d/pipelines/registration/Registration.cpp +++ b/cpp/open3d/pipelines/registration/Registration.cpp @@ -235,13 +235,15 @@ RegistrationResult RegistrationRANSACBasedOnCorrespondence( max_correspondence_distance, transformation); - // Update exit condition if necessary. - // If confidence is 1.0, then it is safely inf, we always - // consume all the iterations. + // Update exit condition if necessary double est_k_local_d = std::log(1.0 - criteria.confidence_) / std::log(1.0 - std::pow(corres_inlier_ratio, ransac_n)); + // This prevents having a negative number of iterations: + // est_k_local_d = -inf if corres_inlier_ratio = 0.0 + est_k_local_d = + est_k_local_d < 0 ? est_k_local : est_k_local_d; est_k_local = est_k_local_d < est_k_global ? static_cast(std::ceil(est_k_local_d)) From f1f275bb03a7fb69e7bb59c4cc4b5dcd93741134 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:47:27 -0700 Subject: [PATCH 25/82] Camera coordinate frame visualization with LineSet (#6796) --- cpp/open3d/geometry/LineSetFactory.cpp | 19 +++++++ cpp/open3d/t/geometry/LineSet.cpp | 76 ++++++++++++++++++++++++++ cpp/open3d/t/geometry/LineSet.h | 22 ++++++++ cpp/pybind/t/geometry/lineset.cpp | 31 +++++++++++ 4 files changed, 148 insertions(+) diff --git a/cpp/open3d/geometry/LineSetFactory.cpp b/cpp/open3d/geometry/LineSetFactory.cpp index 41e44d1db5f..3a66d86d300 100644 --- a/cpp/open3d/geometry/LineSetFactory.cpp +++ b/cpp/open3d/geometry/LineSetFactory.cpp @@ -170,6 +170,25 @@ std::shared_ptr LineSet::CreateCameraVisualization( lines->lines_.push_back({4, 1}); lines->PaintUniformColor({0.0f, 0.0f, 1.0f}); + // Add XYZ axes + lines->points_.push_back( + mult(m, Eigen::Vector3d{intrinsic(0, 0) * scale, 0.0, 0.0})); + lines->points_.push_back( + mult(m, Eigen::Vector3d{0.0, intrinsic(1, 1) * scale, 0.0})); + lines->points_.push_back( + mult(m, Eigen::Vector3d{intrinsic(0, 2) * scale, + intrinsic(1, 2) * scale, scale})); + + // Add lines for the axes + lines->lines_.push_back({0, 5}); // X axis (red) + lines->lines_.push_back({0, 6}); // Y axis (green) + lines->lines_.push_back({0, 7}); // Z axis (blue) + + // Set colors for the axes + lines->colors_.push_back({1.0f, 0.0f, 0.0f}); // Red + lines->colors_.push_back({0.0f, 1.0f, 0.0f}); // Green + lines->colors_.push_back({0.0f, 0.0f, 1.0f}); // Blue + return lines; } diff --git a/cpp/open3d/t/geometry/LineSet.cpp b/cpp/open3d/t/geometry/LineSet.cpp index c2d95f0ced2..f525694ca45 100644 --- a/cpp/open3d/t/geometry/LineSet.cpp +++ b/cpp/open3d/t/geometry/LineSet.cpp @@ -9,6 +9,7 @@ #include +#include "open3d/core/Dtype.h" #include "open3d/core/EigenConverter.h" #include "open3d/core/ShapeUtil.h" #include "open3d/core/Tensor.h" @@ -211,6 +212,81 @@ OrientedBoundingBox LineSet::GetOrientedBoundingBox() const { return OrientedBoundingBox::CreateFromPoints(GetPointPositions()); } +LineSet &LineSet::PaintUniformColor(const core::Tensor &color) { + core::AssertTensorShape(color, {3}); + core::Tensor clipped_color = color.To(GetDevice()); + if (color.GetDtype() == core::Float32 || + color.GetDtype() == core::Float64) { + clipped_color = clipped_color.Clip(0.0f, 1.0f); + } + core::Tensor ls_colors = + core::Tensor::Empty({GetLineIndices().GetLength(), 3}, + clipped_color.GetDtype(), GetDevice()); + ls_colors.AsRvalue() = clipped_color; + SetLineColors(ls_colors); + + return *this; +} + +LineSet LineSet::CreateCameraVisualization(int view_width_px, + int view_height_px, + const core::Tensor &intrinsic_in, + const core::Tensor &extrinsic_in, + double scale, + const core::Tensor &color) { + core::AssertTensorShape(intrinsic_in, {3, 3}); + core::AssertTensorShape(extrinsic_in, {4, 4}); + core::Tensor intrinsic = intrinsic_in.To(core::Float32, "CPU:0"); + core::Tensor extrinsic = extrinsic_in.To(core::Float32, "CPU:0"); + + // Calculate points for camera visualization + float w(view_width_px), h(view_height_px), s(scale); + float fx = intrinsic[0][0].Item(), + fy = intrinsic[1][1].Item(), + cx = intrinsic[0][2].Item(), + cy = intrinsic[1][2].Item(); + core::Tensor points = core::Tensor::Init({{0.f, 0.f, 0.f}, // origin + {0.f, 0.f, s}, + {w * s, 0.f, s}, + {w * s, h * s, s}, + {0.f, h * s, s}, + // Add XYZ axes + {fx * s, 0.f, 0.f}, + {0.f, fy * s, 0.f}, + {cx * s, cy * s, s}}); + points = (intrinsic.Inverse().Matmul(points.T()) - + extrinsic.Slice(0, 0, 3).Slice(1, 3, 4)) + .T() + .Matmul(extrinsic.Slice(0, 0, 3).Slice(1, 0, 3)); + + // Add lines for camera frame and XYZ axes + core::Tensor lines = core::Tensor::Init({{0, 1}, + {0, 2}, + {0, 3}, + {0, 4}, + {1, 2}, + {2, 3}, + {3, 4}, + {4, 1}, + // Add XYZ axes + {0, 5}, + {0, 6}, + {0, 7}}); + + LineSet lineset(points, lines); + if (color.NumElements() == 3) { + lineset.PaintUniformColor(color); + } else { + lineset.PaintUniformColor(core::Tensor::Init({0.f, 0.f, 1.f})); + } + auto &lscolors = lineset.GetLineColors(); + lscolors[8] = core::Tensor::Init({1.f, 0.f, 0.f}); // Red + lscolors[9] = core::Tensor::Init({0.f, 1.f, 0.f}); // Green + lscolors[10] = core::Tensor::Init({0.f, 0.f, 1.f}); // Blue + + return lineset; +} + } // namespace geometry } // namespace t } // namespace open3d diff --git a/cpp/open3d/t/geometry/LineSet.h b/cpp/open3d/t/geometry/LineSet.h index 6c3ecb66da8..38fb7c61be2 100644 --- a/cpp/open3d/t/geometry/LineSet.h +++ b/cpp/open3d/t/geometry/LineSet.h @@ -337,6 +337,12 @@ class LineSet : public Geometry, public DrawableGeometry { /// \return Rotated line set. LineSet &Rotate(const core::Tensor &R, const core::Tensor ¢er); + /// \brief Assigns uniform color to all lines of the LineSet. + /// + /// \param color RGB color for the LineSet. {3,} shaped Tensor. + /// Floating color values are clipped between 0.0 and 1.0. + LineSet &PaintUniformColor(const core::Tensor &color); + /// \brief Returns the device attribute of this LineSet. core::Device GetDevice() const override { return device_; } @@ -385,6 +391,22 @@ class LineSet : public Geometry, public DrawableGeometry { double scale = 1.0, bool capping = true) const; + /// Factory function to create a LineSet from intrinsic and extrinsic + /// matrices. + /// + /// \param view_width_px The width of the view, in pixels. + /// \param view_height_px The height of the view, in pixels. + /// \param intrinsic The intrinsic matrix {3,3} shape. + /// \param extrinsic The extrinsic matrix {4,4} shape. + /// \param scale camera scale + /// \param color tensor with float32 dtype and shape {3}. Default is blue. + static LineSet CreateCameraVisualization(int view_width_px, + int view_height_px, + const core::Tensor &intrinsic, + const core::Tensor &extrinsic, + double scale, + const core::Tensor &color = {}); + protected: core::Device device_ = core::Device("CPU:0"); TensorMap point_attr_; diff --git a/cpp/pybind/t/geometry/lineset.cpp b/cpp/pybind/t/geometry/lineset.cpp index d9bf8adbe43..299deef36b0 100644 --- a/cpp/pybind/t/geometry/lineset.cpp +++ b/cpp/pybind/t/geometry/lineset.cpp @@ -299,6 +299,37 @@ transformation as :math:`P = R(P) + t`)"); mesh = lines.extrude_linear([0,1,0]) o3d.visualization.draw([{'name': 'L', 'geometry': mesh}]) +)"); + line_set.def("paint_uniform_color", &LineSet::PaintUniformColor, "color"_a, + "Assigns unifom color to all the lines of the LineSet. " + "Floating color values are clipped between 00 and 1.0. Input " + "`color` should be a (3,) shape tensor."); + line_set.def_static( + "create_camera_visualization", &LineSet::CreateCameraVisualization, + "view_width_px"_a, "view_height_px"_a, "intrinsic"_a, "extrinsic"_a, + "scale"_a = 1.f, "color"_a = core::Tensor({}, core::Float32), + R"(Factory function to create a LineSet from intrinsic and extrinsic +matrices. Camera reference frame is shown with XYZ axes in RGB. + +Args: + view_width_px (int): The width of the view, in pixels. + view_height_px (int): The height of the view, in pixels. + intrinsic (open3d.core.Tensor): The intrinsic matrix {3,3} shape. + extrinsic (open3d.core.Tensor): The extrinsic matrix {4,4} shape. + scale (float): camera scale + color (open3d.core.Tensor): color with float32 and shape {3}. Default is blue. + +Example: + + Draw a purple camera frame with XYZ axes in RGB. + + import open3d.core as o3c + from open3d.t.geometry import LineSet + from open3d.visualization import draw + K = o3c.Tensor([[512, 0, 512], [0, 512, 512], [0, 0, 1]], dtype=o3c.float32) + T = o3c.Tensor.eye(4, dtype=o3c.float32) + ls = LineSet.create_camera_visualization(1024, 1024, K, T, 1, [0.8, 0.2, 0.8]) + draw([ls]) )"); } From 525c4e6567f3a885f930571c3cc83233e35573aa Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:02:33 -0700 Subject: [PATCH 26/82] Add security policy, GITHUB_TOKEN access restrictions. (#6814) * Add security policy * contents:write for artifact upload, github releases * Add actions:write for concurrency cancellation --- .github/workflows/clean-gcloud-profiles.yml | 7 ++----- .github/workflows/documentation.yml | 3 +++ .github/workflows/macos.yml | 3 +++ .github/workflows/style.yml | 3 +++ .github/workflows/ubuntu-cuda.yml | 3 +++ .github/workflows/ubuntu-openblas.yml | 3 +++ .github/workflows/ubuntu-sycl.yml | 3 +++ .github/workflows/ubuntu-wheel.yml | 5 ++++- .github/workflows/ubuntu.yml | 3 +++ .github/workflows/vtk_packages.yml | 4 ++-- .github/workflows/webrtc.yml | 3 +++ .github/workflows/windows.yml | 3 +++ SECURITY.md | 5 +++++ 13 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 SECURITY.md diff --git a/.github/workflows/clean-gcloud-profiles.yml b/.github/workflows/clean-gcloud-profiles.yml index 0aec813a48f..2521b1de160 100644 --- a/.github/workflows/clean-gcloud-profiles.yml +++ b/.github/workflows/clean-gcloud-profiles.yml @@ -16,14 +16,11 @@ # happens, run this workflow manually to clean up the login profiles. name: Clean GCloud Profiles +permissions: + contents: read on: workflow_dispatch: - # push: - # branches: - # - main - # pull_request: - # types: [opened, reopened, synchronize] env: GCE_GPU_CI_SA: ${{ secrets.GCE_GPU_CI_SA }} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 00580af33d9..83527c68b56 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,4 +1,7 @@ name: Documentation +permissions: + contents: write + actions: write on: workflow_dispatch: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f0234f975ee..362cc2327f0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -1,4 +1,7 @@ name: MacOS +permissions: + contents: write + actions: write on: workflow_dispatch: diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index e0de61516be..3c27504fdd9 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -1,4 +1,7 @@ name: Style Check +permissions: + contents: read + actions: write on: workflow_dispatch: diff --git a/.github/workflows/ubuntu-cuda.yml b/.github/workflows/ubuntu-cuda.yml index 1ff1d20c75b..9cf3cd3e749 100644 --- a/.github/workflows/ubuntu-cuda.yml +++ b/.github/workflows/ubuntu-cuda.yml @@ -1,4 +1,7 @@ name: Ubuntu CUDA +permissions: + contents: write + actions: write on: workflow_dispatch: diff --git a/.github/workflows/ubuntu-openblas.yml b/.github/workflows/ubuntu-openblas.yml index 9bbb423f457..acfe20e4ddf 100644 --- a/.github/workflows/ubuntu-openblas.yml +++ b/.github/workflows/ubuntu-openblas.yml @@ -1,4 +1,7 @@ name: Ubuntu OpenBLAS +permissions: + contents: read + actions: write on: workflow_dispatch: diff --git a/.github/workflows/ubuntu-sycl.yml b/.github/workflows/ubuntu-sycl.yml index 4f4c9f6d9c1..984d0fe9485 100644 --- a/.github/workflows/ubuntu-sycl.yml +++ b/.github/workflows/ubuntu-sycl.yml @@ -1,4 +1,7 @@ name: Ubuntu SYCL +permissions: + contents: read + actions: write on: workflow_dispatch: diff --git a/.github/workflows/ubuntu-wheel.yml b/.github/workflows/ubuntu-wheel.yml index 2ff88c74b6a..733c52218ee 100644 --- a/.github/workflows/ubuntu-wheel.yml +++ b/.github/workflows/ubuntu-wheel.yml @@ -1,4 +1,7 @@ name: Ubuntu Wheel +permissions: + contents: write + actions: write on: workflow_dispatch: @@ -102,7 +105,7 @@ jobs: run: | gsutil cp ${GITHUB_WORKSPACE}/${{ env.CCACHE_TAR_NAME }}.tar.gz gs://open3d-ci-cache/ - name: Update devel release - # if: ${{ github.ref == 'refs/heads/main' }} + if: ${{ github.ref == 'refs/heads/main' }} env: GH_TOKEN: ${{ github.token }} run: | diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index ba2765c82c7..08ef6721fea 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -1,4 +1,7 @@ name: Ubuntu +permissions: + contents: write + actions: write on: workflow_dispatch: diff --git a/.github/workflows/vtk_packages.yml b/.github/workflows/vtk_packages.yml index acd20c5260b..a134daeb71a 100644 --- a/.github/workflows/vtk_packages.yml +++ b/.github/workflows/vtk_packages.yml @@ -1,8 +1,8 @@ name: VTK Packages +permissions: + contents: write on: - # pull_request: - # branches: [ main ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.github/workflows/webrtc.yml b/.github/workflows/webrtc.yml index 90c45e053e7..91a9a5b122d 100644 --- a/.github/workflows/webrtc.yml +++ b/.github/workflows/webrtc.yml @@ -1,4 +1,7 @@ name: WebRTC +permissions: + contents: write + actions: write on: workflow_dispatch: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 141f1917a30..c3c1d1e8395 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,4 +1,7 @@ name: Windows +permissions: + contents: write + actions: write on: workflow_dispatch: diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..38d9c833993 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy +Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. + +## Reporting a Vulnerability +Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). \ No newline at end of file From 0bdedd347a223b5434b3788aef2e79ea604dbbdf Mon Sep 17 00:00:00 2001 From: daizhirui Date: Fri, 14 Jun 2024 11:26:02 -0700 Subject: [PATCH 27/82] add support to fmt-v10 (#6783) * fix build of examples OnlineSLAMRGBD with fmt 10.2.0 * fix build with Intel-LLVM toolchains * change dependencies of fmt from 9.0.0 to 10.2.0 except Windows * change fmt from 6.0.0 to 10.2.0 for windows * fix build on Windows with CUDA enabled * fix build when BUILD_AZURE_KINECT is ON * fix build on Windows with MSVC v142 and CUDA enabled --- 3rdparty/find_dependencies.cmake | 4 +- 3rdparty/fmt/fmt.cmake | 20 ++-- CHANGELOG.md | 1 + cpp/open3d/core/DLPack.h | 52 +++++++++ cpp/open3d/core/linalg/LinalgHeadersCUDA.h | 103 ++++++++++++++++++ cpp/open3d/io/IJsonConvertibleIO.h | 2 +- cpp/open3d/io/sensor/azure_kinect/K4aPlugin.h | 35 ++++++ cpp/open3d/ml/pytorch/CMakeLists.txt | 11 +- cpp/open3d/t/geometry/RaycastingScene.cpp | 42 ++++++- .../t/io/sensor/realsense/RSBagReader.cpp | 3 + cpp/open3d/utility/Logging.h | 3 + cpp/open3d/utility/ParallelScan.h | 8 ++ .../visualization/rendering/RendererHandle.h | 2 +- .../filament/FilamentResourceManager.cpp | 4 +- .../webrtc_server/PeerConnectionManager.h | 42 +++++++ examples/cpp/OnlineSLAMUtil.h | 4 + 16 files changed, 321 insertions(+), 15 deletions(-) diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index d929b27728a..45555b83a84 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -1554,7 +1554,9 @@ if(OPEN3D_USE_ONEAPI_PACKAGES) TARGETS TBB::tbb ) list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_tbb) - + target_compile_definitions(3rdparty_tbb INTERFACE OPEN3D_USE_ONEAPI_PACKAGES=1) + target_compile_definitions(3rdparty_tbb INTERFACE _PSTL_UDR_PRESENT=0) + target_compile_definitions(3rdparty_tbb INTERFACE _PSTL_UDS_PRESENT=0) # 2. oneDPL # /opt/intel/oneapi/dpl/latest/lib/cmake/oneDPL open3d_find_package_3rdparty_library(3rdparty_onedpl diff --git a/3rdparty/fmt/fmt.cmake b/3rdparty/fmt/fmt.cmake index 88cd8e2fcef..d7698e1a645 100644 --- a/3rdparty/fmt/fmt.cmake +++ b/3rdparty/fmt/fmt.cmake @@ -2,16 +2,20 @@ include(ExternalProject) set(FMT_LIB_NAME fmt) -if (MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") - # MSVC has errors when building fmt >6, up till 9.1 - # SYCL / DPC++ needs fmt ver <=6 or >= 9.2: https://github.com/fmtlib/fmt/issues/3005 - set(FMT_VER "6.0.0") - set(FMT_SHA256 - "f1907a58d5e86e6c382e51441d92ad9e23aea63827ba47fd647eacc0d3a16c78") +if (MSVC AND BUILD_CUDA_MODULE) + if (MSVC_VERSION GREATER_EQUAL 1930) # v143 + set(FMT_VER "10.1.1") + set(FMT_SHA256 + "78b8c0a72b1c35e4443a7e308df52498252d1cefc2b08c9a97bc9ee6cfe61f8b") + else() + set(FMT_VER "6.0.0") + set(FMT_SHA256 + "f1907a58d5e86e6c382e51441d92ad9e23aea63827ba47fd647eacc0d3a16c78") + endif() else() - set(FMT_VER "9.0.0") + set(FMT_VER "10.2.1") set(FMT_SHA256 - "9a1e0e9e843a356d65c7604e2c8bf9402b50fe294c355de0095ebd42fb9bd2c5") + "1250e4cc58bf06ee631567523f48848dc4596133e163f02615c97f78bab6c811") endif() ExternalProject_Add( diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d9d4bbb575..743afd15834 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - Fix KDTreeFlann possibly using a dangling pointer instead of internal storage and simplified its members (PR #6734) - Fix RANSAC early stop if no inliers in a specific iteration (PR #6789) - Fix segmentation fault (infinite recursion) of DetectPlanarPatches if multiple points have same coordinates (PR #6794) +- Fix build with fmt v10.2.0 (#6783) ## 0.13 diff --git a/cpp/open3d/core/DLPack.h b/cpp/open3d/core/DLPack.h index 9a0c3e57aff..1cec10a712f 100644 --- a/cpp/open3d/core/DLPack.h +++ b/cpp/open3d/core/DLPack.h @@ -188,4 +188,56 @@ typedef struct DLManagedTensor { #ifdef __cplusplus } // DLPACK_EXTERN_C #endif + +#include +#include + +namespace fmt { + +template <> +struct formatter { + template + auto format(const DLDeviceType& c, FormatContext& ctx) const + -> decltype(ctx.out()) { + const char* text = nullptr; + switch (c) { + case kDLCPU: + text = "kDLCPU"; + break; + case kDLGPU: + text = "kDLGPU"; + break; + case kDLCPUPinned: + text = "kDLCPUPinned"; + break; + case kDLOpenCL: + text = "kDLOpenCL"; + break; + case kDLVulkan: + text = "kDLVulkan"; + break; + case kDLMetal: + text = "kDLMetal"; + break; + case kDLVPI: + text = "kDLVPI"; + break; + case kDLROCM: + text = "kDLROCM"; + break; + case kDLExtDev: + text = "kDLExtDev"; + break; + } + return format_to(ctx.out(), text); + } + + template + constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } +}; + +} // namespace fmt + #endif // DLPACK_DLPACK_H_ diff --git a/cpp/open3d/core/linalg/LinalgHeadersCUDA.h b/cpp/open3d/core/linalg/LinalgHeadersCUDA.h index d34394dc813..cf1b8d635ba 100644 --- a/cpp/open3d/core/linalg/LinalgHeadersCUDA.h +++ b/cpp/open3d/core/linalg/LinalgHeadersCUDA.h @@ -16,4 +16,107 @@ #include #include #include +#include +#include + +namespace fmt { + +template <> +struct formatter { + template + auto format(const cusolverStatus_t& c, FormatContext& ctx) const + -> decltype(ctx.out()) { + const char* text = nullptr; + switch (c) { + case CUSOLVER_STATUS_SUCCESS: + text = "CUSOLVER_STATUS_SUCCESS"; + break; + case CUSOLVER_STATUS_NOT_INITIALIZED: + text = "CUSOLVER_STATUS_NOT_INITIALIZED"; + break; + case CUSOLVER_STATUS_ALLOC_FAILED: + text = "CUSOLVER_STATUS_ALLOC_FAILED"; + break; + case CUSOLVER_STATUS_INVALID_VALUE: + text = "CUSOLVER_STATUS_INVALID_VALUE"; + break; + case CUSOLVER_STATUS_ARCH_MISMATCH: + text = "CUSOLVER_STATUS_ARCH_MISMATCH"; + break; + case CUSOLVER_STATUS_MAPPING_ERROR: + text = "CUSOLVER_STATUS_MAPPING_ERROR"; + break; + case CUSOLVER_STATUS_EXECUTION_FAILED: + text = "CUSOLVER_STATUS_EXECUTION_FAILED"; + break; + case CUSOLVER_STATUS_INTERNAL_ERROR: + text = "CUSOLVER_STATUS_INTERNAL_ERROR"; + break; + case CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED: + text = "CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED"; + break; + case CUSOLVER_STATUS_NOT_SUPPORTED: + text = "CUSOLVER_STATUS_NOT_SUPPORTED"; + break; + case CUSOLVER_STATUS_ZERO_PIVOT: + text = "CUSOLVER_STATUS_ZERO_PIVOT"; + break; + case CUSOLVER_STATUS_INVALID_LICENSE: + text = "CUSOLVER_STATUS_INVALID_LICENSE"; + break; + case CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED: + text = "CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED"; + break; + case CUSOLVER_STATUS_IRS_PARAMS_INVALID: + text = "CUSOLVER_STATUS_IRS_PARAMS_INVALID"; + break; + case CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC: + text = "CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC"; + break; + case CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE: + text = "CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE"; + break; + case CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER: + text = "CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER"; + break; + case CUSOLVER_STATUS_IRS_INTERNAL_ERROR: + text = "CUSOLVER_STATUS_IRS_INTERNAL_ERROR"; + break; + case CUSOLVER_STATUS_IRS_NOT_SUPPORTED: + text = "CUSOLVER_STATUS_IRS_NOT_SUPPORTED"; + break; + case CUSOLVER_STATUS_IRS_OUT_OF_RANGE: + text = "CUSOLVER_STATUS_IRS_OUT_OF_RANGE"; + break; + case CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES: + text = "CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_" + "GMRES"; + break; + case CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED: + text = "CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED"; + break; + case CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED: + text = "CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED"; + break; + case CUSOLVER_STATUS_IRS_MATRIX_SINGULAR: + text = "CUSOLVER_STATUS_IRS_MATRIX_SINGULAR"; + break; + case CUSOLVER_STATUS_INVALID_WORKSPACE: + text = "CUSOLVER_STATUS_INVALID_WORKSPACE"; + break; + default: + text = "CUSOLVER_STATUS_UNKNOWN"; + break; + } + return format_to(ctx.out(), text); + } + + template + constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } +}; + +} // namespace fmt + #endif diff --git a/cpp/open3d/io/IJsonConvertibleIO.h b/cpp/open3d/io/IJsonConvertibleIO.h index 8bc9f03b13b..a366b4f5ef8 100644 --- a/cpp/open3d/io/IJsonConvertibleIO.h +++ b/cpp/open3d/io/IJsonConvertibleIO.h @@ -82,7 +82,7 @@ bool WriteIJsonConvertibleToJSONString(std::string &json_string, [&str](const std::pair &es_pair) \ -> bool { return es_pair.second == str; }); \ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ - utility::LogDebug("{} -> {}", str, e); \ + utility::LogDebug("{} -> {}", str, enum_to_string(e)); \ } } // namespace io diff --git a/cpp/open3d/io/sensor/azure_kinect/K4aPlugin.h b/cpp/open3d/io/sensor/azure_kinect/K4aPlugin.h index cd9a6eb1db1..15e461d4285 100644 --- a/cpp/open3d/io/sensor/azure_kinect/K4aPlugin.h +++ b/cpp/open3d/io/sensor/azure_kinect/K4aPlugin.h @@ -295,3 +295,38 @@ k4a_result_t k4a_transformation_depth_image_to_point_cloud( } // namespace k4a_plugin } // namespace io } // namespace open3d + +#include + +namespace fmt { + +template <> +struct formatter { + template + auto format(const k4a_wait_result_t &c, FormatContext &ctx) const + -> decltype(ctx.out()) { + const char *text = nullptr; + switch (c) { + case K4A_WAIT_RESULT_SUCCEEDED: + text = "K4A_WAIT_RESULT_SUCCEEDED"; + break; + case K4A_WAIT_RESULT_FAILED: + text = "K4A_WAIT_RESULT_FAILED"; + break; + case K4A_WAIT_RESULT_TIMEOUT: + text = "K4A_WAIT_RESULT_TIMEOUT"; + break; + default: + text = "Unknown k4a_wait_result_t"; + break; + } + return format_to(ctx.out(), text); + } + + template + constexpr auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } +}; + +} // namespace fmt diff --git a/cpp/open3d/ml/pytorch/CMakeLists.txt b/cpp/open3d/ml/pytorch/CMakeLists.txt index adc9ac48e3d..3cb75e531de 100644 --- a/cpp/open3d/ml/pytorch/CMakeLists.txt +++ b/cpp/open3d/ml/pytorch/CMakeLists.txt @@ -140,9 +140,18 @@ target_link_libraries(open3d_torch_ops PRIVATE Open3D::3rdparty_eigen3 Open3D::3rdparty_fmt Open3D::3rdparty_nanoflann - Open3D::3rdparty_parallelstl Open3D::3rdparty_tbb ) +if (TARGET Open3D::3rdparty_parallelstl) + target_link_libraries(open3d_torch_ops PRIVATE + Open3D::3rdparty_parallelstl + ) +endif() +if (TARGET Open3D::3rdparty_onedpl) + target_link_libraries(open3d_torch_ops PRIVATE + Open3D::3rdparty_onedpl + ) +endif() if (BUILD_CUDA_MODULE) target_link_libraries(open3d_torch_ops PRIVATE diff --git a/cpp/open3d/t/geometry/RaycastingScene.cpp b/cpp/open3d/t/geometry/RaycastingScene.cpp index 14f9962c26c..8906f6373e5 100644 --- a/cpp/open3d/t/geometry/RaycastingScene.cpp +++ b/cpp/open3d/t/geometry/RaycastingScene.cpp @@ -1173,4 +1173,44 @@ uint32_t RaycastingScene::INVALID_ID() { return RTC_INVALID_GEOMETRY_ID; } } // namespace geometry } // namespace t -} // namespace open3d \ No newline at end of file +} // namespace open3d + +namespace fmt { +template <> +struct formatter { + template + auto format(const RTCError& c, FormatContext& ctx) { + const char* name = nullptr; + switch (c) { + case RTC_ERROR_NONE: + name = "RTC_ERROR_NONE"; + break; + case RTC_ERROR_UNKNOWN: + name = "RTC_ERROR_UNKNOWN"; + break; + case RTC_ERROR_INVALID_ARGUMENT: + name = "RTC_ERROR_INVALID_ARGUMENT"; + break; + case RTC_ERROR_INVALID_OPERATION: + name = "RTC_ERROR_INVALID_OPERATION"; + break; + case RTC_ERROR_OUT_OF_MEMORY: + name = "RTC_ERROR_OUT_OF_MEMORY"; + break; + case RTC_ERROR_UNSUPPORTED_CPU: + name = "RTC_ERROR_UNSUPPORTED_CPU"; + break; + case RTC_ERROR_CANCELLED: + name = "RTC_ERROR_CANCELLED"; + break; + } + // return formatter::format(name, ctx); + return format_to(ctx.out(), name); + } + + template + constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } +}; +} // namespace fmt diff --git a/cpp/open3d/t/io/sensor/realsense/RSBagReader.cpp b/cpp/open3d/t/io/sensor/realsense/RSBagReader.cpp index 356fdbed6bc..1bb6dd76081 100644 --- a/cpp/open3d/t/io/sensor/realsense/RSBagReader.cpp +++ b/cpp/open3d/t/io/sensor/realsense/RSBagReader.cpp @@ -7,6 +7,9 @@ #include "open3d/t/io/sensor/realsense/RSBagReader.h" +#if FMT_VERSION >= 100000 +#include +#endif #include #include diff --git a/cpp/open3d/utility/Logging.h b/cpp/open3d/utility/Logging.h index 15fe28fa539..fdb132f9b06 100644 --- a/cpp/open3d/utility/Logging.h +++ b/cpp/open3d/utility/Logging.h @@ -21,6 +21,9 @@ #include #include #include +#if FMT_VERSION >= 100000 +#include +#endif #define DEFAULT_IO_BUFFER_SIZE 1024 diff --git a/cpp/open3d/utility/ParallelScan.h b/cpp/open3d/utility/ParallelScan.h index a98015053f8..0479d611eb5 100644 --- a/cpp/open3d/utility/ParallelScan.h +++ b/cpp/open3d/utility/ParallelScan.h @@ -13,6 +13,14 @@ // clang-format off #if TBB_INTERFACE_VERSION >= 10000 #ifdef OPEN3D_USE_ONEAPI_PACKAGES + #ifdef _PSTL_UDR_PRESENT + #undef _PSTL_UDR_PRESENT + #endif + #define _PSTL_UDR_PRESENT 0 + #ifdef _PSTL_UDS_PRESENT + #undef _PSTL_UDS_PRESENT + #endif + #define _PSTL_UDS_PRESENT 0 #include #include #else diff --git a/cpp/open3d/visualization/rendering/RendererHandle.h b/cpp/open3d/visualization/rendering/RendererHandle.h index 45b59a70c60..dc8b06382e7 100644 --- a/cpp/open3d/visualization/rendering/RendererHandle.h +++ b/cpp/open3d/visualization/rendering/RendererHandle.h @@ -98,7 +98,7 @@ struct REHandle : public REHandle_abstract { id = REHandle_abstract::kBadId + 1; } - return std::move(REHandle(id)); + return REHandle(id); } static REHandle Concretize(const REHandle_abstract& abstract) { diff --git a/cpp/open3d/visualization/rendering/filament/FilamentResourceManager.cpp b/cpp/open3d/visualization/rendering/filament/FilamentResourceManager.cpp index 0c831e9d2d0..1dcf1715368 100644 --- a/cpp/open3d/visualization/rendering/filament/FilamentResourceManager.cpp +++ b/cpp/open3d/visualization/rendering/filament/FilamentResourceManager.cpp @@ -77,8 +77,8 @@ using ResourcesContainer = template std::shared_ptr MakeShared(ResourceType* pointer, filament::Engine& engine) { - return std::move(std::shared_ptr( - pointer, [&engine](ResourceType* p) { engine.destroy(p); })); + return std::shared_ptr( + pointer, [&engine](ResourceType* p) { engine.destroy(p); }); } template diff --git a/cpp/open3d/visualization/webrtc_server/PeerConnectionManager.h b/cpp/open3d/visualization/webrtc_server/PeerConnectionManager.h index ceb0fc9df74..8ef27e79a54 100644 --- a/cpp/open3d/visualization/webrtc_server/PeerConnectionManager.h +++ b/cpp/open3d/visualization/webrtc_server/PeerConnectionManager.h @@ -456,3 +456,45 @@ class PeerConnectionManager { } // namespace webrtc_server } // namespace visualization } // namespace open3d + +namespace fmt { + +template <> +struct formatter { + template + auto format(const webrtc::PeerConnectionInterface::SignalingState& state, + FormatContext& ctx) const -> decltype(ctx.out()) { + using namespace webrtc; + const char* text = nullptr; + switch (state) { + case PeerConnectionInterface::SignalingState::kStable: + text = "kStable"; + break; + case PeerConnectionInterface::SignalingState::kHaveLocalOffer: + text = "kHaveLocalOffer"; + break; + case PeerConnectionInterface::SignalingState::kHaveLocalPrAnswer: + text = "kHaveLocalPrAnswer"; + break; + case PeerConnectionInterface::SignalingState::kHaveRemoteOffer: + text = "kHaveRemoteOffer"; + break; + case PeerConnectionInterface::SignalingState::kHaveRemotePrAnswer: + text = "kHaveRemotePrAnswer"; + break; + case PeerConnectionInterface::SignalingState::kClosed: + text = "kClosed"; + break; + default: + text = "unknown"; + } + return format_to(ctx.out(), "{}", text); + } + + template + constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } +}; + +} // namespace fmt diff --git a/examples/cpp/OnlineSLAMUtil.h b/examples/cpp/OnlineSLAMUtil.h index 585f18a8fbe..358256de3df 100644 --- a/examples/cpp/OnlineSLAMUtil.h +++ b/examples/cpp/OnlineSLAMUtil.h @@ -5,6 +5,10 @@ // SPDX-License-Identifier: MIT // ---------------------------------------------------------------------------- +#if FMT_VERSION >= 100000 +#include +#endif + #include #include #include From f7161f4949d1a9c2b016899d2f8c7bdbfd6bf6f9 Mon Sep 17 00:00:00 2001 From: Nicola Loi Date: Fri, 14 Jun 2024 23:19:14 +0200 Subject: [PATCH 28/82] Fix segfault (lambda reference capture) of VisualizerWithCustomAnimation::Play (#6804) * Make ProgressBar dtor virtual. --------- Co-authored-by: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> --- CHANGELOG.md | 1 + cpp/open3d/utility/ProgressBar.h | 1 + .../visualizer/VisualizerWithCustomAnimation.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 743afd15834..e192ce692e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ - Fix RANSAC early stop if no inliers in a specific iteration (PR #6789) - Fix segmentation fault (infinite recursion) of DetectPlanarPatches if multiple points have same coordinates (PR #6794) - Fix build with fmt v10.2.0 (#6783) +- Fix segmentation fault (lambda reference capture) of VisualizerWithCustomAnimation::Play (PR #6804) ## 0.13 diff --git a/cpp/open3d/utility/ProgressBar.h b/cpp/open3d/utility/ProgressBar.h index dcff07c0d2e..710fa370f0e 100644 --- a/cpp/open3d/utility/ProgressBar.h +++ b/cpp/open3d/utility/ProgressBar.h @@ -23,6 +23,7 @@ class ProgressBar { virtual ProgressBar &operator++(); void SetCurrentCount(size_t n); size_t GetCurrentCount() const; + virtual ~ProgressBar() = default; protected: const size_t resolution_ = 40; diff --git a/cpp/open3d/visualization/visualizer/VisualizerWithCustomAnimation.cpp b/cpp/open3d/visualization/visualizer/VisualizerWithCustomAnimation.cpp index afc39224794..795b3a4a449 100644 --- a/cpp/open3d/visualization/visualizer/VisualizerWithCustomAnimation.cpp +++ b/cpp/open3d/visualization/visualizer/VisualizerWithCustomAnimation.cpp @@ -78,8 +78,8 @@ void VisualizerWithCustomAnimation::Play( is_redraw_required_ = true; UpdateWindowTitle(); recording_file_index_ = 0; - utility::ProgressBar progress_bar(view_control.NumOfFrames(), - "Play animation: "); + auto progress_bar_ptr = std::make_shared( + view_control.NumOfFrames(), "Play animation: "); auto trajectory_ptr = std::make_shared(); bool recording_trajectory = view_control.IsValidPinholeCameraTrajectory(); if (recording) { @@ -94,7 +94,7 @@ void VisualizerWithCustomAnimation::Play( RegisterAnimationCallback([this, recording, recording_depth, close_window_when_animation_ends, recording_trajectory, trajectory_ptr, - &progress_bar](Visualizer *vis) { + progress_bar_ptr](Visualizer *vis) { // The lambda function captures no references to avoid dangling // references auto &view_control = @@ -121,7 +121,7 @@ void VisualizerWithCustomAnimation::Play( } } view_control.Step(1.0); - ++progress_bar; + ++(*progress_bar_ptr); if (view_control.IsPlayingEnd(recording_file_index_)) { view_control.SetAnimationMode( ViewControlWithCustomAnimation::AnimationMode::FreeMode); From ad17a26332d93c0de4bd67218d0134c9deab7b93 Mon Sep 17 00:00:00 2001 From: nsaiapova Date: Sat, 15 Jun 2024 22:11:49 +0200 Subject: [PATCH 29/82] Implement ComputeTriangleAreas, GetNonManifoldEdges and RemoveNonManifoldEdges in t::geometry::TriangleMesh (#6657) Split the logic from ComputeSurfaceArea into a helper static function and introduce a new method which computes triangle areas and writes the resulting tensor into attributes of the mesh. t::geometry::TriangleMesh::GetNonManifoldEdges mimics the logic of the legacy method. t::geometry::TriangleMesh::RemoveNonManifoldEdges follows the logic of the legacy method but there are a few differences: * the main difference is that the outer while-loop is removed. I don't see how after the first iteration any edge can have more than 2 adjacent triangles, which makes the further iterations unnecessary. * I count triangles with non-negative areas immediately and do not rely on the total number of adjacent triangles (which would also include triangles marked for removal). * To choose a triangle with the minimal area out of the existing adjacent triangles I use a heap structure. * Use unordered / sorted comparison for GetNonManifoldEdges() test, return empty areas property if there are no triangles. --------- Co-authored-by: Sameer Sheorey --- cpp/open3d/t/geometry/TriangleMesh.cpp | 213 ++++++++++++++++++-- cpp/open3d/t/geometry/TriangleMesh.h | 19 ++ cpp/pybind/t/geometry/trianglemesh.cpp | 39 +++- cpp/tests/t/geometry/TriangleMesh.cpp | 89 ++++++++ python/test/t/geometry/test_trianglemesh.py | 71 +++++++ 5 files changed, 412 insertions(+), 19 deletions(-) diff --git a/cpp/open3d/t/geometry/TriangleMesh.cpp b/cpp/open3d/t/geometry/TriangleMesh.cpp index 43899aaf58f..4342c0a2d9e 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.cpp +++ b/cpp/open3d/t/geometry/TriangleMesh.cpp @@ -283,6 +283,45 @@ TriangleMesh &TriangleMesh::ComputeVertexNormals(bool normalized) { return *this; } +static core::Tensor ComputeTriangleAreasHelper(const TriangleMesh &mesh) { + const int64_t triangle_num = mesh.GetTriangleIndices().GetLength(); + const core::Dtype dtype = mesh.GetVertexPositions().GetDtype(); + core::Tensor triangle_areas({triangle_num}, dtype, mesh.GetDevice()); + if (mesh.IsCPU()) { + kernel::trianglemesh::ComputeTriangleAreasCPU( + mesh.GetVertexPositions().Contiguous(), + mesh.GetTriangleIndices().Contiguous(), triangle_areas); + } else if (mesh.IsCUDA()) { + CUDA_CALL(kernel::trianglemesh::ComputeTriangleAreasCUDA, + mesh.GetVertexPositions().Contiguous(), + mesh.GetTriangleIndices().Contiguous(), triangle_areas); + } else { + utility::LogError("Unimplemented device"); + } + + return triangle_areas; +} + +TriangleMesh &TriangleMesh::ComputeTriangleAreas() { + if (IsEmpty()) { + utility::LogWarning("TriangleMesh is empty."); + return *this; + } + + if (!HasTriangleIndices()) { + SetTriangleAttr("areas", core::Tensor::Empty( + {0}, GetVertexPositions().GetDtype(), + GetDevice())); + utility::LogWarning("TriangleMesh has no triangle indices."); + return *this; + } + + core::Tensor triangle_areas = ComputeTriangleAreasHelper(*this); + SetTriangleAttr("areas", triangle_areas); + + return *this; +} + double TriangleMesh::GetSurfaceArea() const { double surface_area = 0; if (IsEmpty()) { @@ -295,22 +334,7 @@ double TriangleMesh::GetSurfaceArea() const { return surface_area; } - const int64_t triangle_num = GetTriangleIndices().GetLength(); - const core::Dtype dtype = GetVertexPositions().GetDtype(); - core::Tensor triangle_areas({triangle_num}, dtype, GetDevice()); - - if (IsCPU()) { - kernel::trianglemesh::ComputeTriangleAreasCPU( - GetVertexPositions().Contiguous(), - GetTriangleIndices().Contiguous(), triangle_areas); - } else if (IsCUDA()) { - CUDA_CALL(kernel::trianglemesh::ComputeTriangleAreasCUDA, - GetVertexPositions().Contiguous(), - GetTriangleIndices().Contiguous(), triangle_areas); - } else { - utility::LogError("Unimplemented device"); - } - + core::Tensor triangle_areas = ComputeTriangleAreasHelper(*this); surface_area = triangle_areas.Sum({0}).To(core::Float64).Item(); return surface_area; @@ -1326,6 +1350,163 @@ TriangleMesh TriangleMesh::RemoveUnreferencedVertices() { return *this; } +template ::value && + !std::is_same::value, + T>::type * = nullptr> +using Edge = std::tuple; + +/// brief Helper function to get an edge with ordered vertex indices. +template +static inline Edge GetOrderedEdge(T vidx0, T vidx1) { + return (vidx0 < vidx1) ? Edge{vidx0, vidx1} : Edge{vidx1, vidx0}; +} + +/// brief Helper +/// +template +static std::unordered_map, + std::vector, + utility::hash_tuple>> +GetEdgeToTrianglesMap(const core::Tensor &tris_cpu) { + std::unordered_map, std::vector, + utility::hash_tuple>> + tris_per_edge; + auto AddEdge = [&](T vidx0, T vidx1, int64_t tidx) { + tris_per_edge[GetOrderedEdge(vidx0, vidx1)].push_back(tidx); + }; + const T *tris_ptr = tris_cpu.GetDataPtr(); + for (int64_t tidx = 0; tidx < tris_cpu.GetLength(); ++tidx) { + const T *triangle = &tris_ptr[3 * tidx]; + AddEdge(triangle[0], triangle[1], tidx); + AddEdge(triangle[1], triangle[2], tidx); + AddEdge(triangle[2], triangle[0], tidx); + } + return tris_per_edge; +} + +TriangleMesh TriangleMesh::RemoveNonManifoldEdges() { + if (!HasVertexPositions() || GetVertexPositions().GetLength() == 0) { + utility::LogWarning( + "[RemoveNonManifildEdges] TriangleMesh has no vertices."); + return *this; + } + + if (!HasTriangleIndices() || GetTriangleIndices().GetLength() == 0) { + utility::LogWarning( + "[RemoveNonManifoldEdges] TriangleMesh has no triangles."); + return *this; + } + + GetVertexAttr().AssertSizeSynchronized(); + GetTriangleAttr().AssertSizeSynchronized(); + + core::Tensor tris_cpu = + GetTriangleIndices().To(core::Device()).Contiguous(); + + ComputeTriangleAreas(); + core::Tensor tri_areas_cpu = + GetTriangleAttr("areas").To(core::Device()).Contiguous(); + + DISPATCH_FLOAT_INT_DTYPE_TO_TEMPLATE( + GetVertexPositions().GetDtype(), tris_cpu.GetDtype(), [&]() { + scalar_t *tri_areas_ptr = tri_areas_cpu.GetDataPtr(); + auto edges_to_tris = GetEdgeToTrianglesMap(tris_cpu); + + // lambda to compare triangles areas by index + auto area_greater_compare = [&tri_areas_ptr](size_t lhs, + size_t rhs) { + return tri_areas_ptr[lhs] > tri_areas_ptr[rhs]; + }; + + // go through all edges and for those that have more than 2 + // triangles attached, remove the triangles with the minimal + // area + for (auto &kv : edges_to_tris) { + // remove all triangles which are already marked for removal + // (area < 0) note, the erasing of triangles happens + // afterwards + auto tris_end = std::remove_if( + kv.second.begin(), kv.second.end(), + [=](size_t t) { return tri_areas_ptr[t] < 0; }); + // count non-removed triangles (with area > 0). + int n_tris = std::distance(kv.second.begin(), tris_end); + + if (n_tris <= 2) { + // nothing to do here as either: + // - all triangles of the edge are already marked for + // deletion + // - the edge is manifold: it has 1 or 2 triangles with + // a non-negative area + continue; + } + + // now erase all triangle indices already marked for removal + kv.second.erase(tris_end, kv.second.end()); + + // find first to triangles with the maximal area + std::nth_element(kv.second.begin(), kv.second.begin() + 1, + kv.second.end(), area_greater_compare); + + // mark others for deletion + for (auto it = kv.second.begin() + 2; it < kv.second.end(); + ++it) { + tri_areas_ptr[*it] = -1; + } + } + }); + + // mask for triangles with positive area + core::Tensor tri_mask = tri_areas_cpu.Gt(0.0).To(GetDevice()); + + // pick up positive-area triangles (and their attributes) + for (auto item : GetTriangleAttr()) { + SetTriangleAttr(item.first, item.second.IndexGet({tri_mask})); + } + + return *this; +} + +core::Tensor TriangleMesh::GetNonManifoldEdges( + bool allow_boundary_edges /* = true */) const { + if (!HasVertexPositions()) { + utility::LogWarning( + "[GetNonManifoldEdges] TriangleMesh has no vertices."); + return {}; + } + + if (!HasTriangleIndices()) { + utility::LogWarning( + "[GetNonManifoldEdges] TriangleMesh has no triangles."); + return {}; + } + + core::Tensor result; + core::Tensor tris_cpu = + GetTriangleIndices().To(core::Device()).Contiguous(); + core::Dtype tri_dtype = tris_cpu.GetDtype(); + + DISPATCH_INT_DTYPE_PREFIX_TO_TEMPLATE(tri_dtype, tris, [&]() { + auto edges = GetEdgeToTrianglesMap(tris_cpu); + std::vector non_manifold_edges; + + for (auto &kv : edges) { + if ((allow_boundary_edges && + (kv.second.size() < 1 || kv.second.size() > 2)) || + (!allow_boundary_edges && kv.second.size() != 2)) { + non_manifold_edges.push_back(std::get<0>(kv.first)); + non_manifold_edges.push_back(std::get<1>(kv.first)); + } + } + + result = core::Tensor(non_manifold_edges, + {(long int)non_manifold_edges.size() / 2, 2}, + tri_dtype, GetTriangleIndices().GetDevice()); + }); + + return result; +} + } // namespace geometry } // namespace t } // namespace open3d diff --git a/cpp/open3d/t/geometry/TriangleMesh.h b/cpp/open3d/t/geometry/TriangleMesh.h index fc592b0a9f9..c2916c987ca 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.h +++ b/cpp/open3d/t/geometry/TriangleMesh.h @@ -671,6 +671,11 @@ class TriangleMesh : public Geometry, public DrawableGeometry { /// of the individual triangle surfaces. double GetSurfaceArea() const; + /// \brief Function to compute triangle areas and save it as a triangle + /// attribute "areas". Prints a warning, if mesh is empty or has no + /// triangles. + TriangleMesh &ComputeTriangleAreas(); + /// \brief Clip mesh with a plane. /// This method clips the triangle mesh with the specified plane. /// Parts of the mesh on the positive side of the plane will be kept and @@ -974,6 +979,20 @@ class TriangleMesh : public Geometry, public DrawableGeometry { /// \return The reference to itself. TriangleMesh RemoveUnreferencedVertices(); + /// Removes all non-manifold edges, by successively deleting triangles + /// with the smallest surface area adjacent to the + /// non-manifold edge until the number of adjacent triangles to the edge is + /// `<= 2`. If mesh is empty or has no triangles, prints a warning and + /// returns immediately. \return The reference to itself. + TriangleMesh RemoveNonManifoldEdges(); + + /// Returns the non-manifold edges of the triangle mesh. + /// If \param allow_boundary_edges is set to false, then also boundary + /// edges are returned. + /// \return 2d integer tensor with shape {n,2} encoding ordered edges. + /// If mesh is empty or has no triangles, returns an empty tensor. + core::Tensor GetNonManifoldEdges(bool allow_boundary_edges = true) const; + protected: core::Device device_ = core::Device("CPU:0"); TensorMap vertex_attr_; diff --git a/cpp/pybind/t/geometry/trianglemesh.cpp b/cpp/pybind/t/geometry/trianglemesh.cpp index 96307fddc91..3227b3e63db 100644 --- a/cpp/pybind/t/geometry/trianglemesh.cpp +++ b/cpp/pybind/t/geometry/trianglemesh.cpp @@ -985,9 +985,42 @@ or has a negative value, it is ignored. top_face = box.select_by_index([2, 3, 6, 7]) )"); - triangle_mesh.def("remove_unreferenced_vertices", - &TriangleMesh::RemoveUnreferencedVertices, - "Removes unreferenced vertices from the mesh in-place."); + triangle_mesh.def( + "remove_unreferenced_vertices", + &TriangleMesh::RemoveUnreferencedVertices, + R"(Removes unreferenced vertices from the mesh in-place.)"); + + triangle_mesh.def( + "compute_triangle_areas", &TriangleMesh::ComputeTriangleAreas, + R"(Compute triangle areas and save it as \"areas\" triangle attribute. + +Returns: + The mesh. + +Example: + + This code computes the overall surface area of a box: + + import open3d as o3d + box = o3d.t.geometry.TriangleMesh.create_box() + surface_area = box.compute_triangle_areas().triangle.areas.sum() +)"); + + triangle_mesh.def("remove_non_manifold_edges", + &TriangleMesh::RemoveNonManifoldEdges, + R"(Function that removes all non-manifold edges, by +successively deleting triangles with the smallest surface +area adjacent to the non-manifold edge until the number of +adjacent triangles to the edge is `<= 2`. + +Returns: + The mesh. +)"); + + triangle_mesh.def("get_non_manifold_edges", + &TriangleMesh::GetNonManifoldEdges, + "allow_boundary_edges"_a = true, + R"(Returns the list consisting of non-manifold edges.)"); } } // namespace geometry diff --git a/cpp/tests/t/geometry/TriangleMesh.cpp b/cpp/tests/t/geometry/TriangleMesh.cpp index aa0c75b8b7c..c79ae78748d 100644 --- a/cpp/tests/t/geometry/TriangleMesh.cpp +++ b/cpp/tests/t/geometry/TriangleMesh.cpp @@ -1343,5 +1343,94 @@ TEST_P(TriangleMeshPermuteDevices, RemoveUnreferencedVertices) { EXPECT_TRUE(torus.GetTriangleNormals().AllClose(expected_tri_normals)); } +TEST_P(TriangleMeshPermuteDevices, ComputeTriangleAreas) { + core::Device device = GetParam(); + t::geometry::TriangleMesh mesh_empty; + EXPECT_NO_THROW(mesh_empty.ComputeTriangleAreas()); + + std::shared_ptr mesh = + open3d::geometry::TriangleMesh::CreateSphere(1.0, 3); + t::geometry::TriangleMesh t_mesh = t::geometry::TriangleMesh::FromLegacy( + *mesh, core::Float64, core::Int64, device); + std::vector areas; + mesh->GetSurfaceArea(areas); + t_mesh.ComputeTriangleAreas(); + EXPECT_TRUE(t_mesh.GetTriangleAttr("areas").AllClose( + core::Tensor(areas, {(int)areas.size()}, core::Float64))); +} + +TEST_P(TriangleMeshPermuteDevices, RemoveNonManifoldEdges) { + using ::testing::UnorderedElementsAreArray; + core::Device device = GetParam(); + t::geometry::TriangleMesh mesh_empty; + EXPECT_TRUE(mesh_empty.RemoveNonManifoldEdges().IsEmpty()); + + core::Tensor verts = core::Tensor::Init( + { + {0.0, 0.0, 0.0}, + {1.0, 0.0, 0.0}, + {0.0, 0.0, 1.0}, + {1.0, 0.0, 1.0}, + {0.0, 1.0, 0.0}, + {1.0, 1.0, 0.0}, + {0.0, 1.0, 1.0}, + {1.0, 1.0, 1.0}, + {0.0, -0.2, 0.0}, + }, + device); + + mesh_empty.SetVertexPositions(verts); + EXPECT_TRUE(mesh_empty.GetVertexPositions().AllClose(verts)); + + core::Tensor tris = core::Tensor::Init( + {{4, 7, 5}, {8, 0, 1}, {8, 0, 1}, {8, 0, 1}, {4, 6, 7}, {0, 2, 4}, + {2, 6, 4}, {0, 1, 2}, {1, 3, 2}, {1, 5, 7}, {8, 0, 2}, {8, 0, 2}, + {8, 0, 1}, {1, 7, 3}, {2, 3, 7}, {2, 7, 6}, {8, 0, 2}, {6, 6, 7}, + {0, 4, 1}, {8, 0, 4}, {1, 4, 5}}, + device); + + core::Tensor tri_labels = tris * 10; + + t::geometry::TriangleMesh mesh(verts, tris); + mesh.SetTriangleAttr("labels", tri_labels); + + geometry::TriangleMesh legacy_mesh = mesh.ToLegacy(); + core::Tensor expected_edges = + core::eigen_converter::EigenVector2iVectorToTensor( + legacy_mesh.GetNonManifoldEdges(), core::Int64, device); + EXPECT_TRUE(mesh.GetNonManifoldEdges().AllClose(expected_edges)); + + expected_edges = core::eigen_converter::EigenVector2iVectorToTensor( + legacy_mesh.GetNonManifoldEdges(true), core::Int64, device); + EXPECT_TRUE(mesh.GetNonManifoldEdges(true).AllClose(expected_edges)); + EXPECT_THAT( + core::eigen_converter::TensorToEigenVector2iVector( + mesh.GetNonManifoldEdges(false)), + UnorderedElementsAreArray(std::vector{{0, 8}, + {1, 8}, + {0, 1}, + {6, 7}, + {0, 2}, + {0, 4}, + {6, 6}, + {4, 8}, + {2, 8}})); + + mesh.RemoveNonManifoldEdges(); + + EXPECT_TRUE(mesh.GetNonManifoldEdges(true).AllClose( + core::Tensor({0, 2}, core::Int64))); + + EXPECT_TRUE(mesh.GetNonManifoldEdges(false).AllClose( + core::Tensor({0, 2}, core::Int64))); + + t::geometry::TriangleMesh box = t::geometry::TriangleMesh::CreateBox(); + EXPECT_TRUE(mesh.GetVertexPositions().AllClose(verts)); + EXPECT_TRUE(mesh.GetTriangleIndices().AllClose(box.GetTriangleIndices())); + core::Tensor expected_labels = tri_labels.IndexGet( + {core::Tensor::Init({1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 0, 0, 1, 0, 1})}); + EXPECT_TRUE(mesh.GetTriangleAttr("labels").AllClose(expected_labels)); +} } // namespace tests } // namespace open3d diff --git a/python/test/t/geometry/test_trianglemesh.py b/python/test/t/geometry/test_trianglemesh.py index 0eec5b3a4e5..b583d159acf 100644 --- a/python/test/t/geometry/test_trianglemesh.py +++ b/python/test/t/geometry/test_trianglemesh.py @@ -709,3 +709,74 @@ def check_remove_unreferenced_vertices(device, int_t, float_t): def test_remove_unreferenced_vertices(device, int_t, float_t): check_no_unreferenced_vertices(device, int_t, float_t) check_remove_unreferenced_vertices(device, int_t, float_t) + + +@pytest.mark.parametrize("device", list_devices()) +@pytest.mark.parametrize("int_t", (o3c.int32, o3c.int64)) +@pytest.mark.parametrize("float_t", (o3c.float32, o3c.float64)) +def test_compute_triangle_areas(device, int_t, float_t): + torus = o3d.t.geometry.TriangleMesh.create_torus(2, 1, 6, 3, float_t, int_t, + device) + + expected_areas = o3c.Tensor([ + 2.341874249399399, 1.1709371246996996, 1.299038105676658, + 1.2990381056766576, 1.1709371246996996, 2.3418742493993996, + 2.341874249399399, 1.1709371246996996, 1.299038105676658, + 1.2990381056766573, 1.1709371246996996, 2.341874249399399, + 2.341874249399399, 1.1709371246996998, 1.2990381056766582, + 1.2990381056766576, 1.1709371246996993, 2.3418742493993996, + 2.3418742493993987, 1.1709371246996996, 1.2990381056766578, + 1.299038105676657, 1.1709371246996991, 2.3418742493993987, + 2.3418742493993987, 1.1709371246996996, 1.299038105676658, + 1.2990381056766573, 1.170937124699699, 2.341874249399399, + 2.3418742493994, 1.1709371246997002, 1.299038105676659, + 1.2990381056766582, 1.1709371246997, 2.3418742493994005 + ], float_t, device) + assert torus.compute_triangle_areas().triangle.areas.allclose( + expected_areas) + + +@pytest.mark.parametrize("device", list_devices()) +@pytest.mark.parametrize("int_t", (o3c.int32, o3c.int64)) +@pytest.mark.parametrize("float_t", (o3c.float32, o3c.float64)) +def test_remove_non_manifold_edges(device, int_t, float_t): + verts = o3c.Tensor([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0], + [1.0, 0.0, 1.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0], + [0.0, 1.0, 1.0], [1.0, 1.0, 1.0], [0.0, -0.2, 0.0]], + float_t, device) + + tris = o3c.Tensor( + [[4, 7, 5], [8, 0, 1], [8, 0, 1], [8, 0, 1], [4, 6, 7], [0, 2, 4], + [2, 6, 4], [0, 1, 2], [1, 3, 2], [1, 5, 7], [8, 0, 2], [8, 0, 2], + [8, 0, 1], [1, 7, 3], [2, 3, 7], [2, 7, 6], [8, 0, 2], [6, 6, 7], + [0, 4, 1], [8, 0, 4], [1, 4, 5]], int_t, device) + + test_box = o3d.t.geometry.TriangleMesh(verts, tris) + test_box_legacy = test_box.to_legacy() + + # allow boundary edges + expected_edges = test_box_legacy.get_non_manifold_edges() + np.testing.assert_allclose(test_box.get_non_manifold_edges().numpy(), + np.asarray(expected_edges)) + # disallow boundary edges + # MSVC produces edges in a different order, so compare sorted legacy results + expected_edges = np.array([ + [0, 1], + [0, 2], + [0, 4], + [0, 6], + [1, 7], + [2, 8], + [4, 8], + [6, 8], + [6, 8], + ]) + edges = np.sort(test_box.get_non_manifold_edges(False).numpy(), axis=0) + np.testing.assert_allclose(edges, expected_edges) + + test_box.remove_non_manifold_edges() + + box = o3d.t.geometry.TriangleMesh.create_box(float_dtype=float_t, + int_dtype=int_t) + assert test_box.vertex.positions.allclose(verts) + assert test_box.triangle.indices.allclose(box.triangle.indices) From ef44ea1c2d859f75fe57fdb21dea571032c8147b Mon Sep 17 00:00:00 2001 From: Reini Urban Date: Tue, 18 Jun 2024 19:02:24 +0200 Subject: [PATCH 30/82] cmake: add llvm version deps for c++ (#6832) Co-authored-by: Reini Urban --- 3rdparty/find_dependencies.cmake | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index 45555b83a84..1db3494e3ed 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -1311,6 +1311,10 @@ if(BUILD_GUI) # search path. LLVM version must be >= 7 to compile Filament. if (NOT CLANG_LIBDIR) set(ubuntu_default_llvm_lib_dirs + /usr/lib/llvm-19/lib + /usr/lib/llvm-18/lib + /usr/lib/llvm-17/lib + /usr/lib/llvm-16/lib /usr/lib/llvm-15/lib /usr/lib/llvm-14/lib /usr/lib/llvm-13/lib @@ -1337,6 +1341,10 @@ if(BUILD_GUI) # is not enforced by CMake. if (NOT CLANG_LIBDIR) find_library(CPPABI_LIBRARY c++abi PATH_SUFFIXES + llvm-19/lib + llvm-18/lib + llvm-17/lib + llvm-16/lib llvm-15/lib llvm-14/lib llvm-13/lib From fcf98ee454df1ccb62be01e011449856ccc10c15 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:12:12 -0700 Subject: [PATCH 31/82] Job level token permissions for github actions (#6830) * Run CUDA and ARM CI for every PR change * Remove old release artifacts regularly in a separate workflow for cleaning old releases --- .github/workflows/documentation.yml | 56 +++++++-------------------- .github/workflows/macos.yml | 14 +++++-- .github/workflows/style.yml | 6 +-- .github/workflows/ubuntu-cuda.yml | 11 +++--- .github/workflows/ubuntu-openblas.yml | 11 +++--- .github/workflows/ubuntu-sycl.yml | 6 +-- .github/workflows/ubuntu-wheel.yml | 8 ++-- .github/workflows/ubuntu.yml | 6 +-- .github/workflows/update-release.yml | 38 ++++++++++++++++++ .github/workflows/vtk_packages.yml | 9 ++++- .github/workflows/webrtc.yml | 8 ++-- .github/workflows/windows.yml | 10 +++-- 12 files changed, 108 insertions(+), 75 deletions(-) create mode 100644 .github/workflows/update-release.yml diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 83527c68b56..f6f1dae9f5e 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,7 +1,5 @@ name: Documentation -permissions: - contents: write - actions: write +permissions: {} on: workflow_dispatch: @@ -21,8 +19,9 @@ concurrency: cancel-in-progress: true jobs: - headless-docs: - # Build headless and docs + headless-docs: # Build headless and docs + permissions: + contents: write # Artifact upload and release upload runs-on: ubuntu-latest # Warn about build issues in new versions env: OPEN3D_ML_ROOT: ${{ github.workspace }}/Open3D-ML @@ -78,52 +77,25 @@ jobs: ccache -s source util/ci_utils.sh build_docs "$DEVELOPER_BUILD" + # PWD: Open3D/docs ccache -s + tar_file="open3d-${GITHUB_SHA}-docs.tar.gz" + rm -rf "${GITHUB_WORKSPACE}/${tar_file}" + # Docs in docs/_out/html + tar -C _out -cvzf "${GITHUB_WORKSPACE}/${tar_file}" html - name: Upload docs uses: actions/upload-artifact@v4 with: - name: open3d_docs - path: docs/_out/html + name: open3d-${{ github.sha }}-docs.tar.gz + path: open3d-${{ github.sha }}-docs.tar.gz if-no-files-found: error + compression-level: 0 # no compression - - name: Deploy docs if all artifacts available + - name: Update devel release if: ${{ github.ref == 'refs/heads/main' }} env: GH_TOKEN: ${{ github.token }} run: | - tar_file="open3d-${GITHUB_SHA}-docs.tar.gz" - rm -rf ${tar_file} - # Docs in docs/_out/html - tar -C docs/_out -cvzf ${tar_file} html - - echo "Waiting for other release assets..." - this_sha=$(echo ${GITHUB_SHA} | cut -c 1-6) - n_this_sha_assets=$(gh release view main-devel --json assets --jq ".assets | map(select(.name | contains(\"${this_sha}\"))) | length") - # Total assets from each main branch commmit: - # Python wheels (4x4) + Viewer (3) + C++ libs (4+2+2) = 27, - while ((n_this_sha_assets < 27)); do - sleep 60 - echo -n "." - n_this_sha_assets=$(gh release view main-devel --json assets --jq ".assets | map(select(.name | contains(\"${this_sha}\"))) | length") - done - gh release upload main-devel ${tar_file} --clobber - gh release view main-devel - - echo "\nAll assets ready. Removing release assets except from last 3 commits: ${last_shas[@]}" - release_assets=($(gh release view main-devel --json assets --jq '.assets[] | .name')) - last_shas=($(git log --pretty=format:%h --abbrev-commit -n 3)) - for relass in "${release_assets[@]}"; do - found=false - for last_sha in "${last_shas[@]}"; do - if [[ $relass == *${last_sha}* ]]; then - found=true - fi - done - if [ $found == false ]; then - set -x - gh release delete-asset main-devel $relass - set +x - fi - done + gh release upload main-devel open3d-${{ github.sha }}-docs.tar.gz --clobber gh release view main-devel diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 362cc2327f0..82b6472653d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -1,7 +1,5 @@ name: MacOS -permissions: - contents: write - actions: write +permissions: {} on: workflow_dispatch: @@ -28,6 +26,8 @@ env: jobs: MacOS: + permissions: + contents: write # upload runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -139,6 +139,8 @@ jobs: if-no-files-found: error fuse-viewer: + permissions: + contents: write # Release upload name: Fuse x64 and ARM64 viewer app runs-on: [macos-12] needs: [MacOS] @@ -182,6 +184,8 @@ jobs: build-wheel: name: Build wheel + permissions: + contents: write # upload runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -289,6 +293,8 @@ jobs: fuse-wheel: name: Fuse universal2 wheel + permissions: + contents: write # Release upload runs-on: [macos-12] needs: [build-wheel] strategy: @@ -354,6 +360,8 @@ jobs: test-wheel: name: Test wheel + permissions: + contents: read runs-on: ${{ matrix.os }} needs: [build-wheel] strategy: diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 3c27504fdd9..043cb0c7b97 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -1,7 +1,5 @@ name: Style Check -permissions: - contents: read - actions: write +permissions: {} on: workflow_dispatch: @@ -17,6 +15,8 @@ concurrency: jobs: style-check: + permissions: + contents: read runs-on: ubuntu-latest steps: - name: Checkout source code diff --git a/.github/workflows/ubuntu-cuda.yml b/.github/workflows/ubuntu-cuda.yml index 9cf3cd3e749..0a6a71ed239 100644 --- a/.github/workflows/ubuntu-cuda.yml +++ b/.github/workflows/ubuntu-cuda.yml @@ -1,7 +1,5 @@ name: Ubuntu CUDA -permissions: - contents: write - actions: write +permissions: {} on: workflow_dispatch: @@ -14,8 +12,7 @@ on: branches: - main pull_request: - # Reduce CI frequency for paid CI. - types: [review_requested] + types: [opened, reopened, synchronize] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -30,6 +27,8 @@ jobs: skip-check: runs-on: ubuntu-latest name: Skip job for forks + permissions: + contents: read outputs: skip: ${{ steps.check.outputs.skip }} steps: @@ -46,6 +45,8 @@ jobs: build-and-run-docker: name: Build and run + permissions: + contents: write # upload runs-on: ubuntu-latest needs: [skip-check] if: needs.skip-check.outputs.skip == 'no' diff --git a/.github/workflows/ubuntu-openblas.yml b/.github/workflows/ubuntu-openblas.yml index acfe20e4ddf..a7a42539ed9 100644 --- a/.github/workflows/ubuntu-openblas.yml +++ b/.github/workflows/ubuntu-openblas.yml @@ -1,7 +1,5 @@ name: Ubuntu OpenBLAS -permissions: - contents: read - actions: write +permissions: {} on: workflow_dispatch: @@ -9,8 +7,7 @@ on: branches: - main pull_request: - # Reduce CI frequency for paid CI. - types: [review_requested] + types: [opened, reopened, synchronize] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -22,6 +19,8 @@ env: jobs: openblas-amd64: + permissions: + contents: read runs-on: ubuntu-latest strategy: fail-fast: false @@ -57,6 +56,8 @@ jobs: fi openblas-arm64: + permissions: + contents: read runs-on: ubuntu-latest needs: [skip-arm64-check-on-fork] if: needs.skip-arm64-check-on-fork.outputs.skip == 'no' diff --git a/.github/workflows/ubuntu-sycl.yml b/.github/workflows/ubuntu-sycl.yml index 984d0fe9485..daad144ce08 100644 --- a/.github/workflows/ubuntu-sycl.yml +++ b/.github/workflows/ubuntu-sycl.yml @@ -1,7 +1,5 @@ name: Ubuntu SYCL -permissions: - contents: read - actions: write +permissions: {} on: workflow_dispatch: @@ -21,6 +19,8 @@ env: jobs: ubuntu-sycl: + permissions: + contents: read runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/ubuntu-wheel.yml b/.github/workflows/ubuntu-wheel.yml index 733c52218ee..3b935a75811 100644 --- a/.github/workflows/ubuntu-wheel.yml +++ b/.github/workflows/ubuntu-wheel.yml @@ -1,7 +1,5 @@ name: Ubuntu Wheel -permissions: - contents: write - actions: write +permissions: {} on: workflow_dispatch: @@ -28,6 +26,8 @@ env: jobs: build-wheel: + permissions: + contents: write # Release upload name: Build wheel runs-on: ubuntu-latest strategy: @@ -115,6 +115,8 @@ jobs: test-wheel-cpu: name: Test wheel CPU + permissions: + contents: read runs-on: ubuntu-20.04 needs: [build-wheel] strategy: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 08ef6721fea..a11bfe3a9fb 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -1,7 +1,5 @@ name: Ubuntu -permissions: - contents: write - actions: write +permissions: {} on: workflow_dispatch: @@ -25,6 +23,8 @@ env: jobs: ubuntu: + permissions: + contents: write # Release upload runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/update-release.yml b/.github/workflows/update-release.yml new file mode 100644 index 00000000000..538881bf21e --- /dev/null +++ b/.github/workflows/update-release.yml @@ -0,0 +1,38 @@ +name: Clean release +permissions: {} +on: + workflow_run: # Triggered when long running macos workflow ends + workflows: [macos] + types: [completed] + # branches: [main] + +jobs: + clean-release: + permissions: + contents: write # Release upload + env: + GH_TOKEN: ${{ github.token }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Clean old release assets + run: | + # Total assets from each main branch commmit: + # Python wheels (4x4) + Viewer (3) + C++ libs (4+2+2) = 27, + release_assets=($(gh release view main-devel --json assets --jq '.assets[] | .name')) + last_shas=($(git log --pretty=format:%h --abbrev-commit -n 3)) + echo "Removing release assets except from last 3 commits: ${last_shas[@]}" + for relass in "${release_assets[@]}"; do + found=false + for last_sha in "${last_shas[@]}"; do + if [[ $relass == *${last_sha}* ]]; then + found=true + fi + done + if [ $found == false ]; then + set -x + gh release delete-asset main-devel $relass + set +x + fi + done + gh release view main-devel diff --git a/.github/workflows/vtk_packages.yml b/.github/workflows/vtk_packages.yml index a134daeb71a..25116f9d6ce 100644 --- a/.github/workflows/vtk_packages.yml +++ b/.github/workflows/vtk_packages.yml @@ -1,6 +1,5 @@ name: VTK Packages -permissions: - contents: write +permissions: {} on: # Allows you to run this workflow manually from the Actions tab @@ -9,6 +8,8 @@ on: jobs: Linux: + permissions: + contents: write # TODO: Convert to docker runs-on: ubuntu-18.04 steps: @@ -34,6 +35,8 @@ jobs: if-no-files-found: error Windows: + permissions: + contents: write runs-on: windows-2019 env: SRC_DIR: "D:\\a\\open3d\\open3d" @@ -84,6 +87,8 @@ jobs: if-no-files-found: error MacOS: + permissions: + contents: write runs-on: macos-12 strategy: fail-fast: false diff --git a/.github/workflows/webrtc.yml b/.github/workflows/webrtc.yml index 91a9a5b122d..7689c9d681f 100644 --- a/.github/workflows/webrtc.yml +++ b/.github/workflows/webrtc.yml @@ -1,7 +1,5 @@ name: WebRTC -permissions: - contents: write - actions: write +permissions: {} on: workflow_dispatch: @@ -26,6 +24,8 @@ env: jobs: Unix: + permissions: + contents: write # upload runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -73,6 +73,8 @@ jobs: if-no-files-found: error Windows: + permissions: + contents: write # upload # https://chromium.googlesource.com/chromium/src/+/HEAD/docs/windows_build_instructions.md runs-on: windows-2019 env: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c3c1d1e8395..c5dab02900f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,7 +1,5 @@ name: Windows -permissions: - contents: write - actions: write +permissions: {} on: workflow_dispatch: @@ -34,6 +32,8 @@ env: jobs: windows: + permissions: + contents: write # upload runs-on: windows-2019 strategy: fail-fast: false @@ -234,6 +234,8 @@ jobs: build-wheel: name: Build wheel + permissions: + contents: write # upload runs-on: windows-2019 strategy: fail-fast: false @@ -320,6 +322,8 @@ jobs: test-wheel: name: Test wheel + permissions: + contents: read runs-on: windows-2019 needs: [build-wheel] strategy: From 160209d055214a199719559d7e65fcecd14960ce Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Tue, 9 Jul 2024 00:15:29 +0200 Subject: [PATCH 32/82] add cstdint header to fix missing uint8_t type on ubuntu 24.04 (#6847) --- cpp/tests/test_utility/Raw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/tests/test_utility/Raw.h b/cpp/tests/test_utility/Raw.h index c77ceb71d57..1ee7b1aa7ff 100644 --- a/cpp/tests/test_utility/Raw.h +++ b/cpp/tests/test_utility/Raw.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include From 02c26d981e469728b456da7140ddc30697a8a01c Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Tue, 9 Jul 2024 19:40:35 +0200 Subject: [PATCH 33/82] Update Assimp to fix compilation with gcc 13 (#6846) * Update Assimp to fix compilation with gcc 13 * Assimp v5.24.2 (latest release) cmake 3.22 (required by assimp) * ASSIMP_WARNINGS_AS_ERRORS=OFF --------- Co-authored-by: Sameer Sheorey --- 3rdparty/assimp/assimp.cmake | 5 +++-- CMakeLists.txt | 14 +++----------- docker/docker_build.sh | 22 +++++++++++----------- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/3rdparty/assimp/assimp.cmake b/3rdparty/assimp/assimp.cmake index e6da41c3534..d0da7f6a166 100644 --- a/3rdparty/assimp/assimp.cmake +++ b/3rdparty/assimp/assimp.cmake @@ -17,8 +17,8 @@ endif() ExternalProject_Add( ext_assimp PREFIX assimp - URL https://github.com/assimp/assimp/archive/cfed74516b46a7c2bdf19c1643c448363bd90ad7.tar.gz - URL_HASH SHA256=b2f1c9450609f3bf201aa63b0b16023073d0ebb1c6e9ae5a832441f1e43c634c + URL https://github.com/assimp/assimp/archive/refs/tags/v5.4.2.zip + URL_HASH SHA256=03e38d123f6bf19a48658d197fd09c9a69db88c076b56a476ab2da9f5eb87dcc DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/assimp" UPDATE_COMMAND "" CMAKE_ARGS @@ -32,6 +32,7 @@ ExternalProject_Add( -DASSIMP_BUILD_ZLIB=ON -DASSIMP_NO_EXPORT=OFF -DHUNTER_ENABLED=OFF # Renamed to "ASSIMP_HUNTER_ENABLED" in newer assimp. + -DASSIMP_WARNINGS_AS_ERRORS=OFF -DCMAKE_DEBUG_POSTFIX= BUILD_BYPRODUCTS /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX} diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e5784bb4aa..3e4781e7e72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,8 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.22) # If you're using Ubuntu 18.04, we suggest you install the latest CMake from the # official repository https://apt.kitware.com/. -# -# CMake 3.19+ is required to: -# - allow linking with OBJECT libraries -# - prevent erroneous -gencode option deduplication with CUDA -# - simplify generator expressions for selecting compile flags and setting -# global hardened link flags -# - use first-class language support for ISPC (3.19.2 patch release required) -# -# CMake 3.20+ is required to: -# - detect IntelLLVM compiler for SYCL +# CMake 3.22+ is required by Assimp v5.4.2 +# CMake 3.20+ is required to detect IntelLLVM compiler for SYCL # CMAKE_HOST_SYSTEM_PROCESSOR is only available after calling project(), # which depends on ${OPEN3D_VERSION}, which depends on ${DEVELOPER_BUILD}. diff --git a/docker/docker_build.sh b/docker/docker_build.sh index f55276baed5..2f767884f1a 100755 --- a/docker/docker_build.sh +++ b/docker/docker_build.sh @@ -76,8 +76,8 @@ HOST_OPEN3D_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. >/dev/null 2>&1 && pw # Shared variables CCACHE_VERSION=4.3 -CMAKE_VERSION=cmake-3.20.6-linux-x86_64 -CMAKE_VERSION_AARCH64=cmake-3.20.6-linux-aarch64 +CMAKE_VERSION=cmake-3.22.5-linux-x86_64 +CMAKE_VERSION_AARCH64=cmake-3.22.5-linux-aarch64 CUDA_VERSION=11.7.1-cudnn8 print_usage_and_exit_docker_build() { @@ -284,7 +284,7 @@ ci_build() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON - # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch + # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch export BUILD_TENSORFLOW_OPS=OFF export BUILD_PYTORCH_OPS=ON export PACKAGE=ON @@ -300,7 +300,7 @@ ci_build() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON - # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch + # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch export BUILD_TENSORFLOW_OPS=OFF export BUILD_PYTORCH_OPS=ON export PACKAGE=ON @@ -316,7 +316,7 @@ ci_build() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON - # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi + # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi export BUILD_TENSORFLOW_OPS=ON export BUILD_PYTORCH_OPS=OFF export PACKAGE=ON @@ -332,7 +332,7 @@ ci_build() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON - # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi + # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi export BUILD_TENSORFLOW_OPS=ON export BUILD_PYTORCH_OPS=OFF export PACKAGE=ON @@ -348,7 +348,7 @@ ci_build() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=OFF export BUILD_CUDA_MODULE=ON - # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch + # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch export BUILD_TENSORFLOW_OPS=OFF export BUILD_PYTORCH_OPS=ON export PACKAGE=OFF @@ -379,7 +379,7 @@ cpu-shared_export_env() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=OFF - # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi + # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi export BUILD_TENSORFLOW_OPS=ON export BUILD_PYTORCH_OPS=OFF export PACKAGE=ON @@ -395,7 +395,7 @@ cpu-shared-ml_export_env() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=OFF - # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch + # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch export BUILD_TENSORFLOW_OPS=OFF export BUILD_PYTORCH_OPS=ON export PACKAGE=ON @@ -411,7 +411,7 @@ cpu-shared-release_export_env() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=OFF - # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi + # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi export BUILD_TENSORFLOW_OPS=ON export BUILD_PYTORCH_OPS=OFF export PACKAGE=ON @@ -427,7 +427,7 @@ cpu-shared-ml-release_export_env() { export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=OFF - # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch + # TODO: re-enable tensorflow support, off due to due to cxx11_abi issue with PyTorch export BUILD_TENSORFLOW_OPS=OFF export BUILD_PYTORCH_OPS=ON export PACKAGE=ON From 8b25949d18e0c5be97a8b1296d6f09b281306601 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:43:24 -0700 Subject: [PATCH 34/82] Fix triangle area and non-manifold edge tests for CUDA (#6859) Fix python test, fix mkl download URL typo, restrict numpy to v1.x --- 3rdparty/mkl/mkl.cmake | 3 +- cpp/tests/t/geometry/TriangleMesh.cpp | 33 +- python/requirements.txt | 2 +- python/test/t/geometry/test_trianglemesh.py | 993 ++++++++++++++++---- 4 files changed, 819 insertions(+), 212 deletions(-) diff --git a/3rdparty/mkl/mkl.cmake b/3rdparty/mkl/mkl.cmake index 3341a2b9d40..6a8a923717b 100644 --- a/3rdparty/mkl/mkl.cmake +++ b/3rdparty/mkl/mkl.cmake @@ -7,12 +7,13 @@ # # The name "STATIC" is used to avoid naming collisions for other 3rdparty CMake # files (e.g. PyTorch) that also depends on MKL. +# FIXME: anaconda.org URLs don't work anymore. include(ExternalProject) if(WIN32) set(MKL_INCLUDE_URL - https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-include-2020.1-intel_216-win-64.tar.bz2W + https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-include-2020.1-intel_216-win-64.tar.bz2 https://anaconda.org/intel/mkl-include/2020.1/download/win-64/mkl-include-2020.1-intel_216.tar.bz2 ) set(MKL_INCLUDE_SHA256 65cedb770358721fd834224cd8be1fe1cc10b37ef2a1efcc899fc2fefbeb5b31) diff --git a/cpp/tests/t/geometry/TriangleMesh.cpp b/cpp/tests/t/geometry/TriangleMesh.cpp index c79ae78748d..f4af2974f20 100644 --- a/cpp/tests/t/geometry/TriangleMesh.cpp +++ b/cpp/tests/t/geometry/TriangleMesh.cpp @@ -1348,21 +1348,26 @@ TEST_P(TriangleMeshPermuteDevices, ComputeTriangleAreas) { t::geometry::TriangleMesh mesh_empty; EXPECT_NO_THROW(mesh_empty.ComputeTriangleAreas()); - std::shared_ptr mesh = - open3d::geometry::TriangleMesh::CreateSphere(1.0, 3); - t::geometry::TriangleMesh t_mesh = t::geometry::TriangleMesh::FromLegacy( - *mesh, core::Float64, core::Int64, device); - std::vector areas; - mesh->GetSurfaceArea(areas); + t::geometry::TriangleMesh t_mesh = + t::geometry::TriangleMesh::CreateSphere(1.0, 3).To(device); + core::Tensor ref_areas = core::Tensor::Init( + {0.39031237489989984, 0.39031237489989995, 0.39031237489989973, + 0.39031237489989995, 0.39031237489989984, 0.3903123748999, + 0.3903123748998997, 0.3903123748998999, 0.39031237489989973, + 0.39031237489989995, 0.3903123748999, 0.3903123748999002, + 0.4330127018922192, 0.43301270189221924, 0.43301270189221924, + 0.43301270189221924, 0.43301270189221924, 0.4330127018922193, + 0.4330127018922191, 0.43301270189221913, 0.4330127018922192, + 0.43301270189221924, 0.4330127018922195, 0.43301270189221963}, + device); t_mesh.ComputeTriangleAreas(); - EXPECT_TRUE(t_mesh.GetTriangleAttr("areas").AllClose( - core::Tensor(areas, {(int)areas.size()}, core::Float64))); + EXPECT_TRUE(t_mesh.GetTriangleAttr("areas").AllClose(ref_areas)); } TEST_P(TriangleMeshPermuteDevices, RemoveNonManifoldEdges) { using ::testing::UnorderedElementsAreArray; core::Device device = GetParam(); - t::geometry::TriangleMesh mesh_empty; + t::geometry::TriangleMesh mesh_empty(device); EXPECT_TRUE(mesh_empty.RemoveNonManifoldEdges().IsEmpty()); core::Tensor verts = core::Tensor::Init( @@ -1419,17 +1424,19 @@ TEST_P(TriangleMeshPermuteDevices, RemoveNonManifoldEdges) { mesh.RemoveNonManifoldEdges(); EXPECT_TRUE(mesh.GetNonManifoldEdges(true).AllClose( - core::Tensor({0, 2}, core::Int64))); + core::Tensor({0, 2}, core::Int64, device))); EXPECT_TRUE(mesh.GetNonManifoldEdges(false).AllClose( - core::Tensor({0, 2}, core::Int64))); + core::Tensor({0, 2}, core::Int64, device))); - t::geometry::TriangleMesh box = t::geometry::TriangleMesh::CreateBox(); + t::geometry::TriangleMesh box = + t::geometry::TriangleMesh::CreateBox().To(device); EXPECT_TRUE(mesh.GetVertexPositions().AllClose(verts)); EXPECT_TRUE(mesh.GetTriangleIndices().AllClose(box.GetTriangleIndices())); core::Tensor expected_labels = tri_labels.IndexGet( {core::Tensor::Init({1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, 1, 0, 1})}); + 0, 0, 1, 1, 1, 0, 0, 1, 0, 1}, + device)}); EXPECT_TRUE(mesh.GetTriangleAttr("labels").AllClose(expected_labels)); } } // namespace tests diff --git a/python/requirements.txt b/python/requirements.txt index 8677bc49f79..148650326df 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,4 +1,4 @@ -numpy>=1.18.0 +numpy>=1.18.0,<2.0.0 dash>=2.6.0 werkzeug>=2.2.3 nbformat>=5.7.0 diff --git a/python/test/t/geometry/test_trianglemesh.py b/python/test/t/geometry/test_trianglemesh.py index b583d159acf..679c65f3582 100644 --- a/python/test/t/geometry/test_trianglemesh.py +++ b/python/test/t/geometry/test_trianglemesh.py @@ -40,14 +40,38 @@ def test_create_box(device): box_default = o3d.t.geometry.TriangleMesh.create_box(device=device) vertex_positions_default = o3c.Tensor( - [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], - [0.0, 1.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 1.0], [1.0, 1.0, 1.0]], - o3c.float32, device) + [ + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [1.0, 0.0, 1.0], + [0.0, 1.0, 0.0], + [1.0, 1.0, 0.0], + [0.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + ], + o3c.float32, + device, + ) triangle_indices_default = o3c.Tensor( - [[4, 7, 5], [4, 6, 7], [0, 2, 4], [2, 6, 4], [0, 1, 2], [1, 3, 2], - [1, 5, 7], [1, 7, 3], [2, 3, 7], [2, 7, 6], [0, 4, 1], [1, 4, 5]], - o3c.int64, device) + [ + [4, 7, 5], + [4, 6, 7], + [0, 2, 4], + [2, 6, 4], + [0, 1, 2], + [1, 3, 2], + [1, 5, 7], + [1, 7, 3], + [2, 3, 7], + [2, 7, 6], + [0, 4, 1], + [1, 4, 5], + ], + o3c.int64, + device, + ) assert box_default.vertex.positions.allclose(vertex_positions_default) assert box_default.triangle.indices.allclose(triangle_indices_default) @@ -57,14 +81,38 @@ def test_create_box(device): o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[0.0, 0.0, 0.0], [2.0, 0.0, 0.0], [0.0, 0.0, 4.0], [2.0, 0.0, 4.0], - [0.0, 3.0, 0.0], [2.0, 3.0, 0.0], [0.0, 3.0, 4.0], [2.0, 3.0, 4.0]], - o3c.float64, device) + [ + [0.0, 0.0, 0.0], + [2.0, 0.0, 0.0], + [0.0, 0.0, 4.0], + [2.0, 0.0, 4.0], + [0.0, 3.0, 0.0], + [2.0, 3.0, 0.0], + [0.0, 3.0, 4.0], + [2.0, 3.0, 4.0], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[4, 7, 5], [4, 6, 7], [0, 2, 4], [2, 6, 4], [0, 1, 2], [1, 3, 2], - [1, 5, 7], [1, 7, 3], [2, 3, 7], [2, 7, 6], [0, 4, 1], [1, 4, 5]], - o3c.int32, device) + [ + [4, 7, 5], + [4, 6, 7], + [0, 2, 4], + [2, 6, 4], + [0, 1, 2], + [1, 3, 2], + [1, 5, 7], + [1, 7, 3], + [2, 3, 7], + [2, 7, 6], + [0, 4, 1], + [1, 4, 5], + ], + o3c.int32, + device, + ) assert box_custom.vertex.positions.allclose(vertex_positions_custom) assert box_custom.triangle.indices.allclose(triangle_indices_custom) @@ -77,19 +125,56 @@ def test_create_sphere(device): 1, 3, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[0.0, 0.0, 1.0], [0.0, 0.0, -1.0], [0.866025, 0, 0.5], - [0.433013, 0.75, 0.5], [-0.433013, 0.75, 0.5], [-0.866025, 0.0, 0.5], - [-0.433013, -0.75, 0.5], [0.433013, -0.75, 0.5], [0.866025, 0.0, -0.5], - [0.433013, 0.75, -0.5], [-0.433013, 0.75, -0.5], - [-0.866025, 0.0, -0.5], [-0.433013, -0.75, -0.5], - [0.433013, -0.75, -0.5]], o3c.float64, device) + [ + [0.0, 0.0, 1.0], + [0.0, 0.0, -1.0], + [0.866025, 0, 0.5], + [0.433013, 0.75, 0.5], + [-0.433013, 0.75, 0.5], + [-0.866025, 0.0, 0.5], + [-0.433013, -0.75, 0.5], + [0.433013, -0.75, 0.5], + [0.866025, 0.0, -0.5], + [0.433013, 0.75, -0.5], + [-0.433013, 0.75, -0.5], + [-0.866025, 0.0, -0.5], + [-0.433013, -0.75, -0.5], + [0.433013, -0.75, -0.5], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[0, 2, 3], [1, 9, 8], [0, 3, 4], [1, 10, 9], [0, 4, 5], [1, 11, 10], - [0, 5, 6], [1, 12, 11], [0, 6, 7], [1, 13, 12], [0, 7, 2], [1, 8, 13], - [8, 3, 2], [8, 9, 3], [9, 4, 3], [9, 10, 4], [10, 5, 4], [10, 11, 5], - [11, 6, 5], [11, 12, 6], [12, 7, 6], [12, 13, 7], [13, 2, 7], - [13, 8, 2]], o3c.int32, device) + [ + [0, 2, 3], + [1, 9, 8], + [0, 3, 4], + [1, 10, 9], + [0, 4, 5], + [1, 11, 10], + [0, 5, 6], + [1, 12, 11], + [0, 6, 7], + [1, 13, 12], + [0, 7, 2], + [1, 8, 13], + [8, 3, 2], + [8, 9, 3], + [9, 4, 3], + [9, 10, 4], + [10, 5, 4], + [10, 11, 5], + [11, 6, 5], + [11, 12, 6], + [12, 7, 6], + [12, 13, 7], + [13, 2, 7], + [13, 8, 2], + ], + o3c.int32, + device, + ) assert sphere_custom.vertex.positions.allclose(vertex_positions_custom) assert sphere_custom.triangle.indices.allclose(triangle_indices_custom) @@ -102,8 +187,15 @@ def test_create_tetrahedron(device): 2, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[1.88562, 0.0, -0.666667], [-0.942809, 1.63299, -0.666667], - [-0.942809, -1.63299, -0.666667], [0.0, 0.0, 2]], o3c.float64, device) + [ + [1.88562, 0.0, -0.666667], + [-0.942809, 1.63299, -0.666667], + [-0.942809, -1.63299, -0.666667], + [0.0, 0.0, 2], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( [[0, 2, 1], [0, 3, 2], [0, 1, 3], [1, 2, 3]], o3c.int32, device) @@ -119,12 +211,32 @@ def test_create_octahedron(device): 2, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[2.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 2.0], [-2.0, 0.0, 0.0], - [0.0, -2.0, 0.0], [0.0, 0.0, -2.0]], o3c.float64, device) + [ + [2.0, 0.0, 0.0], + [0.0, 2.0, 0.0], + [0.0, 0.0, 2.0], + [-2.0, 0.0, 0.0], + [0.0, -2.0, 0.0], + [0.0, 0.0, -2.0], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[0, 1, 2], [1, 3, 2], [3, 4, 2], [4, 0, 2], [0, 5, 1], [1, 5, 3], - [3, 5, 4], [4, 5, 0]], o3c.int32, device) + [ + [0, 1, 2], + [1, 3, 2], + [3, 4, 2], + [4, 0, 2], + [0, 5, 1], + [1, 5, 3], + [3, 5, 4], + [4, 5, 0], + ], + o3c.int32, + device, + ) assert octahedron_custom.vertex.positions.allclose(vertex_positions_custom) assert octahedron_custom.triangle.indices.allclose(triangle_indices_custom) @@ -137,17 +249,50 @@ def test_create_icosahedron(device): 2, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[-2.0, 0.0, 3.23607], [2.0, 0.0, 3.23607], [2.0, 0.0, -3.23607], - [-2.0, 0.0, -3.23607], [0.0, -3.23607, 2.0], [0.0, 3.23607, 2.0], - [0.0, 3.23607, -2.0], [0.0, -3.23607, -2.0], [-3.23607, -2.0, 0.0], - [3.23607, -2.0, 0.0], [3.23607, 2.0, 0.0], [-3.23607, 2.0, 0.0]], - o3c.float64, device) + [ + [-2.0, 0.0, 3.23607], + [2.0, 0.0, 3.23607], + [2.0, 0.0, -3.23607], + [-2.0, 0.0, -3.23607], + [0.0, -3.23607, 2.0], + [0.0, 3.23607, 2.0], + [0.0, 3.23607, -2.0], + [0.0, -3.23607, -2.0], + [-3.23607, -2.0, 0.0], + [3.23607, -2.0, 0.0], + [3.23607, 2.0, 0.0], + [-3.23607, 2.0, 0.0], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[0, 4, 1], [0, 1, 5], [1, 4, 9], [1, 9, 10], [1, 10, 5], [0, 8, 4], - [0, 11, 8], [0, 5, 11], [5, 6, 11], [5, 10, 6], [4, 8, 7], [4, 7, 9], - [3, 6, 2], [3, 2, 7], [2, 6, 10], [2, 10, 9], [2, 9, 7], [3, 11, 6], - [3, 8, 11], [3, 7, 8]], o3c.int32, device) + [ + [0, 4, 1], + [0, 1, 5], + [1, 4, 9], + [1, 9, 10], + [1, 10, 5], + [0, 8, 4], + [0, 11, 8], + [0, 5, 11], + [5, 6, 11], + [5, 10, 6], + [4, 8, 7], + [4, 7, 9], + [3, 6, 2], + [3, 2, 7], + [2, 6, 10], + [2, 10, 9], + [2, 9, 7], + [3, 11, 6], + [3, 8, 11], + [3, 7, 8], + ], + o3c.int32, + device, + ) assert icosahedron_custom.vertex.positions.allclose(vertex_positions_custom) assert icosahedron_custom.triangle.indices.allclose(triangle_indices_custom) @@ -160,19 +305,56 @@ def test_create_cylinder(device): 1, 2, 3, 3, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[0.0, 0.0, 1.0], [0.0, 0.0, -1.0], [1.0, 0.0, 1.0], - [-0.5, 0.866025, 1.0], [-0.5, -0.866025, 1.0], [1.0, 0.0, 0.333333], - [-0.5, 0.866025, 0.333333], [-0.5, -0.866025, 0.333333], - [1.0, 0.0, -0.333333], [-0.5, 0.866025, -0.333333], - [-0.5, -0.866025, -0.333333], [1.0, 0.0, -1.0], [-0.5, 0.866025, -1.0], - [-0.5, -0.866025, -1.0]], o3c.float64, device) + [ + [0.0, 0.0, 1.0], + [0.0, 0.0, -1.0], + [1.0, 0.0, 1.0], + [-0.5, 0.866025, 1.0], + [-0.5, -0.866025, 1.0], + [1.0, 0.0, 0.333333], + [-0.5, 0.866025, 0.333333], + [-0.5, -0.866025, 0.333333], + [1.0, 0.0, -0.333333], + [-0.5, 0.866025, -0.333333], + [-0.5, -0.866025, -0.333333], + [1.0, 0.0, -1.0], + [-0.5, 0.866025, -1.0], + [-0.5, -0.866025, -1.0], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[0, 2, 3], [1, 12, 11], [0, 3, 4], [1, 13, 12], [0, 4, 2], [1, 11, 13], - [5, 3, 2], [5, 6, 3], [6, 4, 3], [6, 7, 4], [7, 2, 4], [7, 5, 2], - [8, 6, 5], [8, 9, 6], [9, 7, 6], [9, 10, 7], [10, 5, 7], [10, 8, 5], - [11, 9, 8], [11, 12, 9], [12, 10, 9], [12, 13, 10], [13, 8, 10], - [13, 11, 8]], o3c.int32, device) + [ + [0, 2, 3], + [1, 12, 11], + [0, 3, 4], + [1, 13, 12], + [0, 4, 2], + [1, 11, 13], + [5, 3, 2], + [5, 6, 3], + [6, 4, 3], + [6, 7, 4], + [7, 2, 4], + [7, 5, 2], + [8, 6, 5], + [8, 9, 6], + [9, 7, 6], + [9, 10, 7], + [10, 5, 7], + [10, 8, 5], + [11, 9, 8], + [11, 12, 9], + [12, 10, 9], + [12, 13, 10], + [13, 8, 10], + [13, 11, 8], + ], + o3c.int32, + device, + ) assert cylinder_custom.vertex.positions.allclose(vertex_positions_custom) assert cylinder_custom.triangle.indices.allclose(triangle_indices_custom) @@ -185,14 +367,38 @@ def test_create_cone(device): 2, 4, 3, 2, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[0.0, 0.0, 0.0], [0.0, 0.0, 4.0], [2.0, 0.0, 0.0], - [-1.0, 1.73205, 0.0], [-1.0, -1.73205, 0.0], [1.0, 0.0, 2.0], - [-0.5, 0.866025, 2], [-0.5, -0.866025, 2]], o3c.float64, device) + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 4.0], + [2.0, 0.0, 0.0], + [-1.0, 1.73205, 0.0], + [-1.0, -1.73205, 0.0], + [1.0, 0.0, 2.0], + [-0.5, 0.866025, 2], + [-0.5, -0.866025, 2], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[0, 3, 2], [1, 5, 6], [0, 4, 3], [1, 6, 7], [0, 2, 4], [1, 7, 5], - [6, 2, 3], [6, 5, 2], [7, 3, 4], [7, 6, 3], [5, 4, 2], [5, 7, 4]], - o3c.int32, device) + [ + [0, 3, 2], + [1, 5, 6], + [0, 4, 3], + [1, 6, 7], + [0, 2, 4], + [1, 7, 5], + [6, 2, 3], + [6, 5, 2], + [7, 3, 4], + [7, 6, 3], + [5, 4, 2], + [5, 7, 4], + ], + o3c.int32, + device, + ) assert cone_custom.vertex.positions.allclose(vertex_positions_custom) assert cone_custom.triangle.indices.allclose(triangle_indices_custom) @@ -205,24 +411,72 @@ def test_create_torus(device): 2, 1, 6, 3, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[3.0, 0.0, 0.0], [1.5, 0.0, 0.866025], [1.5, 0.0, -0.866025], - [1.5, 2.59808, 0.0], [0.75, 1.29904, 0.866025], - [0.75, 1.29904, -0.866025], [-1.5, 2.59808, 0], - [-0.75, 1.29904, 0.866025], [-0.75, 1.29904, -0.866025], - [-3.0, 0.0, 0.0], [-1.5, 0.0, 0.866025], [-1.5, 0.0, -0.866025], - [-1.5, -2.59808, 0.0], [-0.75, -1.29904, 0.866025], - [-0.75, -1.29904, -0.866025], [1.5, -2.59808, 0.0], - [0.75, -1.29904, 0.866025], [0.75, -1.29904, -0.866025]], o3c.float64, - device) + [ + [3.0, 0.0, 0.0], + [1.5, 0.0, 0.866025], + [1.5, 0.0, -0.866025], + [1.5, 2.59808, 0.0], + [0.75, 1.29904, 0.866025], + [0.75, 1.29904, -0.866025], + [-1.5, 2.59808, 0], + [-0.75, 1.29904, 0.866025], + [-0.75, 1.29904, -0.866025], + [-3.0, 0.0, 0.0], + [-1.5, 0.0, 0.866025], + [-1.5, 0.0, -0.866025], + [-1.5, -2.59808, 0.0], + [-0.75, -1.29904, 0.866025], + [-0.75, -1.29904, -0.866025], + [1.5, -2.59808, 0.0], + [0.75, -1.29904, 0.866025], + [0.75, -1.29904, -0.866025], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[3, 4, 0], [0, 4, 1], [4, 5, 1], [1, 5, 2], [5, 3, 2], [2, 3, 0], - [6, 7, 3], [3, 7, 4], [7, 8, 4], [4, 8, 5], [8, 6, 5], [5, 6, 3], - [9, 10, 6], [6, 10, 7], [10, 11, 7], [7, 11, 8], [11, 9, 8], [8, 9, 6], - [12, 13, 9], [9, 13, 10], [13, 14, 10], [10, 14, 11], [14, 12, 11], - [11, 12, 9], [15, 16, 12], [12, 16, 13], [16, 17, 13], [13, 17, 14], - [17, 15, 14], [14, 15, 12], [0, 1, 15], [15, 1, 16], [1, 2, 16], - [16, 2, 17], [2, 0, 17], [17, 0, 15]], o3c.int32, device) + [ + [3, 4, 0], + [0, 4, 1], + [4, 5, 1], + [1, 5, 2], + [5, 3, 2], + [2, 3, 0], + [6, 7, 3], + [3, 7, 4], + [7, 8, 4], + [4, 8, 5], + [8, 6, 5], + [5, 6, 3], + [9, 10, 6], + [6, 10, 7], + [10, 11, 7], + [7, 11, 8], + [11, 9, 8], + [8, 9, 6], + [12, 13, 9], + [9, 13, 10], + [13, 14, 10], + [10, 14, 11], + [14, 12, 11], + [11, 12, 9], + [15, 16, 12], + [12, 16, 13], + [16, 17, 13], + [13, 17, 14], + [17, 15, 14], + [14, 15, 12], + [0, 1, 15], + [15, 1, 16], + [1, 2, 16], + [16, 2, 17], + [2, 0, 17], + [17, 0, 15], + ], + o3c.int32, + device, + ) assert torus_custom.vertex.positions.allclose(vertex_positions_custom) assert torus_custom.triangle.indices.allclose(triangle_indices_custom) @@ -235,18 +489,58 @@ def test_create_arrow(device): 1, 2, 4, 2, 4, 1, 1, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[0.0, 0.0, 4.0], [0.0, 0.0, 0.0], [1.0, 0.0, 4.0], [0.0, 1.0, 4.0], - [-1.0, 0.0, 4.0], [0.0, -1.0, 4.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], - [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 4.0], [0.0, 0.0, 6.0], - [2.0, 0.0, 4.0], [0.0, 2.0, 4.0], [-2.0, 0.0, 4.0], [0.0, -2.0, 4.0]], - o3c.float64, device) + [ + [0.0, 0.0, 4.0], + [0.0, 0.0, 0.0], + [1.0, 0.0, 4.0], + [0.0, 1.0, 4.0], + [-1.0, 0.0, 4.0], + [0.0, -1.0, 4.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [-1.0, 0.0, 0.0], + [0.0, -1.0, 0.0], + [0.0, 0.0, 4.0], + [0.0, 0.0, 6.0], + [2.0, 0.0, 4.0], + [0.0, 2.0, 4.0], + [-2.0, 0.0, 4.0], + [0.0, -2.0, 4.0], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[0, 2, 3], [1, 7, 6], [0, 3, 4], [1, 8, 7], [0, 4, 5], [1, 9, 8], - [0, 5, 2], [1, 6, 9], [6, 3, 2], [6, 7, 3], [7, 4, 3], [7, 8, 4], - [8, 5, 4], [8, 9, 5], [9, 2, 5], [9, 6, 2], [10, 13, 12], [11, 12, 13], - [10, 14, 13], [11, 13, 14], [10, 15, 14], [11, 14, 15], [10, 12, 15], - [11, 15, 12]], o3c.int32, device) + [ + [0, 2, 3], + [1, 7, 6], + [0, 3, 4], + [1, 8, 7], + [0, 4, 5], + [1, 9, 8], + [0, 5, 2], + [1, 6, 9], + [6, 3, 2], + [6, 7, 3], + [7, 4, 3], + [7, 8, 4], + [8, 5, 4], + [8, 9, 5], + [9, 2, 5], + [9, 6, 2], + [10, 13, 12], + [11, 12, 13], + [10, 14, 13], + [11, 13, 14], + [10, 15, 14], + [11, 14, 15], + [10, 12, 15], + [11, 15, 12], + ], + o3c.int32, + device, + ) assert arrow_custom.vertex.positions.allclose(vertex_positions_custom) assert arrow_custom.triangle.indices.allclose(triangle_indices_custom) @@ -259,30 +553,65 @@ def test_create_mobius(device): 10, 2, 1, 1, 1, 1, 1, o3c.float64, o3c.int32, device) vertex_positions_custom = o3c.Tensor( - [[0.5, 0.0, 0.0], [1.5, 0.0, 0.0], [0.424307, 0.308277, -0.154508], - [1.19373, 0.867294, 0.154508], [0.184017, 0.566346, -0.293893], - [0.434017, 1.33577, 0.293893], [-0.218199, 0.671548, -0.404508], - [-0.399835, 1.23057, 0.404508], [-0.684017, 0.496967, -0.475528], - [-0.934017, 0.678603, 0.475528], [-1.0, 0.0, -0.5], [-1.0, 0.0, 0.5], - [-0.934017, -0.678603, -0.475528], [-0.684017, -0.496967, 0.475528], - [-0.399835, -1.23057, -0.404508], [-0.218199, -0.671548, 0.404508], - [0.434017, -1.33577, -0.293893], [0.184017, -0.566346, 0.293893], - [1.19373, -0.867294, -0.154508], [0.424307, -0.308277, 0.154508]], - o3c.float64, device) + [ + [0.5, 0.0, 0.0], + [1.5, 0.0, 0.0], + [0.424307, 0.308277, -0.154508], + [1.19373, 0.867294, 0.154508], + [0.184017, 0.566346, -0.293893], + [0.434017, 1.33577, 0.293893], + [-0.218199, 0.671548, -0.404508], + [-0.399835, 1.23057, 0.404508], + [-0.684017, 0.496967, -0.475528], + [-0.934017, 0.678603, 0.475528], + [-1.0, 0.0, -0.5], + [-1.0, 0.0, 0.5], + [-0.934017, -0.678603, -0.475528], + [-0.684017, -0.496967, 0.475528], + [-0.399835, -1.23057, -0.404508], + [-0.218199, -0.671548, 0.404508], + [0.434017, -1.33577, -0.293893], + [0.184017, -0.566346, 0.293893], + [1.19373, -0.867294, -0.154508], + [0.424307, -0.308277, 0.154508], + ], + o3c.float64, + device, + ) triangle_indices_custom = o3c.Tensor( - [[0, 3, 1], [0, 2, 3], [3, 2, 4], [3, 4, 5], [4, 7, 5], [4, 6, 7], - [7, 6, 8], [7, 8, 9], [8, 11, 9], [8, 10, 11], [11, 10, 12], - [11, 12, 13], [12, 15, 13], [12, 14, 15], [15, 14, 16], [15, 16, 17], - [16, 19, 17], [16, 18, 19], [18, 19, 1], [1, 19, 0]], o3c.int32, - device) + [ + [0, 3, 1], + [0, 2, 3], + [3, 2, 4], + [3, 4, 5], + [4, 7, 5], + [4, 6, 7], + [7, 6, 8], + [7, 8, 9], + [8, 11, 9], + [8, 10, 11], + [11, 10, 12], + [11, 12, 13], + [12, 15, 13], + [12, 14, 15], + [15, 14, 16], + [15, 16, 17], + [16, 19, 17], + [16, 18, 19], + [18, 19, 1], + [1, 19, 0], + ], + o3c.int32, + device, + ) assert mobius_custom.vertex.positions.allclose(vertex_positions_custom) assert mobius_custom.triangle.indices.allclose(triangle_indices_custom) def test_create_text(): - mesh = o3d.t.geometry.TriangleMesh.create_text('Open3D', depth=1) + mesh = o3d.t.geometry.TriangleMesh.create_text("Open3D", depth=1) assert mesh.vertex.positions.shape == (624, 3) assert mesh.triangle.indices.shape == (936, 3) @@ -334,44 +663,117 @@ def test_hole_filling(): def test_uvatlas(): box = o3d.t.geometry.TriangleMesh.create_box() box.compute_uvatlas() - assert box.triangle['texture_uvs'].shape == (12, 3, 2) + assert box.triangle["texture_uvs"].shape == (12, 3, 2) def test_bake_vertex_attr_textures(): - desired = np.array([ - [[0., 0., 0.], [0., 0., 0.], [1., 0.25, 0.75], [1., 0.75, 0.75], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [1., 0.25, 0.25], [1., 0.75, 0.25], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0.75, 0., 0.75], [0.75, 0., 0.25], [0.75, 0.25, 0.], [0.75, 0.75, 0.], - [0.75, 1., 0.25], [0.75, 1., 0.75], [0., 0., 0.], [0., 0., 0.]], - [[0.25, 0., 0.75], [0.25, 0., 0.25], [0.25, 0.25, 0.], [0.25, 0.75, 0.], - [0.25, 1., 0.25], [0.25, 1., 0.75], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [0., 0.25, 0.25], [0., 0.75, 0.25], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [0., 0.25, 0.75], [0., 0.75, 0.75], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [0.25, 0.25, 1.], [0.25, 0.75, 1.], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [0.75, 0.25, 1.], [0.75, 0.75, 1.], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] - ], - dtype=np.float32) + desired = np.array( + [ + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [1.0, 0.25, 0.75], + [1.0, 0.75, 0.75], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [1.0, 0.25, 0.25], + [1.0, 0.75, 0.25], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + [ + [0.75, 0.0, 0.75], + [0.75, 0.0, 0.25], + [0.75, 0.25, 0.0], + [0.75, 0.75, 0.0], + [0.75, 1.0, 0.25], + [0.75, 1.0, 0.75], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + [ + [0.25, 0.0, 0.75], + [0.25, 0.0, 0.25], + [0.25, 0.25, 0.0], + [0.25, 0.75, 0.0], + [0.25, 1.0, 0.25], + [0.25, 1.0, 0.75], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.25, 0.25], + [0.0, 0.75, 0.25], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.25, 0.75], + [0.0, 0.75, 0.75], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.25, 0.25, 1.0], + [0.25, 0.75, 1.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.75, 0.25, 1.0], + [0.75, 0.75, 1.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + ], + ], + dtype=np.float32, + ) box = o3d.geometry.TriangleMesh.create_box(create_uv_map=True) box = o3d.t.geometry.TriangleMesh.from_legacy(box) - textures = box.bake_vertex_attr_textures(8, {'positions'}, margin=0.1) + textures = box.bake_vertex_attr_textures(8, {"positions"}, margin=0.1) - np.testing.assert_allclose(textures['positions'].numpy(), desired) + np.testing.assert_allclose(textures["positions"].numpy(), desired) def test_bake_triangle_attr_textures(): desired = np.array( - [[-1, -1, 7, 7, -1, -1, -1, -1], [-1, -1, 7, 6, -1, -1, -1, -1], - [5, 5, 10, 11, 0, 0, -1, -1], [5, 4, 10, 10, 0, 1, -1, -1], - [-1, -1, 2, 2, -1, -1, -1, -1], [-1, -1, 2, 3, -1, -1, -1, -1], - [-1, -1, 8, 9, -1, -1, -1, -1], [-1, -1, 8, 8, -1, -1, -1, -1]], - dtype=np.int64) + [ + [-1, -1, 7, 7, -1, -1, -1, -1], + [-1, -1, 7, 6, -1, -1, -1, -1], + [5, 5, 10, 11, 0, 0, -1, -1], + [5, 4, 10, 10, 0, 1, -1, -1], + [-1, -1, 2, 2, -1, -1, -1, -1], + [-1, -1, 2, 3, -1, -1, -1, -1], + [-1, -1, 8, 9, -1, -1, -1, -1], + [-1, -1, 8, 8, -1, -1, -1, -1], + ], + dtype=np.int64, + ) box = o3d.geometry.TriangleMesh.create_box(create_uv_map=True) box = o3d.t.geometry.TriangleMesh.from_legacy(box) @@ -379,10 +781,10 @@ def test_bake_triangle_attr_textures(): # shift the uvs to avoid pixel centers exactly at triangle boundaries. box.triangle.texture_uvs[:, :, 0] += 0.01 - textures = box.bake_triangle_attr_textures(8, {'index'}, + textures = box.bake_triangle_attr_textures(8, {"index"}, margin=0.1, fill=-1) - np.testing.assert_equal(textures['index'].numpy(), desired) + np.testing.assert_equal(textures["index"].numpy(), desired) def test_extrude_rotation(): @@ -413,10 +815,14 @@ def test_pickle(device): assert mesh_load.device == device assert mesh_load.vertex.positions.dtype == o3c.float32 assert mesh_load.triangle.indices.dtype == o3c.int64 - np.testing.assert_equal(mesh_load.vertex.positions.cpu().numpy(), - mesh.vertex.positions.cpu().numpy()) - np.testing.assert_equal(mesh_load.triangle.indices.cpu().numpy(), - mesh.triangle.indices.cpu().numpy()) + np.testing.assert_equal( + mesh_load.vertex.positions.cpu().numpy(), + mesh.vertex.positions.cpu().numpy(), + ) + np.testing.assert_equal( + mesh_load.triangle.indices.cpu().numpy(), + mesh.triangle.indices.cpu().numpy(), + ) @pytest.mark.parametrize("device", list_devices()) @@ -446,9 +852,17 @@ def test_select_faces_by_mask_32(device): 1, 3, o3c.float64, o3c.int32, device) expected_verts = o3c.Tensor( - [[0.0, 0.0, 1.0], [0.866025, 0, 0.5], [0.433013, 0.75, 0.5], - [-0.866025, 0.0, 0.5], [-0.433013, -0.75, 0.5], [0.433013, -0.75, 0.5] - ], o3c.float64, device) + [ + [0.0, 0.0, 1.0], + [0.866025, 0, 0.5], + [0.433013, 0.75, 0.5], + [-0.866025, 0.0, 0.5], + [-0.433013, -0.75, 0.5], + [0.433013, -0.75, 0.5], + ], + o3c.float64, + device, + ) expected_tris = o3c.Tensor([[0, 1, 2], [0, 3, 4], [0, 4, 5], [0, 5, 1]], o3c.int32, device) @@ -460,18 +874,48 @@ def test_select_faces_by_mask_32(device): selected = sphere_custom.select_faces_by_mask(mask_2d) # check indices type mismatch - mask_float = o3c.Tensor([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], o3c.float32, device) + mask_float = o3c.Tensor( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0 + ], + o3c.float32, + device, + ) with pytest.raises(RuntimeError): selected = sphere_custom.select_faces_by_mask(mask_float) # check the basic case - mask = o3c.Tensor([ - True, False, False, False, False, False, True, False, True, False, True, - False, False, False, False, False, False, False, False, False, False, - False, False, False - ], o3c.bool, device) + mask = o3c.Tensor( + [ + True, + False, + False, + False, + False, + False, + True, + False, + True, + False, + True, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + ], + o3c.bool, + device, + ) selected = sphere_custom.select_faces_by_mask(mask) assert selected.vertex.positions.allclose(expected_verts) assert selected.triangle.indices.allclose(expected_tris) @@ -497,25 +941,63 @@ def test_select_faces_by_mask_64(device): selected = sphere_custom.select_faces_by_mask(mask_2d) # check indices type mismatch - mask_float = o3c.Tensor([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], o3c.float32, device) + mask_float = o3c.Tensor( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0 + ], + o3c.float32, + device, + ) with pytest.raises(RuntimeError): selected = sphere_custom.select_faces_by_mask(mask_float) expected_verts = o3c.Tensor( - [[0.0, 0.0, 1.0], [0.866025, 0, 0.5], [0.433013, 0.75, 0.5], - [-0.866025, 0.0, 0.5], [-0.433013, -0.75, 0.5], [0.433013, -0.75, 0.5] - ], o3c.float64, device) + [ + [0.0, 0.0, 1.0], + [0.866025, 0, 0.5], + [0.433013, 0.75, 0.5], + [-0.866025, 0.0, 0.5], + [-0.433013, -0.75, 0.5], + [0.433013, -0.75, 0.5], + ], + o3c.float64, + device, + ) expected_tris = o3c.Tensor([[0, 1, 2], [0, 3, 4], [0, 4, 5], [0, 5, 1]], o3c.int64, device) # check the basic case - mask = o3c.Tensor([ - True, False, False, False, False, False, True, False, True, False, True, - False, False, False, False, False, False, False, False, False, False, - False, False, False - ], o3c.bool, device) + mask = o3c.Tensor( + [ + True, + False, + False, + False, + False, + False, + True, + False, + True, + False, + True, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + False, + ], + o3c.bool, + device, + ) selected = sphere_custom.select_faces_by_mask(mask) assert selected.vertex.positions.allclose(expected_verts) @@ -536,9 +1018,17 @@ def test_select_by_index_32(device): 1, 3, o3c.float64, o3c.int32, device) expected_verts = o3c.Tensor( - [[0.0, 0.0, 1.0], [0.866025, 0, 0.5], [0.433013, 0.75, 0.5], - [-0.866025, 0.0, 0.5], [-0.433013, -0.75, 0.5], [0.433013, -0.75, 0.5] - ], o3c.float64, device) + [ + [0.0, 0.0, 1.0], + [0.866025, 0, 0.5], + [0.433013, 0.75, 0.5], + [-0.866025, 0.0, 0.5], + [-0.433013, -0.75, 0.5], + [0.433013, -0.75, 0.5], + ], + o3c.float64, + device, + ) expected_tris = o3c.Tensor([[0, 1, 2], [0, 3, 4], [0, 4, 5], [0, 5, 1]], o3c.int32, device) @@ -612,9 +1102,17 @@ def test_select_by_index_64(device): selected = sphere_custom.select_by_index(indices_float) expected_verts = o3c.Tensor( - [[0.0, 0.0, 1.0], [0.866025, 0, 0.5], [0.433013, 0.75, 0.5], - [-0.866025, 0.0, 0.5], [-0.433013, -0.75, 0.5], [0.433013, -0.75, 0.5] - ], o3c.float64, device) + [ + [0.0, 0.0, 1.0], + [0.866025, 0, 0.5], + [0.433013, 0.75, 0.5], + [-0.866025, 0.0, 0.5], + [-0.433013, -0.75, 0.5], + [0.433013, -0.75, 0.5], + ], + o3c.float64, + device, + ) expected_tris = o3c.Tensor([[0, 1, 2], [0, 3, 4], [0, 4, 5], [0, 5, 1]], o3c.int64, device) @@ -680,22 +1178,60 @@ def check_remove_unreferenced_vertices(device, int_t, float_t): 10, 2, 1, 1, 1, 1, 1, float_t, int_t, device) verts = o3c.Tensor( - [[0.5, 0.0, 0.0], [1.5, 0.0, 0.0], [0.424307, 0.308277, -0.154508], - [1.19373, 0.867294, 0.154508], [0.184017, 0.566346, -0.293893], - [0.434017, 1.33577, 0.293893], [-0.218199, 0.671548, -0.404508], - [-0.399835, 1.23057, 0.404508], [-0.684017, 0.496967, -0.475528], - [-0.934017, 0.678603, 0.475528], [-1.0, 0.0, -0.5], [-1.0, 0.0, 0.5], - [-0.934017, -0.678603, -0.475528], [-0.684017, -0.496967, 0.475528], - [-0.399835, -1.23057, -0.404508], [-0.218199, -0.671548, 0.404508], - [0.434017, -1.33577, -0.293893], [0.184017, -0.566346, 0.293893], - [0, 0, 0], [1.19373, -0.867294, -0.154508], [1, 1, 1], - [0.424307, -0.308277, 0.154508]], float_t, device) + [ + [0.5, 0.0, 0.0], + [1.5, 0.0, 0.0], + [0.424307, 0.308277, -0.154508], + [1.19373, 0.867294, 0.154508], + [0.184017, 0.566346, -0.293893], + [0.434017, 1.33577, 0.293893], + [-0.218199, 0.671548, -0.404508], + [-0.399835, 1.23057, 0.404508], + [-0.684017, 0.496967, -0.475528], + [-0.934017, 0.678603, 0.475528], + [-1.0, 0.0, -0.5], + [-1.0, 0.0, 0.5], + [-0.934017, -0.678603, -0.475528], + [-0.684017, -0.496967, 0.475528], + [-0.399835, -1.23057, -0.404508], + [-0.218199, -0.671548, 0.404508], + [0.434017, -1.33577, -0.293893], + [0.184017, -0.566346, 0.293893], + [0, 0, 0], + [1.19373, -0.867294, -0.154508], + [1, 1, 1], + [0.424307, -0.308277, 0.154508], + ], + float_t, + device, + ) tris = o3c.Tensor( - [[0, 3, 1], [0, 2, 3], [3, 2, 4], [3, 4, 5], [4, 7, 5], [4, 6, 7], - [7, 6, 8], [7, 8, 9], [8, 11, 9], [8, 10, 11], [11, 10, 12], - [11, 12, 13], [12, 15, 13], [12, 14, 15], [15, 14, 16], [15, 16, 17], - [16, 21, 17], [16, 19, 21], [19, 21, 1], [1, 21, 0]], int_t, device) + [ + [0, 3, 1], + [0, 2, 3], + [3, 2, 4], + [3, 4, 5], + [4, 7, 5], + [4, 6, 7], + [7, 6, 8], + [7, 8, 9], + [8, 11, 9], + [8, 10, 11], + [11, 10, 12], + [11, 12, 13], + [12, 15, 13], + [12, 14, 15], + [15, 14, 16], + [15, 16, 17], + [16, 21, 17], + [16, 19, 21], + [19, 21, 1], + [1, 21, 0], + ], + int_t, + device, + ) mobius = o3d.t.geometry.TriangleMesh(verts, tris) mobius.remove_unreferenced_vertices() @@ -718,20 +1254,48 @@ def test_compute_triangle_areas(device, int_t, float_t): torus = o3d.t.geometry.TriangleMesh.create_torus(2, 1, 6, 3, float_t, int_t, device) - expected_areas = o3c.Tensor([ - 2.341874249399399, 1.1709371246996996, 1.299038105676658, - 1.2990381056766576, 1.1709371246996996, 2.3418742493993996, - 2.341874249399399, 1.1709371246996996, 1.299038105676658, - 1.2990381056766573, 1.1709371246996996, 2.341874249399399, - 2.341874249399399, 1.1709371246996998, 1.2990381056766582, - 1.2990381056766576, 1.1709371246996993, 2.3418742493993996, - 2.3418742493993987, 1.1709371246996996, 1.2990381056766578, - 1.299038105676657, 1.1709371246996991, 2.3418742493993987, - 2.3418742493993987, 1.1709371246996996, 1.299038105676658, - 1.2990381056766573, 1.170937124699699, 2.341874249399399, - 2.3418742493994, 1.1709371246997002, 1.299038105676659, - 1.2990381056766582, 1.1709371246997, 2.3418742493994005 - ], float_t, device) + expected_areas = o3c.Tensor( + [ + 2.341874249399399, + 1.1709371246996996, + 1.299038105676658, + 1.2990381056766576, + 1.1709371246996996, + 2.3418742493993996, + 2.341874249399399, + 1.1709371246996996, + 1.299038105676658, + 1.2990381056766573, + 1.1709371246996996, + 2.341874249399399, + 2.341874249399399, + 1.1709371246996998, + 1.2990381056766582, + 1.2990381056766576, + 1.1709371246996993, + 2.3418742493993996, + 2.3418742493993987, + 1.1709371246996996, + 1.2990381056766578, + 1.299038105676657, + 1.1709371246996991, + 2.3418742493993987, + 2.3418742493993987, + 1.1709371246996996, + 1.299038105676658, + 1.2990381056766573, + 1.170937124699699, + 2.341874249399399, + 2.3418742493994, + 1.1709371246997002, + 1.299038105676659, + 1.2990381056766582, + 1.1709371246997, + 2.3418742493994005, + ], + float_t, + device, + ) assert torus.compute_triangle_areas().triangle.areas.allclose( expected_areas) @@ -740,23 +1304,56 @@ def test_compute_triangle_areas(device, int_t, float_t): @pytest.mark.parametrize("int_t", (o3c.int32, o3c.int64)) @pytest.mark.parametrize("float_t", (o3c.float32, o3c.float64)) def test_remove_non_manifold_edges(device, int_t, float_t): - verts = o3c.Tensor([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0], - [1.0, 0.0, 1.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0], - [0.0, 1.0, 1.0], [1.0, 1.0, 1.0], [0.0, -0.2, 0.0]], - float_t, device) + verts = o3c.Tensor( + [ + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [1.0, 0.0, 1.0], + [0.0, 1.0, 0.0], + [1.0, 1.0, 0.0], + [0.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [0.0, -0.2, 0.0], + ], + float_t, + device, + ) tris = o3c.Tensor( - [[4, 7, 5], [8, 0, 1], [8, 0, 1], [8, 0, 1], [4, 6, 7], [0, 2, 4], - [2, 6, 4], [0, 1, 2], [1, 3, 2], [1, 5, 7], [8, 0, 2], [8, 0, 2], - [8, 0, 1], [1, 7, 3], [2, 3, 7], [2, 7, 6], [8, 0, 2], [6, 6, 7], - [0, 4, 1], [8, 0, 4], [1, 4, 5]], int_t, device) + [ + [4, 7, 5], + [8, 0, 1], + [8, 0, 1], + [8, 0, 1], + [4, 6, 7], + [0, 2, 4], + [2, 6, 4], + [0, 1, 2], + [1, 3, 2], + [1, 5, 7], + [8, 0, 2], + [8, 0, 2], + [8, 0, 1], + [1, 7, 3], + [2, 3, 7], + [2, 7, 6], + [8, 0, 2], + [6, 6, 7], + [0, 4, 1], + [8, 0, 4], + [1, 4, 5], + ], + int_t, + device, + ) test_box = o3d.t.geometry.TriangleMesh(verts, tris) test_box_legacy = test_box.to_legacy() # allow boundary edges expected_edges = test_box_legacy.get_non_manifold_edges() - np.testing.assert_allclose(test_box.get_non_manifold_edges().numpy(), + np.testing.assert_allclose(test_box.get_non_manifold_edges().cpu().numpy(), np.asarray(expected_edges)) # disallow boundary edges # MSVC produces edges in a different order, so compare sorted legacy results @@ -771,12 +1368,14 @@ def test_remove_non_manifold_edges(device, int_t, float_t): [6, 8], [6, 8], ]) - edges = np.sort(test_box.get_non_manifold_edges(False).numpy(), axis=0) + edges = np.sort(test_box.get_non_manifold_edges(False).cpu().numpy(), + axis=0) np.testing.assert_allclose(edges, expected_edges) test_box.remove_non_manifold_edges() box = o3d.t.geometry.TriangleMesh.create_box(float_dtype=float_t, - int_dtype=int_t) + int_dtype=int_t, + device=device) assert test_box.vertex.positions.allclose(verts) assert test_box.triangle.indices.allclose(box.triangle.indices) From afb23f84e1e3fc09b049fe2016013becb684b7ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:44:13 -0700 Subject: [PATCH 35/82] Bump certifi in /python in the pip group across 1 directory (#6857) Bumps the pip group with 1 update in the /python directory: [certifi](https://github.com/certifi/python-certifi). Updates `certifi` from 2023.7.22 to 2024.7.4 - [Commits](https://github.com/certifi/python-certifi/compare/2023.07.22...2024.07.04) --- updated-dependencies: - dependency-name: certifi dependency-type: direct:production dependency-group: pip ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- python/requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/requirements_test.txt b/python/requirements_test.txt index 4a2f418b6bb..7bb3fd72143 100644 --- a/python/requirements_test.txt +++ b/python/requirements_test.txt @@ -3,4 +3,4 @@ pytest-randomly==3.8.0 scipy==1.10.1 tensorboard==2.13.0 oauthlib==3.2.2 -certifi==2023.7.22 +certifi==2024.7.4 From c9f671323ffd762f74249b52e0e4c4223aecce49 Mon Sep 17 00:00:00 2001 From: intelshashi <99051119+intelshashi@users.noreply.github.com> Date: Tue, 16 Jul 2024 10:44:14 -0700 Subject: [PATCH 36/82] VTK based implementation of extract trianglemesh (#6648) FlyingEdges --------- Co-authored-by: Shashidhara K. Ganjugunte Co-authored-by: Benjamin Ummenhofer Co-authored-by: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> --- cpp/open3d/t/geometry/TriangleMesh.h | 14 +++++++ cpp/open3d/t/geometry/TriangleMeshFactory.cpp | 23 ++++++++++ cpp/open3d/t/geometry/VtkUtils.cpp | 33 ++++++++++++++- cpp/open3d/t/geometry/VtkUtils.h | 9 ++++ cpp/pybind/t/geometry/trianglemesh.cpp | 42 +++++++++++++++++++ python/test/t/geometry/test_trianglemesh.py | 13 ++++++ 6 files changed, 133 insertions(+), 1 deletion(-) diff --git a/cpp/open3d/t/geometry/TriangleMesh.h b/cpp/open3d/t/geometry/TriangleMesh.h index c2916c987ca..a526542faab 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.h +++ b/cpp/open3d/t/geometry/TriangleMesh.h @@ -596,6 +596,20 @@ class TriangleMesh : public Geometry, public DrawableGeometry { core::Dtype int_dtype = core::Int64, const core::Device &device = core::Device("CPU:0")); + /// Create a mesh from a 3D scalar field (volume) by computing the + /// isosurface. This method uses the Flying Edges dual contouring method + /// that computes the isosurface similar to Marching Cubes. The center of + /// the first voxel of the volume is at the origin (0,0,0). The center of + /// the voxel at index [z,y,x] will be at (x,y,z). + /// \param volume 3D tensor with the volume. + /// \param contour_values A list of contour values at which isosurfaces will + /// be generated. The default value is 0. + /// \param device The device for the returned mesh. + static TriangleMesh CreateIsosurfaces( + const core::Tensor &volume, + const std::vector contour_values = {0.0}, + const core::Device &device = core::Device("CPU:0")); + public: /// Clear all data in the trianglemesh. TriangleMesh &Clear() override { diff --git a/cpp/open3d/t/geometry/TriangleMeshFactory.cpp b/cpp/open3d/t/geometry/TriangleMeshFactory.cpp index fb04d856c7c..442f6e4e70c 100644 --- a/cpp/open3d/t/geometry/TriangleMeshFactory.cpp +++ b/cpp/open3d/t/geometry/TriangleMeshFactory.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: MIT // ---------------------------------------------------------------------------- +#include #include #include #include @@ -285,6 +286,28 @@ TriangleMesh TriangleMesh::CreateText(const std::string &text, return tmesh; } +TriangleMesh TriangleMesh::CreateIsosurfaces( + const core::Tensor &volume, + const std::vector contour_values, + const core::Device &device) { + using namespace vtkutils; + core::AssertTensorShape(volume, {core::None, core::None, core::None}); + core::AssertTensorDtypes(volume, {core::Float32, core::Float64}); + + auto image_data = vtkutils::CreateVtkImageDataFromTensor( + const_cast(volume)); + vtkNew method; + method->SetNumberOfContours(contour_values.size()); + for (int i = 0; i < int(contour_values.size()); ++i) { + method->SetValue(i, contour_values[i]); + } + method->SetInputData(image_data); + method->Update(); + auto polydata = method->GetOutput(); + auto tmesh = CreateTriangleMeshFromVtkPolyData(polydata); + return tmesh.To(device); +} + } // namespace geometry } // namespace t } // namespace open3d diff --git a/cpp/open3d/t/geometry/VtkUtils.cpp b/cpp/open3d/t/geometry/VtkUtils.cpp index e6b7245d833..9cd7b06d572 100644 --- a/cpp/open3d/t/geometry/VtkUtils.cpp +++ b/cpp/open3d/t/geometry/VtkUtils.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -182,6 +183,34 @@ static vtkSmartPointer CreateVtkPointsFromTensor( return pts; } +OPEN3D_LOCAL vtkSmartPointer CreateVtkImageDataFromTensor( + core::Tensor& tensor, bool copy) { + core::AssertTensorDtypes(tensor, + {core::UInt8, core::Float32, core::Float64}); + if (tensor.NumDims() != 2 && tensor.NumDims() != 3) { + utility::LogError( + "Cannot convert Tensor to vtkImageData. The number of " + "dimensions must be 2 or 3 but is {}", + tensor.NumDims()); + } + + // Create a flat tensor that can be converted to a vtkDataArray + auto tensor_flat = tensor.Reshape({tensor.NumElements(), 1}); + if (tensor.GetDataPtr() != tensor_flat.GetDataPtr()) { + copy = true; + } + auto data_array = CreateVtkDataArrayFromTensor(tensor_flat, copy); + + vtkSmartPointer im = vtkSmartPointer::New(); + im->GetPointData()->SetScalars(data_array); + std::array size{1, 1, 1}; + for (int i = 0; i < tensor.NumDims(); ++i) { + size[i] = tensor.GetShape(tensor.NumDims() - i - 1); + } + im->SetDimensions(size.data()); + return im; +} + namespace { // Helper for creating the offset array from Common/DataModel/vtkCellArray.cxx struct GenerateOffsetsImpl { @@ -213,7 +242,9 @@ static vtkSmartPointer CreateVtkCellArrayFromTensor( const int cell_size = tensor.GetShape()[1]; auto tensor_flat = tensor.Reshape({tensor.NumElements(), 1}).Contiguous(); - copy = copy && tensor.GetDataPtr() == tensor_flat.GetDataPtr(); + if (tensor.GetDataPtr() != tensor_flat.GetDataPtr()) { + copy = true; + } auto connectivity = CreateVtkDataArrayFromTensor(tensor_flat, copy); // vtk nightly build (9.1.20220520) has a function cells->SetData(cell_size, diff --git a/cpp/open3d/t/geometry/VtkUtils.h b/cpp/open3d/t/geometry/VtkUtils.h index 85aeabe6770..4136354d691 100644 --- a/cpp/open3d/t/geometry/VtkUtils.h +++ b/cpp/open3d/t/geometry/VtkUtils.h @@ -5,6 +5,7 @@ // SPDX-License-Identifier: MIT // ---------------------------------------------------------------------------- +#include #include #include @@ -22,6 +23,14 @@ namespace vtkutils { /// Logs an error if no conversion exists. int DtypeToVtkType(const core::Dtype& dtype); +/// Creates a vtkImageData object from a Tensor. +/// The returned object may directly use the memory of the tensor and the tensor +/// must be kept alive until the returned vtkImageData is deleted. +/// \param tensor The source tensor. +/// \param copy If true always create a copy of the data. +vtkSmartPointer CreateVtkImageDataFromTensor(core::Tensor& tensor, + bool copy = false); + /// Creates a vtkPolyData object from a point cloud or triangle mesh. /// The returned vtkPolyData object may directly use the memory of the tensors /// stored inside the Geometry object. Therefore, the Geometry object must be diff --git a/cpp/pybind/t/geometry/trianglemesh.cpp b/cpp/pybind/t/geometry/trianglemesh.cpp index 3227b3e63db..783e7c0489c 100644 --- a/cpp/pybind/t/geometry/trianglemesh.cpp +++ b/cpp/pybind/t/geometry/trianglemesh.cpp @@ -541,6 +541,48 @@ This example shows how to create a hemisphere from a sphere:: mesh = o3d.t.geometry.TriangleMesh.create_text('Open3D', depth=1) o3d.visualization.draw([{'name': 'text', 'geometry': mesh}]) +)"); + + triangle_mesh.def_static( + "create_isosurfaces", + // Accept anything for contour_values that pybind can convert to + // std::list. This also avoids o3d.utility.DoubleVector. + [](const core::Tensor& volume, std::list contour_values, + const core::Device& device) { + std::vector cv(contour_values.begin(), + contour_values.end()); + return TriangleMesh::CreateIsosurfaces(volume, cv, device); + }, + "volume"_a, "contour_values"_a = std::list{0.0}, + "device"_a = core::Device("CPU:0"), + R"(Create a mesh from a 3D scalar field (volume) by computing the +isosurface. + +This method uses the Flying Edges dual contouring method that computes the +isosurface similar to Marching Cubes. The center of the first voxel of the +volume is at the origin (0,0,0). The center of the voxel at index [z,y,x] +will be at (x,y,z). + +Args: + volume (open3d.core.Tensor): 3D tensor with the volume. + contour_values (list): A list of contour values at which isosurfaces will + be generated. The default value is 0. + device (o3d.core.Device): The device for the returned mesh. + +Returns: + A TriangleMesh with the extracted isosurfaces. + + +This example shows how to create a sphere from a volume:: + + import open3d as o3d + import numpy as np + + coords = np.stack(np.meshgrid(*3*[np.linspace(-1,1,num=64)], indexing='ij'), axis=-1) + vol = np.linalg.norm(coords, axis=-1) - 0.5 + mesh = o3d.t.geometry.TriangleMesh.create_isosurfaces(vol) + o3d.visualization.draw(mesh) + )"); triangle_mesh.def( diff --git a/python/test/t/geometry/test_trianglemesh.py b/python/test/t/geometry/test_trianglemesh.py index 679c65f3582..44915d709cd 100644 --- a/python/test/t/geometry/test_trianglemesh.py +++ b/python/test/t/geometry/test_trianglemesh.py @@ -616,6 +616,19 @@ def test_create_text(): assert mesh.triangle.indices.shape == (936, 3) +def test_create_isosurfaces(): + """Create signed distance field for sphere of radius 0.5 and extract sphere + from it. + """ + coords = np.stack(np.meshgrid(*3 * [np.linspace(-1, 1, num=64)], + indexing='ij'), + axis=-1) + vol = np.linalg.norm(coords, axis=-1) - 0.5 + mesh = o3d.t.geometry.TriangleMesh.create_isosurfaces(vol) + assert mesh.vertex.positions.shape[0] == 4728 + assert mesh.triangle.indices.shape[0] == 9452 + + def test_simplify_quadric_decimation(): cube = o3d.t.geometry.TriangleMesh.from_legacy( o3d.geometry.TriangleMesh.create_box().subdivide_midpoint(3)) From d96ecbfb66d9b682351bda36f62401b5b5eb3267 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Tue, 16 Jul 2024 10:37:13 -0700 Subject: [PATCH 37/82] Update windows CUDA version to match Linux, TF version, torch as latest supporting Intel Mac, update werkzeug, flask to matching versions. --- .github/workflows/windows.yml | 2 +- python/requirements.txt | 3 ++- util/ci_utils.sh | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3b738c288e8..65e1b216e1f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -47,7 +47,7 @@ jobs: STATIC_RUNTIME: ON include: - BUILD_CUDA_MODULE: ON - CUDA_VERSION: 11.0.3 + CUDA_VERSION: 11.8.0 env: BUILD_WEBRTC: ${{ ( matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' ) && 'ON' || 'OFF' }} diff --git a/python/requirements.txt b/python/requirements.txt index 148650326df..a7da4963446 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,5 +1,6 @@ numpy>=1.18.0,<2.0.0 dash>=2.6.0 -werkzeug>=2.2.3 +werkzeug>=3.0.0 +flask>=3.0.0 nbformat>=5.7.0 configargparse diff --git a/util/ci_utils.sh b/util/ci_utils.sh index 6e77244a429..565cc09161c 100644 --- a/util/ci_utils.sh +++ b/util/ci_utils.sh @@ -25,8 +25,8 @@ LOW_MEM_USAGE=${LOW_MEM_USAGE:-OFF} # Dependency versions: # CUDA: see docker/docker_build.sh # ML -TENSORFLOW_VER="2.16.1" -TORCH_VER="2.2.0" +TENSORFLOW_VER="2.16.2" +TORCH_VER="2.2.2" TORCH_CPU_GLNX_VER="${TORCH_VER}+cpu" TORCH_CUDA_GLNX_VER="${TORCH_VER}+cu118" # match CUDA_VERSION in docker/docker_build.sh TORCH_MACOS_VER="${TORCH_VER}" From 1f66cb09193068001057a0282764d4dbb7400490 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Wed, 17 Jul 2024 09:56:40 +0200 Subject: [PATCH 38/82] add missing initialization for compute_vertex_normals (#6873) --- cpp/open3d/geometry/TriangleMesh.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/open3d/geometry/TriangleMesh.cpp b/cpp/open3d/geometry/TriangleMesh.cpp index 88f602770cb..7078a675c4f 100644 --- a/cpp/open3d/geometry/TriangleMesh.cpp +++ b/cpp/open3d/geometry/TriangleMesh.cpp @@ -126,7 +126,9 @@ TriangleMesh &TriangleMesh::ComputeTriangleNormals( TriangleMesh &TriangleMesh::ComputeVertexNormals(bool normalized /* = true*/) { ComputeTriangleNormals(false); - vertex_normals_.resize(vertices_.size(), Eigen::Vector3d::Zero()); + vertex_normals_.resize(vertices_.size()); + std::fill(vertex_normals_.begin(), vertex_normals_.end(), + Eigen::Vector3d::Zero()); for (size_t i = 0; i < triangles_.size(); i++) { auto &triangle = triangles_[i]; vertex_normals_[triangle(0)] += triangle_normals_[i]; From 2c8bb608f960301401ff33867c6b5831231ea462 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:45:06 -0700 Subject: [PATCH 39/82] Update pybind11 to latest. (#6874) --- 3rdparty/pybind11/pybind11.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3rdparty/pybind11/pybind11.cmake b/3rdparty/pybind11/pybind11.cmake index 9a9c1b6df77..1d3d7856203 100644 --- a/3rdparty/pybind11/pybind11.cmake +++ b/3rdparty/pybind11/pybind11.cmake @@ -3,8 +3,8 @@ include(FetchContent) FetchContent_Declare( ext_pybind11 PREFIX pybind11 - URL https://github.com/pybind/pybind11/archive/refs/tags/v2.11.1.tar.gz - URL_HASH SHA256=d475978da0cdc2d43b73f30910786759d593a9d8ee05b1b6846d1eb16c6d2e0c + URL https://github.com/pybind/pybind11/archive/refs/tags/v2.13.1.tar.gz + URL_HASH SHA256=51631e88960a8856f9c497027f55c9f2f9115cafb08c0005439838a05ba17bfc DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/pybind11" ) From c1b55eebaf1e4e969560baf039ced0df5c74e0e7 Mon Sep 17 00:00:00 2001 From: Robin <102535177+rxba@users.noreply.github.com> Date: Sat, 20 Jul 2024 01:10:58 +0200 Subject: [PATCH 40/82] add numpy version warning to documentation (#6879) --- docs/getting_started.in.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index a2aa5a745ee..534b76ff36d 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -50,6 +50,16 @@ Pip (PyPI) pip install open3d # or pip install open3d-cpu # Smaller CPU only wheel on x86_64 Linux (since v0.17+) +.. warning:: + + Versions of ``numpy>=2.0.0`` require the latest development version of Open3D or + ``Open3D>0.18.0``. If you are using an older version of Open3D, downgrade ``numpy`` + with + + .. code-block:: bash + + pip install "numpy<2.0.0" + .. warning:: Please upgrade your ``pip`` to a version >=20.3 to install Open3D in Linux, From 90810a335a544903bbad349177741ea6dd516184 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Tue, 23 Jul 2024 17:44:20 +0200 Subject: [PATCH 41/82] Fix links to release links of main-devel tag (#6883) --- docs/getting_started.in.rst | 60 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index 534b76ff36d..82352383ab3 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -13,9 +13,9 @@ interact with it. You can download the latest stable release app from `Github releases `__. The latest development version (``HEAD`` of ``main`` branch) viewer app is provided here [#]_: -* `Linux (Ubuntu 18.04+ or glibc 2.27+) `__ [#]_ -* `MacOSX v10.15+ (Intel or Apple Silicon) `__ -* `Windows 10+ (64-bit) `__ +* `Linux (Ubuntu 18.04+ or glibc 2.27+) `__ [#]_ +* `MacOSX v10.15+ (Intel or Apple Silicon) `__ +* `Windows 10+ (64-bit) `__ .. [#] Please use these links from the `latest version of this page `__ only. .. [#] To check the `glibc` version on your system, run :code:`ldd --version`. @@ -52,8 +52,8 @@ Pip (PyPI) .. warning:: - Versions of ``numpy>=2.0.0`` require the latest development version of Open3D or - ``Open3D>0.18.0``. If you are using an older version of Open3D, downgrade ``numpy`` + Versions of ``numpy>=2.0.0`` require ``Open3D>0.18.0`` or the latest development + version of Open3D. If you are using an older version of Open3D, downgrade ``numpy`` with .. code-block:: bash @@ -67,7 +67,7 @@ Pip (PyPI) .. code-block:: bash - pip install -U pip>=20.3 + pip install -U "pip>=20.3" .. note:: In general, we recommend using a @@ -95,28 +95,28 @@ version (``HEAD`` of ``main`` branch): :widths: auto * - Linux - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ * - Linux (CPU) - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ * - MacOS - - `Python 3.8 (x86_64) `__ - - `Python 3.9 (x86_64) `__ - - `Python 3.10 (x86_64+arm64) `__ - - `Python 3.11 (x86_64+arm64) `__ + - `Python 3.8 (x86_64) `__ + - `Python 3.9 (x86_64) `__ + - `Python 3.10 (x86_64+arm64) `__ + - `Python 3.11 (x86_64+arm64) `__ * - Windows - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ Please use these links from the `latest version of this page `__ only. You can also @@ -187,24 +187,24 @@ available for the main supported platforms. Also, the latest development version .. hlist:: :columns: 2 - * `x86_64 (CXX11 ABI) `__ - * `x86_64 (CXX11 ABI) with CUDA 11.x `__ - * `x86_64 (pre CXX11 ABI) `__ - * `x86_64 (pre CXX11 ABI) with CUDA 11.x `__ + * `x86_64 (CXX11 ABI) `__ + * `x86_64 (CXX11 ABI) with CUDA 11.x `__ + * `x86_64 (pre CXX11 ABI) `__ + * `x86_64 (pre CXX11 ABI) with CUDA 11.x `__ :MacOSX v10.15+: .. hlist:: :columns: 2 - * `x86_64 `__ - * `arm64 `__ + * `x86_64 `__ + * `arm64 `__ :Windows 10+: .. hlist:: :columns: 2 - * `x86_64 Release `__ - * `x86_64 Debug `__ + * `x86_64 Release `__ + * `x86_64 Debug `__ .. [#] Please use these links from the `latest version of this page `__ only. From 0c58ba324acd6436f2e3fc09db95950fa199ca47 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Fri, 26 Jul 2024 00:32:49 +0200 Subject: [PATCH 42/82] fix pip install statement for latest dev whl on getting started page (#6890) * Upload macOS devel wheels for Py 3.8 and Py 3.9 --------- Co-authored-by: Sameer Sheorey --- .github/workflows/macos.yml | 9 +++++++++ docs/getting_started.in.rst | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 82b6472653d..8d43e10d608 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -291,6 +291,15 @@ jobs: path: build/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} if-no-files-found: error + - name: Update devel release (x86_64 only wheels) + if: ${{ github.ref == 'refs/heads/main' && (matrix.python_version == '3.8' || matrix.python_version == '3.9') }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release upload main-devel build/lib/python_package/pip_package/${{ env.PIP_PKG_NAME }} --clobber + gh release view main-devel + + fuse-wheel: name: Fuse universal2 wheel permissions: diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index 82352383ab3..e81dfdf1af8 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -124,7 +124,7 @@ install the latest development version directly with pip: .. code-block:: bash - pip install -U -f https://www.open3d.org/docs/latest/getting_started.html open3d + pip install -U -f https://www.open3d.org/docs/latest/getting_started.html --only-binary open3d open3d .. warning:: The development wheels for Linux are named according to PEP600. Please From 9f03592261895f6503714268afcb317620149f7c Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:34:28 -0700 Subject: [PATCH 43/82] Copy examples for building user projects with Open3D to Open3D repo (#6884) Earlier example location: https://github.com/intel-isl/open3d-cmake-find-package https://github.com/isl-org/open3d-cmake-external-project Copying them into the main repo to reduce maintenance overhead and ensure they are up to date. --- docker/docker_test.sh | 6 +- docs/cpp_project.rst | 4 +- .../CMakeLists.txt | 52 +++++++++++++ .../open3d-cmake-external-project/Draw.cpp | 26 +++++++ .../open3d-cmake-external-project/README.md | 56 ++++++++++++++ .../open3d-cmake-find-package/CMakeLists.txt | 33 ++++++++ .../cmake/open3d-cmake-find-package/Draw.cpp | 26 +++++++ .../cmake/open3d-cmake-find-package/README.md | 75 +++++++++++++++++++ util/ci_utils.sh | 11 ++- 9 files changed, 277 insertions(+), 12 deletions(-) create mode 100644 examples/cmake/open3d-cmake-external-project/CMakeLists.txt create mode 100644 examples/cmake/open3d-cmake-external-project/Draw.cpp create mode 100644 examples/cmake/open3d-cmake-external-project/README.md create mode 100644 examples/cmake/open3d-cmake-find-package/CMakeLists.txt create mode 100644 examples/cmake/open3d-cmake-find-package/Draw.cpp create mode 100644 examples/cmake/open3d-cmake-find-package/README.md diff --git a/docker/docker_test.sh b/docker/docker_test.sh index 544eeb755e4..0f1c18d9490 100755 --- a/docker/docker_test.sh +++ b/docker/docker_test.sh @@ -170,8 +170,7 @@ cpp_python_linking_uninstall_test() { fi ${docker_run} -i --rm "${DOCKER_TAG}" /bin/bash -c "\ - git clone https://github.com/isl-org/open3d-cmake-find-package.git \ - && cd open3d-cmake-find-package \ + cd examples/cmake/open3d-cmake-find-package \ && mkdir build \ && pushd build \ && echo Testing build with cmake \ @@ -182,8 +181,7 @@ cpp_python_linking_uninstall_test() { if [ "${BUILD_SHARED_LIBS}" == "ON" ] && [ "${BUILD_SYCL_MODULE}" == "OFF" ]; then ${docker_run} -i --rm "${DOCKER_TAG}" /bin/bash -c "\ - git clone https://github.com/isl-org/open3d-cmake-find-package.git \ - && cd open3d-cmake-find-package \ + cd examples/cmake/open3d-cmake-find-package \ && mkdir build \ && pushd build \ && echo Testing build with pkg-config \ diff --git a/docs/cpp_project.rst b/docs/cpp_project.rst index 89bfd98b70f..bd8432552a0 100644 --- a/docs/cpp_project.rst +++ b/docs/cpp_project.rst @@ -9,10 +9,10 @@ CMake We provide two example CMake projects to demonstrate how to use Open3D in your CMake projects. -* `Find Pre-Installed Open3D Package in CMake `_ +* `Find Pre-Installed Open3D Package in CMake `_ This option can be used if you'd like to build and install Open3D first, then link your project to Open3D. -* `Use Open3D as a CMake External Project `_ +* `Use Open3D as a CMake External Project `_ This option can be used if you'd like Open3D to build alongside with your project. diff --git a/examples/cmake/open3d-cmake-external-project/CMakeLists.txt b/examples/cmake/open3d-cmake-external-project/CMakeLists.txt new file mode 100644 index 00000000000..37ed1f3d6dd --- /dev/null +++ b/examples/cmake/open3d-cmake-external-project/CMakeLists.txt @@ -0,0 +1,52 @@ +# On Ubuntu 18.04, get the latest CMake from https://apt.kitware.com/. +cmake_minimum_required(VERSION 3.18) + +project(Open3DCMakeExternalProject LANGUAGES C CXX) + +option(GLIBCXX_USE_CXX11_ABI "Set -D_GLIBCXX_USE_CXX11_ABI=1" OFF) +option(STATIC_WINDOWS_RUNTIME "Use static (MT/MTd) Windows runtime" ON ) + +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "No CMAKE_BUILD_TYPE specified, default to Release.") + set(CMAKE_BUILD_TYPE "Release") +endif() + +# Option 1: Use ExternalProject_Add, as shown in this CMake example. +# Option 2: Install Open3D first and use find_package, see +# http://www.open3d.org/docs/release/cpp_project.html for details. +include(ExternalProject) +ExternalProject_Add( + external_open3d + PREFIX open3d + GIT_REPOSITORY https://github.com/isl-org/Open3D.git + GIT_TAG main # Use a specific tag, e.g. v0.18.0 to pin Open3D version. + GIT_SHALLOW ON + UPDATE_COMMAND "" + # Check out https://github.com/intel-isl/Open3D/blob/master/CMakeLists.txt + # For the full list of available options. + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX= + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DGLIBCXX_USE_CXX11_ABI=${GLIBCXX_USE_CXX11_ABI} + -DSTATIC_WINDOWS_RUNTIME=${STATIC_WINDOWS_RUNTIME} + -DBUILD_SHARED_LIBS=ON + -DBUILD_PYTHON_MODULE=OFF + -DBUILD_EXAMPLES=OFF +) + +# Simulate importing Open3D::Open3D +ExternalProject_Get_Property(external_open3d INSTALL_DIR) +add_library(Open3DHelper INTERFACE) +add_dependencies(Open3DHelper external_open3d) +target_compile_features(Open3DHelper INTERFACE cxx_std_14) +target_compile_definitions(Open3DHelper INTERFACE _GLIBCXX_USE_CXX11_ABI=$) +target_include_directories(Open3DHelper INTERFACE "${INSTALL_DIR}/include" "${INSTALL_DIR}/include/open3d/3rdparty") +target_link_directories(Open3DHelper INTERFACE "${INSTALL_DIR}/lib") +target_link_libraries(Open3DHelper INTERFACE Open3D) +add_library(Open3D::Open3D ALIAS Open3DHelper) + +add_executable(Draw) +target_sources(Draw PRIVATE Draw.cpp) +target_link_libraries(Draw PRIVATE Open3D::Open3D) diff --git a/examples/cmake/open3d-cmake-external-project/Draw.cpp b/examples/cmake/open3d-cmake-external-project/Draw.cpp new file mode 100644 index 00000000000..5415508ed11 --- /dev/null +++ b/examples/cmake/open3d-cmake-external-project/Draw.cpp @@ -0,0 +1,26 @@ +// ---------------------------------------------------------------------------- +// - Open3D: www.open3d.org - +// ---------------------------------------------------------------------------- +// Copyright (c) 2018-2023 www.open3d.org +// SPDX-License-Identifier: MIT +// ---------------------------------------------------------------------------- + +#include + +#include "open3d/Open3D.h" + +int main(int argc, char *argv[]) { + if (argc == 2) { + std::string option(argv[1]); + if (option == "--skip-for-unit-test") { + open3d::utility::LogInfo("Skiped for unit test."); + return 0; + } + } + + auto sphere = open3d::geometry::TriangleMesh::CreateSphere(1.0); + sphere->ComputeVertexNormals(); + sphere->PaintUniformColor({0.0, 1.0, 0.0}); + open3d::visualization::DrawGeometries({sphere}); + return 0; +} diff --git a/examples/cmake/open3d-cmake-external-project/README.md b/examples/cmake/open3d-cmake-external-project/README.md new file mode 100644 index 00000000000..fb8aec6c650 --- /dev/null +++ b/examples/cmake/open3d-cmake-external-project/README.md @@ -0,0 +1,56 @@ +# Use Open3D as a CMake External Project + +This is one of the two CMake examples showing how to use Open3D in your CMake +project: + +- [Find Pre-Installed Open3D Package in CMake](../open3d-cmake-find-package) +- [Use Open3D as a CMake External Project](../open3d-cmake-external-project) + +For more details, check out the [Open3D repo](https://github.com/isl-org/Open3D) and +[Open3D docs](http://www.open3d.org/docs/release/cpp_project.html). + +## Step 1: Install Open3D dependencies + +On Ubuntu: + +```bash +# Install minimal Open3D compilation dependencies. For the full list, checkout: +# https://github.com/isl-org/Open3D/blob/master/util/install_deps_ubuntu.sh +sudo apt-get --yes install xorg-dev libglu1-mesa-dev +``` + +On macOS/Windows: + +```bash +# Skip this step +``` + +## Step 2: Use Open3D in this example project + +You can specify the number of parallel jobs to speed up compilation. + +On Ubuntu/macOS: + +```bash +wget https://github.com/isl-org/Open3D/archive/refs/heads/main.zip -o Open3D-main.zip +unzip Open3D-main.zip 'Open3D-main/cmake/ispc_isas/*' -d example-project +cd example-project/Open3D-main/examples/cmake/open3d-cmake-external-project +mkdir build +cd build +cmake .. +make -j 12 +./Draw +``` + +On Windows: + +```batch +wget https://github.com/isl-org/Open3D/archive/refs/heads/main.zip -o Open3D-main.zip +unzip Open3D-main.zip 'Open3D-main/cmake/ispc_isas/*' -d example-project +cd example-project/Open3D-main/examples/cmake/open3d-cmake-external-project +mkdir build +cd build +cmake .. +cmake --build . --config Release --parallel 12 +Release\Draw +``` diff --git a/examples/cmake/open3d-cmake-find-package/CMakeLists.txt b/examples/cmake/open3d-cmake-find-package/CMakeLists.txt new file mode 100644 index 00000000000..ea3773b1550 --- /dev/null +++ b/examples/cmake/open3d-cmake-find-package/CMakeLists.txt @@ -0,0 +1,33 @@ +# On Ubuntu 18.04, get the latest CMake from https://apt.kitware.com/. +cmake_minimum_required(VERSION 3.18) + +project(Open3DCMakeFindPackage LANGUAGES C CXX) + +# The options need to be the same as Open3D's default +# If Open3D is configured and built with custom options, you'll also need to +# specify the same custom options. +option(STATIC_WINDOWS_RUNTIME "Use static (MT/MTd) Windows runtime" ON) +if(STATIC_WINDOWS_RUNTIME) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +else() + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") +endif() + +# Find installed Open3D, which exports Open3D::Open3D +find_package(Open3D REQUIRED) + +add_executable(Draw) +target_sources(Draw PRIVATE Draw.cpp) +target_link_libraries(Draw PRIVATE Open3D::Open3D) + +# On Windows if BUILD_SHARED_LIBS is enabled, copy .dll files to the executable directory +if(WIN32) + get_target_property(open3d_type Open3D::Open3D TYPE) + if(open3d_type STREQUAL "SHARED_LIBRARY") + message(STATUS "Copying Open3D.dll to ${CMAKE_CURRENT_BINARY_DIR}/$") + add_custom_command(TARGET Draw POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_INSTALL_PREFIX}/bin/Open3D.dll + ${CMAKE_CURRENT_BINARY_DIR}/$) + endif() +endif() diff --git a/examples/cmake/open3d-cmake-find-package/Draw.cpp b/examples/cmake/open3d-cmake-find-package/Draw.cpp new file mode 100644 index 00000000000..5415508ed11 --- /dev/null +++ b/examples/cmake/open3d-cmake-find-package/Draw.cpp @@ -0,0 +1,26 @@ +// ---------------------------------------------------------------------------- +// - Open3D: www.open3d.org - +// ---------------------------------------------------------------------------- +// Copyright (c) 2018-2023 www.open3d.org +// SPDX-License-Identifier: MIT +// ---------------------------------------------------------------------------- + +#include + +#include "open3d/Open3D.h" + +int main(int argc, char *argv[]) { + if (argc == 2) { + std::string option(argv[1]); + if (option == "--skip-for-unit-test") { + open3d::utility::LogInfo("Skiped for unit test."); + return 0; + } + } + + auto sphere = open3d::geometry::TriangleMesh::CreateSphere(1.0); + sphere->ComputeVertexNormals(); + sphere->PaintUniformColor({0.0, 1.0, 0.0}); + open3d::visualization::DrawGeometries({sphere}); + return 0; +} diff --git a/examples/cmake/open3d-cmake-find-package/README.md b/examples/cmake/open3d-cmake-find-package/README.md new file mode 100644 index 00000000000..5bae407b7fa --- /dev/null +++ b/examples/cmake/open3d-cmake-find-package/README.md @@ -0,0 +1,75 @@ +# Find Pre-Installed Open3D Package in CMake + +This is one of the two CMake examples showing how to use Open3D in your CMake +project: + +- [Find Pre-Installed Open3D Package in CMake](../open3d-cmake-find-package) +- [Use Open3D as a CMake External Project](../open3d-cmake-external-project) + +For more details, check out the [Open3D repo](https://github.com/isl-org/Open3D) and +[Open3D docs](http://www.open3d.org/docs/release/cpp_project.html). + +You may download a precompiled binary package (recommended), or compile your +own. + +## Step 1a: Download pre-compiled Open3D binary package + +Download the pre-compiled Open3D binary package from the [Open3D release page](https://github.com/isl-org/Open3D/releases). The binary package is available for Ubuntu (with and without CUDA), macOS (Inel and Apple Si), and Windows. You may download a stable release or a development build (devel-main). + +## Step 1b: Compile and install Open3D + +Follow the [Open3D compilation guide](http://www.open3d.org/docs/release/compilation.html), +compile and install Open3D in your preferred location. You can specify the +installation path with `CMAKE_INSTALL_PREFIX` and the number of parallel jobs +to speed up compilation. + +On Ubuntu/macOS: + +```bash +git clone https://github.com/isl-org/Open3D.git +cd Open3D +mkdir build +cd build +cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=${HOME}/open3d_install .. +make install -j 12 +cd ../.. +``` + +On Windows: + +```batch +git clone https://github.com/isl-org/Open3D.git +cd Open3D +mkdir build +cd build +cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=C:\open3d_install .. +cmake --build . --config Release --parallel 12 --target install +cd ..\.. +``` + +Note: `-DBUILD_SHARED_LIBS=ON` is recommended if `-DBUILD_CUDA_MODULE=ON`. + +## Step 2: Use Open3D in this example project + +On Ubuntu/macOS: + +```bash +cp -ar Open3D/examples/cmake/open3d-cmake-find-package . +cd open3d-cmake-find-package +mkdir build +cd build +cmake -DOpen3D_ROOT=${HOME}/open3d_install .. +make -j 12 +./Draw +``` + +On Windows: + +```batch +cp -ar Open3D/examples/cmake/open3d-cmake-find-package . +cd open3d-cmake-find-package +mkdir build +cmake -DOpen3D_ROOT=C:\open3d_install .. +cmake --build . --config Release --parallel 12 +Release\Draw +``` diff --git a/util/ci_utils.sh b/util/ci_utils.sh index aa30d2c12ad..52db35f727a 100644 --- a/util/ci_utils.sh +++ b/util/ci_utils.sh @@ -316,11 +316,9 @@ run_cpp_unit_tests() { # Need variable OPEN3D_INSTALL_DIR test_cpp_example() { # Now I am in Open3D/build/ - cd .. - git clone https://github.com/isl-org/open3d-cmake-find-package.git - cd open3d-cmake-find-package + pushd ../examples/cmake/open3d-cmake-find-package mkdir build - cd build + pushd build echo Testing build with cmake cmake -DCMAKE_INSTALL_PREFIX=${OPEN3D_INSTALL_DIR} .. make -j"$NPROC" VERBOSE=1 @@ -338,8 +336,9 @@ test_cpp_example() { ./Draw --skip-for-unit-test fi fi - # Now I am in Open3D/open3d-cmake-find-package/build/ - cd ../../build + popd + popd + # Now I am in Open3D/build/ } # Install dependencies needed for building documentation (on Ubuntu 18.04) From 78e4bfd43f8c1317a7e725f33d6f33575c034858 Mon Sep 17 00:00:00 2001 From: "Kang, Hsin-Yi" Date: Fri, 26 Jul 2024 06:38:42 +0800 Subject: [PATCH 44/82] Add O3DVisualizer API to enable collapse control of verts in the side panel (#6865) * Add GetText/SetText to CollapsableVert --------- Co-authored-by: Ewing Kang --- CHANGELOG.md | 1 + cpp/open3d/visualization/gui/Layout.cpp | 14 ++++++++++---- cpp/open3d/visualization/gui/Layout.h | 5 +++++ .../visualization/visualizer/O3DVisualizer.cpp | 16 ++++++++++++++++ .../visualization/visualizer/O3DVisualizer.h | 1 + cpp/pybind/visualization/gui/gui.cpp | 4 ++++ cpp/pybind/visualization/o3dvisualizer.cpp | 3 +++ 7 files changed, 40 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e192ce692e5..8d9d0c88065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - Fix segmentation fault (infinite recursion) of DetectPlanarPatches if multiple points have same coordinates (PR #6794) - Fix build with fmt v10.2.0 (#6783) - Fix segmentation fault (lambda reference capture) of VisualizerWithCustomAnimation::Play (PR #6804) +- Add O3DVisualizer API to enable collapse control of verts in the side panel (PR #6865) ## 0.13 diff --git a/cpp/open3d/visualization/gui/Layout.cpp b/cpp/open3d/visualization/gui/Layout.cpp index f61c26eafe6..8da97bc899f 100644 --- a/cpp/open3d/visualization/gui/Layout.cpp +++ b/cpp/open3d/visualization/gui/Layout.cpp @@ -386,10 +386,7 @@ CollapsableVert::CollapsableVert(const char* text, int spacing, const Margins& margins /*= Margins()*/) : Vert(spacing, margins), impl_(new CollapsableVert::Impl()) { - static int g_next_id = 1; - - impl_->text_ = text; - impl_->id_ = impl_->text_ + "##collapsing_" + std::to_string(g_next_id++); + SetText(text); } CollapsableVert::~CollapsableVert() {} @@ -398,6 +395,15 @@ void CollapsableVert::SetIsOpen(bool is_open) { impl_->is_open_ = is_open; } bool CollapsableVert::GetIsOpen() { return impl_->is_open_; } +void CollapsableVert::SetText(const char* text) { + static int g_next_id = 1; + + impl_->text_ = text; + impl_->id_ = impl_->text_ + "##collapsing_" + std::to_string(g_next_id++); +} + +std::string CollapsableVert::GetText() const { return impl_->text_; }; + FontId CollapsableVert::GetFontId() const { return impl_->font_id_; } void CollapsableVert::SetFontId(FontId font_id) { impl_->font_id_ = font_id; } diff --git a/cpp/open3d/visualization/gui/Layout.h b/cpp/open3d/visualization/gui/Layout.h index 8d069e49273..7421c6522cb 100644 --- a/cpp/open3d/visualization/gui/Layout.h +++ b/cpp/open3d/visualization/gui/Layout.h @@ -7,6 +7,8 @@ #pragma once +#include + #include "open3d/visualization/gui/Widget.h" namespace open3d { @@ -148,6 +150,9 @@ class CollapsableVert : public Vert { /// Returns true if open and false if collapsed. bool GetIsOpen(); + void SetText(const char* text); + std::string GetText() const; + FontId GetFontId() const; void SetFontId(FontId font_id); diff --git a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp index 554d38b2466..69dbcbadb61 100644 --- a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp +++ b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp @@ -1706,6 +1706,18 @@ Ctrl-alt-click to polygon select)"; } } + void SetPanelOpen(const std::string &name, bool open) { + if (name == settings.mouse_panel->GetText()) { + settings.mouse_panel->SetIsOpen(open); + } else if (name == settings.scene_panel->GetText()) { + settings.scene_panel->SetIsOpen(open); + } else if (name == settings.light_panel->GetText()) { + settings.light_panel->SetIsOpen(open); + } else if (name == settings.geometries_panel->GetText()) { + settings.geometries_panel->SetIsOpen(open); + } + } + void SetPicking() { if (selections_->GetNumberOfSets() == 0) { NewSelectionSet(); @@ -2446,6 +2458,10 @@ void O3DVisualizer::SetMouseMode(SceneWidget::Controls mode) { impl_->SetMouseMode(mode); } +void O3DVisualizer::SetPanelOpen(const std::string &name, bool open) { + impl_->SetPanelOpen(name, open); +} + void O3DVisualizer::EnableGroup(const std::string &group, bool enable) { impl_->EnableGroup(group, enable); } diff --git a/cpp/open3d/visualization/visualizer/O3DVisualizer.h b/cpp/open3d/visualization/visualizer/O3DVisualizer.h index 4ceec56de76..af0ced3d6ba 100644 --- a/cpp/open3d/visualization/visualizer/O3DVisualizer.h +++ b/cpp/open3d/visualization/visualizer/O3DVisualizer.h @@ -175,6 +175,7 @@ class O3DVisualizer : public gui::Window { void SetLineWidth(int line_width); void EnableGroup(const std::string& group, bool enable); void SetMouseMode(gui::SceneWidget::Controls mode); + void SetPanelOpen(const std::string& name, bool open); std::vector GetSelectionSets() const; diff --git a/cpp/pybind/visualization/gui/gui.cpp b/cpp/pybind/visualization/gui/gui.cpp index a211d233a05..9f086e50ef5 100644 --- a/cpp/pybind/visualization/gui/gui.cpp +++ b/cpp/pybind/visualization/gui/gui.cpp @@ -1772,6 +1772,10 @@ void pybind_gui_classes(py::module &m) { "window is visible") .def("get_is_open", &CollapsableVert::GetIsOpen, "Check if widget is open.") + .def("set_text", &CollapsableVert::SetText, "text"_a, + "Sets the text for the CollapsableVert") + .def("get_text", &CollapsableVert::GetText, + "Gets the text for the CollapsableVert") .def_property("font_id", &CollapsableVert::GetFontId, &CollapsableVert::SetFontId, "Set the font using the FontId returned from " diff --git a/cpp/pybind/visualization/o3dvisualizer.cpp b/cpp/pybind/visualization/o3dvisualizer.cpp index a97d108f918..0859658a081 100644 --- a/cpp/pybind/visualization/o3dvisualizer.cpp +++ b/cpp/pybind/visualization/o3dvisualizer.cpp @@ -344,6 +344,9 @@ void pybind_o3dvisualizer(py::module& m) { "enable"_a) .def("show_skybox", &O3DVisualizer::ShowSkybox, "Show/Hide the skybox", "show"_a) + .def("set_panel_open", &O3DVisualizer::SetPanelOpen, + "Expand/Collapse verts(panels) within the settings panel", + "name"_a, "open"_a) .def_property( "show_settings", [](const O3DVisualizer& dv) { From f02e7d24ea115e716445a7fae5093bce60a37d20 Mon Sep 17 00:00:00 2001 From: David Conde Date: Fri, 26 Jul 2024 00:45:52 +0200 Subject: [PATCH 45/82] Build with CUDA >= 12.0 with dynamic libraries as well (#6815) * Added BUILD_WITH_CUDA_STATIC flag --- 3rdparty/find_dependencies.cmake | 70 +++++++++++++++++++++++--------- CMakeLists.txt | 1 + 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index 1db3494e3ed..5c02fac0a32 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -1782,16 +1782,30 @@ if(BUILD_CUDA_MODULE) CUDA::culibos ) else() - # In CUDA12.0 the liblapack_static.a is deprecated and removed. + # In CUDA 12.0 the liblapack_static.a is deprecated and removed. # Use the libcusolver_lapack_static.a instead. - target_link_libraries(3rdparty_cublas INTERFACE - CUDA::cusolver_static - ${CUDAToolkit_LIBRARY_DIR}/libcusolver_lapack_static.a - CUDA::cusparse_static - CUDA::cublas_static - CUDA::cublasLt_static - CUDA::culibos - ) + # Use of static libraries is preferred. + if(BUILD_WITH_CUDA_STATIC) + # Use static CUDA libraries. + target_link_libraries(3rdparty_cublas INTERFACE + CUDA::cusolver_static + ${CUDAToolkit_LIBRARY_DIR}/libcusolver_lapack_static.a + CUDA::cusparse_static + CUDA::cublas_static + CUDA::cublasLt_static + CUDA::culibos + ) + else() + # Use shared CUDA libraries. + target_link_libraries(3rdparty_cublas INTERFACE + CUDA::cusolver + ${CUDAToolkit_LIBRARY_DIR}/libcusolver.so + CUDA::cusparse + CUDA::cublas + CUDA::cublasLt + CUDA::culibos + ) + endif() endif() if(NOT BUILD_SHARED_LIBS) # Listed in ${CMAKE_INSTALL_PREFIX}/lib/cmake/Open3D/Open3DTargets.cmake. @@ -1818,16 +1832,34 @@ if (BUILD_CUDA_MODULE) CUDA::nppial ) else() - open3d_find_package_3rdparty_library(3rdparty_cuda_npp - REQUIRED - PACKAGE CUDAToolkit - TARGETS CUDA::nppc_static - CUDA::nppicc_static - CUDA::nppif_static - CUDA::nppig_static - CUDA::nppim_static - CUDA::nppial_static - ) + if(BUILD_WITH_CUDA_STATIC) + # Use static CUDA libraries. + open3d_find_package_3rdparty_library(3rdparty_cuda_npp + REQUIRED + PACKAGE CUDAToolkit + TARGETS CUDA::nppc_static + CUDA::nppicc_static + CUDA::nppif_static + CUDA::nppig_static + CUDA::nppim_static + CUDA::nppial_static + ) + else() + # Use shared CUDA libraries. + open3d_find_package_3rdparty_library(3rdparty_cuda_npp + REQUIRED + PACKAGE CUDAToolkit + TARGETS CUDA::nppc + CUDA::nppicc + CUDA::nppif + CUDA::nppig + CUDA::nppim + CUDA::nppial + ) + endif() + endif() + if(NOT 3rdparty_cuda_npp_FOUND) + message(FATAL_ERROR "CUDA NPP libraries not found.") endif() list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_cuda_npp) endif () diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e4781e7e72..f7fc2b3eb5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ option(BUILD_UNIT_TESTS "Build Open3D unit tests" OFF option(BUILD_BENCHMARKS "Build the micro benchmarks" OFF) option(BUILD_PYTHON_MODULE "Build the python module" ON ) option(BUILD_CUDA_MODULE "Build the CUDA module" OFF) +option(BUILD_WITH_CUDA_STATIC "Build with static CUDA libraries" ON ) option(BUILD_COMMON_CUDA_ARCHS "Build for common CUDA GPUs (for release)" OFF) if (WIN32) # Causes CUDA runtime error on Windows (See issue #6555) option(ENABLE_CACHED_CUDA_MANAGER "Enable cached CUDA memory manager" OFF) From b271acbad61bce803749b46aa65afdce27f709b7 Mon Sep 17 00:00:00 2001 From: cdh981009 <35074913+cdh981009@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:19:58 +0900 Subject: [PATCH 46/82] Fix color update in gpu (#6886) --- cpp/open3d/visualization/rendering/filament/FilamentScene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/open3d/visualization/rendering/filament/FilamentScene.cpp b/cpp/open3d/visualization/rendering/filament/FilamentScene.cpp index 64dffe9dc69..13f2d7800d7 100644 --- a/cpp/open3d/visualization/rendering/filament/FilamentScene.cpp +++ b/cpp/open3d/visualization/rendering/filament/FilamentScene.cpp @@ -590,7 +590,7 @@ void FilamentScene::UpdateGeometry(const std::string& object_name, const size_t color_array_size = n_vertices * 3 * sizeof(float); if (pcloud_is_gpu) { auto color_data = static_cast(malloc(color_array_size)); - memcpy(color_data, cpu_pcloud.GetPointPositions().GetDataPtr(), + memcpy(color_data, cpu_pcloud.GetPointColors().GetDataPtr(), color_array_size); filament::VertexBuffer::BufferDescriptor color_descriptor( color_data, color_array_size, DeallocateBuffer); From 8f5d3b421bafa7d4b3f28c8aeebe8efead13e48b Mon Sep 17 00:00:00 2001 From: Nicola Loi Date: Wed, 7 Aug 2024 14:48:02 +0200 Subject: [PATCH 47/82] Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (#6898) * Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests * Update CHANGELOG --- CHANGELOG.md | 1 + cpp/open3d/geometry/MeshBase.cpp | 2 +- cpp/tests/geometry/TetraMesh.cpp | 224 +++++++++++++++++++++++++++ cpp/tests/geometry/TriangleMesh.cpp | 226 ++++++++++++++++++++++++++++ 4 files changed, 452 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d9d0c88065..ed7a0c786cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - Fix build with fmt v10.2.0 (#6783) - Fix segmentation fault (lambda reference capture) of VisualizerWithCustomAnimation::Play (PR #6804) - Add O3DVisualizer API to enable collapse control of verts in the side panel (PR #6865) +- Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (PR #6898) ## 0.13 diff --git a/cpp/open3d/geometry/MeshBase.cpp b/cpp/open3d/geometry/MeshBase.cpp index 1e403968900..b8986460e1b 100644 --- a/cpp/open3d/geometry/MeshBase.cpp +++ b/cpp/open3d/geometry/MeshBase.cpp @@ -51,7 +51,7 @@ OrientedBoundingBox MeshBase::GetOrientedBoundingBox(bool robust) const { } OrientedBoundingBox MeshBase::GetMinimalOrientedBoundingBox(bool robust) const { - return OrientedBoundingBox::CreateFromPoints(vertices_, robust); + return OrientedBoundingBox::CreateFromPointsMinimal(vertices_, robust); } MeshBase &MeshBase::Transform(const Eigen::Matrix4d &transformation) { diff --git a/cpp/tests/geometry/TetraMesh.cpp b/cpp/tests/geometry/TetraMesh.cpp index 5b5e29db246..6c26a6784f4 100644 --- a/cpp/tests/geometry/TetraMesh.cpp +++ b/cpp/tests/geometry/TetraMesh.cpp @@ -9,6 +9,7 @@ #include +#include "open3d/geometry/BoundingVolume.h" #include "open3d/geometry/PointCloud.h" #include "open3d/geometry/TriangleMesh.h" #include "tests/Tests.h" @@ -119,6 +120,229 @@ TEST(TetraMesh, GetMaxBound) { tm.GetMaxBound()); } +TEST(TetraMesh, GetCenter) { + int size = 100; + + Eigen::Vector3d dmin(0.0, 0.0, 0.0); + Eigen::Vector3d dmax(1000.0, 1000.0, 1000.0); + + geometry::TetraMesh tm; + + tm.vertices_.resize(size); + Rand(tm.vertices_, dmin, dmax, 0); + + ExpectEQ(tm.GetCenter(), + Eigen::Vector3d(531.137254, 535.176470, 501.882352)); + + geometry::TetraMesh tm_empty; + ExpectEQ(tm_empty.GetCenter(), Eigen::Vector3d(0, 0, 0)); +} + +TEST(TetraMesh, GetAxisAlignedBoundingBox) { + geometry::TetraMesh tm; + geometry::AxisAlignedBoundingBox aabb; + + tm = geometry::TetraMesh(); + aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1)); + + tm = geometry::TetraMesh({{0, 0, 0}}, {{0, 0, 0, 0}}); + aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1)); + + tm = geometry::TetraMesh({{0, 2, 0}, + {1, 1, 2}, + {1, 0, 3}, + {0, 1, 4}, + {1, 2, 5}, + {1, 3, 6}, + {0, 0, 7}, + {0, 3, 8}, + {1, 0, 9}, + {0, 2, 10}}, + {{0, 1, 2, 3}, + {4, 0, 1, 2}, + {4, 5, 2, 3}, + {4, 0, 2, 3}, + {4, 0, 5, 3}, + {6, 0, 5, 3}, + {6, 7, 8, 5}, + {9, 4, 7, 1}, + {9, 4, 0, 1}, + {9, 0, 1, 3}, + {9, 6, 7, 8}, + {9, 4, 7, 5}, + {9, 6, 7, 5}, + {9, 4, 0, 5}, + {9, 6, 0, 5}}); + aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(1, 3, 10)); + EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1)); +} + +TEST(TetraMesh, GetOrientedBoundingBox) { + geometry::TetraMesh tm; + geometry::OrientedBoundingBox obb; + + // Empty (GetOrientedBoundingBox requires >=4 points) + tm = geometry::TetraMesh(); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + + // Point + tm = geometry::TetraMesh({{0, 0, 0}}, {{0, 0, 0, 0}}); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, + {{0, 1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetOrientedBoundingBox(true)); + + // Plane + tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}}, + {{0, 1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetOrientedBoundingBox(true)); + + // Valid 4 points + tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 1, 1}}, + {{0, 1, 2, 3}}); + tm.GetOrientedBoundingBox(); + + // 8 points with known ground truth + tm = geometry::TetraMesh({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}, + {{0, 1, 2, 3}, + {4, 5, 6, 7}, + {0, 1, 4, 5}, + {2, 3, 6, 7}, + {0, 2, 4, 6}, + {1, 3, 5, 7}}); + obb = tm.GetOrientedBoundingBox(); + EXPECT_EQ(obb.center_, Eigen::Vector3d(1.5, 1, 0.5)); + EXPECT_EQ(obb.extent_, Eigen::Vector3d(3, 2, 1)); + EXPECT_EQ(obb.color_, Eigen::Vector3d(1, 1, 1)); + EXPECT_EQ(obb.R_, Eigen::Matrix3d::Identity()); + ExpectEQ(Sort(obb.GetBoxPoints()), + Sort(std::vector({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}))); + + // Check for a bug where the OBB rotation contained a reflection for this + // example. + tm = geometry::TetraMesh({{0, 2, 4}, {7, 9, 1}, {5, 2, 0}, {3, 8, 7}}, + {{0, 1, 2, 3}}); + obb = tm.GetOrientedBoundingBox(); + EXPECT_GT(obb.R_.determinant(), 0.999); +} + +TEST(TetraMesh, GetMinimalOrientedBoundingBox) { + geometry::TetraMesh tm; + geometry::OrientedBoundingBox obb; + + // Empty (GetOrientedBoundingBox requires >=4 points) + tm = geometry::TetraMesh(); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + + // Point + tm = geometry::TetraMesh({{0, 0, 0}}, {{0, 0, 0, 0}}); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, + {{0, 1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetMinimalOrientedBoundingBox(true)); + + // Plane + tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}}, + {{0, 1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetMinimalOrientedBoundingBox(true)); + + // Valid 4 points + tm = geometry::TetraMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 1, 1}}, + {{0, 1, 2, 3}}); + tm.GetMinimalOrientedBoundingBox(); + + // 8 points with known ground truth + tm = geometry::TetraMesh({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}, + {{0, 1, 2, 3}, + {4, 5, 6, 7}, + {0, 1, 4, 5}, + {2, 3, 6, 7}, + {0, 2, 4, 6}, + {1, 3, 5, 7}}); + obb = tm.GetMinimalOrientedBoundingBox(); + EXPECT_EQ(obb.center_, Eigen::Vector3d(1.5, 1, 0.5)); + EXPECT_EQ(obb.extent_, Eigen::Vector3d(3, 2, 1)); + EXPECT_EQ(obb.color_, Eigen::Vector3d(1, 1, 1)); + ExpectEQ(Sort(obb.GetBoxPoints()), + Sort(std::vector({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}))); + + // Check for a bug where the OBB rotation contained a reflection for this + // example. + tm = geometry::TetraMesh({{0, 2, 4}, {7, 9, 1}, {5, 2, 0}, {3, 8, 7}}, + {{0, 1, 2, 3}}); + obb = tm.GetMinimalOrientedBoundingBox(); + EXPECT_GT(obb.R_.determinant(), 0.999); + + // should always be equal/smaller than axis aligned- & oriented bounding box + tm = geometry::TetraMesh({{0.866, 0.474, 0.659}, + {0.943, 0.025, 0.789}, + {0.386, 0.264, 0.691}, + {0.938, 0.588, 0.496}, + {0.221, 0.116, 0.257}, + {0.744, 0.182, 0.052}, + {0.019, 0.525, 0.699}, + {0.722, 0.134, 0.668}}, + {{0, 1, 2, 3}, + {4, 0, 2, 3}, + {4, 0, 1, 2}, + {5, 4, 6, 3}, + {5, 4, 2, 3}, + {5, 4, 1, 2}, + {7, 1, 2, 6}, + {7, 5, 2, 6}, + {7, 5, 1, 2}, + {7, 5, 4, 6}, + {7, 5, 4, 1}, + {7, 0, 1, 6}, + {7, 4, 0, 6}, + {7, 4, 0, 1}}); + geometry::OrientedBoundingBox mobb = tm.GetMinimalOrientedBoundingBox(); + obb = tm.GetOrientedBoundingBox(); + geometry::AxisAlignedBoundingBox aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_GT(obb.Volume(), mobb.Volume()); + EXPECT_GT(aabb.Volume(), mobb.Volume()); +} + TEST(TetraMesh, Transform) { std::vector ref_vertices = { {1.411252, 4.274168, 3.130918}, {1.231757, 4.154505, 3.183678}, diff --git a/cpp/tests/geometry/TriangleMesh.cpp b/cpp/tests/geometry/TriangleMesh.cpp index ff6d75f6c8c..a562059cbc2 100644 --- a/cpp/tests/geometry/TriangleMesh.cpp +++ b/cpp/tests/geometry/TriangleMesh.cpp @@ -168,6 +168,232 @@ TEST(TriangleMesh, GetMaxBound) { tm.GetMaxBound()); } +TEST(TriangleMesh, GetCenter) { + int size = 100; + + Eigen::Vector3d dmin(0.0, 0.0, 0.0); + Eigen::Vector3d dmax(1000.0, 1000.0, 1000.0); + + geometry::TriangleMesh tm; + + tm.vertices_.resize(size); + Rand(tm.vertices_, dmin, dmax, 0); + + ExpectEQ(tm.GetCenter(), + Eigen::Vector3d(531.137254, 535.176470, 501.882352)); + + geometry::TriangleMesh tm_empty; + ExpectEQ(tm_empty.GetCenter(), Eigen::Vector3d(0, 0, 0)); +} + +TEST(TriangleMesh, GetAxisAlignedBoundingBox) { + geometry::TriangleMesh tm; + geometry::AxisAlignedBoundingBox aabb; + + tm = geometry::TriangleMesh(); + aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1)); + + tm = geometry::TriangleMesh({{0, 0, 0}}, {{0, 0, 0}}); + aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1)); + + tm = geometry::TriangleMesh({{0, 2, 0}, + {1, 1, 2}, + {1, 0, 3}, + {0, 1, 4}, + {1, 2, 5}, + {1, 3, 6}, + {0, 0, 7}, + {0, 3, 8}, + {1, 0, 9}, + {0, 2, 10}}, + {{3, 1, 0}, + {3, 2, 1}, + {1, 2, 4}, + {5, 4, 2}, + {2, 3, 5}, + {6, 3, 0}, + {6, 5, 3}, + {5, 6, 8}, + {8, 7, 5}, + {1, 7, 9}, + {1, 4, 7}, + {0, 1, 9}, + {9, 8, 6}, + {7, 8, 9}, + {4, 5, 7}, + {9, 6, 0}}); + aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_EQ(aabb.min_bound_, Eigen::Vector3d(0, 0, 0)); + EXPECT_EQ(aabb.max_bound_, Eigen::Vector3d(1, 3, 10)); + EXPECT_EQ(aabb.color_, Eigen::Vector3d(1, 1, 1)); +} + +TEST(TriangleMesh, GetOrientedBoundingBox) { + geometry::TriangleMesh tm; + geometry::OrientedBoundingBox obb; + + // Empty (GetOrientedBoundingBox requires >=4 points) + tm = geometry::TriangleMesh(); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + + // Point + tm = geometry::TriangleMesh({{0, 0, 0}}, {{0, 0, 0}}); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + tm = geometry::TriangleMesh({{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, + {{0, 1, 2}, {1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetOrientedBoundingBox(true)); + + // Plane + tm = geometry::TriangleMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}}, + {{0, 1, 2}, {1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetOrientedBoundingBox(true)); + + // Valid 4 points + tm = geometry::TriangleMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 1, 1}}, + {{0, 1, 2}, {1, 2, 3}}); + tm.GetOrientedBoundingBox(); + + // 8 points with known ground truth + tm = geometry::TriangleMesh({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}, + {{0, 1, 2}, + {1, 2, 3}, + {4, 5, 6}, + {5, 6, 7}, + {0, 1, 4}, + {1, 4, 5}, + {2, 3, 6}, + {3, 6, 7}}); + obb = tm.GetOrientedBoundingBox(); + EXPECT_EQ(obb.center_, Eigen::Vector3d(1.5, 1, 0.5)); + EXPECT_EQ(obb.extent_, Eigen::Vector3d(3, 2, 1)); + EXPECT_EQ(obb.color_, Eigen::Vector3d(1, 1, 1)); + EXPECT_EQ(obb.R_, Eigen::Matrix3d::Identity()); + ExpectEQ(Sort(obb.GetBoxPoints()), + Sort(std::vector({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}))); + + // Check for a bug where the OBB rotation contained a reflection for this + // example. + tm = geometry::TriangleMesh({{0, 2, 4}, {7, 9, 1}, {5, 2, 0}, {3, 8, 7}}, + {{0, 1, 2}, {1, 2, 3}}); + obb = tm.GetOrientedBoundingBox(); + EXPECT_GT(obb.R_.determinant(), 0.999); +} + +TEST(TriangleMesh, GetMinimalOrientedBoundingBox) { + geometry::TriangleMesh tm; + geometry::OrientedBoundingBox obb; + + // Empty (GetOrientedBoundingBox requires >=4 points) + tm = geometry::TriangleMesh(); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + + // Point + tm = geometry::TriangleMesh({{0, 0, 0}}, {{0, 0, 0}}); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + tm = geometry::TriangleMesh({{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, + {{0, 1, 2}, {1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetMinimalOrientedBoundingBox(true)); + + // Plane + tm = geometry::TriangleMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}}, + {{0, 1, 2}, {1, 2, 3}}); + EXPECT_ANY_THROW(tm.GetMinimalOrientedBoundingBox()); + EXPECT_NO_THROW(tm.GetMinimalOrientedBoundingBox(true)); + + // Valid 4 points + tm = geometry::TriangleMesh({{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 1, 1}}, + {{0, 1, 2}, {1, 2, 3}}); + tm.GetMinimalOrientedBoundingBox(); + + // 8 points with known ground truth + tm = geometry::TriangleMesh({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}, + {{0, 1, 2}, + {1, 2, 3}, + {4, 5, 6}, + {5, 6, 7}, + {0, 1, 4}, + {1, 4, 5}, + {2, 3, 6}, + {3, 6, 7}}); + obb = tm.GetMinimalOrientedBoundingBox(); + EXPECT_EQ(obb.center_, Eigen::Vector3d(1.5, 1, 0.5)); + EXPECT_EQ(obb.extent_, Eigen::Vector3d(3, 2, 1)); + EXPECT_EQ(obb.color_, Eigen::Vector3d(1, 1, 1)); + ExpectEQ(Sort(obb.GetBoxPoints()), + Sort(std::vector({{0, 0, 0}, + {0, 0, 1}, + {0, 2, 0}, + {0, 2, 1}, + {3, 0, 0}, + {3, 0, 1}, + {3, 2, 0}, + {3, 2, 1}}))); + + // Check for a bug where the OBB rotation contained a reflection for this + // example. + tm = geometry::TriangleMesh({{0, 2, 4}, {7, 9, 1}, {5, 2, 0}, {3, 8, 7}}, + {{0, 1, 2}, {1, 2, 3}}); + obb = tm.GetMinimalOrientedBoundingBox(); + EXPECT_GT(obb.R_.determinant(), 0.999); + + // should always be equal/smaller than axis aligned- & oriented bounding box + tm = geometry::TriangleMesh({{0.866, 0.474, 0.659}, + {0.943, 0.025, 0.789}, + {0.386, 0.264, 0.691}, + {0.938, 0.588, 0.496}, + {0.221, 0.116, 0.257}, + {0.744, 0.182, 0.052}, + {0.019, 0.525, 0.699}, + {0.722, 0.134, 0.668}}, + {{6, 5, 4}, + {6, 3, 5}, + {2, 6, 4}, + {6, 2, 0}, + {3, 6, 0}, + {5, 3, 1}, + {3, 0, 1}, + {7, 1, 2}, + {1, 0, 2}, + {4, 1, 7}, + {4, 5, 1}, + {7, 2, 4}}); + geometry::OrientedBoundingBox mobb = tm.GetMinimalOrientedBoundingBox(); + obb = tm.GetOrientedBoundingBox(); + geometry::AxisAlignedBoundingBox aabb = tm.GetAxisAlignedBoundingBox(); + EXPECT_GT(obb.Volume(), mobb.Volume()); + EXPECT_GT(aabb.Volume(), mobb.Volume()); +} + TEST(TriangleMesh, Transform) { std::vector ref_vertices = { {1.411252, 4.274168, 3.130918}, {1.231757, 4.154505, 3.183678}, From e86fcb38ddb5cb5e3d77c53cab234ddd687fbcfd Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Thu, 8 Aug 2024 23:47:42 -0700 Subject: [PATCH 48/82] Update min OS to Ubuntu 20.04 (#6902) * Update min OS to Ubuntu 20.04 * Switch latest Ubuntu from noble to jammy: No CUDNN docker image for noble. * Update CUDA version selection, add support for Hopper * Remove CPU rendering support for Ubuntu 18.04 (old mesa SW) * Autodetect CUDA version to install the correct PyTorch in CI * CUDA latest 11.8 to support PyTorch 2.0 Fix CUDAARCHS in Windows CI, since no GPU present. ARM Linux: Fix for "cannot allocate memory in TLS blc * Fix for CUDA ARCH case with no GPU * new docker version fix * remove docker docs for Ubutu 18.04 --- .github/workflows/ubuntu-cuda.yml | 10 +- .github/workflows/ubuntu-wheel.yml | 2 +- .github/workflows/vtk_packages.yml | 2 +- .github/workflows/webrtc.yml | 6 +- 3rdparty/curl/curl.cmake | 2 +- 3rdparty/find_dependencies.cmake | 18 --- 3rdparty/mesa/build-mesa-cpu.sh | 62 --------- 3rdparty/webrtc/Dockerfile.webrtc | 2 +- CMakeLists.txt | 52 ++++++-- cmake/Open3DMakeCudaArchitectures.cmake | 78 ------------ cpp/pybind/CMakeLists.txt | 3 +- docker/Dockerfile.wheel | 2 +- docker/docker_build.sh | 118 +++++++++--------- docker/docker_test.sh | 36 +++--- docs/docker.in.rst | 41 +----- docs/tutorial/visualization/cpu_rendering.rst | 33 +---- python/open3d/__init__.py | 7 -- util/ci_utils.sh | 38 +++--- util/install_deps_ubuntu.sh | 12 +- 19 files changed, 156 insertions(+), 368 deletions(-) delete mode 100755 3rdparty/mesa/build-mesa-cpu.sh delete mode 100644 cmake/Open3DMakeCudaArchitectures.cmake diff --git a/.github/workflows/ubuntu-cuda.yml b/.github/workflows/ubuntu-cuda.yml index 0a6a71ed239..daab8f7af61 100644 --- a/.github/workflows/ubuntu-cuda.yml +++ b/.github/workflows/ubuntu-cuda.yml @@ -54,15 +54,15 @@ jobs: fail-fast: false matrix: include: - - CI_CONFIG: 2-bionic - - CI_CONFIG: 3-ml-shared-bionic - - CI_CONFIG: 4-shared-bionic - - CI_CONFIG: 5-ml-focal + - CI_CONFIG: 2-focal + - CI_CONFIG: 3-ml-shared-focal + - CI_CONFIG: 4-shared-focal + - CI_CONFIG: 5-ml-jammy env: # Export everything from matrix to be easily used. # Docker tag and ccache names must be consistent with docker_build.sh CI_CONFIG : ${{ matrix.CI_CONFIG }} - BUILD_PACKAGE : ${{ contains(fromJson('["3-ml-shared-bionic", "4-shared-bionic"]'), matrix.CI_CONFIG) }} + BUILD_PACKAGE : ${{ contains(fromJson('["3-ml-shared-focal", "4-shared-focal"]'), matrix.CI_CONFIG) }} GCE_INSTANCE_PREFIX: open3d-ci-${{ matrix.CI_CONFIG }} DOCKER_TAG : open3d-ci:${{ matrix.CI_CONFIG }} CCACHE_TAR_NAME : open3d-ci-${{ matrix.CI_CONFIG }} diff --git a/.github/workflows/ubuntu-wheel.yml b/.github/workflows/ubuntu-wheel.yml index 3b935a75811..591d0852238 100644 --- a/.github/workflows/ubuntu-wheel.yml +++ b/.github/workflows/ubuntu-wheel.yml @@ -46,7 +46,7 @@ jobs: env: DEVELOPER_BUILD: ${{ github.event.inputs.developer_build || 'ON' }} PYTHON_VERSION: ${{ matrix.python_version }} - CCACHE_TAR_NAME: open3d-ubuntu-1804-cuda-ci-ccache + CCACHE_TAR_NAME: open3d-ubuntu-2004-cuda-ci-ccache OPEN3D_CPU_RENDERING: true steps: - name: Checkout source code diff --git a/.github/workflows/vtk_packages.yml b/.github/workflows/vtk_packages.yml index 25116f9d6ce..f447193a5bd 100644 --- a/.github/workflows/vtk_packages.yml +++ b/.github/workflows/vtk_packages.yml @@ -11,7 +11,7 @@ jobs: permissions: contents: write # TODO: Convert to docker - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout source code uses: actions/checkout@v4 diff --git a/.github/workflows/webrtc.yml b/.github/workflows/webrtc.yml index 7689c9d681f..263c5462130 100644 --- a/.github/workflows/webrtc.yml +++ b/.github/workflows/webrtc.yml @@ -30,10 +30,10 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-18.04, macos-11] + os: [ubuntu-20.04, macos-12] GLIBCXX_USE_CXX11_ABI: [0, 1] exclude: - - os: macos-11 + - os: macos-12 GLIBCXX_USE_CXX11_ABI: 0 env: GLIBCXX_USE_CXX11_ABI: ${{ matrix.GLIBCXX_USE_CXX11_ABI }} @@ -48,7 +48,7 @@ jobs: python-version: 3.8 - name: Install dependencies - if: ${{ matrix.os == 'ubuntu-18.04' }} + if: ${{ matrix.os == 'ubuntu-20.04' }} run: | source 3rdparty/webrtc/webrtc_build.sh install_dependencies_ubuntu diff --git a/3rdparty/curl/curl.cmake b/3rdparty/curl/curl.cmake index 14ee47f2210..c45dbea0dd9 100644 --- a/3rdparty/curl/curl.cmake +++ b/3rdparty/curl/curl.cmake @@ -65,7 +65,7 @@ else() # Optimize for Ubuntu x86. Curl can take a long time to configure. # # To generate pre-compiled curl: - # 1. Use Ubuntu 18.04 (eg. in docker), not 20.04+. + # 1. Use oldest supported Ubuntu (eg. in docker), not the latest. # 2. -DBUILD_CURL_FROM_SOURCE=ON, build Open3D: make ext_curl # 3. cd build/curl # 4. tar -czvf curl_7.88.0_linux_x86_64.tar.gz include lib diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index 5c02fac0a32..f3f68698bd3 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -1405,24 +1405,6 @@ else() endif() list(APPEND Open3D_3RDPARTY_HEADER_TARGETS_FROM_SYSTEM Open3D::3rdparty_opengl) -# CPU Rendering -if(BUILD_GUI AND UNIX AND NOT APPLE) - include(FetchContent) - FetchContent_Declare( - download_mesa_libgl - PREFIX mesa - URL https://github.com/isl-org/open3d_downloads/releases/download/mesa-libgl/mesa_libGL_22.1.4.tar.bz2 - URL_HASH SHA256=5732bfb70e8fcc747018820bc8fd31cd1867ebae5aa09baf65482b42c134d45a - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/mesa" - ) - FetchContent_MakeAvailable(download_mesa_libgl) - - set(MESA_CPU_GL_LIBRARY "${download_mesa_libgl_SOURCE_DIR}/libGL.so.1.2.0" "${download_mesa_libgl_SOURCE_DIR}/libEGL.so.1.0.0" - "${download_mesa_libgl_SOURCE_DIR}/libgallium_dri.so" "${download_mesa_libgl_SOURCE_DIR}/kms_swrast_dri.so" - "${download_mesa_libgl_SOURCE_DIR}/swrast_dri.so") - message(STATUS "MESA_CPU_GL_LIBRARY: ${MESA_CPU_GL_LIBRARY}") -endif() - # RPC interface # zeromq if(USE_SYSTEM_ZEROMQ) diff --git a/3rdparty/mesa/build-mesa-cpu.sh b/3rdparty/mesa/build-mesa-cpu.sh deleted file mode 100755 index 3260510bd5d..00000000000 --- a/3rdparty/mesa/build-mesa-cpu.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -# Build Mesa CPU rendering library libGL.so following instructions from -# https://docs.mesa3d.org/install.html -# -# To build on Ubuntu, simply run this script. sudo access is needed and it will -# modify your system. Alternately, you can build in an isolated docker container -# as follows: -# -# cd Open3D/3rdparty/mesa -# docker run --rm -v $PWD:/host_dir --entrypoint /host_dir/build-mesa-cpu.sh \ -# --workdir /root/ ubuntu:bionic -# sudo chown $(id -u):$(id -g) libGL.so.1.5.0 -# -# This will build libGL.so.1.5.0 in the docker container and copy the result -# back to the current directory. - -MESA_VER=22.1.4 # latest tag on 2022-08-01 -[ "$EUID" -ne 0 ] && SUDO="sudo" || SUDO="command" - -echo "Enable source repositories and get build dependencies." -$SUDO sed -i '/deb-src/s/^# //' /etc/apt/sources.list && -$SUDO apt-get update && -$SUDO apt-get --yes build-dep mesa && -$SUDO apt-get --yes install wget -echo "Check and update meson (Ubuntu 18.04 has a too old meson.)" -if dpkg --compare-versions "$(meson -v)" lt 0.46.1 ; then - $SUDO apt-get --yes install python3-pip - yes | pip3 install "meson>=0.46.1,<0.62.0" # 0.62.0 disables Python 3.6 suport - export PATH=/usr/local/bin:$PATH # meson from pip before system meson -fi -echo "Disable source repositories again." -$SUDO sed -i '/deb-src/s/^/# /' /etc/apt/sources.list - -echo "Get Mesa source code version $MESA_VER" -wget -c \ -https://archive.mesa3d.org/mesa-${MESA_VER}.tar.xz \ --O - | tar -xJ -pushd mesa-${MESA_VER} -echo Configure... -meson build/ \ - `# SW rendering` \ - -Dglx=dri -Dgallium-drivers=swrast -Dplatforms=x11 \ - `# Enable EGL` \ - -Degl=enabled -Degl-native-platform=surfaceless \ - `# Disable HW drivers` \ - -Ddri3=false -Ddri-drivers= -Dvulkan-drivers= \ - -Dgbm=disabled -Dlmsensors=disabled \ - `# Optimization, remove debug info` \ - -Dbuildtype=release -Doptimization=3 -Db_lto=true -Dstrip=true \ - `# Security hardening` \ - -Dcpp_args="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat -Wformat-security" \ - -Dcpp_link_args="-Wl,-z,noexecstack -Wl,-z,relro,-z,now" - -echo Build... -ninja -C build/ -echo "Copy libGL.so out" -[ -d /host_dir ] && OUT_DIR="/host_dir" || OUT_DIR=".." -cp build/src/glx/libGL.so.1.2.0 $OUT_DIR -cp build/src/egl/libEGL.so.1.0.0 $OUT_DIR -cp build/src/gallium/targets/dri/*.so $OUT_DIR -popd diff --git a/3rdparty/webrtc/Dockerfile.webrtc b/3rdparty/webrtc/Dockerfile.webrtc index a604ad9e048..b935d0e8ef7 100644 --- a/3rdparty/webrtc/Dockerfile.webrtc +++ b/3rdparty/webrtc/Dockerfile.webrtc @@ -13,7 +13,7 @@ # - docker run --rm --entrypoint cat open3d-webrtc:abi1 \ # webrtc_60e6748_cxx-abi-1.tar.gz > webrtc_60e6748_cxx-abi-1.tar.gz -FROM ubuntu:18.04 +FROM ubuntu:20.04 ARG SUDO=command COPY 3rdparty/webrtc 3rdparty/webrtc diff --git a/CMakeLists.txt b/CMakeLists.txt index f7fc2b3eb5d..dc76ee40176 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ -cmake_minimum_required(VERSION 3.22) -# If you're using Ubuntu 18.04, we suggest you install the latest CMake from the +cmake_minimum_required(VERSION 3.24) +# If you're using Ubuntu 20.04, we suggest you install the latest CMake from the # official repository https://apt.kitware.com/. +# CMake 3.24+ is required for CUDA native arch selection # CMake 3.22+ is required by Assimp v5.4.2 # CMake 3.20+ is required to detect IntelLLVM compiler for SYCL @@ -401,17 +402,44 @@ cmake_language(EVAL CODE "cmake_language(DEFER CALL open3d_patch_findthreads_mod # Build CUDA module by default if CUDA is available if(BUILD_CUDA_MODULE) - include(Open3DMakeCudaArchitectures) - open3d_make_cuda_architectures(CUDA_ARCHS) - set(CMAKE_CUDA_ARCHITECTURES ${CUDA_ARCHS}) - - message(STATUS "Using CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}") + if(BUILD_COMMON_CUDA_ARCHS) + if (CMAKE_CUDA_ARCHITECTURES) + message(STATUS "Building with user-provided architectures: ${CMAKE_CUDA_ARCHITECTURES}") + else() + # Build with all supported architectures for previous 2 generations and + # M0 (minor=0) architectures for previous generations (including + # deprecated). Note that cubin for M0 runs on GPUs with architecture Mx. + # This is a tradeoff between binary size / build time and runtime on + # older architectures. See: + # https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#building-for-maximum-compatibility + # https://docs.nvidia.com/cuda/ampere-compatibility-guide/index.html#application-compatibility-on-ampere + # https://en.wikipedia.org/wiki/CUDA#GPUs_supported + find_package(CUDAToolkit REQUIRED) + if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.8") + set(CMAKE_CUDA_ARCHITECTURES 75-real 80-real 86-real 89-real 90) # Turing, Ampere, Ada Lovelace, Hopper + elseif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.1") + set(CMAKE_CUDA_ARCHITECTURES 70-real 75-real 80-real 86) # Volta, Turing, Ampere + elseif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.0") + set(CMAKE_CUDA_ARCHITECTURES 60-real 70-real 72-real 75-real 80) # Pascal, Volta, Turing, Ampere + else() + set(CMAKE_CUDA_ARCHITECTURES 30-real 50-real 60-real 70-real 75) # Kepler, Maxwell, Pascal, Turing + endif() + message(STATUS "Using CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}") + endif() + else() + execute_process(COMMAND nvidia-smi RESULT_VARIABLE NVIDIA_CHECK OUTPUT_QUIET) + if (NVIDIA_CHECK EQUAL 0) + message(STATUS "Building with native CUDA architecture.") + set(CMAKE_CUDA_ARCHITECTURES native) + else() + message(WARNING "No CUDA GPU detected. Building with CMake default CUDA architecture.") + endif() + endif() enable_language(CUDA) - if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS "10.1") message(FATAL_ERROR "CUDA 10.0 and older are not supported. Please upgrade to CUDA 10.1 or newer.") endif() -endif () +endif() # ISPC language emulation support include(Open3DISPC) @@ -488,6 +516,12 @@ macro(add_source_group module_name) source_group("Source Files\\Material" FILES ${MODULE_MATERIAL_FILES}) endmacro() +if (LINUX_AARCH64) +# Fix for ImportError: ... /pybind.cpython-310-aarch64-linux-gnu.so: cannot allocate memory in static TLS block +# https://bugs.launchpad.net/ubuntu/+source/mysql-8.0/+bug/1889851 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftls-model=global-dynamic") +endif() + # Include convenience functions include(Open3DLink3rdpartyLibraries) include(Open3DSetGlobalProperties) diff --git a/cmake/Open3DMakeCudaArchitectures.cmake b/cmake/Open3DMakeCudaArchitectures.cmake deleted file mode 100644 index 1f09241bb89..00000000000 --- a/cmake/Open3DMakeCudaArchitectures.cmake +++ /dev/null @@ -1,78 +0,0 @@ -# open3d_make_cuda_architectures(cuda_archs) -# -# Sets up CUDA architectures based on the following precedence rules -# and stores them into the variable. -# 1. All common architectures if BUILD_COMMON_CUDA_ARCHS=ON -# 2. User-defined architectures -# 3. Architectures detected on the current machine -# 4. CMake's default architectures -function(open3d_make_cuda_architectures cuda_archs) - unset(${cuda_archs}) - - find_package(CUDAToolkit REQUIRED) - - if(BUILD_COMMON_CUDA_ARCHS) - # Build with all supported architectures for previous 2 generations and - # M0 (minor=0) architectures for previous generations (including - # deprecated). Note that cubin for M0 runs on GPUs with architecture Mx. - # This is a tradeoff between binary size / build time and runtime on - # older architectures. See: - # https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#building-for-maximum-compatibility - # https://docs.nvidia.com/cuda/ampere-compatibility-guide/index.html#application-compatibility-on-ampere - # https://github.com/Kitware/CMake/blob/master/Modules/FindCUDA/select_compute_arch.cmake - if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.1") - set(${cuda_archs} 60-real 70-real 72-real 75-real 80-real 86) - elseif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.0") - set(${cuda_archs} 60-real 70-real 72-real 75-real 80) - else() - set(${cuda_archs} 30-real 50-real 60-real 70-real 72-real 75) - endif() - else() - if(CMAKE_CUDA_ARCHITECTURES) - set(${cuda_archs} ${CMAKE_CUDA_ARCHITECTURES}) - message(STATUS "Building with user-provided architectures") - else() - file(WRITE - "${CMAKE_CURRENT_BINARY_DIR}/cuda_architectures.c" - " - #include - #include - int main() { - int n; - if (cudaGetDeviceCount(&n) == cudaSuccess) { - for (int i = 0; i < n; ++i) { - int major, minor; - if (cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, - i) == cudaSuccess && - cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, - i) == cudaSuccess) { - if (i > 0) { - printf(\";\"); - } - printf(\"%d%d-real\", major, minor); - } - } - } - return 0; - } - ") - - try_run( - DETECTION_RETURN_VALUE DETECTION_COMPILED - "${CMAKE_CURRENT_BINARY_DIR}" - "${CMAKE_CURRENT_BINARY_DIR}/cuda_architectures.c" - LINK_LIBRARIES CUDA::cudart - RUN_OUTPUT_VARIABLE DETECTED_ARCHITECTURES) - - if(DETECTED_ARCHITECTURES) - message(STATUS "Building with detected architectures") - set(${cuda_archs} ${DETECTED_ARCHITECTURES}) - else() - message(STATUS "Failed to detect architectures. Falling back to CMake's default architectures") - endif() - endif() - endif() - - set(${cuda_archs} ${${cuda_archs}} PARENT_SCOPE) - -endfunction() diff --git a/cpp/pybind/CMakeLists.txt b/cpp/pybind/CMakeLists.txt index 2072615e6c9..11c4d3ea16a 100644 --- a/cpp/pybind/CMakeLists.txt +++ b/cpp/pybind/CMakeLists.txt @@ -84,8 +84,7 @@ set_target_properties(pybind PROPERTIES # libc++.so is a linker script including libc++.so.1 and libc++abi.so, so append 1 to libc++.so set(PYTHON_EXTRA_LIBRARIES "") if (BUILD_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") - list(APPEND PYTHON_EXTRA_LIBRARIES ${CPP_LIBRARY}.1 ${CPPABI_LIBRARY} - ${MESA_CPU_GL_LIBRARY}) + list(APPEND PYTHON_EXTRA_LIBRARIES ${CPP_LIBRARY}.1 ${CPPABI_LIBRARY}) endif() if (WITH_OPENMP AND APPLE AND NOT BUILD_SHARED_LIBS) # Package libomp v11.1.0, if it is not installed. Later version cause crash on diff --git a/docker/Dockerfile.wheel b/docker/Dockerfile.wheel index a719262ece2..8faa243bf26 100644 --- a/docker/Dockerfile.wheel +++ b/docker/Dockerfile.wheel @@ -1,5 +1,5 @@ # FROM must be called before other ARGS except for ARG BASE_IMAGE -ARG BASE_IMAGE=nvidia/cuda:11.7.1-cudnn8-devel-ubuntu18.04 +ARG BASE_IMAGE=nvidia/cuda:11.7.1-cudnn8-devel-ubuntu20.04 FROM ${BASE_IMAGE} # Customizable build arguments from cuda.yml diff --git a/docker/docker_build.sh b/docker/docker_build.sh index 2f767884f1a..bc150b06e11 100755 --- a/docker/docker_build.sh +++ b/docker/docker_build.sh @@ -8,9 +8,9 @@ # Guidelines: # - Use a flat list of options. # We don't want to have a cartesian product of different combinations of -# options. E.g., to support Ubuntu {18.04, 20.04} with Python {3.7, 3.8}, we +# options. E.g., to support Ubuntu {20.04, 24.04} with Python {3.7, 3.8}, we # don't specify the OS and Python version separately, instead, we have a flat -# list of combinations: [u1804_py37, u1804_py38, u2004_py37, u2004_py38]. +# list of combinations: [u2004_py39, u2004_py310, u2404_py39, u2404_py310]. # - No external environment variables. # This script should not make assumptions on external environment variables. # This make the Docker image reproducible across different machines. @@ -54,12 +54,12 @@ OPTION: sycl-static : SYCL (oneAPI) with static lib # ML CIs (Dockerfile.ci) - 2-bionic : CUDA CI, 2-bionic, developer mode - 3-ml-shared-bionic-release : CUDA CI, 3-ml-shared-bionic (pre_cxx11_abi), release mode - 3-ml-shared-bionic : CUDA CI, 3-ml-shared-bionic (pre_cxx11_abi), developer mode - 4-shared-bionic : CUDA CI, 4-shared-bionic (cxx11_abi), developer mode - 4-shared-bionic-release : CUDA CI, 4-shared-bionic (cxx11_abi), release mode - 5-ml-focal : CUDA CI, 5-ml-focal, developer mode + 2-focal : CUDA CI, 2-bionic, developer mode + 3-ml-shared-focal-release : CUDA CI, 3-ml-shared-bionic (pre_cxx11_abi), release mode + 3-ml-shared-focal : CUDA CI, 3-ml-shared-bionic (pre_cxx11_abi), developer mode + 4-shared-focal : CUDA CI, 4-shared-bionic (cxx11_abi), developer mode + 4-shared-focal-release : CUDA CI, 4-shared-bionic (cxx11_abi), release mode + 5-ml-jammy : CUDA CI, 5-ml-focal, developer mode # CUDA wheels (Dockerfile.wheel) cuda_wheel_py38_dev : CUDA Python 3.8 wheel, developer mode @@ -76,9 +76,10 @@ HOST_OPEN3D_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. >/dev/null 2>&1 && pw # Shared variables CCACHE_VERSION=4.3 -CMAKE_VERSION=cmake-3.22.5-linux-x86_64 -CMAKE_VERSION_AARCH64=cmake-3.22.5-linux-aarch64 +CMAKE_VERSION=cmake-3.24.4-linux-x86_64 +CMAKE_VERSION_AARCH64=cmake-3.24.4-linux-aarch64 CUDA_VERSION=11.7.1-cudnn8 +CUDA_VERSION_LATEST=11.8.0-cudnn8 print_usage_and_exit_docker_build() { echo "$__usage_docker_build" @@ -101,13 +102,13 @@ openblas_export_env() { if [[ "amd64" =~ ^($options)$ ]]; then echo "[openblas_export_env()] platform AMD64" export DOCKER_TAG=open3d-ci:openblas-amd64 - export BASE_IMAGE=ubuntu:18.04 + export BASE_IMAGE=ubuntu:20.04 export CONDA_SUFFIX=x86_64 export CMAKE_VERSION=${CMAKE_VERSION} elif [[ "arm64" =~ ^($options)$ ]]; then echo "[openblas_export_env()] platform ARM64" export DOCKER_TAG=open3d-ci:openblas-arm64 - export BASE_IMAGE=arm64v8/ubuntu:18.04 + export BASE_IMAGE=arm64v8/ubuntu:20.04 export CONDA_SUFFIX=aarch64 export CMAKE_VERSION=${CMAKE_VERSION_AARCH64} else @@ -153,7 +154,6 @@ openblas_build() { pushd "${HOST_OPEN3D_ROOT}" docker build \ - --progress plain \ --build-arg BASE_IMAGE="${BASE_IMAGE}" \ --build-arg CONDA_SUFFIX="${CONDA_SUFFIX}" \ --build-arg CMAKE_VERSION="${CMAKE_VERSION}" \ @@ -169,8 +169,8 @@ openblas_build() { } cuda_wheel_build() { - BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu18.04 - CCACHE_TAR_NAME=open3d-ubuntu-1804-cuda-ci-ccache + BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 + CCACHE_TAR_NAME=open3d-ubuntu-2004-cuda-ci-ccache options="$(echo "$@" | tr ' ' '|')" echo "[cuda_wheel_build()] options: ${options}" @@ -198,7 +198,6 @@ cuda_wheel_build() { pushd "${HOST_OPEN3D_ROOT}" docker build \ - --progress plain \ --build-arg BASE_IMAGE="${BASE_IMAGE}" \ --build-arg DEVELOPER_BUILD="${DEVELOPER_BUILD}" \ --build-arg CCACHE_TAR_NAME="${CCACHE_TAR_NAME}" \ @@ -237,7 +236,6 @@ ci_build() { pushd "${HOST_OPEN3D_ROOT}" docker build \ - --progress plain \ --build-arg BASE_IMAGE="${BASE_IMAGE}" \ --build-arg DEVELOPER_BUILD="${DEVELOPER_BUILD}" \ --build-arg CCACHE_TAR_NAME="${CCACHE_TAR_NAME}" \ @@ -260,12 +258,12 @@ ci_build() { && chown $(id -u):$(id -g) /opt/mount/open3d*" } -2-bionic_export_env() { - export DOCKER_TAG=open3d-ci:2-bionic +2-focal_export_env() { + export DOCKER_TAG=open3d-ci:2-focal - export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu18.04 + export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 export DEVELOPER_BUILD=ON - export CCACHE_TAR_NAME=open3d-ci-2-bionic + export CCACHE_TAR_NAME=open3d-ci-2-focal export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=OFF export BUILD_CUDA_MODULE=ON @@ -275,12 +273,12 @@ ci_build() { export BUILD_SYCL_MODULE=OFF } -3-ml-shared-bionic_export_env() { - export DOCKER_TAG=open3d-ci:3-ml-shared-bionic +3-ml-shared-focal_export_env() { + export DOCKER_TAG=open3d-ci:3-ml-shared-focal - export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu18.04 + export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 export DEVELOPER_BUILD=ON - export CCACHE_TAR_NAME=open3d-ci-3-ml-shared-bionic + export CCACHE_TAR_NAME=open3d-ci-3-ml-shared-focal export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON @@ -291,12 +289,12 @@ ci_build() { export BUILD_SYCL_MODULE=OFF } -3-ml-shared-bionic-release_export_env() { - export DOCKER_TAG=open3d-ci:3-ml-shared-bionic +3-ml-shared-focal-release_export_env() { + export DOCKER_TAG=open3d-ci:3-ml-shared-focal - export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu18.04 + export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 export DEVELOPER_BUILD=OFF - export CCACHE_TAR_NAME=open3d-ci-3-ml-shared-bionic + export CCACHE_TAR_NAME=open3d-ci-3-ml-shared-focal export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON @@ -307,12 +305,12 @@ ci_build() { export BUILD_SYCL_MODULE=OFF } -4-shared-bionic_export_env() { - export DOCKER_TAG=open3d-ci:4-shared-bionic +4-shared-focal_export_env() { + export DOCKER_TAG=open3d-ci:4-shared-focal - export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu18.04 + export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 export DEVELOPER_BUILD=ON - export CCACHE_TAR_NAME=open3d-ci-4-shared-bionic + export CCACHE_TAR_NAME=open3d-ci-4-shared-focal export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON @@ -323,12 +321,12 @@ ci_build() { export BUILD_SYCL_MODULE=OFF } -4-shared-bionic-release_export_env() { - export DOCKER_TAG=open3d-ci:4-shared-bionic +4-shared-focal-release_export_env() { + export DOCKER_TAG=open3d-ci:4-shared-focal - export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu18.04 + export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 export DEVELOPER_BUILD=OFF - export CCACHE_TAR_NAME=open3d-ci-4-shared-bionic + export CCACHE_TAR_NAME=open3d-ci-4-shared-focal export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON @@ -339,12 +337,12 @@ ci_build() { export BUILD_SYCL_MODULE=OFF } -5-ml-focal_export_env() { - export DOCKER_TAG=open3d-ci:5-ml-focal +5-ml-jammy_export_env() { + export DOCKER_TAG=open3d-ci:5-ml-jammy - export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 + export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION_LATEST}-devel-ubuntu22.04 export DEVELOPER_BUILD=ON - export CCACHE_TAR_NAME=open3d-ci-5-ml-focal + export CCACHE_TAR_NAME=open3d-ci-5-ml-jammy export PYTHON_VERSION=3.8 export BUILD_SHARED_LIBS=OFF export BUILD_CUDA_MODULE=ON @@ -358,7 +356,7 @@ ci_build() { cpu-static_export_env() { export DOCKER_TAG=open3d-ci:cpu-static - export BASE_IMAGE=ubuntu:18.04 + export BASE_IMAGE=ubuntu:20.04 export DEVELOPER_BUILD=ON export CCACHE_TAR_NAME=open3d-ci-cpu export PYTHON_VERSION=3.8 @@ -373,7 +371,7 @@ cpu-static_export_env() { cpu-shared_export_env() { export DOCKER_TAG=open3d-ci:cpu-shared - export BASE_IMAGE=ubuntu:18.04 + export BASE_IMAGE=ubuntu:20.04 export DEVELOPER_BUILD=ON export CCACHE_TAR_NAME=open3d-ci-cpu export PYTHON_VERSION=3.8 @@ -389,7 +387,7 @@ cpu-shared_export_env() { cpu-shared-ml_export_env() { export DOCKER_TAG=open3d-ci:cpu-shared-ml - export BASE_IMAGE=ubuntu:18.04 + export BASE_IMAGE=ubuntu:20.04 export DEVELOPER_BUILD=ON export CCACHE_TAR_NAME=open3d-ci-cpu export PYTHON_VERSION=3.8 @@ -405,7 +403,7 @@ cpu-shared-ml_export_env() { cpu-shared-release_export_env() { export DOCKER_TAG=open3d-ci:cpu-shared - export BASE_IMAGE=ubuntu:18.04 + export BASE_IMAGE=ubuntu:20.04 export DEVELOPER_BUILD=OFF export CCACHE_TAR_NAME=open3d-ci-cpu export PYTHON_VERSION=3.8 @@ -421,7 +419,7 @@ cpu-shared-release_export_env() { cpu-shared-ml-release_export_env() { export DOCKER_TAG=open3d-ci:cpu-shared-ml - export BASE_IMAGE=ubuntu:18.04 + export BASE_IMAGE=ubuntu:20.04 export DEVELOPER_BUILD=OFF export CCACHE_TAR_NAME=open3d-ci-cpu export PYTHON_VERSION=3.8 @@ -438,7 +436,7 @@ sycl-shared_export_env() { export DOCKER_TAG=open3d-ci:sycl-shared # https://hub.docker.com/r/intel/oneapi-basekit - # https://github.com/intel/oneapi-containers/blob/main/images/docker/basekit/Dockerfile.ubuntu-18.04 + # https://github.com/intel/oneapi-containers/blob/main/images/docker/basekit/Dockerfile.ubuntu-20.04 export BASE_IMAGE=intel/oneapi-basekit:2022.2-devel-ubuntu20.04 export DEVELOPER_BUILD=ON export CCACHE_TAR_NAME=open3d-ci-sycl @@ -455,7 +453,7 @@ sycl-static_export_env() { export DOCKER_TAG=open3d-ci:sycl-static # https://hub.docker.com/r/intel/oneapi-basekit - # https://github.com/intel/oneapi-containers/blob/main/images/docker/basekit/Dockerfile.ubuntu-18.04 + # https://github.com/intel/oneapi-containers/blob/main/images/docker/basekit/Dockerfile.ubuntu-20.04 export BASE_IMAGE=intel/oneapi-basekit:2022.2-devel-ubuntu20.04 export DEVELOPER_BUILD=ON export CCACHE_TAR_NAME=open3d-ci-sycl @@ -602,28 +600,28 @@ function main() { ;; # ML CIs - 2-bionic) - 2-bionic_export_env + 2-focal) + 2-focal_export_env ci_build ;; - 3-ml-shared-bionic-release) - 3-ml-shared-bionic-release_export_env + 3-ml-shared-focal-release) + 3-ml-shared-focal-release_export_env ci_build ;; - 3-ml-shared-bionic) - 3-ml-shared-bionic_export_env + 3-ml-shared-focal) + 3-ml-shared-focal_export_env ci_build ;; - 4-shared-bionic-release) - 4-shared-bionic-release_export_env + 4-shared-focal-release) + 4-shared-focal-release_export_env ci_build ;; - 4-shared-bionic) - 4-shared-bionic_export_env + 4-shared-focal) + 4-shared-focal_export_env ci_build ;; - 5-ml-focal) - 5-ml-focal_export_env + 5-ml-jammy) + 5-ml-jammy_export_env ci_build ;; *) diff --git a/docker/docker_test.sh b/docker/docker_test.sh index 0f1c18d9490..441b17ed4e3 100755 --- a/docker/docker_test.sh +++ b/docker/docker_test.sh @@ -47,12 +47,12 @@ OPTION: sycl-static : SYCL (oneAPI) with static lib # ML CIs (Dockerfile.ci) - 2-bionic : CUDA CI, 2-bionic, developer mode - 3-ml-shared-bionic-release : CUDA CI, 3-ml-shared-bionic, release mode - 3-ml-shared-bionic : CUDA CI, 3-ml-shared-bionic, developer mode - 4-shared-bionic : CUDA CI, 4-shared-bionic, developer mode - 4-shared-bionic-release : CUDA CI, 4-shared-bionic, release mode - 5-ml-focal : CUDA CI, 5-ml-focal, developer mode + 2-focal : CUDA CI, 2-focal, developer mode + 3-ml-shared-focal-release : CUDA CI, 3-ml-shared-focal, release mode + 3-ml-shared-focal : CUDA CI, 3-ml-shared-focal, developer mode + 4-shared-focal : CUDA CI, 4-shared-focal, developer mode + 4-shared-focal-release : CUDA CI, 4-shared-focal, release mode + 5-ml-jammy : CUDA CI, 5-ml-jammy, developer mode " HOST_OPEN3D_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. >/dev/null 2>&1 && pwd)" @@ -331,33 +331,33 @@ sycl-static) ;; # ML CIs -2-bionic) - 2-bionic_export_env +2-focal) + 2-focal_export_env ci_print_env cpp_python_linking_uninstall_test ;; -3-ml-shared-bionic) - 3-ml-shared-bionic_export_env +3-ml-shared-focal) + 3-ml-shared-focal_export_env ci_print_env cpp_python_linking_uninstall_test ;; -3-ml-shared-bionic-release) - 3-ml-shared-bionic-release_export_env +3-ml-shared-focal-release) + 3-ml-shared-focal-release_export_env ci_print_env cpp_python_linking_uninstall_test ;; -4-shared-bionic) - 4-shared-bionic_export_env +4-shared-focal) + 4-shared-focal_export_env ci_print_env cpp_python_linking_uninstall_test ;; -4-shared-bionic-release) - 4-shared-bionic-release_export_env +4-shared-focal-release) + 4-shared-focal-release_export_env ci_print_env cpp_python_linking_uninstall_test ;; -5-ml-focal) - 5-ml-focal_export_env +5-ml-jammy) + 5-ml-jammy_export_env ci_print_env cpp_python_linking_uninstall_test ;; diff --git a/docs/docker.in.rst b/docs/docker.in.rst index 3647b99d48f..17c7055b809 100644 --- a/docs/docker.in.rst +++ b/docs/docker.in.rst @@ -139,46 +139,7 @@ driver with an environment variable (``EGL_PLATFORM=surfaceless``): # Run headless rendering example without GPU (CPU rendering) docker run -v "$PWD":/root open3d-headless:latest -In Ubuntu 18.04, we need to install some additional dependencies. Here is an -example Ubuntu / Debian based docker file that runs the ``render_to_image.py`` -rendering example. Other (old) Linux (e.g. RHEL) distributions will need -different dependency packages. - -.. code-block:: bash - - mkdir open3d-headless-docker && cd open3d-headless-docker - wget https://raw.githubusercontent.com/isl-org/Open3D/v@OPEN3D_VERSION@/examples/python/visualization/render_to_image.py - # Build docker image - docker build -t open3d-headless -f- . <`__. - This is automatically downloaded to - `build/_deps/download_mesa_libgl-src/libGL.so.1.5.0` when you build Open3D - from source. The prebuilt version works on Ubuntu 18.04 and Ubuntu 20.04. If - you want to use CPU rendering all the time, install this library to - ``/usr/local/lib`` or ``$HOME/.local/lib`` and *prepend* it to your - ``LD_LIBRARY_PATH``: - - .. code:: bash - - export LD_LIBRARY_PATH=$HOME/.local/lib:$LD_LIBRARY_PATH - - For occasional use, you can instead launch a program with CPU rendering with: - - .. code:: bash - - LD_PRELOAD=$HOME/.local/lib/libGL.so.1.5.0 Open3D - - Or with Python code: - - .. code:: bash - - LD_PRELOAD=$HOME/.local/lib/libGL.so.1.5.0 python examples/python/visualization/draw.py + python examples/python/visualization/draw.py \ No newline at end of file diff --git a/python/open3d/__init__.py b/python/open3d/__init__.py index ef0adba1bd5..e67c146a949 100644 --- a/python/open3d/__init__.py +++ b/python/open3d/__init__.py @@ -46,13 +46,6 @@ def load_cdll(path): except StopIteration: # Not found: check system paths while loading pass -# Enable CPU rendering based on env vars -if _build_config["BUILD_GUI"] and sys.platform.startswith("linux") and ( - os.getenv("OPEN3D_CPU_RENDERING", default="") == "true"): - os.environ["LIBGL_DRIVERS_PATH"] = str(Path(__file__).parent) - load_cdll(Path(__file__).parent / "libEGL.so.1") - load_cdll(Path(__file__).parent / "libGL.so.1") - __DEVICE_API__ = "cpu" if _build_config["BUILD_CUDA_MODULE"]: # Load CPU pybind dll gracefully without introducing new python variable. diff --git a/util/ci_utils.sh b/util/ci_utils.sh index 52db35f727a..93e48c2e463 100644 --- a/util/ci_utils.sh +++ b/util/ci_utils.sh @@ -27,9 +27,6 @@ LOW_MEM_USAGE=${LOW_MEM_USAGE:-OFF} # ML TENSORFLOW_VER="2.13.0" TORCH_VER="2.0.1" -TORCH_CPU_GLNX_VER="${TORCH_VER}+cpu" -TORCH_CUDA_GLNX_VER="${TORCH_VER}+cu117" # match CUDA_VERSION in docker/docker_build.sh -TORCH_MACOS_VER="${TORCH_VER}" TORCH_REPO_URL="https://download.pytorch.org/whl/torch/" # Python PIP_VER="23.2.1" @@ -53,7 +50,8 @@ install_python_dependencies() { if [[ "with-cuda" =~ ^($options)$ ]]; then TF_ARCH_NAME=tensorflow TF_ARCH_DISABLE_NAME=tensorflow-cpu - TORCH_GLNX="torch==$TORCH_CUDA_GLNX_VER" + CUDA_VER=$(nvcc --version | grep "release " | cut -c33-37 | sed 's|[^0-9]||g') # e.g.: 117, 118, 121, ... + TORCH_GLNX="torch==${TORCH_VER}+cu${CUDA_VER}" else # tensorflow-cpu wheels for macOS arm64 are not available if [[ "$OSTYPE" == "darwin"* ]]; then @@ -63,7 +61,7 @@ install_python_dependencies() { TF_ARCH_NAME=tensorflow-cpu TF_ARCH_DISABLE_NAME=tensorflow fi - TORCH_GLNX="torch==$TORCH_CPU_GLNX_VER" + TORCH_GLNX="torch==${TORCH_VER}+cpu" fi # TODO: modify other locations to use requirements.txt @@ -83,7 +81,7 @@ install_python_dependencies() { python -m pip install -U "${TORCH_GLNX}" -f "$TORCH_REPO_URL" tensorboard elif [[ "$OSTYPE" == "darwin"* ]]; then - python -m pip install -U torch=="$TORCH_MACOS_VER" -f "$TORCH_REPO_URL" tensorboard + python -m pip install -U torch=="$TORCH_VER" -f "$TORCH_REPO_URL" tensorboard else echo "unknown OS $OSTYPE" exit 1 @@ -251,8 +249,8 @@ test_wheel() { python -m pip --version echo "Installing Open3D wheel $wheel_path in virtual environment..." python -m pip install "$wheel_path" - python -c "import open3d; print('Installed:', open3d); print('BUILD_CUDA_MODULE: ', open3d._build_config['BUILD_CUDA_MODULE'])" - python -c "import open3d; print('CUDA available: ', open3d.core.cuda.is_available())" + python -W default -c "import open3d; print('Installed:', open3d); print('BUILD_CUDA_MODULE: ', open3d._build_config['BUILD_CUDA_MODULE'])" + python -W default -c "import open3d; print('CUDA available: ', open3d.core.cuda.is_available())" echo # echo "Dynamic libraries used:" # DLL_PATH=$(dirname $(python -c "import open3d; print(open3d.cpu.pybind.__file__)"))/.. @@ -262,27 +260,21 @@ test_wheel() { # find "$DLL_PATH"/cpu/ -type f -execdir otool -L {} \; # fi echo - # FIXME: Needed because Open3D-ML main TF and PyTorch is older than dev. - if [ $BUILD_CUDA_MODULE == ON ]; then - install_python_dependencies with-cuda - else - install_python_dependencies - fi if [ "$BUILD_PYTORCH_OPS" == ON ]; then - # python -m pip install -r "$OPEN3D_ML_ROOT/requirements-torch.txt" - python -c \ + python -m pip install -r "$OPEN3D_ML_ROOT/requirements-torch.txt" + python -W default -c \ "import open3d.ml.torch; print('PyTorch Ops library loaded:', open3d.ml.torch._loaded)" fi if [ "$BUILD_TENSORFLOW_OPS" == ON ]; then - # python -m pip install -r "$OPEN3D_ML_ROOT/requirements-tensorflow.txt" - python -c \ + python -m pip install -r "$OPEN3D_ML_ROOT/requirements-tensorflow.txt" + python -W default -c \ "import open3d.ml.tf.ops; print('TensorFlow Ops library loaded:', open3d.ml.tf.ops)" fi if [ "$BUILD_TENSORFLOW_OPS" == ON ] && [ "$BUILD_PYTORCH_OPS" == ON ]; then echo "Importing TensorFlow and torch in the reversed order" - python -c "import tensorflow as tf; import torch; import open3d.ml.torch as o3d" + python -W default -c "import tensorflow as tf; import torch; import open3d.ml.torch as o3d" echo "Importing TensorFlow and torch in the normal order" - python -c "import open3d.ml.torch as o3d; import tensorflow as tf; import torch" + python -W default -c "import open3d.ml.torch as o3d; import tensorflow as tf; import torch" fi deactivate open3d_test.venv # argument prevents unbound variable error } @@ -341,14 +333,14 @@ test_cpp_example() { # Now I am in Open3D/build/ } -# Install dependencies needed for building documentation (on Ubuntu 18.04) +# Install dependencies needed for building documentation (on Ubuntu 20.04) # Usage: install_docs_dependencies "${OPEN3D_ML_ROOT}" install_docs_dependencies() { echo echo Install ubuntu dependencies - echo Update cmake needed in Ubuntu 18.04 + echo Update cmake needed in Ubuntu 20.04 sudo apt-key adv --fetch-keys https://apt.kitware.com/keys/kitware-archive-latest.asc - sudo apt-add-repository --yes 'deb https://apt.kitware.com/ubuntu/ bionic main' + sudo apt-add-repository --yes 'deb https://apt.kitware.com/ubuntu/ focal main' ./util/install_deps_ubuntu.sh assume-yes sudo apt-get install --yes cmake sudo apt-get install --yes libxml2-dev libxslt-dev python3-dev diff --git a/util/install_deps_ubuntu.sh b/util/install_deps_ubuntu.sh index a2a3ad6e4d3..1613372b2ce 100755 --- a/util/install_deps_ubuntu.sh +++ b/util/install_deps_ubuntu.sh @@ -38,12 +38,12 @@ eval $( echo DISTRIB_ID="$DISTRIB_ID"; echo DISTRIB_RELEASE="$DISTRIB_RELEASE" ) -if [ "$DISTRIB_ID" == "Ubuntu" -a "$DISTRIB_RELEASE" == "18.04" ]; then - # Ubuntu 18.04's clang/libc++-dev/libc++abi-dev are version 6. - # To build Filament from source, we need version 7+. - deps=("${deps[@]/clang/clang-7}") - deps=("${deps[@]/libc++-dev/libc++-7-dev}") - deps=("${deps[@]/libc++abi-dev/libc++abi-7-dev}") +if [ "$DISTRIB_ID" == "Ubuntu" -a "$DISTRIB_RELEASE" == "20.04" ]; then + # Ubuntu 20.04's clang/libc++-dev/libc++abi-dev are version 10. + # To build Filament from source, we need version 12+. + deps=("${deps[@]/clang/clang-12}") + deps=("${deps[@]/libc++-dev/libc++-12-dev}") + deps=("${deps[@]/libc++abi-dev/libc++abi-12-dev}") fi # Special case for ARM64 From 9e1b5ae68f94c7f26dfc5f796f2eb55bde00c193 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Wed, 14 Aug 2024 06:12:52 -0700 Subject: [PATCH 49/82] use c++17 if pytorch ops are enabled --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e4781e7e72..57751184320 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -291,8 +291,8 @@ endif() # Global flag to set CXX standard. # This does not affect 3rd party libraries. # Tensorflow 2.9+ requires cxx_17, but MSVC 19.29 throws errors with C++17 -# enabled. -if (BUILD_SYCL_MODULE OR BUILD_TENSORFLOW_OPS) +# enabled. ATen in Pytorch 2.2 requires cxx_17 +if (BUILD_SYCL_MODULE OR BUILD_TENSORFLOW_OPS OR BUILD_PYTORCH_OPS) set(CMAKE_CXX_STANDARD 17) else() set(CMAKE_CXX_STANDARD 14) From 1ff496f602733a3cbdf5f44d1ae9e1737749164c Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Wed, 14 Aug 2024 06:40:05 -0700 Subject: [PATCH 50/82] update url for Windows CUDA installer --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 65e1b216e1f..807bc5be5be 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -70,7 +70,7 @@ jobs: $CUDA_VER_ID = "$($CUDA_VER_ARR[0])_$($CUDA_VER_ARR[1])" # Installer url if ( $CUDA_VER_ARR[0] -ge 11 ) { - $CUDA_URL = "http://developer.download.nvidia.com/compute/cuda/$CUDA_VER_FULL/network_installers/cuda_$($CUDA_VER_FULL)_win10_network.exe" + $CUDA_URL = "http://developer.download.nvidia.com/compute/cuda/$CUDA_VER_FULL/network_installers/cuda_$($CUDA_VER_FULL)_windows_network.exe" } else { $CUDA_URL = "http://developer.download.nvidia.com/compute/cuda/$CUDA_VER/Prod/network_installers/cuda_$($CUDA_VER_FULL)_win10_network.exe" } From 7f9377d91fabb9b1c219773931a81ceeeeb95738 Mon Sep 17 00:00:00 2001 From: daizhirui Date: Wed, 14 Aug 2024 10:26:24 -0700 Subject: [PATCH 51/82] upgrade embree to v4.3.3 and fix fmt formatter for RTCError (#6901) * use rtcGetErrorString instead * upgrade embree to v4.3.3 --- 3rdparty/embree/embree.cmake | 4 ++-- cpp/open3d/t/geometry/RaycastingScene.cpp | 26 +---------------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/3rdparty/embree/embree.cmake b/3rdparty/embree/embree.cmake index 35d27f11048..2495c987bbd 100644 --- a/3rdparty/embree/embree.cmake +++ b/3rdparty/embree/embree.cmake @@ -67,8 +67,8 @@ endif() ExternalProject_Add( ext_embree PREFIX embree - URL https://github.com/embree/embree/archive/refs/tags/v4.3.1.tar.gz - URL_HASH SHA256=824edcbb7a8cd393c5bdb7a16738487b21ecc4e1d004ac9f761e934f97bb02a4 + URL https://github.com/embree/embree/archive/refs/tags/v4.3.3.tar.gz + URL_HASH SHA256=8a3bc3c3e21aa209d9861a28f8ba93b2f82ed0dc93341dddac09f1f03c36ef2d DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/embree" UPDATE_COMMAND "" CMAKE_ARGS diff --git a/cpp/open3d/t/geometry/RaycastingScene.cpp b/cpp/open3d/t/geometry/RaycastingScene.cpp index 8906f6373e5..31fdbeef856 100644 --- a/cpp/open3d/t/geometry/RaycastingScene.cpp +++ b/cpp/open3d/t/geometry/RaycastingScene.cpp @@ -1180,31 +1180,7 @@ template <> struct formatter { template auto format(const RTCError& c, FormatContext& ctx) { - const char* name = nullptr; - switch (c) { - case RTC_ERROR_NONE: - name = "RTC_ERROR_NONE"; - break; - case RTC_ERROR_UNKNOWN: - name = "RTC_ERROR_UNKNOWN"; - break; - case RTC_ERROR_INVALID_ARGUMENT: - name = "RTC_ERROR_INVALID_ARGUMENT"; - break; - case RTC_ERROR_INVALID_OPERATION: - name = "RTC_ERROR_INVALID_OPERATION"; - break; - case RTC_ERROR_OUT_OF_MEMORY: - name = "RTC_ERROR_OUT_OF_MEMORY"; - break; - case RTC_ERROR_UNSUPPORTED_CPU: - name = "RTC_ERROR_UNSUPPORTED_CPU"; - break; - case RTC_ERROR_CANCELLED: - name = "RTC_ERROR_CANCELLED"; - break; - } - // return formatter::format(name, ctx); + const char* name = rtcGetErrorString(c); return format_to(ctx.out(), name); } From 48ccf2a4ea0471919fae2cb42d8d0889bb9562e2 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Wed, 14 Aug 2024 22:01:17 +0200 Subject: [PATCH 52/82] Split pybind declarations and definitions (#6869) As explained in #6867 there are several instances where C++ types are shown in the Python binding documentation. Some of those instances can be solved by splitting the py::_class and py::enum_ declarations from the method/function definitions using .def(...). This ensures, that all types are properly declared before usage. --------- Co-authored-by: Sameer Sheorey --- CHANGELOG.md | 1 + cpp/pybind/camera/camera.cpp | 97 +- cpp/pybind/camera/camera.h | 3 +- cpp/pybind/core/blob.cpp | 4 +- cpp/pybind/core/core.cpp | 45 +- cpp/pybind/core/core.h | 39 +- cpp/pybind/core/cuda_utils.cpp | 6 +- cpp/pybind/core/device.cpp | 14 +- cpp/pybind/core/dtype.cpp | 8 +- cpp/pybind/core/hashmap.cpp | 12 +- cpp/pybind/core/kernel.cpp | 5 +- cpp/pybind/core/linalg.cpp | 2 +- .../core/nns/nearest_neighbor_search.cpp | 29 +- cpp/pybind/core/nns/nearest_neighbor_search.h | 3 +- cpp/pybind/core/scalar.cpp | 6 +- cpp/pybind/core/size_vector.cpp | 17 +- cpp/pybind/core/sycl_utils.cpp | 4 +- cpp/pybind/core/tensor.cpp | 6 +- cpp/pybind/core/tensor_function.cpp | 18 +- cpp/pybind/geometry/boundingvolume.cpp | 30 +- cpp/pybind/geometry/geometry.cpp | 134 +-- cpp/pybind/geometry/geometry.h | 46 +- cpp/pybind/geometry/halfedgetrianglemesh.cpp | 30 +- cpp/pybind/geometry/image.cpp | 28 +- cpp/pybind/geometry/kdtreeflann.cpp | 48 +- cpp/pybind/geometry/keypoint.cpp | 29 +- cpp/pybind/geometry/lineset.cpp | 10 +- cpp/pybind/geometry/meshbase.cpp | 18 +- cpp/pybind/geometry/octree.cpp | 106 ++- cpp/pybind/geometry/pointcloud.cpp | 11 +- cpp/pybind/geometry/tetramesh.cpp | 16 +- cpp/pybind/geometry/trianglemesh.cpp | 10 +- cpp/pybind/geometry/voxelgrid.cpp | 23 +- cpp/pybind/io/class_io.cpp | 4 +- cpp/pybind/io/io.cpp | 17 +- cpp/pybind/io/io.h | 16 +- cpp/pybind/io/rpc.cpp | 179 ++-- cpp/pybind/io/sensor.cpp | 38 +- cpp/pybind/ml/contrib/contrib.cpp | 10 +- cpp/pybind/ml/contrib/contrib.h | 8 +- cpp/pybind/ml/contrib/contrib_subsample.cpp | 2 +- cpp/pybind/ml/contrib/iou.cpp | 2 +- cpp/pybind/ml/ml.cpp | 9 +- cpp/pybind/ml/ml.h | 3 +- cpp/pybind/open3d_pybind.cpp | 29 +- cpp/pybind/pipelines/color_map/color_map.cpp | 57 +- cpp/pybind/pipelines/color_map/color_map.h | 5 +- .../pipelines/integration/integration.cpp | 102 ++- .../pipelines/integration/integration.h | 3 +- cpp/pybind/pipelines/odometry/odometry.cpp | 141 +-- cpp/pybind/pipelines/odometry/odometry.h | 3 +- cpp/pybind/pipelines/pipelines.cpp | 18 +- cpp/pybind/pipelines/pipelines.h | 5 +- cpp/pybind/pipelines/registration/feature.cpp | 39 +- .../registration/global_optimization.cpp | 121 ++- .../pipelines/registration/registration.cpp | 506 ++++++----- .../pipelines/registration/registration.h | 14 +- .../pipelines/registration/robust_kernels.cpp | 163 ++-- cpp/pybind/t/geometry/boundingvolume.cpp | 76 +- cpp/pybind/t/geometry/drawablegeometry.cpp | 9 +- cpp/pybind/t/geometry/geometry.cpp | 47 +- cpp/pybind/t/geometry/geometry.h | 35 +- cpp/pybind/t/geometry/image.cpp | 30 +- cpp/pybind/t/geometry/lineset.cpp | 7 +- cpp/pybind/t/geometry/pointcloud.cpp | 68 +- cpp/pybind/t/geometry/raycasting_scene.cpp | 44 +- cpp/pybind/t/geometry/tensormap.cpp | 11 +- cpp/pybind/t/geometry/trianglemesh.cpp | 8 +- cpp/pybind/t/geometry/voxel_block_grid.cpp | 7 +- cpp/pybind/t/io/class_io.cpp | 71 +- cpp/pybind/t/io/io.cpp | 11 +- cpp/pybind/t/io/io.h | 10 +- cpp/pybind/t/io/sensor.cpp | 206 +++-- cpp/pybind/t/pipelines/odometry/odometry.cpp | 259 +++--- cpp/pybind/t/pipelines/odometry/odometry.h | 3 +- cpp/pybind/t/pipelines/pipelines.cpp | 17 +- cpp/pybind/t/pipelines/pipelines.h | 5 +- .../t/pipelines/registration/feature.cpp | 25 +- .../t/pipelines/registration/registration.cpp | 283 +++--- .../t/pipelines/registration/registration.h | 8 +- .../pipelines/registration/robust_kernel.cpp | 24 +- cpp/pybind/t/pipelines/slac/slac.cpp | 139 +-- cpp/pybind/t/pipelines/slac/slac.h | 3 +- cpp/pybind/t/pipelines/slam/slam.cpp | 42 +- cpp/pybind/t/pipelines/slam/slam.h | 3 +- cpp/pybind/t/t.cpp | 16 +- cpp/pybind/t/t.h | 3 +- cpp/pybind/utility/eigen.cpp | 23 +- cpp/pybind/utility/logging.cpp | 19 +- cpp/pybind/utility/utility.cpp | 15 +- cpp/pybind/utility/utility.h | 10 +- cpp/pybind/visualization/app/viewer.cpp | 18 +- cpp/pybind/visualization/app/viewer.h | 3 +- cpp/pybind/visualization/gui/events.cpp | 239 ++--- cpp/pybind/visualization/gui/gui.cpp | 842 ++++++++++-------- cpp/pybind/visualization/gui/gui.h | 7 +- cpp/pybind/visualization/o3dvisualizer.cpp | 55 +- .../visualization/rendering/material.cpp | 8 +- cpp/pybind/visualization/rendering/material.h | 5 +- .../visualization/rendering/rendering.cpp | 264 +++--- .../visualization/rendering/rendering.h | 5 +- cpp/pybind/visualization/renderoption.cpp | 109 +-- cpp/pybind/visualization/utility.cpp | 65 +- cpp/pybind/visualization/viewcontrol.cpp | 9 +- cpp/pybind/visualization/visualization.cpp | 46 +- cpp/pybind/visualization/visualization.h | 24 +- cpp/pybind/visualization/visualizer.cpp | 63 +- .../webrtc_server/webrtc_window_system.cpp | 26 +- .../webrtc_server/webrtc_window_system.h | 3 +- docs/builddocs.rst | 19 + examples/python/benchmark/benchmark_fgr.py | 1 + examples/python/benchmark/benchmark_pre.py | 1 + examples/python/benchmark/benchmark_ransac.py | 1 + examples/python/benchmark/benchmark_tsdf.py | 2 +- 114 files changed, 3248 insertions(+), 2456 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed7a0c786cb..98836909e7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - Fix build with fmt v10.2.0 (#6783) - Fix segmentation fault (lambda reference capture) of VisualizerWithCustomAnimation::Play (PR #6804) - Add O3DVisualizer API to enable collapse control of verts in the side panel (PR #6865) +- Split pybind declarations/definitions to avoid C++ types in Python docs (PR #6869) - Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (PR #6898) ## 0.13 diff --git a/cpp/pybind/camera/camera.cpp b/cpp/pybind/camera/camera.cpp index fa4f201e70a..6f78b631f00 100644 --- a/cpp/pybind/camera/camera.cpp +++ b/cpp/pybind/camera/camera.cpp @@ -14,12 +14,48 @@ namespace open3d { namespace camera { -void pybind_camera_classes(py::module &m) { - // open3d.camera.PinholeCameraIntrinsic +void pybind_camera_declarations(py::module &m) { + py::module m_camera = m.def_submodule("camera"); py::class_ pinhole_intr( - m, "PinholeCameraIntrinsic", + m_camera, "PinholeCameraIntrinsic", "PinholeCameraIntrinsic class stores intrinsic camera matrix, and " "image height and width."); + // open3d.camera.PinholeCameraIntrinsicParameters + py::enum_ pinhole_intr_params( + m_camera, "PinholeCameraIntrinsicParameters", py::arithmetic(), + "PinholeCameraIntrinsicParameters"); + pinhole_intr_params + .value("PrimeSenseDefault", + PinholeCameraIntrinsicParameters::PrimeSenseDefault, + "Default camera intrinsic parameter for PrimeSense.") + .value("Kinect2DepthCameraDefault", + PinholeCameraIntrinsicParameters::Kinect2DepthCameraDefault, + "Default camera intrinsic parameter for Kinect2 depth " + "camera.") + .value("Kinect2ColorCameraDefault", + PinholeCameraIntrinsicParameters::Kinect2ColorCameraDefault, + "Default camera intrinsic parameter for Kinect2 color " + "camera.") + .export_values(); + pinhole_intr_params.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class that contains default camera intrinsic " + "parameters for different sensors."; + }), + py::none(), py::none(), ""); + py::class_ pinhole_param( + m_camera, "PinholeCameraParameters", + "Contains both intrinsic and extrinsic pinhole camera parameters."); + py::class_ pinhole_traj( + m_camera, "PinholeCameraTrajectory", + "Contains a list of ``PinholeCameraParameters``, useful to storing " + "trajectories."); +} +void pybind_camera_definitions(py::module &m) { + auto m_camera = static_cast(m.attr("camera")); + // open3d.camera.PinholeCameraIntrinsic + auto pinhole_intr = static_cast>( + m_camera.attr("PinholeCameraIntrinsic")); py::detail::bind_default_constructor(pinhole_intr); py::detail::bind_copy_functions(pinhole_intr); pinhole_intr @@ -64,8 +100,9 @@ void pybind_camera_classes(py::module &m) { std::string( ".\nAccess intrinsics with intrinsic_matrix."); }); - docstring::ClassMethodDocInject(m, "PinholeCameraIntrinsic", "__init__"); - docstring::ClassMethodDocInject(m, "PinholeCameraIntrinsic", + docstring::ClassMethodDocInject(m_camera, "PinholeCameraIntrinsic", + "__init__"); + docstring::ClassMethodDocInject(m_camera, "PinholeCameraIntrinsic", "set_intrinsics", {{"width", "Width of the image."}, {"height", "Height of the image."}, @@ -73,41 +110,18 @@ void pybind_camera_classes(py::module &m) { {"fy", "Y-axis focal length."}, {"cx", "X-axis principle point."}, {"cy", "Y-axis principle point."}}); - docstring::ClassMethodDocInject(m, "PinholeCameraIntrinsic", + docstring::ClassMethodDocInject(m_camera, "PinholeCameraIntrinsic", "get_focal_length"); - docstring::ClassMethodDocInject(m, "PinholeCameraIntrinsic", + docstring::ClassMethodDocInject(m_camera, "PinholeCameraIntrinsic", "get_principal_point"); - docstring::ClassMethodDocInject(m, "PinholeCameraIntrinsic", "get_skew"); - docstring::ClassMethodDocInject(m, "PinholeCameraIntrinsic", "is_valid"); - - // open3d.camera.PinholeCameraIntrinsicParameters - py::enum_ pinhole_intr_params( - m, "PinholeCameraIntrinsicParameters", py::arithmetic(), - "PinholeCameraIntrinsicParameters"); - pinhole_intr_params - .value("PrimeSenseDefault", - PinholeCameraIntrinsicParameters::PrimeSenseDefault, - "Default camera intrinsic parameter for PrimeSense.") - .value("Kinect2DepthCameraDefault", - PinholeCameraIntrinsicParameters::Kinect2DepthCameraDefault, - "Default camera intrinsic parameter for Kinect2 depth " - "camera.") - .value("Kinect2ColorCameraDefault", - PinholeCameraIntrinsicParameters::Kinect2ColorCameraDefault, - "Default camera intrinsic parameter for Kinect2 color " - "camera.") - .export_values(); - pinhole_intr_params.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class that contains default camera intrinsic " - "parameters for different sensors."; - }), - py::none(), py::none(), ""); + docstring::ClassMethodDocInject(m_camera, "PinholeCameraIntrinsic", + "get_skew"); + docstring::ClassMethodDocInject(m_camera, "PinholeCameraIntrinsic", + "is_valid"); // open3d.camera.PinholeCameraParameters - py::class_ pinhole_param( - m, "PinholeCameraParameters", - "Contains both intrinsic and extrinsic pinhole camera parameters."); + auto pinhole_param = static_cast>( + m_camera.attr("PinholeCameraParameters")); py::detail::bind_default_constructor( pinhole_param); py::detail::bind_copy_functions(pinhole_param); @@ -125,10 +139,8 @@ void pybind_camera_classes(py::module &m) { }); // open3d.camera.PinholeCameraTrajectory - py::class_ pinhole_traj( - m, "PinholeCameraTrajectory", - "Contains a list of ``PinholeCameraParameters``, useful to storing " - "trajectories."); + auto pinhole_traj = static_cast>( + m_camera.attr("PinholeCameraTrajectory")); py::detail::bind_default_constructor(pinhole_traj); py::detail::bind_copy_functions(pinhole_traj); pinhole_traj @@ -141,10 +153,5 @@ void pybind_camera_classes(py::module &m) { }); } -void pybind_camera(py::module &m) { - py::module m_submodule = m.def_submodule("camera"); - pybind_camera_classes(m_submodule); -} - } // namespace camera } // namespace open3d diff --git a/cpp/pybind/camera/camera.h b/cpp/pybind/camera/camera.h index 38ef9c76afc..b7de65643fe 100644 --- a/cpp/pybind/camera/camera.h +++ b/cpp/pybind/camera/camera.h @@ -12,7 +12,8 @@ namespace open3d { namespace camera { -void pybind_camera(py::module &m); +void pybind_camera_declarations(py::module &m); +void pybind_camera_definitions(py::module &m); } // namespace camera } // namespace open3d diff --git a/cpp/pybind/core/blob.cpp b/cpp/pybind/core/blob.cpp index 309e9381f9b..228347cf81b 100644 --- a/cpp/pybind/core/blob.cpp +++ b/cpp/pybind/core/blob.cpp @@ -14,7 +14,9 @@ namespace open3d { namespace core { -void pybind_core_blob(py::module &m) { py::class_ blob(m, "Blob"); } +void pybind_core_blob_declarations(py::module &m) { + py::class_ blob(m, "Blob"); +} } // namespace core } // namespace open3d diff --git a/cpp/pybind/core/core.cpp b/cpp/pybind/core/core.cpp index 3ce45511c0a..764852ae2e7 100644 --- a/cpp/pybind/core/core.cpp +++ b/cpp/pybind/core/core.cpp @@ -16,27 +16,42 @@ namespace open3d { namespace core { -void pybind_core(py::module& m) { +void pybind_core_declarations(py::module& m) { py::module m_core = m.def_submodule("core"); // opn3d::core namespace. - pybind_cuda_utils(m_core); - pybind_sycl_utils(m_core); - pybind_core_blob(m_core); - pybind_core_dtype(m_core); - pybind_core_device(m_core); - pybind_core_size_vector(m_core); - pybind_core_tensor(m_core); - pybind_core_tensor_function(m_core); - pybind_core_linalg(m_core); - pybind_core_kernel(m_core); - pybind_core_hashmap(m_core); - pybind_core_hashset(m_core); - pybind_core_scalar(m_core); + pybind_cuda_utils_declarations(m_core); + pybind_core_blob_declarations(m_core); + pybind_core_dtype_declarations(m_core); + pybind_core_device_declarations(m_core); + pybind_core_size_vector_declarations(m_core); + pybind_core_tensor_declarations(m_core); + pybind_core_kernel_declarations(m_core); + pybind_core_hashmap_declarations(m_core); + pybind_core_hashset_declarations(m_core); + pybind_core_scalar_declarations(m_core); // opn3d::core::nns namespace. py::module m_nns = m_core.def_submodule("nns"); - nns::pybind_core_nns(m_nns); + nns::pybind_core_nns_declarations(m_nns); +} + +void pybind_core_definitions(py::module& m) { + auto m_core = static_cast(m.attr("core")); + pybind_cuda_utils_definitions(m_core); + pybind_sycl_utils_definitions(m_core); + pybind_core_dtype_definitions(m_core); + pybind_core_device_definitions(m_core); + pybind_core_size_vector_definitions(m_core); + pybind_core_tensor_definitions(m_core); + pybind_core_tensor_function_definitions(m_core); + pybind_core_linalg_definitions(m_core); + pybind_core_kernel_definitions(m_core); + pybind_core_hashmap_definitions(m_core); + pybind_core_hashset_definitions(m_core); + pybind_core_scalar_definitions(m_core); + auto m_nns = static_cast(m_core.attr("nns")); + nns::pybind_core_nns_definitions(m_nns); } } // namespace core diff --git a/cpp/pybind/core/core.h b/cpp/pybind/core/core.h index 14897df8fdb..634b697a55a 100644 --- a/cpp/pybind/core/core.h +++ b/cpp/pybind/core/core.h @@ -13,21 +13,32 @@ namespace open3d { namespace core { -void pybind_core(py::module& m); -void pybind_cuda_utils(py::module& m); -void pybind_sycl_utils(py::module& m); -void pybind_core_blob(py::module& m); -void pybind_core_dtype(py::module& m); -void pybind_core_device(py::module& m); -void pybind_core_size_vector(py::module& m); -void pybind_core_tensor(py::module& m); +void pybind_core_declarations(py::module& m); +void pybind_cuda_utils_declarations(py::module& m); +void pybind_core_blob_declarations(py::module& m); +void pybind_core_dtype_declarations(py::module& m); +void pybind_core_device_declarations(py::module& m); +void pybind_core_size_vector_declarations(py::module& m); +void pybind_core_tensor_declarations(py::module& m); void pybind_core_tensor_accessor(py::class_& t); -void pybind_core_tensor_function(py::module& m); -void pybind_core_linalg(py::module& m); -void pybind_core_kernel(py::module& m); -void pybind_core_hashmap(py::module& m); -void pybind_core_hashset(py::module& m); -void pybind_core_scalar(py::module& m); +void pybind_core_tensor_function_definitions(py::module& m); +void pybind_core_kernel_declarations(py::module& m); +void pybind_core_hashmap_declarations(py::module& m); +void pybind_core_hashset_declarations(py::module& m); +void pybind_core_scalar_declarations(py::module& m); + +void pybind_core_definitions(py::module& m); +void pybind_cuda_utils_definitions(py::module& m); +void pybind_sycl_utils_definitions(py::module& m); +void pybind_core_dtype_definitions(py::module& m); +void pybind_core_device_definitions(py::module& m); +void pybind_core_size_vector_definitions(py::module& m); +void pybind_core_tensor_definitions(py::module& m); +void pybind_core_linalg_definitions(py::module& m); +void pybind_core_kernel_definitions(py::module& m); +void pybind_core_hashmap_definitions(py::module& m); +void pybind_core_hashset_definitions(py::module& m); +void pybind_core_scalar_definitions(py::module& m); } // namespace core } // namespace open3d diff --git a/cpp/pybind/core/cuda_utils.cpp b/cpp/pybind/core/cuda_utils.cpp index 3befab655c2..30b0f84511f 100644 --- a/cpp/pybind/core/cuda_utils.cpp +++ b/cpp/pybind/core/cuda_utils.cpp @@ -12,9 +12,11 @@ namespace open3d { namespace core { -void pybind_cuda_utils(py::module& m) { +void pybind_cuda_utils_declarations(py::module& m) { py::module m_cuda = m.def_submodule("cuda"); - +} +void pybind_cuda_utils_definitions(py::module& m) { + auto m_cuda = static_cast(m.attr("cuda")); m_cuda.def("device_count", cuda::DeviceCount, "Returns the number of available CUDA devices. Returns 0 if " "Open3D is not compiled with CUDA support."); diff --git a/cpp/pybind/core/device.cpp b/cpp/pybind/core/device.cpp index bcf11805173..f72f7ace754 100644 --- a/cpp/pybind/core/device.cpp +++ b/cpp/pybind/core/device.cpp @@ -14,10 +14,17 @@ namespace open3d { namespace core { -void pybind_core_device(py::module &m) { +void pybind_core_device_declarations(py::module &m) { py::class_ device( m, "Device", "Device context specifying device type and device id."); + py::enum_(device, "DeviceType") + .value("CPU", Device::DeviceType::CPU) + .value("CUDA", Device::DeviceType::CUDA) + .export_values(); +} +void pybind_core_device_definitions(py::module &m) { + auto device = static_cast>(m.attr("Device")); device.def(py::init<>()); device.def(py::init()); device.def(py::init()); @@ -41,11 +48,6 @@ void pybind_core_device(py::module &m) { return Device(t[0].cast(), t[1].cast()); })); - - py::enum_(device, "DeviceType") - .value("CPU", Device::DeviceType::CPU) - .value("CUDA", Device::DeviceType::CUDA) - .export_values(); } } // namespace core diff --git a/cpp/pybind/core/dtype.cpp b/cpp/pybind/core/dtype.cpp index e4ab4489c4b..5a17a6cb105 100644 --- a/cpp/pybind/core/dtype.cpp +++ b/cpp/pybind/core/dtype.cpp @@ -15,10 +15,14 @@ namespace open3d { namespace core { -void pybind_core_dtype(py::module &m) { - // open3d.core.Dtype class +void pybind_core_dtype_declarations(py::module &m) { py::class_> dtype(m, "Dtype", "Open3D data types."); +} +void pybind_core_dtype_definitions(py::module &m) { + // open3d.core.Dtype class + auto dtype = static_cast>>( + m.attr("Dtype")); dtype.def(py::init()); dtype.def_readonly_static("Undefined", &core::Undefined); dtype.def_readonly_static("Float32", &core::Float32); diff --git a/cpp/pybind/core/hashmap.cpp b/cpp/pybind/core/hashmap.cpp index 4c6740d1bfa..44220aaeae1 100644 --- a/cpp/pybind/core/hashmap.cpp +++ b/cpp/pybind/core/hashmap.cpp @@ -50,11 +50,13 @@ const std::unordered_map argument_docs = { {"values_buffer_id", "Index of the value buffer tensor."}, {"device_id", "Target CUDA device ID."}}; -void pybind_core_hashmap(py::module& m) { +void pybind_core_hashmap_declarations(py::module& m) { py::class_ hashmap(m, "HashMap", "A HashMap is an unordered map from key to " "value wrapped by Tensors."); - +} +void pybind_core_hashmap_definitions(py::module& m) { + auto hashmap = static_cast>(m.attr("HashMap")); hashmap.def(py::init(), "init_capacity"_a, "key_dtype"_a, "key_element_shape"_a, @@ -193,11 +195,13 @@ void pybind_core_hashmap(py::module& m) { hashmap.def_property_readonly("is_cuda", &HashMap::IsCUDA); } -void pybind_core_hashset(py::module& m) { +void pybind_core_hashset_declarations(py::module& m) { py::class_ hashset( m, "HashSet", "A HashSet is an unordered set of keys wrapped by Tensors."); - +} +void pybind_core_hashset_definitions(py::module& m) { + auto hashset = static_cast>(m.attr("HashSet")); hashset.def( py::init(), "init_capacity"_a, "key_dtype"_a, "key_element_shape"_a, diff --git a/cpp/pybind/core/kernel.cpp b/cpp/pybind/core/kernel.cpp index 1d083778997..abfa9785e63 100644 --- a/cpp/pybind/core/kernel.cpp +++ b/cpp/pybind/core/kernel.cpp @@ -14,8 +14,11 @@ namespace open3d { namespace core { -void pybind_core_kernel(py::module &m) { +void pybind_core_kernel_declarations(py::module &m) { py::module m_kernel = m.def_submodule("kernel"); +} +void pybind_core_kernel_definitions(py::module &m) { + auto m_kernel = static_cast(m.attr("kernel")); m_kernel.def("test_linalg_integration", &core::kernel::TestLinalgIntegration); } diff --git a/cpp/pybind/core/linalg.cpp b/cpp/pybind/core/linalg.cpp index 39ec10650f2..c225daf6861 100644 --- a/cpp/pybind/core/linalg.cpp +++ b/cpp/pybind/core/linalg.cpp @@ -21,7 +21,7 @@ namespace open3d { namespace core { -void pybind_core_linalg(py::module &m) { +void pybind_core_linalg_definitions(py::module &m) { m.def( "matmul", [](const Tensor &A, const Tensor &B) { diff --git a/cpp/pybind/core/nns/nearest_neighbor_search.cpp b/cpp/pybind/core/nns/nearest_neighbor_search.cpp index feab8bbcdbf..5512bccf3be 100644 --- a/cpp/pybind/core/nns/nearest_neighbor_search.cpp +++ b/cpp/pybind/core/nns/nearest_neighbor_search.cpp @@ -18,24 +18,17 @@ namespace open3d { namespace core { namespace nns { -void pybind_core_nns(py::module &m_nns) { - static const std::unordered_map - map_nearest_neighbor_search_method_docs = { - {"query_points", "The query tensor of shape {n_query, d}."}, - {"radii", - "Tensor of shape {n_query,} containing multiple radii, " - "one for each query point."}, - {"radius", "Radius value for radius search."}, - {"max_knn", - "Maximum number of neighbors to search per query point."}, - {"knn", "Number of neighbors to search per query point."}}; - +void pybind_core_nns_declarations(py::module &m_nns) { py::class_> nns(m_nns, "NearestNeighborSearch", "NearestNeighborSearch class for nearest neighbor search. " "Construct a NearestNeighborSearch object with input " "dataset_points of shape {n_dataset, d}."); - +} +void pybind_core_nns_definitions(py::module &m_nns) { + auto nns = static_cast>>( + m_nns.attr("NearestNeighborSearch")); // Constructors. nns.def(py::init(), "dataset_points"_a, "index_dtype"_a = core::Int64); @@ -91,6 +84,16 @@ void pybind_core_nns(py::module &m_nns) { "Perform hybrid search."); // Docstrings. + static const std::unordered_map + map_nearest_neighbor_search_method_docs = { + {"query_points", "The query tensor of shape {n_query, d}."}, + {"radii", + "Tensor of shape {n_query,} containing multiple radii, " + "one for each query point."}, + {"radius", "Radius value for radius search."}, + {"max_knn", + "Maximum number of neighbors to search per query point."}, + {"knn", "Number of neighbors to search per query point."}}; docstring::ClassMethodDocInject(m_nns, "NearestNeighborSearch", "knn_search", map_nearest_neighbor_search_method_docs); diff --git a/cpp/pybind/core/nns/nearest_neighbor_search.h b/cpp/pybind/core/nns/nearest_neighbor_search.h index a391160b525..278f240b64b 100644 --- a/cpp/pybind/core/nns/nearest_neighbor_search.h +++ b/cpp/pybind/core/nns/nearest_neighbor_search.h @@ -14,7 +14,8 @@ namespace open3d { namespace core { namespace nns { -void pybind_core_nns(py::module &m); +void pybind_core_nns_declarations(py::module &m); +void pybind_core_nns_definitions(py::module &m); } // namespace nns } // namespace core diff --git a/cpp/pybind/core/scalar.cpp b/cpp/pybind/core/scalar.cpp index 8a16a690f97..fc6512d09dd 100644 --- a/cpp/pybind/core/scalar.cpp +++ b/cpp/pybind/core/scalar.cpp @@ -12,10 +12,12 @@ namespace open3d { namespace core { -void pybind_core_scalar(py::module& m) { +void pybind_core_scalar_declarations(py::module& m) { py::class_ scalar( m, "Scalar", "A Scalar can store one of {double, int64, bool}."); - +} +void pybind_core_scalar_definitions(py::module& m) { + auto scalar = static_cast>(m.attr("Scalar")); scalar.def(py::init([](float val) { return Scalar(val); })); scalar.def(py::init([](double val) { return Scalar(val); })); scalar.def(py::init([](int8_t val) { return Scalar(val); })); diff --git a/cpp/pybind/core/size_vector.cpp b/cpp/pybind/core/size_vector.cpp index 434dcb6ff59..f9344908a3b 100644 --- a/cpp/pybind/core/size_vector.cpp +++ b/cpp/pybind/core/size_vector.cpp @@ -13,12 +13,17 @@ namespace open3d { namespace core { -void pybind_core_size_vector(py::module& m) { +void pybind_core_size_vector_declarations(py::module& m) { // bind_vector takes care of most common methods for Python list. auto sv = py::bind_vector( m, "SizeVector", "A vector of integers for specifying shape, strides, etc."); - + auto dsv = py::bind_vector( + m, "DynamicSizeVector", + "A vector of integers for specifying shape, strides, etc. Some " + "elements can be None."); +} +void pybind_core_size_vector_definitions(py::module& m) { // In Python, We want (3), (3,), [3] and [3,] to represent the same thing. // The following are all equivalent to core::SizeVector({3}): // - o3d.core.SizeVector(3) # int @@ -33,17 +38,15 @@ void pybind_core_size_vector(py::module& m) { // // The API difference is due to the NumPy convention which allows integer to // represent a 1-element tuple, and the C++ constructor for vectors. + auto sv = static_cast>(m.attr("SizeVector")); sv.def(py::init([](int64_t i) { return SizeVector({i}); })); py::implicitly_convertible(); // Allows tuple and list implicit conversions to SizeVector. py::implicitly_convertible(); py::implicitly_convertible(); - - auto dsv = py::bind_vector( - m, "DynamicSizeVector", - "A vector of integers for specifying shape, strides, etc. Some " - "elements can be None."); + auto dsv = static_cast>( + m.attr("DynamicSizeVector")); dsv.def("__repr__", [](const DynamicSizeVector& dsv) { return dsv.ToString(); }); } diff --git a/cpp/pybind/core/sycl_utils.cpp b/cpp/pybind/core/sycl_utils.cpp index b0b486ba6f0..0acf8c37a5b 100644 --- a/cpp/pybind/core/sycl_utils.cpp +++ b/cpp/pybind/core/sycl_utils.cpp @@ -12,7 +12,9 @@ namespace open3d { namespace core { -void pybind_sycl_utils(py::module& m) { m.def("sycl_demo", &sycl::SYCLDemo); } +void pybind_sycl_utils_definitions(py::module& m) { + m.def("sycl_demo", &sycl::SYCLDemo); +} } // namespace core } // namespace open3d diff --git a/cpp/pybind/core/tensor.cpp b/cpp/pybind/core/tensor.cpp index 56e1aaa2f2c..d81cccaf7de 100644 --- a/cpp/pybind/core/tensor.cpp +++ b/cpp/pybind/core/tensor.cpp @@ -250,11 +250,13 @@ static void BindTensorFullCreation(py::module& m, py::class_& tensor) { "device"_a = py::none()); } -void pybind_core_tensor(py::module& m) { +void pybind_core_tensor_declarations(py::module& m) { py::class_ tensor( m, "Tensor", "A Tensor is a view of a data Blob with shape, stride, data_ptr."); - +} +void pybind_core_tensor_definitions(py::module& m) { + auto tensor = static_cast>(m.attr("Tensor")); // o3c.Tensor(np.array([[0, 1, 2], [3, 4, 5]]), dtype=None, device=None). tensor.def(py::init([](const py::array& np_array, utility::optional dtype, diff --git a/cpp/pybind/core/tensor_function.cpp b/cpp/pybind/core/tensor_function.cpp index 78a6da686a1..01adb644ce0 100644 --- a/cpp/pybind/core/tensor_function.cpp +++ b/cpp/pybind/core/tensor_function.cpp @@ -13,7 +13,7 @@ namespace open3d { namespace core { -void pybind_core_tensor_function(py::module& m) { +void pybind_core_tensor_function_definitions(py::module& m) { m.def( "concatenate", [](const std::vector& tensors, @@ -27,7 +27,7 @@ void pybind_core_tensor_function(py::module& m) { axis into a new tensor. All the tensors must have same data-type, device, and number of dimensions. All dimensions must be the same, except the dimension along the axis the tensors are to be concatenated. -Using Concatenate for a single tensor, the tensor is split along its first +Using Concatenate for a single tensor, the tensor is split along its first dimension (length), and concatenated along the axis. This is the same as NumPy's semantics: @@ -59,17 +59,17 @@ This is the same as NumPy's semantics: } return core::Append(self, values); }, - R"(Appends the `values` tensor to the `self` tensor, along the + R"(Appends the `values` tensor to the `self` tensor, along the given axis and returns a new tensor. Both the tensors must have same data-type device, and number of dimensions. All dimensions must be the same, except the -dimension along the axis the tensors are to be appended. +dimension along the axis the tensors are to be appended. This is the same as NumPy's semantics: - https://numpy.org/doc/stable/reference/generated/numpy.append.html Returns: - A copy of the `self` tensor with `values` appended to axis. Note that - append does not occur in-place: a new array is allocated and filled. + A copy of the `self` tensor with `values` appended to axis. Note that + append does not occur in-place: a new array is allocated and filled. If axis is null, out is a flattened tensor. Example: @@ -78,20 +78,20 @@ This is the same as NumPy's semantics: [2 3], [4 5]] Tensor[shape={3, 2}, stride={2, 1}, Int64, CPU:0, 0x55555abc6b00] - + >>> o3d.core.append([[0, 1], [2, 3]], [[4, 5]]) [0 1 2 3 4 5] Tensor[shape={6}, stride={1}, Int64, CPU:0, 0x55555abc6b70])", "self"_a, "values"_a, "axis"_a = py::none()); m.def("maximum", &core::Maximum, - R"(Computes the element-wise maximum of input and other. The tensors + R"(Computes the element-wise maximum of input and other. The tensors must have same data type and device. If input.GetShape() != other.GetShape(), then they will be broadcasted to a common shape (which becomes the shape of the output).)", "input"_a, "other"_a); m.def("minimum", &core::Minimum, - R"(Computes the element-wise minimum of input and other. The tensors + R"(Computes the element-wise minimum of input and other. The tensors must have same data type and device. If input.GetShape() != other.GetShape(), then they will be broadcasted to a common shape (which becomes the shape of the output).)", diff --git a/cpp/pybind/geometry/boundingvolume.cpp b/cpp/pybind/geometry/boundingvolume.cpp index 719e4edfa55..e178adfe19b 100644 --- a/cpp/pybind/geometry/boundingvolume.cpp +++ b/cpp/pybind/geometry/boundingvolume.cpp @@ -16,12 +16,26 @@ namespace open3d { namespace geometry { -void pybind_boundingvolume(py::module &m) { +void pybind_boundingvolume_declarations(py::module &m) { py::class_, std::shared_ptr, Geometry3D> oriented_bounding_box(m, "OrientedBoundingBox", "Class that defines an oriented box that can " "be computed from 3D geometries."); + py::class_, + std::shared_ptr, Geometry3D> + axis_aligned_bounding_box(m, "AxisAlignedBoundingBox", + "Class that defines an axis_aligned box " + "that can be computed from 3D " + "geometries, The axis aligned bounding " + "box uses the coordinate axes for " + "bounding box generation."); +} +void pybind_boundingvolume_definitions(py::module &m) { + auto oriented_bounding_box = static_cast< + py::class_, + std::shared_ptr, Geometry3D>>( + m.attr("OrientedBoundingBox")); py::detail::bind_default_constructor( oriented_bounding_box); py::detail::bind_copy_functions(oriented_bounding_box); @@ -62,7 +76,7 @@ The returned bounding box is an approximation to the minimal bounding box. Args: points (open3d.utility.Vector3dVector): Input points. - robust (bool): If set to true uses a more robust method which works in + robust (bool): If set to true uses a more robust method which works in degenerate cases but introduces noise to the points coordinates. Returns: @@ -93,14 +107,10 @@ The returned bounding box is an approximation to the minimal bounding box. "AxisAlignedBoundingBox object from which OrientedBoundingBox is " "created."}}); - py::class_, - std::shared_ptr, Geometry3D> - axis_aligned_bounding_box(m, "AxisAlignedBoundingBox", - "Class that defines an axis_aligned box " - "that can be computed from 3D " - "geometries, The axis aligned bounding " - "box uses the coordinate axes for " - "bounding box generation."); + auto axis_aligned_bounding_box = static_cast, + std::shared_ptr, Geometry3D>>( + m.attr("AxisAlignedBoundingBox")); py::detail::bind_default_constructor( axis_aligned_bounding_box); py::detail::bind_copy_functions( diff --git a/cpp/pybind/geometry/geometry.cpp b/cpp/pybind/geometry/geometry.cpp index 8158c0de25c..22b63c8184c 100644 --- a/cpp/pybind/geometry/geometry.cpp +++ b/cpp/pybind/geometry/geometry.cpp @@ -14,7 +14,39 @@ namespace open3d { namespace geometry { -void pybind_geometry_classes(py::module &m) { +void pybind_geometry_classes_declarations(py::module &m) { + py::class_, std::shared_ptr> + geometry(m, "Geometry", "The base geometry class."); + py::enum_ geometry_type(geometry, "Type", + py::arithmetic()); + // Trick to write docs without listing the members in the enum class again. + geometry_type.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for Geometry types."; + }), + py::none(), py::none(), ""); + + geometry_type.value("Unspecified", Geometry::GeometryType::Unspecified) + .value("PointCloud", Geometry::GeometryType::PointCloud) + .value("VoxelGrid", Geometry::GeometryType::VoxelGrid) + .value("LineSet", Geometry::GeometryType::LineSet) + .value("TriangleMesh", Geometry::GeometryType::TriangleMesh) + .value("HalfEdgeTriangleMesh", + Geometry::GeometryType::HalfEdgeTriangleMesh) + .value("Image", Geometry::GeometryType::Image) + .value("RGBDImage", Geometry::GeometryType::RGBDImage) + .value("TetraMesh", Geometry::GeometryType::TetraMesh) + .export_values(); + py::class_, + std::shared_ptr, Geometry> + geometry3d(m, "Geometry3D", + "The base geometry class for 3D geometries."); + py::class_, + std::shared_ptr, Geometry> + geometry2d(m, "Geometry2D", + "The base geometry class for 2D geometries."); +} +void pybind_geometry_classes_definitions(py::module &m) { // open3d.geometry functions m.def("get_rotation_matrix_from_xyz", &Geometry3D::GetRotationMatrixFromXYZ, "rotation"_a); @@ -34,8 +66,9 @@ void pybind_geometry_classes(py::module &m) { &Geometry3D::GetRotationMatrixFromQuaternion, "rotation"_a); // open3d.geometry.Geometry - py::class_, std::shared_ptr> - geometry(m, "Geometry", "The base geometry class."); + auto geometry = static_cast, + std::shared_ptr>>( + m.attr("Geometry")); geometry.def("clear", &Geometry::Clear, "Clear all elements in the geometry.") .def("is_empty", &Geometry::IsEmpty, @@ -48,33 +81,11 @@ void pybind_geometry_classes(py::module &m) { docstring::ClassMethodDocInject(m, "Geometry", "is_empty"); docstring::ClassMethodDocInject(m, "Geometry", "get_geometry_type"); docstring::ClassMethodDocInject(m, "Geometry", "dimension"); - - // open3d.geometry.Geometry.Type - py::enum_ geometry_type(geometry, "Type", - py::arithmetic()); - // Trick to write docs without listing the members in the enum class again. - geometry_type.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for Geometry types."; - }), - py::none(), py::none(), ""); - - geometry_type.value("Unspecified", Geometry::GeometryType::Unspecified) - .value("PointCloud", Geometry::GeometryType::PointCloud) - .value("VoxelGrid", Geometry::GeometryType::VoxelGrid) - .value("LineSet", Geometry::GeometryType::LineSet) - .value("TriangleMesh", Geometry::GeometryType::TriangleMesh) - .value("HalfEdgeTriangleMesh", - Geometry::GeometryType::HalfEdgeTriangleMesh) - .value("Image", Geometry::GeometryType::Image) - .value("RGBDImage", Geometry::GeometryType::RGBDImage) - .value("TetraMesh", Geometry::GeometryType::TetraMesh) - .export_values(); - - py::class_, - std::shared_ptr, Geometry> - geometry3d(m, "Geometry3D", - "The base geometry class for 3D geometries."); + // open3d.geometry.Geometry3D + auto geometry3d = + static_cast, + std::shared_ptr, Geometry>>( + m.attr("Geometry3D")); geometry3d .def("get_min_bound", &Geometry3D::GetMinBound, "Returns min bounds for geometry coordinates.") @@ -94,7 +105,7 @@ Computes the oriented bounding box based on the PCA of the convex hull. The returned bounding box is an approximation to the minimal bounding box. Args: - robust (bool): If set to true uses a more robust method which works in + robust (bool): If set to true uses a more robust method which works in degenerate cases but introduces noise to the points coordinates. Returns: @@ -192,10 +203,10 @@ at the end, return the box with the smallest volume {"center", "Rotation center used for transformation."}}); // open3d.geometry.Geometry2D - py::class_, - std::shared_ptr, Geometry> - geometry2d(m, "Geometry2D", - "The base geometry class for 2D geometries."); + auto geometry2d = + static_cast, + std::shared_ptr, Geometry>>( + m.attr("Geometry2D")); geometry2d .def("get_min_bound", &Geometry2D::GetMinBound, "Returns min bounds for geometry coordinates.") @@ -205,27 +216,38 @@ at the end, return the box with the smallest volume docstring::ClassMethodDocInject(m, "Geometry2D", "get_max_bound"); } -void pybind_geometry(py::module &m) { - py::module m_submodule = m.def_submodule("geometry"); - pybind_geometry_classes(m_submodule); - pybind_kdtreeflann(m_submodule); - pybind_pointcloud(m_submodule); - pybind_keypoint(m_submodule); - pybind_voxelgrid(m_submodule); - pybind_lineset(m_submodule); - pybind_meshbase(m_submodule); - pybind_trianglemesh(m_submodule); - pybind_halfedgetrianglemesh(m_submodule); - pybind_image(m_submodule); - pybind_tetramesh(m_submodule); - pybind_pointcloud_methods(m_submodule); - pybind_voxelgrid_methods(m_submodule); - pybind_meshbase_methods(m_submodule); - pybind_lineset_methods(m_submodule); - pybind_image_methods(m_submodule); - pybind_octree_methods(m_submodule); - pybind_octree(m_submodule); - pybind_boundingvolume(m_submodule); +void pybind_geometry_declarations(py::module &m) { + py::module m_geometry = m.def_submodule("geometry"); + pybind_geometry_classes_declarations(m_geometry); + pybind_kdtreeflann_declarations(m_geometry); + pybind_pointcloud_declarations(m_geometry); + pybind_keypoint_declarations(m_geometry); + pybind_voxelgrid_declarations(m_geometry); + pybind_lineset_declarations(m_geometry); + pybind_meshbase_declarations(m_geometry); + pybind_trianglemesh_declarations(m_geometry); + pybind_halfedgetrianglemesh_declarations(m_geometry); + pybind_image_declarations(m_geometry); + pybind_tetramesh_declarations(m_geometry); + pybind_octree_declarations(m_geometry); + pybind_boundingvolume_declarations(m_geometry); +} + +void pybind_geometry_definitions(py::module &m) { + auto m_geometry = static_cast(m.attr("geometry")); + pybind_geometry_classes_definitions(m_geometry); + pybind_kdtreeflann_definitions(m_geometry); + pybind_pointcloud_definitions(m_geometry); + pybind_keypoint_definitions(m_geometry); + pybind_voxelgrid_definitions(m_geometry); + pybind_lineset_definitions(m_geometry); + pybind_meshbase_definitions(m_geometry); + pybind_trianglemesh_definitions(m_geometry); + pybind_halfedgetrianglemesh_definitions(m_geometry); + pybind_image_definitions(m_geometry); + pybind_tetramesh_definitions(m_geometry); + pybind_octree_definitions(m_geometry); + pybind_boundingvolume_definitions(m_geometry); } } // namespace geometry diff --git a/cpp/pybind/geometry/geometry.h b/cpp/pybind/geometry/geometry.h index 41a9e31cd01..ec9d6f6f3fa 100644 --- a/cpp/pybind/geometry/geometry.h +++ b/cpp/pybind/geometry/geometry.h @@ -12,27 +12,33 @@ namespace open3d { namespace geometry { -void pybind_geometry(py::module &m); +void pybind_geometry_declarations(py::module &m); +void pybind_kdtreeflann_declarations(py::module &m); +void pybind_pointcloud_declarations(py::module &m); +void pybind_keypoint_declarations(py::module &m); +void pybind_voxelgrid_declarations(py::module &m); +void pybind_lineset_declarations(py::module &m); +void pybind_meshbase_declarations(py::module &m); +void pybind_trianglemesh_declarations(py::module &m); +void pybind_halfedgetrianglemesh_declarations(py::module &m); +void pybind_image_declarations(py::module &m); +void pybind_tetramesh_declarations(py::module &m); +void pybind_octree_declarations(py::module &m); +void pybind_boundingvolume_declarations(py::module &m); -void pybind_pointcloud(py::module &m); -void pybind_keypoint(py::module &m); -void pybind_voxelgrid(py::module &m); -void pybind_lineset(py::module &m); -void pybind_meshbase(py::module &m); -void pybind_trianglemesh(py::module &m); -void pybind_halfedgetrianglemesh(py::module &m); -void pybind_image(py::module &m); -void pybind_tetramesh(py::module &m); -void pybind_kdtreeflann(py::module &m); -void pybind_pointcloud_methods(py::module &m); -void pybind_voxelgrid_methods(py::module &m); -void pybind_meshbase_methods(py::module &m); -void pybind_trianglemesh_methods(py::module &m); -void pybind_lineset_methods(py::module &m); -void pybind_image_methods(py::module &m); -void pybind_octree_methods(py::module &m); -void pybind_octree(py::module &m); -void pybind_boundingvolume(py::module &m); +void pybind_geometry_definitions(py::module &m); +void pybind_kdtreeflann_definitions(py::module &m); +void pybind_pointcloud_definitions(py::module &m); +void pybind_keypoint_definitions(py::module &m); +void pybind_voxelgrid_definitions(py::module &m); +void pybind_lineset_definitions(py::module &m); +void pybind_meshbase_definitions(py::module &m); +void pybind_trianglemesh_definitions(py::module &m); +void pybind_halfedgetrianglemesh_definitions(py::module &m); +void pybind_image_definitions(py::module &m); +void pybind_tetramesh_definitions(py::module &m); +void pybind_octree_definitions(py::module &m); +void pybind_boundingvolume_definitions(py::module &m); } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/halfedgetrianglemesh.cpp b/cpp/pybind/geometry/halfedgetrianglemesh.cpp index 437c4f12dfd..ee269a33f84 100644 --- a/cpp/pybind/geometry/halfedgetrianglemesh.cpp +++ b/cpp/pybind/geometry/halfedgetrianglemesh.cpp @@ -16,11 +16,24 @@ namespace open3d { namespace geometry { -void pybind_half_edge(py::module &m) { +void pybind_half_edge(py::module &m) {} + +void pybind_halfedgetrianglemesh_declarations(py::module &m) { py::class_ half_edge( m, "HalfEdge", "HalfEdge class contains vertex, triangle info about a half edge, " "as well as relations of next and twin half edge."); + py::class_, + std::shared_ptr, MeshBase> + half_edge_triangle_mesh( + m, "HalfEdgeTriangleMesh", + "HalfEdgeTriangleMesh inherits TriangleMesh class with the " + "addition of HalfEdge data structure for each half edge in " + "the mesh as well as related functions."); +} +void pybind_halfedgetrianglemesh_definitions(py::module &m) { + auto half_edge = static_cast>( + m.attr("HalfEdge")); py::detail::bind_default_constructor( half_edge); py::detail::bind_copy_functions(half_edge); @@ -52,19 +65,12 @@ void pybind_half_edge(py::module &m) { "triangle_index", &HalfEdgeTriangleMesh::HalfEdge::triangle_index_, "int: Index of the triangle containing this half edge"); -} - -void pybind_halfedgetrianglemesh(py::module &m) { - pybind_half_edge(m); // open3d.geometry.HalfEdgeTriangleMesh - py::class_, - std::shared_ptr, MeshBase> - half_edge_triangle_mesh( - m, "HalfEdgeTriangleMesh", - "HalfEdgeTriangleMesh inherits TriangleMesh class with the " - "addition of HalfEdge data structure for each half edge in " - "the mesh as well as related functions."); + auto half_edge_triangle_mesh = static_cast< + py::class_, + std::shared_ptr, MeshBase>>( + m.attr("HalfEdgeTriangleMesh")); py::detail::bind_default_constructor( half_edge_triangle_mesh); py::detail::bind_copy_functions( diff --git a/cpp/pybind/geometry/image.cpp b/cpp/pybind/geometry/image.cpp index 70e63184e64..910826708de 100644 --- a/cpp/pybind/geometry/image.cpp +++ b/cpp/pybind/geometry/image.cpp @@ -37,7 +37,7 @@ static const std::unordered_map "When ``True``, image in the pyramid will first be filtered " "by a 3x3 Gaussian kernel before downsampling."}}; -void pybind_image(py::module &m) { +void pybind_image_declarations(py::module &m) { py::enum_ image_filter_type(m, "ImageFilterType"); image_filter_type.value("Gaussian3", Image::FilterType::Gaussian3) .value("Gaussian5", Image::FilterType::Gaussian5) @@ -50,11 +50,22 @@ void pybind_image(py::module &m) { return "Enum class for Image filter types."; }), py::none(), py::none(), ""); - py::class_, std::shared_ptr, Geometry2D> image(m, "Image", py::buffer_protocol(), "The image class stores image with customizable width, " "height, num of channels and bytes per channel."); + py::class_, std::shared_ptr, + Geometry2D> + rgbd_image(m, "RGBDImage", + "RGBDImage is for a pair of registered color and depth " + "images, viewed from the same view, of the same " + "resolution. If you have other format, convert it " + "first."); +} +void pybind_image_definitions(py::module &m) { + auto image = static_cast, + std::shared_ptr, Geometry2D>>( + m.attr("Image")); py::detail::bind_default_constructor(image); py::detail::bind_copy_functions(image); image.def(py::init([](py::buffer b) { @@ -204,13 +215,10 @@ void pybind_image(py::module &m) { docstring::ClassMethodDocInject(m, "Image", "filter_pyramid", map_shared_argument_docstrings); - py::class_, std::shared_ptr, - Geometry2D> - rgbd_image(m, "RGBDImage", - "RGBDImage is for a pair of registered color and depth " - "images, viewed from the same view, of the same " - "resolution. If you have other format, convert it " - "first."); + auto rgbd_image = + static_cast, + std::shared_ptr, Geometry2D>>( + m.attr("RGBDImage")); py::detail::bind_default_constructor(rgbd_image); rgbd_image .def_readwrite("color", &RGBDImage::color_, @@ -275,7 +283,5 @@ void pybind_image(py::module &m) { map_shared_argument_docstrings); } -void pybind_image_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/kdtreeflann.cpp b/cpp/pybind/geometry/kdtreeflann.cpp index 785aa2a09f9..42dfaba3795 100644 --- a/cpp/pybind/geometry/kdtreeflann.cpp +++ b/cpp/pybind/geometry/kdtreeflann.cpp @@ -14,15 +14,9 @@ namespace open3d { namespace geometry { -void pybind_kdtreeflann(py::module &m) { - // open3d.geometry.KDTreeSearchParam +void pybind_kdtreeflann_declarations(py::module &m) { py::class_ kdtreesearchparam( m, "KDTreeSearchParam", "Base class for KDTree search parameters."); - kdtreesearchparam.def("get_search_type", &KDTreeSearchParam::GetSearchType, - "Get the search type (KNN, Radius, Hybrid) for the " - "search parameter."); - docstring::ClassMethodDocInject(m, "KDTreeSearchParam", "get_search_type"); - // open3d.geometry.KDTreeSearchParam.Type py::enum_ kdtree_search_param_type( kdtreesearchparam, "Type", py::arithmetic()); @@ -36,11 +30,31 @@ void pybind_kdtreeflann(py::module &m) { return "Enum class for Geometry types."; }), py::none(), py::none(), ""); - - // open3d.geometry.KDTreeSearchParamKNN py::class_ kdtreesearchparam_knn( m, "KDTreeSearchParamKNN", kdtreesearchparam, "KDTree search parameters for pure KNN search."); + py::class_ kdtreesearchparam_radius( + m, "KDTreeSearchParamRadius", kdtreesearchparam, + "KDTree search parameters for pure radius search."); + py::class_ kdtreesearchparam_hybrid( + m, "KDTreeSearchParamHybrid", kdtreesearchparam, + "KDTree search parameters for hybrid KNN and radius search."); + py::class_> kdtreeflann( + m, "KDTreeFlann", "KDTree with FLANN for nearest neighbor search."); +} + +void pybind_kdtreeflann_definitions(py::module &m) { + // open3d.geometry.KDTreeSearchParam + auto kdtreesearchparam = static_cast>( + m.attr("KDTreeSearchParam")); + kdtreesearchparam.def("get_search_type", &KDTreeSearchParam::GetSearchType, + "Get the search type (KNN, Radius, Hybrid) for the " + "search parameter."); + docstring::ClassMethodDocInject(m, "KDTreeSearchParam", "get_search_type"); + + // open3d.geometry.KDTreeSearchParamKNN + auto kdtreesearchparam_knn = static_cast>( + m.attr("KDTreeSearchParamKNN")); kdtreesearchparam_knn.def(py::init(), "knn"_a = 30) .def("__repr__", [](const KDTreeSearchParamKNN ¶m) { @@ -53,9 +67,9 @@ void pybind_kdtreeflann(py::module &m) { "Number of the neighbors that will be searched."); // open3d.geometry.KDTreeSearchParamRadius - py::class_ kdtreesearchparam_radius( - m, "KDTreeSearchParamRadius", kdtreesearchparam, - "KDTree search parameters for pure radius search."); + auto kdtreesearchparam_radius = + static_cast>( + m.attr("KDTreeSearchParamRadius")); kdtreesearchparam_radius.def(py::init(), "radius"_a) .def("__repr__", [](const KDTreeSearchParamRadius ¶m) { @@ -68,9 +82,9 @@ void pybind_kdtreeflann(py::module &m) { "Search radius."); // open3d.geometry.KDTreeSearchParamHybrid - py::class_ kdtreesearchparam_hybrid( - m, "KDTreeSearchParamHybrid", kdtreesearchparam, - "KDTree search parameters for hybrid KNN and radius search."); + auto kdtreesearchparam_hybrid = + static_cast>( + m.attr("KDTreeSearchParamHybrid")); kdtreesearchparam_hybrid .def(py::init(), "radius"_a, "max_nn"_a) .def("__repr__", @@ -88,6 +102,8 @@ void pybind_kdtreeflann(py::module &m) { "At maximum, ``max_nn`` neighbors will be searched."); // open3d.geometry.KDTreeFlann + auto kdtreeflann = + static_cast>(m.attr("KDTreeFlann")); static const std::unordered_map map_kd_tree_flann_method_docs = { {"query", "The input query point."}, @@ -97,8 +113,6 @@ void pybind_kdtreeflann(py::module &m) { {"knn", "``knn`` neighbors will be searched."}, {"feature", "Feature data."}, {"data", "Matrix data."}}; - py::class_> kdtreeflann( - m, "KDTreeFlann", "KDTree with FLANN for nearest neighbor search."); kdtreeflann.def(py::init<>()) .def(py::init(), "data"_a) .def("set_matrix_data", &KDTreeFlann::SetMatrixData, diff --git a/cpp/pybind/geometry/keypoint.cpp b/cpp/pybind/geometry/keypoint.cpp index a4253670255..97865a7f36c 100644 --- a/cpp/pybind/geometry/keypoint.cpp +++ b/cpp/pybind/geometry/keypoint.cpp @@ -19,17 +19,21 @@ namespace open3d { namespace geometry { -void pybind_keypoint_methods(py::module &m) { - m.def("compute_iss_keypoints", &keypoint::ComputeISSKeypoints, - "Function that computes the ISS keypoints from an input point " - "cloud. This implements the keypoint detection modules " - "proposed in Yu Zhong, 'Intrinsic Shape Signatures: A Shape " - "Descriptor for 3D Object Recognition', 2009.", - "input"_a, "salient_radius"_a = 0.0, "non_max_radius"_a = 0.0, - "gamma_21"_a = 0.975, "gamma_32"_a = 0.975, "min_neighbors"_a = 5); - +void pybind_keypoint_declarations(py::module &m) { + py::module m_keypoint = m.def_submodule("keypoint", "Keypoint Detectors."); +} +void pybind_keypoint_definitions(py::module &m) { + auto m_keypoint = static_cast(m.attr("keypoint")); + m_keypoint.def( + "compute_iss_keypoints", &keypoint::ComputeISSKeypoints, + "Function that computes the ISS keypoints from an input point " + "cloud. This implements the keypoint detection modules " + "proposed in Yu Zhong, 'Intrinsic Shape Signatures: A Shape " + "Descriptor for 3D Object Recognition', 2009.", + "input"_a, "salient_radius"_a = 0.0, "non_max_radius"_a = 0.0, + "gamma_21"_a = 0.975, "gamma_32"_a = 0.975, "min_neighbors"_a = 5); docstring::FunctionDocInject( - m, "compute_iss_keypoints", + m_keypoint, "compute_iss_keypoints", {{"input", "The Input point cloud."}, {"salient_radius", "The radius of the spherical neighborhood used to detect " @@ -49,10 +53,5 @@ void pybind_keypoint_methods(py::module &m) { "keypoint"}}); } -void pybind_keypoint(py::module &m) { - py::module m_submodule = m.def_submodule("keypoint", "Keypoint Detectors."); - pybind_keypoint_methods(m_submodule); -} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/lineset.cpp b/cpp/pybind/geometry/lineset.cpp index 41e47a1c7ca..fe13fd0b848 100644 --- a/cpp/pybind/geometry/lineset.cpp +++ b/cpp/pybind/geometry/lineset.cpp @@ -16,13 +16,19 @@ namespace open3d { namespace geometry { -void pybind_lineset(py::module &m) { +void pybind_lineset_declarations(py::module &m) { py::class_, std::shared_ptr, Geometry3D> lineset(m, "LineSet", "LineSet define a sets of lines in 3D. A typical " "application is to display the point cloud correspondence " "pairs."); +} +void pybind_lineset_definitions(py::module &m) { + auto lineset = + static_cast, + std::shared_ptr, Geometry3D>>( + m.attr("LineSet")); py::detail::bind_default_constructor(lineset); py::detail::bind_copy_functions(lineset); lineset.def(py::init &, @@ -125,7 +131,5 @@ void pybind_lineset(py::module &m) { {{"mesh", "The input tetra mesh."}}); } -void pybind_lineset_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/meshbase.cpp b/cpp/pybind/geometry/meshbase.cpp index cbc71520e4f..d3c3ec447e6 100644 --- a/cpp/pybind/geometry/meshbase.cpp +++ b/cpp/pybind/geometry/meshbase.cpp @@ -15,16 +15,13 @@ namespace open3d { namespace geometry { -void pybind_meshbase(py::module &m) { +void pybind_meshbase_declarations(py::module &m) { py::class_, std::shared_ptr, Geometry3D> meshbase(m, "MeshBase", "MeshBase class. Triangle mesh contains vertices. " "Optionally, the mesh " "may also contain vertex normals and vertex colors."); - py::detail::bind_default_constructor(meshbase); - py::detail::bind_copy_functions(meshbase); - py::enum_(m, "SimplificationContraction") .value("Average", MeshBase::SimplificationContraction::Average, @@ -33,7 +30,6 @@ void pybind_meshbase(py::module &m) { "The vertex positions are computed by minimizing the " "distance to the adjacent triangle planes.") .export_values(); - py::enum_(m, "FilterScope") .value("All", MeshBase::FilterScope::All, "All properties (color, normal, vertex position) are " @@ -45,7 +41,6 @@ void pybind_meshbase(py::module &m) { .value("Vertex", MeshBase::FilterScope::Vertex, "Only the vertex positions are filtered.") .export_values(); - py::enum_( m, "DeformAsRigidAsPossibleEnergy") .value("Spokes", MeshBase::DeformAsRigidAsPossibleEnergy::Spokes, @@ -55,6 +50,15 @@ void pybind_meshbase(py::module &m) { MeshBase::DeformAsRigidAsPossibleEnergy::Smoothed, "adds a rotation smoothing term to the rotations.") .export_values(); +} + +void pybind_meshbase_definitions(py::module &m) { + auto meshbase = + static_cast, + std::shared_ptr, Geometry3D>>( + m.attr("MeshBase")); + py::detail::bind_default_constructor(meshbase); + py::detail::bind_copy_functions(meshbase); meshbase.def("__repr__", [](const MeshBase &mesh) { @@ -101,7 +105,5 @@ void pybind_meshbase(py::module &m) { docstring::ClassMethodDocInject(m, "MeshBase", "compute_convex_hull"); } -void pybind_meshbase_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/octree.cpp b/cpp/pybind/geometry/octree.cpp index e9ebafae522..9e4c2c62c71 100644 --- a/cpp/pybind/geometry/octree.cpp +++ b/cpp/pybind/geometry/octree.cpp @@ -33,14 +33,52 @@ static const std::unordered_map "bigger than the original point cloud bounds to accommodate " "all points."}}; -void pybind_octree(py::module &m) { - // OctreeNodeInfo +void pybind_octree_declarations(py::module &m) { // Binds std::shared_ptr<...> to avoid non-allocated free in python code py::class_> octree_node_info(m, "OctreeNodeInfo", "OctreeNode's information. OctreeNodeInfo is " "computed on the fly, " "not stored with the Node."); + py::class_, + std::shared_ptr> + octree_node(m, "OctreeNode", "The base class for octree node."); + py::class_, + std::shared_ptr, OctreeNode> + octree_internal_node(m, "OctreeInternalNode", + "OctreeInternalNode class, containing " + "OctreeNode children."); + py::class_, + std::shared_ptr, OctreeInternalNode> + octree_internal_point_node( + m, "OctreeInternalPointNode", + "OctreeInternalPointNode class is an " + "OctreeInternalNode with a list of point " + "indices (from point cloud) belonging to " + "children of this node."); + py::class_, + std::shared_ptr, OctreeNode> + octree_leaf_node(m, "OctreeLeafNode", "OctreeLeafNode base class."); + py::class_, + std::shared_ptr, OctreeLeafNode> + octree_color_leaf_node(m, "OctreeColorLeafNode", + "OctreeColorLeafNode class is an " + "OctreeLeafNode containing color."); + py::class_, + std::shared_ptr, OctreeLeafNode> + octree_point_color_leaf_node(m, "OctreePointColorLeafNode", + "OctreePointColorLeafNode class is an " + "OctreeLeafNode containing color."); + py::class_, std::shared_ptr, + Geometry3D> + octree(m, "Octree", "Octree datastructure."); +} +void pybind_octree_definitions(py::module &m) { + // OctreeNodeInfo + auto octree_node_info = static_cast< + py::class_>>( + m.attr("OctreeNodeInfo")); octree_node_info.def(py::init([](const Eigen::Vector3d &origin, double size, size_t depth, size_t child_index) { return new OctreeNodeInfo(origin, size, depth, @@ -75,20 +113,20 @@ void pybind_octree(py::module &m) { docstring::ClassMethodDocInject(m, "OctreeNodeInfo", "__init__"); // OctreeNode - py::class_, - std::shared_ptr> - octree_node(m, "OctreeNode", "The base class for octree node."); + auto octree_node = + static_cast, + std::shared_ptr>>( + m.attr("OctreeNode")); octree_node.def("__repr__", [](const OctreeNode &octree_node) { return "OctreeNode instance."; }); docstring::ClassMethodDocInject(m, "OctreeNode", "__init__"); // OctreeInternalNode - py::class_, - std::shared_ptr, OctreeNode> - octree_internal_node(m, "OctreeInternalNode", - "OctreeInternalNode class, containing " - "OctreeNode children."); + auto octree_internal_node = static_cast< + py::class_, + std::shared_ptr, OctreeNode>>( + m.attr("OctreeInternalNode")); octree_internal_node .def("__repr__", [](const OctreeInternalNode &internal_node) { @@ -122,14 +160,10 @@ void pybind_octree(py::module &m) { docstring::ClassMethodDocInject(m, "OctreeInternalNode", "__init__"); // OctreeInternalPointNode - py::class_, - std::shared_ptr, OctreeInternalNode> - octree_internal_point_node( - m, "OctreeInternalPointNode", - "OctreeInternalPointNode class is an " - "OctreeInternalNode with a list of point " - "indices (from point cloud) belonging to " - "children of this node."); + auto octree_internal_point_node = static_cast, + std::shared_ptr, OctreeInternalNode>>( + m.attr("OctreeInternalPointNode")); octree_internal_point_node .def("__repr__", [](const OctreeInternalPointNode &internal_point_node) { @@ -172,9 +206,10 @@ void pybind_octree(py::module &m) { docstring::ClassMethodDocInject(m, "OctreeInternalPointNode", "__init__"); // OctreeLeafNode - py::class_, - std::shared_ptr, OctreeNode> - octree_leaf_node(m, "OctreeLeafNode", "OctreeLeafNode base class."); + auto octree_leaf_node = static_cast< + py::class_, + std::shared_ptr, OctreeNode>>( + m.attr("OctreeLeafNode")); octree_leaf_node .def("__repr__", [](const OctreeLeafNode &leaf_node) { @@ -189,11 +224,10 @@ void pybind_octree(py::module &m) { docstring::ClassMethodDocInject(m, "OctreeLeafNode", "__init__"); // OctreeColorLeafNode - py::class_, - std::shared_ptr, OctreeLeafNode> - octree_color_leaf_node(m, "OctreeColorLeafNode", - "OctreeColorLeafNode class is an " - "OctreeLeafNode containing color."); + auto octree_color_leaf_node = static_cast, + std::shared_ptr, OctreeLeafNode>>( + m.attr("OctreeColorLeafNode")); octree_color_leaf_node .def("__repr__", [](const OctreeColorLeafNode &color_leaf_node) { @@ -223,12 +257,12 @@ void pybind_octree(py::module &m) { octree_color_leaf_node); // OctreePointColorLeafNode - py::class_, - std::shared_ptr, OctreeLeafNode> - octree_point_color_leaf_node(m, "OctreePointColorLeafNode", - "OctreePointColorLeafNode class is an " - "OctreeLeafNode containing color."); + auto octree_point_color_leaf_node = + static_cast, + std::shared_ptr, + OctreeLeafNode>>( + m.attr("OctreePointColorLeafNode")); octree_point_color_leaf_node .def("__repr__", [](const OctreePointColorLeafNode &color_leaf_node) { @@ -264,9 +298,9 @@ void pybind_octree(py::module &m) { octree_point_color_leaf_node); // Octree - py::class_, std::shared_ptr, - Geometry3D> - octree(m, "Octree", "Octree datastructure."); + auto octree = static_cast, + std::shared_ptr, Geometry3D>>( + m.attr("Octree")); py::detail::bind_default_constructor(octree); py::detail::bind_copy_functions(octree); octree.def(py::init([](size_t max_depth) { return new Octree(max_depth); }), @@ -344,7 +378,5 @@ void pybind_octree(py::module &m) { {{"voxel_grid", "geometry.VoxelGrid: The source voxel grid."}}); } -void pybind_octree_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/pointcloud.cpp b/cpp/pybind/geometry/pointcloud.cpp index 2af8319e93d..9a77636f628 100644 --- a/cpp/pybind/geometry/pointcloud.cpp +++ b/cpp/pybind/geometry/pointcloud.cpp @@ -19,13 +19,20 @@ namespace open3d { namespace geometry { -void pybind_pointcloud(py::module &m) { +void pybind_pointcloud_declarations(py::module &m) { py::class_, std::shared_ptr, Geometry3D> pointcloud(m, "PointCloud", "PointCloud class. A point cloud consists of point " "coordinates, and optionally point colors and point " "normals."); +} + +void pybind_pointcloud_definitions(py::module &m) { + auto pointcloud = + static_cast, + std::shared_ptr, Geometry3D>>( + m.attr("PointCloud")); py::detail::bind_default_constructor(pointcloud); py::detail::bind_copy_functions(pointcloud); pointcloud @@ -413,7 +420,5 @@ camera. Given depth value d at (u, v) image coordinate, the corresponding 3d poi {"extrnsic", "Extrinsic parameters of the camera."}}); } -void pybind_pointcloud_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/tetramesh.cpp b/cpp/pybind/geometry/tetramesh.cpp index 5ef8ce0d757..62e7ec7e807 100644 --- a/cpp/pybind/geometry/tetramesh.cpp +++ b/cpp/pybind/geometry/tetramesh.cpp @@ -15,16 +15,22 @@ namespace open3d { namespace geometry { -void pybind_tetramesh(py::module &m) { +void pybind_tetramesh_declarations(py::module &m) { py::class_, std::shared_ptr, MeshBase> trianglemesh(m, "TetraMesh", "TetraMesh class. Tetra mesh contains vertices " "and tetrahedra represented by the indices to the " "vertices."); - py::detail::bind_default_constructor(trianglemesh); - py::detail::bind_copy_functions(trianglemesh); - trianglemesh +} +void pybind_tetramesh_definitions(py::module &m) { + auto tetramesh = + static_cast, + std::shared_ptr, MeshBase>>( + m.attr("TetraMesh")); + py::detail::bind_default_constructor(tetramesh); + py::detail::bind_copy_functions(tetramesh); + tetramesh .def(py::init &, const std::vector &>(), @@ -95,7 +101,5 @@ void pybind_tetramesh(py::module &m) { {{"point_cloud", "A PointCloud."}}); } -void pybind_tetramesh_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/trianglemesh.cpp b/cpp/pybind/geometry/trianglemesh.cpp index 311aa311b86..1c0fbd8c365 100644 --- a/cpp/pybind/geometry/trianglemesh.cpp +++ b/cpp/pybind/geometry/trianglemesh.cpp @@ -16,7 +16,7 @@ namespace open3d { namespace geometry { -void pybind_trianglemesh(py::module &m) { +void pybind_trianglemesh_declarations(py::module &m) { py::class_, std::shared_ptr, MeshBase> trianglemesh(m, "TriangleMesh", @@ -24,6 +24,12 @@ void pybind_trianglemesh(py::module &m) { "and triangles represented by the indices to the " "vertices. Optionally, the mesh may also contain " "triangle normals, vertex normals and vertex colors."); +} +void pybind_trianglemesh_definitions(py::module &m) { + auto trianglemesh = + static_cast, + std::shared_ptr, MeshBase>>( + m.attr("TriangleMesh")); py::detail::bind_default_constructor(trianglemesh); py::detail::bind_copy_functions(trianglemesh); trianglemesh @@ -779,7 +785,5 @@ void pybind_trianglemesh(py::module &m) { {"scale", "Scale the complete Mobius strip."}}); } -void pybind_trianglemesh_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/geometry/voxelgrid.cpp b/cpp/pybind/geometry/voxelgrid.cpp index a50efa8c17b..591d8705af6 100644 --- a/cpp/pybind/geometry/voxelgrid.cpp +++ b/cpp/pybind/geometry/voxelgrid.cpp @@ -20,9 +20,18 @@ namespace open3d { namespace geometry { -void pybind_voxelgrid(py::module &m) { +void pybind_voxelgrid_declarations(py::module &m) { py::class_> voxel( m, "Voxel", "Base Voxel class, containing grid id and color"); + py::class_, std::shared_ptr, + Geometry3D> + voxelgrid(m, "VoxelGrid", + "VoxelGrid is a collection of voxels which are aligned " + "in grid."); +} +void pybind_voxelgrid_definitions(py::module &m) { + auto voxel = static_cast>>( + m.attr("Voxel")); py::detail::bind_default_constructor(voxel); py::detail::bind_copy_functions(voxel); voxel.def("__repr__", @@ -50,12 +59,10 @@ void pybind_voxelgrid(py::module &m) { .def_readwrite( "color", &Voxel::color_, "Float64 numpy array of shape (3,): Color of the voxel."); - - py::class_, std::shared_ptr, - Geometry3D> - voxelgrid(m, "VoxelGrid", - "VoxelGrid is a collection of voxels which are aligned " - "in grid."); + auto voxelgrid = + static_cast, + std::shared_ptr, Geometry3D>>( + m.attr("VoxelGrid")); py::detail::bind_default_constructor(voxelgrid); py::detail::bind_copy_functions(voxelgrid); voxelgrid @@ -230,7 +237,5 @@ void pybind_voxelgrid(py::module &m) { "Maximum boundary point for the VoxelGrid to create."}}); } -void pybind_voxelgrid_methods(py::module &m) {} - } // namespace geometry } // namespace open3d diff --git a/cpp/pybind/io/class_io.cpp b/cpp/pybind/io/class_io.cpp index 2ba0f6fad88..137d1784708 100644 --- a/cpp/pybind/io/class_io.cpp +++ b/cpp/pybind/io/class_io.cpp @@ -83,7 +83,7 @@ static const std::unordered_map "If set to true a progress bar is visualized in the console"}, }; -void pybind_class_io(py::module &m_io) { +void pybind_class_io_declarations(py::module &m_io) { py::enum_ geom_type(m_io, "FileGeometry", py::arithmetic()); // Trick to write docs without listing the members in the enum class again. geom_type.attr("__doc__") = docstring::static_property( @@ -96,6 +96,8 @@ void pybind_class_io(py::module &m_io) { .value("CONTAINS_LINES", FileGeometry::CONTAINS_LINES) .value("CONTAINS_TRIANGLES", FileGeometry::CONTAINS_TRIANGLES) .export_values(); +} +void pybind_class_io_definitions(py::module &m_io) { m_io.def( "read_file_geometry_type", &ReadFileGeometryType, "Returns the type of geometry of the file. This is a faster way of " diff --git a/cpp/pybind/io/io.cpp b/cpp/pybind/io/io.cpp index 63afc83e85a..27c6c32e705 100644 --- a/cpp/pybind/io/io.cpp +++ b/cpp/pybind/io/io.cpp @@ -12,12 +12,21 @@ namespace open3d { namespace io { -void pybind_io(py::module &m) { +void pybind_io_declarations(py::module &m) { py::module m_io = m.def_submodule("io"); - pybind_class_io(m_io); - pybind_rpc(m_io); + pybind_class_io_declarations(m_io); + pybind_rpc_declarations(m_io); #ifdef BUILD_AZURE_KINECT - pybind_sensor(m_io); + pybind_sensor_declarations(m_io); +#endif +} + +void pybind_io_definitions(py::module &m) { + auto m_io = static_cast(m.attr("io")); + pybind_class_io_definitions(m_io); + pybind_rpc_definitions(m_io); +#ifdef BUILD_AZURE_KINECT + pybind_sensor_definitions(m_io); #endif } diff --git a/cpp/pybind/io/io.h b/cpp/pybind/io/io.h index 6d54dfeada1..c08a1d1fa90 100644 --- a/cpp/pybind/io/io.h +++ b/cpp/pybind/io/io.h @@ -12,14 +12,18 @@ namespace open3d { namespace io { -void pybind_io(py::module& m); - -void pybind_class_io(py::module& m); - -void pybind_rpc(py::module& m); +void pybind_io_declarations(py::module& m); +void pybind_class_io_declarations(py::module& m); +void pybind_rpc_declarations(py::module& m); +#ifdef BUILD_AZURE_KINECT +void pybind_sensor_declarations(py::module& m); +#endif +void pybind_io_definitions(py::module& m); +void pybind_class_io_definitions(py::module& m); +void pybind_rpc_definitions(py::module& m); #ifdef BUILD_AZURE_KINECT -void pybind_sensor(py::module& m); +void pybind_sensor_definitions(py::module& m); #endif } // namespace io diff --git a/cpp/pybind/io/rpc.cpp b/cpp/pybind/io/rpc.cpp index 8d04d1265c9..590f2bd3d66 100644 --- a/cpp/pybind/io/rpc.cpp +++ b/cpp/pybind/io/rpc.cpp @@ -18,47 +18,58 @@ namespace open3d { namespace io { -void pybind_rpc(py::module& m_io) { - py::module m = m_io.def_submodule("rpc"); - - // this is to cleanly shutdown the zeromq context on windows. - auto atexit = py::module::import("atexit"); - atexit.attr("register")( - py::cpp_function([]() { rpc::DestroyZMQContext(); })); - +void pybind_rpc_declarations(py::module& m_io) { + py::module m_rpc = m_io.def_submodule("rpc"); py::class_>( - m, "_ConnectionBase"); - + m_rpc, "_ConnectionBase"); py::class_, - rpc::ConnectionBase>(m, "Connection", R"doc( + rpc::ConnectionBase>(m_rpc, "Connection", R"doc( The default connection class which uses a ZeroMQ socket. -)doc") - .def(py::init([](std::string address, int connect_timeout, - int timeout) { - return std::shared_ptr( - new rpc::Connection(address, connect_timeout, - timeout)); - }), - "Creates a connection object", - "address"_a = "tcp://127.0.0.1:51454", - "connect_timeout"_a = 5000, "timeout"_a = 10000); - +)doc"); py::class_, - rpc::ConnectionBase>(m, "BufferConnection", R"doc( + rpc::ConnectionBase>(m_rpc, "BufferConnection", R"doc( A connection writing to a memory buffer. -)doc") - .def(py::init<>()) +)doc"); + py::class_>( + m_rpc, "_DummyReceiver", + "Dummy receiver for the server side receiving requests from a " + "client."); +} +void pybind_rpc_definitions(py::module& m_io) { + auto m_rpc = static_cast(m_io.attr("rpc")); + // this is to cleanly shutdown the zeromq context on windows. + auto atexit = py::module::import("atexit"); + atexit.attr("register")( + py::cpp_function([]() { rpc::DestroyZMQContext(); })); + auto connection = static_cast< + py::class_, + rpc::ConnectionBase>>(m_rpc.attr("Connection")); + connection.def( + py::init([](std::string address, int connect_timeout, int timeout) { + return std::shared_ptr( + new rpc::Connection(address, connect_timeout, timeout)); + }), + "Creates a connection object", + "address"_a = "tcp://127.0.0.1:51454", "connect_timeout"_a = 5000, + "timeout"_a = 10000); + + auto buffer_connection = + static_cast, + rpc::ConnectionBase>>( + m_rpc.attr("BufferConnection")); + buffer_connection.def(py::init<>()) .def( "get_buffer", [](const rpc::BufferConnection& self) { return py::bytes(self.buffer().str()); }, "Returns a copy of the buffer."); - - py::class_>( - m, "_DummyReceiver", - "Dummy receiver for the server side receiving requests from a " - "client.") + auto dummy_receiver = + static_cast>>( + m_rpc.attr("_DummyReceiver")); + dummy_receiver .def(py::init([](std::string address, int timeout) { return std::shared_ptr( new rpc::DummyReceiver(address, timeout)); @@ -73,15 +84,15 @@ A connection writing to a memory buffer. "function blocks until the mainloop is done with processing " "messages that have already been received."); - m.def("destroy_zmq_context", &rpc::DestroyZMQContext, - "Destroys the ZMQ context."); + m_rpc.def("destroy_zmq_context", &rpc::DestroyZMQContext, + "Destroys the ZMQ context."); - m.def("set_point_cloud", &rpc::SetPointCloud, "pcd"_a, "path"_a = "", - "time"_a = 0, "layer"_a = "", - "connection"_a = std::shared_ptr(), - "Sends a point cloud message to a viewer."); + m_rpc.def("set_point_cloud", &rpc::SetPointCloud, "pcd"_a, "path"_a = "", + "time"_a = 0, "layer"_a = "", + "connection"_a = std::shared_ptr(), + "Sends a point cloud message to a viewer."); docstring::FunctionDocInject( - m, "set_point_cloud", + m_rpc, "set_point_cloud", { {"pcd", "Point cloud object."}, {"path", "A path descriptor, e.g., 'mygroup/points'."}, @@ -92,14 +103,14 @@ A connection writing to a memory buffer. "the connection."}, }); - m.def("set_triangle_mesh", - py::overload_cast>( - &rpc::SetTriangleMesh), - "mesh"_a, "path"_a = "", "time"_a = 0, "layer"_a = "", - "connection"_a = std::shared_ptr(), - R"doc(Sends a triangle mesh to a viewer. + m_rpc.def("set_triangle_mesh", + py::overload_cast>( + &rpc::SetTriangleMesh), + "mesh"_a, "path"_a = "", "time"_a = 0, "layer"_a = "", + "connection"_a = std::shared_ptr(), + R"doc(Sends a triangle mesh to a viewer. Args: mesh (o3d.geometry.TriangleMesh): The triangle mesh. path (str): The path in the scene graph. @@ -111,14 +122,14 @@ A connection writing to a memory buffer. Returns True if the data was successfully received. )doc"); - m.def("set_triangle_mesh", - py::overload_cast>( - &rpc::SetTriangleMesh), - "mesh"_a, "path"_a = "", "time"_a = 0, "layer"_a = "", - "connection"_a = std::shared_ptr(), - R"doc(Sends a triangle mesh to a viewer. + m_rpc.def("set_triangle_mesh", + py::overload_cast>( + &rpc::SetTriangleMesh), + "mesh"_a, "path"_a = "", "time"_a = 0, "layer"_a = "", + "connection"_a = std::shared_ptr(), + R"doc(Sends a triangle mesh to a viewer. Args: mesh (o3d.t.geometry.TriangleMesh): The triangle mesh. path (str): The path in the scene graph. @@ -130,23 +141,23 @@ A connection writing to a memory buffer. Returns True if the data was successfully received. )doc"); - m.def("set_mesh_data", &rpc::SetMeshData, "path"_a = "", "time"_a = 0, - "layer"_a = "", "vertices"_a = core::Tensor({0}, core::Float32), - "vertex_attributes"_a = std::map(), - "faces"_a = core::Tensor({0}, core::Int32), - "face_attributes"_a = std::map(), - "lines"_a = core::Tensor({0}, core::Int32), - "line_attributes"_a = std::map(), - "material"_a = "", - "material_scalar_attributes"_a = std::map(), - "material_vector_attributes"_a = - std::map(), - "texture_maps"_a = std::map(), - "o3d_type"_a = "", - "connection"_a = std::shared_ptr(), - "Sends a set_mesh_data message."); + m_rpc.def("set_mesh_data", &rpc::SetMeshData, "path"_a = "", "time"_a = 0, + "layer"_a = "", "vertices"_a = core::Tensor({0}, core::Float32), + "vertex_attributes"_a = std::map(), + "faces"_a = core::Tensor({0}, core::Int32), + "face_attributes"_a = std::map(), + "lines"_a = core::Tensor({0}, core::Int32), + "line_attributes"_a = std::map(), + "material"_a = "", + "material_scalar_attributes"_a = std::map(), + "material_vector_attributes"_a = + std::map(), + "texture_maps"_a = std::map(), + "o3d_type"_a = "", + "connection"_a = std::shared_ptr(), + "Sends a set_mesh_data message."); docstring::FunctionDocInject( - m, "set_mesh_data", + m_rpc, "set_mesh_data", { {"path", "A path descriptor, e.g., 'mygroup/points'."}, {"time", "The time associated with this data."}, @@ -181,12 +192,12 @@ A connection writing to a memory buffer. "the connection."}, }); - m.def("set_legacy_camera", &rpc::SetLegacyCamera, "camera"_a, "path"_a = "", - "time"_a = 0, "layer"_a = "", - "connection"_a = std::shared_ptr(), - "Sends a PinholeCameraParameters object."); + m_rpc.def("set_legacy_camera", &rpc::SetLegacyCamera, "camera"_a, + "path"_a = "", "time"_a = 0, "layer"_a = "", + "connection"_a = std::shared_ptr(), + "Sends a PinholeCameraParameters object."); docstring::FunctionDocInject( - m, "set_legacy_camera", + m_rpc, "set_legacy_camera", { {"path", "A path descriptor, e.g., 'mygroup/camera'."}, {"time", "The time associated with this data."}, @@ -196,11 +207,11 @@ A connection writing to a memory buffer. "the connection."}, }); - m.def("set_time", &rpc::SetTime, "time"_a, - "connection"_a = std::shared_ptr(), - "Sets the time in the external visualizer."); + m_rpc.def("set_time", &rpc::SetTime, "time"_a, + "connection"_a = std::shared_ptr(), + "Sets the time in the external visualizer."); docstring::FunctionDocInject( - m, "set_time", + m_rpc, "set_time", { {"time", "The time value to set."}, {"connection", @@ -208,11 +219,11 @@ A connection writing to a memory buffer. "the connection."}, }); - m.def("set_active_camera", &rpc::SetActiveCamera, "path"_a, - "connection"_a = std::shared_ptr(), - "Sets the object with the specified path as the active camera."); + m_rpc.def("set_active_camera", &rpc::SetActiveCamera, "path"_a, + "connection"_a = std::shared_ptr(), + "Sets the object with the specified path as the active camera."); docstring::FunctionDocInject( - m, "set_active_camera", + m_rpc, "set_active_camera", { {"path", "A path descriptor, e.g., 'mygroup/camera'."}, {"connection", @@ -220,8 +231,8 @@ A connection writing to a memory buffer. "the connection."}, }); - m.def("data_buffer_to_meta_geometry", &rpc::DataBufferToMetaGeometry, - "data"_a, R"doc( + m_rpc.def("data_buffer_to_meta_geometry", &rpc::DataBufferToMetaGeometry, + "data"_a, R"doc( This function returns the geometry, the path and the time stored in a SetMeshData message. data must contain the Request header message followed by the SetMeshData message. The function returns None for the geometry if not diff --git a/cpp/pybind/io/sensor.cpp b/cpp/pybind/io/sensor.cpp index 3b07e0d10a5..b9108d69116 100644 --- a/cpp/pybind/io/sensor.cpp +++ b/cpp/pybind/io/sensor.cpp @@ -16,7 +16,19 @@ namespace open3d { namespace io { -void pybind_sensor(py::module &m) { +void pybind_sensor_declarations(py::module &m) { + py::class_ azure_kinect_sensor_config( + m, "AzureKinectSensorConfig", "AzureKinect sensor configuration."); + py::class_ azure_kinect_mkv_metadata( + m, "AzureKinectMKVMetadata", "AzureKinect mkv metadata."); + py::class_ azure_kinect_sensor(m, "AzureKinectSensor", + "AzureKinect sensor."); + py::class_ azure_kinect_recorder( + m, "AzureKinectRecorder", "AzureKinect recorder."); + py::class_ azure_kinect_mkv_reader( + m, "AzureKinectMKVReader", "AzureKinect mkv file reader."); +} +void pybind_sensor_definitions(py::module &m) { static const std::unordered_map map_shared_argument_docstrings = { {"sensor_index", "The selected device index."}, @@ -29,8 +41,9 @@ void pybind_sensor(py::module &m) { "visualizer."}}; // Class kinect config - py::class_ azure_kinect_sensor_config( - m, "AzureKinectSensorConfig", "AzureKinect sensor configuration."); + auto azure_kinect_sensor_config = + static_cast>( + m.attr("AzureKinectSensorConfig")); py::detail::bind_default_constructor( azure_kinect_sensor_config); azure_kinect_sensor_config.def( @@ -39,9 +52,8 @@ void pybind_sensor(py::module &m) { return new AzureKinectSensorConfig(config); }), "config"_a); - - py::class_ azure_kinect_mkv_metadata( - m, "AzureKinectMKVMetadata", "AzureKinect mkv metadata."); + auto azure_kinect_mkv_metadata = static_cast>( + m.attr("AzureKinectMKVMetadata")); py::detail::bind_default_constructor( azure_kinect_mkv_metadata); azure_kinect_mkv_metadata @@ -53,9 +65,8 @@ void pybind_sensor(py::module &m) { "Length of the video (usec)"); // Class sensor - py::class_ azure_kinect_sensor(m, "AzureKinectSensor", - "AzureKinect sensor."); - + auto azure_kinect_sensor = static_cast>( + m.attr("AzureKinectSensor")); azure_kinect_sensor.def( py::init([](const AzureKinectSensorConfig &sensor_config) { return new AzureKinectSensor(sensor_config); @@ -78,9 +89,8 @@ void pybind_sensor(py::module &m) { map_shared_argument_docstrings); // Class recorder - py::class_ azure_kinect_recorder( - m, "AzureKinectRecorder", "AzureKinect recorder."); - + auto azure_kinect_recorder = static_cast>( + m.attr("AzureKinectRecorder")); azure_kinect_recorder.def( py::init([](const AzureKinectSensorConfig &sensor_config, size_t sensor_index) { @@ -113,8 +123,8 @@ void pybind_sensor(py::module &m) { map_shared_argument_docstrings); // Class mkv reader - py::class_ azure_kinect_mkv_reader( - m, "AzureKinectMKVReader", "AzureKinect mkv file reader."); + auto azure_kinect_mkv_reader = + static_cast>(m.attr("AzureKinectMKVReader")); azure_kinect_mkv_reader.def(py::init([]() { return MKVReader(); })); azure_kinect_mkv_reader .def("is_opened", &MKVReader::IsOpened, diff --git a/cpp/pybind/ml/contrib/contrib.cpp b/cpp/pybind/ml/contrib/contrib.cpp index 9ed422ee12d..bd3b87d45cd 100644 --- a/cpp/pybind/ml/contrib/contrib.cpp +++ b/cpp/pybind/ml/contrib/contrib.cpp @@ -15,11 +15,13 @@ namespace open3d { namespace ml { namespace contrib { -void pybind_contrib(py::module& m) { +void pybind_contrib_declarations(py::module& m) { py::module m_contrib = m.def_submodule("contrib"); - - pybind_contrib_subsample(m_contrib); - pybind_contrib_iou(m_contrib); +} +void pybind_contrib_definitions(py::module& m) { + auto m_contrib = static_cast(m.attr("contrib")); + pybind_contrib_subsample_definitions(m_contrib); + pybind_contrib_iou_definitions(m_contrib); } } // namespace contrib diff --git a/cpp/pybind/ml/contrib/contrib.h b/cpp/pybind/ml/contrib/contrib.h index 9caff96487b..1b13668843d 100644 --- a/cpp/pybind/ml/contrib/contrib.h +++ b/cpp/pybind/ml/contrib/contrib.h @@ -14,9 +14,11 @@ namespace open3d { namespace ml { namespace contrib { -void pybind_contrib(py::module &m); -void pybind_contrib_subsample(py::module &m_contrib); -void pybind_contrib_iou(py::module &m_contrib); +void pybind_contrib_declarations(py::module &m); + +void pybind_contrib_definitions(py::module &m); +void pybind_contrib_subsample_definitions(py::module &m_contrib); +void pybind_contrib_iou_definitions(py::module &m_contrib); } // namespace contrib } // namespace ml diff --git a/cpp/pybind/ml/contrib/contrib_subsample.cpp b/cpp/pybind/ml/contrib/contrib_subsample.cpp index b7946a69451..ce028afff07 100644 --- a/cpp/pybind/ml/contrib/contrib_subsample.cpp +++ b/cpp/pybind/ml/contrib/contrib_subsample.cpp @@ -343,7 +343,7 @@ const py::object Subsample(py::array points, } } -void pybind_contrib_subsample(py::module& m_contrib) { +void pybind_contrib_subsample_definitions(py::module& m_contrib) { m_contrib.def("subsample", &Subsample, "points"_a, "features"_a = py::none(), "classes"_a = py::none(), "sampleDl"_a = 0.1, "verbose"_a = 0); diff --git a/cpp/pybind/ml/contrib/iou.cpp b/cpp/pybind/ml/contrib/iou.cpp index 1f14af19814..58a83e4b593 100644 --- a/cpp/pybind/ml/contrib/iou.cpp +++ b/cpp/pybind/ml/contrib/iou.cpp @@ -117,7 +117,7 @@ py::array Iou3dCUDA(py::array boxes_a, py::array boxes_b) { } #endif -void pybind_contrib_iou(py::module& m_contrib) { +void pybind_contrib_iou_definitions(py::module& m_contrib) { m_contrib.def("iou_bev_cpu", &IouBevCPU, "boxes_a"_a, "boxes_b"_a); m_contrib.def("iou_3d_cpu", &Iou3dCPU, "boxes_a"_a, "boxes_b"_a); diff --git a/cpp/pybind/ml/ml.cpp b/cpp/pybind/ml/ml.cpp index de71caf71b0..c0e4c882241 100644 --- a/cpp/pybind/ml/ml.cpp +++ b/cpp/pybind/ml/ml.cpp @@ -13,9 +13,14 @@ namespace open3d { namespace ml { -void pybind_ml(py::module &m) { +void pybind_ml_declarations(py::module &m) { py::module m_ml = m.def_submodule("ml"); - contrib::pybind_contrib(m_ml); + contrib::pybind_contrib_declarations(m_ml); +} + +void pybind_ml_definitions(py::module &m) { + auto m_ml = static_cast(m.attr("ml")); + contrib::pybind_contrib_definitions(m_ml); } } // namespace ml diff --git a/cpp/pybind/ml/ml.h b/cpp/pybind/ml/ml.h index 3cbbc0e6a8e..454771411b0 100644 --- a/cpp/pybind/ml/ml.h +++ b/cpp/pybind/ml/ml.h @@ -12,7 +12,8 @@ namespace open3d { namespace ml { -void pybind_ml(py::module& m); +void pybind_ml_declarations(py::module& m); +void pybind_ml_definitions(py::module& m); } // namespace ml } // namespace open3d diff --git a/cpp/pybind/open3d_pybind.cpp b/cpp/pybind/open3d_pybind.cpp index a6b71969804..5b587408398 100644 --- a/cpp/pybind/open3d_pybind.cpp +++ b/cpp/pybind/open3d_pybind.cpp @@ -38,17 +38,26 @@ PYBIND11_MODULE(pybind, m) { // The binding order matters: if a class haven't been binded, binding the // user of this class will result in "could not convert default argument // into a Python object" error. - utility::pybind_utility(m); - - camera::pybind_camera(m); - core::pybind_core(m); + utility::pybind_utility_declarations(m); + camera::pybind_camera_declarations(m); + core::pybind_core_declarations(m); data::pybind_data(m); - geometry::pybind_geometry(m); - t::pybind_t(m); - ml::pybind_ml(m); - io::pybind_io(m); - pipelines::pybind_pipelines(m); - visualization::pybind_visualization(m); + geometry::pybind_geometry_declarations(m); + t::pybind_t_declarations(m); + ml::pybind_ml_declarations(m); + io::pybind_io_declarations(m); + pipelines::pybind_pipelines_declarations(m); + visualization::pybind_visualization_declarations(m); + + utility::pybind_utility_definitions(m); + camera::pybind_camera_definitions(m); + core::pybind_core_definitions(m); + geometry::pybind_geometry_definitions(m); + t::pybind_t_definitions(m); + ml::pybind_ml_definitions(m); + io::pybind_io_definitions(m); + pipelines::pybind_pipelines_definitions(m); + visualization::pybind_visualization_definitions(m); // pybind11 will internally manage the lifetime of default arguments for // function bindings. Since these objects will live longer than the memory diff --git a/cpp/pybind/pipelines/color_map/color_map.cpp b/cpp/pybind/pipelines/color_map/color_map.cpp index 141d0bdab28..dfc57470bb6 100644 --- a/cpp/pybind/pipelines/color_map/color_map.cpp +++ b/cpp/pybind/pipelines/color_map/color_map.cpp @@ -19,7 +19,19 @@ namespace open3d { namespace pipelines { namespace color_map { -void pybind_color_map_options(py::module &m) { +void pybind_color_map_declarations(py::module &m) { + py::module m_color_map = + m.def_submodule("color_map", "Color map optimization pipeline"); + py::class_ + rigid_optimizer_option(m_color_map, "RigidOptimizerOption", + "Rigid optimizer option class."); + py::class_ + non_rigid_optimizer_option(m_color_map, "NonRigidOptimizerOption", + "Non Rigid optimizer option class."); +} + +void pybind_color_map_definitions(py::module &m) { + auto m_color_map = static_cast(m.attr("color_map")); static std::unordered_map colormap_docstrings = { {"non_rigid_camera_coordinate", "bool: (Default ``False``) Set to ``True`` to enable non-rigid " @@ -86,10 +98,9 @@ void pybind_color_map_options(py::module &m) { "If specified, the intermediate results will be stored in in the " "debug output dir. Existing files will be overwritten if the " "names are the same."}}; - - py::class_ - rigid_optimizer_option(m, "RigidOptimizerOption", - "Rigid optimizer option class."); + auto rigid_optimizer_option = + static_cast>( + m_color_map.attr("RigidOptimizerOption")); rigid_optimizer_option.def( py::init([](int maximum_iteration, double maximum_allowable_depth, double depth_threshold_for_visibility_check, @@ -120,12 +131,11 @@ void pybind_color_map_options(py::module &m) { "image_boundary_margin"_a = 10, "invisible_vertex_color_knn"_a = 3, "debug_output_dir"_a = ""); - docstring::ClassMethodDocInject(m, "RigidOptimizerOption", "__init__", - colormap_docstrings); - - py::class_ - non_rigid_optimizer_option(m, "NonRigidOptimizerOption", - "Non Rigid optimizer option class."); + docstring::ClassMethodDocInject(m_color_map, "RigidOptimizerOption", + "__init__", colormap_docstrings); + auto non_rigid_optimizer_option = static_cast< + py::class_>( + m_color_map.attr("NonRigidOptimizerOption")); non_rigid_optimizer_option.def( py::init([](int number_of_vertical_anchors, double non_rigid_anchor_point_weight, @@ -164,23 +174,14 @@ void pybind_color_map_options(py::module &m) { "image_boundary_margin"_a = 10, "invisible_vertex_color_knn"_a = 3, "debug_output_dir"_a = ""); - docstring::ClassMethodDocInject(m, "NonRigidOptimizerOption", "__init__", - colormap_docstrings); -} - -void pybind_color_map_classes(py::module &m) { - m.def("run_rigid_optimizer", &pipelines::color_map::RunRigidOptimizer, - "Run rigid optimization."); - m.def("run_non_rigid_optimizer", - &pipelines::color_map::RunNonRigidOptimizer, - "Run non-rigid optimization."); -} - -void pybind_color_map(py::module &m) { - py::module m_submodule = - m.def_submodule("color_map", "Color map optimization pipeline"); - pybind_color_map_options(m_submodule); - pybind_color_map_classes(m_submodule); + docstring::ClassMethodDocInject(m_color_map, "NonRigidOptimizerOption", + "__init__", colormap_docstrings); + m_color_map.def("run_rigid_optimizer", + &pipelines::color_map::RunRigidOptimizer, + "Run rigid optimization."); + m_color_map.def("run_non_rigid_optimizer", + &pipelines::color_map::RunNonRigidOptimizer, + "Run non-rigid optimization."); } } // namespace color_map diff --git a/cpp/pybind/pipelines/color_map/color_map.h b/cpp/pybind/pipelines/color_map/color_map.h index 12bffb93c91..bea1a3c84a4 100644 --- a/cpp/pybind/pipelines/color_map/color_map.h +++ b/cpp/pybind/pipelines/color_map/color_map.h @@ -13,8 +13,9 @@ namespace open3d { namespace pipelines { namespace color_map { -void pybind_color_map(py::module &m); +void pybind_color_map_declarations(py::module &m); +void pybind_color_map_definitions(py::module &m); -} +} // namespace color_map } // namespace pipelines } // namespace open3d diff --git a/cpp/pybind/pipelines/integration/integration.cpp b/cpp/pybind/pipelines/integration/integration.cpp index d0482bcc8fe..1ffca9843c6 100644 --- a/cpp/pybind/pipelines/integration/integration.cpp +++ b/cpp/pybind/pipelines/integration/integration.cpp @@ -38,10 +38,12 @@ class PyTSDFVolume : public TSDFVolumeBase { } }; -void pybind_integration_classes(py::module &m) { +void pybind_integration_declarations(py::module &m) { + py::module m_integration = + m.def_submodule("integration", "Integration pipeline."); // open3d.integration.TSDFVolumeColorType py::enum_ tsdf_volume_color_type( - m, "TSDFVolumeColorType", py::arithmetic()); + m_integration, "TSDFVolumeColorType", py::arithmetic()); tsdf_volume_color_type.value("NoColor", TSDFVolumeColorType::NoColor) .value("RGB8", TSDFVolumeColorType::RGB8) .value("Gray32", TSDFVolumeColorType::Gray32) @@ -52,10 +54,8 @@ void pybind_integration_classes(py::module &m) { return "Enum class for TSDFVolumeColorType."; }), py::none(), py::none(), ""); - - // open3d.integration.TSDFVolume py::class_> tsdfvolume( - m, "TSDFVolume", R"(Base class of the Truncated + m_integration, "TSDFVolume", R"(Base class of the Truncated Signed Distance Function (TSDF) volume This volume is usually used to integrate surface data (e.g., a series of RGB-D images) into a Mesh or PointCloud. The basic technique is presented in the following paper: @@ -65,6 +65,40 @@ A volumetric method for building complex models from range images B. Curless and M. Levoy In SIGGRAPH, 1996)"); + py::class_, TSDFVolume> + uniform_tsdfvolume( + m_integration, "UniformTSDFVolume", + "UniformTSDFVolume implements the classic TSDF " + "volume with uniform voxel grid (Curless and Levoy 1996)."); + py::class_, TSDFVolume> + scalable_tsdfvolume(m_integration, "ScalableTSDFVolume", R"(The +ScalableTSDFVolume implements a more memory efficient data structure for +volumetric integration. + +This implementation is based on the following repository: +https://github.com/qianyizh/ElasticReconstruction/tree/master/Integrate + +An observed depth pixel gives two types of information: (a) an approximation +of the nearby surface, and (b) empty space from the camera to the surface. +They induce two core concepts of volumetric integration: weighted average of +a truncated signed distance function (TSDF), and carving. The weighted +average of TSDF is great in addressing the Gaussian noise along surface +normal and producing a smooth surface output. The carving is great in +removing outlier structures like floating noise pixels and bumps along +structure edges. + +Ref: Dense Scene Reconstruction with Points of Interest + +Q.-Y. Zhou and V. Koltun + +In SIGGRAPH, 2013)"); +} +void pybind_integration_definitions(py::module &m) { + auto m_integration = static_cast(m.attr("integration")); + // open3d.integration.TSDFVolume + auto tsdfvolume = + static_cast>>( + m_integration.attr("TSDFVolume")); tsdfvolume .def("reset", &TSDFVolume::Reset, "Function to reset the TSDFVolume") @@ -83,21 +117,21 @@ In SIGGRAPH, 1996)"); .def_readwrite("color_type", &TSDFVolume::color_type_, "integration.TSDFVolumeColorType: Color type of the " "TSDF volume."); - docstring::ClassMethodDocInject(m, "TSDFVolume", "extract_point_cloud"); - docstring::ClassMethodDocInject(m, "TSDFVolume", "extract_triangle_mesh"); + docstring::ClassMethodDocInject(m_integration, "TSDFVolume", + "extract_point_cloud"); + docstring::ClassMethodDocInject(m_integration, "TSDFVolume", + "extract_triangle_mesh"); docstring::ClassMethodDocInject( - m, "TSDFVolume", "integrate", + m_integration, "TSDFVolume", "integrate", {{"image", "RGBD image."}, {"intrinsic", "Pinhole camera intrinsic parameters."}, {"extrinsic", "Extrinsic parameters."}}); - docstring::ClassMethodDocInject(m, "TSDFVolume", "reset"); + docstring::ClassMethodDocInject(m_integration, "TSDFVolume", "reset"); // open3d.integration.UniformTSDFVolume: open3d.integration.TSDFVolume - py::class_, TSDFVolume> - uniform_tsdfvolume( - m, "UniformTSDFVolume", - "UniformTSDFVolume implements the classic TSDF " - "volume with uniform voxel grid (Curless and Levoy 1996)."); + auto uniform_tsdfvolume = static_cast, TSDFVolume>>( + m_integration.attr("UniformTSDFVolume")); py::detail::bind_copy_functions(uniform_tsdfvolume); uniform_tsdfvolume .def(py::init([](double length, int resolution, double sdf_trunc, @@ -142,32 +176,13 @@ In SIGGRAPH, 1996)"); .def_readwrite("resolution", &UniformTSDFVolume::resolution_, "Resolution over the total length, where " "``voxel_length = length / resolution``"); - docstring::ClassMethodDocInject(m, "UniformTSDFVolume", + docstring::ClassMethodDocInject(m_integration, "UniformTSDFVolume", "extract_voxel_point_cloud"); // open3d.integration.ScalableTSDFVolume: open3d.integration.TSDFVolume - py::class_, TSDFVolume> - scalable_tsdfvolume(m, "ScalableTSDFVolume", R"(The -ScalableTSDFVolume implements a more memory efficient data structure for -volumetric integration. - -This implementation is based on the following repository: -https://github.com/qianyizh/ElasticReconstruction/tree/master/Integrate - -An observed depth pixel gives two types of information: (a) an approximation -of the nearby surface, and (b) empty space from the camera to the surface. -They induce two core concepts of volumetric integration: weighted average of -a truncated signed distance function (TSDF), and carving. The weighted -average of TSDF is great in addressing the Gaussian noise along surface -normal and producing a smooth surface output. The carving is great in -removing outlier structures like floating noise pixels and bumps along -structure edges. - -Ref: Dense Scene Reconstruction with Points of Interest - -Q.-Y. Zhou and V. Koltun - -In SIGGRAPH, 2013)"); + auto scalable_tsdfvolume = static_cast, TSDFVolume>>( + m_integration.attr("ScalableTSDFVolume")); py::detail::bind_copy_functions(scalable_tsdfvolume); scalable_tsdfvolume .def(py::init([](double voxel_length, double sdf_trunc, @@ -193,21 +208,10 @@ In SIGGRAPH, 2013)"); &ScalableTSDFVolume::ExtractVoxelPointCloud, "Debug function to extract the voxel data into a point " "cloud."); - docstring::ClassMethodDocInject(m, "ScalableTSDFVolume", + docstring::ClassMethodDocInject(m_integration, "ScalableTSDFVolume", "extract_voxel_point_cloud"); } -void pybind_integration_methods(py::module &m) { - // Currently empty -} - -void pybind_integration(py::module &m) { - py::module m_submodule = - m.def_submodule("integration", "Integration pipeline."); - pybind_integration_classes(m_submodule); - pybind_integration_methods(m_submodule); -} - } // namespace integration } // namespace pipelines } // namespace open3d diff --git a/cpp/pybind/pipelines/integration/integration.h b/cpp/pybind/pipelines/integration/integration.h index ed316740bb5..47c2454a32e 100644 --- a/cpp/pybind/pipelines/integration/integration.h +++ b/cpp/pybind/pipelines/integration/integration.h @@ -13,7 +13,8 @@ namespace open3d { namespace pipelines { namespace integration { -void pybind_integration(py::module &m); +void pybind_integration_declarations(py::module &m); +void pybind_integration_definitions(py::module &m); } // namespace integration } // namespace pipelines diff --git a/cpp/pybind/pipelines/odometry/odometry.cpp b/cpp/pybind/pipelines/odometry/odometry.cpp index 972a4452aad..b365edfc7b7 100644 --- a/cpp/pybind/pipelines/odometry/odometry.cpp +++ b/cpp/pybind/pipelines/odometry/odometry.cpp @@ -41,10 +41,50 @@ class PyRGBDOdometryJacobian : public RGBDOdometryJacobianBase { } }; -void pybind_odometry_classes(py::module &m) { - // open3d.odometry.OdometryOption +void pybind_odometry_declarations(py::module &m) { + py::module m_odometry = m.def_submodule("odometry", "Odometry pipeline."); py::class_ odometry_option( - m, "OdometryOption", "Class that defines Odometry options."); + m_odometry, "OdometryOption", + "Class that defines Odometry options."); + py::class_> + jacobian( + m_odometry, "RGBDOdometryJacobian", + "Base class that computes Jacobian from two RGB-D images."); + py::class_, + RGBDOdometryJacobian> + jacobian_color(m_odometry, "RGBDOdometryJacobianFromColorTerm", + R"(Class to Compute Jacobian using color term. + +Energy: :math:`(I_p-I_q)^2.` + +Reference: + +F. Steinbrucker, J. Sturm, and D. Cremers. + +Real-time visual odometry from dense RGB-D images. + +In ICCV Workshops, 2011.)"); + py::class_, + RGBDOdometryJacobian> + jacobian_hybrid(m_odometry, "RGBDOdometryJacobianFromHybridTerm", + R"(Class to compute Jacobian using hybrid term + +Energy: :math:`(I_p-I_q)^2 + \lambda(D_p-D_q')^2` + +Reference: + +J. Park, Q.-Y. Zhou, and V. Koltun + +Anonymous submission.)"); +} +void pybind_odometry_definitions(py::module &m) { + auto m_odometry = static_cast(m.attr("odometry")); + // open3d.odometry.OdometryOption + auto odometry_option = static_cast>( + m_odometry.attr("OdometryOption")); odometry_option .def(py::init( [](std::vector iteration_number_per_pyramid_level, @@ -102,12 +142,10 @@ void pybind_odometry_classes(py::module &m) { }); // open3d.odometry.RGBDOdometryJacobian - py::class_> - jacobian( - m, "RGBDOdometryJacobian", - "Base class that computes Jacobian from two RGB-D images."); - + auto jacobian = static_cast< + py::class_>>( + m_odometry.attr("RGBDOdometryJacobian")); jacobian.def( "compute_jacobian_and_residual", &RGBDOdometryJacobian::ComputeJacobianAndResidual, @@ -121,21 +159,11 @@ void pybind_odometry_classes(py::module &m) { "corresps"_a); // open3d.odometry.RGBDOdometryJacobianFromColorTerm: RGBDOdometryJacobian - py::class_, - RGBDOdometryJacobian> - jacobian_color(m, "RGBDOdometryJacobianFromColorTerm", - R"(Class to Compute Jacobian using color term. - -Energy: :math:`(I_p-I_q)^2.` - -Reference: - -F. Steinbrucker, J. Sturm, and D. Cremers. - -Real-time visual odometry from dense RGB-D images. - -In ICCV Workshops, 2011.)"); + auto jacobian_color = static_cast, + RGBDOdometryJacobian>>( + m_odometry.attr("RGBDOdometryJacobianFromColorTerm")); py::detail::bind_default_constructor( jacobian_color); py::detail::bind_copy_functions( @@ -146,19 +174,11 @@ In ICCV Workshops, 2011.)"); }); // open3d.odometry.RGBDOdometryJacobianFromHybridTerm: RGBDOdometryJacobian - py::class_, - RGBDOdometryJacobian> - jacobian_hybrid(m, "RGBDOdometryJacobianFromHybridTerm", - R"(Class to compute Jacobian using hybrid term - -Energy: :math:`(I_p-I_q)^2 + \lambda(D_p-D_q')^2` - -Reference: - -J. Park, Q.-Y. Zhou, and V. Koltun - -Anonymous submission.)"); + auto jacobian_hybrid = static_cast, + RGBDOdometryJacobian>>( + m_odometry.attr("RGBDOdometryJacobianFromHybridTerm")); py::detail::bind_default_constructor( jacobian_hybrid); py::detail::bind_copy_functions( @@ -167,20 +187,18 @@ Anonymous submission.)"); "__repr__", [](const RGBDOdometryJacobianFromHybridTerm &te) { return std::string("RGBDOdometryJacobianFromHybridTerm"); }); -} - -void pybind_odometry_methods(py::module &m) { - m.def("compute_rgbd_odometry", &ComputeRGBDOdometry, - py::call_guard(), - "Function to estimate 6D rigid motion from two RGBD image pairs. " - "Output: (is_success, 4x4 motion matrix, 6x6 information matrix).", - "rgbd_source"_a, "rgbd_target"_a, - "pinhole_camera_intrinsic"_a = camera::PinholeCameraIntrinsic(), - "odo_init"_a = Eigen::Matrix4d::Identity(), - "jacobian"_a = RGBDOdometryJacobianFromHybridTerm(), - "option"_a = OdometryOption()); + m_odometry.def( + "compute_rgbd_odometry", &ComputeRGBDOdometry, + py::call_guard(), + "Function to estimate 6D rigid motion from two RGBD image pairs. " + "Output: (is_success, 4x4 motion matrix, 6x6 information matrix).", + "rgbd_source"_a, "rgbd_target"_a, + "pinhole_camera_intrinsic"_a = camera::PinholeCameraIntrinsic(), + "odo_init"_a = Eigen::Matrix4d::Identity(), + "jacobian"_a = RGBDOdometryJacobianFromHybridTerm(), + "option"_a = OdometryOption()); docstring::FunctionDocInject( - m, "compute_rgbd_odometry", + m_odometry, "compute_rgbd_odometry", { {"rgbd_source", "Source RGBD image."}, {"rgbd_target", "Target RGBD image."}, @@ -195,15 +213,16 @@ void pybind_odometry_methods(py::module &m) { {"option", "Odometry hyper parameters."}, }); - m.def("compute_correspondence", &ComputeCorrespondence, - py::call_guard(), - "Function to estimate point to point correspondences from two depth " - "images. A vector of u_s, v_s, u_t, v_t which maps the 2d " - "coordinates of source to target.", - "intrinsic_matrix"_a, "extrinsic"_a, "depth_s"_a, "depth_t"_a, - "option"_a = OdometryOption()); + m_odometry.def("compute_correspondence", &ComputeCorrespondence, + py::call_guard(), + "Function to estimate point to point correspondences from " + "two depth " + "images. A vector of u_s, v_s, u_t, v_t which maps the 2d " + "coordinates of source to target.", + "intrinsic_matrix"_a, "extrinsic"_a, "depth_s"_a, + "depth_t"_a, "option"_a = OdometryOption()); docstring::FunctionDocInject( - m, "compute_correspondence", + m_odometry, "compute_correspondence", { {"intrinsic_matrix", "Camera intrinsic parameters."}, {"extrinsic", @@ -214,12 +233,6 @@ void pybind_odometry_methods(py::module &m) { }); } -void pybind_odometry(py::module &m) { - py::module m_submodule = m.def_submodule("odometry", "Odometry pipeline."); - pybind_odometry_classes(m_submodule); - pybind_odometry_methods(m_submodule); -} - } // namespace odometry } // namespace pipelines } // namespace open3d diff --git a/cpp/pybind/pipelines/odometry/odometry.h b/cpp/pybind/pipelines/odometry/odometry.h index 48cd0edc657..0a7d2868969 100644 --- a/cpp/pybind/pipelines/odometry/odometry.h +++ b/cpp/pybind/pipelines/odometry/odometry.h @@ -13,7 +13,8 @@ namespace open3d { namespace pipelines { namespace odometry { -void pybind_odometry(py::module &m); +void pybind_odometry_declarations(py::module &m); +void pybind_odometry_definitions(py::module &m); } // namespace odometry } // namespace pipelines diff --git a/cpp/pybind/pipelines/pipelines.cpp b/cpp/pybind/pipelines/pipelines.cpp index aa696a50245..9b1f4422828 100644 --- a/cpp/pybind/pipelines/pipelines.cpp +++ b/cpp/pybind/pipelines/pipelines.cpp @@ -16,12 +16,20 @@ namespace open3d { namespace pipelines { -void pybind_pipelines(py::module& m) { +void pybind_pipelines_declarations(py::module& m) { py::module m_pipelines = m.def_submodule("pipelines"); - color_map::pybind_color_map(m_pipelines); - integration::pybind_integration(m_pipelines); - registration::pybind_registration(m_pipelines); - odometry::pybind_odometry(m_pipelines); + color_map::pybind_color_map_declarations(m_pipelines); + integration::pybind_integration_declarations(m_pipelines); + registration::pybind_registration_declarations(m_pipelines); + odometry::pybind_odometry_declarations(m_pipelines); +} + +void pybind_pipelines_definitions(py::module& m) { + auto m_pipelines = static_cast(m.attr("pipelines")); + color_map::pybind_color_map_definitions(m_pipelines); + integration::pybind_integration_definitions(m_pipelines); + registration::pybind_registration_definitions(m_pipelines); + odometry::pybind_odometry_definitions(m_pipelines); } } // namespace pipelines diff --git a/cpp/pybind/pipelines/pipelines.h b/cpp/pybind/pipelines/pipelines.h index 3f01c128cfe..7783055cff0 100644 --- a/cpp/pybind/pipelines/pipelines.h +++ b/cpp/pybind/pipelines/pipelines.h @@ -12,7 +12,8 @@ namespace open3d { namespace pipelines { -void pybind_pipelines(py::module& m); +void pybind_pipelines_declarations(py::module& m); +void pybind_pipelines_definitions(py::module& m); -} +} // namespace pipelines } // namespace open3d diff --git a/cpp/pybind/pipelines/registration/feature.cpp b/cpp/pybind/pipelines/registration/feature.cpp index bdc4b5d8e51..5896243b5c3 100644 --- a/cpp/pybind/pipelines/registration/feature.cpp +++ b/cpp/pybind/pipelines/registration/feature.cpp @@ -15,10 +15,15 @@ namespace open3d { namespace pipelines { namespace registration { -void pybind_feature(py::module &m) { - // open3d.registration.Feature +void pybind_feature_declarations(py::module &m_registration) { py::class_> feature( - m, "Feature", "Class to store featrues for registration."); + m_registration, "Feature", + "Class to store featrues for registration."); +} +void pybind_feature_definitions(py::module &m_registration) { + // open3d.registration.Feature + auto feature = static_cast>>( + m_registration.attr("Feature")); py::detail::bind_default_constructor(feature); py::detail::bind_copy_functions(feature); feature.def("resize", &Feature::Resize, "dim"_a, "n"_a, @@ -38,28 +43,26 @@ void pybind_feature(py::module &m) { std::string(" and num = ") + std::to_string(f.Num()) + std::string("\nAccess its data via data member."); }); - docstring::ClassMethodDocInject(m, "Feature", "dimension"); - docstring::ClassMethodDocInject(m, "Feature", "num"); - docstring::ClassMethodDocInject(m, "Feature", "resize", + docstring::ClassMethodDocInject(m_registration, "Feature", "dimension"); + docstring::ClassMethodDocInject(m_registration, "Feature", "num"); + docstring::ClassMethodDocInject(m_registration, "Feature", "resize", {{"dim", "Feature dimension per point."}, {"n", "Number of points."}}); -} - -void pybind_feature_methods(py::module &m) { - m.def("compute_fpfh_feature", &ComputeFPFHFeature, - "Function to compute FPFH feature for a point cloud", "input"_a, - "search_param"_a); + m_registration.def("compute_fpfh_feature", &ComputeFPFHFeature, + "Function to compute FPFH feature for a point cloud", + "input"_a, "search_param"_a); docstring::FunctionDocInject( - m, "compute_fpfh_feature", + m_registration, "compute_fpfh_feature", {{"input", "The Input point cloud."}, {"search_param", "KDTree KNN search parameter."}}); - m.def("correspondences_from_features", &CorrespondencesFromFeatures, - "Function to find nearest neighbor correspondences from features", - "source_features"_a, "target_features"_a, "mutual_filter"_a = false, - "mutual_consistency_ratio"_a = 0.1f); + m_registration.def( + "correspondences_from_features", &CorrespondencesFromFeatures, + "Function to find nearest neighbor correspondences from features", + "source_features"_a, "target_features"_a, "mutual_filter"_a = false, + "mutual_consistency_ratio"_a = 0.1f); docstring::FunctionDocInject( - m, "correspondences_from_features", + m_registration, "correspondences_from_features", {{"source_features", "The source features stored in (dim, N)."}, {"target_features", "The target features stored in (dim, M)."}, {"mutual_filter", diff --git a/cpp/pybind/pipelines/registration/global_optimization.cpp b/cpp/pybind/pipelines/registration/global_optimization.cpp index 58607de6597..b1c4d246c45 100644 --- a/cpp/pybind/pipelines/registration/global_optimization.cpp +++ b/cpp/pybind/pipelines/registration/global_optimization.cpp @@ -29,10 +29,50 @@ class PyGlobalOptimizationMethod : public GlobalOptimizationMethodBase { } }; -void pybind_global_optimization(py::module &m) { - // open3d.registration.PoseGraphNode +void pybind_global_optimization_declarations(py::module &m_registration) { py::class_> pose_graph_node( - m, "PoseGraphNode", "Node of ``PoseGraph``."); + m_registration, "PoseGraphNode", "Node of ``PoseGraph``."); + auto pose_graph_node_vector = py::bind_vector>( + m_registration, "PoseGraphNodeVector"); + py::class_> pose_graph_edge( + m_registration, "PoseGraphEdge", "Edge of ``PoseGraph``."); + auto pose_graph_edge_vector = py::bind_vector>( + m_registration, "PoseGraphEdgeVector"); + py::class_> pose_graph( + m_registration, "PoseGraph", + "Data structure defining the pose graph."); + py::class_> + global_optimization_method( + m_registration, "GlobalOptimizationMethod", + "Base class for global optimization method."); + py::class_, + GlobalOptimizationMethod> + global_optimization_method_lm( + m_registration, "GlobalOptimizationLevenbergMarquardt", + "Global optimization with Levenberg-Marquardt algorithm. " + "Recommended over the Gauss-Newton method since the LM has " + "better convergence characteristics."); + py::class_, + GlobalOptimizationMethod> + global_optimization_method_gn( + m_registration, "GlobalOptimizationGaussNewton", + "Global optimization with Gauss-Newton algorithm."); + py::class_ criteria( + m_registration, "GlobalOptimizationConvergenceCriteria", + "Convergence criteria of GlobalOptimization."); + py::class_ option( + m_registration, "GlobalOptimizationOption", + "Option for GlobalOptimization."); +} + +void pybind_global_optimization_definitions(py::module &m_registration) { + // open3d.registration.PoseGraphNode + auto pose_graph_node = static_cast< + py::class_>>( + m_registration.attr("PoseGraphNode")); py::detail::bind_default_constructor(pose_graph_node); py::detail::bind_copy_functions(pose_graph_node); pose_graph_node.def_readwrite("pose", &PoseGraphNode::pose_) @@ -49,8 +89,10 @@ void pybind_global_optimization(py::module &m) { }); // open3d.registration.PoseGraphNodeVector - auto pose_graph_node_vector = py::bind_vector>( - m, "PoseGraphNodeVector"); + auto pose_graph_node_vector = + static_cast>( + m_registration, "PoseGraphNodeVector"))>( + m_registration.attr("PoseGraphNodeVector")); pose_graph_node_vector.attr("__doc__") = docstring::static_property( py::cpp_function([](py::handle arg) -> std::string { return "Vector of PoseGraphNode"; @@ -58,8 +100,9 @@ void pybind_global_optimization(py::module &m) { py::none(), py::none(), ""); // open3d.registration.PoseGraphEdge - py::class_> pose_graph_edge( - m, "PoseGraphEdge", "Edge of ``PoseGraph``."); + auto pose_graph_edge = static_cast< + py::class_>>( + m_registration.attr("PoseGraphEdge")); py::detail::bind_default_constructor(pose_graph_edge); py::detail::bind_copy_functions(pose_graph_edge); pose_graph_edge @@ -109,8 +152,10 @@ void pybind_global_optimization(py::module &m) { }); // open3d.registration.PoseGraphEdgeVector - auto pose_graph_edge_vector = py::bind_vector>( - m, "PoseGraphEdgeVector"); + auto pose_graph_edge_vector = + static_cast>( + m_registration, "PoseGraphEdgeVector"))>( + m_registration.attr("PoseGraphEdgeVector")); pose_graph_edge_vector.attr("__doc__") = docstring::static_property( py::cpp_function([](py::handle arg) -> std::string { return "Vector of PoseGraphEdge"; @@ -118,8 +163,9 @@ void pybind_global_optimization(py::module &m) { py::none(), py::none(), ""); // open3d.registration.PoseGraph - py::class_> pose_graph( - m, "PoseGraph", "Data structure defining the pose graph."); + auto pose_graph = + static_cast>>( + m_registration.attr("PoseGraph")); py::detail::bind_default_constructor(pose_graph); py::detail::bind_copy_functions(pose_graph); pose_graph @@ -138,29 +184,25 @@ void pybind_global_optimization(py::module &m) { }); // open3d.registration.GlobalOptimizationMethod - py::class_> - global_optimization_method( - m, "GlobalOptimizationMethod", - "Base class for global optimization method."); + auto global_optimization_method = static_cast< + py::class_>>( + m_registration.attr("GlobalOptimizationMethod")); global_optimization_method.def("OptimizePoseGraph", &GlobalOptimizationMethod::OptimizePoseGraph, "pose_graph"_a, "criteria"_a, "option"_a, "Run pose graph optimization."); docstring::ClassMethodDocInject( - m, "GlobalOptimizationMethod", "OptimizePoseGraph", + m_registration, "GlobalOptimizationMethod", "OptimizePoseGraph", {{"pose_graph", "The pose graph to be optimized (in-place)."}, {"criteria", "Convergence criteria."}, {"option", "Global optimization options."}}); - py::class_, - GlobalOptimizationMethod> - global_optimization_method_lm( - m, "GlobalOptimizationLevenbergMarquardt", - "Global optimization with Levenberg-Marquardt algorithm. " - "Recommended over the Gauss-Newton method since the LM has " - "better convergence characteristics."); + auto global_optimization_method_lm = static_cast, + GlobalOptimizationMethod>>( + m_registration.attr("GlobalOptimizationLevenbergMarquardt")); py::detail::bind_default_constructor( global_optimization_method_lm); py::detail::bind_copy_functions( @@ -170,12 +212,11 @@ void pybind_global_optimization(py::module &m) { return std::string("GlobalOptimizationLevenbergMarquardt"); }); - py::class_, - GlobalOptimizationMethod> - global_optimization_method_gn( - m, "GlobalOptimizationGaussNewton", - "Global optimization with Gauss-Newton algorithm."); + auto global_optimization_method_gn = static_cast, + GlobalOptimizationMethod>>( + m_registration.attr("GlobalOptimizationGaussNewton")); py::detail::bind_default_constructor( global_optimization_method_gn); py::detail::bind_copy_functions( @@ -185,9 +226,10 @@ void pybind_global_optimization(py::module &m) { return std::string("GlobalOptimizationGaussNewton"); }); - py::class_ criteria( - m, "GlobalOptimizationConvergenceCriteria", - "Convergence criteria of GlobalOptimization."); + auto criteria = + static_cast>( + m_registration.attr( + "GlobalOptimizationConvergenceCriteria")); py::detail::bind_default_constructor( criteria); py::detail::bind_copy_functions( @@ -252,8 +294,8 @@ void pybind_global_optimization(py::module &m) { std::to_string(cr.lower_scale_factor_); }); - py::class_ option( - m, "GlobalOptimizationOption", "Option for GlobalOptimization."); + auto option = static_cast>( + m_registration.attr("GlobalOptimizationOption")); py::detail::bind_default_constructor(option); py::detail::bind_copy_functions(option); option.def_readwrite( @@ -302,10 +344,7 @@ void pybind_global_optimization(py::module &m) { std::string("\n> reference_node : ") + std::to_string(goo.reference_node_); }); -} - -void pybind_global_optimization_methods(py::module &m) { - m.def( + m_registration.def( "global_optimization", [](PoseGraph &pose_graph, const GlobalOptimizationMethod &method, const GlobalOptimizationConvergenceCriteria &criteria, @@ -315,7 +354,7 @@ void pybind_global_optimization_methods(py::module &m) { "Function to optimize PoseGraph", "pose_graph"_a, "method"_a, "criteria"_a, "option"_a); docstring::FunctionDocInject( - m, "global_optimization", + m_registration, "global_optimization", {{"pose_graph", "The pose_graph to be optimized (in-place)."}, {"method", "Global optimization method. Either " diff --git a/cpp/pybind/pipelines/registration/registration.cpp b/cpp/pybind/pipelines/registration/registration.cpp index cddcb7e7e80..b57836bdcee 100644 --- a/cpp/pybind/pipelines/registration/registration.cpp +++ b/cpp/pybind/pipelines/registration/registration.cpp @@ -63,16 +63,109 @@ class PyCorrespondenceChecker : public CorrespondenceCheckerBase { } }; -void pybind_registration_classes(py::module &m) { - // open3d.registration.ICPConvergenceCriteria +void pybind_registration_declarations(py::module &m) { + py::module m_registration = + m.def_submodule("registration", "Registration pipeline."); py::class_ convergence_criteria( - m, "ICPConvergenceCriteria", + m_registration, "ICPConvergenceCriteria", "Class that defines the convergence criteria of ICP. ICP " "algorithm " "stops if the relative change of fitness and rmse hit " "``relative_fitness`` and ``relative_rmse`` individually, " "or the " "iteration number exceeds ``max_iteration``."); + py::class_ ransac_criteria( + m_registration, "RANSACConvergenceCriteria", + "Class that defines the convergence criteria of " + "RANSAC. RANSAC algorithm stops if the iteration " + "number hits ``max_iteration``, or the fitness " + "measured during validation suggests that the " + "algorithm can be terminated early with some " + "``confidence``. Early termination takes place " + "when the number of iterations reaches ``k = " + "log(1 - confidence)/log(1 - fitness^{ransac_n})``, " + "where ``ransac_n`` is the number of points used " + "during a ransac iteration. Use confidence=1.0 " + "to avoid early termination."); + py::class_> + te(m_registration, "TransformationEstimation", + "Base class that estimates a transformation between two point " + "clouds. The virtual function ComputeTransformation() must be " + "implemented in subclasses."); + py::class_, + TransformationEstimation> + te_p2p(m_registration, "TransformationEstimationPointToPoint", + "Class to estimate a transformation for point to point " + "distance."); + py::class_, + TransformationEstimation> + te_p2l(m_registration, "TransformationEstimationPointToPlane", + "Class to estimate a transformation for point to plane " + "distance."); + py::class_< + TransformationEstimationForColoredICP, + PyTransformationEstimation, + TransformationEstimation> + te_col(m_registration, "TransformationEstimationForColoredICP", + "Class to estimate a transformation between two point " + "clouds using color information"); + py::class_, + TransformationEstimation> + te_gicp(m_registration, "TransformationEstimationForGeneralizedICP", + "Class to estimate a transformation for Generalized ICP."); + py::class_> + cc(m_registration, "CorrespondenceChecker", + "Base class that checks if two (small) point clouds can be " + "aligned. This class is used in feature based matching " + "algorithms (such as RANSAC and FastGlobalRegistration) to " + "prune out outlier correspondences. The virtual function " + "Check() must be implemented in subclasses."); + py::class_, + CorrespondenceChecker> + cc_el(m_registration, "CorrespondenceCheckerBasedOnEdgeLength", + "Check if two point clouds build the polygons with similar " + "edge lengths. That is, checks if the lengths of any two " + "arbitrary edges (line formed by two vertices) individually " + "drawn within the source point cloud and within the target " + "point cloud with correspondences are similar. The only " + "parameter similarity_threshold is a number between 0 " + "(loose) and 1 (strict)"); + py::class_, + CorrespondenceChecker> + cc_d(m_registration, "CorrespondenceCheckerBasedOnDistance", + "Class to check if aligned point clouds are close (less than " + "specified threshold)."); + py::class_, + CorrespondenceChecker> + cc_n(m_registration, "CorrespondenceCheckerBasedOnNormal", + "Class to check if two aligned point clouds have similar " + "normals. It considers vertex normal affinity of any " + "correspondences. It computes dot product of two normal " + "vectors. It takes radian value for the threshold."); + py::class_ fgr_option( + m_registration, "FastGlobalRegistrationOption", + "Options for FastGlobalRegistration."); + py::class_ registration_result( + m_registration, "RegistrationResult", + "Class that contains the registration results."); + pybind_feature_declarations(m_registration); + pybind_global_optimization_declarations(m_registration); + pybind_robust_kernels_declarations(m_registration); +} +void pybind_registration_definitions(py::module &m) { + auto m_registration = static_cast(m.attr("registration")); + // open3d.registration.ICPConvergenceCriteria + auto convergence_criteria = static_cast>( + m_registration.attr("ICPConvergenceCriteria")); py::detail::bind_copy_functions( convergence_criteria); convergence_criteria @@ -103,19 +196,8 @@ void pybind_registration_classes(py::module &m) { }); // open3d.registration.RANSACConvergenceCriteria - py::class_ ransac_criteria( - m, "RANSACConvergenceCriteria", - "Class that defines the convergence criteria of " - "RANSAC. RANSAC algorithm stops if the iteration " - "number hits ``max_iteration``, or the fitness " - "measured during validation suggests that the " - "algorithm can be terminated early with some " - "``confidence``. Early termination takes place " - "when the number of iterations reaches ``k = " - "log(1 - confidence)/log(1 - fitness^{ransac_n})``, " - "where ``ransac_n`` is the number of points used " - "during a ransac iteration. Use confidence=1.0 " - "to avoid early termination."); + auto ransac_criteria = static_cast>( + m_registration.attr("RANSACConvergenceCriteria")); py::detail::bind_copy_functions(ransac_criteria); ransac_criteria .def(py::init([](int max_iteration, double confidence) { @@ -139,12 +221,10 @@ void pybind_registration_classes(py::module &m) { }); // open3d.registration.TransformationEstimation - py::class_> - te(m, "TransformationEstimation", - "Base class that estimates a transformation between two point " - "clouds. The virtual function ComputeTransformation() must be " - "implemented in subclasses."); + auto te = static_cast< + py::class_>>( + m_registration.attr("TransformationEstimation")); te.def("compute_rmse", &TransformationEstimation::ComputeRMSE, "source"_a, "target"_a, "corres"_a, "Compute RMSE between source and target points cloud given " @@ -155,13 +235,14 @@ void pybind_registration_classes(py::module &m) { "Compute transformation from source to target point cloud given " "correspondences."); docstring::ClassMethodDocInject( - m, "TransformationEstimation", "compute_rmse", + m_registration, "TransformationEstimation", "compute_rmse", {{"source", "Source point cloud."}, {"target", "Target point cloud."}, {"corres", "Correspondence set between source and target point cloud."}}); docstring::ClassMethodDocInject( - m, "TransformationEstimation", "compute_transformation", + m_registration, "TransformationEstimation", + "compute_transformation", {{"source", "Source point cloud."}, {"target", "Target point cloud."}, {"corres", @@ -169,12 +250,11 @@ void pybind_registration_classes(py::module &m) { // open3d.registration.TransformationEstimationPointToPoint: // TransformationEstimation - py::class_, - TransformationEstimation> - te_p2p(m, "TransformationEstimationPointToPoint", - "Class to estimate a transformation for point to point " - "distance."); + auto te_p2p = static_cast, + TransformationEstimation>>( + m_registration.attr("TransformationEstimationPointToPoint")); py::detail::bind_copy_functions( te_p2p); te_p2p.def(py::init([](bool with_scaling) { @@ -205,12 +285,11 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. // open3d.registration.TransformationEstimationPointToPlane: // TransformationEstimation - py::class_, - TransformationEstimation> - te_p2l(m, "TransformationEstimationPointToPlane", - "Class to estimate a transformation for point to plane " - "distance."); + auto te_p2l = static_cast, + TransformationEstimation>>( + m_registration.attr("TransformationEstimationPointToPlane")); py::detail::bind_default_constructor( te_p2l); py::detail::bind_copy_functions( @@ -229,13 +308,11 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. "Robust Kernel used in the Optimization"); // open3d.registration.TransformationEstimationForColoredICP : - py::class_< + auto te_col = static_cast, - TransformationEstimation> - te_col(m, "TransformationEstimationForColoredICP", - "Class to estimate a transformation between two point " - "clouds using color information"); + TransformationEstimation>>( + m_registration.attr("TransformationEstimationForColoredICP")); py::detail::bind_default_constructor( te_col); py::detail::bind_copy_functions( @@ -274,12 +351,12 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. // open3d.registration.TransformationEstimationForGeneralizedICP: // TransformationEstimation - py::class_, - TransformationEstimation> - te_gicp(m, "TransformationEstimationForGeneralizedICP", - "Class to estimate a transformation for Generalized ICP."); + auto te_gicp = static_cast< + py::class_, + TransformationEstimation>>( + m_registration.attr("TransformationEstimationForGeneralizedICP")); py::detail::bind_default_constructor< TransformationEstimationForGeneralizedICP>(te_gicp); py::detail::bind_copy_functions( @@ -316,14 +393,10 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. "Robust Kernel used in the Optimization"); // open3d.registration.CorrespondenceChecker - py::class_> - cc(m, "CorrespondenceChecker", - "Base class that checks if two (small) point clouds can be " - "aligned. This class is used in feature based matching " - "algorithms (such as RANSAC and FastGlobalRegistration) to " - "prune out outlier correspondences. The virtual function " - "Check() must be implemented in subclasses."); + auto cc = static_cast< + py::class_>>( + m_registration.attr("CorrespondenceChecker")); cc.def("Check", &CorrespondenceChecker::Check, "source"_a, "target"_a, "corres"_a, "transformation"_a, "Function to check if two points can be aligned. The two input " @@ -335,7 +408,7 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. "the edge length checker. Some checkers do, e.g., the distance " "checker."); docstring::ClassMethodDocInject( - m, "CorrespondenceChecker", "Check", + m_registration, "CorrespondenceChecker", "Check", {{"source", "Source point cloud."}, {"target", "Target point cloud."}, {"corres", @@ -344,17 +417,11 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. // open3d.registration.CorrespondenceCheckerBasedOnEdgeLength: // CorrespondenceChecker - py::class_, - CorrespondenceChecker> - cc_el(m, "CorrespondenceCheckerBasedOnEdgeLength", - "Check if two point clouds build the polygons with similar " - "edge lengths. That is, checks if the lengths of any two " - "arbitrary edges (line formed by two vertices) individually " - "drawn within the source point cloud and within the target " - "point cloud with correspondences are similar. The only " - "parameter similarity_threshold is a number between 0 " - "(loose) and 1 (strict)"); + auto cc_el = static_cast, + CorrespondenceChecker>>( + m_registration.attr("CorrespondenceCheckerBasedOnEdgeLength")); py::detail::bind_copy_functions( cc_el); cc_el.def(py::init([](double similarity_threshold) { @@ -385,12 +452,11 @@ must hold true for all edges.)"); // open3d.registration.CorrespondenceCheckerBasedOnDistance: // CorrespondenceChecker - py::class_, - CorrespondenceChecker> - cc_d(m, "CorrespondenceCheckerBasedOnDistance", - "Class to check if aligned point clouds are close (less than " - "specified threshold)."); + auto cc_d = static_cast, + CorrespondenceChecker>>( + m_registration.attr("CorrespondenceCheckerBasedOnDistance")); py::detail::bind_copy_functions(cc_d); cc_d.def(py::init([](double distance_threshold) { return new CorrespondenceCheckerBasedOnDistance( @@ -412,14 +478,11 @@ must hold true for all edges.)"); // open3d.registration.CorrespondenceCheckerBasedOnNormal: // CorrespondenceChecker - py::class_, - CorrespondenceChecker> - cc_n(m, "CorrespondenceCheckerBasedOnNormal", - "Class to check if two aligned point clouds have similar " - "normals. It considers vertex normal affinity of any " - "correspondences. It computes dot product of two normal " - "vectors. It takes radian value for the threshold."); + auto cc_n = static_cast, + CorrespondenceChecker>>( + m_registration.attr("CorrespondenceCheckerBasedOnNormal")); py::detail::bind_copy_functions(cc_n); cc_n.def(py::init([](double normal_angle_threshold) { return new CorrespondenceCheckerBasedOnNormal( @@ -440,9 +503,8 @@ must hold true for all edges.)"); "Radian value for angle threshold."); // open3d.registration.FastGlobalRegistrationOption: - py::class_ fgr_option( - m, "FastGlobalRegistrationOption", - "Options for FastGlobalRegistration."); + auto fgr_option = static_cast>( + m_registration.attr("FastGlobalRegistrationOption")); py::detail::bind_copy_functions(fgr_option); fgr_option .def(py::init([](double division_factor, bool use_absolute_scale, @@ -509,9 +571,8 @@ must hold true for all edges.)"); }); // open3d.registration.RegistrationResult - py::class_ registration_result( - m, "RegistrationResult", - "Class that contains the registration results."); + auto registration_result = static_cast>( + m_registration.attr("RegistrationResult")); py::detail::bind_default_constructor( registration_result); py::detail::bind_copy_functions(registration_result); @@ -542,162 +603,161 @@ must hold true for all edges.)"); rr.fitness_, rr.inlier_rmse_, rr.correspondence_set_.size()); }); -} - -// Registration functions have similar arguments, sharing arg docstrings -static const std::unordered_map - map_shared_argument_docstrings = { - {"checkers", - "Vector of Checker class to check if two point " - "clouds can be aligned. One of " - "(``CorrespondenceCheckerBasedOnEdgeLength``, " - "``CorrespondenceCheckerBasedOnDistance``, " - "``CorrespondenceCheckerBasedOnNormal``)"}, - {"confidence", - "Desired probability of success for RANSAC. Used for " - "estimating early termination by k = log(1 - " - "confidence)/log(1 - inlier_ratio^{ransac_n}."}, - {"corres", - "o3d.utility.Vector2iVector that stores indices of " - "corresponding point or feature arrays."}, - {"criteria", "Convergence criteria"}, - {"estimation_method", - "Estimation method. One of " - "(``TransformationEstimationPointToPoint``, " - "``TransformationEstimationPointToPlane``, " - "``TransformationEstimationForGeneralizedICP``, " - "``TransformationEstimationForColoredICP``)"}, - {"init", "Initial transformation estimation"}, - {"lambda_geometric", "lambda_geometric value"}, - {"epsilon", "epsilon value"}, - {"kernel", "Robust Kernel used in the Optimization"}, - {"max_correspondence_distance", - "Maximum correspondence points-pair distance."}, - {"mutual_filter", - "Enables mutual filter such that the correspondence of the " - "source point's correspondence is itself."}, - {"option", "Registration option"}, - {"ransac_n", "Fit ransac with ``ransac_n`` correspondences"}, - {"source_feature", "Source point cloud feature."}, - {"source", "The source point cloud."}, - {"target_feature", "Target point cloud feature."}, - {"target", "The target point cloud."}, - {"transformation", - "The 4x4 transformation matrix to transform ``source`` to " - "``target``"}}; - -void pybind_registration_methods(py::module &m) { - m.def("evaluate_registration", &EvaluateRegistration, - py::call_guard(), - "Function for evaluating registration between point clouds", - "source"_a, "target"_a, "max_correspondence_distance"_a, - "transformation"_a = Eigen::Matrix4d::Identity()); - docstring::FunctionDocInject(m, "evaluate_registration", + // Registration functions have similar arguments, sharing arg docstrings + static const std::unordered_map + map_shared_argument_docstrings = { + {"checkers", + "Vector of Checker class to check if two point " + "clouds can be aligned. One of " + "(``CorrespondenceCheckerBasedOnEdgeLength``, " + "``CorrespondenceCheckerBasedOnDistance``, " + "``CorrespondenceCheckerBasedOnNormal``)"}, + {"confidence", + "Desired probability of success for RANSAC. Used for " + "estimating early termination by k = log(1 - " + "confidence)/log(1 - inlier_ratio^{ransac_n}."}, + {"corres", + "o3d.utility.Vector2iVector that stores indices of " + "corresponding point or feature arrays."}, + {"criteria", "Convergence criteria"}, + {"estimation_method", + "Estimation method. One of " + "(``TransformationEstimationPointToPoint``, " + "``TransformationEstimationPointToPlane``, " + "``TransformationEstimationForGeneralizedICP``, " + "``TransformationEstimationForColoredICP``)"}, + {"init", "Initial transformation estimation"}, + {"lambda_geometric", "lambda_geometric value"}, + {"epsilon", "epsilon value"}, + {"kernel", "Robust Kernel used in the Optimization"}, + {"max_correspondence_distance", + "Maximum correspondence points-pair distance."}, + {"mutual_filter", + "Enables mutual filter such that the correspondence of " + "the " + "source point's correspondence is itself."}, + {"option", "Registration option"}, + {"ransac_n", + "Fit ransac with ``ransac_n`` correspondences"}, + {"source_feature", "Source point cloud feature."}, + {"source", "The source point cloud."}, + {"target_feature", "Target point cloud feature."}, + {"target", "The target point cloud."}, + {"transformation", + "The 4x4 transformation matrix to transform ``source`` to " + "``target``"}}; + m_registration.def( + "evaluate_registration", &EvaluateRegistration, + py::call_guard(), + "Function for evaluating registration between point clouds", + "source"_a, "target"_a, "max_correspondence_distance"_a, + "transformation"_a = Eigen::Matrix4d::Identity()); + docstring::FunctionDocInject(m_registration, "evaluate_registration", map_shared_argument_docstrings); - m.def("registration_icp", &RegistrationICP, - py::call_guard(), - "Function for ICP registration", "source"_a, "target"_a, - "max_correspondence_distance"_a, - "init"_a = Eigen::Matrix4d::Identity(), - "estimation_method"_a = TransformationEstimationPointToPoint(false), - "criteria"_a = ICPConvergenceCriteria()); - docstring::FunctionDocInject(m, "registration_icp", + m_registration.def( + "registration_icp", &RegistrationICP, + py::call_guard(), + "Function for ICP registration", "source"_a, "target"_a, + "max_correspondence_distance"_a, + "init"_a = Eigen::Matrix4d::Identity(), + "estimation_method"_a = TransformationEstimationPointToPoint(false), + "criteria"_a = ICPConvergenceCriteria()); + docstring::FunctionDocInject(m_registration, "registration_icp", map_shared_argument_docstrings); - m.def("registration_colored_icp", &RegistrationColoredICP, - py::call_guard(), - "Function for Colored ICP registration", "source"_a, "target"_a, - "max_correspondence_distance"_a, - "init"_a = Eigen::Matrix4d::Identity(), - "estimation_method"_a = TransformationEstimationForColoredICP(0.968), - "criteria"_a = ICPConvergenceCriteria()); - docstring::FunctionDocInject(m, "registration_colored_icp", + m_registration.def("registration_colored_icp", &RegistrationColoredICP, + py::call_guard(), + "Function for Colored ICP registration", "source"_a, + "target"_a, "max_correspondence_distance"_a, + "init"_a = Eigen::Matrix4d::Identity(), + "estimation_method"_a = + TransformationEstimationForColoredICP(0.968), + "criteria"_a = ICPConvergenceCriteria()); + docstring::FunctionDocInject(m_registration, "registration_colored_icp", map_shared_argument_docstrings); - m.def("registration_generalized_icp", &RegistrationGeneralizedICP, - py::call_guard(), - "Function for Generalized ICP registration", "source"_a, "target"_a, - "max_correspondence_distance"_a, - "init"_a = Eigen::Matrix4d::Identity(), - "estimation_method"_a = - TransformationEstimationForGeneralizedICP(1e-3), - "criteria"_a = ICPConvergenceCriteria()); - docstring::FunctionDocInject(m, "registration_generalized_icp", + m_registration.def("registration_generalized_icp", + &RegistrationGeneralizedICP, + py::call_guard(), + "Function for Generalized ICP registration", "source"_a, + "target"_a, "max_correspondence_distance"_a, + "init"_a = Eigen::Matrix4d::Identity(), + "estimation_method"_a = + TransformationEstimationForGeneralizedICP(1e-3), + "criteria"_a = ICPConvergenceCriteria()); + docstring::FunctionDocInject(m_registration, "registration_generalized_icp", map_shared_argument_docstrings); - m.def("registration_ransac_based_on_correspondence", - &RegistrationRANSACBasedOnCorrespondence, - py::call_guard(), - "Function for global RANSAC registration based on a set of " - "correspondences", - "source"_a, "target"_a, "corres"_a, "max_correspondence_distance"_a, - "estimation_method"_a = TransformationEstimationPointToPoint(false), - "ransac_n"_a = 3, - "checkers"_a = std::vector< - std::reference_wrapper>(), - "criteria"_a = RANSACConvergenceCriteria(100000, 0.999)); - docstring::FunctionDocInject(m, + m_registration.def( + "registration_ransac_based_on_correspondence", + &RegistrationRANSACBasedOnCorrespondence, + py::call_guard(), + "Function for global RANSAC registration based on a set of " + "correspondences", + "source"_a, "target"_a, "corres"_a, "max_correspondence_distance"_a, + "estimation_method"_a = TransformationEstimationPointToPoint(false), + "ransac_n"_a = 3, + "checkers"_a = std::vector< + std::reference_wrapper>(), + "criteria"_a = RANSACConvergenceCriteria(100000, 0.999)); + docstring::FunctionDocInject(m_registration, "registration_ransac_based_on_correspondence", map_shared_argument_docstrings); - m.def("registration_ransac_based_on_feature_matching", - &RegistrationRANSACBasedOnFeatureMatching, - py::call_guard(), - "Function for global RANSAC registration based on feature matching", - "source"_a, "target"_a, "source_feature"_a, "target_feature"_a, - "mutual_filter"_a, "max_correspondence_distance"_a, - "estimation_method"_a = TransformationEstimationPointToPoint(false), - "ransac_n"_a = 3, - "checkers"_a = std::vector< - std::reference_wrapper>(), - "criteria"_a = RANSACConvergenceCriteria(100000, 0.999)); + m_registration.def( + "registration_ransac_based_on_feature_matching", + &RegistrationRANSACBasedOnFeatureMatching, + py::call_guard(), + "Function for global RANSAC registration based on feature matching", + "source"_a, "target"_a, "source_feature"_a, "target_feature"_a, + "mutual_filter"_a, "max_correspondence_distance"_a, + "estimation_method"_a = TransformationEstimationPointToPoint(false), + "ransac_n"_a = 3, + "checkers"_a = std::vector< + std::reference_wrapper>(), + "criteria"_a = RANSACConvergenceCriteria(100000, 0.999)); docstring::FunctionDocInject( - m, "registration_ransac_based_on_feature_matching", + m_registration, "registration_ransac_based_on_feature_matching", map_shared_argument_docstrings); - m.def("registration_fgr_based_on_correspondence", - &FastGlobalRegistrationBasedOnCorrespondence, - py::call_guard(), - "Function for fast global registration based on a set of " - "correspondences", - "source"_a, "target"_a, "corres"_a, - "option"_a = FastGlobalRegistrationOption()); - docstring::FunctionDocInject(m, "registration_fgr_based_on_correspondence", + m_registration.def( + "registration_fgr_based_on_correspondence", + &FastGlobalRegistrationBasedOnCorrespondence, + py::call_guard(), + "Function for fast global registration based on a set of " + "correspondences", + "source"_a, "target"_a, "corres"_a, + "option"_a = FastGlobalRegistrationOption()); + docstring::FunctionDocInject(m_registration, + "registration_fgr_based_on_correspondence", map_shared_argument_docstrings); - m.def("registration_fgr_based_on_feature_matching", - &FastGlobalRegistrationBasedOnFeatureMatching, - py::call_guard(), - "Function for fast global registration based on feature matching", - "source"_a, "target"_a, "source_feature"_a, "target_feature"_a, - "option"_a = FastGlobalRegistrationOption()); - docstring::FunctionDocInject(m, + m_registration.def( + "registration_fgr_based_on_feature_matching", + &FastGlobalRegistrationBasedOnFeatureMatching, + py::call_guard(), + "Function for fast global registration based on feature matching", + "source"_a, "target"_a, "source_feature"_a, "target_feature"_a, + "option"_a = FastGlobalRegistrationOption()); + docstring::FunctionDocInject(m_registration, "registration_fgr_based_on_feature_matching", map_shared_argument_docstrings); - m.def("get_information_matrix_from_point_clouds", - &GetInformationMatrixFromPointClouds, - py::call_guard(), - "Function for computing information matrix from transformation " - "matrix", - "source"_a, "target"_a, "max_correspondence_distance"_a, - "transformation"_a); - docstring::FunctionDocInject(m, "get_information_matrix_from_point_clouds", + m_registration.def( + "get_information_matrix_from_point_clouds", + &GetInformationMatrixFromPointClouds, + py::call_guard(), + "Function for computing information matrix from transformation " + "matrix", + "source"_a, "target"_a, "max_correspondence_distance"_a, + "transformation"_a); + docstring::FunctionDocInject(m_registration, + "get_information_matrix_from_point_clouds", map_shared_argument_docstrings); -} - -void pybind_registration(py::module &m) { - py::module m_submodule = - m.def_submodule("registration", "Registration pipeline."); - pybind_registration_classes(m_submodule); - pybind_registration_methods(m_submodule); - - pybind_feature(m_submodule); - pybind_feature_methods(m_submodule); - pybind_global_optimization(m_submodule); - pybind_global_optimization_methods(m_submodule); - pybind_robust_kernels(m_submodule); + pybind_feature_definitions(m_registration); + pybind_global_optimization_definitions(m_registration); + pybind_robust_kernels_definitions(m_registration); } } // namespace registration diff --git a/cpp/pybind/pipelines/registration/registration.h b/cpp/pybind/pipelines/registration/registration.h index 17eb18a3b02..1e98308e073 100644 --- a/cpp/pybind/pipelines/registration/registration.h +++ b/cpp/pybind/pipelines/registration/registration.h @@ -13,13 +13,15 @@ namespace open3d { namespace pipelines { namespace registration { -void pybind_registration(py::module &m); +void pybind_registration_declarations(py::module &m); +void pybind_feature_declarations(py::module &m_registration); +void pybind_global_optimization_declarations(py::module &m_registration); +void pybind_robust_kernels_declarations(py::module &m_registration); -void pybind_feature(py::module &m); -void pybind_feature_methods(py::module &m); -void pybind_global_optimization(py::module &m); -void pybind_global_optimization_methods(py::module &m); -void pybind_robust_kernels(py::module &m); +void pybind_registration_definitions(py::module &m); +void pybind_feature_definitions(py::module &m_registration); +void pybind_global_optimization_definitions(py::module &m_registration); +void pybind_robust_kernels_definitions(py::module &m_registration); } // namespace registration } // namespace pipelines diff --git a/cpp/pybind/pipelines/registration/robust_kernels.cpp b/cpp/pybind/pipelines/registration/robust_kernels.cpp index 5cd8bb83181..477c5812427 100644 --- a/cpp/pybind/pipelines/registration/robust_kernels.cpp +++ b/cpp/pybind/pipelines/registration/robust_kernels.cpp @@ -39,10 +39,9 @@ using PyCauchyLoss = PyRobustKernelT; using PyGMLoss = PyRobustKernelT; using PyTukeyLoss = PyRobustKernelT; -void pybind_robust_kernels(py::module &m) { - // open3d.registration.RobustKernel +void pybind_robust_kernels_declarations(py::module &m_registration) { py::class_, PyRobustKernel> rk( - m, "RobustKernel", + m_registration, "RobustKernel", R"( Base class that models a robust kernel for outlier rejection. The virtual function ``weight()`` must be implemented in derived classes. @@ -114,16 +113,8 @@ Philippe Babin et al. For more information please also see: **"Adaptive Robust Kernels for Non-Linear Least Squares Problems"**, by Nived Chebrolu et al. )"); - rk.def("weight", &RobustKernel::Weight, "residual"_a, - "Obtain the weight for the given residual according to the " - "robust kernel model."); - docstring::ClassMethodDocInject( - m, "RobustKernel", "weight", - {{"residual", "value obtained during the optimization problem"}}); - - // open3d.registration.L2Loss py::class_, PyL2Loss, RobustKernel> l2_loss( - m, "L2Loss", + m_registration, "L2Loss", R"( The loss :math:`\rho(r)` for a given residual ``r`` is given by: @@ -133,16 +124,8 @@ The weight :math:`w(r)` for a given residual ``r`` is given by: .. math:: w(r) = 1 )"); - py::detail::bind_default_constructor(l2_loss); - py::detail::bind_copy_functions(l2_loss); - l2_loss.def("__repr__", [](const L2Loss &rk) { - (void)rk; - return "RobustKernel::L2Loss"; - }); - - // open3d.registration.L1Loss:RobustKernel py::class_, PyL1Loss, RobustKernel> l1_loss( - m, "L1Loss", + m_registration, "L1Loss", R"( The loss :math:`\rho(r)` for a given residual ``r`` is given by: @@ -152,16 +135,8 @@ The weight :math:`w(r)` for a given residual ``r`` is given by: .. math:: w(r) = \frac{1}{|r|} )"); - py::detail::bind_default_constructor(l1_loss); - py::detail::bind_copy_functions(l1_loss); - l1_loss.def("__repr__", [](const L1Loss &rk) { - (void)rk; - return "RobustKernel::L1Loss"; - }); - - // open3d.registration.HuberLoss py::class_, PyHuberLoss, RobustKernel> - h_loss(m, "HuberLoss", + h_loss(m_registration, "HuberLoss", R"( The loss :math:`\rho(r)` for a given residual ``r`` is: @@ -185,21 +160,9 @@ The weight :math:`w(r)` for a given residual ``r`` is given by: \end{cases} \end{equation} )"); - py::detail::bind_copy_functions(h_loss); - h_loss.def(py::init( - [](double k) { return std::make_shared(k); }), - "k"_a) - .def("__repr__", - [](const HuberLoss &rk) { - return std::string("RobustKernel::HuberLoss with k=") + - std::to_string(rk.k_); - }) - .def_readwrite("k", &HuberLoss::k_, "Parameter of the loss"); - - // open3d.registration.CauchyLoss py::class_, PyCauchyLoss, RobustKernel> - c_loss(m, "CauchyLoss", + c_loss(m_registration, "CauchyLoss", R"( The loss :math:`\rho(r)` for a given residual ``r`` is: @@ -217,21 +180,8 @@ The weight :math:`w(r)` for a given residual ``r`` is given by: \frac{1}{1 + \left(\frac{r}{k}\right)^2} \end{equation} )"); - py::detail::bind_copy_functions(c_loss); - c_loss.def(py::init([](double k) { - return std::make_shared(k); - }), - "k"_a) - .def("__repr__", - [](const CauchyLoss &rk) { - return std::string("RobustKernel::CauchyLoss with k=") + - std::to_string(rk.k_); - }) - .def_readwrite("k", &CauchyLoss::k_, "Parameter of the loss."); - - // open3d.registration.GMLoss py::class_, PyGMLoss, RobustKernel> gm_loss( - m, "GMLoss", + m_registration, "GMLoss", R"( The loss :math:`\rho(r)` for a given residual ``r`` is: @@ -249,19 +199,8 @@ The weight :math:`w(r)` for a given residual ``r`` is given by: \frac{k}{\left(k + r^2\right)^2} \end{equation} )"); - py::detail::bind_copy_functions(gm_loss); - gm_loss.def(py::init([](double k) { return std::make_shared(k); }), - "k"_a) - .def("__repr__", - [](const GMLoss &rk) { - return std::string("RobustKernel::GMLoss with k=") + - std::to_string(rk.k_); - }) - .def_readwrite("k", &GMLoss::k_, "Parameter of the loss."); - - // open3d.registration.TukeyLoss:RobustKernel py::class_, PyTukeyLoss, RobustKernel> - t_loss(m, "TukeyLoss", + t_loss(m_registration, "TukeyLoss", R"( The loss :math:`\rho(r)` for a given residual ``r`` is: @@ -285,6 +224,92 @@ The weight :math:`w(r)` for a given residual ``r`` is given by: \end{cases} \end{equation} )"); +} +void pybind_robust_kernels_definitions(py::module &m_registration) { + // open3d.registration.RobustKernel + auto rk = + static_cast, + PyRobustKernel>>( + m_registration.attr("RobustKernel")); + rk.def("weight", &RobustKernel::Weight, "residual"_a, + "Obtain the weight for the given residual according to the " + "robust kernel model."); + docstring::ClassMethodDocInject( + m_registration, "RobustKernel", "weight", + {{"residual", "value obtained during the optimization problem"}}); + + // open3d.registration.L2Loss + auto l2_loss = static_cast, + PyL2Loss, RobustKernel>>( + m_registration.attr("L2Loss")); + py::detail::bind_default_constructor(l2_loss); + py::detail::bind_copy_functions(l2_loss); + l2_loss.def("__repr__", [](const L2Loss &rk) { + (void)rk; + return "RobustKernel::L2Loss"; + }); + + // open3d.registration.L1Loss:RobustKernel + auto l1_loss = static_cast, + PyL1Loss, RobustKernel>>( + m_registration.attr("L1Loss")); + py::detail::bind_default_constructor(l1_loss); + py::detail::bind_copy_functions(l1_loss); + l1_loss.def("__repr__", [](const L1Loss &rk) { + (void)rk; + return "RobustKernel::L1Loss"; + }); + + // open3d.registration.HuberLoss + auto h_loss = static_cast, + PyHuberLoss, RobustKernel>>( + m_registration.attr("HuberLoss")); + py::detail::bind_copy_functions(h_loss); + h_loss.def(py::init( + [](double k) { return std::make_shared(k); }), + "k"_a) + .def("__repr__", + [](const HuberLoss &rk) { + return std::string("RobustKernel::HuberLoss with k=") + + std::to_string(rk.k_); + }) + .def_readwrite("k", &HuberLoss::k_, "Parameter of the loss"); + + // open3d.registration.CauchyLoss + auto c_loss = + static_cast, + PyCauchyLoss, RobustKernel>>( + m_registration.attr("CauchyLoss")); + py::detail::bind_copy_functions(c_loss); + c_loss.def(py::init([](double k) { + return std::make_shared(k); + }), + "k"_a) + .def("__repr__", + [](const CauchyLoss &rk) { + return std::string("RobustKernel::CauchyLoss with k=") + + std::to_string(rk.k_); + }) + .def_readwrite("k", &CauchyLoss::k_, "Parameter of the loss."); + + // open3d.registration.GMLoss + auto gm_loss = static_cast, + PyGMLoss, RobustKernel>>( + m_registration.attr("GMLoss")); + py::detail::bind_copy_functions(gm_loss); + gm_loss.def(py::init([](double k) { return std::make_shared(k); }), + "k"_a) + .def("__repr__", + [](const GMLoss &rk) { + return std::string("RobustKernel::GMLoss with k=") + + std::to_string(rk.k_); + }) + .def_readwrite("k", &GMLoss::k_, "Parameter of the loss."); + + // open3d.registration.TukeyLoss:RobustKernel + auto t_loss = static_cast, + PyTukeyLoss, RobustKernel>>( + m_registration.attr("TukeyLoss")); py::detail::bind_copy_functions(t_loss); t_loss.def(py::init( [](double k) { return std::make_shared(k); }), diff --git a/cpp/pybind/t/geometry/boundingvolume.cpp b/cpp/pybind/t/geometry/boundingvolume.cpp index 946fb9617df..981b8f3dd46 100644 --- a/cpp/pybind/t/geometry/boundingvolume.cpp +++ b/cpp/pybind/t/geometry/boundingvolume.cpp @@ -17,7 +17,7 @@ namespace open3d { namespace t { namespace geometry { -void pybind_boundingvolume(py::module& m) { +void pybind_boundingvolume_declarations(py::module& m) { py::class_, std::shared_ptr, Geometry, DrawableGeometry> @@ -43,6 +43,37 @@ axes. - Value tensor must have shape {3,}. - Value tensor can only be float32 (default) or float64. - Value tensor can only be range [0.0, 1.0].)"); + py::class_, + std::shared_ptr, Geometry, DrawableGeometry> + obb(m, "OrientedBoundingBox", + R"(A bounding box oriented along an arbitrary frame of reference. +- (center, rotation, extent): The oriented bounding box is defined by its +center position, rotation maxtrix and extent. + - Usage + - OrientedBoundingBox::GetCenter() + - OrientedBoundingBox::SetCenter(const core::Tensor ¢er) + - OrientedBoundingBox::GetRotation() + - OrientedBoundingBox::SetRotation(const core::Tensor &rotation) + - Value tensor of center and extent must have shape {3,}. + - Value tensor of rotation must have shape {3, 3}. + - Value tensor must have the same data type and device. + - Value tensor can only be float32 (default) or float64. + - The device of the tensor determines the device of the box. + +- color: Color of the bounding box. + - Usage + - OrientedBoundingBox::GetColor() + - OrientedBoundingBox::SetColor(const core::Tensor &color) + - Value tensor must have shape {3,}. + - Value tensor can only be float32 (default) or float64. + - Value tensor can only be range [0.0, 1.0].)"); +} +void pybind_boundingvolume_definitions(py::module& m) { + auto aabb = static_cast, + std::shared_ptr, + Geometry, DrawableGeometry>>( + m.attr("AxisAlignedBoundingBox")); aabb.def(py::init(), "device"_a = core::Device("CPU:0"), "Construct an empty axis-aligned box on the provided " @@ -50,7 +81,7 @@ axes. aabb.def(py::init(), "min_bound"_a, "max_bound"_a, R"(Construct an axis-aligned box from min/max bound. -The axis-aligned box will be created on the device of the given bound +The axis-aligned box will be created on the device of the given bound tensor, which must be on the same device and have the same data type.)"); docstring::ClassMethodDocInject( m, "AxisAlignedBoundingBox", "__init__", @@ -125,7 +156,7 @@ translation is applied to make the box's center at the given translation.)", aabb.def("scale", &AxisAlignedBoundingBox::Scale, R"(Scale the axis-aligned box. If \f$mi\f$ is the min_bound and \f$ma\f$ is the max_bound of the axis aligned -bounding box, and \f$s\f$ and \f$c\f$ are the provided scaling factor and +bounding box, and \f$s\f$ and \f$c\f$ are the provided scaling factor and center respectively, then the new min_bound and max_bound are given by \f$mi = c + s (mi - c)\f$ and \f$ma = c + s (ma - c)\f$. The scaling center will be the box center if it is not specified.)", @@ -198,38 +229,17 @@ The scaling center will be the box center if it is not specified.)", {{"points", "A list of points with data type of float32 or float64 (N x 3 " "tensor)."}}); - - py::class_, - std::shared_ptr, Geometry, DrawableGeometry> - obb(m, "OrientedBoundingBox", - R"(A bounding box oriented along an arbitrary frame of reference. -- (center, rotation, extent): The oriented bounding box is defined by its -center position, rotation maxtrix and extent. - - Usage - - OrientedBoundingBox::GetCenter() - - OrientedBoundingBox::SetCenter(const core::Tensor ¢er) - - OrientedBoundingBox::GetRotation() - - OrientedBoundingBox::SetRotation(const core::Tensor &rotation) - - Value tensor of center and extent must have shape {3,}. - - Value tensor of rotation must have shape {3, 3}. - - Value tensor must have the same data type and device. - - Value tensor can only be float32 (default) or float64. - - The device of the tensor determines the device of the box. - -- color: Color of the bounding box. - - Usage - - OrientedBoundingBox::GetColor() - - OrientedBoundingBox::SetColor(const core::Tensor &color) - - Value tensor must have shape {3,}. - - Value tensor can only be float32 (default) or float64. - - Value tensor can only be range [0.0, 1.0].)"); + auto obb = static_cast, + std::shared_ptr, Geometry, DrawableGeometry>>( + m.attr("OrientedBoundingBox")); obb.def(py::init(), "device"_a = core::Device("CPU:0"), "Construct an empty OrientedBoundingBox on the provided device."); obb.def(py::init(), "center"_a, "rotation"_a, "extent"_a, - R"(Construct an OrientedBoundingBox from center, rotation and extent. -The OrientedBoundingBox will be created on the device of the given tensors, which + R"(Construct an OrientedBoundingBox from center, rotation and extent. +The OrientedBoundingBox will be created on the device of the given tensors, which must be on the same device and have the same data type.)"); docstring::ClassMethodDocInject( m, "OrientedBoundingBox", "__init__", @@ -295,7 +305,7 @@ must be on the same device and have the same data type.)"); obb.def("translate", &OrientedBoundingBox::Translate, R"(Translate the oriented box by the given translation. If relative is true, the translation is -added to the center of the box. If false, the center will be assigned to the +added to the center of the box. If false, the center will be assigned to the translation.)", "translation"_a, "relative"_a = true); obb.def("rotate", &OrientedBoundingBox::Rotate, @@ -309,7 +319,7 @@ The rotation center will be the box center if it is not specified.)", obb.def("scale", &OrientedBoundingBox::Scale, R"(Scale the axis-aligned box. If \f$mi\f$ is the min_bound and \f$ma\f$ is the max_bound of the axis aligned -bounding box, and \f$s\f$ and \f$c\f$ are the provided scaling factor and +bounding box, and \f$s\f$ and \f$c\f$ are the provided scaling factor and center respectively, then the new min_bound and max_bound are given by \f$mi = c + s (mi - c)\f$ and \f$ma = c + s (ma - c)\f$. The scaling center will be the box center if it is not specified.)", @@ -341,7 +351,7 @@ The scaling center will be the box center if it is not specified.)", obb.def_static("create_from_points", &OrientedBoundingBox::CreateFromPoints, R"(Creates an oriented bounding box using a PCA. Note that this is only an approximation to the minimum oriented bounding box -that could be computed for example with O'Rourke's algorithm +that could be computed for example with O'Rourke's algorithm (cf. http://cs.smith.edu/~jorourke/Papers/MinVolBox.pdf, https://www.geometrictools.com/Documentation/MinimumVolumeBox.pdf) This is a wrapper for a CPU implementation.)", "points"_a, "robust"_a = false); diff --git a/cpp/pybind/t/geometry/drawablegeometry.cpp b/cpp/pybind/t/geometry/drawablegeometry.cpp index 7d4a11493fc..57ef37b2f3a 100644 --- a/cpp/pybind/t/geometry/drawablegeometry.cpp +++ b/cpp/pybind/t/geometry/drawablegeometry.cpp @@ -13,12 +13,17 @@ namespace open3d { namespace t { namespace geometry { -void pybind_drawable_geometry_class(py::module& m) { - // open3d.t.geometry.DrawableGeometry +void pybind_drawable_geometry_class_declarations(py::module& m) { py::class_> drawable_geometry( m, "DrawableGeometry", "Base class for geometry types which can be visualized."); +} +void pybind_drawable_geometry_class_definitions(py::module& m) { + // open3d.t.geometry.DrawableGeometry + auto drawable_geometry = static_cast< + py::class_>>( + m.attr("DrawableGeometry")); drawable_geometry.def("has_valid_material", &DrawableGeometry::HasMaterial, "Returns true if the geometry's material is valid."); drawable_geometry.def_property( diff --git a/cpp/pybind/t/geometry/geometry.cpp b/cpp/pybind/t/geometry/geometry.cpp index 9563fbb215f..47c13bebc25 100644 --- a/cpp/pybind/t/geometry/geometry.cpp +++ b/cpp/pybind/t/geometry/geometry.cpp @@ -14,11 +14,16 @@ namespace open3d { namespace t { namespace geometry { -void pybind_geometry_class(py::module& m) { - // open3d.t.geometry.Geometry +void pybind_geometry_class_declarations(py::module& m) { py::class_, std::shared_ptr> geometry(m, "Geometry", "The base geometry class."); +} +void pybind_geometry_class_definitions(py::module& m) { + // open3d.t.geometry.Geometry + auto geometry = static_cast, + std::shared_ptr>>( + m.attr("Geometry")); geometry.def("clear", &Geometry::Clear, "Clear all elements in the geometry."); geometry.def("is_empty", &Geometry::IsEmpty, @@ -33,20 +38,34 @@ void pybind_geometry_class(py::module& m) { docstring::ClassMethodDocInject(m, "Geometry", "is_empty"); } -void pybind_geometry(py::module& m) { - py::module m_submodule = m.def_submodule( +void pybind_geometry_declarations(py::module& m) { + py::module m_geometry = m.def_submodule( "geometry", "Tensor-based geometry defining module."); - pybind_geometry_class(m_submodule); - pybind_drawable_geometry_class(m_submodule); - pybind_tensormap(m_submodule); - pybind_pointcloud(m_submodule); - pybind_lineset(m_submodule); - pybind_trianglemesh(m_submodule); - pybind_image(m_submodule); - pybind_boundingvolume(m_submodule); - pybind_voxel_block_grid(m_submodule); - pybind_raycasting_scene(m_submodule); + pybind_geometry_class_declarations(m_geometry); + pybind_drawable_geometry_class_declarations(m_geometry); + pybind_tensormap_declarations(m_geometry); + pybind_pointcloud_declarations(m_geometry); + pybind_lineset_declarations(m_geometry); + pybind_trianglemesh_declarations(m_geometry); + pybind_image_declarations(m_geometry); + pybind_boundingvolume_declarations(m_geometry); + pybind_voxel_block_grid_declarations(m_geometry); + pybind_raycasting_scene_declarations(m_geometry); +} + +void pybind_geometry_definitions(py::module& m) { + auto m_geometry = static_cast(m.attr("geometry")); + pybind_geometry_class_definitions(m_geometry); + pybind_drawable_geometry_class_definitions(m_geometry); + pybind_tensormap_definitions(m_geometry); + pybind_pointcloud_definitions(m_geometry); + pybind_lineset_definitions(m_geometry); + pybind_trianglemesh_definitions(m_geometry); + pybind_image_definitions(m_geometry); + pybind_boundingvolume_definitions(m_geometry); + pybind_voxel_block_grid_definitions(m_geometry); + pybind_raycasting_scene_definitions(m_geometry); } } // namespace geometry diff --git a/cpp/pybind/t/geometry/geometry.h b/cpp/pybind/t/geometry/geometry.h index 6e1271083df..a1c9f81ea62 100644 --- a/cpp/pybind/t/geometry/geometry.h +++ b/cpp/pybind/t/geometry/geometry.h @@ -34,18 +34,29 @@ class PyGeometry : public GeometryBase { } }; -void pybind_geometry(py::module& m); -void pybind_geometry_class(py::module& m); -void pybind_drawable_geometry_class(py::module& m); -void pybind_tensormap(py::module& m); -void pybind_image(py::module& m); -void pybind_pointcloud(py::module& m); -void pybind_lineset(py::module& m); -void pybind_trianglemesh(py::module& m); -void pybind_image(py::module& m); -void pybind_boundingvolume(py::module& m); -void pybind_voxel_block_grid(py::module& m); -void pybind_raycasting_scene(py::module& m); +void pybind_geometry_declarations(py::module& m); +void pybind_geometry_class_declarations(py::module& m); +void pybind_drawable_geometry_class_declarations(py::module& m); +void pybind_tensormap_declarations(py::module& m); +void pybind_pointcloud_declarations(py::module& m); +void pybind_lineset_declarations(py::module& m); +void pybind_trianglemesh_declarations(py::module& m); +void pybind_image_declarations(py::module& m); +void pybind_boundingvolume_declarations(py::module& m); +void pybind_voxel_block_grid_declarations(py::module& m); +void pybind_raycasting_scene_declarations(py::module& m); + +void pybind_geometry_definitions(py::module& m); +void pybind_geometry_class_definitions(py::module& m); +void pybind_drawable_geometry_class_definitions(py::module& m); +void pybind_tensormap_definitions(py::module& m); +void pybind_pointcloud_definitions(py::module& m); +void pybind_lineset_definitions(py::module& m); +void pybind_trianglemesh_definitions(py::module& m); +void pybind_image_definitions(py::module& m); +void pybind_boundingvolume_definitions(py::module& m); +void pybind_voxel_block_grid_definitions(py::module& m); +void pybind_raycasting_scene_definitions(py::module& m); } // namespace geometry } // namespace t diff --git a/cpp/pybind/t/geometry/image.cpp b/cpp/pybind/t/geometry/image.cpp index b9fe3c20725..6f42c4eca35 100644 --- a/cpp/pybind/t/geometry/image.cpp +++ b/cpp/pybind/t/geometry/image.cpp @@ -53,12 +53,11 @@ static const std::unordered_map {"distance_sigma", "Standard deviation for the image pixel positions."}}; -void pybind_image(py::module &m) { +void pybind_image_declarations(py::module &m) { py::class_, std::shared_ptr, Geometry> image(m, "Image", py::buffer_protocol(), "The Image class stores image with customizable rols, cols, " "channels, dtype and device."); - py::enum_(m, "InterpType", "Interpolation type.") .value("Nearest", Image::InterpType::Nearest) .value("Linear", Image::InterpType::Linear) @@ -66,7 +65,19 @@ void pybind_image(py::module &m) { .value("Lanczos", Image::InterpType::Lanczos) .value("Super", Image::InterpType::Super) .export_values(); - + py::class_, std::shared_ptr, + Geometry> + rgbd_image( + m, "RGBDImage", + "RGBDImage is a pair of color and depth images. For most " + "processing, the image pair should be aligned (same " + "viewpoint and " + "resolution)."); +} +void pybind_image_definitions(py::module &m) { + auto image = static_cast, + std::shared_ptr, Geometry>>( + m.attr("Image")); // Constructors image.def(py::init(), "Row-major storage is used, similar to OpenCV. Use (row, col, " @@ -266,15 +277,10 @@ void pybind_image(py::module &m) { docstring::ClassMethodDocInject(m, "Image", "clear"); docstring::ClassMethodDocInject(m, "Image", "is_empty"); docstring::ClassMethodDocInject(m, "Image", "to_legacy"); - - py::class_, std::shared_ptr, - Geometry> - rgbd_image( - m, "RGBDImage", - "RGBDImage is a pair of color and depth images. For most " - "processing, the image pair should be aligned (same " - "viewpoint and " - "resolution)."); + auto rgbd_image = + static_cast, + std::shared_ptr, Geometry>>( + m.attr("RGBDImage")); rgbd_image // Constructors. .def(py::init<>(), "Construct an empty RGBDImage.") diff --git a/cpp/pybind/t/geometry/lineset.cpp b/cpp/pybind/t/geometry/lineset.cpp index 299deef36b0..fe5bdb8ad33 100644 --- a/cpp/pybind/t/geometry/lineset.cpp +++ b/cpp/pybind/t/geometry/lineset.cpp @@ -19,7 +19,7 @@ namespace open3d { namespace t { namespace geometry { -void pybind_lineset(py::module& m) { +void pybind_lineset_declarations(py::module& m) { py::class_, std::shared_ptr, Geometry, DrawableGeometry> line_set(m, "LineSet", R"( @@ -72,7 +72,12 @@ The attributes of the line set have different levels:: lineset.point.labels = o3d.core.Tensor(...) lineset.line.features = o3d.core.Tensor(...) )"); +} +void pybind_lineset_definitions(py::module& m) { + auto line_set = static_cast< + py::class_, std::shared_ptr, + Geometry, DrawableGeometry>>(m.attr("LineSet")); // Constructors. line_set.def(py::init(), "device"_a = core::Device("CPU:0"), diff --git a/cpp/pybind/t/geometry/pointcloud.cpp b/cpp/pybind/t/geometry/pointcloud.cpp index a36813fd353..ddcfd89d788 100644 --- a/cpp/pybind/t/geometry/pointcloud.cpp +++ b/cpp/pybind/t/geometry/pointcloud.cpp @@ -47,7 +47,7 @@ static const std::unordered_map "neighbors search radius parameter to use HybridSearch. " "[Recommended ~1.4x voxel size]."}}; -void pybind_pointcloud(py::module& m) { +void pybind_pointcloud_declarations(py::module& m) { py::class_, std::shared_ptr, Geometry, DrawableGeometry> pointcloud(m, "PointCloud", @@ -95,7 +95,12 @@ The attributes of the point cloud have different levels:: pcd.point.intensities = o3d.core.Tensor([0.3, 0.1, 0.4], dtype, device) pcd.point.labels = o3d.core.Tensor([3, 1, 4], o3d.core.int32, device) )"); - +} +void pybind_pointcloud_definitions(py::module& m) { + auto pointcloud = + static_cast, + std::shared_ptr, Geometry, + DrawableGeometry>>(m.attr("PointCloud")); // Constructors. pointcloud .def(py::init(), @@ -252,11 +257,10 @@ The attributes of the point cloud have different levels:: "by selecting the farthest point from previous selected " "points iteratively", "num_samples"_a); - pointcloud.def( - "remove_radius_outliers", &PointCloud::RemoveRadiusOutliers, - "nb_points"_a, "search_radius"_a, - R"(Remove points that have less than nb_points neighbors in a -sphere of a given search radius. + pointcloud.def("remove_radius_outliers", &PointCloud::RemoveRadiusOutliers, + "nb_points"_a, "search_radius"_a, + R"(Remove points that have less than nb_points neighbors in a +sphere of a given search radius. Args: nb_points: Number of neighbor points required within the radius. @@ -270,7 +274,7 @@ sphere of a given search radius. &PointCloud::RemoveStatisticalOutliers, "nb_neighbors"_a, "std_ratio"_a, R"(Remove points that are further away from their \p nb_neighbor -neighbors in average. This function is not recommended to use on GPU. +neighbors in average. This function is not recommended to use on GPU. Args: nb_neighbors: Number of neighbors around the target point. @@ -285,9 +289,9 @@ neighbors in average. This function is not recommended to use on GPU. pointcloud.def( "remove_non_finite_points", &PointCloud::RemoveNonFinitePoints, "remove_nan"_a = true, "remove_infinite"_a = true, - R"(Remove all points from the point cloud that have a nan entry, or -infinite value. It also removes the corresponding attributes. - + R"(Remove all points from the point cloud that have a nan entry, or +infinite value. It also removes the corresponding attributes. + Args: remove_nan: Remove NaN values from the PointCloud. remove_infinite: Remove infinite values from the PointCloud. @@ -326,31 +330,31 @@ infinite value. It also removes the corresponding attributes. "lambda"_a = 0.0, "cos_alpha_tol"_a = 1.0, R"(Function to consistently orient the normals of a point cloud based on tangent planes. -The algorithm is described in Hoppe et al., "Surface Reconstruction from Unorganized Points", 1992. +The algorithm is described in Hoppe et al., "Surface Reconstruction from Unorganized Points", 1992. Additional information about the choice of lambda and cos_alpha_tol for complex -point clouds can be found in Piazza, Valentini, Varetti, "Mesh Reconstruction from Point Cloud", 2023 +point clouds can be found in Piazza, Valentini, Varetti, "Mesh Reconstruction from Point Cloud", 2023 (https://eugeniovaretti.github.io/meshreco/Piazza_Valentini_Varetti_MeshReconstructionFromPointCloud_2023.pdf). Args: k (int): Number of neighbors to use for tangent plane estimation. - lambda (float): A non-negative real parameter that influences the distance - metric used to identify the true neighbors of a point in complex - geometries. It penalizes the distance between a point and the tangent - plane defined by the reference point and its normal vector, helping to - mitigate misclassification issues encountered with traditional + lambda (float): A non-negative real parameter that influences the distance + metric used to identify the true neighbors of a point in complex + geometries. It penalizes the distance between a point and the tangent + plane defined by the reference point and its normal vector, helping to + mitigate misclassification issues encountered with traditional Euclidean distance metrics. - cos_alpha_tol (float): Cosine threshold angle used to determine the - inclusion boundary of neighbors based on the direction of the normal + cos_alpha_tol (float): Cosine threshold angle used to determine the + inclusion boundary of neighbors based on the direction of the normal vector. Example: We use Bunny point cloud to compute its normals and orient them consistently. - The initial reconstruction adheres to Hoppe's algorithm (raw), whereas the - second reconstruction utilises the lambda and cos_alpha_tol parameters. + The initial reconstruction adheres to Hoppe's algorithm (raw), whereas the + second reconstruction utilises the lambda and cos_alpha_tol parameters. Due to the high density of the Bunny point cloud available in Open3D a larger - value of the parameter k is employed to test the algorithm. Usually you do - not have at disposal such a refined point clouds, thus you cannot find a - proper choice of k: refer to + value of the parameter k is employed to test the algorithm. Usually you do + not have at disposal such a refined point clouds, thus you cannot find a + proper choice of k: refer to https://eugeniovaretti.github.io/meshreco for these cases.:: import open3d as o3d @@ -690,13 +694,13 @@ The implementation is inspired by the PCL implementation. Reference: Args: angle (float): The rotation angle in degree. - + axis (open3d.core.Tensor): The rotation axis. - + resolution (int): The resolution defines the number of intermediate sweeps about the rotation axis. - translation (float): The translation along the rotation axis. + translation (float): The translation along the rotation axis. Returns: A line set with the result of the sweep operation. @@ -719,9 +723,9 @@ The implementation is inspired by the PCL implementation. Reference: R"(Sweeps the point cloud along a direction vector. Args: - + vector (open3d.core.Tensor): The direction vector. - + scale (float): Scalar factor which essentially scales the direction vector. Returns: @@ -731,7 +735,7 @@ The implementation is inspired by the PCL implementation. Reference: Example: This code generates a set of straight lines from a point cloud:: - + import open3d as o3d import numpy as np pcd = o3d.t.geometry.PointCloud(np.random.rand(10,3)) @@ -743,7 +747,7 @@ The implementation is inspired by the PCL implementation. Reference: pointcloud.def("pca_partition", &PointCloud::PCAPartition, "max_points"_a, R"(Partition the point cloud by recursively doing PCA. -This function creates a new point attribute with the name "partition_ids" storing +This function creates a new point attribute with the name "partition_ids" storing the partition id for each point. Args: diff --git a/cpp/pybind/t/geometry/raycasting_scene.cpp b/cpp/pybind/t/geometry/raycasting_scene.cpp index e7f6dcecbd5..b3e8983aaef 100644 --- a/cpp/pybind/t/geometry/raycasting_scene.cpp +++ b/cpp/pybind/t/geometry/raycasting_scene.cpp @@ -13,7 +13,7 @@ namespace open3d { namespace t { namespace geometry { -void pybind_raycasting_scene(py::module& m) { +void pybind_raycasting_scene_declarations(py::module& m) { py::class_ raycasting_scene(m, "RaycastingScene", R"doc( A scene class with basic ray casting and closest point queries. @@ -52,7 +52,11 @@ The following shows how to create a scene and compute ray intersections:: plt.imshow(ans['t_hit'].numpy()) )doc"); +} +void pybind_raycasting_scene_definitions(py::module& m) { + auto raycasting_scene = + static_cast>(m.attr("RaycastingScene")); // Constructors. raycasting_scene.def(py::init(), "nthreads"_a = 0, R"doc( Create a RaycastingScene. @@ -219,7 +223,7 @@ Lists the intersections of the rays with the scene:: # Calculate intersection coordinates using ray_ids c = rays[lx['ray_ids']][:,:3] + rays[lx['ray_ids']][:,3:]*lx['t_hit'][...,None] - + # Visualize the rays and intersections. lines = o3d.t.geometry.LineSet() lines.point.positions = np.hstack([orig,dest]).reshape(-1,3) @@ -234,35 +238,35 @@ Lists the intersections of the rays with the scene:: Float32 describing the rays; {..} can be any number of dimensions. The last dimension must be 6 and has the format [ox, oy, oz, dx, dy, dz] with [ox,oy,oz] as the origin and [dx,dy,dz] as the direction. It is not - necessary to normalize the direction although it should be normalised if + necessary to normalize the direction although it should be normalised if t_hit is to be calculated in coordinate units. nthreads (int): The number of threads to use. Set to 0 for automatic. Returns: The returned dictionary contains - + ray_splits A tensor with ray intersection splits. Can be used to iterate over all intersections for each ray. The shape is {num_rays + 1}. - + ray_ids A tensor with ray IDs. The shape is {num_intersections}. - + t_hit - A tensor with the distance to the hit. The shape is {num_intersections}. - + A tensor with the distance to the hit. The shape is {num_intersections}. + geometry_ids A tensor with the geometry IDs. The shape is {num_intersections}. primitive_ids A tensor with the primitive IDs, which corresponds to the triangle index. The shape is {num_intersections}. - - primitive_uvs - A tensor with the barycentric coordinates of the intersection points within + + primitive_uvs + A tensor with the barycentric coordinates of the intersection points within the triangles. The shape is {num_intersections, 2}. - - + + An example of using ray_splits:: ray_splits: [0, 2, 3, 6, 6, 8] # note that the length of this is num_rays+1 @@ -271,8 +275,8 @@ An example of using ray_splits:: for ray_id, (start, end) in enumerate(zip(ray_splits[:-1], ray_splits[1:])): for i,t in enumerate(t_hit[start:end]): print(f'ray {ray_id}, intersection {i} at {t}') - - + + )doc"); raycasting_scene.def("compute_closest_points", @@ -302,12 +306,12 @@ Computes the closest points on the surfaces of the scene. A tensor with the primitive IDs, which corresponds to the triangle index. The shape is {..}. - primitive_uvs - A tensor with the barycentric coordinates of the closest points within + primitive_uvs + A tensor with the barycentric coordinates of the closest points within the triangles. The shape is {.., 2}. - primitive_normals - A tensor with the normals of the closest triangle . The shape is + primitive_normals + A tensor with the normals of the closest triangle . The shape is {.., 3}. )doc"); @@ -353,7 +357,7 @@ the intersections of a rays starting at the query points. nsamples (int): The number of rays used for determining the inside. This must be an odd number. The default is 1. Use a higher value if you - notice sign flipping, which can occur when rays hit exactly an edge or + notice sign flipping, which can occur when rays hit exactly an edge or vertex in the scene. Returns: diff --git a/cpp/pybind/t/geometry/tensormap.cpp b/cpp/pybind/t/geometry/tensormap.cpp index 36fea31b472..45882dc3115 100644 --- a/cpp/pybind/t/geometry/tensormap.cpp +++ b/cpp/pybind/t/geometry/tensormap.cpp @@ -116,7 +116,11 @@ static py::class_ bind_tensor_map(py::handle scope, return cl; } -void pybind_tensormap(py::module &m) { +void pybind_tensormap_declarations(py::module &m) { + auto tm = bind_tensor_map( + m, "TensorMap", "Map of String to Tensor with a primary key."); +} +void pybind_tensormap_definitions(py::module &m) { // Bind to the generic dictionary interface such that it works the same as a // regular dictionary in Python, except that types are enforced. Supported // functions include `__bool__`, `__iter__`, `items`, `__getitem__`, @@ -124,9 +128,8 @@ void pybind_tensormap(py::module &m) { // The `__delitem__` function is removed from bind_map, in bind_tensor_map, // and defined in this function, to use TensorMap::Erase, in order to // protect users from deleting the `private_key`. - auto tm = bind_tensor_map( - m, "TensorMap", "Map of String to Tensor with a primary key."); - + auto tm = static_cast>>( + m.attr("TensorMap")); tm.def("__delitem__", [](TensorMap &m, const std::string &k) { return m.Erase(k); }); diff --git a/cpp/pybind/t/geometry/trianglemesh.cpp b/cpp/pybind/t/geometry/trianglemesh.cpp index 783e7c0489c..5c2de11fa18 100644 --- a/cpp/pybind/t/geometry/trianglemesh.cpp +++ b/cpp/pybind/t/geometry/trianglemesh.cpp @@ -19,7 +19,7 @@ namespace open3d { namespace t { namespace geometry { -void pybind_trianglemesh(py::module& m) { +void pybind_trianglemesh_declarations(py::module& m) { py::class_, std::shared_ptr, Geometry, DrawableGeometry> triangle_mesh(m, "TriangleMesh", @@ -77,7 +77,13 @@ The attributes of the triangle mesh have different levels:: pcd.vertex.labels = o3d.core.Tensor(...) pcd.triangle.features = o3d.core.Tensor(...) )"); +} +void pybind_trianglemesh_definitions(py::module& m) { + auto triangle_mesh = + static_cast, + std::shared_ptr, Geometry, + DrawableGeometry>>(m.attr("TriangleMesh")); // Constructors. triangle_mesh .def(py::init(), diff --git a/cpp/pybind/t/geometry/voxel_block_grid.cpp b/cpp/pybind/t/geometry/voxel_block_grid.cpp index ff47ffa0e85..9928fcde83f 100644 --- a/cpp/pybind/t/geometry/voxel_block_grid.cpp +++ b/cpp/pybind/t/geometry/voxel_block_grid.cpp @@ -17,14 +17,17 @@ namespace open3d { namespace t { namespace geometry { -void pybind_voxel_block_grid(py::module& m) { +void pybind_voxel_block_grid_declarations(py::module& m) { py::class_ vbg( m, "VoxelBlockGrid", "A voxel block grid is a sparse grid of voxel blocks. Each voxel " "block is a dense 3D array, preserving local data distribution. If " "the block_resolution is set to 1, then the VoxelBlockGrid " "degenerates to a sparse voxel grid."); - +} +void pybind_voxel_block_grid_definitions(py::module& m) { + auto vbg = + static_cast>(m.attr("VoxelBlockGrid")); vbg.def(py::init&, const std::vector&, const std::vector&, float, int64_t, diff --git a/cpp/pybind/t/io/class_io.cpp b/cpp/pybind/t/io/class_io.cpp index 9ec88f08475..756187a8aa4 100644 --- a/cpp/pybind/t/io/class_io.cpp +++ b/cpp/pybind/t/io/class_io.cpp @@ -67,7 +67,35 @@ static const std::unordered_map "If set to true a progress bar is visualized in the console."}, }; -void pybind_class_io(py::module &m_io) { +void pybind_class_io_declarations(py::module &m_io) { + py::class_ depth_noise_simulator( + m_io, "DepthNoiseSimulator", + R"(Simulate depth image noise from a given noise distortion model. The distortion model is based on *Teichman et. al. "Unsupervised intrinsic calibration of depth sensors via SLAM" RSS 2009*. Also see __ + +Example:: + + import open3d as o3d + + # Redwood Indoor LivingRoom1 (Augmented ICL-NUIM) + # http://redwood-data.org/indoor/ + data = o3d.data.RedwoodIndoorLivingRoom1() + noise_model_path = data.noise_model_path + im_src_path = data.depth_paths[0] + depth_scale = 1000.0 + + # Read clean depth image (uint16) + im_src = o3d.t.io.read_image(im_src_path) + + # Run noise model simulation + simulator = o3d.t.io.DepthNoiseSimulator(noise_model_path) + im_dst = simulator.simulate(im_src, depth_scale=depth_scale) + + # Save noisy depth image (uint16) + o3d.t.io.write_image("noisy_depth.png", im_dst) + )"); +} + +void pybind_class_io_definitions(py::module &m_io) { // open3d::t::geometry::Image m_io.def( "read_image", @@ -153,25 +181,25 @@ The following example reads a triangle mesh with the .ply extension:: Args: filename (str): Path to the mesh file. - enable_post_processing (bool): If True enables post-processing. - Post-processing will + enable_post_processing (bool): If True enables post-processing. + Post-processing will - triangulate meshes with polygonal faces - remove redundant materials - pretransform vertices - generate face normals if needed - + For more information see ASSIMPs documentation on the flags - `aiProcessPreset_TargetRealtime_Fast, aiProcess_RemoveRedundantMaterials, + `aiProcessPreset_TargetRealtime_Fast, aiProcess_RemoveRedundantMaterials, aiProcess_OptimizeMeshes, aiProcess_PreTransformVertices`. - + Note that identical vertices will always be joined regardless of whether - post-processing is enabled or not, which changes the number of vertices + post-processing is enabled or not, which changes the number of vertices in the mesh. The `ply`-format is not affected by the post-processing. print_progress (bool): If True print the reading progress to the terminal. - + Returns: Returns the mesh object. On failure an empty mesh is returned. )doc"); @@ -196,31 +224,8 @@ The following example reads a triangle mesh with the .ply extension:: map_shared_argument_docstrings); // DepthNoiseSimulator - py::class_ depth_noise_simulator( - m_io, "DepthNoiseSimulator", - R"(Simulate depth image noise from a given noise distortion model. The distortion model is based on *Teichman et. al. "Unsupervised intrinsic calibration of depth sensors via SLAM" RSS 2009*. Also see __ - -Example:: - - import open3d as o3d - - # Redwood Indoor LivingRoom1 (Augmented ICL-NUIM) - # http://redwood-data.org/indoor/ - data = o3d.data.RedwoodIndoorLivingRoom1() - noise_model_path = data.noise_model_path - im_src_path = data.depth_paths[0] - depth_scale = 1000.0 - - # Read clean depth image (uint16) - im_src = o3d.t.io.read_image(im_src_path) - - # Run noise model simulation - simulator = o3d.t.io.DepthNoiseSimulator(noise_model_path) - im_dst = simulator.simulate(im_src, depth_scale=depth_scale) - - # Save noisy depth image (uint16) - o3d.t.io.write_image("noisy_depth.png", im_dst) - )"); + auto depth_noise_simulator = static_cast>( + m_io.attr("DepthNoiseSimulator")); depth_noise_simulator.def(py::init([](const fs::path &fielname) { return DepthNoiseSimulator(fielname.string()); }), diff --git a/cpp/pybind/t/io/io.cpp b/cpp/pybind/t/io/io.cpp index 5cbdb441879..8282c3a94fb 100644 --- a/cpp/pybind/t/io/io.cpp +++ b/cpp/pybind/t/io/io.cpp @@ -13,11 +13,16 @@ namespace open3d { namespace t { namespace io { -void pybind_io(py::module& m) { +void pybind_io_declarations(py::module& m) { py::module m_io = m.def_submodule("io", "Tensor-based input-output handling module."); - pybind_class_io(m_io); - pybind_sensor(m_io); + pybind_class_io_declarations(m_io); + pybind_sensor_declarations(m_io); +} +void pybind_io_definitions(py::module& m) { + auto m_io = static_cast(m.attr("io")); + pybind_class_io_definitions(m_io); + pybind_sensor_definitions(m_io); } } // namespace io diff --git a/cpp/pybind/t/io/io.h b/cpp/pybind/t/io/io.h index dc3aef9327a..84561452aa3 100644 --- a/cpp/pybind/t/io/io.h +++ b/cpp/pybind/t/io/io.h @@ -13,9 +13,13 @@ namespace open3d { namespace t { namespace io { -void pybind_io(py::module& m); -void pybind_class_io(py::module& m); -void pybind_sensor(py::module& m); +void pybind_io_declarations(py::module& m); +void pybind_class_io_declarations(py::module& m); +void pybind_sensor_declarations(py::module& m); + +void pybind_io_definitions(py::module& m); +void pybind_class_io_definitions(py::module& m); +void pybind_sensor_definitions(py::module& m); } // namespace io } // namespace t diff --git a/cpp/pybind/t/io/sensor.cpp b/cpp/pybind/t/io/sensor.cpp index 241d763d3cc..b0d9cf13cc2 100644 --- a/cpp/pybind/t/io/sensor.cpp +++ b/cpp/pybind/t/io/sensor.cpp @@ -22,7 +22,98 @@ namespace open3d { namespace t { namespace io { -void pybind_sensor(py::module &m) { +// RGBD video reader trampoline +class PyRGBDVideoReader : public RGBDVideoReader { +public: + using RGBDVideoReader::RGBDVideoReader; + bool IsOpened() const override { + PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, ); + } + + bool IsEOF() const override { + PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, ); + } + + bool Open(const std::string &filename) override { + PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, Open, filename); + } + + void Close() override { PYBIND11_OVERLOAD_PURE(void, RGBDVideoReader, ); } + + RGBDVideoMetadata &GetMetadata() override { + PYBIND11_OVERLOAD_PURE(RGBDVideoMetadata &, RGBDVideoReader, ); + } + + const RGBDVideoMetadata &GetMetadata() const override { + PYBIND11_OVERLOAD_PURE(const RGBDVideoMetadata &, RGBDVideoReader, ); + } + + bool SeekTimestamp(uint64_t timestamp) override { + PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, SeekTimestamp, timestamp); + } + + uint64_t GetTimestamp() const override { + PYBIND11_OVERLOAD_PURE(uint64_t, RGBDVideoReader, ); + } + + t::geometry::RGBDImage NextFrame() override { + PYBIND11_OVERLOAD_PURE(t::geometry::RGBDImage, RGBDVideoReader, ); + } + + std::string GetFilename() const override { + PYBIND11_OVERLOAD_PURE(std::string, RGBDVideoReader, ); + } +}; + +void pybind_sensor_declarations(py::module &m) { + py::enum_(m, "SensorType", "Sensor type") + .value("AZURE_KINECT", SensorType::AZURE_KINECT) + .value("REAL_SENSE", SensorType::REAL_SENSE); + py::class_ rgbd_video_metadata(m, "RGBDVideoMetadata", + "RGBD Video metadata."); + py::class_> + rgbd_video_reader(m, "RGBDVideoReader", "RGBD Video file reader."); + py::class_ rgbd_sensor( + m, "RGBDSensor", "Interface class for control of RGBD cameras."); +#ifdef BUILD_LIBREALSENSE + py::class_, RGBDVideoReader> + rs_bag_reader( + m, "RSBagReader", + "RealSense Bag file reader.\n" + "\tOnly the first color and depth streams from the bag " + "file will be read.\n" + " - The streams must have the same frame rate.\n" + " - The color stream must have RGB 8 bit (RGB8/BGR8) pixel " + "format\n" + " - The depth stream must have 16 bit unsigned int (Z16) " + "pixel format\n" + "The output is synchronized color and depth frame pairs " + "with the depth frame aligned to the color frame. " + "Unsynchronized frames will be dropped. With alignment, " + "the depth and color frames have the same viewpoint and " + "resolution. See format documentation `here " + "`__" + "\n\n" + ".. warning:: A few frames may be dropped if user code " + "takes a long time (>10 frame intervals) to process a " + "frame."); + py::class_ realsense_sensor_config( + m, "RealSenseSensorConfig", "Configuration for a RealSense camera"); + py::class_ realsense_valid_configs( + m, "RealSenseValidConfigs", + "Store set of valid configuration options for a connected " + "RealSense device. From this structure, a user can construct a " + "RealSenseSensorConfig object meeting their specifications."); + py::class_ realsense_sensor( + m, "RealSenseSensor", + "RealSense camera discovery, configuration, streaming and " + "recording"); +#endif +} + +void pybind_sensor_definitions(py::module &m) { static const std::unordered_map map_shared_argument_docstrings = { {"timestamp", "Timestamp in the video (usec)."}, @@ -39,13 +130,9 @@ void pybind_sensor(py::module &m) { "Size of internal frame buffer, increase this if you " "experience frame drops."}}; - py::enum_(m, "SensorType", "Sensor type") - .value("AZURE_KINECT", SensorType::AZURE_KINECT) - .value("REAL_SENSE", SensorType::REAL_SENSE); - // Class RGBD video metadata - py::class_ rgbd_video_metadata(m, "RGBDVideoMetadata", - "RGBD Video metadata."); + auto rgbd_video_metadata = static_cast>( + m.attr("RGBDVideoMetadata")); rgbd_video_metadata.def(py::init<>()) .def_readwrite("intrinsics", &RGBDVideoMetadata::intrinsics_, "Shared intrinsics between RGB & depth") @@ -78,57 +165,11 @@ void pybind_sensor(py::module &m) { "Number of color channels.") .def("__repr__", &RGBDVideoMetadata::ToString); - // RGBD video reader trampoline - class PyRGBDVideoReader : public RGBDVideoReader { - public: - using RGBDVideoReader::RGBDVideoReader; - bool IsOpened() const override { - PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, ); - } - - bool IsEOF() const override { - PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, ); - } - - bool Open(const std::string &filename) override { - PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, Open, filename); - } - - void Close() override { - PYBIND11_OVERLOAD_PURE(void, RGBDVideoReader, ); - } - - RGBDVideoMetadata &GetMetadata() override { - PYBIND11_OVERLOAD_PURE(RGBDVideoMetadata &, RGBDVideoReader, ); - } - - const RGBDVideoMetadata &GetMetadata() const override { - PYBIND11_OVERLOAD_PURE(const RGBDVideoMetadata &, - RGBDVideoReader, ); - } - - bool SeekTimestamp(uint64_t timestamp) override { - PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, SeekTimestamp, - timestamp); - } - - uint64_t GetTimestamp() const override { - PYBIND11_OVERLOAD_PURE(uint64_t, RGBDVideoReader, ); - } - - t::geometry::RGBDImage NextFrame() override { - PYBIND11_OVERLOAD_PURE(t::geometry::RGBDImage, RGBDVideoReader, ); - } - - std::string GetFilename() const override { - PYBIND11_OVERLOAD_PURE(std::string, RGBDVideoReader, ); - } - }; - // Class RGBD video reader - py::class_> - rgbd_video_reader(m, "RGBDVideoReader", "RGBD Video file reader."); + auto rgbd_video_reader = + static_cast>>( + m.attr("RGBDVideoReader")); rgbd_video_reader.def(py::init<>()) .def_static( "create", @@ -147,34 +188,15 @@ void pybind_sensor(py::module &m) { map_shared_argument_docstrings); // Class RGBD sensor - py::class_ rgbd_sensor( - m, "RGBDSensor", "Interface class for control of RGBD cameras."); + auto rgbd_sensor = + static_cast>(m.attr("RGBDSensor")); rgbd_sensor.def("__repr__", &RGBDSensor::ToString); #ifdef BUILD_LIBREALSENSE // Class RS bag reader - py::class_, RGBDVideoReader> - rs_bag_reader( - m, "RSBagReader", - "RealSense Bag file reader.\n" - "\tOnly the first color and depth streams from the bag " - "file will be read.\n" - " - The streams must have the same frame rate.\n" - " - The color stream must have RGB 8 bit (RGB8/BGR8) pixel " - "format\n" - " - The depth stream must have 16 bit unsigned int (Z16) " - "pixel format\n" - "The output is synchronized color and depth frame pairs " - "with the depth frame aligned to the color frame. " - "Unsynchronized frames will be dropped. With alignment, " - "the depth and color frames have the same viewpoint and " - "resolution. See format documentation `here " - "`__" - "\n\n" - ".. warning:: A few frames may be dropped if user code " - "takes a long time (>10 frame intervals) to process a " - "frame."); + auto rs_bag_reader = + static_cast, + RGBDVideoReader>>(m.attr("RSBagReader")); rs_bag_reader.def(py::init<>()) .def(py::init(), "buffer_size"_a = RSBagReader::DEFAULT_BUFFER_SIZE) @@ -218,19 +240,16 @@ void pybind_sensor(py::module &m) { map_shared_argument_docstrings); // Class RealSenseSensorConfig - py::class_ realsense_sensor_config( - m, "RealSenseSensorConfig", "Configuration for a RealSense camera"); - + auto realsense_sensor_config = + static_cast>( + m.attr("RealSenseSensorConfig")); realsense_sensor_config.def(py::init<>(), "Default config will be used") .def(py::init &>(), "config"_a, "Initialize config with a map"); - - py::class_ realsense_valid_configs( - m, "RealSenseValidConfigs", - "Store set of valid configuration options for a connected " - "RealSense device. From this structure, a user can construct a " - "RealSenseSensorConfig object meeting their specifications."); + auto realsense_valid_configs = + static_cast>( + m.attr("RealSenseValidConfigs")); realsense_valid_configs .def_readwrite("serial", &RealSenseValidConfigs::serial, "Device serial number.") @@ -241,10 +260,9 @@ void pybind_sensor(py::module &m) { "list of valid values."); // Class RealSenseSensor - py::class_ realsense_sensor( - m, "RealSenseSensor", - "RealSense camera discovery, configuration, streaming and " - "recording"); + auto realsense_sensor = + static_cast>( + m.attr("RealSenseSensor")); realsense_sensor.def(py::init<>(), "Initialize with default settings.") .def_static("list_devices", &RealSenseSensor::ListDevices, py::call_guard(), diff --git a/cpp/pybind/t/pipelines/odometry/odometry.cpp b/cpp/pybind/t/pipelines/odometry/odometry.cpp index a6744e6a0b9..120d5827522 100644 --- a/cpp/pybind/t/pipelines/odometry/odometry.cpp +++ b/cpp/pybind/t/pipelines/odometry/odometry.cpp @@ -15,20 +15,95 @@ namespace t { namespace pipelines { namespace odometry { -void pybind_odometry_classes(py::module &m) { - py::enum_(m, "Method", "Tensor odometry estimation method.") +// Odometry functions have similar arguments, sharing arg docstrings. +static const std::unordered_map + map_shared_argument_docstrings = { + {"criteria", "Odometry convergence criteria."}, + {"criteria_list", "List of Odometry convergence criteria."}, + {"depth_outlier_trunc", + "Depth difference threshold used to filter projective " + "associations."}, + {"depth_huber_delta", + "Huber norm parameter used in depth loss."}, + {"depth_scale", + "Converts depth pixel values to meters by dividing the scale " + "factor."}, + {"init_source_to_target", + "(4, 4) initial transformation matrix from source to target."}, + {"intrinsics", "(3, 3) intrinsic matrix for projection."}, + {"intensity_huber_delta", + "Huber norm parameter used in intensity loss."}, + {"method", + "Estimation method used to apply RGBD odometry. " + "One of (``PointToPlane``, ``Intensity``, ``Hybrid``)"}, + {"params", "Odometry loss parameters."}, + {"source", "The source RGBD image."}, + {"source_depth", + "(row, col, channel = 1) Float32 source depth image obtained " + "by PreprocessDepth before calling this function."}, + {"source_intensity", + "(row, col, channel = 1) Float32 source intensity image " + "obtained by RGBToGray before calling this function"}, + {"source_vertex_map", + "(row, col, channel = 3) Float32 source vertex image obtained " + "by CreateVertexMap before calling this function."}, + {"target", "The target RGBD image."}, + {"target_depth", + "(row, col, channel = 1) Float32 target depth image obtained " + "by PreprocessDepth before calling this function."}, + {"target_depth_dx", + "(row, col, channel = 1) Float32 target depth gradient image " + "along x-axis obtained by FilterSobel before calling this " + "function."}, + {"target_depth_dy", + "(row, col, channel = 1) Float32 target depth gradient image " + "along y-axis obtained by FilterSobel before calling this " + "function."}, + {"target_intensity", + "(row, col, channel = 1) Float32 target intensity image " + "obtained by RGBToGray before calling this function"}, + {"target_intensity_dx", + "(row, col, channel = 1) Float32 target intensity gradient " + "image along x-axis obtained by FilterSobel before calling " + "this function."}, + {"target_intensity_dy", + "(row, col, channel = 1) Float32 target intensity gradient " + "image along y-axis obtained by FilterSobel before calling " + "this function."}, + {"target_normal_map", + "(row, col, channel = 3) Float32 target normal image obtained " + "by CreateNormalMap before calling this function."}, + {"target_vertex_map", + "(row, col, channel = 3) Float32 target vertex image obtained " + "by CreateVertexMap before calling this function."}}; + +void pybind_odometry_declarations(py::module &m) { + py::module m_odometry = + m.def_submodule("odometry", "Tensor odometry pipeline."); + py::enum_(m_odometry, "Method", + "Tensor odometry estimation method.") .value("PointToPlane", Method::PointToPlane) .value("Intensity", Method::Intensity) .value("Hybrid", Method::Hybrid) .export_values(); - - // open3d.t.pipelines.odometry.OdometryConvergenceCriteria py::class_ odometry_convergence_criteria( - m, "OdometryConvergenceCriteria", + m_odometry, "OdometryConvergenceCriteria", "Convergence criteria of odometry. " "Odometry algorithm stops if the relative change of fitness and " "rmse hit ``relative_fitness`` and ``relative_rmse`` individually, " "or the iteration number exceeds ``max_iteration``."); + py::class_ odometry_result(m_odometry, "OdometryResult", + "Odometry results."); + py::class_ odometry_loss_params( + m_odometry, "OdometryLossParams", "Odometry loss parameters."); +} + +void pybind_odometry_definitions(py::module &m) { + auto m_odometry = static_cast(m.attr("odometry")); + // open3d.t.pipelines.odometry.OdometryConvergenceCriteria + auto odometry_convergence_criteria = + static_cast>( + m_odometry.attr("OdometryConvergenceCriteria")); py::detail::bind_copy_functions( odometry_convergence_criteria); odometry_convergence_criteria @@ -56,8 +131,8 @@ void pybind_odometry_classes(py::module &m) { }); // open3d.t.pipelines.odometry.OdometryResult - py::class_ odometry_result(m, "OdometryResult", - "Odometry results."); + auto odometry_result = static_cast>( + m_odometry.attr("OdometryResult")); py::detail::bind_copy_functions(odometry_result); odometry_result .def(py::init(), @@ -82,8 +157,8 @@ void pybind_odometry_classes(py::module &m) { }); // open3d.t.pipelines.odometry.OdometryLossParams - py::class_ odometry_loss_params( - m, "OdometryLossParams", "Odometry loss parameters."); + auto odometry_loss_params = static_cast>( + m_odometry.attr("OdometryLossParams")); py::detail::bind_copy_functions(odometry_loss_params); odometry_loss_params .def(py::init(), @@ -107,88 +182,25 @@ void pybind_odometry_classes(py::module &m) { olp.depth_outlier_trunc_, olp.depth_huber_delta_, olp.intensity_huber_delta_); }); -} - -// Odometry functions have similar arguments, sharing arg docstrings. -static const std::unordered_map - map_shared_argument_docstrings = { - {"criteria", "Odometry convergence criteria."}, - {"criteria_list", "List of Odometry convergence criteria."}, - {"depth_outlier_trunc", - "Depth difference threshold used to filter projective " - "associations."}, - {"depth_huber_delta", - "Huber norm parameter used in depth loss."}, - {"depth_scale", - "Converts depth pixel values to meters by dividing the scale " - "factor."}, - {"init_source_to_target", - "(4, 4) initial transformation matrix from source to target."}, - {"intrinsics", "(3, 3) intrinsic matrix for projection."}, - {"intensity_huber_delta", - "Huber norm parameter used in intensity loss."}, - {"method", - "Estimation method used to apply RGBD odometry. " - "One of (``PointToPlane``, ``Intensity``, ``Hybrid``)"}, - {"params", "Odometry loss parameters."}, - {"source", "The source RGBD image."}, - {"source_depth", - "(row, col, channel = 1) Float32 source depth image obtained " - "by PreprocessDepth before calling this function."}, - {"source_intensity", - "(row, col, channel = 1) Float32 source intensity image " - "obtained by RGBToGray before calling this function"}, - {"source_vertex_map", - "(row, col, channel = 3) Float32 source vertex image obtained " - "by CreateVertexMap before calling this function."}, - {"target", "The target RGBD image."}, - {"target_depth", - "(row, col, channel = 1) Float32 target depth image obtained " - "by PreprocessDepth before calling this function."}, - {"target_depth_dx", - "(row, col, channel = 1) Float32 target depth gradient image " - "along x-axis obtained by FilterSobel before calling this " - "function."}, - {"target_depth_dy", - "(row, col, channel = 1) Float32 target depth gradient image " - "along y-axis obtained by FilterSobel before calling this " - "function."}, - {"target_intensity", - "(row, col, channel = 1) Float32 target intensity image " - "obtained by RGBToGray before calling this function"}, - {"target_intensity_dx", - "(row, col, channel = 1) Float32 target intensity gradient " - "image along x-axis obtained by FilterSobel before calling " - "this function."}, - {"target_intensity_dy", - "(row, col, channel = 1) Float32 target intensity gradient " - "image along y-axis obtained by FilterSobel before calling " - "this function."}, - {"target_normal_map", - "(row, col, channel = 3) Float32 target normal image obtained " - "by CreateNormalMap before calling this function."}, - {"target_vertex_map", - "(row, col, channel = 3) Float32 target vertex image obtained " - "by CreateVertexMap before calling this function."}}; - -void pybind_odometry_methods(py::module &m) { - m.def("rgbd_odometry_multi_scale", &RGBDOdometryMultiScale, - py::call_guard(), - "Function for Multi Scale RGBD odometry.", "source"_a, "target"_a, - "intrinsics"_a, - "init_source_to_target"_a = - core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")), - "depth_scale"_a = 1000.0f, "depth_max"_a = 3.0f, - "criteria_list"_a = - std::vector({10, 5, 3}), - "method"_a = Method::Hybrid, "params"_a = OdometryLossParams()); - docstring::FunctionDocInject(m, "rgbd_odometry_multi_scale", + m_odometry.def( + "rgbd_odometry_multi_scale", &RGBDOdometryMultiScale, + py::call_guard(), + "Function for Multi Scale RGBD odometry.", "source"_a, "target"_a, + "intrinsics"_a, + "init_source_to_target"_a = + core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")), + "depth_scale"_a = 1000.0f, "depth_max"_a = 3.0f, + "criteria_list"_a = + std::vector({10, 5, 3}), + "method"_a = Method::Hybrid, "params"_a = OdometryLossParams()); + docstring::FunctionDocInject(m_odometry, "rgbd_odometry_multi_scale", map_shared_argument_docstrings); - m.def("compute_odometry_result_point_to_plane", - &ComputeOdometryResultPointToPlane, - py::call_guard(), - R"(Estimates the OdometryResult (4x4 rigid transformation T from + m_odometry.def( + "compute_odometry_result_point_to_plane", + &ComputeOdometryResultPointToPlane, + py::call_guard(), + R"(Estimates the OdometryResult (4x4 rigid transformation T from source to target, with inlier rmse and fitness). Performs one iteration of RGBD odometry using Loss function: :math:`[(V_p - V_q)^T N_p]^2` @@ -199,15 +211,18 @@ where, q is obtained by transforming p with init_source_to_target then projecting with intrinsics. Reference: KinectFusion, ISMAR 2011.)", - "source_vertex_map"_a, "target_vertex_map"_a, "target_normal_map"_a, - "intrinsics"_a, "init_source_to_target"_a, "depth_outlier_trunc"_a, - "depth_huber_delta"_a); - docstring::FunctionDocInject(m, "compute_odometry_result_point_to_plane", + "source_vertex_map"_a, "target_vertex_map"_a, "target_normal_map"_a, + "intrinsics"_a, "init_source_to_target"_a, "depth_outlier_trunc"_a, + "depth_huber_delta"_a); + docstring::FunctionDocInject(m_odometry, + "compute_odometry_result_point_to_plane", map_shared_argument_docstrings); - m.def("compute_odometry_result_intensity", &ComputeOdometryResultIntensity, - py::call_guard(), - R"(Estimates the OdometryResult (4x4 rigid transformation T from + m_odometry.def( + "compute_odometry_result_intensity", + &ComputeOdometryResultIntensity, + py::call_guard(), + R"(Estimates the OdometryResult (4x4 rigid transformation T from source to target, with inlier rmse and fitness). Performs one iteration of RGBD odometry using Loss function: :math:`(I_p - I_q)^2` @@ -219,17 +234,19 @@ projecting with intrinsics. Reference: Real-time visual odometry from dense RGB-D images, ICCV Workshops, 2017.)", - "source_depth"_a, "target_depth"_a, "source_intensity"_a, - "target_intensity"_a, "target_intensity_dx"_a, - "target_intensity_dy"_a, "source_vertex_map"_a, "intrinsics"_a, - "init_source_to_target"_a, "depth_outlier_trunc"_a, - "intensity_huber_delta"_a); - docstring::FunctionDocInject(m, "compute_odometry_result_intensity", + "source_depth"_a, "target_depth"_a, "source_intensity"_a, + "target_intensity"_a, "target_intensity_dx"_a, + "target_intensity_dy"_a, "source_vertex_map"_a, "intrinsics"_a, + "init_source_to_target"_a, "depth_outlier_trunc"_a, + "intensity_huber_delta"_a); + docstring::FunctionDocInject(m_odometry, + "compute_odometry_result_intensity", map_shared_argument_docstrings); - m.def("compute_odometry_result_hybrid", &ComputeOdometryResultHybrid, - py::call_guard(), - R"(Estimates the OdometryResult (4x4 rigid transformation T from + m_odometry.def( + "compute_odometry_result_hybrid", &ComputeOdometryResultHybrid, + py::call_guard(), + R"(Estimates the OdometryResult (4x4 rigid transformation T from source to target, with inlier rmse and fitness). Performs one iteration of RGBD odometry using Loss function: :math:`(I_p - I_q)^2 + \lambda(D_p - (D_q)')^2` @@ -242,27 +259,21 @@ q is obtained by transforming p with init_source_to_target then projecting with intrinsics. Reference: J. Park, Q.Y. Zhou, and V. Koltun, Colored Point Cloud Registration Revisited, ICCV, 2017.)", - "source_depth"_a, "target_depth"_a, "source_intensity"_a, - "target_intensity"_a, "target_depth_dx"_a, "target_depth_dy"_a, - "target_intensity_dx"_a, "target_intensity_dy"_a, - "source_vertex_map"_a, "intrinsics"_a, "init_source_to_target"_a, - "depth_outlier_trunc"_a, "depth_huber_delta"_a, - "intensity_huber_delta"_a); - docstring::FunctionDocInject(m, "compute_odometry_result_hybrid", + "source_depth"_a, "target_depth"_a, "source_intensity"_a, + "target_intensity"_a, "target_depth_dx"_a, "target_depth_dy"_a, + "target_intensity_dx"_a, "target_intensity_dy"_a, + "source_vertex_map"_a, "intrinsics"_a, "init_source_to_target"_a, + "depth_outlier_trunc"_a, "depth_huber_delta"_a, + "intensity_huber_delta"_a); + docstring::FunctionDocInject(m_odometry, "compute_odometry_result_hybrid", map_shared_argument_docstrings); - m.def("compute_odometry_information_matrix", - &ComputeOdometryInformationMatrix, - py::call_guard(), "source_depth"_a, - "target_depth"_a, "intrinsic"_a, "source_to_target"_a, - "dist_threshold"_a, "depth_scale"_a = 1000.0, "depth_max"_a = 3.0); -} - -void pybind_odometry(py::module &m) { - py::module m_submodule = - m.def_submodule("odometry", "Tensor odometry pipeline."); - pybind_odometry_classes(m_submodule); - pybind_odometry_methods(m_submodule); + m_odometry.def("compute_odometry_information_matrix", + &ComputeOdometryInformationMatrix, + py::call_guard(), "source_depth"_a, + "target_depth"_a, "intrinsic"_a, "source_to_target"_a, + "dist_threshold"_a, "depth_scale"_a = 1000.0, + "depth_max"_a = 3.0); } } // namespace odometry diff --git a/cpp/pybind/t/pipelines/odometry/odometry.h b/cpp/pybind/t/pipelines/odometry/odometry.h index 2a603765675..c8494d66654 100644 --- a/cpp/pybind/t/pipelines/odometry/odometry.h +++ b/cpp/pybind/t/pipelines/odometry/odometry.h @@ -14,7 +14,8 @@ namespace t { namespace pipelines { namespace odometry { -void pybind_odometry(py::module &m); +void pybind_odometry_declarations(py::module &m); +void pybind_odometry_definitions(py::module &m); } // namespace odometry } // namespace pipelines diff --git a/cpp/pybind/t/pipelines/pipelines.cpp b/cpp/pybind/t/pipelines/pipelines.cpp index 6407923bf91..7f3fbe4e551 100644 --- a/cpp/pybind/t/pipelines/pipelines.cpp +++ b/cpp/pybind/t/pipelines/pipelines.cpp @@ -17,13 +17,20 @@ namespace open3d { namespace t { namespace pipelines { -void pybind_pipelines(py::module& m) { +void pybind_pipelines_declarations(py::module& m) { py::module m_pipelines = m.def_submodule( "pipelines", "Tensor-based geometry processing pipelines."); - odometry::pybind_odometry(m_pipelines); - registration::pybind_registration(m_pipelines); - slac::pybind_slac(m_pipelines); - slam::pybind_slam(m_pipelines); + odometry::pybind_odometry_declarations(m_pipelines); + registration::pybind_registration_declarations(m_pipelines); + slac::pybind_slac_declarations(m_pipelines); + slam::pybind_slam_declarations(m_pipelines); +} +void pybind_pipelines_definitions(py::module& m) { + auto m_pipelines = static_cast(m.attr("pipelines")); + odometry::pybind_odometry_definitions(m_pipelines); + registration::pybind_registration_definitions(m_pipelines); + slac::pybind_slac_definitions(m_pipelines); + slam::pybind_slam_definitions(m_pipelines); } } // namespace pipelines diff --git a/cpp/pybind/t/pipelines/pipelines.h b/cpp/pybind/t/pipelines/pipelines.h index 015a3959b56..96635b839a1 100644 --- a/cpp/pybind/t/pipelines/pipelines.h +++ b/cpp/pybind/t/pipelines/pipelines.h @@ -13,8 +13,9 @@ namespace open3d { namespace t { namespace pipelines { -void pybind_pipelines(py::module& m); +void pybind_pipelines_declarations(py::module& m); +void pybind_pipelines_definitions(py::module& m); -} +} // namespace pipelines } // namespace t } // namespace open3d diff --git a/cpp/pybind/t/pipelines/registration/feature.cpp b/cpp/pybind/t/pipelines/registration/feature.cpp index 74de409115d..2317e5ae5cd 100644 --- a/cpp/pybind/t/pipelines/registration/feature.cpp +++ b/cpp/pybind/t/pipelines/registration/feature.cpp @@ -17,16 +17,16 @@ namespace t { namespace pipelines { namespace registration { -void pybind_feature(py::module &m) { - m.def("compute_fpfh_feature", &ComputeFPFHFeature, - py::call_guard(), - R"(Function to compute FPFH feature for a point cloud. +void pybind_feature_definitions(py::module &m_registration) { + m_registration.def("compute_fpfh_feature", &ComputeFPFHFeature, + py::call_guard(), + R"(Function to compute FPFH feature for a point cloud. It uses KNN search (Not recommended to use on GPU) if only max_nn parameter is provided, Radius search (Not recommended to use on GPU) if only radius parameter is provided, and Hybrid search (Recommended) if both are provided.)", - "input"_a, "max_nn"_a = 100, "radius"_a = py::none()); + "input"_a, "max_nn"_a = 100, "radius"_a = py::none()); docstring::FunctionDocInject( - m, "compute_fpfh_feature", + m_registration, "compute_fpfh_feature", {{"input", "The input point cloud with data type float32 or float64."}, {"max_nn", @@ -36,13 +36,14 @@ parameter is provided, and Hybrid search (Recommended) if both are provided.)", "[optional] Neighbor search radius parameter. [Recommended ~5x " "voxel size]"}}); - m.def("correspondences_from_features", &CorrespondencesFromFeatures, - py::call_guard(), - R"(Function to query nearest neighbors of source_features in target_features.)", - "source_features"_a, "target_features"_a, "mutual_filter"_a = false, - "mutual_consistency_ratio"_a = 0.1f); + m_registration.def( + "correspondences_from_features", &CorrespondencesFromFeatures, + py::call_guard(), + R"(Function to query nearest neighbors of source_features in target_features.)", + "source_features"_a, "target_features"_a, "mutual_filter"_a = false, + "mutual_consistency_ratio"_a = 0.1f); docstring::FunctionDocInject( - m, "correspondences_from_features", + m_registration, "correspondences_from_features", {{"source_features", "The source features in shape (N, dim)."}, {"target_features", "The target features in shape (M, dim)."}, {"mutual_filter", diff --git a/cpp/pybind/t/pipelines/registration/registration.cpp b/cpp/pybind/t/pipelines/registration/registration.cpp index 6dbe3f1d27a..10988a80894 100644 --- a/cpp/pybind/t/pipelines/registration/registration.cpp +++ b/cpp/pybind/t/pipelines/registration/registration.cpp @@ -46,14 +46,101 @@ class PyTransformationEstimation : public TransformationEstimationBase { } }; -void pybind_registration_classes(py::module &m) { - // open3d.t.pipelines.registration.ICPConvergenceCriteria +// Registration functions have similar arguments, sharing arg docstrings. +static const std::unordered_map + map_shared_argument_docstrings = { + {"correspondences", + "Tensor of type Int64 containing indices of corresponding " + "target points, where the value is the target index and the " + "index of the value itself is the source index. It contains " + "-1 as value at index with no correspondence."}, + {"criteria", "Convergence criteria"}, + {"criteria_list", + "List of Convergence criteria for each scale of multi-scale " + "icp."}, + {"estimation_method", + "Estimation method. One of " + "(``TransformationEstimationPointToPoint``, " + "``TransformationEstimationPointToPlane``, " + "``TransformationEstimationForColoredICP``, " + "``TransformationEstimationForGeneralizedICP``)"}, + {"init_source_to_target", "Initial transformation estimation"}, + {"max_correspondence_distance", + "Maximum correspondence points-pair distance."}, + {"max_correspondence_distances", + "o3d.utility.DoubleVector of maximum correspondence " + "points-pair distances for multi-scale icp."}, + {"option", "Registration option"}, + {"source", "The source point cloud."}, + {"target", "The target point cloud."}, + {"transformation", + "The 4x4 transformation matrix of type Float64 " + "to transform ``source`` to ``target``"}, + {"voxel_size", + "The input pointclouds will be down-sampled to this " + "`voxel_size` scale. If `voxel_size` < 0, original scale will " + "be used. However it is highly recommended to down-sample the " + "point-cloud for performance. By default original scale of " + "the point-cloud will be used."}, + {"voxel_sizes", + "o3d.utility.DoubleVector of voxel sizes in strictly " + "decreasing order, for multi-scale icp."}, + {"callback_after_iteration", + "Optional lambda function, saves string to tensor map of " + "attributes such as iteration_index, scale_index, " + "scale_iteration_index, inlier_rmse, fitness, transformation, " + "on CPU device, updated after each iteration."}}; + +void pybind_registration_declarations(py::module &m) { + py::module m_registration = m.def_submodule( + "registration", "Tensor-based registration pipeline."); py::class_ convergence_criteria( - m, "ICPConvergenceCriteria", + m_registration, "ICPConvergenceCriteria", "Convergence criteria of ICP. " "ICP algorithm stops if the relative change of fitness and rmse " "hit ``relative_fitness`` and ``relative_rmse`` individually, " "or the iteration number exceeds ``max_iteration``."); + py::class_ registration_result( + m_registration, "RegistrationResult", "Registration results."); + py::class_> + te(m_registration, "TransformationEstimation", + "Base class that estimates a transformation between two " + "point clouds. The virtual function ComputeTransformation() " + "must be implemented in subclasses."); + py::class_, + TransformationEstimation> + te_p2p(m_registration, "TransformationEstimationPointToPoint", + "Class to estimate a transformation for point to " + "point distance."); + py::class_, + TransformationEstimation> + te_p2l(m_registration, "TransformationEstimationPointToPlane", + "Class to estimate a transformation for point to " + "plane distance."); + py::class_< + TransformationEstimationForColoredICP, + PyTransformationEstimation, + TransformationEstimation> + te_col(m_registration, "TransformationEstimationForColoredICP", + "Class to estimate a transformation between two point " + "clouds using color information"); + py::class_< + TransformationEstimationForDopplerICP, + PyTransformationEstimation, + TransformationEstimation> + te_dop(m_registration, "TransformationEstimationForDopplerICP", + "Class to estimate a transformation between two point " + "clouds using color information"); + pybind_robust_kernel_declarations(m_registration); +} +void pybind_registration_definitions(py::module &m) { + auto m_registration = static_cast(m.attr("registration")); + // open3d.t.pipelines.registration.ICPConvergenceCriteria + auto convergence_criteria = static_cast>( + m_registration.attr("ICPConvergenceCriteria")); py::detail::bind_copy_functions( convergence_criteria); convergence_criteria @@ -80,8 +167,8 @@ void pybind_registration_classes(py::module &m) { }); // open3d.t.pipelines.registration.RegistrationResult - py::class_ registration_result(m, "RegistrationResult", - "Registration results."); + auto registration_result = static_cast>( + m_registration.attr("RegistrationResult")); py::detail::bind_default_constructor( registration_result); py::detail::bind_copy_functions(registration_result); @@ -124,12 +211,10 @@ void pybind_registration_classes(py::module &m) { }); // open3d.t.pipelines.registration.TransformationEstimation - py::class_> - te(m, "TransformationEstimation", - "Base class that estimates a transformation between two " - "point clouds. The virtual function ComputeTransformation() " - "must be implemented in subclasses."); + auto te = static_cast< + py::class_>>( + m_registration.attr("TransformationEstimation")); te.def("compute_rmse", &TransformationEstimation::ComputeRMSE, "source"_a, "target"_a, "correspondences"_a, "Compute RMSE between source and target points cloud given " @@ -142,7 +227,7 @@ void pybind_registration_classes(py::module &m) { "iteration"_a = 0, "Compute transformation from source to target point cloud given " "correspondences."); - docstring::ClassMethodDocInject(m, "TransformationEstimation", + docstring::ClassMethodDocInject(m_registration, "TransformationEstimation", "compute_rmse", {{"source", "Source point cloud."}, {"target", "Target point cloud."}, @@ -155,7 +240,8 @@ void pybind_registration_classes(py::module &m) { "index. It contains -1 as value " "at index with no correspondence."}}); docstring::ClassMethodDocInject( - m, "TransformationEstimation", "compute_transformation", + m_registration, "TransformationEstimation", + "compute_transformation", {{"source", "Source point cloud."}, {"target", "Target point cloud."}, {"correspondences", @@ -169,12 +255,11 @@ void pybind_registration_classes(py::module &m) { // open3d.t.pipelines.registration.TransformationEstimationPointToPoint // TransformationEstimation - py::class_, - TransformationEstimation> - te_p2p(m, "TransformationEstimationPointToPoint", - "Class to estimate a transformation for point to " - "point distance."); + auto te_p2p = static_cast, + TransformationEstimation>>( + m_registration.attr("TransformationEstimationPointToPoint")); py::detail::bind_copy_functions( te_p2p); te_p2p.def(py::init()) @@ -185,12 +270,11 @@ void pybind_registration_classes(py::module &m) { // open3d.t.pipelines.registration.TransformationEstimationPointToPlane // TransformationEstimation - py::class_, - TransformationEstimation> - te_p2l(m, "TransformationEstimationPointToPlane", - "Class to estimate a transformation for point to " - "plane distance."); + auto te_p2l = static_cast, + TransformationEstimation>>( + m_registration.attr("TransformationEstimationPointToPlane")); py::detail::bind_default_constructor( te_p2l); py::detail::bind_copy_functions( @@ -209,13 +293,11 @@ void pybind_registration_classes(py::module &m) { // open3d.t.pipelines.registration.TransformationEstimationForColoredICP // TransformationEstimation - py::class_< + auto te_col = static_cast, - TransformationEstimation> - te_col(m, "TransformationEstimationForColoredICP", - "Class to estimate a transformation between two point " - "clouds using color information"); + TransformationEstimation>>( + m_registration.attr("TransformationEstimationForColoredICP")); py::detail::bind_default_constructor( te_col); py::detail::bind_copy_functions( @@ -253,13 +335,11 @@ void pybind_registration_classes(py::module &m) { // open3d.t.pipelines.registration.TransformationEstimationForDopplerICP // TransformationEstimation - py::class_< + auto te_dop = static_cast, - TransformationEstimation> - te_dop(m, "TransformationEstimationForDopplerICP", - "Class to estimate a transformation between two point " - "clouds using color information"); + TransformationEstimation>>( + m_registration.attr("TransformationEstimationForDopplerICP")); py::detail::bind_default_constructor( te_dop); py::detail::bind_copy_functions( @@ -356,102 +436,53 @@ void pybind_registration_classes(py::module &m) { transform_vehicle_to_sensor_, "The 4x4 extrinsic transformation matrix between " "the vehicle and the sensor frames."); -} - -// Registration functions have similar arguments, sharing arg docstrings. -static const std::unordered_map - map_shared_argument_docstrings = { - {"correspondences", - "Tensor of type Int64 containing indices of corresponding " - "target points, where the value is the target index and the " - "index of the value itself is the source index. It contains " - "-1 as value at index with no correspondence."}, - {"criteria", "Convergence criteria"}, - {"criteria_list", - "List of Convergence criteria for each scale of multi-scale " - "icp."}, - {"estimation_method", - "Estimation method. One of " - "(``TransformationEstimationPointToPoint``, " - "``TransformationEstimationPointToPlane``, " - "``TransformationEstimationForColoredICP``, " - "``TransformationEstimationForGeneralizedICP``)"}, - {"init_source_to_target", "Initial transformation estimation"}, - {"max_correspondence_distance", - "Maximum correspondence points-pair distance."}, - {"max_correspondence_distances", - "o3d.utility.DoubleVector of maximum correspondence " - "points-pair distances for multi-scale icp."}, - {"option", "Registration option"}, - {"source", "The source point cloud."}, - {"target", "The target point cloud."}, - {"transformation", - "The 4x4 transformation matrix of type Float64 " - "to transform ``source`` to ``target``"}, - {"voxel_size", - "The input pointclouds will be down-sampled to this " - "`voxel_size` scale. If `voxel_size` < 0, original scale will " - "be used. However it is highly recommended to down-sample the " - "point-cloud for performance. By default original scale of " - "the point-cloud will be used."}, - {"voxel_sizes", - "o3d.utility.DoubleVector of voxel sizes in strictly " - "decreasing order, for multi-scale icp."}, - {"callback_after_iteration", - "Optional lambda function, saves string to tensor map of " - "attributes such as iteration_index, scale_index, " - "scale_iteration_index, inlier_rmse, fitness, transformation, " - "on CPU device, updated after each iteration."}}; - -void pybind_registration_methods(py::module &m) { - m.def("evaluate_registration", &EvaluateRegistration, - py::call_guard(), - "Function for evaluating registration between point clouds", - "source"_a, "target"_a, "max_correspondence_distance"_a, - "transformation"_a = - core::Tensor::Eye(4, core::Float64, core::Device("CPU:0"))); - docstring::FunctionDocInject(m, "evaluate_registration", + m_registration.def( + "evaluate_registration", &EvaluateRegistration, + py::call_guard(), + "Function for evaluating registration between point clouds", + "source"_a, "target"_a, "max_correspondence_distance"_a, + "transformation"_a = + core::Tensor::Eye(4, core::Float64, core::Device("CPU:0"))); + docstring::FunctionDocInject(m_registration, "evaluate_registration", map_shared_argument_docstrings); - m.def("icp", &ICP, py::call_guard(), - "Function for ICP registration", "source"_a, "target"_a, - "max_correspondence_distance"_a, - "init_source_to_target"_a = - core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")), - "estimation_method"_a = TransformationEstimationPointToPoint(), - "criteria"_a = ICPConvergenceCriteria(), "voxel_size"_a = -1.0, - "callback_after_iteration"_a = py::none()); - docstring::FunctionDocInject(m, "icp", map_shared_argument_docstrings); - - m.def("multi_scale_icp", &MultiScaleICP, - py::call_guard(), - "Function for Multi-Scale ICP registration", "source"_a, "target"_a, - "voxel_sizes"_a, "criteria_list"_a, "max_correspondence_distances"_a, - "init_source_to_target"_a = - core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")), - "estimation_method"_a = TransformationEstimationPointToPoint(), - "callback_after_iteration"_a = py::none()); - docstring::FunctionDocInject(m, "multi_scale_icp", + m_registration.def( + "icp", &ICP, py::call_guard(), + "Function for ICP registration", "source"_a, "target"_a, + "max_correspondence_distance"_a, + "init_source_to_target"_a = + core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")), + "estimation_method"_a = TransformationEstimationPointToPoint(), + "criteria"_a = ICPConvergenceCriteria(), "voxel_size"_a = -1.0, + "callback_after_iteration"_a = py::none()); + docstring::FunctionDocInject(m_registration, "icp", map_shared_argument_docstrings); - m.def("get_information_matrix", &GetInformationMatrix, - py::call_guard(), - "Function for computing information matrix from transformation " - "matrix. Information matrix is tensor of shape {6, 6}, dtype Float64 " - "on CPU device.", - "source"_a, "target"_a, "max_correspondence_distance"_a, - "transformation"_a); - docstring::FunctionDocInject(m, "get_information_matrix", + m_registration.def( + "multi_scale_icp", &MultiScaleICP, + py::call_guard(), + "Function for Multi-Scale ICP registration", "source"_a, "target"_a, + "voxel_sizes"_a, "criteria_list"_a, + "max_correspondence_distances"_a, + "init_source_to_target"_a = + core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")), + "estimation_method"_a = TransformationEstimationPointToPoint(), + "callback_after_iteration"_a = py::none()); + docstring::FunctionDocInject(m_registration, "multi_scale_icp", map_shared_argument_docstrings); -} - -void pybind_registration(py::module &m) { - py::module m_submodule = m.def_submodule( - "registration", "Tensor-based registration pipeline."); - pybind_registration_classes(m_submodule); - pybind_registration_methods(m_submodule); - pybind_feature(m_submodule); - pybind_robust_kernels(m_submodule); + m_registration.def( + "get_information_matrix", &GetInformationMatrix, + py::call_guard(), + "Function for computing information matrix from transformation " + "matrix. Information matrix is tensor of shape {6, 6}, dtype " + "Float64 " + "on CPU device.", + "source"_a, "target"_a, "max_correspondence_distance"_a, + "transformation"_a); + docstring::FunctionDocInject(m_registration, "get_information_matrix", + map_shared_argument_docstrings); + pybind_feature_definitions(m_registration); + pybind_robust_kernel_definitions(m_registration); } } // namespace registration diff --git a/cpp/pybind/t/pipelines/registration/registration.h b/cpp/pybind/t/pipelines/registration/registration.h index 671f6e96d0d..518e6b319ed 100644 --- a/cpp/pybind/t/pipelines/registration/registration.h +++ b/cpp/pybind/t/pipelines/registration/registration.h @@ -14,9 +14,11 @@ namespace t { namespace pipelines { namespace registration { -void pybind_feature(py::module &m); -void pybind_registration(py::module &m); -void pybind_robust_kernels(py::module &m); +void pybind_registration_declarations(py::module &m); +void pybind_robust_kernel_declarations(py::module &m_registration); +void pybind_registration_definitions(py::module &m); +void pybind_feature_definitions(py::module &m_registration); +void pybind_robust_kernel_definitions(py::module &m_registration); } // namespace registration } // namespace pipelines diff --git a/cpp/pybind/t/pipelines/registration/robust_kernel.cpp b/cpp/pybind/t/pipelines/registration/robust_kernel.cpp index 2b2e7f5bccc..49198c0fb4b 100644 --- a/cpp/pybind/t/pipelines/registration/robust_kernel.cpp +++ b/cpp/pybind/t/pipelines/registration/robust_kernel.cpp @@ -15,8 +15,11 @@ namespace t { namespace pipelines { namespace registration { -void pybind_robust_kernel_classes(py::module& m) { - py::enum_(m, "RobustKernelMethod", +void pybind_robust_kernel_declarations(py::module& m) { + py::module m_robust_kernel = m.def_submodule( + "robust_kernel", + "Tensor-based robust kernel for outlier rejection."); + py::enum_(m_robust_kernel, "RobustKernelMethod", "Robust kernel method for outlier rejection.") .value("L2Loss", RobustKernelMethod::L2Loss) .value("L1Loss", RobustKernelMethod::L1Loss) @@ -26,9 +29,7 @@ void pybind_robust_kernel_classes(py::module& m) { .value("TukeyLoss", RobustKernelMethod::TukeyLoss) .value("GeneralizedLoss", RobustKernelMethod::GeneralizedLoss) .export_values(); - - // open3d.t.pipelines.odometry.OdometryConvergenceCriteria - py::class_ robust_kernel(m, "RobustKernel", + py::class_ robust_kernel(m_robust_kernel, "RobustKernel", R"( Base class that models a robust kernel for outlier rejection. The virtual function ``weight()`` must be implemented in derived classes. @@ -100,6 +101,12 @@ Philippe Babin et al. For more information please also see: **"Adaptive Robust Kernels for Non-Linear Least Squares Problems"**, by Nived Chebrolu et al. )"); +} + +void pybind_robust_kernel_definitions(py::module& m) { + auto m_robust_kernel = static_cast(m.attr("robust_kernel")); + auto robust_kernel = static_cast>( + m_robust_kernel.attr("RobustKernel")); py::detail::bind_copy_functions(robust_kernel); robust_kernel .def(py::init([](const RobustKernelMethod type, @@ -124,13 +131,6 @@ Non-Linear Least Squares Problems"**, by Nived Chebrolu et al. }); } -void pybind_robust_kernels(py::module& m) { - py::module m_submodule = m.def_submodule( - "robust_kernel", - "Tensor-based robust kernel for outlier rejection."); - pybind_robust_kernel_classes(m_submodule); -} - } // namespace registration } // namespace pipelines } // namespace t diff --git a/cpp/pybind/t/pipelines/slac/slac.cpp b/cpp/pybind/t/pipelines/slac/slac.cpp index a2874e0b172..4510f78207a 100644 --- a/cpp/pybind/t/pipelines/slac/slac.cpp +++ b/cpp/pybind/t/pipelines/slac/slac.cpp @@ -18,10 +18,45 @@ namespace t { namespace pipelines { namespace slac { -void pybind_slac_classes(py::module &m) { +// SLAC functions have similar arguments, sharing arg docstrings. +static const std::unordered_map + map_shared_argument_docstrings = { + {"fnames_processed", + "List of filenames (str) for pre-processed pointcloud " + "fragments."}, + {"fragment_filenames", + "List of filenames (str) for pointcloud fragments."}, + {"fragment_pose_graph", "PoseGraph for pointcloud fragments"}, + {"params", + "slac_optimizer_params Parameters to tune in optimization."}, + {"debug_option", "debug options."}}; + +void pybind_slac_declarations(py::module &m) { + py::module m_slac = m.def_submodule( + "slac", + "Tensor-based Simultaneous Localisation and Calibration pipeline."); py::class_ slac_optimizer_params( - m, "slac_optimizer_params", + m_slac, "slac_optimizer_params", "SLAC parameters to tune in optimization."); + py::class_ slac_debug_option(m_slac, "slac_debug_option", + "SLAC debug options."); + py::class_ control_grid( + m_slac, "control_grid", + " ControlGrid is a spatially hashed voxel grid used for non-rigid " + "point cloud registration and TSDF integration. Each grid stores a " + "map from the initial grid location to the deformed location. You " + "can imagine a control grid as a jelly that is warped upon " + "perturbation with its overall shape preserved. " + "Reference: " + "https://github.com/qianyizh/ElasticReconstruction/blob/master/" + "FragmentOptimizer/OptApp.cpp " + "http://vladlen.info/papers/elastic-fragments.pdf. "); +} + +void pybind_slac_definitions(py::module &m) { + auto m_slac = static_cast(m.attr("slac")); + auto slac_optimizer_params = static_cast>( + m_slac.attr("slac_optimizer_params")); py::detail::bind_copy_functions(slac_optimizer_params); slac_optimizer_params .def(py::init slac_debug_option(m, "slac_debug_option", - "SLAC debug options."); + auto slac_debug_option = static_cast>( + m_slac.attr("slac_debug_option")); py::detail::bind_copy_functions(slac_debug_option); slac_debug_option .def(py::init(), "debug"_a = false, @@ -88,18 +122,8 @@ void pybind_slac_classes(py::module &m) { debug_option.debug_, debug_option.debug_start_node_idx_); }); - - py::class_ control_grid( - m, "control_grid", - " ControlGrid is a spatially hashed voxel grid used for non-rigid " - "point cloud registration and TSDF integration. Each grid stores a " - "map from the initial grid location to the deformed location. You " - "can imagine a control grid as a jelly that is warped upon " - "perturbation with its overall shape preserved. " - "Reference: " - "https://github.com/qianyizh/ElasticReconstruction/blob/master/" - "FragmentOptimizer/OptApp.cpp " - "http://vladlen.info/papers/elastic-fragments.pdf. "); + auto control_grid = + static_cast>(m_slac.attr("control_grid")); py::detail::bind_copy_functions(control_grid); control_grid.def(py::init<>()) .def(py::init(), "grid_size"_a, @@ -205,64 +229,43 @@ void pybind_slac_classes(py::module &m) { "anchor_idx={:d}].", control_grid.Size(), control_grid.GetAnchorIdx()); }); -} - -// SLAC functions have similar arguments, sharing arg docstrings. -static const std::unordered_map - map_shared_argument_docstrings = { - {"fnames_processed", - "List of filenames (str) for pre-processed pointcloud " - "fragments."}, - {"fragment_filenames", - "List of filenames (str) for pointcloud fragments."}, - {"fragment_pose_graph", "PoseGraph for pointcloud fragments"}, - {"params", - "slac_optimizer_params Parameters to tune in optimization."}, - {"debug_option", "debug options."}}; - -void pybind_slac_methods(py::module &m) { - m.def("save_correspondences_for_pointclouds", - &SaveCorrespondencesForPointClouds, - py::call_guard(), - "Read pose graph containing loop closures and odometry to compute " - "correspondences. Uses aggressive pruning -- reject any suspicious " - "pair.", - "fnames_processed"_a, "fragment_pose_graph"_a, - "params"_a = SLACOptimizerParams(), - "debug_option"_a = SLACDebugOption()); - docstring::FunctionDocInject(m, "save_correspondences_for_pointclouds", + m_slac.def( + "save_correspondences_for_pointclouds", + &SaveCorrespondencesForPointClouds, + py::call_guard(), + "Read pose graph containing loop closures and odometry to compute " + "correspondences. Uses aggressive pruning -- reject any suspicious " + "pair.", + "fnames_processed"_a, "fragment_pose_graph"_a, + "params"_a = SLACOptimizerParams(), + "debug_option"_a = SLACDebugOption()); + docstring::FunctionDocInject(m_slac, "save_correspondences_for_pointclouds", map_shared_argument_docstrings); - m.def("run_slac_optimizer_for_fragments", &RunSLACOptimizerForFragments, - "Simultaneous Localization and Calibration: Self-Calibration of " - "Consumer Depth Cameras, CVPR 2014 Qian-Yi Zhou and Vladlen Koltun " - "Estimate a shared control grid for all fragments for scene " - "reconstruction, implemented in " - "https://github.com/qianyizh/ElasticReconstruction. ", - "fragment_filenames"_a, "fragment_pose_graph"_a, - "params"_a = SLACOptimizerParams(), - "debug_option"_a = SLACDebugOption()); - docstring::FunctionDocInject(m, "run_slac_optimizer_for_fragments", + m_slac.def( + "run_slac_optimizer_for_fragments", &RunSLACOptimizerForFragments, + "Simultaneous Localization and Calibration: Self-Calibration of " + "Consumer Depth Cameras, CVPR 2014 Qian-Yi Zhou and Vladlen Koltun " + "Estimate a shared control grid for all fragments for scene " + "reconstruction, implemented in " + "https://github.com/qianyizh/ElasticReconstruction. ", + "fragment_filenames"_a, "fragment_pose_graph"_a, + "params"_a = SLACOptimizerParams(), + "debug_option"_a = SLACDebugOption()); + docstring::FunctionDocInject(m_slac, "run_slac_optimizer_for_fragments", map_shared_argument_docstrings); - m.def("run_rigid_optimizer_for_fragments", &RunRigidOptimizerForFragments, - "Extended ICP to simultaneously align multiple point clouds with " - "dense pairwise point-to-plane distances.", - "fragment_filenames"_a, "fragment_pose_graph"_a, - "params"_a = SLACOptimizerParams(), - "debug_option"_a = SLACDebugOption()); - docstring::FunctionDocInject(m, "run_rigid_optimizer_for_fragments", + m_slac.def( + "run_rigid_optimizer_for_fragments", &RunRigidOptimizerForFragments, + "Extended ICP to simultaneously align multiple point clouds with " + "dense pairwise point-to-plane distances.", + "fragment_filenames"_a, "fragment_pose_graph"_a, + "params"_a = SLACOptimizerParams(), + "debug_option"_a = SLACDebugOption()); + docstring::FunctionDocInject(m_slac, "run_rigid_optimizer_for_fragments", map_shared_argument_docstrings); } -void pybind_slac(py::module &m) { - py::module m_submodule = m.def_submodule( - "slac", - "Tensor-based Simultaneous Localisation and Calibration pipeline."); - pybind_slac_classes(m_submodule); - pybind_slac_methods(m_submodule); -} - } // namespace slac } // namespace pipelines } // namespace t diff --git a/cpp/pybind/t/pipelines/slac/slac.h b/cpp/pybind/t/pipelines/slac/slac.h index aa161593640..c3e734086b4 100644 --- a/cpp/pybind/t/pipelines/slac/slac.h +++ b/cpp/pybind/t/pipelines/slac/slac.h @@ -14,7 +14,8 @@ namespace t { namespace pipelines { namespace slac { -void pybind_slac(py::module &m); +void pybind_slac_declarations(py::module &m); +void pybind_slac_definitions(py::module &m); } // namespace slac } // namespace pipelines diff --git a/cpp/pybind/t/pipelines/slam/slam.cpp b/cpp/pybind/t/pipelines/slam/slam.cpp index 5a2cf260d60..c7ea2940c81 100644 --- a/cpp/pybind/t/pipelines/slam/slam.cpp +++ b/cpp/pybind/t/pipelines/slam/slam.cpp @@ -54,10 +54,18 @@ static const std::unordered_map "--trunc_voxel_multiplier=8 with --voxel_size=0.006(m) " "creates a truncation distance of 0.048(m)."}}; -void pybind_slam_model(py::module &m) { - py::class_ model(m, "Model", "Volumetric model for Dense SLAM."); +void pybind_slam_declarations(py::module &m) { + py::module m_slam = m.def_submodule("slam", "Tensor DenseSLAM pipeline."); + py::class_ model(m_slam, "Model", + "Volumetric model for Dense SLAM."); + py::class_ frame(m_slam, "Frame", + "A frame container that stores a map from keys " + "(color, depth) to tensor images."); +} +void pybind_slam_definitions(py::module &m) { + auto m_slam = static_cast(m.attr("slam")); + auto model = static_cast>(m_slam.attr("Model")); py::detail::bind_copy_functions(model); - model.def(py::init<>()); model.def(py::init(), "Constructor of a VoxelBlockGrid", "voxel_size"_a, @@ -65,7 +73,7 @@ void pybind_slam_model(py::module &m) { "transformation"_a = core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")), "device"_a = core::Device("CUDA:0")); - docstring::ClassMethodDocInject(m, "Model", "__init__", + docstring::ClassMethodDocInject(m_slam, "Model", "__init__", map_shared_argument_docstrings); model.def("get_current_frame_pose", &Model::GetCurrentFramePose); @@ -77,7 +85,7 @@ void pybind_slam_model(py::module &m) { "model_frame"_a, "depth_scale"_a = 1000.0, "depth_min"_a = 0.1, "depth_max"_a = 3.0, "trunc_voxel_multiplier"_a = 8.0, "enable_color"_a = false, "weight_threshold"_a = -1.0); - docstring::ClassMethodDocInject(m, "Model", "synthesize_model_frame", + docstring::ClassMethodDocInject(m_slam, "Model", "synthesize_model_frame", map_shared_argument_docstrings); model.def( @@ -89,7 +97,7 @@ void pybind_slam_model(py::module &m) { "method"_a = odometry::Method::PointToPlane, "criteria"_a = (std::vector){ 6, 3, 1}); - docstring::ClassMethodDocInject(m, "Model", "track_frame_to_model", + docstring::ClassMethodDocInject(m_slam, "Model", "track_frame_to_model", map_shared_argument_docstrings); model.def("integrate", &Model::Integrate, @@ -97,21 +105,21 @@ void pybind_slam_model(py::module &m) { "Integrate an input frame to a volume.", "input_frame"_a, "depth_scale"_a = 1000.0, "depth_max"_a = 3.0, "trunc_voxel_multiplier"_a = 8.0); - docstring::ClassMethodDocInject(m, "Model", "integrate", + docstring::ClassMethodDocInject(m_slam, "Model", "integrate", map_shared_argument_docstrings); model.def("extract_pointcloud", &Model::ExtractPointCloud, py::call_guard(), "Extract point cloud from the volumetric model.", "weight_threshold"_a = 3.0, "estimated_number"_a = -1); - docstring::ClassMethodDocInject(m, "Model", "extract_pointcloud", + docstring::ClassMethodDocInject(m_slam, "Model", "extract_pointcloud", map_shared_argument_docstrings); model.def("extract_trianglemesh", &Model::ExtractTriangleMesh, py::call_guard(), "Extract triangle mesh from the volumetric model.", "weight_threshold"_a = 3.0, "estimated_number"_a = -1); - docstring::ClassMethodDocInject(m, "Model", "extract_trianglemesh", + docstring::ClassMethodDocInject(m_slam, "Model", "extract_trianglemesh", map_shared_argument_docstrings); model.def( @@ -127,17 +135,12 @@ void pybind_slam_model(py::module &m) { "frame to the world frame."); model.def_readwrite("frame_id", &Model::frame_id_, "Get the current frame index in a sequence."); -} - -void pybind_slam_frame(py::module &m) { - py::class_ frame(m, "Frame", - "A frame container that stores a map from keys " - "(color, depth) to tensor images."); + auto frame = static_cast>(m_slam.attr("Frame")); py::detail::bind_copy_functions(frame); frame.def(py::init(), "height"_a, "width"_a, "intrinsics"_a, "device"_a); - docstring::ClassMethodDocInject(m, "Frame", "__init__", + docstring::ClassMethodDocInject(m_slam, "Frame", "__init__", map_shared_argument_docstrings); frame.def("height", &Frame::GetHeight); @@ -153,13 +156,6 @@ void pybind_slam_frame(py::module &m) { "Get a 2D image from from the given key in the map."); } -void pybind_slam(py::module &m) { - py::module m_submodule = - m.def_submodule("slam", "Tensor DenseSLAM pipeline."); - pybind_slam_model(m_submodule); - pybind_slam_frame(m_submodule); -} - } // namespace slam } // namespace pipelines } // namespace t diff --git a/cpp/pybind/t/pipelines/slam/slam.h b/cpp/pybind/t/pipelines/slam/slam.h index eea3c937abf..335e03c65fd 100644 --- a/cpp/pybind/t/pipelines/slam/slam.h +++ b/cpp/pybind/t/pipelines/slam/slam.h @@ -14,7 +14,8 @@ namespace t { namespace pipelines { namespace slam { -void pybind_slam(py::module &m); +void pybind_slam_declarations(py::module &m); +void pybind_slam_definitions(py::module &m); } // namespace slam } // namespace pipelines diff --git a/cpp/pybind/t/t.cpp b/cpp/pybind/t/t.cpp index b3a166efc1c..48a6dd9b811 100644 --- a/cpp/pybind/t/t.cpp +++ b/cpp/pybind/t/t.cpp @@ -15,11 +15,17 @@ namespace open3d { namespace t { -void pybind_t(py::module& m) { - py::module m_submodule = m.def_submodule("t"); - pipelines::pybind_pipelines(m_submodule); - geometry::pybind_geometry(m_submodule); - io::pybind_io(m_submodule); +void pybind_t_declarations(py::module& m) { + py::module m_t = m.def_submodule("t"); + pipelines::pybind_pipelines_declarations(m_t); + geometry::pybind_geometry_declarations(m_t); + io::pybind_io_declarations(m_t); +} +void pybind_t_definitions(py::module& m) { + auto m_t = static_cast(m.attr("t")); + pipelines::pybind_pipelines_definitions(m_t); + geometry::pybind_geometry_definitions(m_t); + io::pybind_io_definitions(m_t); } } // namespace t diff --git a/cpp/pybind/t/t.h b/cpp/pybind/t/t.h index c941372b5b8..95a050b4c02 100644 --- a/cpp/pybind/t/t.h +++ b/cpp/pybind/t/t.h @@ -12,7 +12,8 @@ namespace open3d { namespace t { -void pybind_t(py::module& m); +void pybind_t_declarations(py::module& m); +void pybind_t_definitions(py::module& m); } // namespace t } // namespace open3d diff --git a/cpp/pybind/utility/eigen.cpp b/cpp/pybind/utility/eigen.cpp index f23e94060f3..6220d7e14f8 100644 --- a/cpp/pybind/utility/eigen.cpp +++ b/cpp/pybind/utility/eigen.cpp @@ -306,8 +306,17 @@ py::class_ pybind_eigen_vector_of_matrix( namespace open3d { namespace utility { -void pybind_eigen(py::module &m) { +void pybind_eigen_declarations(py::module &m) { auto intvector = pybind_eigen_vector_of_scalar(m, "IntVector"); + auto doublevector = + pybind_eigen_vector_of_scalar(m, "DoubleVector"); + auto vector3dvector = pybind_eigen_vector_of_vector( + m, "Vector3dVector", "std::vector", + py::py_array_to_vectors_double); +} +void pybind_eigen_definitions(py::module &m) { + auto intvector = static_cast( + m, "IntVector"))>(m.attr("IntVector")); intvector.attr("__doc__") = docstring::static_property( py::cpp_function([](py::handle arg) -> std::string { return R"(Convert int32 numpy array of shape ``(n,)`` to Open3D format.)"; @@ -315,16 +324,18 @@ void pybind_eigen(py::module &m) { py::none(), py::none(), ""); auto doublevector = - pybind_eigen_vector_of_scalar(m, "DoubleVector"); + static_cast( + m, "DoubleVector"))>(m.attr("DoubleVector")); doublevector.attr("__doc__") = docstring::static_property( py::cpp_function([](py::handle arg) -> std::string { return R"(Convert float64 numpy array of shape ``(n,)`` to Open3D format.)"; }), py::none(), py::none(), ""); - - auto vector3dvector = pybind_eigen_vector_of_vector( - m, "Vector3dVector", "std::vector", - py::py_array_to_vectors_double); + auto vector3dvector = + static_cast( + m, "Vector3dVector", "std::vector", + py::py_array_to_vectors_double))>( + m.attr("Vector3dVector")); vector3dvector.attr("__doc__") = docstring::static_property( py::cpp_function([](py::handle arg) -> std::string { return R"(Convert float64 numpy array of shape ``(n, 3)`` to Open3D format. diff --git a/cpp/pybind/utility/logging.cpp b/cpp/pybind/utility/logging.cpp index 2be3832c883..8d50af1213f 100644 --- a/cpp/pybind/utility/logging.cpp +++ b/cpp/pybind/utility/logging.cpp @@ -13,7 +13,7 @@ namespace open3d { namespace utility { -void pybind_logging(py::module& m) { +void pybind_logging_declarations(py::module& m) { py::enum_ vl(m, "VerbosityLevel", py::arithmetic(), "VerbosityLevel"); vl.value("Error", VerbosityLevel::Error) @@ -27,7 +27,13 @@ void pybind_logging(py::module& m) { return "Enum class for VerbosityLevel."; }), py::none(), py::none(), ""); - + py::class_ verbosity_context_manager( + m, "VerbosityContextManager", + "A context manager to " + "temporally change the " + "verbosity level of Open3D"); +} +void pybind_logging_definitions(py::module& m) { m.def("set_verbosity_level", &SetVerbosityLevel, "Set global verbosity level of Open3D", py::arg("verbosity_level")); docstring::FunctionDocInject( @@ -44,11 +50,10 @@ void pybind_logging(py::module& m) { utility::LogInfo("Resetting default logger to print to terminal."); utility::Logger::GetInstance().ResetPrintFunction(); }); - - py::class_(m, "VerbosityContextManager", - "A context manager to " - "temporally change the " - "verbosity level of Open3D") + auto verbosity_context_manager = + static_cast>( + m.attr("VerbosityContextManager")); + verbosity_context_manager .def(py::init(), "Create a VerbosityContextManager with a given VerbosityLevel", "level"_a) diff --git a/cpp/pybind/utility/utility.cpp b/cpp/pybind/utility/utility.cpp index 315aa0a2125..1e26aa1bfb9 100644 --- a/cpp/pybind/utility/utility.cpp +++ b/cpp/pybind/utility/utility.cpp @@ -13,11 +13,16 @@ namespace open3d { namespace utility { -void pybind_utility(py::module &m) { - py::module m_submodule = m.def_submodule("utility"); - pybind_eigen(m_submodule); - pybind_logging(m_submodule); - random::pybind_random(m_submodule); +void pybind_utility_declarations(py::module &m) { + py::module m_utility = m.def_submodule("utility"); + pybind_eigen_declarations(m_utility); + pybind_logging_declarations(m_utility); + random::pybind_random(m_utility); +} +void pybind_utility_definitions(py::module &m) { + auto m_utility = static_cast(m.attr("utility")); + pybind_eigen_definitions(m_utility); + pybind_logging_definitions(m_utility); } } // namespace utility diff --git a/cpp/pybind/utility/utility.h b/cpp/pybind/utility/utility.h index eeb40cb4e8f..2f61661674d 100644 --- a/cpp/pybind/utility/utility.h +++ b/cpp/pybind/utility/utility.h @@ -12,10 +12,12 @@ namespace open3d { namespace utility { -void pybind_utility(py::module &m); - -void pybind_eigen(py::module &m); -void pybind_logging(py::module &m); +void pybind_utility_declarations(py::module &m); +void pybind_eigen_declarations(py::module &m); +void pybind_logging_declarations(py::module &m); +void pybind_utility_definitions(py::module &m); +void pybind_eigen_definitions(py::module &m); +void pybind_logging_definitions(py::module &m); namespace random { void pybind_random(py::module &m); diff --git a/cpp/pybind/visualization/app/viewer.cpp b/cpp/pybind/visualization/app/viewer.cpp index 524d154d45e..f6be9c4d158 100644 --- a/cpp/pybind/visualization/app/viewer.cpp +++ b/cpp/pybind/visualization/app/viewer.cpp @@ -14,8 +14,13 @@ namespace open3d { namespace visualization { namespace app { -static void pybind_app_functions(py::module &m) { - m.def( +void pybind_app_declarations(py::module &m) { + py::module m_app = m.def_submodule( + "app", "Functionality for running the open3d viewer."); +} +void pybind_app_definitions(py::module &m) { + auto m_app = static_cast(m.attr("app")); + m_app.def( "run_viewer", [](const std::vector &args) { const char **argv = new const char *[args.size()]; @@ -26,21 +31,14 @@ static void pybind_app_functions(py::module &m) { delete[] argv; }, "args"_a); - docstring::FunctionDocInject( - m, "run_viewer", + m_app, "run_viewer", {{"args", "List of arguments containing the path of the calling program " "(which should be in the same directory as the gui resources " "folder) and the optional path of the geometry to visualize."}}); } -void pybind_app(py::module &m) { - py::module m_submodule = m.def_submodule( - "app", "Functionality for running the open3d viewer."); - pybind_app_functions(m_submodule); -} - } // namespace app } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/app/viewer.h b/cpp/pybind/visualization/app/viewer.h index 8c62fdc71a0..b39310b6081 100644 --- a/cpp/pybind/visualization/app/viewer.h +++ b/cpp/pybind/visualization/app/viewer.h @@ -13,7 +13,8 @@ namespace open3d { namespace visualization { namespace app { -void pybind_app(py::module &m); +void pybind_app_declarations(py::module &m); +void pybind_app_definitions(py::module &m); } // namespace app } // namespace visualization diff --git a/cpp/pybind/visualization/gui/events.cpp b/cpp/pybind/visualization/gui/events.cpp index d35d1f7bf6d..f6a3876e8ee 100644 --- a/cpp/pybind/visualization/gui/events.cpp +++ b/cpp/pybind/visualization/gui/events.cpp @@ -15,9 +15,9 @@ namespace open3d { namespace visualization { namespace gui { -void pybind_gui_events(py::module& m) { - py::enum_ buttons(m, "MouseButton", "Mouse button identifiers", - py::arithmetic()); +void pybind_gui_events_declarations(py::module& m_gui) { + py::enum_ buttons( + m_gui, "MouseButton", "Mouse button identifiers", py::arithmetic()); buttons.value("NONE", MouseButton::NONE) .value("LEFT", MouseButton::LEFT) .value("MIDDLE", MouseButton::MIDDLE) @@ -25,17 +25,15 @@ void pybind_gui_events(py::module& m) { .value("BUTTON4", MouseButton::BUTTON4) .value("BUTTON5", MouseButton::BUTTON5) .export_values(); - - py::enum_ key_mod(m, "KeyModifier", "Key modifier identifiers", - py::arithmetic()); + py::enum_ key_mod( + m_gui, "KeyModifier", "Key modifier identifiers", py::arithmetic()); key_mod.value("NONE", KeyModifier::NONE) .value("SHIFT", KeyModifier::SHIFT) .value("CTRL", KeyModifier::CTRL) .value("ALT", KeyModifier::ALT) .value("META", KeyModifier::META) .export_values(); - - py::class_ mouse_event(m, "MouseEvent", + py::class_ mouse_event(m_gui, "MouseEvent", "Object that stores mouse events"); py::enum_ mouse_event_type(mouse_event, "Type", py::arithmetic()); @@ -45,115 +43,7 @@ void pybind_gui_events(py::module& m) { .value("BUTTON_UP", MouseEvent::Type::BUTTON_UP) .value("WHEEL", MouseEvent::Type::WHEEL) .export_values(); - mouse_event.def_readwrite("type", &MouseEvent::type, "Mouse event type") - .def_readwrite("x", &MouseEvent::x, - "x coordinate of the mouse event") - .def_readwrite("y", &MouseEvent::y, - "y coordinate of the mouse event") - .def( - "is_modifier_down", - [](const MouseEvent& e, KeyModifier mod) { - return ((e.modifiers & int(mod)) != 0); - }, - "Convenience function to more easily deterimine if a " - "modifier " - "key is down") - .def( - "is_button_down", - [](const MouseEvent& e, MouseButton b) { - if (e.type == MouseEvent::Type::WHEEL) { - return false; - } else if (e.type == MouseEvent::Type::BUTTON_DOWN) { - return (e.button.button == b); - } else { - return ((e.move.buttons & int(b)) != 0); - } - }, - "Convenience function to more easily deterimine if a mouse " - "button is pressed") - .def_readwrite("modifiers", &MouseEvent::modifiers, - "ORed mouse modifiers") - .def_property( - "buttons", - [](const MouseEvent& e) -> int { - if (e.type == MouseEvent::Type::WHEEL) { - return int(MouseButton::NONE); - } else if (e.type == MouseEvent::Type::BUTTON_DOWN) { - return int(e.button.button); - } else { - return e.move.buttons; - } - }, - [](MouseEvent& e, int new_value) { - if (e.type == MouseEvent::Type::WHEEL) { - ; // no button value for wheel events - } else if (e.type == MouseEvent::Type::BUTTON_DOWN) { - e.button.button = MouseButton(new_value); - } else { - e.move.buttons = new_value; - } - }, - "ORed mouse buttons") - .def_property( - "wheel_dx", - [](const MouseEvent& e) -> int { - if (e.type == MouseEvent::Type::WHEEL) { - return e.wheel.dx; - } else { - return 0; - } - }, - [](MouseEvent& e, int new_value) { - if (e.type == MouseEvent::Type::WHEEL) { - e.wheel.dx = new_value; - } else { - utility::LogWarning( - "Cannot set MouseEvent.wheel_dx unless " - "MouseEvent.type == MouseEvent.Type.WHEEL"); - } - }, - "Mouse wheel horizontal motion") - .def_property( - "wheel_dy", - [](const MouseEvent& e) -> int { - if (e.type == MouseEvent::Type::WHEEL) { - return e.wheel.dy; - } else { - return 0; - } - }, - [](MouseEvent& e, int new_value) { - if (e.type == MouseEvent::Type::WHEEL) { - e.wheel.dy = new_value; - } else { - utility::LogWarning( - "Cannot set MouseEvent.wheel_dy unless " - "MouseEvent.type == MouseEvent.Type.WHEEL"); - } - }, - "Mouse wheel vertical motion") - .def_property( - "wheel_is_trackpad", - [](const MouseEvent& e) -> bool { - if (e.type == MouseEvent::Type::WHEEL) { - return e.wheel.isTrackpad; - } else { - return false; - } - }, - [](MouseEvent& e, bool new_value) { - if (e.type == MouseEvent::Type::WHEEL) { - e.wheel.isTrackpad = new_value; - } else { - utility::LogWarning( - "Cannot set MouseEvent.wheel_is_trackpad " - "unless MouseEvent.type == " - "MouseEvent.Type.WHEEL"); - } - }, - "Is mouse wheel event from a trackpad"); - - py::enum_ key_name(m, "KeyName", + py::enum_ key_name(m_gui, "KeyName", "Names of keys. Used by KeyEvent.key"); key_name.value("NONE", KeyName::KEY_NONE) .value("BACKSPACE", KeyName::KEY_BACKSPACE) @@ -260,14 +150,125 @@ void pybind_gui_events(py::module& m) { .value("F12", KeyName::KEY_F12) .value("UNKNOWN", KeyName::KEY_UNKNOWN) .export_values(); - - py::class_ key_event(m, "KeyEvent", + py::class_ key_event(m_gui, "KeyEvent", "Object that stores key events"); py::enum_ key_event_type(key_event, "Type", py::arithmetic()); key_event_type.value("DOWN", KeyEvent::Type::DOWN) .value("UP", KeyEvent::Type::UP) .export_values(); +} +void pybind_gui_events_definitions(py::module& m_gui) { + auto mouse_event = + static_cast>(m_gui.attr("MouseEvent")); + mouse_event.def_readwrite("type", &MouseEvent::type, "Mouse event type") + .def_readwrite("x", &MouseEvent::x, + "x coordinate of the mouse event") + .def_readwrite("y", &MouseEvent::y, + "y coordinate of the mouse event") + .def( + "is_modifier_down", + [](const MouseEvent& e, KeyModifier mod) { + return ((e.modifiers & int(mod)) != 0); + }, + "Convenience function to more easily deterimine if a " + "modifier " + "key is down") + .def( + "is_button_down", + [](const MouseEvent& e, MouseButton b) { + if (e.type == MouseEvent::Type::WHEEL) { + return false; + } else if (e.type == MouseEvent::Type::BUTTON_DOWN) { + return (e.button.button == b); + } else { + return ((e.move.buttons & int(b)) != 0); + } + }, + "Convenience function to more easily deterimine if a mouse " + "button is pressed") + .def_readwrite("modifiers", &MouseEvent::modifiers, + "ORed mouse modifiers") + .def_property( + "buttons", + [](const MouseEvent& e) -> int { + if (e.type == MouseEvent::Type::WHEEL) { + return int(MouseButton::NONE); + } else if (e.type == MouseEvent::Type::BUTTON_DOWN) { + return int(e.button.button); + } else { + return e.move.buttons; + } + }, + [](MouseEvent& e, int new_value) { + if (e.type == MouseEvent::Type::WHEEL) { + ; // no button value for wheel events + } else if (e.type == MouseEvent::Type::BUTTON_DOWN) { + e.button.button = MouseButton(new_value); + } else { + e.move.buttons = new_value; + } + }, + "ORed mouse buttons") + .def_property( + "wheel_dx", + [](const MouseEvent& e) -> int { + if (e.type == MouseEvent::Type::WHEEL) { + return e.wheel.dx; + } else { + return 0; + } + }, + [](MouseEvent& e, int new_value) { + if (e.type == MouseEvent::Type::WHEEL) { + e.wheel.dx = new_value; + } else { + utility::LogWarning( + "Cannot set MouseEvent.wheel_dx unless " + "MouseEvent.type == MouseEvent.Type.WHEEL"); + } + }, + "Mouse wheel horizontal motion") + .def_property( + "wheel_dy", + [](const MouseEvent& e) -> int { + if (e.type == MouseEvent::Type::WHEEL) { + return e.wheel.dy; + } else { + return 0; + } + }, + [](MouseEvent& e, int new_value) { + if (e.type == MouseEvent::Type::WHEEL) { + e.wheel.dy = new_value; + } else { + utility::LogWarning( + "Cannot set MouseEvent.wheel_dy unless " + "MouseEvent.type == MouseEvent.Type.WHEEL"); + } + }, + "Mouse wheel vertical motion") + .def_property( + "wheel_is_trackpad", + [](const MouseEvent& e) -> bool { + if (e.type == MouseEvent::Type::WHEEL) { + return e.wheel.isTrackpad; + } else { + return false; + } + }, + [](MouseEvent& e, bool new_value) { + if (e.type == MouseEvent::Type::WHEEL) { + e.wheel.isTrackpad = new_value; + } else { + utility::LogWarning( + "Cannot set MouseEvent.wheel_is_trackpad " + "unless MouseEvent.type == " + "MouseEvent.Type.WHEEL"); + } + }, + "Is mouse wheel event from a trackpad"); + auto key_event = static_cast>(m_gui.attr("KeyEvent")); key_event.def_readwrite("type", &KeyEvent::type, "Key event type") .def_readwrite("key", &KeyEvent::key, "This is the actual key that was pressed, not the " diff --git a/cpp/pybind/visualization/gui/gui.cpp b/cpp/pybind/visualization/gui/gui.cpp index 9f086e50ef5..708d2c14e2b 100644 --- a/cpp/pybind/visualization/gui/gui.cpp +++ b/cpp/pybind/visualization/gui/gui.cpp @@ -173,17 +173,366 @@ std::shared_ptr RenderToDepthImageWithoutWindow( enum class EventCallbackResult { IGNORED = 0, HANDLED, CONSUMED }; -void pybind_gui_classes(py::module &m) { - // ---- FontStyle ---- - py::enum_ font_style(m, "FontStyle", "Font style"); +class PyImageWidget : public ImageWidget { + using Super = ImageWidget; + +public: + PyImageWidget() : Super() {} + /// Uses image from the specified path. Each ImageWidget will use one + /// draw call. + explicit PyImageWidget(const char *image_path) : Super(image_path) {} + /// Uses existing image. Each ImageWidget will use one draw call. + explicit PyImageWidget(std::shared_ptr image) + : Super(image) {} + /// Uses existing image. Each ImageWidget will use one draw call. + explicit PyImageWidget(std::shared_ptr image) + : Super(image) {} + /// Uses an existing texture, using texture coordinates + /// (u0, v0) to (u1, v1). Does not deallocate texture on destruction. + /// This is useful for using an icon atlas to reduce draw calls. + explicit PyImageWidget( + open3d::visualization::rendering::TextureHandle texture_id, + float u0 = 0.0f, + float v0 = 0.0f, + float u1 = 1.0f, + float v1 = 1.0f) + : Super(texture_id, u0, v0, u1, v1) {} + + ~PyImageWidget() = default; + + void SetOnMouse(std::function f) { on_mouse_ = f; } + void SetOnKey(std::function f) { on_key_ = f; } + + Widget::EventResult Mouse(const MouseEvent &e) override { + if (on_mouse_) { + switch (EventCallbackResult(on_mouse_(e))) { + case EventCallbackResult::CONSUMED: + return Widget::EventResult::CONSUMED; + case EventCallbackResult::HANDLED: { + auto result = Super::Mouse(e); + if (result == Widget::EventResult::IGNORED) { + result = Widget::EventResult::CONSUMED; + } + return result; + } + case EventCallbackResult::IGNORED: + default: + return Super::Mouse(e); + } + } else { + return Super::Mouse(e); + } + } + + Widget::EventResult Key(const KeyEvent &e) override { + if (on_key_) { + switch (EventCallbackResult(on_key_(e))) { + case EventCallbackResult::CONSUMED: + return Widget::EventResult::CONSUMED; + case EventCallbackResult::HANDLED: { + auto result = Super::Key(e); + if (result == Widget::EventResult::IGNORED) { + result = Widget::EventResult::CONSUMED; + } + return result; + } + case EventCallbackResult::IGNORED: + default: + return Super::Key(e); + } + } else { + return Super::Key(e); + } + } + +private: + std::function on_mouse_; + std::function on_key_; +}; + +class PySceneWidget : public SceneWidget { + using Super = SceneWidget; + +public: + void SetOnMouse(std::function f) { on_mouse_ = f; } + void SetOnKey(std::function f) { on_key_ = f; } + + Widget::EventResult Mouse(const MouseEvent &e) override { + if (on_mouse_) { + switch (EventCallbackResult(on_mouse_(e))) { + case EventCallbackResult::CONSUMED: + return Widget::EventResult::CONSUMED; + case EventCallbackResult::HANDLED: { + auto result = Super::Mouse(e); + if (result == Widget::EventResult::IGNORED) { + result = Widget::EventResult::CONSUMED; + } + return result; + } + case EventCallbackResult::IGNORED: + default: + return Super::Mouse(e); + } + } else { + return Super::Mouse(e); + } + } + + Widget::EventResult Key(const KeyEvent &e) override { + if (on_key_) { + switch (EventCallbackResult(on_key_(e))) { + case EventCallbackResult::CONSUMED: + return Widget::EventResult::CONSUMED; + case EventCallbackResult::HANDLED: { + auto result = Super::Key(e); + if (result == Widget::EventResult::IGNORED) { + result = Widget::EventResult::CONSUMED; + } + return result; + } + case EventCallbackResult::IGNORED: + default: + return Super::Key(e); + } + } else { + return Super::Key(e); + } + } + +private: + std::function on_mouse_; + std::function on_key_; +}; + +void pybind_gui_declarations(py::module &m) { + py::module m_gui = m.def_submodule("gui"); + pybind_gui_events_declarations(m_gui); + py::enum_ font_style(m_gui, "FontStyle", "Font style"); font_style.value("NORMAL", FontStyle::NORMAL) .value("BOLD", FontStyle::BOLD) .value("ITALIC", FontStyle::ITALIC) .value("BOLD_ITALIC", FontStyle::BOLD_ITALIC); - - // ---- FontDescription ---- - py::class_ fd(m, "FontDescription", + py::class_ fd(m_gui, "FontDescription", "Class to describe a custom font"); + py::class_ application(m_gui, "Application", + "Global application singleton. This " + "owns the menubar, windows, and event " + "loop"); + py::class_ lc( + m_gui, "LayoutContext", + "Context passed to Window's on_layout callback"); + // Pybind appears to need to know about the base class. It doesn't have + // to be named the same as the C++ class, though. The holder object cannot + // be a shared_ptr or we can crash (see comment for UnownedPointer). + py::class_> window_base( + m_gui, "WindowBase", "Application window"); + py::class_, Window> window( + m_gui, "Window", + "Application window. Create with " + "Application.instance.create_window()."); + py::class_> menu(m_gui, "Menu", + "A menu, possibly a menu tree"); + py::class_ color(m_gui, "Color", "Stores color for gui classes"); + py::class_ theme(m_gui, "Theme", + "Theme parameters such as colors used for drawing " + "widgets (read-only)"); + py::class_ rect(m_gui, "Rect", "Represents a widget frame"); + py::class_ size(m_gui, "Size", "Size object"); + py::class_> widget(m_gui, "Widget", + "Base widget class"); + py::enum_ widget_event_callback_result( + widget, "EventCallbackResult", "Returned by event handlers", + py::arithmetic()); + widget_event_callback_result + .value("IGNORED", EventCallbackResult::IGNORED, + "Event handler ignored the event, widget will " + "handle event normally") + .value("HANDLED", EventCallbackResult::HANDLED, + "Event handler handled the event, but widget " + "will still handle the event normally. This is " + "useful when you are augmenting base " + "functionality") + .value("CONSUMED", EventCallbackResult::CONSUMED, + "Event handler consumed the event, event " + "handling stops, widget will not handle the " + "event. This is useful when you are replacing " + "functionality") + .export_values(); + py::class_ constraints( + widget, "Constraints", + "Constraints object for Widget.calc_preferred_size()"); + py::class_, Widget> widgetProxy( + m_gui, "WidgetProxy", + "Widget container to delegate any widget dynamically." + " Widget can not be managed dynamically. Although it is allowed" + " to add more child widgets, it's impossible to replace some child" + " with new on or remove children. WidgetProxy is designed to solve" + " this problem." + " When WidgetProxy is created, it's invisible and disabled, so it" + " won't be drawn or layout, seeming like it does not exist. When" + " a widget is set by set_widget, all Widget's APIs will be" + " conducted to that child widget. It looks like WidgetProxy is" + " that widget." + " At any time, a new widget could be set, to replace the old one." + " and the old widget will be destroyed." + " Due to the content changing after a new widget is set or cleared," + " a relayout of Window might be called after set_widget." + " The delegated widget could be retrieved by get_widget in case" + " you need to access it directly, like get check status of a" + " CheckBox. API other than set_widget and get_widget has" + " completely same functions as Widget."); + py::class_, WidgetProxy> + widget_stack(m_gui, "WidgetStack", + "A widget stack saves all widgets pushed into by " + "push_widget and always shows the top one. The " + "WidgetStack is a subclass of WidgetProxy, in other" + "words, the topmost widget will delegate itself to " + "WidgetStack. pop_widget will remove the topmost " + "widget and callback set by set_on_top taking the " + "new topmost widget will be called. The WidgetStack " + "disappears in GUI if there is no widget in stack."); + py::class_, Widget> button(m_gui, "Button", + "Button"); + py::class_, Widget> checkbox( + m_gui, "Checkbox", "Checkbox"); + py::class_, Widget> coloredit( + m_gui, "ColorEdit", "Color picker"); + py::class_, Widget> combobox( + m_gui, "Combobox", "Exclusive selection from a pull-down menu"); + py::class_, Widget> radiobtn( + m_gui, "RadioButton", "Exclusive selection from radio button list"); + py::enum_ radiobtn_type(radiobtn, "Type", + py::arithmetic()); + // Trick to write docs without listing the members in the enum class again. + radiobtn_type.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for RadioButton types."; + }), + py::none(), py::none(), ""); + radiobtn_type.value("VERT", RadioButton::Type::VERT) + .value("HORIZ", RadioButton::Type::HORIZ) + .export_values(); + py::class_> uiimage( + m_gui, "UIImage", + "A bitmap suitable for displaying with ImageWidget"); + py::enum_ uiimage_scaling(uiimage, "Scaling", + py::arithmetic()); + uiimage_scaling.value("NONE", UIImage::Scaling::NONE) + .value("ANY", UIImage::Scaling::ANY) + .value("ASPECT", UIImage::Scaling::ASPECT); + py::class_, Widget> + imagewidget(m_gui, "ImageWidget", "Displays a bitmap"); + py::class_, Widget> label(m_gui, "Label", + "Displays text"); + py::class_> label3d( + m_gui, "Label3D", "Displays text in a 3D scene"); + py::class_, Widget> listview( + m_gui, "ListView", "Displays a list of text"); + py::class_, Widget> numedit( + m_gui, "NumberEdit", "Allows the user to enter a number."); + py::enum_ numedit_type(numedit, "Type", py::arithmetic()); + // Trick to write docs without listing the members in the enum class again. + numedit_type.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for NumberEdit types."; + }), + py::none(), py::none(), ""); + numedit_type.value("INT", NumberEdit::Type::INT) + .value("DOUBLE", NumberEdit::Type::DOUBLE) + .export_values(); + py::class_, Widget> progress( + m_gui, "ProgressBar", "Displays a progress bar"); + py::class_, Widget> scene( + m_gui, "SceneWidget", "Displays 3D content"); + py::enum_ scene_ctrl(scene, "Controls", + py::arithmetic()); + // Trick to write docs without listing the members in the enum class again. + scene_ctrl.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class describing mouse interaction."; + }), + py::none(), py::none(), ""); + scene_ctrl.value("ROTATE_CAMERA", SceneWidget::Controls::ROTATE_CAMERA) + .value("ROTATE_CAMERA_SPHERE", + SceneWidget::Controls::ROTATE_CAMERA_SPHERE) + .value("FLY", SceneWidget::Controls::FLY) + .value("ROTATE_SUN", SceneWidget::Controls::ROTATE_SUN) + .value("ROTATE_IBL", SceneWidget::Controls::ROTATE_IBL) + .value("ROTATE_MODEL", SceneWidget::Controls::ROTATE_MODEL) + .value("PICK_POINTS", SceneWidget::Controls::PICK_POINTS) + .export_values(); + py::class_, Widget> slider( + m_gui, "Slider", "A slider widget for visually selecting numbers"); + py::enum_ slider_type(slider, "Type", py::arithmetic()); + // Trick to write docs without listing the members in the enum class again. + slider_type.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for Slider types."; + }), + py::none(), py::none(), ""); + slider_type.value("INT", Slider::Type::INT) + .value("DOUBLE", Slider::Type::DOUBLE) + .export_values(); + py::class_, Widget> stacked( + m_gui, "StackedWidget", "Like a TabControl but without the tabs"); + py::class_, Widget> tabctrl( + m_gui, "TabControl", "Tab control"); + py::class_, Widget> textedit( + m_gui, "TextEdit", "Allows the user to enter or modify text"); + py::class_, Widget> toggle( + m_gui, "ToggleSwitch", "ToggleSwitch"); + py::class_, Widget> treeview( + m_gui, "TreeView", "Hierarchical list"); + py::class_, + Widget> + checkable_cell(m_gui, "CheckableTextTreeCell", + "TreeView cell with a checkbox and text"); + py::class_, Widget> lut_cell( + m_gui, "LUTTreeCell", + "TreeView cell with checkbox, text, and color edit"); + py::class_, Widget> + colormap_cell(m_gui, "ColormapTreeCell", + "TreeView cell with a number edit and color edit"); + py::class_, Widget> vectoredit( + m_gui, "VectorEdit", "Allows the user to edit a 3-space vector"); + py::class_> margins(m_gui, "Margins", + "Margins for layouts"); + py::class_, Widget> layout1d( + m_gui, "Layout1D", "Layout base class"); + py::class_, Layout1D> vlayout(m_gui, "Vert", + "Vertical layout"); + py::class_, Vert> + collapsable(m_gui, "CollapsableVert", + "Vertical layout with title, whose contents are " + "collapsable"); + py::class_, Vert> slayout( + m_gui, "ScrollableVert", "Scrollable vertical layout"); + py::class_, Layout1D> hlayout( + m_gui, "Horiz", "Horizontal layout"); + py::class_, Widget> vgrid(m_gui, "VGrid", + "Grid layout"); + py::class_, Widget> dialog(m_gui, "Dialog", + "Dialog"); + py::class_, Dialog> filedlg( + m_gui, "FileDialog", "File picker dialog"); + py::enum_ filedlg_mode(filedlg, "Mode", py::arithmetic()); + // Trick to write docs without listing the members in the enum class again. + filedlg_mode.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for FileDialog modes."; + }), + py::none(), py::none(), ""); + filedlg_mode.value("OPEN", FileDialog::Mode::OPEN) + .value("SAVE", FileDialog::Mode::SAVE) + .value("OPEN_DIR", FileDialog::Mode::OPEN_DIR) + .export_values(); +} +void pybind_gui_definitions(py::module &m) { + auto m_gui = static_cast(m.attr("gui")); + pybind_gui_events_definitions(m_gui); + // ---- FontDescription ---- + auto fd = static_cast>( + m_gui.attr("FontDescription")); fd.attr("SANS_SERIF") = FontDescription::SANS_SERIF; fd.attr("MONOSPACE") = FontDescription::MONOSPACE; fd.def(py::init(), @@ -222,10 +571,8 @@ void pybind_gui_classes(py::module &m) { "font."); // ---- Application ---- - py::class_ application(m, "Application", - "Global application singleton. This " - "owns the menubar, windows, and event " - "loop"); + auto application = + static_cast>(m_gui.attr("Application")); application.attr("DEFAULT_FONT_ID") = Application::DEFAULT_FONT_ID; application .def("__repr__", @@ -376,9 +723,8 @@ void pybind_gui_classes(py::module &m) { "resources directory"); // ---- LayoutContext ---- - py::class_ lc( - m, "LayoutContext", - "Context passed to Window's on_layout callback"); + auto lc = + static_cast>(m_gui.attr("LayoutContext")); // lc.def_readonly("theme", &LayoutContext::theme); // Pybind can't return a reference (since Theme is a unique_ptr), so // return a copy instead. @@ -388,15 +734,9 @@ void pybind_gui_classes(py::module &m) { }); // ---- Window ---- - // Pybind appears to need to know about the base class. It doesn't have - // to be named the same as the C++ class, though. The holder object cannot - // be a shared_ptr or we can crash (see comment for UnownedPointer). - py::class_> window_base( - m, "WindowBase", "Application window"); - py::class_, Window> window( - m, "Window", - "Application window. Create with " - "Application.instance.create_window()."); + auto window = + static_cast, Window>>( + m_gui.attr("Window")); window.def("__repr__", [](const PyWindow &w) { return "Application window"; }) .def( @@ -489,8 +829,8 @@ void pybind_gui_classes(py::module &m) { "Gets the rendering.Renderer object for the Window"); // ---- Menu ---- - py::class_> menu(m, "Menu", - "A menu, possibly a menu tree"); + auto menu = static_cast>>( + m_gui.attr("Menu")); menu.def(py::init<>()) .def( "add_item", @@ -526,7 +866,7 @@ void pybind_gui_classes(py::module &m) { "Sets menu item (un)checked"); // ---- Color ---- - py::class_ color(m, "Color", "Stores color for gui classes"); + auto color = static_cast>(m_gui.attr("Color")); color.def(py::init([](float r, float g, float b, float a) { return new Color(r, g, b, a); }), @@ -554,9 +894,7 @@ void pybind_gui_classes(py::module &m) { // ---- Theme ---- // Note: no constructor because themes are created by Open3D - py::class_ theme(m, "Theme", - "Theme parameters such as colors used for drawing " - "widgets (read-only)"); + auto theme = static_cast>(m_gui.attr("Theme")); theme.def_readonly("font_size", &Theme::font_size, "Font size (which is also the conventional size of the " "em unit) (read-only)") @@ -569,7 +907,7 @@ void pybind_gui_classes(py::module &m) { "(read-only)"); // ---- Rect ---- - py::class_ rect(m, "Rect", "Represents a widget frame"); + auto rect = static_cast>(m_gui.attr("Rect")); rect.def(py::init<>()) .def(py::init()) .def(py::init([](float x, float y, float w, float h) { @@ -593,7 +931,7 @@ void pybind_gui_classes(py::module &m) { .def("get_bottom", &Rect::GetBottom); // ---- Size ---- - py::class_ size(m, "Size", "Size object"); + auto size = static_cast>(m_gui.attr("Size")); size.def(py::init<>()) .def(py::init()) .def(py::init([](float w, float h) { @@ -617,34 +955,13 @@ void pybind_gui_classes(py::module &m) { // 1) adding an object to multiple objects will cause multiple shared_ptrs // to think they own it, leading to a double-free and crash, and // 2) if the object is never added, the memory will be leaked. - py::class_> widget(m, "Widget", - "Base widget class"); - py::enum_ widget_event_callback_result( - widget, "EventCallbackResult", "Returned by event handlers", - py::arithmetic()); - widget_event_callback_result - .value("IGNORED", EventCallbackResult::IGNORED, - "Event handler ignored the event, widget will " - "handle event normally") - .value("HANDLED", EventCallbackResult::HANDLED, - "Event handler handled the event, but widget " - "will still handle the event normally. This is " - "useful when you are augmenting base " - "functionality") - .value("CONSUMED", EventCallbackResult::CONSUMED, - "Event handler consumed the event, event " - "handling stops, widget will not handle the " - "event. This is useful when you are replacing " - "functionality") - .export_values(); - - py::class_ constraints( - widget, "Constraints", - "Constraints object for Widget.calc_preferred_size()"); + auto widget = static_cast>>( + m_gui.attr("Widget")); + auto constraints = static_cast>( + widget.attr("Constraints")); constraints.def(py::init<>()) .def_readwrite("width", &Widget::Constraints::width) .def_readwrite("height", &Widget::Constraints::height); - widget.def(py::init<>()) .def("__repr__", [](const Widget &w) { @@ -682,26 +999,9 @@ void pybind_gui_classes(py::module &m) { "properly"); // ---- WidgetProxy ---- - py::class_, Widget> widgetProxy( - m, "WidgetProxy", - "Widget container to delegate any widget dynamically." - " Widget can not be managed dynamically. Although it is allowed" - " to add more child widgets, it's impossible to replace some child" - " with new on or remove children. WidgetProxy is designed to solve" - " this problem." - " When WidgetProxy is created, it's invisible and disabled, so it" - " won't be drawn or layout, seeming like it does not exist. When" - " a widget is set by set_widget, all Widget's APIs will be" - " conducted to that child widget. It looks like WidgetProxy is" - " that widget." - " At any time, a new widget could be set, to replace the old one." - " and the old widget will be destroyed." - " Due to the content changing after a new widget is set or cleared," - " a relayout of Window might be called after set_widget." - " The delegated widget could be retrieved by get_widget in case" - " you need to access it directly, like get check status of a" - " CheckBox. API other than set_widget and get_widget has" - " completely same functions as Widget."); + auto widgetProxy = static_cast< + py::class_, Widget>>( + m_gui.attr("WidgetProxy")); widgetProxy.def(py::init<>(), "Creates a widget proxy") .def("__repr__", [](const WidgetProxy &c) { @@ -730,17 +1030,10 @@ void pybind_gui_classes(py::module &m) { "if there is none."); // ---- WidgetStack ---- - py::class_, WidgetProxy> - widgetStack(m, "WidgetStack", - "A widget stack saves all widgets pushed into by " - "push_widget and always shows the top one. The " - "WidgetStack is a subclass of WidgetProxy, in other" - "words, the topmost widget will delegate itself to " - "WidgetStack. pop_widget will remove the topmost " - "widget and callback set by set_on_top taking the " - "new topmost widget will be called. The WidgetStack " - "disappears in GUI if there is no widget in stack."); - widgetStack + auto widget_stack = static_cast< + py::class_, WidgetProxy>>( + m_gui.attr("WidgetStack")); + widget_stack .def(py::init<>(), "Creates a widget stack. The widget stack without any" "widget will not be shown in GUI until set_widget is" @@ -766,8 +1059,9 @@ void pybind_gui_classes(py::module &m) { "out. It won't be called if a widget is pushed into stack" "by set_widget."); // ---- Button ---- - py::class_, Widget> button(m, "Button", - "Button"); + auto button = + static_cast, Widget>>( + m_gui.attr("Button")); button.def(py::init(), "Creates a button with the given text") .def("__repr__", [](const Button &b) { @@ -823,8 +1117,9 @@ void pybind_gui_classes(py::module &m) { "Calls passed function when button is pressed"); // ---- Checkbox ---- - py::class_, Widget> checkbox( - m, "Checkbox", "Checkbox"); + auto checkbox = + static_cast, Widget>>( + m_gui.attr("Checkbox")); checkbox.def(py::init(), "Creates a checkbox with the given text") .def("__repr__", @@ -842,8 +1137,9 @@ void pybind_gui_classes(py::module &m) { "Calls passed function when checkbox changes state"); // ---- ColorEdit ---- - py::class_, Widget> coloredit( - m, "ColorEdit", "Color picker"); + auto coloredit = static_cast< + py::class_, Widget>>( + m_gui.attr("ColorEdit")); coloredit.def(py::init<>()) .def("__repr__", [](const ColorEdit &c) { @@ -864,8 +1160,9 @@ void pybind_gui_classes(py::module &m) { "Calls f(Color) when color changes by user input"); // ---- Combobox ---- - py::class_, Widget> combobox( - m, "Combobox", "Exclusive selection from a pull-down menu"); + auto combobox = + static_cast, Widget>>( + m_gui.attr("Combobox")); combobox.def(py::init<>(), "Creates an empty combobox. Use add_item() to add items") .def("clear_items", &Combobox::ClearItems, "Removes all items") @@ -902,19 +1199,9 @@ void pybind_gui_classes(py::module &m) { "respectively"); // ---- RadioButton ---- - py::class_, Widget> radiobtn( - m, "RadioButton", "Exclusive selection from radio button list"); - py::enum_ radiobtn_type(radiobtn, "Type", - py::arithmetic()); - // Trick to write docs without listing the members in the enum class again. - radiobtn_type.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for RadioButton types."; - }), - py::none(), py::none(), ""); - radiobtn_type.value("VERT", RadioButton::Type::VERT) - .value("HORIZ", RadioButton::Type::HORIZ) - .export_values(); + auto radiobtn = static_cast< + py::class_, Widget>>( + m_gui.attr("RadioButton")); radiobtn.def(py::init(), "Creates an empty radio buttons. Use set_items() to add items") .def("set_items", &RadioButton::SetItems, @@ -930,95 +1217,8 @@ void pybind_gui_classes(py::module &m) { "Calls f(new_idx) when user changes selection"); // ---- ImageWidget ---- - class PyImageWidget : public ImageWidget { - using Super = ImageWidget; - - public: - PyImageWidget() : Super() {} - /// Uses image from the specified path. Each ImageWidget will use one - /// draw call. - explicit PyImageWidget(const char *image_path) : Super(image_path) {} - /// Uses existing image. Each ImageWidget will use one draw call. - explicit PyImageWidget(std::shared_ptr image) - : Super(image) {} - /// Uses existing image. Each ImageWidget will use one draw call. - explicit PyImageWidget( - std::shared_ptr image) - : Super(image) {} - /// Uses an existing texture, using texture coordinates - /// (u0, v0) to (u1, v1). Does not deallocate texture on destruction. - /// This is useful for using an icon atlas to reduce draw calls. - explicit PyImageWidget( - open3d::visualization::rendering::TextureHandle texture_id, - float u0 = 0.0f, - float v0 = 0.0f, - float u1 = 1.0f, - float v1 = 1.0f) - : Super(texture_id, u0, v0, u1, v1) {} - - ~PyImageWidget() = default; - - void SetOnMouse(std::function f) { - on_mouse_ = f; - } - void SetOnKey(std::function f) { on_key_ = f; } - - Widget::EventResult Mouse(const MouseEvent &e) override { - if (on_mouse_) { - switch (EventCallbackResult(on_mouse_(e))) { - case EventCallbackResult::CONSUMED: - return Widget::EventResult::CONSUMED; - case EventCallbackResult::HANDLED: { - auto result = Super::Mouse(e); - if (result == Widget::EventResult::IGNORED) { - result = Widget::EventResult::CONSUMED; - } - return result; - } - case EventCallbackResult::IGNORED: - default: - return Super::Mouse(e); - } - } else { - return Super::Mouse(e); - } - } - - Widget::EventResult Key(const KeyEvent &e) override { - if (on_key_) { - switch (EventCallbackResult(on_key_(e))) { - case EventCallbackResult::CONSUMED: - return Widget::EventResult::CONSUMED; - case EventCallbackResult::HANDLED: { - auto result = Super::Key(e); - if (result == Widget::EventResult::IGNORED) { - result = Widget::EventResult::CONSUMED; - } - return result; - } - case EventCallbackResult::IGNORED: - default: - return Super::Key(e); - } - } else { - return Super::Key(e); - } - } - - private: - std::function on_mouse_; - std::function on_key_; - }; - - py::class_> uiimage( - m, "UIImage", "A bitmap suitable for displaying with ImageWidget"); - - py::enum_ uiimage_scaling(uiimage, "Scaling", - py::arithmetic()); - uiimage_scaling.value("NONE", UIImage::Scaling::NONE) - .value("ANY", UIImage::Scaling::ANY) - .value("ASPECT", UIImage::Scaling::ASPECT); - + auto uiimage = static_cast>>( + m_gui.attr("UIImage")); uiimage.def(py::init<>([](const char *path) { return new UIImage(path); }), "Creates a UIImage from the image at the specified path") .def(py::init<>([](std::shared_ptr image) { @@ -1032,9 +1232,9 @@ void pybind_gui_classes(py::module &m) { "gui.UIImage.Scaling.ANY: scaled to fit\n" "gui.UIImage.Scaling.ASPECT: scaled to fit but " "keeping the image's aspect ratio"); - - py::class_, Widget> - imagewidget(m, "ImageWidget", "Displays a bitmap"); + auto imagewidget = static_cast< + py::class_, Widget>>( + m_gui.attr("ImageWidget")); imagewidget .def(py::init<>([]() { return new PyImageWidget(); }), "Creates an ImageWidget with no image") @@ -1099,8 +1299,8 @@ void pybind_gui_classes(py::module &m) { "as you will exhaust internal texture resources."); // ---- Label ---- - py::class_, Widget> label(m, "Label", - "Displays text"); + auto label = static_cast, Widget>>( + m_gui.attr("Label")); label.def(py::init([](const char *title = "") { return new Label(title); }), "Creates a Label with the given text") .def("__repr__", @@ -1123,8 +1323,8 @@ void pybind_gui_classes(py::module &m) { "Application.add_font()"); // ---- Label3D ---- - py::class_> label3d( - m, "Label3D", "Displays text in a 3D scene"); + auto label3d = static_cast>>( + m_gui.attr("Label3D")); label3d.def(py::init([](const char *text = "", const Eigen::Vector3f &pos = {0.f, 0.f, 0.f}) { return new Label3D(pos, text); @@ -1147,8 +1347,9 @@ void pybind_gui_classes(py::module &m) { "blurry text as the underlying font is not resized."); // ---- ListView ---- - py::class_, Widget> listview( - m, "ListView", "Displays a list of text"); + auto listview = + static_cast, Widget>>( + m_gui.attr("ListView")); listview.def(py::init<>(), "Creates an empty list") .def("__repr__", [](const ListView &lv) { @@ -1176,19 +1377,9 @@ void pybind_gui_classes(py::module &m) { "selection"); // ---- NumberEdit ---- - py::class_, Widget> numedit( - m, "NumberEdit", "Allows the user to enter a number."); - py::enum_ numedit_type(numedit, "Type", py::arithmetic()); - // Trick to write docs without listing the members in the enum class again. - numedit_type.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for NumberEdit types."; - }), - py::none(), py::none(), ""); - numedit_type.value("INT", NumberEdit::Type::INT) - .value("DOUBLE", NumberEdit::Type::DOUBLE) - .export_values(); - + auto numedit = static_cast< + py::class_, Widget>>( + m_gui.attr("NumberEdit")); numedit.def(py::init(), "Creates a NumberEdit that is either integers (INT) or " "floating point (DOUBLE). The initial value is 0 and the " @@ -1237,8 +1428,9 @@ void pybind_gui_classes(py::module &m) { "Sets the preferred width of the NumberEdit"); // ---- ProgressBar---- - py::class_, Widget> progress( - m, "ProgressBar", "Displays a progress bar"); + auto progress = static_cast< + py::class_, Widget>>( + m_gui.attr("ProgressBar")); progress.def(py::init<>()) .def("__repr__", [](const ProgressBar &pb) { @@ -1253,82 +1445,9 @@ void pybind_gui_classes(py::module &m) { "The value of the progress bar, ranges from 0.0 to 1.0"); // ---- SceneWidget ---- - class PySceneWidget : public SceneWidget { - using Super = SceneWidget; - - public: - void SetOnMouse(std::function f) { - on_mouse_ = f; - } - void SetOnKey(std::function f) { on_key_ = f; } - - Widget::EventResult Mouse(const MouseEvent &e) override { - if (on_mouse_) { - switch (EventCallbackResult(on_mouse_(e))) { - case EventCallbackResult::CONSUMED: - return Widget::EventResult::CONSUMED; - case EventCallbackResult::HANDLED: { - auto result = Super::Mouse(e); - if (result == Widget::EventResult::IGNORED) { - result = Widget::EventResult::CONSUMED; - } - return result; - } - case EventCallbackResult::IGNORED: - default: - return Super::Mouse(e); - } - } else { - return Super::Mouse(e); - } - } - - Widget::EventResult Key(const KeyEvent &e) override { - if (on_key_) { - switch (EventCallbackResult(on_key_(e))) { - case EventCallbackResult::CONSUMED: - return Widget::EventResult::CONSUMED; - case EventCallbackResult::HANDLED: { - auto result = Super::Key(e); - if (result == Widget::EventResult::IGNORED) { - result = Widget::EventResult::CONSUMED; - } - return result; - } - case EventCallbackResult::IGNORED: - default: - return Super::Key(e); - } - } else { - return Super::Key(e); - } - } - - private: - std::function on_mouse_; - std::function on_key_; - }; - - py::class_, Widget> scene( - m, "SceneWidget", "Displays 3D content"); - py::enum_ scene_ctrl(scene, "Controls", - py::arithmetic()); - // Trick to write docs without listing the members in the enum class again. - scene_ctrl.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class describing mouse interaction."; - }), - py::none(), py::none(), ""); - scene_ctrl.value("ROTATE_CAMERA", SceneWidget::Controls::ROTATE_CAMERA) - .value("ROTATE_CAMERA_SPHERE", - SceneWidget::Controls::ROTATE_CAMERA_SPHERE) - .value("FLY", SceneWidget::Controls::FLY) - .value("ROTATE_SUN", SceneWidget::Controls::ROTATE_SUN) - .value("ROTATE_IBL", SceneWidget::Controls::ROTATE_IBL) - .value("ROTATE_MODEL", SceneWidget::Controls::ROTATE_MODEL) - .value("PICK_POINTS", SceneWidget::Controls::PICK_POINTS) - .export_values(); - + auto scene = static_cast< + py::class_, Widget>>( + m_gui.attr("SceneWidget")); scene.def(py::init<>(), "Creates an empty SceneWidget. Assign a Scene with the 'scene' " "property") @@ -1396,19 +1515,9 @@ void pybind_gui_classes(py::module &m) { "Removes the 3D text label from the scene"); // ---- Slider ---- - py::class_, Widget> slider( - m, "Slider", "A slider widget for visually selecting numbers"); - py::enum_ slider_type(slider, "Type", py::arithmetic()); - // Trick to write docs without listing the members in the enum class again. - slider_type.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for Slider types."; - }), - py::none(), py::none(), ""); - slider_type.value("INT", Slider::Type::INT) - .value("DOUBLE", Slider::Type::DOUBLE) - .export_values(); - + auto slider = + static_cast, Widget>>( + m_gui.attr("Slider")); slider.def(py::init(), "Creates a NumberEdit that is either integers (INT) or " "floating point (DOUBLE). The initial value is 0 and the limits " @@ -1445,16 +1554,18 @@ void pybind_gui_classes(py::module &m) { "changes widget's value"); // ---- StackedWidget ---- - py::class_, Widget> stacked( - m, "StackedWidget", "Like a TabControl but without the tabs"); + auto stacked = static_cast< + py::class_, Widget>>( + m_gui.attr("StackedWidget")); stacked.def(py::init<>()) .def_property("selected_index", &StackedWidget::GetSelectedIndex, &StackedWidget::SetSelectedIndex, "Selects the index of the child to display"); // ---- TabControl ---- - py::class_, Widget> tabctrl( - m, "TabControl", "Tab control"); + auto tabctrl = static_cast< + py::class_, Widget>>( + m_gui.attr("TabControl")); tabctrl.def(py::init<>()) .def( "add_tab", @@ -1476,8 +1587,9 @@ void pybind_gui_classes(py::module &m) { "different tab"); // ---- TextEdit ---- - py::class_, Widget> textedit( - m, "TextEdit", "Allows the user to enter or modify text"); + auto textedit = + static_cast, Widget>>( + m_gui.attr("TextEdit")); textedit.def(py::init<>(), "Creates a TextEdit widget with an initial value of an empty " "string.") @@ -1504,8 +1616,9 @@ void pybind_gui_classes(py::module &m) { "user completes text editing"); // ---- ToggleSwitch ---- - py::class_, Widget> toggle( - m, "ToggleSwitch", "ToggleSwitch"); + auto toggle = static_cast< + py::class_, Widget>>( + m_gui.attr("ToggleSwitch")); toggle.def(py::init(), "Creates a toggle switch with the given text") .def("__repr__", @@ -1523,8 +1636,9 @@ void pybind_gui_classes(py::module &m) { "state."); // ---- TreeView ---- - py::class_, Widget> treeview( - m, "TreeView", "Hierarchical list"); + auto treeview = + static_cast, Widget>>( + m_gui.attr("TreeView")); treeview.def(py::init<>(), "Creates an empty TreeView widget") .def("__repr__", [](const TreeView &tv) { @@ -1572,10 +1686,10 @@ void pybind_gui_classes(py::module &m) { "changes the selection."); // ---- TreeView cells ---- - py::class_, - Widget> - checkable_cell(m, "CheckableTextTreeCell", - "TreeView cell with a checkbox and text"); + auto checkable_cell = static_cast< + py::class_, Widget>>( + m_gui.attr("CheckableTextTreeCell")); checkable_cell .def(py::init<>([](const char *text, bool checked, std::function on_toggled) { @@ -1592,10 +1706,9 @@ void pybind_gui_classes(py::module &m) { .def_property_readonly("label", &CheckableTextTreeCell::GetLabel, "Returns the label widget " "(property is read-only)"); - - py::class_, Widget> lut_cell( - m, "LUTTreeCell", - "TreeView cell with checkbox, text, and color edit"); + auto lut_cell = static_cast< + py::class_, Widget>>( + m_gui.attr("LUTTreeCell")); lut_cell.def(py::init<>([](const char *text, bool checked, const Color &color, std::function on_enabled, @@ -1618,10 +1731,10 @@ void pybind_gui_classes(py::module &m) { .def_property_readonly("color_edit", &LUTTreeCell::GetColorEdit, "Returns the ColorEdit widget " "(property is read-only)"); - - py::class_, Widget> - colormap_cell(m, "ColormapTreeCell", - "TreeView cell with a number edit and color edit"); + auto colormap_cell = + static_cast, Widget>>( + m_gui.attr("ColormapTreeCell")); colormap_cell .def(py::init<>([](float value, const Color &color, std::function on_value_changed, @@ -1645,8 +1758,9 @@ void pybind_gui_classes(py::module &m) { "(property is read-only)"); // ---- VectorEdit ---- - py::class_, Widget> vectoredit( - m, "VectorEdit", "Allows the user to edit a 3-space vector"); + auto vectoredit = static_cast< + py::class_, Widget>>( + m_gui.attr("VectorEdit")); vectoredit.def(py::init<>()) .def("__repr__", [](const VectorEdit &ve) { @@ -1665,8 +1779,8 @@ void pybind_gui_classes(py::module &m) { "changes the value of a component"); // ---- Margins ---- - py::class_> margins(m, "Margins", - "Margins for layouts"); + auto margins = static_cast>>( + m_gui.attr("Margins")); margins.def(py::init([](int left, int top, int right, int bottom) { return new Margins(left, top, right, bottom); }), @@ -1698,8 +1812,9 @@ void pybind_gui_classes(py::module &m) { .def("get_vert", &Margins::GetVert); // ---- Layout1D ---- - py::class_, Widget> layout1d( - m, "Layout1D", "Layout base class"); + auto layout1d = + static_cast, Widget>>( + m_gui.attr("Layout1D")); layout1d // TODO: write the proper constructor // .def(py::init([]() { return new Layout1D(Layout1D::VERT, @@ -1717,8 +1832,9 @@ void pybind_gui_classes(py::module &m) { "extra space as there is available in the layout"); // ---- Vert ---- - py::class_, Layout1D> vlayout(m, "Vert", - "Vertical layout"); + auto vlayout = + static_cast, Layout1D>>( + m_gui.attr("Vert")); vlayout.def(py::init([](int spacing, const Margins &margins) { return new Vert(spacing, margins); }), @@ -1740,10 +1856,9 @@ void pybind_gui_classes(py::module &m) { "Sets the preferred width of the layout"); // ---- CollapsableVert ---- - py::class_, Vert> - collapsable(m, "CollapsableVert", - "Vertical layout with title, whose contents are " - "collapsable"); + auto collapsable = static_cast< + py::class_, Vert>>( + m_gui.attr("CollapsableVert")); collapsable .def(py::init([](const char *text, int spacing, const Margins &margins) { @@ -1782,8 +1897,9 @@ void pybind_gui_classes(py::module &m) { "Application.add_font()"); // ---- ScrollableVert ---- - py::class_, Vert> slayout( - m, "ScrollableVert", "Scrollable vertical layout"); + auto slayout = static_cast< + py::class_, Vert>>( + m_gui.attr("ScrollableVert")); slayout.def(py::init([](int spacing, const Margins &margins) { return new ScrollableVert(spacing, margins); }), @@ -1803,8 +1919,9 @@ void pybind_gui_classes(py::module &m) { "is the margins. Both default to 0."); // ---- Horiz ---- - py::class_, Layout1D> hlayout( - m, "Horiz", "Horizontal layout"); + auto hlayout = + static_cast, Layout1D>>( + m_gui.attr("Horiz")); hlayout.def(py::init([](int spacing, const Margins &margins) { return new Horiz(spacing, margins); }), @@ -1828,8 +1945,8 @@ void pybind_gui_classes(py::module &m) { "Sets the preferred height of the layout"); // ---- VGrid ---- - py::class_, Widget> vgrid(m, "VGrid", - "Grid layout"); + auto vgrid = static_cast, Widget>>( + m_gui.attr("VGrid")); vgrid.def(py::init([](int n_cols, int spacing, const Margins &margins) { return new VGrid(n_cols, spacing, margins); }), @@ -1863,25 +1980,16 @@ void pybind_gui_classes(py::module &m) { "Sets the preferred width of the layout"); // ---- Dialog ---- - py::class_, Widget> dialog(m, "Dialog", - "Dialog"); + auto dialog = + static_cast, Widget>>( + m_gui.attr("Dialog")); dialog.def(py::init(), "Creates a dialog with the given title"); // ---- FileDialog ---- - py::class_, Dialog> filedlg( - m, "FileDialog", "File picker dialog"); - py::enum_ filedlg_mode(filedlg, "Mode", py::arithmetic()); - // Trick to write docs without listing the members in the enum class again. - filedlg_mode.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for FileDialog modes."; - }), - py::none(), py::none(), ""); - filedlg_mode.value("OPEN", FileDialog::Mode::OPEN) - .value("SAVE", FileDialog::Mode::SAVE) - .value("OPEN_DIR", FileDialog::Mode::OPEN_DIR) - .export_values(); + auto filedlg = static_cast< + py::class_, Dialog>>( + m_gui.attr("FileDialog")); filedlg.def(py::init(), "Creates either an open or save file dialog. The first " "parameter is either FileDialog.OPEN or FileDialog.SAVE. The " @@ -1900,12 +2008,6 @@ void pybind_gui_classes(py::module &m) { "Done callback; required"); } -void pybind_gui(py::module &m) { - py::module m_gui = m.def_submodule("gui"); - pybind_gui_events(m_gui); - pybind_gui_classes(m_gui); -} - } // namespace gui } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/gui/gui.h b/cpp/pybind/visualization/gui/gui.h index 7e5a7972c26..c2a10b990d3 100644 --- a/cpp/pybind/visualization/gui/gui.h +++ b/cpp/pybind/visualization/gui/gui.h @@ -30,10 +30,11 @@ std::shared_ptr RenderToDepthImageWithoutWindow( int height, bool z_in_view_space = false); -void pybind_gui(py::module &m); +void pybind_gui_declarations(py::module &m); +void pybind_gui_definitions(py::module &m); -void pybind_gui_events(py::module &m); -void pybind_gui_classes(py::module &m); +void pybind_gui_events_declarations(py::module &m); +void pybind_gui_events_definitions(py::module &m); } // namespace gui } // namespace visualization diff --git a/cpp/pybind/visualization/o3dvisualizer.cpp b/cpp/pybind/visualization/o3dvisualizer.cpp index 0859658a081..ba422cc9cc7 100644 --- a/cpp/pybind/visualization/o3dvisualizer.cpp +++ b/cpp/pybind/visualization/o3dvisualizer.cpp @@ -21,32 +21,10 @@ namespace visualization { using namespace visualizer; -void pybind_o3dvisualizer(py::module& m) { +void pybind_o3dvisualizer_declarations(py::module& m) { py::class_ selected_index( m, "SelectedIndex", "Information about a point or vertex that was selected"); - selected_index - .def("__repr__", - [](const O3DVisualizerSelections::SelectedIndex& idx) { - std::stringstream s; - s << "{ index: " << idx.index << ", order: " << idx.order - << ", point: (" << idx.point.x() << ", " << idx.point.y() - << ", " << idx.point.z() << ") }"; - return s.str(); - }) - .def_readonly("index", - &O3DVisualizerSelections::SelectedIndex::index, - "The index of this point in the point/vertex " - "array") - .def_readonly("order", - &O3DVisualizerSelections::SelectedIndex::order, - "A monotonically increasing value that can be " - "used to determine in what order the points " - "were selected") - .def_readonly("point", - &O3DVisualizerSelections::SelectedIndex::point, - "The (x, y, z) value of this point"); - py::class_, gui::Window> o3dvis(m, "O3DVisualizer", "Visualization object used by draw()"); @@ -75,6 +53,37 @@ void pybind_o3dvisualizer(py::module& m) { o3dvis, "DrawObject", "Information about an object that is drawn. Do not modify this, it " "can lead to unexpected results."); +} +void pybind_o3dvisualizer_definitions(py::module& m) { + auto selected_index = + static_cast>( + m.attr("SelectedIndex")); + selected_index + .def("__repr__", + [](const O3DVisualizerSelections::SelectedIndex& idx) { + std::stringstream s; + s << "{ index: " << idx.index << ", order: " << idx.order + << ", point: (" << idx.point.x() << ", " << idx.point.y() + << ", " << idx.point.z() << ") }"; + return s.str(); + }) + .def_readonly("index", + &O3DVisualizerSelections::SelectedIndex::index, + "The index of this point in the point/vertex " + "array") + .def_readonly("order", + &O3DVisualizerSelections::SelectedIndex::order, + "A monotonically increasing value that can be " + "used to determine in what order the points " + "were selected") + .def_readonly("point", + &O3DVisualizerSelections::SelectedIndex::point, + "The (x, y, z) value of this point"); + auto o3dvis = + static_cast, + gui::Window>>(m.attr("O3DVisualizer")); + auto drawobj = static_cast>( + o3dvis.attr("DrawObject")); drawobj.def_readonly("name", &O3DVisualizer::DrawObject::name, "The name of the object") .def_property_readonly( diff --git a/cpp/pybind/visualization/rendering/material.cpp b/cpp/pybind/visualization/rendering/material.cpp index 3e109dcdbb3..15ac1a08b09 100644 --- a/cpp/pybind/visualization/rendering/material.cpp +++ b/cpp/pybind/visualization/rendering/material.cpp @@ -27,18 +27,20 @@ namespace open3d { namespace visualization { namespace rendering { -void pybind_material(py::module& m) { +void pybind_material_declarations(py::module& m) { py::bind_map>( m, "TextureMaps"); py::bind_map>(m, "ScalarProperties"); py::bind_map(m, "VectorProperties"); - py::class_> mat( m, "Material", "Properties (texture maps, scalar and vector) related to " "visualization. Materials are optionally set for 3D geometries " "such as TriangleMesh, LineSets, and PointClouds"); - +} +void pybind_material_definitions(py::module& m) { + auto mat = static_cast>>( + m.attr("Material")); mat.def(py::init<>()) .def(py::init(), "", "mat"_a) .def(py::init(), "", "material_name"_a) diff --git a/cpp/pybind/visualization/rendering/material.h b/cpp/pybind/visualization/rendering/material.h index dd9e7404759..6e157cd74a1 100644 --- a/cpp/pybind/visualization/rendering/material.h +++ b/cpp/pybind/visualization/rendering/material.h @@ -13,8 +13,9 @@ namespace open3d { namespace visualization { namespace rendering { -void pybind_material(py::module &m); +void pybind_material_declarations(py::module &m); +void pybind_material_definitions(py::module &m); -} +} // namespace rendering } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/rendering/rendering.cpp b/cpp/pybind/visualization/rendering/rendering.cpp index b243531d859..f670289f420 100644 --- a/cpp/pybind/visualization/rendering/rendering.cpp +++ b/cpp/pybind/visualization/rendering/rendering.cpp @@ -109,10 +109,124 @@ class PyOffscreenRenderer { Open3DScene *scene_; }; -void pybind_rendering_classes(py::module &m) { +void pybind_rendering_declarations(py::module &m) { + py::module m_rendering = m.def_submodule("rendering"); py::class_ renderer( - m, "Renderer", + m_rendering, "Renderer", "Renderer class that manages 3D resources. Get from gui.Window."); + // It would be nice to have this inherit from Renderer, but the problem is + // that Python needs to own this class and Python needs to not own Renderer, + // and pybind does not let us mix the two styles of ownership. + py::class_> + offscreen(m_rendering, "OffscreenRenderer", + "Renderer instance that can be used for rendering to an " + "image"); + py::class_> cam(m_rendering, "Camera", + "Camera object"); + py::enum_ fov_type(cam, "FovType", py::arithmetic(), + "Enum class for Camera field of view " + "types."); + fov_type.value("Vertical", Camera::FovType::Vertical) + .value("Horizontal", Camera::FovType::Horizontal) + .export_values(); + + py::enum_ proj_type(cam, "Projection", py::arithmetic(), + "Enum class for Camera projection " + "types."); + proj_type.value("Perspective", Camera::Projection::Perspective) + .value("Ortho", Camera::Projection::Ortho) + .export_values(); + py::class_> gradient( + m_rendering, "Gradient", + "Manages a gradient for the unlitGradient shader." + "In gradient mode, the array of points specifies points along " + "the gradient, from 0 to 1 (inclusive). These do need to be " + "evenly spaced." + "Simple greyscale:" + " [ ( 0.0, black )," + " ( 1.0, white ) ]" + "Rainbow (note the gaps around green):" + " [ ( 0.000, blue )," + " ( 0.125, cornflower blue )," + " ( 0.250, cyan )," + " ( 0.500, green )," + " ( 0.750, yellow )," + " ( 0.875, orange )," + " ( 1.000, red ) ]" + "The gradient will generate a largish texture, so it should " + "be fairly smooth, but the boundaries may not be exactly as " + "specified due to quantization imposed by the fixed size of " + "the texture." + " The points *must* be sorted from the smallest value to the " + "largest. The values must be in the range [0, 1]."); + py::enum_ gradient_mode(gradient, "Mode", py::arithmetic()); + gradient_mode.value("GRADIENT", Gradient::Mode::kGradient) + .value("LUT", Gradient::Mode::kLUT) + .export_values(); + py::class_ gpt(gradient, "Point"); + py::class_ mat( + m_rendering, "MaterialRecord", + "Describes the real-world, physically based (PBR) " + "material used to render a geometry"); + py::class_> tri_model( + m_rendering, "TriangleMeshModel", + "A list of geometry.TriangleMesh and Material that can describe a " + "complex model with multiple meshes, such as might be stored in an " + "FBX, OBJ, or GLTF file"); + py::class_ tri_model_info(tri_model, + "MeshInfo", ""); + py::class_ color_grading( + m_rendering, "ColorGrading", + "Parameters to control color grading options"); + py::enum_ cgp_quality( + color_grading, "Quality", + "Quality level of color grading operations"); + cgp_quality.value("LOW", ColorGradingParams::Quality::kLow) + .value("MEDIUM", ColorGradingParams::Quality::kMedium) + .value("HIGH", ColorGradingParams::Quality::kHigh) + .value("ULTRA", ColorGradingParams::Quality::kUltra); + py::enum_ cgp_tone( + color_grading, "ToneMapping", + "Specifies the tone-mapping algorithm"); + cgp_tone.value("LINEAR", ColorGradingParams::ToneMapping::kLinear) + .value("ACES_LEGACY", ColorGradingParams::ToneMapping::kAcesLegacy) + .value("ACES", ColorGradingParams::ToneMapping::kAces) + .value("FILMIC", ColorGradingParams::ToneMapping::kFilmic) + .value("UCHIMURA", ColorGradingParams::ToneMapping::kUchimura) + .value("REINHARD", ColorGradingParams::ToneMapping::kReinhard) + .value("DISPLAY_RANGE", + ColorGradingParams::ToneMapping::kDisplayRange); + py::class_> view(m_rendering, "View", + "Low-level view class"); + py::enum_ shadow_type( + view, "ShadowType", "Available shadow mapping algorithm options"); + shadow_type.value("PCF", View::ShadowType::kPCF) + .value("VSM", View::ShadowType::kVSM); + py::class_> scene(m_rendering, "Scene", + "Low-level rendering scene"); + py::enum_ ground_plane( + scene, "GroundPlane", py::arithmetic(), + "Plane on which to show ground plane: XZ, XY, or YZ"); + ground_plane.value("XZ", Scene::GroundPlane::XZ) + .value("XY", Scene::GroundPlane::XY) + .value("YZ", Scene::GroundPlane::YZ) + .export_values(); + py::class_> o3dscene( + m_rendering, "Open3DScene", "High-level scene for rending"); + py::enum_ lighting( + o3dscene, "LightingProfile", py::arithmetic(), + "Enum for conveniently setting lighting"); + lighting.value("HARD_SHADOWS", Open3DScene::LightingProfile::HARD_SHADOWS) + .value("DARK_SHADOWS", Open3DScene::LightingProfile::DARK_SHADOWS) + .value("MED_SHADOWS", Open3DScene::LightingProfile::MED_SHADOWS) + .value("SOFT_SHADOWS", Open3DScene::LightingProfile::SOFT_SHADOWS) + .value("NO_SHADOWS", Open3DScene::LightingProfile::NO_SHADOWS) + .export_values(); +} +void pybind_rendering_definitions(py::module &m) { + auto m_rendering = static_cast(m.attr("rendering")); + auto renderer = + static_cast>(m_rendering.attr("Renderer")); renderer.def("set_clear_color", &Renderer::SetClearColor, "Sets the background color for the renderer, [r, g, b, a]. " "Applies to everything being rendered, so it essentially acts " @@ -143,14 +257,10 @@ void pybind_rendering_classes(py::module &m) { "Deletes the texture. This does not remove the texture from " "any existing materials or GUI widgets, and must be done " "prior to this call."); - - // It would be nice to have this inherit from Renderer, but the problem is - // that Python needs to own this class and Python needs to not own Renderer, - // and pybind does not let us mix the two styles of ownership. - py::class_> - offscreen(m, "OffscreenRenderer", - "Renderer instance that can be used for rendering to an " - "image"); + auto offscreen = + static_cast>>( + m_rendering.attr("OffscreenRenderer")); offscreen .def(py::init([](int w, int h, const std::string &resource_path) { return std::make_shared( @@ -201,22 +311,8 @@ void pybind_rendering_classes(py::module &m) { "camera)."); // ---- Camera ---- - py::class_> cam(m, "Camera", - "Camera object"); - py::enum_ fov_type(cam, "FovType", py::arithmetic(), - "Enum class for Camera field of view " - "types."); - fov_type.value("Vertical", Camera::FovType::Vertical) - .value("Horizontal", Camera::FovType::Horizontal) - .export_values(); - - py::enum_ proj_type(cam, "Projection", py::arithmetic(), - "Enum class for Camera projection " - "types."); - proj_type.value("Perspective", Camera::Projection::Perspective) - .value("Ortho", Camera::Projection::Ortho) - .export_values(); - + auto cam = static_cast>>( + m_rendering.attr("Camera")); cam.def("set_projection", (void (Camera::*)(double, double, double, double, Camera::FovType)) & @@ -279,34 +375,10 @@ void pybind_rendering_classes(py::module &m) { "Returns the model matrix of the camera"); // ---- Gradient ---- - py::class_> gradient( - m, "Gradient", - "Manages a gradient for the unlitGradient shader." - "In gradient mode, the array of points specifies points along " - "the gradient, from 0 to 1 (inclusive). These do need to be " - "evenly spaced." - "Simple greyscale:" - " [ ( 0.0, black )," - " ( 1.0, white ) ]" - "Rainbow (note the gaps around green):" - " [ ( 0.000, blue )," - " ( 0.125, cornflower blue )," - " ( 0.250, cyan )," - " ( 0.500, green )," - " ( 0.750, yellow )," - " ( 0.875, orange )," - " ( 1.000, red ) ]" - "The gradient will generate a largish texture, so it should " - "be fairly smooth, but the boundaries may not be exactly as " - "specified due to quantization imposed by the fixed size of " - "the texture." - " The points *must* be sorted from the smallest value to the " - "largest. The values must be in the range [0, 1]."); - py::enum_ gradient_mode(gradient, "Mode", py::arithmetic()); - gradient_mode.value("GRADIENT", Gradient::Mode::kGradient) - .value("LUT", Gradient::Mode::kLUT) - .export_values(); - py::class_ gpt(gradient, "Point"); + auto gradient = + static_cast>>( + m_rendering.attr("Gradient")); + auto gpt = static_cast>(gradient.attr("Point")); gpt.def(py::init()) .def("__repr__", [](const Gradient::Point &p) { @@ -326,10 +398,8 @@ void pybind_rendering_classes(py::module &m) { .def_property("mode", &Gradient::GetMode, &Gradient::SetMode); // ---- Material ---- - py::class_ mat( - m, "MaterialRecord", - "Describes the real-world, physically based (PBR) " - "material used to render a geometry"); + auto mat = static_cast>( + m_rendering.attr("MaterialRecord")); mat.def(py::init<>()) .def_readwrite("has_alpha", &MaterialRecord::has_alpha) .def_readwrite("base_color", &MaterialRecord::base_color) @@ -375,13 +445,11 @@ void pybind_rendering_classes(py::module &m) { .def_readwrite("shader", &MaterialRecord::shader); // ---- TriangleMeshModel ---- - py::class_> tri_model( - m, "TriangleMeshModel", - "A list of geometry.TriangleMesh and Material that can describe a " - "complex model with multiple meshes, such as might be stored in an " - "FBX, OBJ, or GLTF file"); - py::class_ tri_model_info(tri_model, - "MeshInfo", ""); + auto tri_model = static_cast< + py::class_>>( + m_rendering.attr("TriangleMeshModel")); + auto tri_model_info = static_cast>( + tri_model.attr("MeshInfo")); tri_model_info .def(py::init([](std::shared_ptr mesh, const std::string &name, @@ -397,29 +465,8 @@ void pybind_rendering_classes(py::module &m) { .def_readwrite("materials", &TriangleMeshModel::materials_); // ---- ColorGradingParams --- - py::class_ color_grading( - m, "ColorGrading", "Parameters to control color grading options"); - - py::enum_ cgp_quality( - color_grading, "Quality", - "Quality level of color grading operations"); - cgp_quality.value("LOW", ColorGradingParams::Quality::kLow) - .value("MEDIUM", ColorGradingParams::Quality::kMedium) - .value("HIGH", ColorGradingParams::Quality::kHigh) - .value("ULTRA", ColorGradingParams::Quality::kUltra); - - py::enum_ cgp_tone( - color_grading, "ToneMapping", - "Specifies the tone-mapping algorithm"); - cgp_tone.value("LINEAR", ColorGradingParams::ToneMapping::kLinear) - .value("ACES_LEGACY", ColorGradingParams::ToneMapping::kAcesLegacy) - .value("ACES", ColorGradingParams::ToneMapping::kAces) - .value("FILMIC", ColorGradingParams::ToneMapping::kFilmic) - .value("UCHIMURA", ColorGradingParams::ToneMapping::kUchimura) - .value("REINHARD", ColorGradingParams::ToneMapping::kReinhard) - .value("DISPLAY_RANGE", - ColorGradingParams::ToneMapping::kDisplayRange); - + auto color_grading = static_cast>( + m_rendering.attr("ColorGrading")); color_grading .def(py::init([](ColorGradingParams::Quality q, ColorGradingParams::ToneMapping algorithm) { @@ -443,14 +490,8 @@ void pybind_rendering_classes(py::module &m) { "Tint on the green/magenta axis. Ranges from -1.0 to 1.0."); // ---- View ---- - py::class_> view(m, "View", - "Low-level view class"); - // ---- Shadow Types ---- - py::enum_ shadow_type( - view, "ShadowType", "Available shadow mapping algorithm options"); - shadow_type.value("PCF", View::ShadowType::kPCF) - .value("VSM", View::ShadowType::kVSM); - + auto view = static_cast>>( + m_rendering.attr("View")); view.def("set_color_grading", &View::SetColorGrading, "Sets the parameters to be used for the color grading algorithms") .def("set_post_processing", &View::SetPostProcessing, @@ -483,15 +524,8 @@ void pybind_rendering_classes(py::module &m) { "Returns the Camera associated with this View."); // ---- Scene ---- - py::class_> scene(m, "Scene", - "Low-level rendering scene"); - py::enum_ ground_plane( - scene, "GroundPlane", py::arithmetic(), - "Plane on which to show ground plane: XZ, XY, or YZ"); - ground_plane.value("XZ", Scene::GroundPlane::XZ) - .value("XY", Scene::GroundPlane::XY) - .value("YZ", Scene::GroundPlane::YZ) - .export_values(); + auto scene = static_cast>>( + m_rendering.attr("Scene")); scene.def("add_camera", &Scene::AddCamera, "name"_a, "camera"_a, "Adds a camera to the scene") .def("remove_camera", &Scene::RemoveCamera, "name"_a, @@ -606,18 +640,9 @@ void pybind_rendering_classes(py::module &m) { scene.attr("UPDATE_UV0_FLAG") = py::int_(Scene::kUpdateUv0Flag); // ---- Open3DScene ---- - py::class_> o3dscene( - m, "Open3DScene", "High-level scene for rending"); - py::enum_ lighting( - o3dscene, "LightingProfile", py::arithmetic(), - "Enum for conveniently setting lighting"); - lighting.value("HARD_SHADOWS", Open3DScene::LightingProfile::HARD_SHADOWS) - .value("DARK_SHADOWS", Open3DScene::LightingProfile::DARK_SHADOWS) - .value("MED_SHADOWS", Open3DScene::LightingProfile::MED_SHADOWS) - .value("SOFT_SHADOWS", Open3DScene::LightingProfile::SOFT_SHADOWS) - .value("NO_SHADOWS", Open3DScene::LightingProfile::NO_SHADOWS) - .export_values(); - + auto o3dscene = + static_cast>>( + m_rendering.attr("Open3DScene")); o3dscene.def(py::init()) .def("show_skybox", &Open3DScene::ShowSkybox, "enable"_a, "Toggles display of the skybox") @@ -715,11 +740,6 @@ void pybind_rendering_classes(py::module &m) { "is important"); } -void pybind_rendering(py::module &m) { - py::module m_rendering = m.def_submodule("rendering"); - pybind_rendering_classes(m_rendering); -} - } // namespace rendering } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/rendering/rendering.h b/cpp/pybind/visualization/rendering/rendering.h index 007ba2fbce2..0e5dd6bc035 100644 --- a/cpp/pybind/visualization/rendering/rendering.h +++ b/cpp/pybind/visualization/rendering/rendering.h @@ -13,8 +13,9 @@ namespace open3d { namespace visualization { namespace rendering { -void pybind_rendering(py::module &m); +void pybind_rendering_declarations(py::module &m); +void pybind_rendering_definitions(py::module &m); -} +} // namespace rendering } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/renderoption.cpp b/cpp/pybind/visualization/renderoption.cpp index 903e2db6135..c2a3424c0b4 100644 --- a/cpp/pybind/visualization/renderoption.cpp +++ b/cpp/pybind/visualization/renderoption.cpp @@ -15,10 +15,63 @@ namespace open3d { namespace visualization { -void pybind_renderoption(py::module &m) { - // open3d.visualization.RenderOption +void pybind_renderoption_declarations(py::module &m) { py::class_> renderoption( m, "RenderOption", "Defines rendering options for visualizer."); + // This is a nested class, but now it's bind to the module + // o3d.visualization.PointColorOption + py::enum_ enum_point_color_option( + m, "PointColorOption", py::arithmetic(), "PointColorOption"); + enum_point_color_option.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for point color for ``PointCloud``."; + }), + py::none(), py::none(), ""); + enum_point_color_option + .value("Default", RenderOption::PointColorOption::Default) + .value("Color", RenderOption::PointColorOption::Color) + .value("XCoordinate", RenderOption::PointColorOption::XCoordinate) + .value("YCoordinate", RenderOption::PointColorOption::YCoordinate) + .value("ZCoordinate", RenderOption::PointColorOption::ZCoordinate) + .value("Normal", RenderOption::PointColorOption::Normal) + .export_values(); + // This is a nested class, but now it's bind to the module + // o3d.visualization.MeshShadeOption + py::enum_ enum_mesh_shade_option( + m, "MeshShadeOption", py::arithmetic(), "MeshShadeOption"); + enum_mesh_shade_option.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for mesh shading for ``TriangleMesh``."; + }), + py::none(), py::none(), ""); + enum_mesh_shade_option + .value("Default", RenderOption::MeshShadeOption::FlatShade) + .value("Color", RenderOption::MeshShadeOption::SmoothShade) + .export_values(); + + // This is a nested class, but now it's bind to the module + // o3d.visualization.MeshColorOption + py::enum_ enum_mesh_clor_option( + m, "MeshColorOption", py::arithmetic(), "MeshColorOption"); + enum_mesh_clor_option.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Enum class for color for ``TriangleMesh``."; + }), + py::none(), py::none(), ""); + enum_mesh_clor_option + .value("Default", RenderOption::MeshColorOption::Default) + .value("Color", RenderOption::MeshColorOption::Color) + .value("XCoordinate", RenderOption::MeshColorOption::XCoordinate) + .value("YCoordinate", RenderOption::MeshColorOption::YCoordinate) + .value("ZCoordinate", RenderOption::MeshColorOption::ZCoordinate) + .value("Normal", RenderOption::MeshColorOption::Normal) + .export_values(); +} +void pybind_renderoption_definitions(py::module &m) { + // open3d.visualization.RenderOption + auto renderoption = static_cast< + py::class_>>( + m.attr("RenderOption")); py::detail::bind_default_constructor(renderoption); renderoption .def("__repr__", @@ -77,59 +130,7 @@ void pybind_renderoption(py::module &m) { {{"filename", "Path to file."}}); docstring::ClassMethodDocInject(m, "RenderOption", "save_to_json", {{"filename", "Path to file."}}); - - // This is a nested class, but now it's bind to the module - // o3d.visualization.PointColorOption - py::enum_ enum_point_color_option( - m, "PointColorOption", py::arithmetic(), "PointColorOption"); - enum_point_color_option.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for point color for ``PointCloud``."; - }), - py::none(), py::none(), ""); - enum_point_color_option - .value("Default", RenderOption::PointColorOption::Default) - .value("Color", RenderOption::PointColorOption::Color) - .value("XCoordinate", RenderOption::PointColorOption::XCoordinate) - .value("YCoordinate", RenderOption::PointColorOption::YCoordinate) - .value("ZCoordinate", RenderOption::PointColorOption::ZCoordinate) - .value("Normal", RenderOption::PointColorOption::Normal) - .export_values(); - - // This is a nested class, but now it's bind to the module - // o3d.visualization.MeshShadeOption - py::enum_ enum_mesh_shade_option( - m, "MeshShadeOption", py::arithmetic(), "MeshShadeOption"); - enum_mesh_shade_option.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for mesh shading for ``TriangleMesh``."; - }), - py::none(), py::none(), ""); - enum_mesh_shade_option - .value("Default", RenderOption::MeshShadeOption::FlatShade) - .value("Color", RenderOption::MeshShadeOption::SmoothShade) - .export_values(); - - // This is a nested class, but now it's bind to the module - // o3d.visualization.MeshColorOption - py::enum_ enum_mesh_clor_option( - m, "MeshColorOption", py::arithmetic(), "MeshColorOption"); - enum_mesh_clor_option.attr("__doc__") = docstring::static_property( - py::cpp_function([](py::handle arg) -> std::string { - return "Enum class for color for ``TriangleMesh``."; - }), - py::none(), py::none(), ""); - enum_mesh_clor_option - .value("Default", RenderOption::MeshColorOption::Default) - .value("Color", RenderOption::MeshColorOption::Color) - .value("XCoordinate", RenderOption::MeshColorOption::XCoordinate) - .value("YCoordinate", RenderOption::MeshColorOption::YCoordinate) - .value("ZCoordinate", RenderOption::MeshColorOption::ZCoordinate) - .value("Normal", RenderOption::MeshColorOption::Normal) - .export_values(); } -void pybind_renderoption_method(py::module &m) {} - } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/utility.cpp b/cpp/pybind/visualization/utility.cpp index 603e78bb947..99cb4323d60 100644 --- a/cpp/pybind/visualization/utility.cpp +++ b/cpp/pybind/visualization/utility.cpp @@ -18,10 +18,15 @@ namespace open3d { namespace visualization { -void pybind_visualization_utility(py::module &m) { +void pybind_visualization_utility_declarations(py::module &m) { py::class_ selection_volume( m, "SelectionPolygonVolume", "Select a polygon volume for cropping."); +} + +void pybind_visualization_utility_definitions(py::module &m) { + auto selection_volume = static_cast>( + m.attr("SelectionPolygonVolume")); py::detail::bind_default_constructor( selection_volume); py::detail::bind_copy_functions(selection_volume); @@ -75,36 +80,34 @@ void pybind_visualization_utility(py::module &m) { docstring::ClassMethodDocInject(m, "SelectionPolygonVolume", "crop_in_polygon", {{"input", "The input point cloud xyz."}}); -} - -// Visualization util functions have similar arguments, sharing arg docstrings -static const std::unordered_map - map_shared_argument_docstrings = { - {"callback_function", - "Call back function to be triggered at a key press event."}, - {"filename", "The file path."}, - {"geometry_list", "List of geometries to be visualized."}, - {"height", "The height of the visualization window."}, - {"key_to_callback", "Map of key to call back functions."}, - {"left", "The left margin of the visualization window."}, - {"optional_view_trajectory_json_file", - "Camera trajectory json file path for custom animation."}, - {"top", "The top margin of the visualization window."}, - {"width", "The width of the visualization window."}, - {"point_show_normal", - "Visualize point normals if set to true."}, - {"mesh_show_wireframe", - "Visualize mesh wireframe if set to true."}, - {"mesh_show_back_face", - "Visualize also the back face of the mesh triangles."}, - {"window_name", - "The displayed title of the visualization window."}, - {"lookat", "The lookat vector of the camera."}, - {"up", "The up vector of the camera."}, - {"front", "The front vector of the camera."}, - {"zoom", "The zoom of the camera."}}; - -void pybind_visualization_utility_methods(py::module &m) { + // Visualization util functions have similar arguments, sharing arg + // docstrings + static const std::unordered_map + map_shared_argument_docstrings = { + {"callback_function", + "Call back function to be triggered at a key press " + "event."}, + {"filename", "The file path."}, + {"geometry_list", "List of geometries to be visualized."}, + {"height", "The height of the visualization window."}, + {"key_to_callback", "Map of key to call back functions."}, + {"left", "The left margin of the visualization window."}, + {"optional_view_trajectory_json_file", + "Camera trajectory json file path for custom animation."}, + {"top", "The top margin of the visualization window."}, + {"width", "The width of the visualization window."}, + {"point_show_normal", + "Visualize point normals if set to true."}, + {"mesh_show_wireframe", + "Visualize mesh wireframe if set to true."}, + {"mesh_show_back_face", + "Visualize also the back face of the mesh triangles."}, + {"window_name", + "The displayed title of the visualization window."}, + {"lookat", "The lookat vector of the camera."}, + {"up", "The up vector of the camera."}, + {"front", "The front vector of the camera."}, + {"zoom", "The zoom of the camera."}}; m.def( "draw_geometries", [](const std::vector> diff --git a/cpp/pybind/visualization/viewcontrol.cpp b/cpp/pybind/visualization/viewcontrol.cpp index 80a8aaa7895..695f7d7c626 100644 --- a/cpp/pybind/visualization/viewcontrol.cpp +++ b/cpp/pybind/visualization/viewcontrol.cpp @@ -29,9 +29,14 @@ static const std::unordered_map {"z_far", "The depth of the far z-plane of the visualizer."}, }; -void pybind_viewcontrol(py::module &m) { +void pybind_viewcontrol_declarations(py::module &m) { py::class_, std::shared_ptr> viewcontrol(m, "ViewControl", "View controller for visualizer."); +} +void pybind_viewcontrol_definitions(py::module &m) { + auto viewcontrol = static_cast, + std::shared_ptr>>( + m.attr("ViewControl")); py::detail::bind_default_constructor(viewcontrol); viewcontrol .def("__repr__", @@ -123,7 +128,5 @@ void pybind_viewcontrol(py::module &m) { map_view_control_docstrings); } -void pybind_viewcontrol_method(py::module &m) {} - } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/visualization.cpp b/cpp/pybind/visualization/visualization.cpp index b6e3455fa6d..d143683996b 100644 --- a/cpp/pybind/visualization/visualization.cpp +++ b/cpp/pybind/visualization/visualization.cpp @@ -19,27 +19,41 @@ namespace open3d { namespace visualization { -void pybind_visualization(py::module &m) { +void pybind_visualization_declarations(py::module &m) { py::module m_visualization = m.def_submodule("visualization"); - pybind_renderoption(m_visualization); - pybind_viewcontrol(m_visualization); - pybind_visualizer(m_visualization); - pybind_visualization_utility(m_visualization); - pybind_renderoption_method(m_visualization); - pybind_viewcontrol_method(m_visualization); - pybind_visualizer_method(m_visualization); - pybind_visualization_utility_methods(m_visualization); - rendering::pybind_material(m_visualization); // For RPC serialization - + pybind_renderoption_declarations(m_visualization); + pybind_viewcontrol_declarations(m_visualization); + pybind_visualizer_declarations(m_visualization); + pybind_visualization_utility_declarations(m_visualization); + // For RPC serialization + rendering::pybind_material_declarations(m_visualization); #ifdef BUILD_GUI - rendering::pybind_rendering(m_visualization); - gui::pybind_gui(m_visualization); - pybind_o3dvisualizer(m_visualization); - app::pybind_app(m_visualization); + rendering::pybind_rendering_declarations(m_visualization); + gui::pybind_gui_declarations(m_visualization); + pybind_o3dvisualizer_declarations(m_visualization); + app::pybind_app_declarations(m_visualization); +#endif +#ifdef BUILD_WEBRTC + webrtc_server::pybind_webrtc_server_declarations(m_visualization); #endif +} +void pybind_visualization_definitions(py::module &m) { + auto m_visualization = static_cast(m.attr("visualization")); + pybind_renderoption_definitions(m_visualization); + pybind_viewcontrol_definitions(m_visualization); + pybind_visualizer_definitions(m_visualization); + pybind_visualization_utility_definitions(m_visualization); + // For RPC serialization + rendering::pybind_material_definitions(m_visualization); +#ifdef BUILD_GUI + rendering::pybind_rendering_definitions(m_visualization); + gui::pybind_gui_definitions(m_visualization); + pybind_o3dvisualizer_definitions(m_visualization); + app::pybind_app_definitions(m_visualization); +#endif #ifdef BUILD_WEBRTC - webrtc_server::pybind_webrtc_server(m_visualization); + webrtc_server::pybind_webrtc_server_definitions(m_visualization); #endif } diff --git a/cpp/pybind/visualization/visualization.h b/cpp/pybind/visualization/visualization.h index de6d7aa5757..73dd95c952d 100644 --- a/cpp/pybind/visualization/visualization.h +++ b/cpp/pybind/visualization/visualization.h @@ -53,19 +53,19 @@ std::shared_ptr TakeOwnership(UnownedPointer x) { // Please update docs/python_api_in/open3d.visualization.rst when adding / // reorganizing submodules to open3d.visualization. -void pybind_visualization(py::module &m); +void pybind_visualization_declarations(py::module &m); +void pybind_renderoption_declarations(py::module &m); +void pybind_viewcontrol_declarations(py::module &m); +void pybind_visualizer_declarations(py::module &m); +void pybind_visualization_utility_declarations(py::module &m); +void pybind_o3dvisualizer_declarations(py::module &m); -void pybind_renderoption(py::module &m); -void pybind_viewcontrol(py::module &m); -void pybind_visualizer(py::module &m); -void pybind_visualization_utility(py::module &m); - -void pybind_renderoption_method(py::module &m); -void pybind_viewcontrol_method(py::module &m); -void pybind_visualizer_method(py::module &m); -void pybind_visualization_utility_methods(py::module &m); - -void pybind_o3dvisualizer(py::module &m); +void pybind_visualization_definitions(py::module &m); +void pybind_renderoption_definitions(py::module &m); +void pybind_viewcontrol_definitions(py::module &m); +void pybind_visualizer_definitions(py::module &m); +void pybind_visualization_utility_definitions(py::module &m); +void pybind_o3dvisualizer_definitions(py::module &m); } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/visualizer.cpp b/cpp/pybind/visualization/visualizer.cpp index 990727dd45c..579db2cadb6 100644 --- a/cpp/pybind/visualization/visualizer.cpp +++ b/cpp/pybind/visualization/visualizer.cpp @@ -38,9 +38,32 @@ static const std::unordered_map {"reset_bounding_box", "Set to ``False`` to keep current viewpoint"}}; -void pybind_visualizer(py::module &m) { +void pybind_visualizer_declarations(py::module &m) { py::class_, std::shared_ptr> visualizer(m, "Visualizer", "The main Visualizer class."); + py::class_, + std::shared_ptr> + visualizer_key(m, "VisualizerWithKeyCallback", visualizer, + "Visualizer with custom key callback capabilities."); + py::class_, + std::shared_ptr> + visualizer_edit(m, "VisualizerWithEditing", visualizer, + "Visualizer with editing capabilities."); + py::class_, + std::shared_ptr> + visualizer_vselect( + m, "VisualizerWithVertexSelection", visualizer, + "Visualizer with vertex selection capabilities."); + py::class_ + visualizer_vselect_pickedpoint(m, "PickedPoint"); +} + +void pybind_visualizer_definitions(py::module &m) { + auto visualizer = static_cast, + std::shared_ptr>>( + m.attr("Visualizer")); py::detail::bind_default_constructor(visualizer); visualizer .def("__repr__", @@ -145,12 +168,11 @@ void pybind_visualizer(py::module &m) { "Set the current view status from a json string of " "ViewTrajectory.", "view_status_str"_a); - - py::class_, - std::shared_ptr> - visualizer_key(m, "VisualizerWithKeyCallback", visualizer, - "Visualizer with custom key callback capabilities."); + auto visualizer_key = + static_cast, + std::shared_ptr>>( + m.attr("VisualizerWithKeyCallback")); py::detail::bind_default_constructor( visualizer_key); visualizer_key @@ -212,10 +234,11 @@ void pybind_visualizer(py::module &m) { "key `__.", "callback_func"_a); - py::class_, - std::shared_ptr> - visualizer_edit(m, "VisualizerWithEditing", visualizer, - "Visualizer with editing capabilities."); + auto visualizer_edit = + static_cast, + std::shared_ptr>>( + m.attr("VisualizerWithEditing")); py::detail::bind_default_constructor( visualizer_edit); visualizer_edit @@ -232,12 +255,11 @@ void pybind_visualizer(py::module &m) { &VisualizerWithEditing::GetCroppedGeometry, "Function to get cropped geometry"); - py::class_, - std::shared_ptr> - visualizer_vselect( - m, "VisualizerWithVertexSelection", visualizer, - "Visualizer with vertex selection capabilities."); + auto visualizer_vselect = static_cast< + py::class_, + std::shared_ptr>>( + m.attr("VisualizerWithVertexSelection")); py::detail::bind_default_constructor( visualizer_vselect); visualizer_vselect.def(py::init<>()) @@ -279,8 +301,9 @@ void pybind_visualizer(py::module &m) { "Registers a function to be called after selection moves", "f"_a); - py::class_ - visualizer_vselect_pickedpoint(m, "PickedPoint"); + auto visualizer_vselect_pickedpoint = + static_cast>( + m.attr("PickedPoint")); visualizer_vselect_pickedpoint.def(py::init<>()) .def_readwrite("index", &VisualizerWithVertexSelection::PickedPoint::index) @@ -337,7 +360,5 @@ void pybind_visualizer(py::module &m) { map_visualizer_docstrings); } -void pybind_visualizer_method(py::module &m) {} - } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/webrtc_server/webrtc_window_system.cpp b/cpp/pybind/visualization/webrtc_server/webrtc_window_system.cpp index 84ae5d391c2..cd499897358 100644 --- a/cpp/pybind/visualization/webrtc_server/webrtc_window_system.cpp +++ b/cpp/pybind/visualization/webrtc_server/webrtc_window_system.cpp @@ -15,8 +15,15 @@ namespace open3d { namespace visualization { namespace webrtc_server { -static void pybind_webrtc_server_functions(py::module &m) { - m.def( +void pybind_webrtc_server_declarations(py::module &m) { + py::module m_submodule = m.def_submodule( + "webrtc_server", + "Functionality for remote visualization over WebRTC."); +} + +void pybind_webrtc_server_definitions(py::module &m) { + auto m_webrtc_server = static_cast(m.attr("webrtc_server")); + m_webrtc_server.def( "call_http_api", [](const std::string &entry_point, const std::string &query_string, const std::string &data) { @@ -27,18 +34,18 @@ static void pybind_webrtc_server_functions(py::module &m) { "Emulates Open3D WebRTCWindowSystem's HTTP API calls. This is used " "when the HTTP handshake server is disabled (e.g. in Jupyter), and " "handshakes are done by this function."); - m.def( + m_webrtc_server.def( "enable_webrtc", []() { WebRTCWindowSystem::GetInstance()->EnableWebRTC(); }, "Use WebRTC streams to display rendered gui window."); - m.def( + m_webrtc_server.def( "disable_http_handshake", []() { WebRTCWindowSystem::GetInstance()->DisableHttpHandshake(); }, "Disables the HTTP handshake server. In Jupyter environment, " "WebRTC handshake is performed by call_http_api() with " "Jupyter's own COMMS interface, thus the HTTP server shall " "be turned off."); - m.def( + m_webrtc_server.def( "register_data_channel_message_callback", [](const std::string &class_name, std::function callback) { @@ -78,7 +85,7 @@ if it is not empty. )"); docstring::FunctionDocInject( - m, "register_data_channel_message_callback", + m_webrtc_server, "register_data_channel_message_callback", {{"class_name", "The value of of the ``class_name`` property of the JSON " "object."}, @@ -90,13 +97,6 @@ if it is not empty. "value of a slider) and return a ``string``."}}); } -void pybind_webrtc_server(py::module &m) { - py::module m_submodule = m.def_submodule( - "webrtc_server", - "Functionality for remote visualization over WebRTC."); - pybind_webrtc_server_functions(m_submodule); -} - } // namespace webrtc_server } // namespace visualization } // namespace open3d diff --git a/cpp/pybind/visualization/webrtc_server/webrtc_window_system.h b/cpp/pybind/visualization/webrtc_server/webrtc_window_system.h index cae067d7e85..45b820ccaf1 100644 --- a/cpp/pybind/visualization/webrtc_server/webrtc_window_system.h +++ b/cpp/pybind/visualization/webrtc_server/webrtc_window_system.h @@ -13,7 +13,8 @@ namespace open3d { namespace visualization { namespace webrtc_server { -void pybind_webrtc_server(py::module &m); +void pybind_webrtc_server_declarations(py::module &m); +void pybind_webrtc_server_definitions(py::module &m); } // namespace webrtc_server } // namespace visualization diff --git a/docs/builddocs.rst b/docs/builddocs.rst index 2f8e9539957..42d83349bbb 100644 --- a/docs/builddocs.rst +++ b/docs/builddocs.rst @@ -97,3 +97,22 @@ Open ``docs/_out/html/index.html`` in a web browser to preview the docs. .. code-block:: bash google-chrome docs/_out/html/index.html + + +Create Python stubs (type hints) for type checking and autocomplete +------------------------------------------------------------------- + +You can get type checking and auto-complete features in editors and IDES (e.g. + VS Code, PyCharm, etc.) using type hints produced from Open3D. These can be + created with the pybind11-stubgen tool and placed alongside the Open3D files: + +.. code-block:: bash + + # Install open3d and pybind11-stubgen + pip install pybind11-stubgen open3d + # Print location of install open3d library + pip show open3d + # This outputs a line like: + # Location: path/to/venv/site-packages + # Create stubs and place them next to Open3D files + pybind11-stubgen -o --root-suffix "" open3d \ No newline at end of file diff --git a/examples/python/benchmark/benchmark_fgr.py b/examples/python/benchmark/benchmark_fgr.py index 57b02bd09b1..788476a9fca 100644 --- a/examples/python/benchmark/benchmark_fgr.py +++ b/examples/python/benchmark/benchmark_fgr.py @@ -8,6 +8,7 @@ import os import sys import numpy as np +import open3d as o3d pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(pyexample_path) diff --git a/examples/python/benchmark/benchmark_pre.py b/examples/python/benchmark/benchmark_pre.py index 3d949cc42c3..bc1bca4a651 100644 --- a/examples/python/benchmark/benchmark_pre.py +++ b/examples/python/benchmark/benchmark_pre.py @@ -8,6 +8,7 @@ import os import sys import pickle +import open3d as o3d pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(pyexample_path) diff --git a/examples/python/benchmark/benchmark_ransac.py b/examples/python/benchmark/benchmark_ransac.py index 08d833cb9eb..05c0c6b8a0b 100644 --- a/examples/python/benchmark/benchmark_ransac.py +++ b/examples/python/benchmark/benchmark_ransac.py @@ -8,6 +8,7 @@ import os import sys import numpy as np +import open3d as o3d pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(pyexample_path) diff --git a/examples/python/benchmark/benchmark_tsdf.py b/examples/python/benchmark/benchmark_tsdf.py index 44f38d45370..c915a6e908d 100644 --- a/examples/python/benchmark/benchmark_tsdf.py +++ b/examples/python/benchmark/benchmark_tsdf.py @@ -5,11 +5,11 @@ # SPDX-License-Identifier: MIT # ---------------------------------------------------------------------------- -import open3d as o3d import numpy as np import time import os import sys +import open3d as o3d pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(pyexample_path) From 6cb32f6d7b4d3a39555abccf2e20a56d5f852ec4 Mon Sep 17 00:00:00 2001 From: Robin <102535177+rxba@users.noreply.github.com> Date: Thu, 15 Aug 2024 19:50:46 +0200 Subject: [PATCH 53/82] Warn if projection of point cloud with no positions attribute to image (#6880) --- CHANGELOG.md | 1 + cpp/open3d/t/geometry/PointCloud.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98836909e7a..829d890ef53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ - Add O3DVisualizer API to enable collapse control of verts in the side panel (PR #6865) - Split pybind declarations/definitions to avoid C++ types in Python docs (PR #6869) - Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (PR #6898) +- Fix projection of point cloud to Depth/RGBD image if no position attribute is provided (PR #6880) ## 0.13 diff --git a/cpp/open3d/t/geometry/PointCloud.cpp b/cpp/open3d/t/geometry/PointCloud.cpp index f4c5b47f068..b345e0ea985 100644 --- a/cpp/open3d/t/geometry/PointCloud.cpp +++ b/cpp/open3d/t/geometry/PointCloud.cpp @@ -983,6 +983,12 @@ geometry::Image PointCloud::ProjectToDepthImage(int width, const core::Tensor &extrinsics, float depth_scale, float depth_max) { + if (!HasPointPositions()) { + utility::LogWarning( + "Called ProjectToDepthImage on a point cloud with no Positions " + "attribute. Returning empty image."); + return geometry::Image(); + } core::AssertTensorShape(intrinsics, {3, 3}); core::AssertTensorShape(extrinsics, {4, 4}); @@ -1001,6 +1007,12 @@ geometry::RGBDImage PointCloud::ProjectToRGBDImage( const core::Tensor &extrinsics, float depth_scale, float depth_max) { + if (!HasPointPositions()) { + utility::LogWarning( + "Called ProjectToRGBDImage on a point cloud with no Positions " + "attribute. Returning empty image."); + return geometry::RGBDImage(); + } if (!HasPointColors()) { utility::LogError( "Unable to project to RGBD without the Color attribute in the " From f7d8cebdee0fe5613eb4f6b9b82244fc8105034a Mon Sep 17 00:00:00 2001 From: nwwolf Date: Tue, 20 Aug 2024 05:12:09 +0800 Subject: [PATCH 54/82] bug fix at AddPerpPlaneQuadric (#6911) --- cpp/open3d/geometry/TriangleMeshSimplification.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/open3d/geometry/TriangleMeshSimplification.cpp b/cpp/open3d/geometry/TriangleMeshSimplification.cpp index 90a2a166ce7..081d0842a2c 100644 --- a/cpp/open3d/geometry/TriangleMeshSimplification.cpp +++ b/cpp/open3d/geometry/TriangleMeshSimplification.cpp @@ -298,7 +298,8 @@ std::shared_ptr TriangleMesh::SimplifyQuadricDecimation( const auto& vert1 = mesh->vertices_[vidx1]; const auto& vert2 = mesh->vertices_[vidx2]; Eigen::Vector3d vert2p = (vert2 - vert0).cross(vert2 - vert1); - Eigen::Vector4d plane = ComputeTrianglePlane(vert0, vert1, vert2p); + Eigen::Vector4d plane = + ComputeTrianglePlane(vert0, vert1, vert0 + vert2p); Quadric quad(plane, area * boundary_weight); Qs[vidx0] += quad; Qs[vidx1] += quad; From c219d82a456c4528b34002c9cffd76ff30d555c3 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:44:52 -0700 Subject: [PATCH 55/82] Update Intel libraries (MKL, TBB, IPP). Remove parallelSTL / oneDPL. (#6809) - Add VERSION (API version - major.minor.patch) and SOVERSION (ABI version major.minor) to libOpen3D - Switch to C++17. - Preparing for Intel GPU offload with new MKL (2024.1 for Win, Linux and 2023.2.2 for macOS). - Additional computer vision / image processing functions (e.g. optical flow) from new IPP (2021.11 for Win/Linux and 2021.9.1 for macOS)). No GPU support yet, but get automatic TBB based multi-threading inside image processing operations. - Preparing for OpenMP -> TBB conversion. TBB 2021.12. Replace parallelSTL / oneDPL with TBB. - TBB is now linked dynamically. TBB static linking is not supported and may cause over-subscription / crashes, especially in Python. TBB DSOs are now distributed with Open3D. Add TCM_ENABLE=1 to python to use threading composability manager (if available) to avoid oversubscription. - libc++-10, libc++abi-10 for filament. Later versions also require libunwind. --------- Co-authored-by: Benjamin Ummenhofer --- .clang-format | 2 +- .github/workflows/windows.yml | 3 +- 3rdparty/find_dependencies.cmake | 170 ++++++-------- 3rdparty/imgui/imgui.cmake | 2 +- 3rdparty/{ippicv => ipp}/LICENSE | 0 3rdparty/ipp/ipp.cmake | 60 +++++ 3rdparty/ippicv/CMakeLists.txt | 34 --- 3rdparty/ippicv/ippicv.cmake | 57 ----- ...ttion-of-static-dynamic-MSVC-runtime.patch | 32 --- 3rdparty/mkl/mkl.cmake | 155 ++----------- 3rdparty/mkl/tbb.cmake | 68 +++--- 3rdparty/parallelstl/LICENSE | 218 ------------------ 3rdparty/parallelstl/parallelstl.cmake | 16 -- 3rdparty/qhull/qhull.cmake | 4 - CMakeLists.txt | 30 +-- README.md | 2 +- cmake/Open3DPrintConfigurationSummary.cmake | 2 +- cmake/Open3DSetGlobalProperties.cmake | 25 +- cpp/apps/CMakeLists.txt | 6 +- .../Open3DViewer/Debian/CMakeLists.in.txt | 2 +- cpp/apps/fixup_macosx_bundle.sh | 69 +++--- cpp/benchmarks/CMakeLists.txt | 2 +- .../t/pipelines/odometry/RGBDOdometry.cpp | 4 +- cpp/open3d/CMakeLists.txt | 6 +- cpp/open3d/cmake_uninstall.cmake.in | 13 +- cpp/open3d/ml/pytorch/CMakeLists.txt | 10 +- cpp/open3d/ml/tensorflow/CMakeLists.txt | 11 +- cpp/open3d/t/geometry/Image.cpp | 16 +- cpp/open3d/t/geometry/Image.h | 8 +- cpp/open3d/t/geometry/kernel/CMakeLists.txt | 2 +- cpp/open3d/t/geometry/kernel/IPPImage.cpp | 23 +- cpp/open3d/t/geometry/kernel/IPPImage.h | 14 +- cpp/open3d/utility/ParallelScan.h | 46 ---- cpp/pybind/CMakeLists.txt | 31 ++- cpp/pybind/make_python_package.cmake | 6 +- cpp/tests/CMakeLists.txt | 10 +- cpp/tests/t/geometry/Image.cpp | 38 ++- cpp/tests/t/geometry/PointCloud.cpp | 4 +- .../t/pipelines/odometry/RGBDOdometry.cpp | 8 +- docker/Dockerfile.ci | 2 +- docker/README.md | 2 +- docs/_static/docker/Dockerfile | 2 +- docs/compilation.rst | 18 +- docs/contribute/contribution_recipes.rst | 2 +- docs/contribute/styleguide.rst | 2 +- docs/docker.in.rst | 2 +- docs/getting_started.in.rst | 20 +- docs/release.md | 4 +- .../CMakeLists.txt | 4 +- .../open3d-cmake-find-package/CMakeLists.txt | 17 +- examples/python/visualization/non_english.py | 2 +- python/README.rst | 2 +- python/open3d/__init__.py | 61 +++-- util/check_style.py | 25 +- util/install_deps_ubuntu.sh | 11 +- 55 files changed, 495 insertions(+), 890 deletions(-) rename 3rdparty/{ippicv => ipp}/LICENSE (100%) create mode 100755 3rdparty/ipp/ipp.cmake delete mode 100644 3rdparty/ippicv/CMakeLists.txt delete mode 100755 3rdparty/ippicv/ippicv.cmake delete mode 100644 3rdparty/mkl/0001-Allow-selecttion-of-static-dynamic-MSVC-runtime.patch delete mode 100644 3rdparty/parallelstl/LICENSE delete mode 100644 3rdparty/parallelstl/parallelstl.cmake diff --git a/.clang-format b/.clang-format index 63f78edb55a..e7623066a4d 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,7 @@ BasedOnStyle: Google IndentWidth: 4 ColumnLimit: 80 UseTab: Never -Standard: c++14 +Standard: c++17 ContinuationIndentWidth: 8 AccessModifierOffset: -4 BinPackParameters: false diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c5dab02900f..82c90c2c17a 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -200,10 +200,9 @@ jobs: echo "Add --gtest_random_seed=SEED to the test command to repeat this test sequence." .\bin\${{ matrix.CONFIG }}\tests.exe --gtest_shuffle --gtest_filter=-*ReduceSum64bit2DCase0*:*ReduceSum64bit2DCase3* - name: Linking to Open3D + working-directory: ${{ env.SRC_DIR }}/examples/cmake/open3d-cmake-find-package run: | $ErrorActionPreference = 'Stop' - git clone https://github.com/isl-org/open3d-cmake-find-package.git - cd open3d-cmake-find-package mkdir build cd build cmake -G "Visual Studio 16 2019" -A x64 ` diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index f3f68698bd3..77b7085df69 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -265,9 +265,15 @@ endfunction() # LIBRARIES # the expected library variable names to be found in . # If also defines targets, use them instead and pass them via TARGETS option. +# PATHS +# Paths with hardcoded guesses. Same as in find_package. +# DEPENDS +# Adds targets that should be build before "name" as dependency. # function(open3d_find_package_3rdparty_library name) - cmake_parse_arguments(arg "PUBLIC;HEADER;REQUIRED;QUIET" "PACKAGE;VERSION;PACKAGE_VERSION_VAR" "TARGETS;INCLUDE_DIRS;LIBRARIES" ${ARGN}) + cmake_parse_arguments(arg "PUBLIC;HEADER;REQUIRED;QUIET" + "PACKAGE;VERSION;PACKAGE_VERSION_VAR" + "TARGETS;INCLUDE_DIRS;LIBRARIES;PATHS;DEPENDS" ${ARGN}) if(arg_UNPARSED_ARGUMENTS) message(STATUS "Unparsed: ${arg_UNPARSED_ARGUMENTS}") message(FATAL_ERROR "Invalid syntax: open3d_find_package_3rdparty_library(${name} ${ARGN})") @@ -288,6 +294,9 @@ function(open3d_find_package_3rdparty_library name) if(arg_QUIET) list(APPEND find_package_args "QUIET") endif() + if (arg_PATHS) + list(APPEND find_package_args PATHS ${arg_PATHS} NO_DEFAULT_PATH) + endif() find_package(${arg_PACKAGE} ${find_package_args}) if(${arg_PACKAGE}_FOUND) message(STATUS "Using installed third-party library ${name} ${${arg_PACKAGE}_VERSION}") @@ -319,6 +328,9 @@ function(open3d_find_package_3rdparty_library name) set(Open3D_3RDPARTY_EXTERNAL_MODULES ${Open3D_3RDPARTY_EXTERNAL_MODULES} PARENT_SCOPE) endif() endif() + if(arg_DEPENDS) + add_dependencies(${name} ${arg_DEPENDS}) + endif() set(${name}_FOUND TRUE PARENT_SCOPE) set(${name}_VERSION ${${arg_PACKAGE_VERSION_VAR}} PARENT_SCOPE) add_library(${PROJECT_NAME}::${name} ALIAS ${name}) @@ -424,7 +436,7 @@ function(open3d_import_3rdparty_library name) else() set(HIDDEN 0) endif() - if(arg_GROUPED) + if(arg_GROUPED AND UNIX AND NOT APPLE) target_link_libraries(${name} INTERFACE "-Wl,--start-group") endif() foreach(arg_LIBRARY IN LISTS arg_LIBRARIES) @@ -453,7 +465,7 @@ function(open3d_import_3rdparty_library name) ${OPEN3D_HIDDEN_3RDPARTY_LINK_OPTIONS} PARENT_SCOPE) endif() endforeach() - if(arg_GROUPED) + if(arg_GROUPED AND UNIX AND NOT APPLE) target_link_libraries(${name} INTERFACE "-Wl,--end-group") endif() endif() @@ -1230,6 +1242,10 @@ if(BUILD_GUI) # If the default version is not sufficient, look for some specific versions if(NOT FILAMENT_C_COMPILER OR NOT FILAMENT_CXX_COMPILER) find_program(CLANG_VERSIONED_CC NAMES + clang-19 + clang-18 + clang-17 + clang-16 clang-15 clang-14 clang-13 @@ -1241,6 +1257,10 @@ if(BUILD_GUI) clang-7 ) find_program(CLANG_VERSIONED_CXX NAMES + clang++-19 + clang++-18 + clang++-17 + clang++-16 clang++-15 clang++-14 clang++-13 @@ -1268,7 +1288,7 @@ if(BUILD_GUI) # # On aarch64, the symbolic link path may not work for CMake's # find_library. Therefore, when compiling Filament from source, - # we explicitly find the corresponidng path based on the clang + # we explicitly find the corresponding path based on the clang # version. execute_process(COMMAND ${FILAMENT_CXX_COMPILER} --version OUTPUT_VARIABLE clang_version) if(clang_version MATCHES "clang version ([0-9]+)") @@ -1310,24 +1330,10 @@ if(BUILD_GUI) # We first search for these paths, and then search CMake's default # search path. LLVM version must be >= 7 to compile Filament. if (NOT CLANG_LIBDIR) - set(ubuntu_default_llvm_lib_dirs - /usr/lib/llvm-19/lib - /usr/lib/llvm-18/lib - /usr/lib/llvm-17/lib - /usr/lib/llvm-16/lib - /usr/lib/llvm-15/lib - /usr/lib/llvm-14/lib - /usr/lib/llvm-13/lib - /usr/lib/llvm-12/lib - /usr/lib/llvm-11/lib - /usr/lib/llvm-10/lib - /usr/lib/llvm-9/lib - /usr/lib/llvm-8/lib - /usr/lib/llvm-7/lib - ) - foreach(llvm_lib_dir in ${ubuntu_default_llvm_lib_dirs}) - message(STATUS "Searching ${llvm_lib_dir} for libc++ and libc++abi") - find_library(CPP_LIBRARY c++ PATHS ${llvm_lib_dir} NO_DEFAULT_PATH) + message(STATUS "Searching /usr/lib/llvm-[7..19]/lib/ for libc++ and libc++abi") + foreach(llvm_ver RANGE 7 19) + set(llvm_lib_dir "/usr/lib/llvm-${llvm_ver}/lib") + find_library(CPP_LIBRARY c++ PATHS ${llvm_lib_dir} NO_DEFAULT_PATH) find_library(CPPABI_LIBRARY c++abi PATHS ${llvm_lib_dir} NO_DEFAULT_PATH) if (CPP_LIBRARY AND CPPABI_LIBRARY) set(CLANG_LIBDIR ${llvm_lib_dir}) @@ -1340,6 +1346,7 @@ if(BUILD_GUI) # Fallback to non-ubuntu-default paths. Note that the PATH_SUFFIXES # is not enforced by CMake. if (NOT CLANG_LIBDIR) + message(STATUS "Clang C++ libraries not found. Searching other paths...") find_library(CPPABI_LIBRARY c++abi PATH_SUFFIXES llvm-19/lib llvm-18/lib @@ -1370,8 +1377,7 @@ if(BUILD_GUI) # Ensure that libstdc++ gets linked first. target_link_libraries(3rdparty_filament INTERFACE -lstdc++ ${CPP_LIBRARY} ${CPPABI_LIBRARY}) - message(STATUS "CPP_LIBRARY: ${CPP_LIBRARY}") - message(STATUS "CPPABI_LIBRARY: ${CPPABI_LIBRARY}") + message(STATUS "Filament C++ libraries: ${CPP_LIBRARY} ${CPPABI_LIBRARY}") endif() if (APPLE) find_library(CORE_VIDEO CoreVideo) @@ -1530,34 +1536,14 @@ if(BUILD_SYCL_MODULE) endif() if(BUILD_SYCL_MODULE) - option(OPEN3D_USE_ONEAPI_PACKAGES "Use the oneAPI distribution of MKL/TBB/DPL." ON) + option(OPEN3D_USE_ONEAPI_PACKAGES "Use the oneAPI distribution of MKL/TBB." ON) else() - option(OPEN3D_USE_ONEAPI_PACKAGES "Use the oneAPI distribution of MKL/TBB/DPL." OFF) + option(OPEN3D_USE_ONEAPI_PACKAGES "Use the oneAPI distribution of MKL/TBB." OFF) endif() mark_as_advanced(OPEN3D_USE_ONEAPI_PACKAGES) if(OPEN3D_USE_ONEAPI_PACKAGES) - # 1. oneTBB - # /opt/intel/oneapi/tbb/latest/lib/cmake/tbb - open3d_find_package_3rdparty_library(3rdparty_tbb - PACKAGE TBB - TARGETS TBB::tbb - ) - list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_tbb) - target_compile_definitions(3rdparty_tbb INTERFACE OPEN3D_USE_ONEAPI_PACKAGES=1) - target_compile_definitions(3rdparty_tbb INTERFACE _PSTL_UDR_PRESENT=0) - target_compile_definitions(3rdparty_tbb INTERFACE _PSTL_UDS_PRESENT=0) - # 2. oneDPL - # /opt/intel/oneapi/dpl/latest/lib/cmake/oneDPL - open3d_find_package_3rdparty_library(3rdparty_onedpl - PACKAGE oneDPL - TARGETS oneDPL - ) - target_compile_definitions(3rdparty_onedpl INTERFACE _GLIBCXX_USE_TBB_PAR_BACKEND=0) - target_compile_definitions(3rdparty_onedpl INTERFACE PSTL_USE_PARALLEL_POLICIES=0) - list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_onedpl) - - # 3. oneMKL + # 1. oneMKL # /opt/intel/oneapi/mkl/latest/lib/cmake/mkl set(MKL_THREADING tbb_thread) set(MKL_LINK static) @@ -1576,40 +1562,15 @@ if(OPEN3D_USE_ONEAPI_PACKAGES) target_compile_definitions(3rdparty_mkl INTERFACE OPEN3D_USE_ONEAPI_PACKAGES) list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_mkl) -else() # if(OPEN3D_USE_ONEAPI_PACKAGES) - # TBB - if(USE_SYSTEM_TBB) - open3d_find_package_3rdparty_library(3rdparty_tbb - PACKAGE TBB - TARGETS TBB::tbb - ) - if(NOT 3rdparty_tbb_FOUND) - set(USE_SYSTEM_TBB OFF) - endif() - endif() - if(NOT USE_SYSTEM_TBB) - include(${Open3D_3RDPARTY_DIR}/mkl/tbb.cmake) - open3d_import_3rdparty_library(3rdparty_tbb - INCLUDE_DIRS ${STATIC_TBB_INCLUDE_DIR} - LIB_DIR ${STATIC_TBB_LIB_DIR} - LIBRARIES ${STATIC_TBB_LIBRARIES} - DEPENDS ext_tbb - ) - list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_CUSTOM Open3D::3rdparty_tbb) - else() - list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_tbb) - endif() - - # parallelstl - include(${Open3D_3RDPARTY_DIR}/parallelstl/parallelstl.cmake) - open3d_import_3rdparty_library(3rdparty_parallelstl - PUBLIC - INCLUDE_DIRS ${PARALLELSTL_INCLUDE_DIRS} - INCLUDE_ALL - DEPENDS ext_parallelstl + # 2. oneTBB + # /opt/intel/oneapi/tbb/latest/lib/cmake/tbb + open3d_find_package_3rdparty_library(3rdparty_tbb + PACKAGE TBB + TARGETS TBB::tbb ) - list(APPEND Open3D_3RDPARTY_PUBLIC_TARGETS_FROM_SYSTEM Open3D::3rdparty_parallelstl) + list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_tbb) +else(OPEN3D_USE_ONEAPI_PACKAGES) # MKL/BLAS if(USE_BLAS) if (USE_SYSTEM_BLAS) @@ -1721,6 +1682,7 @@ else() # if(OPEN3D_USE_ONEAPI_PACKAGES) # https://software.intel.com/content/www/us/en/develop/articles/intel-mkl-link-line-advisor.html message(STATUS "Using MKL to support BLAS and LAPACK functionalities.") open3d_import_3rdparty_library(3rdparty_blas + GROUPED HIDDEN INCLUDE_DIRS ${STATIC_MKL_INCLUDE_DIR} LIB_DIR ${STATIC_MKL_LIB_DIR} @@ -1734,7 +1696,25 @@ else() # if(OPEN3D_USE_ONEAPI_PACKAGES) target_compile_definitions(3rdparty_blas INTERFACE "$<$:MKL_ILP64>") list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_CUSTOM Open3D::3rdparty_blas) endif() -endif() # if(OPEN3D_USE_ONEAPI_PACKAGES) + + # TBB + if(USE_SYSTEM_TBB) + open3d_find_package_3rdparty_library(3rdparty_tbb + PACKAGE TBB + TARGETS TBB::tbb + ) + if(NOT 3rdparty_tbb_FOUND) + set(USE_SYSTEM_TBB OFF) + endif() + endif() + if(NOT USE_SYSTEM_TBB) + include(${Open3D_3RDPARTY_DIR}/mkl/tbb.cmake) + list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_CUSTOM TBB::tbb) + else() + list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_tbb) + endif() + +endif(OPEN3D_USE_ONEAPI_PACKAGES) # cuBLAS if(BUILD_CUDA_MODULE) @@ -1847,26 +1827,26 @@ if (BUILD_CUDA_MODULE) endif () # IPP -if (WITH_IPPICV) +if (WITH_IPP) # Ref: https://stackoverflow.com/a/45125525 - set(IPPICV_SUPPORTED_HW AMD64 x86_64 x64 x86 X86 i386 i686) + set(IPP_SUPPORTED_HW AMD64 x86_64 x64) # 32 bit deprecated: x86 X86 i386 i686 # Unsupported: ARM64 aarch64 armv7l armv8b armv8l ... - if (NOT CMAKE_HOST_SYSTEM_PROCESSOR IN_LIST IPPICV_SUPPORTED_HW) - set(WITH_IPPICV OFF) - message(WARNING "IPP-ICV disabled: Unsupported Platform.") + if (NOT CMAKE_HOST_SYSTEM_PROCESSOR IN_LIST IPP_SUPPORTED_HW) + set(WITH_IPP OFF) + message(WARNING "Intel IPP disabled: Unsupported Platform.") else () - include(${Open3D_3RDPARTY_DIR}/ippicv/ippicv.cmake) - if (WITH_IPPICV) - message(STATUS "IPP-ICV ${IPPICV_VERSION_STRING} available. Building interface wrappers IPP-IW.") - open3d_import_3rdparty_library(3rdparty_ippicv + include(${Open3D_3RDPARTY_DIR}/ipp/ipp.cmake) + if (WITH_IPP) + message(STATUS "Using Intel IPP ${IPP_VERSION_STRING}.") + open3d_import_3rdparty_library(3rdparty_ipp HIDDEN - INCLUDE_DIRS ${IPPICV_INCLUDE_DIR} - LIBRARIES ${IPPICV_LIBRARIES} - LIB_DIR ${IPPICV_LIB_DIR} - DEPENDS ext_ippicv + INCLUDE_DIRS ${IPP_INCLUDE_DIR} + LIBRARIES ${IPP_LIBRARIES} + LIB_DIR ${IPP_LIB_DIR} + DEPENDS ext_ipp ) - target_compile_definitions(3rdparty_ippicv INTERFACE ${IPPICV_DEFINITIONS}) - list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_ippicv) + target_compile_definitions(3rdparty_ipp INTERFACE IPP_VERSION_INT=${IPP_VERSION_INT}) + list(APPEND Open3D_3RDPARTY_PRIVATE_TARGETS_FROM_SYSTEM Open3D::3rdparty_ipp) endif() endif() endif () diff --git a/3rdparty/imgui/imgui.cmake b/3rdparty/imgui/imgui.cmake index 12ea3175736..064d227c1f9 100644 --- a/3rdparty/imgui/imgui.cmake +++ b/3rdparty/imgui/imgui.cmake @@ -12,5 +12,5 @@ FetchContent_Declare( INSTALL_COMMAND "" ) -FetchContent_Populate(ext_imgui) +FetchContent_MakeAvailable(ext_imgui) FetchContent_GetProperties(ext_imgui SOURCE_DIR IMGUI_SOURCE_DIR) diff --git a/3rdparty/ippicv/LICENSE b/3rdparty/ipp/LICENSE similarity index 100% rename from 3rdparty/ippicv/LICENSE rename to 3rdparty/ipp/LICENSE diff --git a/3rdparty/ipp/ipp.cmake b/3rdparty/ipp/ipp.cmake new file mode 100755 index 00000000000..ffc9b320512 --- /dev/null +++ b/3rdparty/ipp/ipp.cmake @@ -0,0 +1,60 @@ +# Output variables: +# - IPP_INCLUDE_DIR +# - IPP_LIBRARIES +# - IPP_LIB_DIR +# - IPP_VERSION_STRING +# - IPP_VERSION_INT (for version check) + +include(ExternalProject) + +# These archives are created from the pip wheels for ipp-static, ipp-devel and +# ipp-include and excluding the shared libraries to reduce download size. +# Check in order APPLE -> WIN32 -> UNIX, since UNIX may be defined on APPLE / WIN32 as well +set(IPP_VERSION_STRING "2021.11.0") # From ipp/ippversion.h +set(IPP_VERSION_INT 20211100) +if(APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64) + set(IPP_VERSION_STRING "2021.9.1") # From ipp/ippversion.h + set(IPP_VERSION_INT 20210901) + set(IPP_URL "https://github.com/isl-org/open3d_downloads/releases/download/mkl-static-2024.1/ipp_static-2021.9.1-macosx_10_15_x86_64.tar.xz") + set(IPP_HASH "f27e45da604a1f6d1d2a747a0f67ffafeaff084b0f860a963d8c3996e2f40bb3") +elseif(WIN32 AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL AMD64) + set(IPP_URL "https://github.com/isl-org/open3d_downloads/releases/download/mkl-static-2024.1/ipp_static-2021.11.0-win_amd64.zip") + set(IPP_HASH "69e8a7dc891609de6fea478a67659d2f874d12b51a47bd2e3e5a7c4c473c53a6") +elseif(UNIX AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64) + set(IPP_URL "https://github.com/isl-org/open3d_downloads/releases/download/mkl-static-2024.1/ipp_static-2021.11.0-linux_x86_64.tar.xz") + set(IPP_HASH "51f33fd5bf5011e9eae0e034e5cc70a7c0ac0ba93d6a3f66fd7e145cf1a5e30b") +else() + set(WITH_IPP OFF) + message(FATAL_ERROR "Intel IPP disabled: Unsupported Platform.") + return() +endif() + +if(WIN32) + set(IPP_SUBPATH "Library/") +else() + set(IPP_SUBPATH "") +endif() + +ExternalProject_Add(ext_ipp + PREFIX ipp + URL ${IPP_URL} + URL_HASH SHA256=${IPP_HASH} + DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/ipp" + # Copy all libs from lib/tl/tbb to lib/ since Open3D cmake scripts only support one LIB_DIR per dependency + UPDATE_COMMAND ${CMAKE_COMMAND} -E copy_directory /${IPP_SUBPATH}lib/tl/tbb/ /${IPP_SUBPATH}lib/ + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + ) +ExternalProject_Get_Property(ext_ipp SOURCE_DIR) +set(IPP_INCLUDE_DIR "${SOURCE_DIR}/${IPP_SUBPATH}include/") +# Threading layer libs must be linked first. +# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/ipp-performace-benefits-with-tl-functions.html +# Library dependency order: +# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/library-dependencies-by-domain.html +if (WIN32) + set(IPP_LIBRARIES ipp_iw ippcvmt_tl_tbb ippcvmt ippimt_tl_tbb ippimt ippccmt_tl_tbb ippccmt ippsmt ippvmmt ippcoremt_tl_tbb ippcoremt) +else() + set(IPP_LIBRARIES ipp_iw ippcv_tl_tbb ippcv ippi_tl_tbb ippi ippcc_tl_tbb ippcc ipps ippvm ippcore_tl_tbb ippcore) +endif() +set(IPP_LIB_DIR "${SOURCE_DIR}/${IPP_SUBPATH}lib") \ No newline at end of file diff --git a/3rdparty/ippicv/CMakeLists.txt b/3rdparty/ippicv/CMakeLists.txt deleted file mode 100644 index a5780e29438..00000000000 --- a/3rdparty/ippicv/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Adapted from https://github.com/opencv/opencv/blob/master/3rdparty/ippicv/CMakeLists.txt -cmake_minimum_required(VERSION 3.0) -project(ippiw) - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -file(GLOB lib_srcs iw/src/*.c) -file(GLOB lib_hdrs iw/include/*.h iw/include/iw/*.h iw/include/iw++/*.hpp) -file(GLOB IPPICV_LIBRARY - icv/lib/*/${CMAKE_STATIC_LIBRARY_PREFIX}*${CMAKE_STATIC_LIBRARY_SUFFIX}) - -add_library(ippiw STATIC ${lib_srcs} ${lib_hdrs}) -target_compile_definitions(ippiw PRIVATE IW_BUILD ICV_BASE) -target_include_directories(ippiw PRIVATE icv/include iw/include) -target_compile_definitions(ippiw PRIVATE IW_ENABLE_iwiResize_Nearest=1) - -if(UNIX) - if(${CMAKE_C_COMPILER_ID} MATCHES GNU OR - ${CMAKE_C_COMPILER_ID} MATCHES Clang OR - ${CMAKE_C_COMPILER_ID} MATCHES Intel) - target_compile_options(ippiw PRIVATE -Wno-unused-function -Wno-missing-braces -Wno-missing-field-initializers) - endif() - if(${CMAKE_C_COMPILER_ID} MATCHES Clang) - target_compile_options(ippiw PRIVATE -Wno-self-assign) - endif() -endif() - -install(TARGETS ippiw - ARCHIVE DESTINATION lib) -install(FILES ${IPPICV_LIBRARY} - DESTINATION lib) -install(DIRECTORY iw/include/ DESTINATION include - FILES_MATCHING PATTERN "*.h*") -install(DIRECTORY icv/include/ DESTINATION include/icv - FILES_MATCHING PATTERN "*.h*") diff --git a/3rdparty/ippicv/ippicv.cmake b/3rdparty/ippicv/ippicv.cmake deleted file mode 100755 index e889378f47a..00000000000 --- a/3rdparty/ippicv/ippicv.cmake +++ /dev/null @@ -1,57 +0,0 @@ -# Adapted from: https://github.com/opencv/opencv/blob/master/3rdparty/ippicv/ippicv.cmake -# Downloads IPPICV libraries from the OpenCV 3rd party repo - -include(ExternalProject) - -# Commit SHA in the opencv_3rdparty repo -set(IPPICV_COMMIT "a56b6ac6f030c312b2dce17430eef13aed9af274") -# Check in order APPLE -> WIN32 -> UNIX, since UNIX may be defined on APPLE / -# WIN32 as well -if(APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64) - set(OPENCV_ICV_NAME "ippicv_2020_mac_intel64_20191018_general.tgz") - set(OPENCV_ICV_HASH "3a39aad1eef2f6019dda9555f6db2d34063c1e464dc0e498eaa0c6b55817f2fe") -elseif(WIN32 AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL AMD64) - set(OPENCV_ICV_NAME "ippicv_2020_win_intel64_20191018_general.zip") - set(OPENCV_ICV_HASH "e64e09f8a2e121d4fff440fb12b1298bc0760f1391770aefe5d1deb6630352b7") -elseif(WIN32 AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES 86) # {x86, i386, i686} - set(OPENCV_ICV_NAME "ippicv_2020_win_ia32_20191018_general.zip") - set(OPENCV_ICV_HASH "c1e0e26f32aec4374df05a145cfac09774e15f9b53f0bdfaac3eca3205db6106") -elseif(UNIX AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64) - set(OPENCV_ICV_NAME "ippicv_2020_lnx_intel64_20191018_general.tgz") - set(OPENCV_ICV_HASH "08627fa5660d52d59309a572dd7db5b9c8aea234cfa5aee0942a1dd903554246") -elseif(UNIX AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES 86) # {x86, i386, i686} - set(OPENCV_ICV_NAME "ippicv_2020_lnx_ia32_20191018_general.tgz") - set(OPENCV_ICV_HASH "acf8976ddea689b6d8c9640d6cfa6852d5c74c1fc368b57029b13ed7714fbd95") -else() - set(WITH_IPPICV OFF) - message(FATAL_ERROR "IPP-ICV disabled: Unsupported Platform.") - return() -endif() - -if(WIN32) - set(lib_name ippicvmt) -else() - set(lib_name ippicv) -endif() - -ExternalProject_Add(ext_ippicv - PREFIX ippicv - URL https://raw.githubusercontent.com/opencv/opencv_3rdparty/${IPPICV_COMMIT}/ippicv/${OPENCV_ICV_NAME} - URL_HASH SHA256=${OPENCV_ICV_HASH} - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/ippicv" - UPDATE_COMMAND "" - PATCH_COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/ippicv/CMakeLists.txt - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX= - ${ExternalProject_CMAKE_ARGS_hidden} - BUILD_BYPRODUCTS - /lib/${CMAKE_STATIC_LIBRARY_PREFIX}ippiw${CMAKE_STATIC_LIBRARY_SUFFIX} - /lib/${CMAKE_STATIC_LIBRARY_PREFIX}${lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX} - ) - -ExternalProject_Get_Property(ext_ippicv INSTALL_DIR) -set(IPPICV_INCLUDE_DIR "${INSTALL_DIR}/include/icv/" "${INSTALL_DIR}/include/") -set(IPPICV_LIBRARIES ippiw ${lib_name}) -set(IPPICV_LIB_DIR "${INSTALL_DIR}/lib") -set(IPPICV_VERSION_STRING "2020.0.0 Gold") # From icv/ippversion.h diff --git a/3rdparty/mkl/0001-Allow-selecttion-of-static-dynamic-MSVC-runtime.patch b/3rdparty/mkl/0001-Allow-selecttion-of-static-dynamic-MSVC-runtime.patch deleted file mode 100644 index 9dc35dd1455..00000000000 --- a/3rdparty/mkl/0001-Allow-selecttion-of-static-dynamic-MSVC-runtime.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 92e3a4717f888c9e97e22f9e7e83fc15c8aa15ae Mon Sep 17 00:00:00 2001 -From: Yixing Lao -Date: Mon, 14 Sep 2020 03:30:00 -0700 -Subject: [PATCH] Allow selecttion of static/dynamic MSVC runtime - ---- - CMakeLists.txt | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index e05f27a..5a0c680 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -1,5 +1,15 @@ - cmake_minimum_required(VERSION 3.1 FATAL_ERROR) - -+##### begin open3d patch -+if(POLICY CMP0091) -+ cmake_policy(SET CMP0091 NEW) -+endif() -+option(STATIC_WINDOWS_RUNTIME "Use static (MT/MTd) Windows runtime" OFF) -+if(STATIC_WINDOWS_RUNTIME) -+ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -+endif() -+##### end open3d patch -+ - if (POLICY CMP0048) - # cmake warns if loaded from a min-3.0-required parent dir, so silence the warning: - cmake_policy(SET CMP0048 NEW) --- -2.27.0.windows.1 - diff --git a/3rdparty/mkl/mkl.cmake b/3rdparty/mkl/mkl.cmake index 6a8a923717b..bff63b0faa5 100644 --- a/3rdparty/mkl/mkl.cmake +++ b/3rdparty/mkl/mkl.cmake @@ -7,52 +7,28 @@ # # The name "STATIC" is used to avoid naming collisions for other 3rdparty CMake # files (e.g. PyTorch) that also depends on MKL. -# FIXME: anaconda.org URLs don't work anymore. include(ExternalProject) +# These files are created from the pip MKL devel packages, and only contain +# headers, static libraries, and cmake export files. Shared libraries are +# excluded to reduce download size. Alternately, use: +# pip download -d mkl_static/win_amd64 --platform win_amd64 --no-deps mkl-include==2024.1 mkl-devel==2024.1 mkl-static==2024.1 +# pip download -d mkl_static/linux_x86_64 --platform manylinux1_x86_64 --no-deps mkl-include==2024.1 mkl-devel==2024.1 mkl-static==2024.1 +# pip download -d mkl_static/macosx_x86_64 --platform macosx_11_0_x86_64 --no-deps mkl-include==2023.2.2 mkl-devel==2023.2.2 mkl-static==2023.2.2 +# Extract all files: +# cd mkl_static/win_amd64 && for whl in *.whl; do wheel unpack $whl; done; +# Arrange in the standard layout: bin, include, lib (cmake, pkgconfig), share (cmake) +# Archive and upload to GitHub releases open3d_downloads. if(WIN32) - set(MKL_INCLUDE_URL - https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-include-2020.1-intel_216-win-64.tar.bz2 - https://anaconda.org/intel/mkl-include/2020.1/download/win-64/mkl-include-2020.1-intel_216.tar.bz2 - ) - set(MKL_INCLUDE_SHA256 65cedb770358721fd834224cd8be1fe1cc10b37ef2a1efcc899fc2fefbeb5b31) - - set(MKL_URL - https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-static-2020.1-intel_216-win-64.tar.bz2 - https://anaconda.org/intel/mkl-static/2020.1/download/win-64/mkl-static-2020.1-intel_216.tar.bz2 - ) - set(MKL_SHA256 c6f037aa9e53501d91d5245b6e65020399ebf34174cc4d03637818ebb6e6b6b9) + set(MKL_URL https://github.com/isl-org/open3d_downloads/releases/download/mkl-static-2024.1/mkl_static-2024.1.0-win_amd64.zip) + set(MKL_SHA256 524de5395db5b7a9d9f0d9a76b2223c6edac429d4492c6a1cc79a5c22c4f3346) elseif(APPLE) - set(MKL_INCLUDE_URL - https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-include-2020.1-intel_216-osx-64.tar.bz2 - https://anaconda.org/intel/mkl-include/2020.1/download/osx-64/mkl-include-2020.1-intel_216.tar.bz2 - ) - set(MKL_INCLUDE_SHA256 d4d025bd17ce75b92c134f70759b93ae1dee07801d33bcc59e40778003f05de5) - - set(MKL_URL - https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-static-2020.1-intel_216-osx-64.tar.bz2 - https://anaconda.org/intel/mkl-static/2020.1/download/osx-64/mkl-static-2020.1-intel_216.tar.bz2 - ) - set(MKL_SHA256 ca94ab8933cf58cbb7b42ac1bdc8671a948490fd1e0e9cea71a5b4d613b21be4) + set(MKL_URL https://github.com/isl-org/open3d_downloads/releases/download/mkl-static-2024.1/mkl_static-2023.2.2.9-macosx_x86_64.tar.xz) + set(MKL_SHA256 6cd93bf1d37527d3ab3657e22c1a8a409729d6c6f422c7c381c7a145aa588d6c) else() - set(MKL_INCLUDE_URL - https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-include-2020.1-intel_217-linux-64.tar.bz2 - https://anaconda.org/intel/mkl-include/2020.1/download/linux-64/mkl-include-2020.1-intel_217.tar.bz2 - ) - set(MKL_INCLUDE_SHA256 c0c4e7f261aa9182d811b91132c622211e55a5f3dfb8afb65a5377804f39eb61) - - set(MKL_URL - https://github.com/isl-org/Open3D/releases/download/v0.12.0/mkl-static-2020.1-intel_217-linux-64.tar.bz2 - https://anaconda.org/intel/mkl-static/2020.1/download/linux-64/mkl-static-2020.1-intel_217.tar.bz2 - ) - set(MKL_SHA256 44fe60fa895c8967fe7c70fd1b680700f23ecac6ae038b267aa0a0c48dce3d59) - - # URL for merged libmkl_merged.a for Ubuntu. - set(MKL_MERGED_URL - https://github.com/isl-org/Open3D/releases/download/v0.10.0/linux-merged-mkl-static-2020.1-intel_217.zip - ) - set(MKL_MERGED_SHA256 027c2b0d89c554479edbe5faecb93c26528877c1b682f939f8e1764d96860064) + set(MKL_URL https://github.com/isl-org/open3d_downloads/releases/download/mkl-static-2024.1/mkl_static-2024.1.0-linux_x86_64.tar.xz) + set(MKL_SHA256 f37c9440e3d664d21889a4607effcd47472bcce347da6c2bfc7aae991971b499) endif() # Where MKL and TBB headers and libs will be installed. @@ -62,17 +38,6 @@ set(STATIC_MKL_INCLUDE_DIR "${MKL_INSTALL_PREFIX}/include/") set(STATIC_MKL_LIB_DIR "${MKL_INSTALL_PREFIX}/${Open3D_INSTALL_LIB_DIR}") if(WIN32) - ExternalProject_Add( - ext_mkl_include - PREFIX mkl_include - URL ${MKL_INCLUDE_URL} - URL_HASH SHA256=${MKL_INCLUDE_SHA256} - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/mkl" - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /Library/include ${MKL_INSTALL_PREFIX}/include - ) ExternalProject_Add( ext_mkl PREFIX mkl @@ -83,6 +48,7 @@ if(WIN32) CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /Library/lib ${STATIC_MKL_LIB_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_directory /Library/include ${MKL_INSTALL_PREFIX}/include BUILD_BYPRODUCTS ${STATIC_MKL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}mkl_intel_ilp64${CMAKE_STATIC_LIBRARY_SUFFIX} ${STATIC_MKL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}mkl_core${CMAKE_STATIC_LIBRARY_SUFFIX} @@ -97,23 +63,10 @@ if(WIN32) mkl_core mkl_sequential mkl_tbb_thread - tbb_static - ) - list(REMOVE_ITEM MKL_LIBRARIES "$<$:mkl_tbb_thread>") - list(REMOVE_ITEM MKL_LIBRARIES "$<$:tbb_static>") - list(REMOVE_ITEM MKL_LIBRARIES "$<$:mkl_sequential>") -elseif(APPLE) - ExternalProject_Add( - ext_mkl_include - PREFIX mkl_include - URL ${MKL_INCLUDE_URL} - URL_HASH SHA256=${MKL_INCLUDE_SHA256} - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/mkl" - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${MKL_INSTALL_PREFIX}/include ) + list(REMOVE_ITEM STATIC_MKL_LIBRARIES "$<$:mkl_tbb_thread>") + list(REMOVE_ITEM STATIC_MKL_LIBRARIES "$<$:mkl_sequential>") +else() ExternalProject_Add( ext_mkl PREFIX mkl @@ -124,75 +77,11 @@ elseif(APPLE) CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /lib ${STATIC_MKL_LIB_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${MKL_INSTALL_PREFIX}/include BUILD_BYPRODUCTS ${STATIC_MKL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}mkl_intel_ilp64${CMAKE_STATIC_LIBRARY_SUFFIX} ${STATIC_MKL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}mkl_tbb_thread${CMAKE_STATIC_LIBRARY_SUFFIX} ${STATIC_MKL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}mkl_core${CMAKE_STATIC_LIBRARY_SUFFIX} ) - set(STATIC_MKL_LIBRARIES mkl_intel_ilp64 mkl_tbb_thread mkl_core tbb_static) -else() - ExternalProject_Add( - ext_mkl_include - PREFIX mkl_include - URL ${MKL_INCLUDE_URL} - URL_HASH SHA256=${MKL_INCLUDE_SHA256} - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/mkl" - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${MKL_INSTALL_PREFIX}/include - ) - option(USE_LINUX_MKL_FROM_CONDA_REPO "On linux, use MKL from official conda repo" OFF) - if(USE_LINUX_MKL_FROM_CONDA_REPO) - # Resolving static library circular dependencies. - # - Approach 1: Add `-Wl,--start-group` `-Wl,--end-group` around, but this - # is not friendly with CMake. - # - Approach 2: Set LINK_INTERFACE_MULTIPLICITY to 3. However this does not - # work directly with interface library, and requires big - # changes to the build system. See discussions in: - # - https://gitlab.kitware.com/cmake/cmake/-/issues/17964 - # - https://gitlab.kitware.com/cmake/cmake/-/issues/18415 - # - https://stackoverflow.com/q/50166553/1255535 - # - Approach 3: Merge libmkl_intel_ilp64.a, libmkl_tbb_thread.a and - # libmkl_core.a into libmkl_merged.a. This is the most simple - # approach to integrate with the build system. However, extra - # time is required to merge the libraries and the merged - # library size can be large. We choose to use approach 3. - ExternalProject_Add( - ext_mkl - PREFIX mkl - URL ${MKL_URL} - URL_HASH SHA256=${MKL_SHA256} - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/mkl" - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_IN_SOURCE ON - BUILD_COMMAND echo "Extracting static libs..." - COMMAND ar x lib/libmkl_intel_ilp64.a - COMMAND ar x lib/libmkl_tbb_thread.a - COMMAND ar x lib/libmkl_core.a - COMMAND echo "Merging static libs..." - COMMAND bash -c "ar -qc lib/libmkl_merged.a *.o" - COMMAND echo "Cleaning up *.o files..." - COMMAND bash -c "rm *.o" - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy lib/libmkl_merged.a ${STATIC_MKL_LIB_DIR}/libmkl_merged.a - BUILD_BYPRODUCTS ${STATIC_MKL_LIB_DIR}/libmkl_merged.a - ) - else() - # We also provide a direct download for libmkl_merged.a. - ExternalProject_Add( - ext_mkl - PREFIX mkl - URL ${MKL_MERGED_URL} - URL_HASH SHA256=${MKL_MERGED_SHA256} - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/mkl" - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_IN_SOURCE ON - BUILD_COMMAND "" - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy lib/libmkl_merged.a ${STATIC_MKL_LIB_DIR}/libmkl_merged.a - BUILD_BYPRODUCTS ${STATIC_MKL_LIB_DIR}/libmkl_merged.a - ) - endif() - set(STATIC_MKL_LIBRARIES mkl_merged tbb_static) + set(STATIC_MKL_LIBRARIES mkl_intel_ilp64 mkl_tbb_thread mkl_core) endif() diff --git a/3rdparty/mkl/tbb.cmake b/3rdparty/mkl/tbb.cmake index b87ef79fe3b..385af4b644b 100644 --- a/3rdparty/mkl/tbb.cmake +++ b/3rdparty/mkl/tbb.cmake @@ -1,50 +1,40 @@ # TBB build scripts. -# -# - STATIC_TBB_INCLUDE_DIR -# - STATIC_TBB_LIB_DIR -# - STATIC_TBB_LIBRARIES -# -# Notes: -# The name "STATIC" is used to avoid naming collisions for other 3rdparty CMake -# files (e.g. PyTorch) that also depends on MKL. -include(ExternalProject) +include(FetchContent) +cmake_policy(SET CMP0077 NEW) # Where MKL and TBB headers and libs will be installed. # This needs to be consistent with mkl.cmake. set(MKL_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/mkl_install) -set(STATIC_MKL_INCLUDE_DIR "${MKL_INSTALL_PREFIX}/include/") +set(STATIC_MKL_INCLUDE_DIR "${MKL_INSTALL_PREFIX}/${Open3D_INSTALL_INCLUDE_DIR}/") set(STATIC_MKL_LIB_DIR "${MKL_INSTALL_PREFIX}/${Open3D_INSTALL_LIB_DIR}") -# TBB variables exported for PyTorch Ops and TensorFlow Ops -set(STATIC_TBB_INCLUDE_DIR "${STATIC_MKL_INCLUDE_DIR}") -set(STATIC_TBB_LIB_DIR "${STATIC_MKL_LIB_DIR}") -set(STATIC_TBB_LIBRARIES tbb_static tbbmalloc_static) - -find_package(Git QUIET REQUIRED) - -ExternalProject_Add( +# Save and restore BUILD_SHARED_LIBS since TBB must be built as a shared library +set(_build_shared_libs ${BUILD_SHARED_LIBS}) +set(BUILD_SHARED_LIBS ON) +set(_win_exp_all_syms ${CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS}) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) # ON interferes with TBB symbols +FetchContent_Declare( ext_tbb - PREFIX tbb - URL https://github.com/wjakob/tbb/archive/141b0e310e1fb552bdca887542c9c1a8544d6503.tar.gz # Sept 2020 - URL_HASH SHA256=bb29b76eabf7549660e3dba2feb86ab501469432a15fb0bf2c21e24d6fbc4c72 + URL https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2021.12.0.tar.gz # April 2024 + URL_HASH SHA256=c7bb7aa69c254d91b8f0041a71c5bcc3936acb64408a1719aec0b2b7639dd84f DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/tbb" - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} init - COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace - ${CMAKE_CURRENT_LIST_DIR}/0001-Allow-selecttion-of-static-dynamic-MSVC-runtime.patch - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX=${MKL_INSTALL_PREFIX} - -DSTATIC_WINDOWS_RUNTIME=${STATIC_WINDOWS_RUNTIME} - -DTBB_BUILD_TBBMALLOC=ON - -DTBB_BUILD_TBBMALLOC_PROXYC=OFF - -DTBB_BUILD_SHARED=OFF - -DTBB_BUILD_STATIC=ON - -DTBB_BUILD_TESTS=OFF - -DTBB_INSTALL_ARCHIVE_DIR=${Open3D_INSTALL_LIB_DIR} - -DTBB_CMAKE_PACKAGE_INSTALL_DIR=${Open3D_INSTALL_LIB_DIR}/cmake/tbb - ${ExternalProject_CMAKE_ARGS_hidden} - BUILD_BYPRODUCTS - ${STATIC_TBB_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}tbb_static${CMAKE_STATIC_LIBRARY_SUFFIX} - ${STATIC_TBB_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}tbbmalloc_static${CMAKE_STATIC_LIBRARY_SUFFIX} +) +set(TBBMALLOC_BUILD OFF CACHE BOOL "Enable tbbmalloc build.") +set(TBBMALLOC_PROXY_BUILD OFF CACHE BOOL "Enable tbbmalloc_proxy build.") +set(TBB_TEST OFF CACHE BOOL "Build TBB tests.") +set(TBB_INSTALL OFF CACHE BOOL "Enable installation") +set(TBB_STRICT OFF CACHE BOOL "Treat compiler warnings as errors") +FetchContent_MakeAvailable(ext_tbb) +set(BUILD_SHARED_LIBS ${_build_shared_libs}) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ${_win_exp_all_syms}) + +# TBB is built and linked as a shared library - this is different from all other Open3D dependencies. +install(TARGETS tbb EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${Open3D_INSTALL_LIB_DIR} # Windows .lib files + COMPONENT tbb + LIBRARY DESTINATION ${Open3D_INSTALL_LIB_DIR} + COMPONENT tbb + RUNTIME DESTINATION ${Open3D_INSTALL_BIN_DIR} + COMPONENT tbb ) diff --git a/3rdparty/parallelstl/LICENSE b/3rdparty/parallelstl/LICENSE deleted file mode 100644 index bd8b243dfa0..00000000000 --- a/3rdparty/parallelstl/LICENSE +++ /dev/null @@ -1,218 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - ---- LLVM Exceptions to the Apache 2.0 License ---- - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you -may redistribute such embedded portions in such Object form without complying -with the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a -court of competent jurisdiction determines that the patent provision (Section -3), the indemnity provision (Section 9) or other Section of the License -conflicts with the conditions of the GPLv2, you may retroactively and -prospectively choose to deem waived or otherwise exclude such Section(s) of -the License, but only in their entirety and only with respect to the Combined -Software. diff --git a/3rdparty/parallelstl/parallelstl.cmake b/3rdparty/parallelstl/parallelstl.cmake deleted file mode 100644 index 981b564fcbc..00000000000 --- a/3rdparty/parallelstl/parallelstl.cmake +++ /dev/null @@ -1,16 +0,0 @@ -include(ExternalProject) - -ExternalProject_Add( - ext_parallelstl - PREFIX parallelstl - URL https://github.com/oneapi-src/oneDPL/archive/refs/tags/20190522.tar.gz - URL_HASH SHA256=40d78c3405a42f781348b5bc9038cb0ce1147591e07fca7329538c9842d36a7b - DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/parallelstl" - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" -) - -ExternalProject_Get_Property(ext_parallelstl SOURCE_DIR) -set(PARALLELSTL_INCLUDE_DIRS ${SOURCE_DIR}/include/) # "/" is critical. diff --git a/3rdparty/qhull/qhull.cmake b/3rdparty/qhull/qhull.cmake index fe15e83ca6f..3a02500dfe8 100644 --- a/3rdparty/qhull/qhull.cmake +++ b/3rdparty/qhull/qhull.cmake @@ -8,10 +8,6 @@ FetchContent_Declare( URL_HASH SHA256=8774e9a12c70b0180b95d6b0b563c5aa4bea8d5960c15e18ae3b6d2521d64f8b DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/qhull" - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" ) FetchContent_Populate(ext_qhull) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc76ee40176..dc2bdc62e15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ endif() option(BUILD_COMMON_ISPC_ISAS "Build for common ISPC ISAs (for release)" OFF) option(BUILD_GUI "Builds new GUI" ON ) option(WITH_OPENMP "Use OpenMP multi-threading" ON ) -option(WITH_IPPICV "Use Intel Performance Primitives" ON ) +option(WITH_IPP "Use Intel Integrated Performance Primitives" ON ) option(ENABLE_HEADLESS_RENDERING "Use OSMesa for headless rendering" OFF) if(BUILD_SHARED_LIBS) option(STATIC_WINDOWS_RUNTIME "Use static (MT/MTd) Windows runtime" OFF) @@ -253,6 +253,8 @@ string(CONCAT OPEN3D_VERSION ) set(OPEN3D_VERSION_FULL "${OPEN3D_VERSION}${OPEN3D_VERSION_DEVHASH}" CACHE STRING "Open3D full version.") +set(OPEN3D_ABI_VERSION "${OPEN3D_VERSION_MAJOR}.${OPEN3D_VERSION_MINOR}" CACHE + STRING "Open3D ABI version / SOVERSION (for releases only).") # Set additional info set(PROJECT_EMAIL "open3d@intel.com") set(PROJECT_DOCS "https://www.open3d.org/docs") @@ -292,28 +294,11 @@ endif() # Global flag to set CXX standard. # This does not affect 3rd party libraries. -# Tensorflow 2.9+ requires cxx_17, but MSVC 19.29 throws errors with C++17 -# enabled. -if (BUILD_SYCL_MODULE OR BUILD_TENSORFLOW_OPS) - set(CMAKE_CXX_STANDARD 17) -else() - set(CMAKE_CXX_STANDARD 14) -endif() +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) # Improved compatibility -# FIXME: Remove this workaround once a fixed Visual Studio 16.10 version is released. -if (BUILD_CUDA_MODULE - AND CMAKE_CXX_COMPILER MATCHES "MSVC" - AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19.29" -) - # Keep C++14 standard for unaffected C++ files, but use C++17 for CUDA files. - set(CMAKE_CUDA_STANDARD 17) - # Suppress warnings for deprecated C++17 functions. - add_compile_definitions($<$:_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING>) - message(WARNING "Visual Studio 16.10 (MSVC 19.29) introduced a compiler bug when compiling CUDA code with C++14. " - "Workaround this bug by setting the CUDA standard to C++17.") -endif() - +# Suppress warnings for deprecated C++17 functions (stdgpu->thrust with CUDA 11 for MSVC). +add_compile_definitions($<$:_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING>) # CMake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/cmake") @@ -436,6 +421,7 @@ if(BUILD_CUDA_MODULE) endif() endif() enable_language(CUDA) + set(CMAKE_CUDA_STANDARD 17) if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS "10.1") message(FATAL_ERROR "CUDA 10.0 and older are not supported. Please upgrade to CUDA 10.1 or newer.") endif() @@ -519,7 +505,7 @@ endmacro() if (LINUX_AARCH64) # Fix for ImportError: ... /pybind.cpython-310-aarch64-linux-gnu.so: cannot allocate memory in static TLS block # https://bugs.launchpad.net/ubuntu/+source/mysql-8.0/+bug/1889851 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftls-model=global-dynamic") + add_compile_options("-ftls-model=global-dynamic") endif() # Include convenience functions diff --git a/README.md b/README.md index c024b8b1689..d559c76c26d 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ For more, please visit the [Open3D documentation](https://www.open3d.org/docs). ## Python quick start -Pre-built pip packages support Ubuntu 18.04+, macOS 10.15+ and Windows 10+ +Pre-built pip packages support Ubuntu 20.04+, macOS 10.15+ and Windows 10+ (64-bit) with Python 3.8-3.11. ```bash diff --git a/cmake/Open3DPrintConfigurationSummary.cmake b/cmake/Open3DPrintConfigurationSummary.cmake index c8e8a891a8b..3e3d42da11e 100644 --- a/cmake/Open3DPrintConfigurationSummary.cmake +++ b/cmake/Open3DPrintConfigurationSummary.cmake @@ -61,7 +61,7 @@ function(open3d_print_configuration_summary) GLFW googletest imgui - ippicv + ipp JPEG jsoncpp liblzf diff --git a/cmake/Open3DSetGlobalProperties.cmake b/cmake/Open3DSetGlobalProperties.cmake index 251f046ca70..03e43212a8b 100644 --- a/cmake/Open3DSetGlobalProperties.cmake +++ b/cmake/Open3DSetGlobalProperties.cmake @@ -25,12 +25,24 @@ function(open3d_enable_strip target) endif() endfunction() +# RPATH handling (for TBB DSO). Check current folder, one folder above and the lib sibling folder +set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) +if (APPLE) +# Add options to cover the various ways in which open3d shaed lib or apps can be installed wrt TBB DSO + set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/../;@loader_path/../lib/") +# pybind with open3d shared lib is copied, not cmake-installed, so we need to add .. to build rpath + set(CMAKE_BUILD_RPATH "@loader_path/../") +elseif(UNIX) + set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/../;$ORIGIN/../lib/") + set(CMAKE_BUILD_RPATH "$ORIGIN/../") +endif() + # open3d_set_global_properties(target) # # Sets important project-related properties to . function(open3d_set_global_properties target) - # Tell CMake we want a compiler that supports C++14 features - target_compile_features(${target} PUBLIC cxx_std_14) + # Tell CMake we want a compiler that supports C++17 features + target_compile_features(${target} PUBLIC cxx_std_17) # Detect compiler id and version for utility::CompilerInfo # - OPEN3D_CXX_STANDARD @@ -118,8 +130,8 @@ function(open3d_set_global_properties target) if (USE_BLAS) target_compile_definitions(${target} PRIVATE USE_BLAS) endif() - if (WITH_IPPICV) - target_compile_definitions(${target} PRIVATE WITH_IPPICV) + if (WITH_IPP) + target_compile_definitions(${target} PRIVATE WITH_IPP) endif() if (GLIBCXX_USE_CXX11_ABI) target_compile_definitions(${target} PUBLIC _GLIBCXX_USE_CXX11_ABI=1) @@ -183,10 +195,6 @@ function(open3d_set_global_properties target) target_compile_options(${target} PRIVATE $<$,$>>:-fno-fast-math>) - # TBB static version is used - # See: https://github.com/wjakob/tbb/commit/615d690c165d68088c32b6756c430261b309b79c - target_compile_definitions(${target} PRIVATE __TBB_LIB_NAME=tbb_static) - # Enable strip open3d_enable_strip(${target}) @@ -194,4 +202,5 @@ function(open3d_set_global_properties target) target_compile_options(${target} PRIVATE "$<$:${HARDENING_CFLAGS}>") target_link_options(${target} PRIVATE "$<$:${HARDENING_LDFLAGS}>") target_compile_definitions(${target} PRIVATE "$<$:${HARDENING_DEFINITIONS}>") + endfunction() diff --git a/cpp/apps/CMakeLists.txt b/cpp/apps/CMakeLists.txt index 8b718c9968c..9a3cdea35bb 100644 --- a/cpp/apps/CMakeLists.txt +++ b/cpp/apps/CMakeLists.txt @@ -116,7 +116,7 @@ macro(open3d_add_app_common SRC_DIR APP_NAME TARGET_NAME) MACOSX_BUNDLE_INFO_PLIST "${INFO_PLIST}" XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" # disable OUTPUT_NAME ${APP_NAME} - ) + INSTALL_RPATH "@loader_path;@loader_path/../lib/") elseif (WIN32) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../${APP_NAME}") add_executable(${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES}) @@ -125,10 +125,10 @@ macro(open3d_add_app_common SRC_DIR APP_NAME TARGET_NAME) add_executable(${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES}) set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME} - ) + INSTALL_RPATH "$ORIGIN;$ORIGIN/../lib/") endif() - target_link_libraries(${TARGET_NAME} PRIVATE Open3D::Open3D ${ARGN}) + target_link_libraries(${TARGET_NAME} PRIVATE Open3D::Open3D TBB::tbb ${ARGN}) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "apps") open3d_link_3rdparty_libraries(${TARGET_NAME}) diff --git a/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt b/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt index b7bce43f4c6..bb96becca4d 100644 --- a/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt +++ b/cpp/apps/Open3DViewer/Debian/CMakeLists.in.txt @@ -33,7 +33,7 @@ set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Open3D Viewer for 3D files") set(CPACK_PACKAGE_CONTACT "Open3D team <@PROJECT_EMAIL@>") set(CPACK_DEBIAN_PACKAGE_SECTION "Graphics") set(CPACK_PACKAGE_VERSION "@OPEN3D_VERSION_FULL@") -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc++1, libgomp1, libpng16-16, libglfw3") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc++1, libgomp1, libpng16-16, libglfw3, libtbb12") set(CPACK_PACKAGE_HOMEPAGE_URL "@PROJECT_HOMEPAGE_URL@") include(CPack) diff --git a/cpp/apps/fixup_macosx_bundle.sh b/cpp/apps/fixup_macosx_bundle.sh index a0388ac1c60..a69a0ca70f1 100755 --- a/cpp/apps/fixup_macosx_bundle.sh +++ b/cpp/apps/fixup_macosx_bundle.sh @@ -7,13 +7,13 @@ # within the bundle. if [[ $(uname) != "Darwin" ]]; then - echo "This script is only useful for macOS" - exit 1 + echo "This script is only useful for macOS" + exit 1 fi if [[ $# != 1 ]]; then - echo "Usage: $0 path/to/name.app" - exit 1 + echo "Usage: $0 path/to/name.app" + exit 1 fi # Find the path to the actual executable in the app bundle @@ -21,17 +21,17 @@ appBundle=$1 exeDir="$appBundle/Contents/MacOS" exe=$(find "$exeDir" -type f -perm +111 | grep -v dylib) if [[ ! -f $exe ]]; then - echo "No executable file in app bundle ($appBundle/Contents/MacOS)" - exit 1 + echo "No executable file in app bundle ($appBundle/Contents/MacOS)" + exit 1 fi # Find the rpath paths rpaths=$(otool -l "$exe" | grep "path " | awk '{print $2}') if [[ $rpath != "" ]]; then - echo "@rpath:" - for rp in $rpaths; do - echo " $rp" - done + echo "@rpath:" + for rp in $rpaths; do + echo " $rp" + done fi # Set IFS so that newlines don't become spaces; helps parsing the otool -L output @@ -41,35 +41,34 @@ IFS=' # Copy any external libraries and change the library paths to @executable_path libs=$(otool -L "$exe" | grep -v "$exe" | grep -v /usr/lib | grep -v /System | awk '{ print $1; }') for lib in $libs; do - if [[ ${lib:0:1} != "@" ]]; then # external library with a regular path - # copy the external library - cp -aL "$lib" "$exeDir" + if [[ ${lib:0:1} != "@" ]]; then # external library with a regular path + libname=$(basename $lib) + # copy the external library, resolve symlink chain + cp -aRL "$lib" "$exeDir/$lib" + # change its path in the executable + newpath="@executable_path/$libname" + echo "$lib -> $newpath" + install_name_tool -change "$lib" "$newpath" "$exe" - # change its path in the executable - libname=$(basename $lib) - newpath="@executable_path/$libname" - echo "$lib -> $newpath" - install_name_tool -change "$lib" "$newpath" "$exe" + elif [[ $lib == @rpath/* ]]; then # external library with @rpath + libname=${lib:7} + # copy the external library. Since it uses an rpath, we need to + # prepend each rpath to see which one gives a valid path + for rp in $rpaths; do + if [[ -f "$rp/$libname" ]]; then + cp -aRL "$rp/$libname" "$exeDir/$libname" + break + fi + done - elif [[ $lib == @rpath/* ]]; then # external library with @rpath - libname=${lib:7} - # copy the external library. Since it uses an rpath, we need to - # prepend each rpath to see which one gives a valid path - for rp in $rpaths; do - if [[ -f "$rp/$libname" ]]; then - cp -a "$rp/$libname" "$exeDir" - break - fi - done - - # change its path in the executable - newpath="@executable_path/$libname" - echo "$lib -> $newpath" - install_name_tool -change "$lib" "$newpath" $exe - fi + # change its path in the executable + newpath="@executable_path/$libname" + echo "$lib -> $newpath" + install_name_tool -change "$lib" "$newpath" $exe + fi done # Remove rpaths for rp in $rpaths; do - install_name_tool -delete_rpath "$rp" "$exe" + install_name_tool -delete_rpath "$rp" "$exe" done diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 50dba5a6808..89178ce97cd 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -23,7 +23,7 @@ if (BUILD_CUDA_MODULE) target_include_directories(benchmarks SYSTEM PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) endif() -if (WITH_IPPICV) +if (WITH_IPP) target_compile_definitions(benchmarks PRIVATE IPP_CONDITIONAL_TEST_STR=) # Empty string (test not disabled) else() target_compile_definitions(benchmarks PRIVATE IPP_CONDITIONAL_TEST_STR=DISABLED_) diff --git a/cpp/benchmarks/t/pipelines/odometry/RGBDOdometry.cpp b/cpp/benchmarks/t/pipelines/odometry/RGBDOdometry.cpp index d79cfc0d18a..6b27492a20d 100644 --- a/cpp/benchmarks/t/pipelines/odometry/RGBDOdometry.cpp +++ b/cpp/benchmarks/t/pipelines/odometry/RGBDOdometry.cpp @@ -36,7 +36,7 @@ static core::Tensor CreateIntrisicTensor() { static void ComputeOdometryResultPointToPlane(benchmark::State& state, const core::Device& device) { - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { return; } @@ -97,7 +97,7 @@ static void RGBDOdometryMultiScale( benchmark::State& state, const core::Device& device, const t::pipelines::odometry::Method& method) { - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { return; } diff --git a/cpp/open3d/CMakeLists.txt b/cpp/open3d/CMakeLists.txt index 6da3581e509..e31bb2b83f3 100644 --- a/cpp/open3d/CMakeLists.txt +++ b/cpp/open3d/CMakeLists.txt @@ -5,6 +5,10 @@ # lib, and targets that links to the Open3D lib, e.g pybind, unit tests, etc. function(open3d_set_open3d_lib_properties target) cmake_parse_arguments(arg "HIDDEN" "" "" ${ARGN}) + set_target_properties(${target} PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${OPEN3D_ABI_VERSION} + ) if(NOT BUILD_SHARED_LIBS) target_compile_definitions(${target} PUBLIC OPEN3D_STATIC) endif() @@ -191,7 +195,7 @@ Description: @PROJECT_DESCRIPTION@ URL: @PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Cflags: -std=c++@CMAKE_CXX_STANDARD@ -isystem${includedir} -isystem${includedir}/open3d/3rdparty -D$, -D> -Libs: -L${libdir} -Wl,-rpath,${libdir} -lOpen3D]=] @ONLY NEWLINE_STYLE LF) +Libs: -L${libdir} -Wl,-rpath,${libdir} -lOpen3D -ltbb]=] @ONLY NEWLINE_STYLE LF) file(GENERATE OUTPUT Open3D.pc INPUT "${CMAKE_CURRENT_BINARY_DIR}/Open3D.pc.in" TARGET "Open3D::Open3D") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Open3D.pc" diff --git a/cpp/open3d/cmake_uninstall.cmake.in b/cpp/open3d/cmake_uninstall.cmake.in index 846acdfbccb..9c8b4a68fd7 100644 --- a/cpp/open3d/cmake_uninstall.cmake.in +++ b/cpp/open3d/cmake_uninstall.cmake.in @@ -8,15 +8,12 @@ string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") - exec_program( - "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" - OUTPUT_VARIABLE rm_out - RETURN_VALUE rm_retval) - + execute_process(COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}" + RESULT_VARIABLE rm_retval) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) - else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + else() message(STATUS "File $ENV{DESTDIR}${file} does not exist.") - endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") -endforeach(file) + endif() +endforeach() diff --git a/cpp/open3d/ml/pytorch/CMakeLists.txt b/cpp/open3d/ml/pytorch/CMakeLists.txt index 3cb75e531de..76e40b82791 100644 --- a/cpp/open3d/ml/pytorch/CMakeLists.txt +++ b/cpp/open3d/ml/pytorch/CMakeLists.txt @@ -128,6 +128,12 @@ set_target_properties(open3d_torch_ops PROPERTIES # Do not add "lib" prefix set_target_properties(open3d_torch_ops PROPERTIES PREFIX "") set_target_properties(open3d_torch_ops PROPERTIES DEBUG_POSTFIX "_debug") +# Set BUILD_RPATH to find tbb. We don't install through cmake. +if (APPLE) + set_target_properties(open3d_torch_ops PROPERTIES BUILD_RPATH "@loader_path/..") +elseif (UNIX) + set_target_properties(open3d_torch_ops PROPERTIES BUILD_RPATH "$ORIGIN/..") +endif() target_include_directories(open3d_torch_ops SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/cpp @@ -140,7 +146,7 @@ target_link_libraries(open3d_torch_ops PRIVATE Open3D::3rdparty_eigen3 Open3D::3rdparty_fmt Open3D::3rdparty_nanoflann - Open3D::3rdparty_tbb + TBB::tbb ) if (TARGET Open3D::3rdparty_parallelstl) target_link_libraries(open3d_torch_ops PRIVATE @@ -185,7 +191,7 @@ URL: @PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Requires: Open3D = @PROJECT_VERSION@ Cflags: -Libs: -lopen3d_torch_ops]=] @ONLY NEWLINE_STYLE LF) +Libs: -lopen3d_torch_ops -ltbb]=] @ONLY NEWLINE_STYLE LF) file(GENERATE OUTPUT open3d_torch_ops.pc INPUT "${CMAKE_CURRENT_BINARY_DIR}/open3d_torch_ops.pc.in" TARGET open3d_torch_ops) diff --git a/cpp/open3d/ml/tensorflow/CMakeLists.txt b/cpp/open3d/ml/tensorflow/CMakeLists.txt index cecd4152c49..41fd4c3eeeb 100644 --- a/cpp/open3d/ml/tensorflow/CMakeLists.txt +++ b/cpp/open3d/ml/tensorflow/CMakeLists.txt @@ -135,6 +135,12 @@ set_target_properties(open3d_tf_ops PROPERTIES # Do not add "lib" prefix set_target_properties(open3d_tf_ops PROPERTIES PREFIX "") set_target_properties(open3d_tf_ops PROPERTIES DEBUG_POSTFIX "_debug") +# Set BUILD_RPATH to find tbb. We don't install through cmake. +if (APPLE) + set_target_properties(open3d_tf_ops PROPERTIES BUILD_RPATH "@loader_path/..") +elseif (UNIX) + set_target_properties(open3d_tf_ops PROPERTIES BUILD_RPATH "$ORIGIN/..") +endif() # _GLIBCXX_USER_CXX11_ABI is set separately list(REMOVE_ITEM Tensorflow_DEFINITIONS "_GLIBCXX_USE_CXX11_ABI=0" @@ -159,8 +165,7 @@ target_link_libraries(open3d_tf_ops PRIVATE Open3D::Open3D Open3D::3rdparty_fmt Open3D::3rdparty_nanoflann - Open3D::3rdparty_parallelstl - Open3D::3rdparty_tbb + TBB::tbb ) if (BUILD_CUDA_MODULE) @@ -194,7 +199,7 @@ URL: @PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Requires: Open3D = @PROJECT_VERSION@ Cflags: -Libs: -lopen3d_tf_ops]=] @ONLY NEWLINE_STYLE LF) +Libs: -lopen3d_tf_ops -ltbb]=] @ONLY NEWLINE_STYLE LF) file(GENERATE OUTPUT open3d_tf_ops.pc INPUT "${CMAKE_CURRENT_BINARY_DIR}/open3d_tf_ops.pc.in" TARGET open3d_tf_ops) diff --git a/cpp/open3d/t/geometry/Image.cpp b/cpp/open3d/t/geometry/Image.cpp index 3af4fc20b42..853bc6b6a09 100644 --- a/cpp/open3d/t/geometry/Image.cpp +++ b/cpp/open3d/t/geometry/Image.cpp @@ -104,7 +104,7 @@ Image Image::To(core::Dtype dtype, dst_im.data_ = core::Tensor::Empty( {GetRows(), GetCols(), GetChannels()}, dtype, GetDevice()); } - if (HAVE_IPPICV && // Check for IPP fast implementation. + if (HAVE_IPP && // Check for IPP fast implementation. data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), GetDtype()) > 0 && @@ -140,7 +140,7 @@ Image Image::RGBToGray() const { std::count(npp_supported.begin(), npp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { CUDA_CALL(npp::RGBToGray, data_, dst_im.data_); - } else if (HAVE_IPPICV && data_.IsCPU() && + } else if (HAVE_IPP && data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { IPP_CALL(ipp::RGBToGray, data_, dst_im.data_); @@ -180,7 +180,7 @@ Image Image::Resize(float sampling_rate, InterpType interp_type) const { std::count(npp_supported.begin(), npp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { CUDA_CALL(npp::Resize, data_, dst_im.data_, interp_type); - } else if (HAVE_IPPICV && data_.IsCPU() && + } else if (HAVE_IPP && data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { IPP_CALL(ipp::Resize, data_, dst_im.data_, interp_type); @@ -217,7 +217,7 @@ Image Image::Dilate(int kernel_size) const { std::count(npp_supported.begin(), npp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { CUDA_CALL(npp::Dilate, data_, dst_im.data_, kernel_size); - } else if (HAVE_IPPICV && data_.IsCPU() && + } else if (HAVE_IPP && data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { IPP_CALL(ipp::Dilate, data_, dst_im.data_, kernel_size); @@ -254,7 +254,7 @@ Image Image::FilterBilateral(int kernel_size, std::make_pair(GetDtype(), GetChannels())) > 0) { CUDA_CALL(npp::FilterBilateral, data_, dst_im.data_, kernel_size, value_sigma, dist_sigma); - } else if (HAVE_IPPICV && data_.IsCPU() && + } else if (HAVE_IPP && data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { IPP_CALL(ipp::FilterBilateral, data_, dst_im.data_, kernel_size, @@ -286,7 +286,7 @@ Image Image::Filter(const core::Tensor &kernel) const { std::count(npp_supported.begin(), npp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { CUDA_CALL(npp::Filter, data_, dst_im.data_, kernel); - } else if (HAVE_IPPICV && data_.IsCPU() && + } else if (HAVE_IPP && data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { IPP_CALL(ipp::Filter, data_, dst_im.data_, kernel); @@ -322,7 +322,7 @@ Image Image::FilterGaussian(int kernel_size, float sigma) const { std::count(npp_supported.begin(), npp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { CUDA_CALL(npp::FilterGaussian, data_, dst_im.data_, kernel_size, sigma); - } else if (HAVE_IPPICV && data_.IsCPU() && + } else if (HAVE_IPP && data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { IPP_CALL(ipp::FilterGaussian, data_, dst_im.data_, kernel_size, sigma); @@ -371,7 +371,7 @@ std::pair Image::FilterSobel(int kernel_size) const { std::make_pair(GetDtype(), GetChannels())) > 0) { CUDA_CALL(npp::FilterSobel, data_, dst_im_dx.data_, dst_im_dy.data_, kernel_size); - } else if (HAVE_IPPICV && data_.IsCPU() && + } else if (HAVE_IPP && data_.IsCPU() && std::count(ipp_supported.begin(), ipp_supported.end(), std::make_pair(GetDtype(), GetChannels())) > 0) { IPP_CALL(ipp::FilterSobel, data_, dst_im_dx.data_, dst_im_dy.data_, diff --git a/cpp/open3d/t/geometry/Image.h b/cpp/open3d/t/geometry/Image.h index 105bff8db82..5181c94ebb9 100644 --- a/cpp/open3d/t/geometry/Image.h +++ b/cpp/open3d/t/geometry/Image.h @@ -326,11 +326,11 @@ class Image : public Geometry { /// \brief Text description. std::string ToString() const; - /// Do we use IPP ICV for accelerating image processing operations? -#ifdef WITH_IPPICV - static constexpr bool HAVE_IPPICV = true; + /// Do we use IPP for accelerating image processing operations? +#ifdef WITH_IPP + static constexpr bool HAVE_IPP = true; #else - static constexpr bool HAVE_IPPICV = false; + static constexpr bool HAVE_IPP = false; #endif protected: diff --git a/cpp/open3d/t/geometry/kernel/CMakeLists.txt b/cpp/open3d/t/geometry/kernel/CMakeLists.txt index 081d24a6b96..d7a4f364b09 100644 --- a/cpp/open3d/t/geometry/kernel/CMakeLists.txt +++ b/cpp/open3d/t/geometry/kernel/CMakeLists.txt @@ -26,7 +26,7 @@ if (BUILD_CUDA_MODULE) ) endif() -if (WITH_IPPICV) +if (WITH_IPP) target_sources(tgeometry_kernel PRIVATE IPPImage.cpp ) diff --git a/cpp/open3d/t/geometry/kernel/IPPImage.cpp b/cpp/open3d/t/geometry/kernel/IPPImage.cpp index 275d15b560b..b8287468cb2 100644 --- a/cpp/open3d/t/geometry/kernel/IPPImage.cpp +++ b/cpp/open3d/t/geometry/kernel/IPPImage.cpp @@ -7,10 +7,17 @@ #include "open3d/t/geometry/kernel/IPPImage.h" +#ifdef APPLE // macOS IPP <=v2021.9 uses old directory layout #include #include #include #include +#else // Linux and Windows IPP >=v2021.10 uses new directory layout +#include +#include +#include +#include +#endif #include "open3d/core/Dtype.h" #include "open3d/core/ParallelFor.h" @@ -48,7 +55,7 @@ void To(const core::Tensor &src_im, try { ::ipp::iwiScale(ipp_src_im, ipp_dst_im, scale, offset); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } } @@ -71,7 +78,7 @@ void RGBToGray(const core::Tensor &src_im, core::Tensor &dst_im) { ::ipp::iwiColorConvert(ipp_src_im, ::ipp::iwiColorRGB, ipp_dst_im, ::ipp::iwiColorGray); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } } @@ -111,7 +118,7 @@ void Resize(const open3d::core::Tensor &src_im, try { ::ipp::iwiResize(ipp_src_im, ipp_dst_im, it->second); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } } @@ -148,7 +155,7 @@ void Dilate(const core::Tensor &src_im, core::Tensor &dst_im, int kernel_size) { ::ipp::IwDefault(), /* Do not use IwiFilterMorphologyParams() */ ippBorderRepl); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } } @@ -180,7 +187,7 @@ void Filter(const open3d::core::Tensor &src_im, try { ::ipp::iwiFilter(ipp_src_im, ipp_dst_im, ipp_kernel); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } }; @@ -211,7 +218,7 @@ void FilterBilateral(const core::Tensor &src_im, value_sigma * value_sigma, distance_sigma * distance_sigma); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } } @@ -239,7 +246,7 @@ void FilterGaussian(const core::Tensor &src_im, try { ::ipp::iwiFilterGaussian(ipp_src_im, ipp_dst_im, kernel_size, sigma); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } } @@ -287,7 +294,7 @@ void FilterSobel(const core::Tensor &src_im, // so we need to negate it in-place. dst_im_dx.Neg_(); } catch (const ::ipp::IwException &e) { - // See comments in icv/include/ippicv_types.h for m_status meaning + // See comments in ipp/ipptypes.h for m_status meaning utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string); } } diff --git a/cpp/open3d/t/geometry/kernel/IPPImage.h b/cpp/open3d/t/geometry/kernel/IPPImage.h index b861dd4d920..12430f04e4c 100644 --- a/cpp/open3d/t/geometry/kernel/IPPImage.h +++ b/cpp/open3d/t/geometry/kernel/IPPImage.h @@ -6,13 +6,17 @@ // ---------------------------------------------------------------------------- #pragma once -#ifdef WITH_IPPICV +#ifdef WITH_IPP +// Auto-enable multi-threaded implementations +#define IPP_ENABLED_THREADING_LAYER_REDEFINITIONS 1 #define IPP_CALL(ipp_function, ...) ipp_function(__VA_ARGS__); -// Required by IPPICV headers, defined here to keep other compile commands clean -#define ICV_BASE -#define IW_BUILD +#if IPP_VERSION_INT < \ + 20211000 // macOS IPP v2021.9.11 uses old directory layout #include +#else // Linux and Windows IPP v2021.10+ uses new directory layout +#include +#endif #include "open3d/core/Dtype.h" #include "open3d/core/Tensor.h" @@ -85,4 +89,4 @@ void FilterSobel(const open3d::core::Tensor &srcim, #else #define IPP_CALL(ipp_function, ...) \ utility::LogError("Not built with IPP-IW, cannot call " #ipp_function); -#endif // WITH_IPPICV +#endif // WITH_IPP diff --git a/cpp/open3d/utility/ParallelScan.h b/cpp/open3d/utility/ParallelScan.h index 0479d611eb5..47f114bb26b 100644 --- a/cpp/open3d/utility/ParallelScan.h +++ b/cpp/open3d/utility/ParallelScan.h @@ -10,42 +10,6 @@ #include #include -// clang-format off -#if TBB_INTERFACE_VERSION >= 10000 - #ifdef OPEN3D_USE_ONEAPI_PACKAGES - #ifdef _PSTL_UDR_PRESENT - #undef _PSTL_UDR_PRESENT - #endif - #define _PSTL_UDR_PRESENT 0 - #ifdef _PSTL_UDS_PRESENT - #undef _PSTL_UDS_PRESENT - #endif - #define _PSTL_UDS_PRESENT 0 - #include - #include - #else - // Check if the C++ standard library implements parallel algorithms - // and use this over parallelstl to avoid conflicts. - // Clang does not implement it so far, so checking for C++17 is not sufficient. - #ifdef __cpp_lib_parallel_algorithm - #include - #include - #else - #include - #include - // parallelstl incorrectly assumes MSVC to unconditionally implement - // parallel algorithms even if __cpp_lib_parallel_algorithm is not - // defined. So manually include the header which pulls all - // "pstl::execution" definitions into the "std" namespace. - #if __PSTL_CPP17_EXECUTION_POLICIES_PRESENT - #include - #endif - #endif - #endif -#endif - -// clang-format on - namespace open3d { namespace utility { @@ -77,19 +41,9 @@ class ScanSumBody { template void InclusivePrefixSum(const Tin* first, const Tin* last, Tout* out) { -#if TBB_INTERFACE_VERSION >= 10000 - // use parallelstl if we have TBB 2018 or later -#ifdef OPEN3D_USE_ONEAPI_PACKAGES - std::inclusive_scan(oneapi::dpl::execution::par_unseq, first, last, out); - -#else - std::inclusive_scan(std::execution::par_unseq, first, last, out); -#endif -#else ScanSumBody body(out, first); size_t n = std::distance(first, last); tbb::parallel_scan(tbb::blocked_range(0, n), body); -#endif } } // namespace utility diff --git a/cpp/pybind/CMakeLists.txt b/cpp/pybind/CMakeLists.txt index 11c4d3ea16a..e7a534a3eb3 100644 --- a/cpp/pybind/CMakeLists.txt +++ b/cpp/pybind/CMakeLists.txt @@ -75,14 +75,35 @@ endif() # `build/lib/${CMAKE_BUILD_TYPE}/Python/{cpu|cuda}` set(PYTHON_COMPILED_MODULE_DIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Python/$,cuda,cpu>") + +# Set BUILD_RPATH to find tbb (and a shared libOpen3D). We don't install through cmake. +if (APPLE) + set_target_properties(pybind PROPERTIES BUILD_RPATH "@loader_path;@loader_path/..") +elseif (UNIX) + set_target_properties(pybind PROPERTIES BUILD_RPATH "$ORIGIN;$ORIGIN/..") +endif() set_target_properties(pybind PROPERTIES FOLDER "Python" LIBRARY_OUTPUT_DIRECTORY "${PYTHON_COMPILED_MODULE_DIR}" ARCHIVE_OUTPUT_DIRECTORY "${PYTHON_COMPILED_MODULE_DIR}") + +if (BUILD_SHARED_LIBS) + if (WIN32) # CMake does not add soversion suffix to WIN32 DLLs + set(_libopen3d_soname "Open3D.dll") + elseif(APPLE) + set(_libopen3d_soname "libOpen3D.${OPEN3D_ABI_VERSION}.dylib") + else() + set(_libopen3d_soname "libOpen3D.so.${OPEN3D_ABI_VERSION}") + endif() + add_custom_command(TARGET pybind POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ $/${_libopen3d_soname} + ) +endif() # Include additional libraries that may be absent from the user system -# eg: libc++.so and libc++abi.so (needed by filament) for Linux. +# eg: libc++.so, libc++abi.so (needed by filament) for Linux. # libc++.so is a linker script including libc++.so.1 and libc++abi.so, so append 1 to libc++.so -set(PYTHON_EXTRA_LIBRARIES "") +set(PYTHON_EXTRA_LIBRARIES $) if (BUILD_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") list(APPEND PYTHON_EXTRA_LIBRARIES ${CPP_LIBRARY}.1 ${CPPABI_LIBRARY}) endif() @@ -101,7 +122,7 @@ if (WITH_OPENMP AND APPLE AND NOT BUILD_SHARED_LIBS) file(GENERATE OUTPUT update_pybind_libomp.sh CONTENT [=[libomp_library=$(dyld_info -dependents "$" | grep libomp | tr -d '[:space:]') -install_name_tool -change $libomp_library @loader_path/../libomp.dylib \ +install_name_tool -change $libomp_library @rpath/$(basename $libomp_library) \ "$"]=]) add_custom_command(TARGET pybind POST_BUILD COMMAND bash update_pybind_libomp.sh @@ -121,6 +142,10 @@ message(STATUS "PYPI_PACKAGE_NAME: ${PYPI_PACKAGE_NAME}") # add the open3d python module first set(COMPILED_MODULE_PATH_LIST $) +# add the open3d DSO / DLL if shared +if (BUILD_SHARED_LIBS) + list(APPEND COMPILED_MODULE_PATH_LIST $/${_libopen3d_soname}) +endif() set(GENERATED_OUTPUTS "") diff --git a/cpp/pybind/make_python_package.cmake b/cpp/pybind/make_python_package.cmake index dee6e394d36..01e0d5663f5 100644 --- a/cpp/pybind/make_python_package.cmake +++ b/cpp/pybind/make_python_package.cmake @@ -37,7 +37,11 @@ endforeach() foreach(PYTHON_EXTRA_LIB ${PYTHON_EXTRA_LIBRARIES}) get_filename_component(PYTHON_EXTRA_LIB_REAL ${PYTHON_EXTRA_LIB} REALPATH) get_filename_component(SO_VER_NAME ${PYTHON_EXTRA_LIB_REAL} NAME) - string(REGEX REPLACE "\\.so\\.1\\..*" ".so.1" SO_1_NAME ${SO_VER_NAME}) + if (APPLE) + string(REGEX REPLACE "\\.([0-9]+)\\..*.dylib" ".\\1.dylib" SO_1_NAME ${SO_VER_NAME}) + elseif (UNIX) + string(REGEX REPLACE "\\.so\\.([0-9]+)\\..*" ".so.\\1" SO_1_NAME ${SO_VER_NAME}) + endif() configure_file(${PYTHON_EXTRA_LIB_REAL} ${PYTHON_PACKAGE_DST_DIR}/open3d/${SO_1_NAME} COPYONLY) endforeach() diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 9d8d118e792..3d58a53ad84 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -28,7 +28,7 @@ else() target_compile_definitions(tests PRIVATE GPU_CONDITIONAL_TEST_STR=DISABLED_) endif() -if (WITH_IPPICV) +if (WITH_IPP) target_compile_definitions(tests PRIVATE IPP_CONDITIONAL_TEST_STR=) # Empty string (test not disabled) else() target_compile_definitions(tests PRIVATE IPP_CONDITIONAL_TEST_STR=DISABLED_) @@ -51,6 +51,14 @@ endif() open3d_show_and_abort_on_warning(tests) open3d_set_global_properties(tests) +# On Windows, running tests from the build folder needs tbb.dll to be in the same folder. +if (WIN32) + add_custom_command( + TARGET tests + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/" + ) +endif() if (BUILD_AZURE_KINECT) # K4A headers are directly used in test. Currently we don't need to link diff --git a/cpp/tests/t/geometry/Image.cpp b/cpp/tests/t/geometry/Image.cpp index 1b9fa2112ea..2e270261106 100644 --- a/cpp/tests/t/geometry/Image.cpp +++ b/cpp/tests/t/geometry/Image.cpp @@ -264,7 +264,7 @@ TEST_P(ImagePermuteDevices, FilterBilateral) { core::Tensor(input_data, {5, 5, 1}, core::Float32, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.FilterBilateral(3, 10, 10), std::runtime_error); } else { im = im.FilterBilateral(3, 10, 10); @@ -304,7 +304,7 @@ TEST_P(ImagePermuteDevices, FilterBilateral) { core::Tensor(input_data, {5, 5, 1}, core::UInt8, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.FilterBilateral(3, 5, 5), std::runtime_error); } else { im = im.FilterBilateral(3, 5, 5); @@ -343,7 +343,7 @@ TEST_P(ImagePermuteDevices, FilterGaussian) { core::Tensor data = core::Tensor(input_data, {5, 5, 1}, core::Float32, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.FilterGaussian(3), std::runtime_error); } else { im = im.FilterGaussian(3); @@ -377,7 +377,7 @@ TEST_P(ImagePermuteDevices, FilterGaussian) { core::Tensor data = core::Tensor(input_data, {5, 5, 1}, core::UInt8, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.FilterGaussian(3), std::runtime_error); } else { im = im.FilterGaussian(3); @@ -417,7 +417,7 @@ TEST_P(ImagePermuteDevices, Filter) { core::Tensor kernel = core::Tensor(kernel_data, {5, 5}, core::Float32, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.Filter(kernel), std::runtime_error); } else { t::geometry::Image im_new = im.Filter(kernel); @@ -463,7 +463,7 @@ TEST_P(ImagePermuteDevices, Filter) { core::Tensor kernel = core::Tensor(kernel_data, {5, 5}, core::Float32, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.Filter(kernel), std::runtime_error); } else { im = im.Filter(kernel); @@ -507,7 +507,7 @@ TEST_P(ImagePermuteDevices, FilterSobel) { core::Tensor(input_data, {5, 5, 1}, core::Float32, device); t::geometry::Image im(data); t::geometry::Image dx, dy; - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.FilterSobel(3), std::runtime_error); } else { std::tie(dx, dy) = im.FilterSobel(3); @@ -525,7 +525,7 @@ TEST_P(ImagePermuteDevices, FilterSobel) { .To(core::UInt8); t::geometry::Image im(data); t::geometry::Image dx, dy; - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.FilterSobel(3), std::runtime_error); } else { std::tie(dx, dy) = im.FilterSobel(3); @@ -563,7 +563,7 @@ TEST_P(ImagePermuteDevices, Resize) { core::Tensor data = core::Tensor(input_data, {6, 6, 1}, core::Float32, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW( im.Resize(0.5, t::geometry::Image::InterpType::Nearest), std::runtime_error); @@ -595,7 +595,7 @@ TEST_P(ImagePermuteDevices, Resize) { core::Tensor data = core::Tensor(input_data, {6, 6, 1}, core::UInt8, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.Resize(0.5, t::geometry::Image::InterpType::Super), std::runtime_error); } else { @@ -611,7 +611,7 @@ TEST_P(ImagePermuteDevices, Resize) { EXPECT_TRUE(im_low.AsTensor().AllClose(core::Tensor( output_ref_npp, {3, 3, 1}, core::UInt8, device))); - // Check output in the CI to see if other inteprolations works + // Check output in the CI to see if other interpolations works // with other platforms im_low = im.Resize(0.5, t::geometry::Image::InterpType::Linear); utility::LogInfo("Linear(impl. dependent): {}", @@ -652,7 +652,7 @@ TEST_P(ImagePermuteDevices, PyrDown) { core::Tensor(input_data, {6, 6, 1}, core::Float32, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.PyrDown(), std::runtime_error); } else { im = im.PyrDown(); @@ -684,7 +684,7 @@ TEST_P(ImagePermuteDevices, PyrDown) { core::Tensor(input_data, {6, 6, 1}, core::UInt8, device); t::geometry::Image im(data); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { ASSERT_THROW(im.PyrDown(), std::runtime_error); } else { im = im.PyrDown(); @@ -732,8 +732,7 @@ TEST_P(ImagePermuteDevices, Dilate) { core::Tensor t_input_uint8_t = t_input.To(core::UInt8); // normal static_cast is OK t::geometry::Image input_uint8_t(t_input_uint8_t); - if (!t::geometry::Image::HAVE_IPPICV && - device.IsCPU()) { // Not Implemented + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { // Not Implemented ASSERT_THROW(input_uint8_t.Dilate(kernel_size), std::runtime_error); } else { output = input_uint8_t.Dilate(kernel_size); @@ -748,8 +747,7 @@ TEST_P(ImagePermuteDevices, Dilate) { core::Tensor t_input_uint16_t = t_input.To(core::UInt16); // normal static_cast is OK t::geometry::Image input_uint16_t(t_input_uint16_t); - if (!t::geometry::Image::HAVE_IPPICV && - device.IsCPU()) { // Not Implemented + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { // Not Implemented ASSERT_THROW(input_uint16_t.Dilate(kernel_size), std::runtime_error); } else { output = input_uint16_t.Dilate(kernel_size); @@ -761,8 +759,7 @@ TEST_P(ImagePermuteDevices, Dilate) { } // Float32 - if (!t::geometry::Image::HAVE_IPPICV && - device.IsCPU()) { // Not Implemented + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { // Not Implemented ASSERT_THROW(input.Dilate(kernel_size), std::runtime_error); } else { output = input.Dilate(kernel_size); @@ -886,8 +883,7 @@ TEST_P(ImagePermuteDevices, DISABLED_CreateNormalMap_Visual) { // We have to apply a bilateral filter, otherwise normals would be too // noisy. auto depth_clipped = depth.ClipTransform(1000.0, 0.0, 3.0, invalid_fill); - if (!t::geometry::Image::HAVE_IPPICV && - device.IsCPU()) { // Not Implemented + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { // Not Implemented ASSERT_THROW(depth_clipped.FilterBilateral(5, 5.0, 10.0), std::runtime_error); } else { diff --git a/cpp/tests/t/geometry/PointCloud.cpp b/cpp/tests/t/geometry/PointCloud.cpp index 0b4d7365de6..6fb8e72d2ed 100644 --- a/cpp/tests/t/geometry/PointCloud.cpp +++ b/cpp/tests/t/geometry/PointCloud.cpp @@ -692,10 +692,10 @@ TEST_P(PointCloudPermuteDevices, CreateFromRGBDImage) { TEST_P(PointCloudPermuteDevices, CreateFromRGBDOrDepthImageWithNormals) { core::Device device = GetParam(); - if (!t::geometry::Image::HAVE_IPPICV && + if (!t::geometry::Image::HAVE_IPP && device.GetType() == core::Device::DeviceType::CPU) { // FilterBilateral on CPU - // needs IPPICV + // needs IPP return; } diff --git a/cpp/tests/t/pipelines/odometry/RGBDOdometry.cpp b/cpp/tests/t/pipelines/odometry/RGBDOdometry.cpp index aed406fb98e..614499fe43f 100644 --- a/cpp/tests/t/pipelines/odometry/RGBDOdometry.cpp +++ b/cpp/tests/t/pipelines/odometry/RGBDOdometry.cpp @@ -39,7 +39,7 @@ core::Tensor CreateIntrisicTensor() { TEST_P(OdometryPermuteDevices, ComputeOdometryResultPointToPlane) { core::Device device = GetParam(); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { return; } @@ -106,7 +106,7 @@ TEST_P(OdometryPermuteDevices, ComputeOdometryResultPointToPlane) { TEST_P(OdometryPermuteDevices, RGBDOdometryMultiScalePointToPlane) { core::Device device = GetParam(); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { return; } @@ -173,7 +173,7 @@ TEST_P(OdometryPermuteDevices, RGBDOdometryMultiScalePointToPlane) { TEST_P(OdometryPermuteDevices, RGBDOdometryMultiScaleIntensity) { core::Device device = GetParam(); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { return; } @@ -240,7 +240,7 @@ TEST_P(OdometryPermuteDevices, RGBDOdometryMultiScaleIntensity) { TEST_P(OdometryPermuteDevices, RGBDOdometryMultiScaleHybrid) { core::Device device = GetParam(); - if (!t::geometry::Image::HAVE_IPPICV && device.IsCPU()) { + if (!t::geometry::Image::HAVE_IPP && device.IsCPU()) { return; } diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index 81379352f41..cd52a838882 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -212,7 +212,7 @@ RUN \ && make install -j$(nproc) \ && if [ "${PACKAGE}" = "ON" ]; then make package; fi \ && if [ "${PACKAGE}" = "VIEWER" ]; then make package-Open3DViewer-deb; fi \ - && if [ "${CI:-}a" != "a" ]; then rm -rf _deps assimp embree ippicv mkl mkl_install webrtc; fi + && if [ "${CI:-}a" != "a" ]; then rm -rf _deps assimp embree ipp mkl mkl_install webrtc; fi # If CI is not null or unset, remove all large build folders to save disk space # Compress ccache folder, move to / directory diff --git a/docker/README.md b/docker/README.md index d14d9521c19..6922834cbf7 100644 --- a/docker/README.md +++ b/docker/README.md @@ -48,7 +48,7 @@ To verify that the ARM64 environment is working, run: # The following warning message is expected: "WARNING: The requested image's # platform (linux/arm64/v8) does not match the detected host platform # (linux/amd64) and no specific platform was requested aarch64." -docker run --rm arm64v8/ubuntu:18.04 uname -p +docker run --rm arm64v8/ubuntu:24.04 uname -p ``` ## Build and test Docker diff --git a/docs/_static/docker/Dockerfile b/docs/_static/docker/Dockerfile index ea7e5344f3f..33065b3143b 100644 --- a/docs/_static/docker/Dockerfile +++ b/docs/_static/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 # ENV http_proxy "" # ENV HTTP_PROXY "" diff --git a/docs/compilation.rst b/docs/compilation.rst index c0c2d1cda16..270c928b327 100644 --- a/docs/compilation.rst +++ b/docs/compilation.rst @@ -8,15 +8,15 @@ Build from source System requirements ------------------- -* C++14 compiler: +* C++17 compiler: - * Ubuntu 18.04+: GCC 5+, Clang 7+ + * Ubuntu 20.04+: GCC 5+, Clang 7+ * macOS 10.15+: XCode 8.0+ * Windows 10 (64-bit): Visual Studio 2019+ -* CMake: 3.19+ +* CMake: 3.24+ - * Ubuntu (18.04 / 20.04): + * Ubuntu (20.04+): * Install with ``apt-get``: see `official APT repository `_ * Install with ``snap``: ``sudo snap install cmake --classic`` @@ -238,12 +238,6 @@ The visualization module depends on the Filament rendering engine and, by defaul Open3D uses a prebuilt version of it. You can also build Filament from source by setting ``BUILD_FILAMENT_FROM_SOURCE=ON``. -.. note:: - Whereas Open3D only requires a C++14 compiler, Filament needs a C++17 compiler - and only supports Clang 7+, the most recent version of Xcode, and Visual Studio 2019, - see `their building instructions `_. - Make sure to use one of these compiler if you build Open3D with ``BUILD_FILAMENT_FROM_SOURCE=ON``. - ML Module ````````` @@ -390,8 +384,8 @@ After installing ``ccache``, simply reconfigure and recompile the Open3D library. Open3D's CMake script can detect and use it automatically. You don't need to setup additional paths except for the ``ccache`` program itself. -Ubuntu 18.04, 20.04 -``````````````````` +Ubuntu 20.04+ +````````````` If you install ``ccache`` via ``sudo apt install ccache``, the 3.x version will be installed. To cache CUDA compilations, you'll need the 4.0+ version. Here, we diff --git a/docs/contribute/contribution_recipes.rst b/docs/contribute/contribution_recipes.rst index f03434c461b..eee2b7ca3c8 100644 --- a/docs/contribute/contribution_recipes.rst +++ b/docs/contribute/contribution_recipes.rst @@ -29,7 +29,7 @@ Dos +-------------------------------------------------------------------------------------------------------------+ | [DO] Follow the :ref:`style_guide` and install the required tools | +-------------------------------------------------------------------------------------------------------------+ -| [DO] Use C++14 features when contributing C++ code | +| [DO] Use C++17 features when contributing C++ code | +-------------------------------------------------------------------------------------------------------------+ | [DO] Remember to provide Python bindings when adding new C++ core functionalities | +-------------------------------------------------------------------------------------------------------------+ diff --git a/docs/contribute/styleguide.rst b/docs/contribute/styleguide.rst index 2da3aa4f0b4..d655993b3b9 100644 --- a/docs/contribute/styleguide.rst +++ b/docs/contribute/styleguide.rst @@ -58,7 +58,7 @@ We generally follow the `Google C++ Style Guide `_. diff --git a/docs/docker.in.rst b/docs/docker.in.rst index 17c7055b809..989c9289cd6 100644 --- a/docs/docker.in.rst +++ b/docs/docker.in.rst @@ -57,7 +57,7 @@ To run GUI applications from the docker container, add these options to the - NVIDIA: ``--gpus 'all,"capabilities=compute,utility,graphics"'`` - - No GPU (CPU rendering): ``--env OPEN3D_CPU_RENDERING=true`` on Ubuntu 18.04. Later versions automaticaly select CPU rendering if a GPU is not available. + - No GPU (CPU rendering): CPU rendering is automaticaly selected if a GPU is not available. 2. X server: ``-v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY`` diff --git a/docs/getting_started.in.rst b/docs/getting_started.in.rst index e81dfdf1af8..734cb694e1c 100644 --- a/docs/getting_started.in.rst +++ b/docs/getting_started.in.rst @@ -13,7 +13,7 @@ interact with it. You can download the latest stable release app from `Github releases `__. The latest development version (``HEAD`` of ``main`` branch) viewer app is provided here [#]_: -* `Linux (Ubuntu 18.04+ or glibc 2.27+) `__ [#]_ +* `Linux (Ubuntu 20.04+ or glibc 2.31+) `__ [#]_ * `MacOSX v10.15+ (Intel or Apple Silicon) `__ * `Windows 10+ (64-bit) `__ @@ -95,16 +95,16 @@ version (``HEAD`` of ``main`` branch): :widths: auto * - Linux - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ * - Linux (CPU) - - `Python 3.8 `__ - - `Python 3.9 `__ - - `Python 3.10 `__ - - `Python 3.11 `__ + - `Python 3.8 `__ + - `Python 3.9 `__ + - `Python 3.10 `__ + - `Python 3.11 `__ * - MacOS - `Python 3.8 (x86_64) `__ @@ -183,7 +183,7 @@ rendering resources. These are built with all supported features and are available for the main supported platforms. Also, the latest development version (``HEAD`` of ``main`` branch) binary package archives are provided here [#]_: -:Linux (Ubuntu 18.04+ or glibc 2.27+ [#]_): +:Linux (Ubuntu 20.04+ or glibc 2.31+ [#]_): .. hlist:: :columns: 2 diff --git a/docs/release.md b/docs/release.md index 514114f5a04..90ea6144074 100644 --- a/docs/release.md +++ b/docs/release.md @@ -17,12 +17,12 @@ Collect all release artifacts in the [Github draft release page](https://github. Configure: `cmake -DCMAKE_BUILD_TYPE=Release -DDEVELOPER_BUILD=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DBUILD_TENSORFLOW_OPS=ON -DBUILD_PYTORCH_OPS=ON -DBUNDLE_OPEN3D_ML=ON ..` - - [ ] Ubuntu 18.04 Python (all versions) (follow docs/arm.rst) (desktop) + - [ ] Ubuntu 20.04 Python (all versions) (follow docs/arm.rst) (desktop) Build command: `cd docker; ./docker_build.sh openblas-arm64-py311; ...` - [ ] Build Open3D app - - [ ] Ubuntu 18.04, Windows 10, macOS 10.15 x86_64: (CI) + - [ ] Ubuntu 20.04, Windows 10, macOS 10.15 x86_64: (CI) - [ ] macOS 12 arm64 (desktop) - [ ] macOS (x86_64, arm64) sign (desktop): diff --git a/examples/cmake/open3d-cmake-external-project/CMakeLists.txt b/examples/cmake/open3d-cmake-external-project/CMakeLists.txt index 37ed1f3d6dd..a26a7de64c2 100644 --- a/examples/cmake/open3d-cmake-external-project/CMakeLists.txt +++ b/examples/cmake/open3d-cmake-external-project/CMakeLists.txt @@ -1,5 +1,5 @@ -# On Ubuntu 18.04, get the latest CMake from https://apt.kitware.com/. -cmake_minimum_required(VERSION 3.18) +# On Ubuntu 20.04, get the latest CMake from https://apt.kitware.com/. +cmake_minimum_required(VERSION 3.24) project(Open3DCMakeExternalProject LANGUAGES C CXX) diff --git a/examples/cmake/open3d-cmake-find-package/CMakeLists.txt b/examples/cmake/open3d-cmake-find-package/CMakeLists.txt index ea3773b1550..ca4835515b7 100644 --- a/examples/cmake/open3d-cmake-find-package/CMakeLists.txt +++ b/examples/cmake/open3d-cmake-find-package/CMakeLists.txt @@ -1,5 +1,5 @@ -# On Ubuntu 18.04, get the latest CMake from https://apt.kitware.com/. -cmake_minimum_required(VERSION 3.18) +# On Ubuntu 20.04, get the latest CMake from https://apt.kitware.com/. +cmake_minimum_required(VERSION 3.24) project(Open3DCMakeFindPackage LANGUAGES C CXX) @@ -24,10 +24,13 @@ target_link_libraries(Draw PRIVATE Open3D::Open3D) if(WIN32) get_target_property(open3d_type Open3D::Open3D TYPE) if(open3d_type STREQUAL "SHARED_LIBRARY") - message(STATUS "Copying Open3D.dll to ${CMAKE_CURRENT_BINARY_DIR}/$") - add_custom_command(TARGET Draw POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_INSTALL_PREFIX}/bin/Open3D.dll - ${CMAKE_CURRENT_BINARY_DIR}/$) + set(copy_dlls "${CMAKE_INSTALL_PREFIX}/bin/tbb12$<$:_debug>.dll" + "${CMAKE_INSTALL_PREFIX}/bin/Open3D.dll") + else() + set(copy_dlls "${CMAKE_INSTALL_PREFIX}/bin/tbb12$<$:_debug>.dll") endif() + add_custom_command(TARGET Draw POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${copy_dlls} + ${CMAKE_CURRENT_BINARY_DIR}/$ + COMMENT "Copying Open3D DLLs to ${CMAKE_CURRENT_BINARY_DIR}/$") endif() diff --git a/examples/python/visualization/non_english.py b/examples/python/visualization/non_english.py index 54c17d4df6a..0b926b70f70 100644 --- a/examples/python/visualization/non_english.py +++ b/examples/python/visualization/non_english.py @@ -39,7 +39,7 @@ hanzi = "c:/windows/fonts/msyh.ttc" # YaHei UI chess = "c:/windows/fonts/seguisym.ttf" # Segoe UI Symbol else: - # Assumes Ubuntu 18.04 + # Assumes Ubuntu 20.04 serif = "DejaVuSerif" hanzi = "NotoSansCJK" chess = "/usr/share/fonts/truetype/freefont/FreeSerif.ttf" diff --git a/python/README.rst b/python/README.rst index 232c3b0ca24..0c225d07581 100644 --- a/python/README.rst +++ b/python/README.rst @@ -38,7 +38,7 @@ Supported platforms The package has been tested on: -* Ubuntu 18.04 and 20.04 +* Ubuntu 20.04 and 22.04 * Windows 10 64-bit * macOS High Sierra and above diff --git a/python/open3d/__init__.py b/python/open3d/__init__.py index e67c146a949..141bf426c50 100644 --- a/python/open3d/__init__.py +++ b/python/open3d/__init__.py @@ -15,7 +15,11 @@ # https://github.com/dmlc/xgboost/issues/1715 import os import sys + os.environ["KMP_DUPLICATE_LIB_OK"] = "True" +# Enable thread composability manager to coordinate Intel OpenMP and TBB threads. Only works with Intel OpenMP. +# TBB must not be already loaded. +os.environ["TCM_ENABLE"] = "1" from ctypes import CDLL from ctypes.util import find_library from pathlib import Path @@ -31,13 +35,16 @@ def load_cdll(path): if not path.is_file(): raise FileNotFoundError(f"Shared library file not found: {path}.") - if sys.platform == 'win32' and sys.version_info >= (3, 8): + if sys.platform == "win32" and sys.version_info >= (3, 8): # https://stackoverflow.com/a/64472088/1255535 return CDLL(str(path), winmode=0) else: return CDLL(str(path)) +if sys.platform == "win32": # Unix: Use rpath to find libraries + _win32_dll_dir = os.add_dll_directory(str(Path(__file__).parent)) + if _build_config["BUILD_GUI"] and not (find_library("c++abi") or find_library("c++")): try: # Preload libc++.so and libc++abi.so (required by filament) @@ -56,35 +63,59 @@ def load_cdll(path): warnings.warn( "Open3D was built with CUDA support, but Open3D CPU Python " "bindings were not found. Open3D will not work on systems without" - " CUDA devices.", ImportWarning) + " CUDA devices.", + ImportWarning, + ) try: # Check CUDA availability without importing CUDA pybind symbols to # prevent "symbol already registered" errors if first import fails. _pybind_cuda = load_cdll( str(next((Path(__file__).parent / "cuda").glob("pybind*")))) if _pybind_cuda.open3d_core_cuda_device_count() > 0: - from open3d.cuda.pybind import (core, camera, data, geometry, io, - pipelines, utility, t) + from open3d.cuda.pybind import ( + core, + camera, + data, + geometry, + io, + pipelines, + utility, + t, + ) from open3d.cuda import pybind + __DEVICE_API__ = "cuda" else: warnings.warn( "Open3D was built with CUDA support, but no suitable CUDA " "devices found. If your system has CUDA devices, check your " - "CUDA drivers and runtime.", ImportWarning) + "CUDA drivers and runtime.", + ImportWarning, + ) except OSError as os_error: warnings.warn( - f'Open3D was built with CUDA support, but an error ocurred while loading the Open3D CUDA Python bindings. This is usually because the CUDA libraries could not be found. Check your CUDA installation. Falling back to the CPU pybind library. Reported error: {os_error}.', - ImportWarning) + f"Open3D was built with CUDA support, but an error ocurred while loading the Open3D CUDA Python bindings. This is usually because the CUDA libraries could not be found. Check your CUDA installation. Falling back to the CPU pybind library. Reported error: {os_error}.", + ImportWarning, + ) except StopIteration: warnings.warn( "Open3D was built with CUDA support, but Open3D CUDA Python " "binding library not found! Falling back to the CPU Python " - "binding library.", ImportWarning) + "binding library.", + ImportWarning, + ) if __DEVICE_API__ == "cpu": - from open3d.cpu.pybind import (core, camera, data, geometry, io, pipelines, - utility, t) + from open3d.cpu.pybind import ( + core, + camera, + data, + geometry, + io, + pipelines, + utility, + t, + ) from open3d.cpu import pybind @@ -103,6 +134,7 @@ def _insert_pybind_names(skip_names=()): import open3d.visualization + _insert_pybind_names(skip_names=("ml",)) __version__ = "@PROJECT_VERSION@" @@ -110,9 +142,10 @@ def _insert_pybind_names(skip_names=()): if int(sys.version_info[0]) < 3: raise Exception("Open3D only supports Python 3.") -if _build_config["BUILD_JUPYTER_EXTENSION"] and os.environ.get( - "OPEN3D_DISABLE_WEB_VISUALIZER", "False").lower() != "true": +if (_build_config["BUILD_JUPYTER_EXTENSION"] and os.environ.get( + "OPEN3D_DISABLE_WEB_VISUALIZER", "False").lower() != "true"): import platform + if not (platform.machine().startswith("arm") or platform.machine().startswith("aarch")): try: @@ -181,8 +214,10 @@ def _jupyter_nbextension_paths(): "section": "notebook", "src": "nbextension", "dest": "open3d", - "require": "open3d/extension" + "require": "open3d/extension", }] +if sys.platform == "win32": + _win32_dll_dir.close() del os, sys, CDLL, load_cdll, find_library, Path, warnings, _insert_pybind_names diff --git a/util/check_style.py b/util/check_style.py index 34442dec310..8a5b3ca7983 100644 --- a/util/check_style.py +++ b/util/check_style.py @@ -78,8 +78,13 @@ def _check_style(file_path, clang_format_bin): """ Returns (true, true) if (style, header) is valid. """ - with open(file_path, 'r', encoding='utf-8') as f: - is_valid_header = f.read().startswith(CppFormatter.standard_header) + try: + with open(file_path, 'r', encoding='utf-8') as f: + is_valid_header = (f.read(len(CppFormatter.standard_header)) == + CppFormatter.standard_header) + except Exception as exp: + print(f"Error reading file header {file_path}: {exp}") + is_valid_header = False cmd = [ clang_format_bin, @@ -156,10 +161,14 @@ def _check_style(file_path, style_config): Returns (true, true) if (style, header) is valid. """ - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - is_valid_header = (len(content) == 0 or content.startswith( - PythonFormatter.standard_header)) + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + is_valid_header = (len(content) == 0 or content.startswith( + PythonFormatter.standard_header)) + except Exception as exp: + print(f"Error reading file header {file_path}: {exp}") + is_valid_header = False _, _, changed = yapf.yapflib.yapf_api.FormatFile( file_path, style_config=style_config, in_place=False) @@ -296,7 +305,9 @@ def _glob_files(directories, extensions): for extension in extensions: extension_regex = "*." + extension file_paths.extend(directory.rglob(extension_regex)) - file_paths = [str(file_path) for file_path in file_paths] + file_paths = [ + str(file_path) for file_path in file_paths if file_path.name[0] != '.' + ] file_paths = sorted(list(set(file_paths))) return file_paths diff --git a/util/install_deps_ubuntu.sh b/util/install_deps_ubuntu.sh index 1613372b2ce..3e359a1a6e7 100755 --- a/util/install_deps_ubuntu.sh +++ b/util/install_deps_ubuntu.sh @@ -11,6 +11,7 @@ else fi deps=( + git # Open3D xorg-dev libxcb-shm0 @@ -39,11 +40,11 @@ eval $( echo DISTRIB_RELEASE="$DISTRIB_RELEASE" ) if [ "$DISTRIB_ID" == "Ubuntu" -a "$DISTRIB_RELEASE" == "20.04" ]; then - # Ubuntu 20.04's clang/libc++-dev/libc++abi-dev are version 10. - # To build Filament from source, we need version 12+. - deps=("${deps[@]/clang/clang-12}") - deps=("${deps[@]/libc++-dev/libc++-12-dev}") - deps=("${deps[@]/libc++abi-dev/libc++abi-12-dev}") + # Ubuntu 20.04's clang/libc++-dev/libc++abi-dev are version 8, 10 or 12. + # To avoid dependence on libunwind, we don't want to use versions later than 10. + deps=("${deps[@]/clang/clang-10}") + deps=("${deps[@]/libc++-dev/libc++-10-dev}") + deps=("${deps[@]/libc++abi-dev/libc++abi-10-dev}") fi # Special case for ARM64 From 164a01a8695a4bee55b10d8f005541dae3510317 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Tue, 20 Aug 2024 13:54:05 -0700 Subject: [PATCH 56/82] Update Windows MSVC to CUDA 11.8 Switch tensorflow tests to Py3.12, since TF>2.13.2 not available for Py 3.8 Update stdgpu to try fix thrust-cmake issue CLearer ASSIMP error messages --- .github/workflows/macos.yml | 6 ++++-- .github/workflows/windows.yml | 4 ++-- 3rdparty/README.md | 9 ++------- 3rdparty/find_dependencies.cmake | 1 + 3rdparty/stdgpu/stdgpu.cmake | 3 ++- CMakeLists.txt | 7 +++++++ cpp/open3d/io/file_format/FileASSIMP.cpp | 4 ++-- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 6 +++--- docker/docker_build.sh | 8 ++++---- python/test/t/io/test_realsense.py | 4 +--- 10 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ddda24721d3..361891e9f81 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -62,7 +62,7 @@ jobs: - name: Set up Python version uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install dependencies run: | @@ -312,12 +312,14 @@ jobs: fail-fast: false # https://github.community/t/how-to-conditionally-include-exclude-items-in-matrix-eg-based-on-branch/16853/6 matrix: - python_version: ['3.10', '3.11'] + python_version: ['3.10', '3.11', '3.12'] is_main: - ${{ github.ref == 'refs/heads/main' }} exclude: - is_main: false python_version: '3.10' + - is_main: false + python_version: '3.11' steps: - name: Checkout source code # for gh release upload uses: actions/checkout@v4 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 807bc5be5be..57faf093a81 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -25,6 +25,7 @@ env: STOOLS_VER: "67.3.2" JEDI_VER: "0.17.2" # https://github.com/ipython/ipython/issues/12740 IDNA_VER: "2.8" # https://github.com/psf/requests/issues/5710 + CUDA_VERSION: "11.8.0" SRC_DIR: "D:\\a\\open3d\\open3d" BUILD_DIR: "C:\\Open3D\\build" NPROC: 2 @@ -47,7 +48,6 @@ jobs: STATIC_RUNTIME: ON include: - BUILD_CUDA_MODULE: ON - CUDA_VERSION: 11.8.0 env: BUILD_WEBRTC: ${{ ( matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' ) && 'ON' || 'OFF' }} @@ -64,7 +64,7 @@ jobs: if: ${{ matrix.BUILD_CUDA_MODULE == 'ON' }} run: | # Define variables - $CUDA_VER_FULL = "${{ matrix.CUDA_VERSION }}" + $CUDA_VER_FULL = "${{ env.CUDA_VERSION }}" $CUDA_VER_ARR = $CUDA_VER_FULL.Split(".") $CUDA_VER = "$($CUDA_VER_ARR[0]).$($CUDA_VER_ARR[1])" $CUDA_VER_ID = "$($CUDA_VER_ARR[0])_$($CUDA_VER_ARR[1])" diff --git a/3rdparty/README.md b/3rdparty/README.md index c7c9f94a74e..b5c915fc79b 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -68,7 +68,7 @@ tinyobjloader v1.0.0 MIT license Tiny but powerful single file wavefront obj loader https://github.com/syoyo/tinyobjloader -------------------------------------------------------------------------------- -pybind11 v2.6.2 BSD license +pybind11 v2.13.1 BSD license Python binding for C++11 https://github.com/pybind/pybind11 -------------------------------------------------------------------------------- @@ -76,11 +76,6 @@ PoissonReco 12.0 BSD license Poisson Surface Reconstruction https://github.com/mkazhdan/PoissonRecon -------------------------------------------------------------------------------- -Parallel STL 20190522 Apache-2 license -An implementation of the C++ standard library algorithms with support for -execution policies -https://github.com/oneapi-src/oneDPL --------------------------------------------------------------------------------- CUB 1.8.0 BSD license A flexible library of cooperative threadblock primitives and other utilities for CUDA kernel programming @@ -113,7 +108,7 @@ As an alternative, you can modify 3rdparty/zeromq/zeromq_build.cmake to fetch zeromq from our fork https://github.com/isl-org/libzmq -------------------------------------------------------------------------------- -embree 3.13.0 Apache-2 license +embree 4.3.1 Apache-2 license Embree is a collection of high-performance ray tracing kernels https://github.com/embree/embree -------------------------------------------------------------------------------- diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index f3f68698bd3..eb47efb68a2 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -168,6 +168,7 @@ set(ExternalProject_CMAKE_ARGS -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER} -DCMAKE_CUDA_COMPILER_LAUNCHER=${CMAKE_CUDA_COMPILER_LAUNCHER} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} + -DCMAKE_CUDA_FLAGS=${CMAKE_CUDA_FLAGS} -DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION} -DCMAKE_INSTALL_LIBDIR=${Open3D_INSTALL_LIB_DIR} # Always build 3rd party code in Release mode. Ignored by multi-config diff --git a/3rdparty/stdgpu/stdgpu.cmake b/3rdparty/stdgpu/stdgpu.cmake index f486fc3d4ce..8a543d11a33 100644 --- a/3rdparty/stdgpu/stdgpu.cmake +++ b/3rdparty/stdgpu/stdgpu.cmake @@ -7,7 +7,8 @@ include(ExternalProject) ExternalProject_Add( ext_stdgpu PREFIX stdgpu - URL https://github.com/stotko/stdgpu/archive/e10f6f3ccc9902d693af4380c3bcd188ec34a2e6.tar.gz + # Aug 2024 + URL https://github.com/stotko/stdgpu/archive/d57a4905f64fea0e06c56194131ad7a1c7291a16.tar.gz URL_HASH SHA256=7bb2733b099f7cedc86d2aee7830d128ac1222cfafa34cbaa4e818483c0a93f6 DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/stdgpu" UPDATE_COMMAND "" diff --git a/CMakeLists.txt b/CMakeLists.txt index 08f4252ba2f..1d5a7ca509e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,6 +402,13 @@ cmake_language(EVAL CODE "cmake_language(DEFER CALL open3d_patch_findthreads_mod # Build CUDA module by default if CUDA is available if(BUILD_CUDA_MODULE) + # Suppress nvcc unsupported compiler error for MSVC 2022 with CUDA 11.7 to 12.4 + # https://forums.developer.nvidia.com/t/problems-with-latest-vs2022-update/294150/12 + if (MSVC AND MSVC_VERSION VERSION_LESS_EQUAL "1949") + # Set this before any CUDA checks + set(CMAKE_CUDA_FLAGS "--allow-unsupported-compiler" CACHE STRING "Additional flags for nvcc" FORCE) + message(WARNING "Using --allow-unsupported-compiler flag for nvcc with MSVC 2022.") + endif() if(BUILD_COMMON_CUDA_ARCHS) if (CMAKE_CUDA_ARCHITECTURES) message(STATUS "Building with user-provided architectures: ${CMAKE_CUDA_ARCHITECTURES}") diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index 92a2fda8077..a797b8aa1d3 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -170,7 +170,7 @@ bool ReadTriangleMeshUsingASSIMP( const auto* scene = importer.ReadFile(filename.c_str(), post_process_flags); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("ReadTriangleMeshUsingASSIMP error: {}", importer.GetErrorString()); return false; } @@ -326,7 +326,7 @@ bool ReadModelUsingAssimp(const std::string& filename, const auto* scene = importer.ReadFile(filename.c_str(), kPostProcessFlags_fast); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("ReadTriangleMeshUsingASSIMP error: {}", importer.GetErrorString()); return false; } diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 514d59fca1b..90378622621 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -58,7 +58,7 @@ bool ReadTriangleMeshUsingASSIMP( const auto* scene = importer.ReadFile(filename.c_str(), post_process_flags); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("ReadTriangleMeshUsingASSIMP error: {}", importer.GetErrorString()); return false; } @@ -242,7 +242,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, if (mesh.HasTriangleColors()) { utility::LogWarning( "Exporting triangle colors is not supported. Please convert to " - "vertex colors or export to a format that supporst it."); + "vertex colors or export to a format that supports it."); } Assimp::Exporter exporter; @@ -470,7 +470,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, // Export if (exporter.Export(ai_scene.get(), "glb2", filename.c_str()) == AI_FAILURE) { - utility::LogWarning("Got error: {}", exporter.GetErrorString()); + utility::LogWarning("WriteTriangleMeshUsingASSIMP error: {}", exporter.GetErrorString()); return false; } diff --git a/docker/docker_build.sh b/docker/docker_build.sh index 745473b8e53..c4be56e9e6d 100755 --- a/docker/docker_build.sh +++ b/docker/docker_build.sh @@ -322,7 +322,7 @@ ci_build() { export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 export DEVELOPER_BUILD=ON export CCACHE_TAR_NAME=open3d-ci-4-shared-focal - export PYTHON_VERSION=3.8 + export PYTHON_VERSION=3.12 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi @@ -338,7 +338,7 @@ ci_build() { export BASE_IMAGE=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu20.04 export DEVELOPER_BUILD=OFF export CCACHE_TAR_NAME=open3d-ci-4-shared-focal - export PYTHON_VERSION=3.8 + export PYTHON_VERSION=3.12 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=ON # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi @@ -385,7 +385,7 @@ cpu-shared_export_env() { export BASE_IMAGE=ubuntu:20.04 export DEVELOPER_BUILD=ON export CCACHE_TAR_NAME=open3d-ci-cpu - export PYTHON_VERSION=3.8 + export PYTHON_VERSION=3.12 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=OFF # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi @@ -417,7 +417,7 @@ cpu-shared-release_export_env() { export BASE_IMAGE=ubuntu:20.04 export DEVELOPER_BUILD=OFF export CCACHE_TAR_NAME=open3d-ci-cpu - export PYTHON_VERSION=3.8 + export PYTHON_VERSION=3.12 # no TF versions after 2.13.2 for Python 3.8 export BUILD_SHARED_LIBS=ON export BUILD_CUDA_MODULE=OFF # TODO: tensorflow tests moved here till PyTorch supports cxx11_abi diff --git a/python/test/t/io/test_realsense.py b/python/test/t/io/test_realsense.py index 2d05e1c28d7..c6df67ae181 100755 --- a/python/test/t/io/test_realsense.py +++ b/python/test/t/io/test_realsense.py @@ -64,7 +64,7 @@ def test_RSBagReader(): assert n_frames == 6 # save_frames - bag_reader = o3d.t.io.RGBDVideoReader.create("L515_test_s.bag") + bag_reader = o3d.t.io.RGBDVideoReader.create(sample_l515_bag.path) bag_reader.save_frames("L515_test_s") # Use issubset() since there may be other OS files present assert {'depth', 'color', @@ -79,8 +79,6 @@ def test_RSBagReader(): }.issubset(os.listdir('L515_test_s/color')) shutil.rmtree("L515_test_s") - if os.name != 'nt': # Permission error in Windows - os.remove("L515_test_s.bag") # Test recording from a RealSense camera, if one is connected From 18a47ef53dedd42bf729441f32fb759a9bb7b6a7 Mon Sep 17 00:00:00 2001 From: Nikolaas Steenbergen Date: Wed, 21 Aug 2024 17:33:49 +0200 Subject: [PATCH 57/82] Add color for sampling from mesh (#6842) * First version, colors are not sampled correctly * Fixed uv lookup * Apply style * Add poissant sampling * Add changelog entry * Add triangle_material_id * apply style --- CHANGELOG.md | 2 +- cpp/open3d/geometry/TriangleMesh.cpp | 35 ++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 829d890ef53..d032cdedbf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ## Main - +- Fix TriangleMesh::SamplePointsUniformly and TriangleMesh::SamplePointsPoissonDisk now sampling colors from mesh if available (PR #6842) - Fix TriangleMesh::SamplePointsUniformly not sampling triangle meshes uniformly (PR #6653) - Fix tensor based TSDF integration example. - Use GLIBCXX_USE_CXX11_ABI=ON by default diff --git a/cpp/open3d/geometry/TriangleMesh.cpp b/cpp/open3d/geometry/TriangleMesh.cpp index 7078a675c4f..c73fc3490e8 100644 --- a/cpp/open3d/geometry/TriangleMesh.cpp +++ b/cpp/open3d/geometry/TriangleMesh.cpp @@ -443,6 +443,10 @@ std::shared_ptr TriangleMesh::SamplePointsUniformlyImpl( // sample point cloud bool has_vert_normal = HasVertexNormals(); bool has_vert_color = HasVertexColors(); + bool has_textures_ = HasTextures(); + bool has_triangle_uvs_ = HasTriangleUvs(); + bool has_triangle_material_ids_ = HasTriangleMaterialIds(); + utility::random::UniformRealGenerator uniform_generator(0.0, 1.0); auto pcd = std::make_shared(); pcd->points_.resize(number_of_points); @@ -452,10 +456,9 @@ std::shared_ptr TriangleMesh::SamplePointsUniformlyImpl( if (use_triangle_normal && !HasTriangleNormals()) { ComputeTriangleNormals(true); } - if (has_vert_color) { + if (has_vert_color || (has_textures_ && has_triangle_uvs_)) { pcd->colors_.resize(number_of_points); } - for (size_t point_idx = 0; point_idx < number_of_points; ++point_idx) { double r1 = uniform_generator(); double r2 = uniform_generator(); @@ -475,13 +478,33 @@ std::shared_ptr TriangleMesh::SamplePointsUniformlyImpl( if (use_triangle_normal) { pcd->normals_[point_idx] = triangle_normals_[tidx]; } - if (has_vert_color) { + // if there is no texture, sample from vertex color + if (has_vert_color && !has_textures_ && !has_triangle_uvs_) { pcd->colors_[point_idx] = a * vertex_colors_[triangle(0)] + b * vertex_colors_[triangle(1)] + c * vertex_colors_[triangle(2)]; } + // if there is a texture, sample from texture instead + if (has_textures_ && has_triangle_uvs_ && has_triangle_material_ids_) { + Eigen::Vector2d uv = a * triangle_uvs_[3 * tidx] + + b * triangle_uvs_[3 * tidx + 1] + + c * triangle_uvs_[3 * tidx + 2]; + int material_id = triangle_material_ids_[tidx]; + int w = textures_[material_id].width_; + int h = textures_[material_id].height_; + + pcd->colors_[point_idx] = Eigen::Vector3d( + (double)*(textures_[material_id].PointerAt( + uv(0) * w, uv(1) * h, 0)) / + 255, + (double)*(textures_[material_id].PointerAt( + uv(0) * w, uv(1) * h, 1)) / + 255, + (double)*(textures_[material_id].PointerAt( + uv(0) * w, uv(1) * h, 2)) / + 255); + } } - return pcd; } @@ -621,6 +644,8 @@ std::shared_ptr TriangleMesh::SamplePointsPoissonDisk( // update pcl bool has_vert_normal = pcl->HasNormals(); bool has_vert_color = pcl->HasColors(); + bool has_textures_ = HasTextures(); + bool has_triangle_uvs_ = HasTriangleUvs(); int next_free = 0; for (size_t idx = 0; idx < pcl->points_.size(); ++idx) { if (!deleted[idx]) { @@ -628,7 +653,7 @@ std::shared_ptr TriangleMesh::SamplePointsPoissonDisk( if (has_vert_normal) { pcl->normals_[next_free] = pcl->normals_[idx]; } - if (has_vert_color) { + if (has_vert_color || (has_textures_ && has_triangle_uvs_)) { pcl->colors_[next_free] = pcl->colors_[idx]; } next_free++; From 945e35dab2a85bd0e1019b74aea0352c5acc1322 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Thu, 22 Aug 2024 16:36:01 -0700 Subject: [PATCH 58/82] Always obey CMAKE_CUDA_ARCHITECTURES if used. Downgrade stdgpu to Jun 19 2024 version due to stdgpu::pair error run CI with ss/python-3.12 branch --- .github/workflows/macos.yml | 1 + .github/workflows/ubuntu-wheel.yml | 1 + .github/workflows/windows.yml | 1 + 3rdparty/stdgpu/stdgpu.cmake | 7 ++++--- CMakeLists.txt | 22 +++++++++++----------- docker/Dockerfile.ci | 3 ++- docker/Dockerfile.wheel | 3 ++- util/ci_utils.sh | 3 +-- 8 files changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 361891e9f81..1cc7bdb35f3 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -408,6 +408,7 @@ jobs: uses: actions/checkout@v4 with: repository: isl-org/Open3D-ML + ref: ss/python-3.12 # remove before merge path: ${{ env.OPEN3D_ML_ROOT }} - name: Download wheels diff --git a/.github/workflows/ubuntu-wheel.yml b/.github/workflows/ubuntu-wheel.yml index 411fe6a37bb..c55bc1d65c6 100644 --- a/.github/workflows/ubuntu-wheel.yml +++ b/.github/workflows/ubuntu-wheel.yml @@ -153,6 +153,7 @@ jobs: uses: actions/checkout@v4 with: repository: isl-org/Open3D-ML + ref: ss/python-3.12 # remove before merge path: ${{ env.OPEN3D_ML_ROOT }} - name: Download wheels uses: actions/download-artifact@v4 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b7b2226cb85..9c8d812c55f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -110,6 +110,7 @@ jobs: if (${env:DEVELOPER_BUILD} -ne "OFF") { ${env:DEVELOPER_BUILD}="ON" } + cmake --version cmake -G "Visual Studio 16 2019" -A x64 ` -DDEVELOPER_BUILD="${env:DEVELOPER_BUILD}" ` -DCMAKE_SYSTEM_VERSION="10.0.19041.0" ` diff --git a/3rdparty/stdgpu/stdgpu.cmake b/3rdparty/stdgpu/stdgpu.cmake index 4ee85e32730..fb9a64e09fd 100644 --- a/3rdparty/stdgpu/stdgpu.cmake +++ b/3rdparty/stdgpu/stdgpu.cmake @@ -7,9 +7,9 @@ include(ExternalProject) ExternalProject_Add( ext_stdgpu PREFIX stdgpu - # Aug 2024 - URL https://github.com/stotko/stdgpu/archive/d57a4905f64fea0e06c56194131ad7a1c7291a16.tar.gz - URL_HASH SHA256=c96775aafb44f5a36af96a4b7ec8b2de6b0587c0cf9ed76705306b6dc3b950b5 + # Jun 20 2024. Later versions need CUDA 11.5 and an API update (stdgpu::pair) + URL https://github.com/stotko/stdgpu/archive/1b6a3319f1fbf180166e1bbc1d75f69ab622a0a0.tar.gz + URL_HASH SHA256=faa3bf9cbe49ef9cc09e2e07e60d10bbf3b896edb6089c920bebe0f850fd95e4 DOWNLOAD_DIR "${OPEN3D_THIRD_PARTY_DOWNLOAD_DIR}/stdgpu" UPDATE_COMMAND "" CMAKE_ARGS @@ -18,6 +18,7 @@ ExternalProject_Add( -DSTDGPU_BUILD_SHARED_LIBS=OFF -DSTDGPU_BUILD_EXAMPLES=OFF -DSTDGPU_BUILD_TESTS=OFF + -DSTDGPU_BUILD_BENCHMARKS=OFF -DSTDGPU_ENABLE_CONTRACT_CHECKS=OFF -DTHRUST_INCLUDE_DIR=${CUDAToolkit_INCLUDE_DIRS} ${ExternalProject_CMAKE_ARGS_hidden} diff --git a/CMakeLists.txt b/CMakeLists.txt index a9d0e3b0ee1..f781ea2e708 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -394,10 +394,10 @@ if(BUILD_CUDA_MODULE) set(CMAKE_CUDA_FLAGS "--allow-unsupported-compiler" CACHE STRING "Additional flags for nvcc" FORCE) message(WARNING "Using --allow-unsupported-compiler flag for nvcc with MSVC 2022.") endif() - if(BUILD_COMMON_CUDA_ARCHS) - if (CMAKE_CUDA_ARCHITECTURES) - message(STATUS "Building with user-provided architectures: ${CMAKE_CUDA_ARCHITECTURES}") - else() + if (CMAKE_CUDA_ARCHITECTURES) + message(STATUS "Building with user-provided CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}") + else() + if(BUILD_COMMON_CUDA_ARCHS) # Build with all supported architectures for previous 2 generations and # M0 (minor=0) architectures for previous generations (including # deprecated). Note that cubin for M0 runs on GPUs with architecture Mx. @@ -417,14 +417,14 @@ if(BUILD_CUDA_MODULE) set(CMAKE_CUDA_ARCHITECTURES 30-real 50-real 60-real 70-real 75) # Kepler, Maxwell, Pascal, Turing endif() message(STATUS "Using CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}") - endif() - else() - execute_process(COMMAND nvidia-smi RESULT_VARIABLE NVIDIA_CHECK OUTPUT_QUIET) - if (NVIDIA_CHECK EQUAL 0) - message(STATUS "Building with native CUDA architecture.") - set(CMAKE_CUDA_ARCHITECTURES native) else() - message(WARNING "No CUDA GPU detected. Building with CMake default CUDA architecture.") + execute_process(COMMAND nvidia-smi RESULT_VARIABLE NVIDIA_CHECK OUTPUT_QUIET) + if (NVIDIA_CHECK EQUAL 0) + message(STATUS "Building with native CUDA architecture.") + set(CMAKE_CUDA_ARCHITECTURES native) + else() + message(WARNING "No CUDA GPU detected. Building with CMake default CUDA architecture.") + endif() endif() endif() enable_language(CUDA) diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index cd52a838882..2c0daa949f1 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -139,7 +139,8 @@ RUN CCACHE_DIR=$(ccache -p | grep cache_dir | grep -oE "[^ ]+$") \ # Checkout Open3D-ML main branch # TODO: We may add support for local Open3D-ML repo or pinned ML repo tag ENV OPEN3D_ML_ROOT=/root/Open3D-ML -RUN git clone --depth 1 https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} +# Remove branch before merge +RUN git clone --depth 1 --branch ss/python-3.12 https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} # Open3D repo # Always keep /root/Open3D as the WORKDIR diff --git a/docker/Dockerfile.wheel b/docker/Dockerfile.wheel index 3bcfe4a6a94..9d86856c177 100644 --- a/docker/Dockerfile.wheel +++ b/docker/Dockerfile.wheel @@ -98,7 +98,8 @@ RUN which python \ # Checkout Open3D-ML main branch # TODO: We may add support for local Open3D-ML repo or pinned ML repo tag ENV OPEN3D_ML_ROOT=/root/Open3D-ML -RUN git clone https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} +# Remove branch before merge +RUN git clone --depth 1 --branch ss/python-3.12 https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} # Open3D C++ dependencies # Done before copying the full Open3D directory for better Docker caching diff --git a/util/ci_utils.sh b/util/ci_utils.sh index a3785ea8582..4d1b96faba9 100644 --- a/util/ci_utils.sh +++ b/util/ci_utils.sh @@ -365,8 +365,7 @@ install_docs_dependencies() { echo Installing Open3D-ML dependencies from "${OPEN3D_ML_ROOT}" python -m pip install -r "${OPEN3D_ML_ROOT}/requirements.txt" python -m pip install -r "${OPEN3D_ML_ROOT}/requirements-torch.txt" - python -m pip install -r "${OPEN3D_ML_ROOT}/requirements-tensorflow.txt" || - python -m pip install tensorflow # FIXME: Remove after Open3D-ML update + python -m pip install -r "${OPEN3D_ML_ROOT}/requirements-tensorflow.txt" else echo OPEN3D_ML_ROOT="$OPEN3D_ML_ROOT" not specified or invalid. Skipping ML dependencies. fi From 2222fcb87b5e5ab6146070b3f84e00eeaa763f5a Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Wed, 28 Aug 2024 14:37:03 -0700 Subject: [PATCH 59/82] use shared_ptr to capture array in lambda --- cpp/pybind/core/tensor_converter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/pybind/core/tensor_converter.cpp b/cpp/pybind/core/tensor_converter.cpp index 018d857e5ed..1e07ad91d0c 100644 --- a/cpp/pybind/core/tensor_converter.cpp +++ b/cpp/pybind/core/tensor_converter.cpp @@ -106,10 +106,10 @@ Tensor PyArrayToTensor(py::array array, bool inplace) { Dtype dtype = pybind_utils::ArrayFormatToDtype(info.format, info.itemsize); Device device("CPU:0"); - array.inc_ref(); - std::function deleter = [array](void*) -> void { + auto shared_array = std::make_shared(array); + std::function deleter = [shared_array](void*) mutable -> void { py::gil_scoped_acquire acquire; - array.dec_ref(); + shared_array.reset(); }; auto blob = std::make_shared(device, info.ptr, deleter); Tensor t_inplace(shape, strides, info.ptr, dtype, blob); From 35ee51e10064c2a897dea6c18bf124aac9a24db3 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Sun, 1 Sep 2024 20:57:41 +0200 Subject: [PATCH 60/82] fix tf ml ops tests on macos --- python/open3d/ml/tf/python/layers/neighbor_search.py | 9 +++++++-- python/test/ml_ops/test_sparseconv.py | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/python/open3d/ml/tf/python/layers/neighbor_search.py b/python/open3d/ml/tf/python/layers/neighbor_search.py index 8a9059ef485..d66b28fcf32 100644 --- a/python/open3d/ml/tf/python/layers/neighbor_search.py +++ b/python/open3d/ml/tf/python/layers/neighbor_search.py @@ -121,6 +121,11 @@ def call(self, queries_row_splits = queries.row_splits queries = queries.values + if isinstance(radius, tf.Tensor): + radius_ = tf.cast(radius, points.dtype) + else: + radius_ = radius + if points_row_splits is None: points_row_splits = tf.cast(tf.stack([0, tf.shape(points)[0]]), dtype=tf.int64) @@ -131,7 +136,7 @@ def call(self, table = ops.build_spatial_hash_table( max_hash_table_size=self.max_hash_table_size, points=points, - radius=radius, + radius=radius_, points_row_splits=points_row_splits, hash_table_size_factor=hash_table_size_factor) else: @@ -142,7 +147,7 @@ def call(self, metric=self.metric, points=points, queries=queries, - radius=radius, + radius=radius_, points_row_splits=points_row_splits, queries_row_splits=queries_row_splits, hash_table_splits=table.hash_table_splits, diff --git a/python/test/ml_ops/test_sparseconv.py b/python/test/ml_ops/test_sparseconv.py index 1fd6ed5058b..93bd8b53f3a 100644 --- a/python/test/ml_ops/test_sparseconv.py +++ b/python/test/ml_ops/test_sparseconv.py @@ -91,7 +91,7 @@ def bias_initializer(a): y = mltest.run_op(ml, ml.device, True, sparse_conv, inp_features, inp_positions * voxel_size, out_positions * voxel_size, - voxel_size, inp_importance) + voxel_size=voxel_size, inp_importance=inp_importance) # Compare the output to a standard 3d conv # store features in a volume to use standard 3d convs @@ -212,7 +212,7 @@ def test_compare_to_conv3d_batches(ml, dtype, kernel_size, out_channels, y = mltest.run_op(ml, ml.device, True, sparse_conv, inp_features, inp_positions * voxel_size, out_positions * voxel_size, - voxel_size, inp_importance) + voxel_size=voxel_size, inp_importance=inp_importance) for idx in range(batch_size): inp_pos = inp_positions[idx].numpy() inp_feat = inp_features[idx].numpy() @@ -338,7 +338,7 @@ def bias_initializer(a): y = mltest.run_op(ml, ml.device, True, sparse_conv_transpose, inp_features, inp_positions * voxel_size, out_positions * voxel_size, - voxel_size, out_importance) + voxel_size=voxel_size, out_importance=out_importance) # Compare the output to a standard 3d conv # store features in a volume to use standard 3d convs @@ -465,7 +465,7 @@ def test_compare_to_conv3dtranspose_batches(ml, dtype, kernel_size, y = mltest.run_op(ml, ml.device, True, sparse_conv_transpose, inp_features, inp_positions * voxel_size, out_positions * voxel_size, - voxel_size, out_importance) + voxel_size=voxel_size, out_importance=out_importance) for idx in range(batch_size): inp_pos = inp_positions[idx].numpy() inp_feat = inp_features[idx].numpy() From 594f8202161dc8cb1becd7c072b54502b2b60994 Mon Sep 17 00:00:00 2001 From: Bharath Kumar Date: Mon, 2 Sep 2024 08:56:45 -0700 Subject: [PATCH 61/82] [Mesh] TriangleMesh's "+=" operator appends UVs regardless of the presence of existing features (#6728) * [Mesh] TriangleMesh's "+=" operator applies regardless of the presence of existing features. * added change log * fixed changelog * add special case for += operator with empty mesh * remove blank line in changelog * adding unit test to test += operator for triangle mesh --------- Co-authored-by: Benjamin Ummenhofer --- CHANGELOG.md | 1 + cpp/open3d/geometry/TriangleMesh.cpp | 18 ++-- cpp/tests/geometry/TriangleMesh.cpp | 123 ++++++++++++++++++++++++++- 3 files changed, 133 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d032cdedbf4..022c41d17fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - Fix KDTreeFlann possibly using a dangling pointer instead of internal storage and simplified its members (PR #6734) - Fix RANSAC early stop if no inliers in a specific iteration (PR #6789) - Fix segmentation fault (infinite recursion) of DetectPlanarPatches if multiple points have same coordinates (PR #6794) +- `TriangleMesh`'s `+=` operator appends UVs regardless of the presence of existing features (PR #6728) - Fix build with fmt v10.2.0 (#6783) - Fix segmentation fault (lambda reference capture) of VisualizerWithCustomAnimation::Play (PR #6804) - Add O3DVisualizer API to enable collapse control of verts in the side panel (PR #6865) diff --git a/cpp/open3d/geometry/TriangleMesh.cpp b/cpp/open3d/geometry/TriangleMesh.cpp index c73fc3490e8..9e909858736 100644 --- a/cpp/open3d/geometry/TriangleMesh.cpp +++ b/cpp/open3d/geometry/TriangleMesh.cpp @@ -52,9 +52,12 @@ TriangleMesh &TriangleMesh::Rotate(const Eigen::Matrix3d &R, TriangleMesh &TriangleMesh::operator+=(const TriangleMesh &mesh) { if (mesh.IsEmpty()) return (*this); - bool add_textures = HasTriangleUvs() && HasTextures() && - HasTriangleMaterialIds() && mesh.HasTriangleUvs() && - mesh.HasTextures() && mesh.HasTriangleMaterialIds(); + bool is_empty = IsEmpty(); + bool add_triangle_uvs = + mesh.HasTriangleUvs() && (HasTriangleUvs() || is_empty); + bool add_textures_and_material_ids = + mesh.HasTextures() && mesh.HasTriangleMaterialIds() && + ((HasTextures() && HasTriangleMaterialIds()) || is_empty); size_t old_vert_num = vertices_.size(); MeshBase::operator+=(mesh); size_t old_tri_num = triangles_.size(); @@ -77,19 +80,21 @@ TriangleMesh &TriangleMesh::operator+=(const TriangleMesh &mesh) { if (HasAdjacencyList()) { ComputeAdjacencyList(); } - if (add_textures) { + if (add_triangle_uvs) { size_t old_tri_uv_num = triangle_uvs_.size(); triangle_uvs_.resize(old_tri_uv_num + mesh.triangle_uvs_.size()); for (size_t i = 0; i < mesh.triangle_uvs_.size(); i++) { triangle_uvs_[old_tri_uv_num + i] = mesh.triangle_uvs_[i]; } - + } else { + triangle_uvs_.clear(); + } + if (add_textures_and_material_ids) { size_t old_tex_num = textures_.size(); textures_.resize(old_tex_num + mesh.textures_.size()); for (size_t i = 0; i < mesh.textures_.size(); i++) { textures_[old_tex_num + i] = mesh.textures_[i]; } - size_t old_mat_id_num = triangle_material_ids_.size(); triangle_material_ids_.resize(old_mat_id_num + mesh.triangle_material_ids_.size()); @@ -98,7 +103,6 @@ TriangleMesh &TriangleMesh::operator+=(const TriangleMesh &mesh) { mesh.triangle_material_ids_[i] + (int)old_tex_num; } } else { - triangle_uvs_.clear(); textures_.clear(); triangle_material_ids_.clear(); } diff --git a/cpp/tests/geometry/TriangleMesh.cpp b/cpp/tests/geometry/TriangleMesh.cpp index a562059cbc2..c00084d7ba4 100644 --- a/cpp/tests/geometry/TriangleMesh.cpp +++ b/cpp/tests/geometry/TriangleMesh.cpp @@ -22,8 +22,8 @@ namespace tests { /// \param eq_triangle_vertex_order If true then triangles are only equal if the /// order of the vertices is the same. If false any permutation of the triangle /// indices is allowed. -void ExpectMeshEQ(const open3d::geometry::TriangleMesh& mesh0, - const open3d::geometry::TriangleMesh& mesh1, +void ExpectMeshEQ(const open3d::geometry::TriangleMesh &mesh0, + const open3d::geometry::TriangleMesh &mesh1, double threshold = 1e-6, bool eq_triangle_vertex_order = true) { ExpectEQ(mesh0.vertices_, mesh1.vertices_, threshold); @@ -599,6 +599,125 @@ TEST(TriangleMesh, OperatorADD) { } } +TEST(TriangleMesh, OperatorAdditionAssignment) { + const size_t size = 100; + const size_t texture_size = 10; + + // Define the minimum and maximum bounds for random data generation + Eigen::Vector3d dmin(0.0, 0.0, 0.0); + Eigen::Vector3d dmax(1000.0, 1000.0, 1000.0); + Eigen::Vector3i imin(0, 0, 0); + Eigen::Vector3i imax(size - 1, size - 1, size - 1); + Eigen::Vector2d uvmin(0.0, 0.0); + Eigen::Vector2d uvmax(1.0, 1.0); + + // Mesh 0 contains only UVs but no textures and material ids + geometry::TriangleMesh tm0; + tm0.vertices_.resize(size); + tm0.triangles_.resize(size); + tm0.triangle_uvs_.resize(3 * size); + + // Mesh 1 contains all of UVS, textures and material ids + geometry::TriangleMesh tm1; + tm1.vertices_.resize(size); + tm1.triangles_.resize(size); + tm1.triangle_uvs_.resize(3 * size); + tm1.triangle_material_ids_.resize(size); + tm1.textures_.resize(texture_size); + + // Mesh 2 does not contains any of UVs, textures or material ids + geometry::TriangleMesh tm2; + tm2.vertices_.resize(size); + tm2.triangles_.resize(size); + + // Randomly generate data for tm0, tm1 and tm2 + Rand(tm0.vertices_, dmin, dmax, 0); + Rand(tm0.triangles_, imin, imax, 0); + std::vector + tm0_triangle_uvs(3 * size); + Rand(tm0_triangle_uvs, uvmin, uvmax, 0); + tm0.triangle_uvs_ = std::vector(tm0_triangle_uvs.begin(), + tm0_triangle_uvs.end()); + + Rand(tm1.vertices_, dmin, dmax, 0); + Rand(tm1.triangles_, imin, imax, 0); + std::vector + tm1_triangle_uvs(3 * size); + Rand(tm1_triangle_uvs, uvmin, uvmax, 0); + tm1.triangle_uvs_ = std::vector(tm1_triangle_uvs.begin(), + tm1_triangle_uvs.end()); + for (size_t i = 0; i < texture_size; i++) { + geometry::Image texture_image; + texture_image.Prepare(100, 100, 3, 1); + Rand(texture_image.data_, 0, 255, 0); + tm1.textures_[i] = texture_image; + } + Rand(tm1.triangle_material_ids_, 0, texture_size, 0); + + Rand(tm2.vertices_, dmin, dmax, 0); + Rand(tm2.triangles_, imin, imax, 0); + + geometry::TriangleMesh temp_tm0 = tm0; + geometry::TriangleMesh temp_tm1 = tm1; + geometry::TriangleMesh temp_tm2 = tm2; + // Function to reset values of temp_tm0, temp_tm1 and temp_tm2 + auto reset_values = [&tm0, &tm1, &tm2](geometry::TriangleMesh &temp_tm0, + geometry::TriangleMesh &temp_tm1, + geometry::TriangleMesh &temp_tm2) { + temp_tm0 = tm0; + temp_tm1 = tm1; + temp_tm2 = tm2; + }; + + // Test addition of tm1 with itself + temp_tm1 += tm1; + EXPECT_EQ(temp_tm1.triangles_.size(), 2 * size); + EXPECT_EQ(temp_tm1.triangle_uvs_.size(), 6 * size); + EXPECT_EQ(temp_tm1.vertices_.size(), 2 * size); + EXPECT_EQ(temp_tm1.textures_.size(), 2 * texture_size); + EXPECT_EQ(temp_tm1.triangle_material_ids_.size(), 2 * size); + for (size_t i = 0; i < size; i++) { + ExpectEQ(temp_tm1.vertices_[i], temp_tm1.vertices_[i + size]); + ExpectEQ(temp_tm1.triangles_[i], + (Eigen::Vector3i)(temp_tm1.triangles_[i + size] - + Eigen::Vector3i(size, size, size))); + EXPECT_EQ(temp_tm1.triangle_material_ids_[i], + temp_tm1.triangle_material_ids_[i + size] - texture_size); + } + for (size_t i = 0; i < 3 * size; i++) { + ExpectEQ(temp_tm1.triangle_uvs_[i], + temp_tm1.triangle_uvs_[i + 3 * size]); + } + for (size_t i = 0; i < texture_size; i++) { + ExpectEQ(temp_tm1.textures_[i].data_, + temp_tm1.textures_[i + texture_size].data_); + } + + // Test addition of tm0 and tm1 + reset_values(temp_tm0, temp_tm1, temp_tm2); + temp_tm0 += tm1; + temp_tm1 += tm0; + ExpectMeshEQ(temp_tm0, temp_tm1); + EXPECT_EQ(temp_tm0.textures_.size(), 0); + EXPECT_EQ(temp_tm0.triangle_material_ids_.size(), 0); + + // Test addition of tm1 and tm2 + reset_values(temp_tm0, temp_tm1, temp_tm2); + temp_tm1 += tm2; + temp_tm2 += tm1; + ExpectMeshEQ(temp_tm1, temp_tm2); + EXPECT_EQ(temp_tm0.textures_.size(), 0); + EXPECT_EQ(temp_tm0.triangle_material_ids_.size(), 0); + + // Test addition of tm2 and tm0 + reset_values(temp_tm0, temp_tm1, temp_tm2); + temp_tm2 += tm0; + temp_tm0 += tm2; + ExpectMeshEQ(temp_tm2, temp_tm0); + EXPECT_EQ(temp_tm0.textures_.size(), 0); + EXPECT_EQ(temp_tm0.triangle_material_ids_.size(), 0); +} + TEST(TriangleMesh, ComputeTriangleNormals) { std::vector ref = {{-0.119231, 0.738792, 0.663303}, {-0.115181, 0.730934, 0.672658}, From d18bb95c69f13333e3c54fce4eda9ac06d91b855 Mon Sep 17 00:00:00 2001 From: Nicola Loi Date: Wed, 4 Sep 2024 07:28:35 +0200 Subject: [PATCH 62/82] Support lowercase types (i, u, f) when reading PCD files (#6930) --- CHANGELOG.md | 1 + cpp/open3d/io/file_format/FilePCD.cpp | 21 ++++++++++++--------- cpp/open3d/t/io/file_format/FilePCD.cpp | 25 +++++++++++++++---------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 022c41d17fb..c58729b74da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - Split pybind declarations/definitions to avoid C++ types in Python docs (PR #6869) - Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (PR #6898) - Fix projection of point cloud to Depth/RGBD image if no position attribute is provided (PR #6880) +- Support lowercase types when reading PCD files (PR #6930) ## 0.13 diff --git a/cpp/open3d/io/file_format/FilePCD.cpp b/cpp/open3d/io/file_format/FilePCD.cpp index ce04f2e2d5b..17aa080cd29 100644 --- a/cpp/open3d/io/file_format/FilePCD.cpp +++ b/cpp/open3d/io/file_format/FilePCD.cpp @@ -220,7 +220,8 @@ bool ReadPCDHeader(FILE *file, PCDHeader &header) { double UnpackBinaryPCDElement(const char *data_ptr, const char type, const int size) { - if (type == 'I') { + const char type_uppercase = std::toupper(type, std::locale()); + if (type_uppercase == 'I') { if (size == 1) { std::int8_t data; memcpy(&data, data_ptr, sizeof(data)); @@ -236,7 +237,7 @@ double UnpackBinaryPCDElement(const char *data_ptr, } else { return 0.0; } - } else if (type == 'U') { + } else if (type_uppercase == 'U') { if (size == 1) { std::uint8_t data; memcpy(&data, data_ptr, sizeof(data)); @@ -252,7 +253,7 @@ double UnpackBinaryPCDElement(const char *data_ptr, } else { return 0.0; } - } else if (type == 'F') { + } else if (type_uppercase == 'F') { if (size == 4) { float data; memcpy(&data, data_ptr, sizeof(data)); @@ -281,11 +282,12 @@ double UnpackASCIIPCDElement(const char *data_ptr, const char type, const int size) { char *end; - if (type == 'I') { + const char type_uppercase = std::toupper(type, std::locale()); + if (type_uppercase == 'I') { return (double)std::strtol(data_ptr, &end, 0); - } else if (type == 'U') { + } else if (type_uppercase == 'U') { return (double)std::strtoul(data_ptr, &end, 0); - } else if (type == 'F') { + } else if (type_uppercase == 'F') { return std::strtod(data_ptr, &end); } return 0.0; @@ -297,13 +299,14 @@ Eigen::Vector3d UnpackASCIIPCDColor(const char *data_ptr, if (size == 4) { std::uint8_t data[4] = {0, 0, 0, 0}; char *end; - if (type == 'I') { + const char type_uppercase = std::toupper(type, std::locale()); + if (type_uppercase == 'I') { std::int32_t value = std::strtol(data_ptr, &end, 0); memcpy(data, &value, 4); - } else if (type == 'U') { + } else if (type_uppercase == 'U') { std::uint32_t value = std::strtoul(data_ptr, &end, 0); memcpy(data, &value, 4); - } else if (type == 'F') { + } else if (type_uppercase == 'F') { float value = std::strtof(data_ptr, &end); memcpy(data, &value, 4); } diff --git a/cpp/open3d/t/io/file_format/FilePCD.cpp b/cpp/open3d/t/io/file_format/FilePCD.cpp index 4aa229c0d55..2670e133d5a 100644 --- a/cpp/open3d/t/io/file_format/FilePCD.cpp +++ b/cpp/open3d/t/io/file_format/FilePCD.cpp @@ -89,30 +89,34 @@ struct WriteAttributePtr { }; static core::Dtype GetDtypeFromPCDHeaderField(char type, int size) { - if (type == 'I') { + char type_uppercase = std::toupper(type, std::locale()); + if (type_uppercase == 'I') { if (size == 1) return core::Dtype::Int8; if (size == 2) return core::Dtype::Int16; if (size == 4) return core::Dtype::Int32; if (size == 8) return core::Dtype::Int64; else - utility::LogError("Unsupported data type."); - } else if (type == 'U') { + utility::LogError("Unsupported size {} for data type {}.", size, + type); + } else if (type_uppercase == 'U') { if (size == 1) return core::Dtype::UInt8; if (size == 2) return core::Dtype::UInt16; if (size == 4) return core::Dtype::UInt32; if (size == 8) return core::Dtype::UInt64; else - utility::LogError("Unsupported data type."); - } else if (type == 'F') { + utility::LogError("Unsupported size {} for data type {}.", size, + type); + } else if (type_uppercase == 'F') { if (size == 4) return core::Dtype::Float32; if (size == 8) return core::Dtype::Float64; else - utility::LogError("Unsupported data type."); + utility::LogError("Unsupported size {} for data type {}.", size, + type); } else { - utility::LogError("Unsupported data type."); + utility::LogError("Unsupported data type {}.", type); } } @@ -305,13 +309,14 @@ static void ReadASCIIPCDColorsFromField(ReadAttributePtr &attr, if (field.size == 4) { std::uint8_t data[4] = {0}; char *end; - if (field.type == 'I') { + char type_uppercase = std::toupper(field.type, std::locale()); + if (type_uppercase == 'I') { std::int32_t value = std::strtol(data_ptr, &end, 0); std::memcpy(data, &value, sizeof(std::int32_t)); - } else if (field.type == 'U') { + } else if (type_uppercase == 'U') { std::uint32_t value = std::strtoul(data_ptr, &end, 0); std::memcpy(data, &value, sizeof(std::uint32_t)); - } else if (field.type == 'F') { + } else if (type_uppercase == 'F') { float value = std::strtof(data_ptr, &end); std::memcpy(data, &value, sizeof(float)); } From b2d1f78b971030f460a0bf9b7ec33587fba1d5b0 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Wed, 4 Sep 2024 07:29:32 +0200 Subject: [PATCH 63/82] Updated delocate-fuse to delocate-merge (got removed in delocate>=0.12.0) (#6940) --- .github/workflows/macos.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8d43e10d608..2aea46eb596 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -345,11 +345,8 @@ jobs: PYTAG="-cp$(echo ${{ env.python_version }} | tr -d '.')" mkdir universal_wheels pip install delocate - delocate-fuse -v x64_wheels/open3d-*${PYTAG}*.whl arm64_wheels/open3d-*${PYTAG}*.whl - # Normalize file name as delocate-fuse doesn't update it - OLD_WHL_NAME=$(basename x64_wheels/open3d-*${PYTAG}*.whl) - NEW_WHL_NAME=${OLD_WHL_NAME/x86_64/universal2} - mv x64_wheels/${OLD_WHL_NAME} universal_wheels/${NEW_WHL_NAME} + delocate-merge -v -w universal_wheels x64_wheels/open3d-*${PYTAG}*.whl arm64_wheels/open3d-*${PYTAG}*.whl + NEW_WHL_NAME=$(basename universal_wheels/open3d-*${PYTAG}*.whl) echo "PIP_PKG_NAME=$NEW_WHL_NAME" >> $GITHUB_ENV - name: Upload merged wheels From 170b65027244f6fa431602628d6c96b030b7b6e6 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Wed, 4 Sep 2024 09:41:21 +0200 Subject: [PATCH 64/82] apply style --- .../ml/tf/python/layers/neighbor_search.py | 4 +- python/test/ml_ops/test_sparseconv.py | 48 ++++++++++++++----- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/python/open3d/ml/tf/python/layers/neighbor_search.py b/python/open3d/ml/tf/python/layers/neighbor_search.py index d66b28fcf32..d13dd799d11 100644 --- a/python/open3d/ml/tf/python/layers/neighbor_search.py +++ b/python/open3d/ml/tf/python/layers/neighbor_search.py @@ -122,9 +122,9 @@ def call(self, queries = queries.values if isinstance(radius, tf.Tensor): - radius_ = tf.cast(radius, points.dtype) + radius_ = tf.cast(radius, points.dtype) else: - radius_ = radius + radius_ = radius if points_row_splits is None: points_row_splits = tf.cast(tf.stack([0, tf.shape(points)[0]]), diff --git a/python/test/ml_ops/test_sparseconv.py b/python/test/ml_ops/test_sparseconv.py index 93bd8b53f3a..71a019ccc50 100644 --- a/python/test/ml_ops/test_sparseconv.py +++ b/python/test/ml_ops/test_sparseconv.py @@ -89,9 +89,15 @@ def bias_initializer(a): if ml.module.__name__ == 'torch': sparse_conv.to(ml.device) - y = mltest.run_op(ml, ml.device, True, sparse_conv, inp_features, - inp_positions * voxel_size, out_positions * voxel_size, - voxel_size=voxel_size, inp_importance=inp_importance) + y = mltest.run_op(ml, + ml.device, + True, + sparse_conv, + inp_features, + inp_positions * voxel_size, + out_positions * voxel_size, + voxel_size=voxel_size, + inp_importance=inp_importance) # Compare the output to a standard 3d conv # store features in a volume to use standard 3d convs @@ -210,9 +216,15 @@ def test_compare_to_conv3d_batches(ml, dtype, kernel_size, out_channels, inp_features = tf.RaggedTensor.from_row_splits( values=inp_features, row_splits=inp_positions_row_splits) - y = mltest.run_op(ml, ml.device, True, sparse_conv, inp_features, - inp_positions * voxel_size, out_positions * voxel_size, - voxel_size=voxel_size, inp_importance=inp_importance) + y = mltest.run_op(ml, + ml.device, + True, + sparse_conv, + inp_features, + inp_positions * voxel_size, + out_positions * voxel_size, + voxel_size=voxel_size, + inp_importance=inp_importance) for idx in range(batch_size): inp_pos = inp_positions[idx].numpy() inp_feat = inp_features[idx].numpy() @@ -336,9 +348,15 @@ def bias_initializer(a): if ml.module.__name__ == 'torch': sparse_conv_transpose.to(ml.device) - y = mltest.run_op(ml, ml.device, True, sparse_conv_transpose, inp_features, - inp_positions * voxel_size, out_positions * voxel_size, - voxel_size=voxel_size, out_importance=out_importance) + y = mltest.run_op(ml, + ml.device, + True, + sparse_conv_transpose, + inp_features, + inp_positions * voxel_size, + out_positions * voxel_size, + voxel_size=voxel_size, + out_importance=out_importance) # Compare the output to a standard 3d conv # store features in a volume to use standard 3d convs @@ -463,9 +481,15 @@ def test_compare_to_conv3dtranspose_batches(ml, dtype, kernel_size, inp_features = tf.RaggedTensor.from_row_splits( values=inp_features, row_splits=inp_positions_row_splits) - y = mltest.run_op(ml, ml.device, True, sparse_conv_transpose, inp_features, - inp_positions * voxel_size, out_positions * voxel_size, - voxel_size=voxel_size, out_importance=out_importance) + y = mltest.run_op(ml, + ml.device, + True, + sparse_conv_transpose, + inp_features, + inp_positions * voxel_size, + out_positions * voxel_size, + voxel_size=voxel_size, + out_importance=out_importance) for idx in range(batch_size): inp_pos = inp_positions[idx].numpy() inp_feat = inp_features[idx].numpy() From 553ca86cc11d57324b84eb96ae7cda5c90011091 Mon Sep 17 00:00:00 2001 From: Sharon Berezalsky Date: Thu, 5 Sep 2024 01:04:30 +0300 Subject: [PATCH 65/82] Expose minimal oriented bounding box to python api (#6946) --------- Co-authored-by: Sharon Berezalsky --- cpp/pybind/geometry/boundingvolume.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cpp/pybind/geometry/boundingvolume.cpp b/cpp/pybind/geometry/boundingvolume.cpp index e178adfe19b..4ffcbf8a52d 100644 --- a/cpp/pybind/geometry/boundingvolume.cpp +++ b/cpp/pybind/geometry/boundingvolume.cpp @@ -83,6 +83,27 @@ The returned bounding box is an approximation to the minimal bounding box. open3d.geometry.OrientedBoundingBox: The oriented bounding box. The bounding box is oriented such that the axes are ordered with respect to the principal components. +)doc") + .def_static("create_from_points_minimal", + &OrientedBoundingBox::CreateFromPointsMinimal, + "points"_a, "robust"_a = false, + R"doc( +Creates the oriented bounding box with the smallest volume. + +The algorithm makes use of the fact that at least one edge of +the convex hull must be collinear with an edge of the minimum +bounding box: for each triangle in the convex hull, calculate +the minimal axis aligned box in the frame of that triangle. +at the end, return the box with the smallest volume + +Args: + points (open3d.utility.Vector3dVector): Input points. + robust (bool): If set to true uses a more robust method which works in + degenerate cases but introduces noise to the points coordinates. + +Returns: + open3d.geometry.OrientedBoundingBox: The oriented bounding box. The + bounding box is oriented such that its volume is minimized. )doc") .def("volume", &OrientedBoundingBox::Volume, "Returns the volume of the bounding box.") From 68fca39296d04cd6ed68beb71de5f30ba4fffafb Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:20:54 -0700 Subject: [PATCH 66/82] Install thrust with CUDA --- .github/workflows/windows.yml | 2 +- CMakeLists.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9c8d812c55f..e08bd1960c0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -77,7 +77,7 @@ jobs: # Installer arguments $CUDA_INSTALL_ARGS = "-s" # Required packages - $CUDA_PACKAGES = "nvcc", "visual_studio_integration", "cublas", "cublas_dev", "cudart", "cusolver", "cusolver_dev", "npp", "npp_dev" + $CUDA_PACKAGES = "nvcc", "visual_studio_integration", "cublas", "cublas_dev", "cudart", "cusolver", "cusolver_dev", "npp", "npp_dev", "thrust" $CUDA_PACKAGES.ForEach({ $CUDA_INSTALL_ARGS += " $($_)_$($CUDA_VER)" }) # Download and install CUDA echo "Downloading CUDA installer from $CUDA_URL" diff --git a/CMakeLists.txt b/CMakeLists.txt index f781ea2e708..7b047cd0b82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -392,7 +392,8 @@ if(BUILD_CUDA_MODULE) if (MSVC AND MSVC_VERSION VERSION_LESS_EQUAL "1949") # Set this before any CUDA checks set(CMAKE_CUDA_FLAGS "--allow-unsupported-compiler" CACHE STRING "Additional flags for nvcc" FORCE) - message(WARNING "Using --allow-unsupported-compiler flag for nvcc with MSVC 2022.") + message(WARNING "Using --allow-unsupported-compiler flag for nvcc with MSVC 2022. " + "Set $Env:NVCC_PREPEND_FLAGS='--allow-unsupported-compiler' if nvcc still fails.") endif() if (CMAKE_CUDA_ARCHITECTURES) message(STATUS "Building with user-provided CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}") From f4051a15085ab47f52968c4a2daf443bd95d70d8 Mon Sep 17 00:00:00 2001 From: Owen Burns Date: Wed, 11 Sep 2024 10:27:22 -0400 Subject: [PATCH 67/82] Fixed missing brackets in tensorboard summary.py (#6960) --- python/open3d/visualization/tensorboard_plugin/summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/open3d/visualization/tensorboard_plugin/summary.py b/python/open3d/visualization/tensorboard_plugin/summary.py index 0b6e254b754..4bb35fa5da7 100644 --- a/python/open3d/visualization/tensorboard_plugin/summary.py +++ b/python/open3d/visualization/tensorboard_plugin/summary.py @@ -424,7 +424,7 @@ def get_or_check_shape(prop, tensor_tuple, exp_shape): raise ValueError( f"Property {prop} tensor should have shape[{k}]" f"={s} for all elements but is " - f"{tensor.shape[k-1] for tensor in tensor_tuple}.") + f"{[tensor.shape[k-1] for tensor in tensor_tuple]}.") return shape[:2] From 631404639883639fac870cae55fdc08009b34074 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Thu, 12 Sep 2024 17:10:21 -0700 Subject: [PATCH 68/82] Remove fmt formatter for embree RTCError to fix macOS error --- cpp/open3d/t/geometry/RaycastingScene.cpp | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/cpp/open3d/t/geometry/RaycastingScene.cpp b/cpp/open3d/t/geometry/RaycastingScene.cpp index 31fdbeef856..901bc651ed0 100644 --- a/cpp/open3d/t/geometry/RaycastingScene.cpp +++ b/cpp/open3d/t/geometry/RaycastingScene.cpp @@ -33,7 +33,8 @@ typedef Eigen::Vector3f Vec3f; // Error function called by embree. void ErrorFunction(void* userPtr, enum RTCError error, const char* str) { - open3d::utility::LogError("embree error: {} {}", error, str); + open3d::utility::LogError("Embree error: {} {}", rtcGetErrorString(error), + str); } // Checks the last dim, ensures that the number of dims is >= min_ndim, checks @@ -1173,20 +1174,4 @@ uint32_t RaycastingScene::INVALID_ID() { return RTC_INVALID_GEOMETRY_ID; } } // namespace geometry } // namespace t -} // namespace open3d - -namespace fmt { -template <> -struct formatter { - template - auto format(const RTCError& c, FormatContext& ctx) { - const char* name = rtcGetErrorString(c); - return format_to(ctx.out(), name); - } - - template - constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } -}; -} // namespace fmt +} // namespace open3d \ No newline at end of file From dbb3c7d58b7af5819540dacd8ea4e0a11ab605dc Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:14:16 -0700 Subject: [PATCH 69/82] Always pre-transform vertices while reading with Assimp. (#6959) Better error messages. --- cpp/open3d/io/TriangleMeshIO.h | 1 + cpp/open3d/io/file_format/FileASSIMP.cpp | 24 ++++++++++++++-------- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 19 ++++++++++------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/cpp/open3d/io/TriangleMeshIO.h b/cpp/open3d/io/TriangleMeshIO.h index 3ee4cb0f492..32ec2aa751a 100644 --- a/cpp/open3d/io/TriangleMeshIO.h +++ b/cpp/open3d/io/TriangleMeshIO.h @@ -31,6 +31,7 @@ struct ReadTriangleMeshOptions { /// `aiProcessPreset_TargetRealtime_Fast, /// aiProcess_RemoveRedundantMaterials, aiProcess_OptimizeMeshes, /// aiProcess_PreTransformVertices`. + /// https://github.com/assimp/assimp/blob/master/include/assimp/postprocess.h /// /// Note that identical vertices will always be joined regardless of whether /// post-processing is enabled or not, which changes the number of vertices diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index 92a2fda8077..15bc3ffd26a 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -36,13 +36,16 @@ FileGeometry ReadFileGeometryTypeFBX(const std::string& path) { return FileGeometry(CONTAINS_TRIANGLES | CONTAINS_POINTS); } +// Ref: +// https://github.com/assimp/assimp/blob/master/include/assimp/postprocess.h const unsigned int kPostProcessFlags_compulsory = - aiProcess_JoinIdenticalVertices; + aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | + aiProcess_PreTransformVertices; const unsigned int kPostProcessFlags_fast = - aiProcessPreset_TargetRealtime_Fast | - aiProcess_RemoveRedundantMaterials | aiProcess_OptimizeMeshes | - aiProcess_PreTransformVertices; + kPostProcessFlags_compulsory | aiProcess_GenNormals | + aiProcess_Triangulate | aiProcess_GenUVCoords | + aiProcess_RemoveRedundantMaterials | aiProcess_OptimizeMeshes; struct TextureImages { std::shared_ptr albedo; @@ -65,7 +68,7 @@ void LoadTextures(const std::string& filename, std::string base_path = utility::filesystem::GetFileParentDirectory(filename); - auto texture_loader = [&base_path, &scene, &mat]( + auto texture_loader = [&base_path, &scene, &mat, &filename]( aiTextureType type, std::shared_ptr& img) { if (mat->GetTextureCount(type) > 0) { @@ -94,7 +97,10 @@ void LoadTextures(const std::string& filename, } } else { utility::LogWarning( - "This format of image is not supported."); + "Unsupported texture format for texture {} for " + "file {}: Only jpg and " + "png textures are supported.", + path.C_Str(), filename); } } // Else, build the path to it. @@ -170,7 +176,8 @@ bool ReadTriangleMeshUsingASSIMP( const auto* scene = importer.ReadFile(filename.c_str(), post_process_flags); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("Unable to load file {} with ASSIMP: {}", filename, + importer.GetErrorString()); return false; } @@ -326,7 +333,8 @@ bool ReadModelUsingAssimp(const std::string& filename, const auto* scene = importer.ReadFile(filename.c_str(), kPostProcessFlags_fast); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("Unable to load file {} with ASSIMP: {}", filename, + importer.GetErrorString()); return false; } diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 514d59fca1b..da23953fe79 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -35,14 +35,16 @@ namespace t { namespace io { // Split all polygons with more than 3 edges into triangles. +// Ref: +// https://github.com/assimp/assimp/blob/master/include/assimp/postprocess.h const unsigned int kPostProcessFlags_compulsory = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | - aiProcess_SortByPType; + aiProcess_SortByPType | aiProcess_PreTransformVertices; const unsigned int kPostProcessFlags_fast = - aiProcessPreset_TargetRealtime_Fast | - aiProcess_RemoveRedundantMaterials | aiProcess_OptimizeMeshes | - aiProcess_PreTransformVertices; + kPostProcessFlags_compulsory | aiProcess_GenNormals | + aiProcess_GenUVCoords | aiProcess_RemoveRedundantMaterials | + aiProcess_OptimizeMeshes; bool ReadTriangleMeshUsingASSIMP( const std::string& filename, @@ -58,7 +60,8 @@ bool ReadTriangleMeshUsingASSIMP( const auto* scene = importer.ReadFile(filename.c_str(), post_process_flags); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("Unable to load file {} with ASSIMP: {}", filename, + importer.GetErrorString()); return false; } @@ -219,7 +222,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, // Sanity checks... if (write_ascii) { utility::LogWarning( - "TriangleMesh can't be saved in ASCII fromat as .glb"); + "TriangleMesh can't be saved in ASCII format as .glb"); return false; } if (compressed) { @@ -470,7 +473,9 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, // Export if (exporter.Export(ai_scene.get(), "glb2", filename.c_str()) == AI_FAILURE) { - utility::LogWarning("Got error: {}", exporter.GetErrorString()); + utility::LogWarning( + "Got error: ({}) while writing TriangleMesh to file {}.", + exporter.GetErrorString(), filename); return false; } From 99bc91fd8f635b88564f35740450d8ac5198211f Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Sun, 15 Sep 2024 11:30:52 -0700 Subject: [PATCH 70/82] switch to miniforge --- docker/Dockerfile.ci | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index 2c0daa949f1..d910927c2cb 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -76,18 +76,19 @@ RUN apt-get update && apt-get install -y \ pkg-config \ && rm -rf /var/lib/apt/lists/* -# Miniconda or Intel conda +# Miniforge or Intel conda # The **/open3d/bin paths are used during docker run, in this way docker run # does not need to activate the environment again. -ENV PATH="/root/miniconda3/bin:${PATH}" -ENV PATH="/root/miniconda3/envs/open3d/bin:${PATH}" +ENV PATH="/root/miniforge3/bin:${PATH}" +ENV PATH="/root/miniforge3/envs/open3d/bin:${PATH}" ENV PATH="/opt/intel/oneapi/intelpython/latest/bin:${PATH}" ENV PATH="/opt/intel/oneapi/intelpython/latest/envs/open3d/bin:${PATH}" RUN if [ "${BUILD_SYCL_MODULE}" = "OFF" ]; then \ - wget -q https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh; \ - bash Miniconda3-latest-Linux-x86_64.sh -b; \ - rm Miniconda3-latest-Linux-x86_64.sh; \ + wget "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"; \ + bash Miniforge3-$(uname)-$(uname -m).sh -b; \ + rm Miniforge3-$(uname)-$(uname -m).sh; \ fi + RUN conda --version \ && conda create -y -n open3d python=${PYTHON_VERSION} From 7cdd7e337bfeaca2fc690eace7fda7b942cc39ea Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Sun, 15 Sep 2024 11:31:51 -0700 Subject: [PATCH 71/82] use cuda 12.3 as suggested here https://github.com/tensorflow/tensorflow/issues/63356 --- docker/docker_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker_build.sh b/docker/docker_build.sh index c4be56e9e6d..48cae885419 100755 --- a/docker/docker_build.sh +++ b/docker/docker_build.sh @@ -84,8 +84,8 @@ HOST_OPEN3D_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. >/dev/null 2>&1 && pw CCACHE_VERSION=4.3 CMAKE_VERSION=cmake-3.24.4-linux-x86_64 CMAKE_VERSION_AARCH64=cmake-3.24.4-linux-aarch64 -CUDA_VERSION=11.8.0-cudnn8 -CUDA_VERSION_LATEST=11.8.0-cudnn8 +CUDA_VERSION=12.3.2-cudnn9 +CUDA_VERSION_LATEST=12.3.2-cudnn9 print_usage_and_exit_docker_build() { echo "$__usage_docker_build" From 13699e8cb10357199b99ad7f351e7981da739414 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Mon, 16 Sep 2024 15:31:59 -0700 Subject: [PATCH 72/82] change to cuda 12.1 for pytorch and tensorflow --- docker/docker_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker_build.sh b/docker/docker_build.sh index 48cae885419..c92aed26829 100755 --- a/docker/docker_build.sh +++ b/docker/docker_build.sh @@ -84,8 +84,8 @@ HOST_OPEN3D_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. >/dev/null 2>&1 && pw CCACHE_VERSION=4.3 CMAKE_VERSION=cmake-3.24.4-linux-x86_64 CMAKE_VERSION_AARCH64=cmake-3.24.4-linux-aarch64 -CUDA_VERSION=12.3.2-cudnn9 -CUDA_VERSION_LATEST=12.3.2-cudnn9 +CUDA_VERSION=12.1.0-cudnn8 +CUDA_VERSION_LATEST=12.1.0-cudnn8 print_usage_and_exit_docker_build() { echo "$__usage_docker_build" From 165ff2b15b095ed8c0be72a626bdf30e042b6ec4 Mon Sep 17 00:00:00 2001 From: daizhirui Date: Mon, 16 Sep 2024 16:41:53 -0700 Subject: [PATCH 73/82] fix build with fmt-v11 (#6969) --- cpp/open3d/t/geometry/RaycastingScene.cpp | 2 +- cpp/open3d/utility/IJsonConvertible.h | 2 +- cpp/open3d/visualization/rendering/RendererHandle.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/open3d/t/geometry/RaycastingScene.cpp b/cpp/open3d/t/geometry/RaycastingScene.cpp index 31fdbeef856..bc003f8cbed 100644 --- a/cpp/open3d/t/geometry/RaycastingScene.cpp +++ b/cpp/open3d/t/geometry/RaycastingScene.cpp @@ -1179,7 +1179,7 @@ namespace fmt { template <> struct formatter { template - auto format(const RTCError& c, FormatContext& ctx) { + auto format(const RTCError& c, FormatContext& ctx) const { const char* name = rtcGetErrorString(c); return format_to(ctx.out(), name); } diff --git a/cpp/open3d/utility/IJsonConvertible.h b/cpp/open3d/utility/IJsonConvertible.h index 8febffa566f..e18e2948a23 100644 --- a/cpp/open3d/utility/IJsonConvertible.h +++ b/cpp/open3d/utility/IJsonConvertible.h @@ -86,7 +86,7 @@ namespace fmt { template <> struct formatter { template - auto format(const Json::Value &value, FormatContext &ctx) + auto format(const Json::Value &value, FormatContext &ctx) const -> decltype(ctx.out()) { return format_to(ctx.out(), "{}", open3d::utility::JsonToString(value)); } diff --git a/cpp/open3d/visualization/rendering/RendererHandle.h b/cpp/open3d/visualization/rendering/RendererHandle.h index dc8b06382e7..be69a3d99a8 100644 --- a/cpp/open3d/visualization/rendering/RendererHandle.h +++ b/cpp/open3d/visualization/rendering/RendererHandle.h @@ -164,7 +164,7 @@ struct formatter< char>> { template auto format(const open3d::visualization::rendering::REHandle_abstract& uid, - FormatContext& ctx) -> decltype(ctx.out()) { + FormatContext& ctx) const -> decltype(ctx.out()) { return format_to(ctx.out(), "[{}, {}, hash: {}]", open3d::visualization::rendering::REHandle_abstract:: TypeToString(uid.type), From 267df1aa47984a489576754f6dc015a213530a98 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Tue, 17 Sep 2024 01:41:59 -0700 Subject: [PATCH 74/82] set TORCH_CUDA_ARCH_LIST as workaround for CUDA 12 and Pytorch <2.4 --- docker/Dockerfile.ci | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index d910927c2cb..edca2aedc54 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -183,7 +183,10 @@ RUN \ export CMAKE_CXX_COMPILER=g++; \ export CMAKE_C_COMPILER=gcc; \ # TODO: PyTorch still use old CXX ABI, remove this line when PyTorch is updated + # TODO: Using CUDA 12.x and Pytorch <2.4 gives the error "Unknown CUDA Architecture Name 9.0a in CUDA_SELECT_NVCC_ARCH_FLAGS". + # As a workaround we explicitly set TORCH_CUDA_ARCH_LIST if [ "$BUILD_PYTORCH_OPS" = "ON" ]; then \ + export TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0" \ export GLIBCXX_USE_CXX11_ABI=OFF; \ else \ export GLIBCXX_USE_CXX11_ABI=ON; \ From a71ab5eecaa07afd04ef50fca38cedd554025bf1 Mon Sep 17 00:00:00 2001 From: Daniel Simon Date: Tue, 17 Sep 2024 07:28:10 -0700 Subject: [PATCH 75/82] Add missing build byproducts to enable support for Ninja on Windows (#6971) * Added missing build byproduct for BoringSSL * Added missing build byproduct for libcurl * Added missing build byproduct for ipp * Fixed incorrect build byproduct for UVAtlas * Added missing build byproduct for VTK --- 3rdparty/boringssl/boringssl.cmake | 4 +++- 3rdparty/curl/curl.cmake | 5 ++++- 3rdparty/ipp/ipp.cmake | 28 ++++++++++++++++++---------- 3rdparty/uvatlas/uvatlas.cmake | 2 +- 3rdparty/vtk/vtk_build.cmake | 15 ++++++++++----- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/3rdparty/boringssl/boringssl.cmake b/3rdparty/boringssl/boringssl.cmake index 5f9aeea9e33..5048b76648f 100644 --- a/3rdparty/boringssl/boringssl.cmake +++ b/3rdparty/boringssl/boringssl.cmake @@ -48,7 +48,9 @@ ExternalProject_Add( CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - BUILD_BYPRODUCTS "" + BUILD_BYPRODUCTS + /$<$:$,Debug,Release>/>lib/${CMAKE_STATIC_LIBRARY_PREFIX}ssl${CMAKE_STATIC_LIBRARY_SUFFIX} + /$<$:$,Debug,Release>/>lib/${CMAKE_STATIC_LIBRARY_PREFIX}crypto${CMAKE_STATIC_LIBRARY_SUFFIX} ) ExternalProject_Get_Property(ext_boringssl SOURCE_DIR) diff --git a/3rdparty/curl/curl.cmake b/3rdparty/curl/curl.cmake index c45dbea0dd9..7a73051c1c0 100644 --- a/3rdparty/curl/curl.cmake +++ b/3rdparty/curl/curl.cmake @@ -51,6 +51,7 @@ if(BUILD_CURL_FROM_SOURCE) ${ExternalProject_CMAKE_ARGS_hidden} BUILD_BYPRODUCTS /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX} + /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}-d${CMAKE_STATIC_LIBRARY_SUFFIX} ) ExternalProject_Get_Property(ext_curl INSTALL_DIR) @@ -79,7 +80,9 @@ else() CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - BUILD_BYPRODUCTS "" + BUILD_BYPRODUCTS + /lib/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX} + /lib/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}-d${CMAKE_STATIC_LIBRARY_SUFFIX} ) ExternalProject_Get_Property(ext_curl SOURCE_DIR) diff --git a/3rdparty/ipp/ipp.cmake b/3rdparty/ipp/ipp.cmake index ffc9b320512..deaed2ab179 100755 --- a/3rdparty/ipp/ipp.cmake +++ b/3rdparty/ipp/ipp.cmake @@ -35,6 +35,20 @@ else() set(IPP_SUBPATH "") endif() +# Threading layer libs must be linked first. +# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/ipp-performace-benefits-with-tl-functions.html +# Library dependency order: +# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/library-dependencies-by-domain.html +if (WIN32) + set(IPP_LIBRARIES ipp_iw ippcvmt_tl_tbb ippcvmt ippimt_tl_tbb ippimt ippccmt_tl_tbb ippccmt ippsmt ippvmmt ippcoremt_tl_tbb ippcoremt) +else() + set(IPP_LIBRARIES ipp_iw ippcv_tl_tbb ippcv ippi_tl_tbb ippi ippcc_tl_tbb ippcc ipps ippvm ippcore_tl_tbb ippcore) +endif() + +foreach(item IN LISTS IPP_LIBRARIES) + list(APPEND IPP_BUILD_BYPRODUCTS /${IPP_SUBPATH}lib/${CMAKE_STATIC_LIBRARY_PREFIX}${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) +endforeach() + ExternalProject_Add(ext_ipp PREFIX ipp URL ${IPP_URL} @@ -45,16 +59,10 @@ ExternalProject_Add(ext_ipp CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - ) + BUILD_BYPRODUCTS + ${IPP_BUILD_BYPRODUCTS} +) + ExternalProject_Get_Property(ext_ipp SOURCE_DIR) set(IPP_INCLUDE_DIR "${SOURCE_DIR}/${IPP_SUBPATH}include/") -# Threading layer libs must be linked first. -# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/ipp-performace-benefits-with-tl-functions.html -# Library dependency order: -# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/library-dependencies-by-domain.html -if (WIN32) - set(IPP_LIBRARIES ipp_iw ippcvmt_tl_tbb ippcvmt ippimt_tl_tbb ippimt ippccmt_tl_tbb ippccmt ippsmt ippvmmt ippcoremt_tl_tbb ippcoremt) -else() - set(IPP_LIBRARIES ipp_iw ippcv_tl_tbb ippcv ippi_tl_tbb ippi ippcc_tl_tbb ippcc ipps ippvm ippcore_tl_tbb ippcore) -endif() set(IPP_LIB_DIR "${SOURCE_DIR}/${IPP_SUBPATH}lib") \ No newline at end of file diff --git a/3rdparty/uvatlas/uvatlas.cmake b/3rdparty/uvatlas/uvatlas.cmake index 51831059941..63642f80e3d 100644 --- a/3rdparty/uvatlas/uvatlas.cmake +++ b/3rdparty/uvatlas/uvatlas.cmake @@ -52,7 +52,7 @@ ExternalProject_Add( -Ddirectxmath_DIR= DEPENDS ext_directxheaders ext_directxmath BUILD_BYPRODUCTS - /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}uvatlas${CMAKE_STATIC_LIBRARY_SUFFIX} + /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}UVAtlas${CMAKE_STATIC_LIBRARY_SUFFIX} ) diff --git a/3rdparty/vtk/vtk_build.cmake b/3rdparty/vtk/vtk_build.cmake index 271d3789667..f9217f2301e 100644 --- a/3rdparty/vtk/vtk_build.cmake +++ b/3rdparty/vtk/vtk_build.cmake @@ -26,13 +26,13 @@ set(VTK_LIBRARIES vtksys-${VTK_VERSION}${VTK_LIB_SUFFIX} ) -foreach(item IN LISTS VTK_LIBRARIES) - list(APPEND VTK_BUILD_BYPRODUCTS /${Open3D_INSTALL_LIB_DIR}/${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) -endforeach() - if(BUILD_VTK_FROM_SOURCE) + foreach(item IN LISTS VTK_LIBRARIES) + list(APPEND VTK_BUILD_BYPRODUCTS /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) + endforeach() + ExternalProject_Add( ext_vtk PREFIX vtk @@ -294,6 +294,10 @@ if(BUILD_VTK_FROM_SOURCE) else() #### download prebuilt vtk + foreach(item IN LISTS VTK_LIBRARIES) + list(APPEND VTK_BUILD_BYPRODUCTS /lib/${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) + endforeach() + if(LINUX_AARCH64) message(FATAL "No precompiled vtk for platform. Enable BUILD_VTK_FROM_SOURCE") elseif(APPLE_AARCH64) @@ -334,7 +338,8 @@ else() #### download prebuilt vtk CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - BUILD_BYPRODUCTS "" + BUILD_BYPRODUCTS + ${VTK_BUILD_BYPRODUCTS} ) ExternalProject_Get_Property(ext_vtk SOURCE_DIR) From 85981ff8b902669899a4b9e4f6029647a1ee7f97 Mon Sep 17 00:00:00 2001 From: Robin <102535177+rxba@users.noreply.github.com> Date: Wed, 18 Sep 2024 03:12:23 +0200 Subject: [PATCH 76/82] fix visualization/draw ICP example and add warnings (#6933) * fix mismatch of selection sets, no target points in set and overall mismatch in number of correspondences. --------- Co-authored-by: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> --- CHANGELOG.md | 1 + examples/python/visualization/draw.py | 57 +++++++++++++++++++-------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c58729b74da..e5803ea862f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (PR #6898) - Fix projection of point cloud to Depth/RGBD image if no position attribute is provided (PR #6880) - Support lowercase types when reading PCD files (PR #6930) +- Fix visualization/draw ICP example and add warnings (PR #6933) ## 0.13 diff --git a/examples/python/visualization/draw.py b/examples/python/visualization/draw.py index f354f55783e..231a0af8bb0 100644 --- a/examples/python/visualization/draw.py +++ b/examples/python/visualization/draw.py @@ -11,6 +11,7 @@ import open3d.visualization as vis import os import random +import warnings pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) test_data_path = os.path.join(os.path.dirname(pyexample_path), 'test_data') @@ -142,30 +143,46 @@ def selections(): source_name = "Source (yellow)" target_name = "Target (blue)" - def do_icp_one_set(o3dvis): + def _prep_correspondences(o3dvis, two_set=False): # sets: [name: [{ "index": int, "order": int, "point": (x, y, z)}, ...], # ...] sets = o3dvis.get_selection_sets() - source_picked = sorted(list(sets[0][source_name]), - key=lambda x: x.order) - target_picked = sorted(list(sets[0][target_name]), - key=lambda x: x.order) - source_indices = [idx.index for idx in source_picked] - target_indices = [idx.index for idx in target_picked] - - t = get_icp_transform(source, target, source_indices, target_indices) - source.transform(t) - - # Update the source geometry - o3dvis.remove_geometry(source_name) - o3dvis.add_geometry({"name": source_name, "geometry": source}) + if not sets: + warnings.warn( + "Empty selection sets. Select point correspondences for initial rough transform.", + RuntimeWarning) + return [], [] + if source_name not in sets[0]: + warnings.warn( + "First selection set should contain Source (yellow) points.", + RuntimeWarning) + return [], [] - def do_icp_two_sets(o3dvis): - sets = o3dvis.get_selection_sets() source_set = sets[0][source_name] - target_set = sets[1][target_name] + if two_set: + if not len(sets) == 2: + warnings.warn( + "Two set registration requires exactly two selection sets of corresponding points.", + RuntimeWarning) + return [], [] + target_set = sets[1][target_name] + else: + if target_name not in sets[0]: + warnings.warn( + "Selection set should contain Target (blue) points.", + RuntimeWarning) + return [], [] + target_set = sets[0][target_name] source_picked = sorted(list(source_set), key=lambda x: x.order) target_picked = sorted(list(target_set), key=lambda x: x.order) + if len(source_picked) != len(target_picked): + warnings.warn( + f"Registration requires equal number of corresponding points (current selection: {len(source_picked)} source, {len(target_picked)} target).", + RuntimeWarning) + return [], [] + return source_picked, target_picked + + def _do_icp(o3dvis, source_picked, target_picked): source_indices = [idx.index for idx in source_picked] target_indices = [idx.index for idx in target_picked] @@ -176,6 +193,12 @@ def do_icp_two_sets(o3dvis): o3dvis.remove_geometry(source_name) o3dvis.add_geometry({"name": source_name, "geometry": source}) + def do_icp_one_set(o3dvis): + _do_icp(o3dvis, *_prep_correspondences(o3dvis)) + + def do_icp_two_sets(o3dvis): + _do_icp(o3dvis, *_prep_correspondences(o3dvis, two_set=True)) + vis.draw([{ "name": source_name, "geometry": source From f4e1fa94f6b8385092eaddc721d6dfe9799f9a68 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:27:45 -0700 Subject: [PATCH 77/82] Add remote visualizer example. Allow changing rpc address in draw. (#6973) --- .../python/visualization/remote_visualizer.py | 71 +++++++++++++++++++ .../visualization/_external_visualizer.py | 7 ++ python/open3d/visualization/draw.py | 11 +-- 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 examples/python/visualization/remote_visualizer.py diff --git a/examples/python/visualization/remote_visualizer.py b/examples/python/visualization/remote_visualizer.py new file mode 100644 index 00000000000..e360e55272b --- /dev/null +++ b/examples/python/visualization/remote_visualizer.py @@ -0,0 +1,71 @@ +# ---------------------------------------------------------------------------- +# - Open3D: www.open3d.org - +# ---------------------------------------------------------------------------- +# Copyright (c) 2018-2023 www.open3d.org +# SPDX-License-Identifier: MIT +# ---------------------------------------------------------------------------- +"""This example shows Open3D's remote visualization feature using RPC +communication. To run this example, start the client first by running + +python remote_visualizer.py client + +and then run the server by running + +python remote_visualizer.py server + +Port 51454 is used by default for communication. For remote visualization (client +and server running on different machines), use ssh to forward the remote server +port to your local computer: + + ssh -N -R 51454:localhost:51454 user@remote_host + +See documentation for more details (e.g. to use a different port). +""" +import sys +import numpy as np +import open3d as o3d +import open3d.visualization as vis + + +def make_point_cloud(npts, center, radius, colorize): + pts = np.random.uniform(-radius, radius, size=[npts, 3]) + center + cloud = o3d.geometry.PointCloud() + cloud.points = o3d.utility.Vector3dVector(pts) + if colorize: + colors = np.random.uniform(0.0, 1.0, size=[npts, 3]) + cloud.colors = o3d.utility.Vector3dVector(colors) + return cloud + + +def server_time_animation(): + orig = make_point_cloud(200, (0, 0, 0), 1.0, True) + clouds = [{"name": "t=0", "geometry": orig, "time": 0}] + drift_dir = (1.0, 0.0, 0.0) + expand = 1.0 + n = 20 + ev = o3d.visualization.ExternalVisualizer() + for i in range(1, n): + amount = float(i) / float(n - 1) + cloud = o3d.geometry.PointCloud() + pts = np.asarray(orig.points) + pts = pts * (1.0 + amount * expand) + [amount * v for v in drift_dir] + cloud.points = o3d.utility.Vector3dVector(pts) + cloud.colors = orig.colors + ev.set(obj=cloud, time=i, path=f"points at t={i}") + print('.', end='', flush=True) + print() + + +def client_time_animation(): + o3d.visualization.draw(title="Open3D - Remote Visualizer Client", + show_ui=True, + rpc_interface=True) + + +if __name__ == "__main__": + assert len(sys.argv) == 2 and sys.argv[1] in ('client', 'server'), ( + "Usage: python remote_visualizer.py [client|server]") + if sys.argv[1] == "client": + client_time_animation() + elif sys.argv[1] == "server": + server_time_animation() diff --git a/python/open3d/visualization/_external_visualizer.py b/python/open3d/visualization/_external_visualizer.py index ac7209127f0..774ddbaebe1 100644 --- a/python/open3d/visualization/_external_visualizer.py +++ b/python/open3d/visualization/_external_visualizer.py @@ -37,17 +37,23 @@ def set(self, obj=None, path='', time=0, layer='', connection=None): Example: To quickly send a single object just write:: + ev.set(point_cloud) To place the object at a specific location in the scene tree do:: + ev.set(point_cloud, path='group/mypoints', time=42, layer='') + Note that depending on the visualizer some arguments like time or layer may not be supported and will be ignored. To set multiple objects use a list to pass multiple objects:: + ev.set([point_cloud, mesh, camera]) + Each entry in the list can be a tuple specifying all or some of the location parameters:: + ev.set(objs=[(point_cloud,'group/mypoints', 1, 'layer1'), (mesh, 'group/mymesh'), camera @@ -147,6 +153,7 @@ def draw(self, geometry=None, *args, **kwargs): Example: Here we use draw with the default external visualizer:: + import open3d as o3d torus = o3d.geometry.TriangleMesh.create_torus() diff --git a/python/open3d/visualization/draw.py b/python/open3d/visualization/draw.py index f7764b41400..aa6e29e2b2c 100644 --- a/python/open3d/visualization/draw.py +++ b/python/open3d/visualization/draw.py @@ -83,9 +83,10 @@ def draw(geometry=None, animation_time_step (float): Duration in seconds for each animation frame. animation_duration (float): Total animation duration in seconds. - rpc_interface (bool): Start an RPC interface at http://localhost:51454 and - listen for drawing requests. The requests can be made with - :class:`open3d.visualization.ExternalVisualizer`. + rpc_interface (bool or str): Start an RPC interface at this local + address and listen for drawing requests. If rpc_interface is True, the + default address "tcp://localhost:51454" is used. The requests can be + made with :class:`open3d.visualization.ExternalVisualizer`. on_init (Callable): Extra initialization procedure for the underlying GUI window. The procedure receives a single argument of type :class:`open3d.visualization.O3DVisualizer`. @@ -202,7 +203,9 @@ def add(g, n): w.show_skybox(show_skybox) if rpc_interface: - w.start_rpc_interface(address="tcp://127.0.0.1:51454", timeout=10000) + if not isinstance(rpc_interface, str): + rpc_interface = "tcp://127.0.0.1:51454" + w.start_rpc_interface(address=rpc_interface, timeout=10000) def stop_rpc(): w.stop_rpc_interface() From cc015124b5d3396244fce9ffb7795c87ffc19777 Mon Sep 17 00:00:00 2001 From: Benjamin Ummenhofer Date: Thu, 19 Sep 2024 01:40:11 -0700 Subject: [PATCH 78/82] fix failed merge --- cpp/open3d/t/geometry/RaycastingScene.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/open3d/t/geometry/RaycastingScene.cpp b/cpp/open3d/t/geometry/RaycastingScene.cpp index 4a4482ebc4d..12f082d0ee1 100644 --- a/cpp/open3d/t/geometry/RaycastingScene.cpp +++ b/cpp/open3d/t/geometry/RaycastingScene.cpp @@ -1174,7 +1174,7 @@ uint32_t RaycastingScene::INVALID_ID() { return RTC_INVALID_GEOMETRY_ID; } } // namespace geometry } // namespace t - +} // namespace open3d namespace fmt { template <> @@ -1191,4 +1191,3 @@ struct formatter { } }; } // namespace fmt - From e88c7b13270d961393ab1e64c03d1f7ccd047cce Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Sun, 29 Sep 2024 23:07:07 -0700 Subject: [PATCH 79/82] Replace conda with pyenv to fix incorrect libstdc++ use in jammy CI. (#6966) * Replace conda with pyenv to fix incorrect libstdc++ use in jammy CI. * Use RPATH instead of RUNPATH to load libc++abi.so directly in Python. No need to find and load explicitly. * Use libc++11 to build in Ubuntu 22.04. Warn if newer version is used. * TODO: Solution for Ubuntu 24.04 --- 3rdparty/find_dependencies.cmake | 9 ++++++ cpp/open3d/core/nns/NanoFlannImpl.h | 25 ++++------------ cpp/pybind/CMakeLists.txt | 3 ++ cpp/tests/t/geometry/TensorMap.cpp | 7 +++++ docker/Dockerfile.ci | 44 +++++++++++++++++++---------- docker/docker_test.sh | 2 +- python/open3d/__init__.py | 8 ------ util/install_deps_ubuntu.sh | 11 ++++++-- 8 files changed, 63 insertions(+), 46 deletions(-) diff --git a/3rdparty/find_dependencies.cmake b/3rdparty/find_dependencies.cmake index 77b7085df69..d91377a3138 100644 --- a/3rdparty/find_dependencies.cmake +++ b/3rdparty/find_dependencies.cmake @@ -1338,6 +1338,7 @@ if(BUILD_GUI) if (CPP_LIBRARY AND CPPABI_LIBRARY) set(CLANG_LIBDIR ${llvm_lib_dir}) message(STATUS "CLANG_LIBDIR found in ubuntu-default: ${CLANG_LIBDIR}") + set(LIBCPP_VERSION ${llvm_ver}) break() endif() endforeach() @@ -1362,7 +1363,10 @@ if(BUILD_GUI) llvm-8/lib llvm-7/lib ) + file(REAL_PATH ${CPPABI_LIBRARY} CPPABI_LIBRARY) get_filename_component(CLANG_LIBDIR ${CPPABI_LIBRARY} DIRECTORY) + string(REGEX MATCH "llvm-([0-9]+)/lib" _ ${CLANG_LIBDIR}) + set(LIBCPP_VERSION ${CMAKE_MATCH_1}) endif() # Find clang libraries at the exact path ${CLANG_LIBDIR}. @@ -1378,6 +1382,11 @@ if(BUILD_GUI) target_link_libraries(3rdparty_filament INTERFACE -lstdc++ ${CPP_LIBRARY} ${CPPABI_LIBRARY}) message(STATUS "Filament C++ libraries: ${CPP_LIBRARY} ${CPPABI_LIBRARY}") + if (LIBCPP_VERSION GREATER 11) + message(WARNING "libc++ (LLVM) version ${LIBCPP_VERSION} > 11 includes libunwind that " + "interferes with the system libunwind.so.8 and may crash Python code when exceptions " + "are used. Please consider using libc++ (LLVM) v11.") + endif() endif() if (APPLE) find_library(CORE_VIDEO CoreVideo) diff --git a/cpp/open3d/core/nns/NanoFlannImpl.h b/cpp/open3d/core/nns/NanoFlannImpl.h index 027d6d0c4ee..9a9ceb100c4 100644 --- a/cpp/open3d/core/nns/NanoFlannImpl.h +++ b/cpp/open3d/core/nns/NanoFlannImpl.h @@ -118,13 +118,6 @@ void _KnnSearchCPU(NanoFlannIndexHolderBase *holder, return; } - auto points_equal = [](const T *const p1, const T *const p2, - size_t dimension) { - std::vector p1_vec(p1, p1 + dimension); - std::vector p2_vec(p2, p2 + dimension); - return p1_vec == p2_vec; - }; - std::vector> neighbors_indices(num_queries); std::vector> neighbors_distances(num_queries); std::vector neighbors_count(num_queries, 0); @@ -147,8 +140,9 @@ void _KnnSearchCPU(NanoFlannIndexHolderBase *holder, for (size_t valid_i = 0; valid_i < num_valid; ++valid_i) { TIndex idx = result_indices[valid_i]; if (ignore_query_point && - points_equal(&queries[i * dimension], - &points[idx * dimension], dimension)) { + std::equal(&queries[i * dimension], + &queries[i * dimension] + dimension, + &points[idx * dimension])) { continue; } neighbors_indices[i].push_back(idx); @@ -222,13 +216,6 @@ void _RadiusSearchCPU(NanoFlannIndexHolderBase *holder, return; } - auto points_equal = [](const T *const p1, const T *const p2, - size_t dimension) { - std::vector p1_vec(p1, p1 + dimension); - std::vector p2_vec(p2, p2 + dimension); - return p1_vec == p2_vec; - }; - std::vector> neighbors_indices(num_queries); std::vector> neighbors_distances(num_queries); std::vector neighbors_count(num_queries, 0); @@ -255,9 +242,9 @@ void _RadiusSearchCPU(NanoFlannIndexHolderBase *holder, int num_neighbors = 0; for (const auto &idx_dist : search_result) { if (ignore_query_point && - points_equal(&queries[i * dimension], - &points[idx_dist.first * dimension], - dimension)) { + std::equal(&queries[i * dimension], + &queries[i * dimension] + dimension, + &points[idx_dist.first * dimension])) { continue; } neighbors_indices[i].push_back(idx_dist.first); diff --git a/cpp/pybind/CMakeLists.txt b/cpp/pybind/CMakeLists.txt index e7a534a3eb3..c79bbd96719 100644 --- a/cpp/pybind/CMakeLists.txt +++ b/cpp/pybind/CMakeLists.txt @@ -80,6 +80,9 @@ set(PYTHON_COMPILED_MODULE_DIR if (APPLE) set_target_properties(pybind PROPERTIES BUILD_RPATH "@loader_path;@loader_path/..") elseif (UNIX) + # Use RPATH instead of RUNPATH in pybind so that needed libc++.so can find child dependant libc++abi.so in RPATH + # https://stackoverflow.com/questions/69662319/managing-secondary-dependencies-of-shared-libraries + target_link_options(pybind PRIVATE "LINKER:--disable-new-dtags") set_target_properties(pybind PROPERTIES BUILD_RPATH "$ORIGIN;$ORIGIN/..") endif() set_target_properties(pybind PROPERTIES diff --git a/cpp/tests/t/geometry/TensorMap.cpp b/cpp/tests/t/geometry/TensorMap.cpp index 372c6b82270..8512ce68ccb 100644 --- a/cpp/tests/t/geometry/TensorMap.cpp +++ b/cpp/tests/t/geometry/TensorMap.cpp @@ -32,6 +32,13 @@ TEST_P(TensorMapPermuteDevices, Constructor) { // Primary key is required. EXPECT_ANY_THROW(t::geometry::TensorMap()); + // Delete primary key. + EXPECT_ANY_THROW(tm0.Erase("positions")); + + // Reserved keys. + EXPECT_ANY_THROW(tm0.insert( + {"primary_key", core::Tensor::Zeros({2, 3}, dtype, device)})); + // Iterators. std::map tensor_map( {{"positions", core::Tensor::Zeros({10, 3}, dtype, device)}, diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index cd52a838882..73feb7bffd6 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -67,34 +67,48 @@ RUN if [ "${BUILD_SYCL_MODULE}" = "ON" ]; then \ rm -rf /etc/apt/sources.list.d/oneAPI.list; \ fi -# Dependencies: basic +# Dependencies: basic and python-build RUN apt-get update && apt-get install -y \ git \ wget \ curl \ build-essential \ pkg-config \ + zlib1g \ + zlib1g-dev \ + libssl-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + libncursesw5-dev \ + xz-utils \ + tk-dev \ + libxml2-dev \ + libxmlsec1-dev \ + libffi-dev \ + liblzma-dev \ && rm -rf /var/lib/apt/lists/* -# Miniconda or Intel conda -# The **/open3d/bin paths are used during docker run, in this way docker run +# pyenv or Intel Python +# The pyenv python paths are used during docker run, in this way docker run # does not need to activate the environment again. -ENV PATH="/root/miniconda3/bin:${PATH}" -ENV PATH="/root/miniconda3/envs/open3d/bin:${PATH}" +# The soft link from the python patch level version to the python mino version +# ensures python wheel commands (i.e. open3d) are in PATH, since we don't know +# which patch level pyenv will install (latest). +ENV PYENV_ROOT=/root/.pyenv +ENV PATH="$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PYENV_ROOT/versions/$PYTHON_VERSION/bin:$PATH" ENV PATH="/opt/intel/oneapi/intelpython/latest/bin:${PATH}" -ENV PATH="/opt/intel/oneapi/intelpython/latest/envs/open3d/bin:${PATH}" RUN if [ "${BUILD_SYCL_MODULE}" = "OFF" ]; then \ - wget -q https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh; \ - bash Miniconda3-latest-Linux-x86_64.sh -b; \ - rm Miniconda3-latest-Linux-x86_64.sh; \ + curl https://pyenv.run | bash \ + && pyenv update \ + && pyenv install $PYTHON_VERSION \ + && pyenv global $PYTHON_VERSION \ + && pyenv rehash \ + && ln -s $PYENV_ROOT/versions/${PYTHON_VERSION}* $PYENV_ROOT/versions/${PYTHON_VERSION}; \ fi -RUN conda --version \ - && conda create -y -n open3d python=${PYTHON_VERSION} +RUN python --version && pip --version -# Activate open3d virtualenv -# This works during docker build. It becomes the prefix of all RUN commands. -# Ref: https://stackoverflow.com/a/60148365/1255535 -SHELL ["conda", "run", "-n", "open3d", "/bin/bash", "-o", "pipefail", "-c"] +SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Dependencies: cmake ENV PATH=${HOME}/${CMAKE_VERSION}/bin:${PATH} diff --git a/docker/docker_test.sh b/docker/docker_test.sh index 441b17ed4e3..d328d14535d 100755 --- a/docker/docker_test.sh +++ b/docker/docker_test.sh @@ -141,7 +141,7 @@ cpp_python_linking_uninstall_test() { # Python test echo "pytest is randomized, add --randomly-seed=SEED to repeat the test sequence." ${docker_run} -i --rm "${DOCKER_TAG}" /bin/bash -c " \ - python -m pytest python/test ${pytest_args} -s" + python -W default -m pytest python/test ${pytest_args} -s" restart_docker_daemon_if_on_gcloud # Command-line tools test diff --git a/python/open3d/__init__.py b/python/open3d/__init__.py index 141bf426c50..f7354b2a90c 100644 --- a/python/open3d/__init__.py +++ b/python/open3d/__init__.py @@ -45,14 +45,6 @@ def load_cdll(path): if sys.platform == "win32": # Unix: Use rpath to find libraries _win32_dll_dir = os.add_dll_directory(str(Path(__file__).parent)) -if _build_config["BUILD_GUI"] and not (find_library("c++abi") or - find_library("c++")): - try: # Preload libc++.so and libc++abi.so (required by filament) - load_cdll(str(next((Path(__file__).parent).glob("*c++abi.*")))) - load_cdll(str(next((Path(__file__).parent).glob("*c++.*")))) - except StopIteration: # Not found: check system paths while loading - pass - __DEVICE_API__ = "cpu" if _build_config["BUILD_CUDA_MODULE"]: # Load CPU pybind dll gracefully without introducing new python variable. diff --git a/util/install_deps_ubuntu.sh b/util/install_deps_ubuntu.sh index 3e359a1a6e7..2786b19c23b 100755 --- a/util/install_deps_ubuntu.sh +++ b/util/install_deps_ubuntu.sh @@ -39,17 +39,22 @@ eval $( echo DISTRIB_ID="$DISTRIB_ID"; echo DISTRIB_RELEASE="$DISTRIB_RELEASE" ) +# To avoid dependence on libunwind, we don't want to use clang / libc++ versions later than 11. +# Ubuntu 20.04's has versions 8, 10 or 12 while Ubuntu 22.04 has versions 11 and later. if [ "$DISTRIB_ID" == "Ubuntu" -a "$DISTRIB_RELEASE" == "20.04" ]; then - # Ubuntu 20.04's clang/libc++-dev/libc++abi-dev are version 8, 10 or 12. - # To avoid dependence on libunwind, we don't want to use versions later than 10. deps=("${deps[@]/clang/clang-10}") deps=("${deps[@]/libc++-dev/libc++-10-dev}") deps=("${deps[@]/libc++abi-dev/libc++abi-10-dev}") fi +if [ "$DISTRIB_ID" == "Ubuntu" -a "$DISTRIB_RELEASE" == "22.04" ]; then + deps=("${deps[@]/clang/clang-11}") + deps=("${deps[@]/libc++-dev/libc++-11-dev}") + deps=("${deps[@]/libc++abi-dev/libc++abi-11-dev}") +fi # Special case for ARM64 if [ "$(uname -m)" == "aarch64" ]; then - # For compling LAPACK in OpenBLAS + # For compiling LAPACK in OpenBLAS deps+=("gfortran") fi From 4ccae4217bffe7fcbb18716a39e7d120c3c537e0 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Thu, 3 Oct 2024 00:05:24 -0700 Subject: [PATCH 80/82] Match PyTorch ops CUDA arch list to that of Open3D --- 3rdparty/cmake/FindPytorch.cmake | 23 +++++++++++++++++++++++ docker/Dockerfile.ci | 3 --- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/3rdparty/cmake/FindPytorch.cmake b/3rdparty/cmake/FindPytorch.cmake index eb2a53e2ec5..28da7ec71ba 100644 --- a/3rdparty/cmake/FindPytorch.cmake +++ b/3rdparty/cmake/FindPytorch.cmake @@ -14,6 +14,20 @@ # # and import the target 'torch'. +# "80-real" to "8.0" and "80" to "8.0+PTX": +macro(translate_arch_string input output) + if("${input}" MATCHES "[0-9]+-real") + string(REGEX REPLACE "([1-9])([0-9])-real" "\\1.\\2" version "${input}") + elseif("${input}" MATCHES "([0-9]+)") + string(REGEX REPLACE "([1-9])([0-9])" "\\1.\\2+PTX" version "${input}") + elseif(input STREQUAL "native") + set(version "Auto") + else() + message(FATAL_ERROR "Invalid architecture string: ${input}") + endif() + set(${output} "${version}") +endmacro() + if(NOT Pytorch_FOUND) # Searching for pytorch requires the python executable if (NOT Python3_EXECUTABLE) @@ -41,6 +55,15 @@ if(NOT Pytorch_FOUND) unset(PyTorch_FETCH_PROPERTIES) unset(PyTorch_PROPERTIES) + # Using CUDA 12.x and Pytorch <2.4 gives the error "Unknown CUDA Architecture Name 9.0a in CUDA_SELECT_NVCC_ARCH_FLAGS". + # As a workaround we explicitly set TORCH_CUDA_ARCH_LIST + set(TORCH_CUDA_ARCH_LIST "") + foreach(arch IN LISTS CMAKE_CUDA_ARCHITECTURES) + translate_arch_string("${arch}" ptarch) + list(APPEND TORCH_CUDA_ARCH_LIST "${ptarch}") + endforeach() + + message(STATUS "Using top level CMAKE_CUDA_ARCHITECTURES for TORCH_CUDA_ARCH_LIST: ${TORCH_CUDA_ARCH_LIST}") # Use the cmake config provided by torch find_package(Torch REQUIRED PATHS "${Pytorch_ROOT}" NO_DEFAULT_PATH) diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index dcdc0599dd9..5c5991e5a76 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -196,10 +196,7 @@ RUN \ export CMAKE_CXX_COMPILER=g++; \ export CMAKE_C_COMPILER=gcc; \ # TODO: PyTorch still use old CXX ABI, remove this line when PyTorch is updated - # TODO: Using CUDA 12.x and Pytorch <2.4 gives the error "Unknown CUDA Architecture Name 9.0a in CUDA_SELECT_NVCC_ARCH_FLAGS". - # As a workaround we explicitly set TORCH_CUDA_ARCH_LIST if [ "$BUILD_PYTORCH_OPS" = "ON" ]; then \ - export TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0" \ export GLIBCXX_USE_CXX11_ABI=OFF; \ else \ export GLIBCXX_USE_CXX11_ABI=ON; \ From b6cc8321383cb657ac84eb077ad897cb2686b2d7 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Thu, 3 Oct 2024 10:32:14 -0700 Subject: [PATCH 81/82] do not install cuda pytorch before build_pip_package --- 3rdparty/cmake/FindPytorch.cmake | 14 ++++++++------ docker/Dockerfile.wheel | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/3rdparty/cmake/FindPytorch.cmake b/3rdparty/cmake/FindPytorch.cmake index 28da7ec71ba..31babdca149 100644 --- a/3rdparty/cmake/FindPytorch.cmake +++ b/3rdparty/cmake/FindPytorch.cmake @@ -55,15 +55,17 @@ if(NOT Pytorch_FOUND) unset(PyTorch_FETCH_PROPERTIES) unset(PyTorch_PROPERTIES) + if(BUILD_CUDA_MODULE) # Using CUDA 12.x and Pytorch <2.4 gives the error "Unknown CUDA Architecture Name 9.0a in CUDA_SELECT_NVCC_ARCH_FLAGS". # As a workaround we explicitly set TORCH_CUDA_ARCH_LIST - set(TORCH_CUDA_ARCH_LIST "") - foreach(arch IN LISTS CMAKE_CUDA_ARCHITECTURES) - translate_arch_string("${arch}" ptarch) - list(APPEND TORCH_CUDA_ARCH_LIST "${ptarch}") - endforeach() + set(TORCH_CUDA_ARCH_LIST "") + foreach(arch IN LISTS CMAKE_CUDA_ARCHITECTURES) + translate_arch_string("${arch}" ptarch) + list(APPEND TORCH_CUDA_ARCH_LIST "${ptarch}") + endforeach() + message(STATUS "Using top level CMAKE_CUDA_ARCHITECTURES for TORCH_CUDA_ARCH_LIST: ${TORCH_CUDA_ARCH_LIST}") + endif() - message(STATUS "Using top level CMAKE_CUDA_ARCHITECTURES for TORCH_CUDA_ARCH_LIST: ${TORCH_CUDA_ARCH_LIST}") # Use the cmake config provided by torch find_package(Torch REQUIRED PATHS "${Pytorch_ROOT}" NO_DEFAULT_PATH) diff --git a/docker/Dockerfile.wheel b/docker/Dockerfile.wheel index 9d86856c177..71a16a2ac13 100644 --- a/docker/Dockerfile.wheel +++ b/docker/Dockerfile.wheel @@ -113,7 +113,7 @@ COPY ./python/requirements.txt /root/Open3D/python/ COPY ./python/requirements_jupyter_build.txt /root/Open3D/python/ COPY ./python/requirements_jupyter_install.txt /root/Open3D/python/ RUN source /root/Open3D/util/ci_utils.sh \ - && install_python_dependencies with-cuda with-jupyter + && install_python_dependencies with-jupyter # Open3D Jupyter dependencies RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \ From 712412f219b8eb1f953315c2caaf3ae3c42a2d59 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Fri, 4 Oct 2024 10:45:22 -0700 Subject: [PATCH 82/82] Use CUDA 12.1 everywhere. Final cleanup. Update and sort library list in 3rdparty/README.md --- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 3rdparty/README.md | 180 +++++++++++++++++----------------- docker/Dockerfile.ci | 3 +- docker/Dockerfile.wheel | 5 +- docker/README.md | 2 +- docs/arm.rst | 2 +- util/ci_utils.sh | 3 - 8 files changed, 99 insertions(+), 100 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7c24fd710a7..1c3ff74deba 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -405,7 +405,7 @@ jobs: uses: actions/checkout@v4 with: repository: isl-org/Open3D-ML - ref: ss/python-3.12 # remove before merge + ref: main path: ${{ env.OPEN3D_ML_ROOT }} - name: Download wheels diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e08bd1960c0..04a408802d0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -25,7 +25,7 @@ env: STOOLS_VER: "67.3.2" JEDI_VER: "0.17.2" # https://github.com/ipython/ipython/issues/12740 IDNA_VER: "2.8" # https://github.com/psf/requests/issues/5710 - CUDA_VERSION: "11.8.0" + CUDA_VERSION: "12.1.0" SRC_DIR: "D:\\a\\open3d\\open3d" BUILD_DIR: "C:\\Open3D\\build" NPROC: 2 diff --git a/3rdparty/README.md b/3rdparty/README.md index b5c915fc79b..b494ac77fc7 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -8,134 +8,138 @@ system dependencies. ```txt -------------------------------------------------------------------------------- +benchmark 1.5.5 Apache-2 license +A microbenchmark support library +https://github.com/google/benchmark +-------------------------------------------------------------------------------- +boringssl: edfe413 Dual OpenSSL, SSLeay, ISC license +BoringSSL is a fork of OpenSSL that is designed to meet Google's needs. +https://github.com/google/boringssl +-------------------------------------------------------------------------------- +CUB 1.8.0 BSD license +A flexible library of cooperative threadblock primitives and other utilities for +CUDA kernel programming +https://github.com/NVlabs/cub +-------------------------------------------------------------------------------- +cppzmq 4.6.0 MIT license +Header-only C++ binding for libzmq +https://github.com/zeromq/cppzmq +As an alternative, you can modify 3rdparty/zeromq/zeromq_build.cmake to fetch +zeromq from our fork +https://github.com/isl-org/libzmq +-------------------------------------------------------------------------------- +curl 7.88.0 Curl license +Curl is a command-line tool for transferring data specified with URL syntax. +https://github.com/curl/curl +-------------------------------------------------------------------------------- +CUTLASS 1.3.3 BSD license +CUDA Templates for Linear Algebra Subroutines +https://github.com/NVIDIA/cutlass +-------------------------------------------------------------------------------- +dirent 1.21 MIT license +https://github.com/tronkko/dirent +A C/C++ programming interface for cross-platform filesystem +-------------------------------------------------------------------------------- +DirectX-Headers v1.606.3 MIT license +Official DirectX headers available under an open source license +https://github.com/microsoft/DirectX-Headers +-------------------------------------------------------------------------------- +DirectXMath may2022 MIT license +DirectXMath is an all inline SIMD C++ linear algebra library for use in games +and graphics apps +https://github.com/microsoft/DirectXMath +-------------------------------------------------------------------------------- Eigen 3.4 Mainly MPL2 license A high-level C++ library of template headers for linear algebra, matrix and vector operations, numerical solvers and related algorithms http://eigen.tuxfamily.org/ -------------------------------------------------------------------------------- -GLFW 3.3.0 (dev) zlib/libpng license -A cross-platform library for creating windows with OpenGL contexts and receiving -input and events -http://www.glfw.org/ +embree 4.3.1 Apache-2 license +Embree is a collection of high-performance ray tracing kernels +https://github.com/embree/embree +-------------------------------------------------------------------------------- +flann 1.8.4 BSD license +A C++ library for performing fast approximate nearest neighbor searches in high +dimensional spaces +http://www.cs.ubc.ca/research/flann/ -------------------------------------------------------------------------------- GLEW 2.1.0 MIT License A cross-platform open-source C/C++ extension loading library http://glew.sourceforge.net/ -------------------------------------------------------------------------------- -RPly 1.1.3 MIT license -A library to read and write PLY files -http://w3.impa.br/~diego/software/rply/ --------------------------------------------------------------------------------- -zlib 1.2.8 zlib license -A lossless data-compression library used by libpng -http://www.zlib.net/ --------------------------------------------------------------------------------- -libpng 1.6.18 libpng license -The free reference library for reading and writing PNGs -http://www.libpng.org/ --------------------------------------------------------------------------------- -libjpeg 9a libjpeg license -A widely used C library for reading and writing JPEG image files -http://libjpeg.sourceforge.net/ +GLFW 3.3.0 (dev) zlib/libpng license +A cross-platform library for creating windows with OpenGL contexts and receiving +input and events +http://www.glfw.org/ -------------------------------------------------------------------------------- jsoncpp 1.8.4 MIT license A C++ library that allows manipulating JSON values https://github.com/open-source-parsers/jsoncpp -------------------------------------------------------------------------------- -flann 1.8.4 BSD license -A C++ library for performing fast approximate nearest neighbor searches in high -dimensional spaces -http://www.cs.ubc.ca/research/flann/ +libjpeg-turbo 2.1.5.1 BSD-style license +A widely used C library for reading and writing JPEG image files +https://github.com/libjpeg-turbo/libjpeg-turbo -------------------------------------------------------------------------------- -dirent 1.21 MIT license -https://github.com/tronkko/dirent -A C/C++ programming interface for cross-platform filesystem +libpng 1.6.37 libpng license +The free reference library for reading and writing PNGs +http://www.libpng.org/ -------------------------------------------------------------------------------- librealsense 2.44.0 Apache-2 license A cross-platform library for capturing data from the Intel RealSense F200, SR300, R200 and L500 cameras https://github.com/IntelRealSense/librealsense -------------------------------------------------------------------------------- -tinyfiledialogs 2.7.2 zlib license -A lightweight cross-platform file dialog library -https://sourceforge.net/projects/tinyfiledialogs/ --------------------------------------------------------------------------------- -tinygltf v2.2.0 MIT license -Header only C++11 tiny glTF 2.0 library -https://github.com/syoyo/tinygltf --------------------------------------------------------------------------------- -tinyobjloader v1.0.0 MIT license -Tiny but powerful single file wavefront obj loader -https://github.com/syoyo/tinyobjloader --------------------------------------------------------------------------------- -pybind11 v2.13.1 BSD license -Python binding for C++11 -https://github.com/pybind/pybind11 --------------------------------------------------------------------------------- -PoissonReco 12.0 BSD license -Poisson Surface Reconstruction -https://github.com/mkazhdan/PoissonRecon +libzmq 4.3.3 LGPLv3 + static link exception license +ZeroMQ is a high-performance asynchronous messaging library +https://github.com/zeromq/libzmq -------------------------------------------------------------------------------- -CUB 1.8.0 BSD license -A flexible library of cooperative threadblock primitives and other utilities for -CUDA kernel programming -https://github.com/NVlabs/cub +msgpack-c 3.3.0 Boost Software License 1.0 +MessagePack implementation for C and C++ +https://github.com/msgpack/msgpack-c/tree/cpp_master -------------------------------------------------------------------------------- nanoflann 1.3.1 BSD license A C++11 header-only library for Nearest Neighbor (NN) search with KD-trees https://github.com/jlblancoc/nanoflann -------------------------------------------------------------------------------- -CUTLASS 1.3.3 BSD license -CUDA Templates for Linear Algebra Subroutines -https://github.com/NVIDIA/cutlass +PoissonReco 12.0 BSD license +Poisson Surface Reconstruction +https://github.com/mkazhdan/PoissonRecon -------------------------------------------------------------------------------- -benchmark 1.5.0 Apache-2 license -A microbenchmark support library -https://github.com/google/benchmark +pybind11 v2.13.1 BSD license +Python binding for C++11 +https://github.com/pybind/pybind11 -------------------------------------------------------------------------------- -msgpack-c da2fc25f8 Boost Software License 1.0 -MessagePack implementation for C and C++ -https://github.com/msgpack/msgpack-c/tree/cpp_master +RPly 1.1.3 MIT license +A library to read and write PLY files +http://w3.impa.br/~diego/software/rply/ -------------------------------------------------------------------------------- -libzmq 4.3.2 LGPLv3 + static link exception license -ZeroMQ is a high-performance asynchronous messaging library -https://github.com/zeromq/libzmq +stdgpu 1b6a3319 Apache-2.0 license +Efficient STL-like Data Structures on the GPU +https://github.com/stotko/stdgpu/ -------------------------------------------------------------------------------- -cppzmq 4.6.0 MIT license -Header-only C++ binding for libzmq -https://github.com/zeromq/cppzmq -As an alternative, you can modify 3rdparty/zeromq/zeromq_build.cmake to fetch -zeromq from our fork -https://github.com/isl-org/libzmq +tinyfiledialogs 2.7.2 zlib license +A lightweight cross-platform file dialog library +https://sourceforge.net/projects/tinyfiledialogs/ -------------------------------------------------------------------------------- -embree 4.3.1 Apache-2 license -Embree is a collection of high-performance ray tracing kernels -https://github.com/embree/embree +tinygltf 72f4a55 MIT license +Header only C++11 tiny glTF 2.0 library +https://github.com/syoyo/tinygltf -------------------------------------------------------------------------------- -curl 7.79.1 Curl license -Curl is a command-line tool for transferring data specified with URL syntax. -https://github.com/curl/curl +tinyobjloader v1.0.0 MIT license +Tiny but powerful single file wavefront obj loader +https://github.com/syoyo/tinyobjloader -------------------------------------------------------------------------------- -boringssl: edfe413 Dual OpenSSL, SSLeay, ISC license -BoringSSL is a fork of OpenSSL that is designed to meet Google's needs. -https://github.com/google/boringssl +UVAtlas may2022 MIT license +UVAtlas isochart texture atlas +https://github.com/microsoft/uvatlas -------------------------------------------------------------------------------- vtk 9.1 BSD license The Visualization Toolkit (VTK) https://gitlab.kitware.com/vtk/vtk -------------------------------------------------------------------------------- -DirectX-Headers v1.606.3 MIT license -Official DirectX headers available under an open source license -https://github.com/microsoft/DirectX-Headers --------------------------------------------------------------------------------- -DirectXMath may2022 MIT license -DirectXMath is an all inline SIMD C++ linear algebra library for use in games -and graphics apps -https://github.com/microsoft/DirectXMath --------------------------------------------------------------------------------- -UVAtlas may2022 MIT license -UVAtlas isochart texture atlas -https://github.com/microsoft/uvatlas +zlib 1.2.13 zlib license +A lossless data-compression library used by libpng +http://www.zlib.net/ -------------------------------------------------------------------------------- ``` diff --git a/docker/Dockerfile.ci b/docker/Dockerfile.ci index 5c5991e5a76..73feb7bffd6 100644 --- a/docker/Dockerfile.ci +++ b/docker/Dockerfile.ci @@ -153,8 +153,7 @@ RUN CCACHE_DIR=$(ccache -p | grep cache_dir | grep -oE "[^ ]+$") \ # Checkout Open3D-ML main branch # TODO: We may add support for local Open3D-ML repo or pinned ML repo tag ENV OPEN3D_ML_ROOT=/root/Open3D-ML -# Remove branch before merge -RUN git clone --depth 1 --branch ss/python-3.12 https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} +RUN git clone --depth 1 https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} # Open3D repo # Always keep /root/Open3D as the WORKDIR diff --git a/docker/Dockerfile.wheel b/docker/Dockerfile.wheel index 71a16a2ac13..25372da258d 100644 --- a/docker/Dockerfile.wheel +++ b/docker/Dockerfile.wheel @@ -1,5 +1,5 @@ # FROM must be called before other ARGS except for ARG BASE_IMAGE -ARG BASE_IMAGE=nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04 +ARG BASE_IMAGE=nvidia/cuda:12.1.0-cudnn8-devel-ubuntu20.04 FROM ${BASE_IMAGE} # Customizable build arguments from cuda.yml @@ -98,8 +98,7 @@ RUN which python \ # Checkout Open3D-ML main branch # TODO: We may add support for local Open3D-ML repo or pinned ML repo tag ENV OPEN3D_ML_ROOT=/root/Open3D-ML -# Remove branch before merge -RUN git clone --depth 1 --branch ss/python-3.12 https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} +RUN git clone --depth 1 https://github.com/isl-org/Open3D-ML.git ${OPEN3D_ML_ROOT} # Open3D C++ dependencies # Done before copying the full Open3D directory for better Docker caching diff --git a/docker/README.md b/docker/README.md index 0501f0a3caa..cd3a8198579 100644 --- a/docker/README.md +++ b/docker/README.md @@ -26,7 +26,7 @@ to install Nvidia Docker to run the CUDA container. To verify that the Nvidia Docker is working, run: ```bash -docker run --rm --gpus all nvidia/cuda:11.8-base nvidia-smi +docker run --rm --gpus all nvidia/cuda:12.1-base nvidia-smi ``` ### ARM64 Docker diff --git a/docs/arm.rst b/docs/arm.rst index 93d5b47f126..35ddf813bed 100644 --- a/docs/arm.rst +++ b/docs/arm.rst @@ -71,7 +71,7 @@ commands: ./docker_build.sh openblas-arm64-py39 # Python 3.9 ./docker_build.sh openblas-arm64-py310 # Python 3.10 ./docker_build.sh openblas-arm64-py311 # Python 3.11 - ./docker_build.sh openblas-arm64-py311 # Python 3.12 + ./docker_build.sh openblas-arm64-py312 # Python 3.12 After running ``docker_build.sh``, you shall see a ``.whl`` file generated the current directly on the host. Then simply install the ``.whl`` file by: diff --git a/util/ci_utils.sh b/util/ci_utils.sh index 4d1b96faba9..5ba30b071dc 100644 --- a/util/ci_utils.sh +++ b/util/ci_utils.sh @@ -27,9 +27,6 @@ LOW_MEM_USAGE=${LOW_MEM_USAGE:-OFF} # ML TENSORFLOW_VER="2.16.2" TORCH_VER="2.2.2" -TORCH_CPU_GLNX_VER="${TORCH_VER}+cpu" -TORCH_CUDA_GLNX_VER="${TORCH_VER}+cu118" # match CUDA_VERSION in docker/docker_build.sh -TORCH_MACOS_VER="${TORCH_VER}" TORCH_REPO_URL="https://download.pytorch.org/whl/torch/" # Python PIP_VER="23.2.1"