├── .clang-format ├── .cmake-format.json ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── workflows │ ├── build.yml │ ├── lint.yml │ └── publish-to-pypi.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── CITATION.cff ├── CMakeLists.txt ├── LICENSE ├── README.md ├── README_CN.md ├── cmake ├── Modules │ └── Platform │ │ └── ios.toolchain.cmake ├── XRPrimerConfig.cmake.in ├── code-coverage.cmake ├── compiler_options.cmake ├── conan.cmake ├── dependencies.cmake ├── external │ ├── ceres.cmake │ ├── common.cmake │ ├── eigen.cmake │ ├── jsoncpp.cmake │ ├── opencv.cmake │ ├── pnpsolver.cmake │ ├── pybind11.cmake │ └── spdlog.cmake ├── setup_deps.cmake ├── unit_test_dep.cmake └── version.cmake ├── conanfile.txt ├── cpp ├── CMakeLists.txt ├── pybind │ ├── CMakeLists.txt │ ├── calibration │ │ ├── calibrator_api.cpp │ │ └── calibrator_api.h │ ├── common │ │ ├── version.cpp │ │ └── version.h │ ├── data_structure │ │ ├── camera.cpp │ │ ├── camera.h │ │ ├── pose.cpp │ │ └── pose.h │ ├── ops │ │ ├── pnpsolver.cpp │ │ └── pnpsolver.h │ ├── xrprimer_pybind.cpp │ └── xrprimer_pybind.h ├── samples │ ├── CMakeLists.txt │ └── sample.cpp ├── tests │ ├── CMakeLists.txt │ ├── catch.hpp │ ├── filesystem_utils.cpp │ ├── filesystem_utils.hpp │ ├── test_calibrator.cpp │ ├── test_camera.cpp │ ├── test_image.cpp │ ├── test_multi_camera_calibrator.py │ ├── test_pnpsolver.py │ ├── test_pose.cpp │ └── test_version.cpp └── xrprimer │ ├── CMakeLists.txt │ ├── calibration │ ├── calibrator.cpp │ ├── calibrator.h │ ├── calibrator_api.cpp │ └── calibrator_api.h │ ├── common │ ├── config.h.in │ ├── version.cpp │ └── version.h │ ├── data_structure │ ├── camera │ │ ├── camera.cpp │ │ ├── camera.h │ │ ├── fisheye_camera.cpp │ │ ├── fisheye_camera.h │ │ ├── json_helper_internal.h │ │ ├── omni_camera.cpp │ │ ├── omni_camera.h │ │ ├── pinhole_camera.cpp │ │ └── pinhole_camera.h │ ├── image.cpp │ ├── image.h │ ├── math_util.h │ ├── pose.cpp │ └── pose.h │ └── utils │ ├── logging.cpp │ └── logging.h ├── dockerfiles ├── publish_manylinux │ ├── Dockerfile │ └── build_publish_docker.sh └── runtime_ubt18 │ ├── Dockerfile │ └── build_runtime_docker.sh ├── docs ├── en │ ├── Doxyfile.in │ ├── Makefile │ ├── _static │ │ ├── doxygen-awesome.css │ │ └── xr_logo.ico │ ├── api.rst │ ├── build_docs.py │ ├── changelog.md │ ├── conf.py │ ├── cpp_api.rst │ ├── data_structure │ │ ├── camera.md │ │ ├── image.md │ │ ├── keypoints.md │ │ ├── limbs.md │ │ └── pose.md │ ├── faq.md │ ├── index.rst │ ├── installation │ │ ├── cpp.md │ │ └── python.md │ ├── license.rst │ ├── make.bat │ ├── ops │ │ ├── projector.md │ │ └── triangulator.md │ ├── test.md │ ├── tools │ │ └── calibrate_multiple_cameras.md │ ├── transform │ │ └── camera_convention.md │ └── visualization │ │ └── keypoints_visualization.md └── zh_cn │ └── installation.md ├── python ├── config │ ├── calibration │ │ ├── mview_fisheye_calibrator.py │ │ ├── mview_pinhole_calibrator.py │ │ └── sview_fisheye_distortion_calibrator.py │ └── ops │ │ ├── projection │ │ └── opencv_projector.py │ │ └── triangulation │ │ └── opencv_triangulator.py ├── tests │ ├── calibration │ │ ├── test_mview_calibrator.py │ │ └── test_sview_intrinsic_calibrator.py │ ├── data_structure │ │ ├── test_fisheye_camera_parameter.py │ │ ├── test_keypoints.py │ │ ├── test_limbs.py │ │ ├── test_omni_camera_parameter.py │ │ └── test_pinhole_camera_parameter.py │ ├── ops │ │ ├── test_projection.py │ │ └── test_triangulation.py │ ├── transform │ │ ├── camera │ │ │ ├── test_distortion.py │ │ │ └── test_extrinsic.py │ │ ├── convention │ │ │ ├── test_camera_convention.py │ │ │ └── test_keypoints_convention.py │ │ ├── test_image.py │ │ └── test_limbs_transform.py │ ├── utils │ │ ├── test_ffmpeg_utils.py │ │ ├── test_log_utils.py │ │ └── test_path_utils.py │ └── visualization │ │ ├── keypoints │ │ ├── test_visualize_keypoints2d.py │ │ └── test_visualize_keypoints3d.py │ │ ├── matplotlib │ │ ├── test_plot_frame_plt.py │ │ └── test_plot_video_plt.py │ │ └── opencv │ │ ├── test_plot_frame_cv2.py │ │ └── test_plot_video_cv2.py └── xrprimer │ ├── __init__.py │ ├── calibration │ ├── __init__.py │ ├── base_calibrator.py │ ├── builder.py │ ├── mview_fisheye_calibrator.py │ ├── mview_pinhole_calibrator.py │ └── sview_fisheye_calibrator.py │ ├── data_structure │ ├── __init__.py │ ├── camera │ │ ├── .gitkeep │ │ ├── __init__.py │ │ ├── camera.py │ │ ├── fisheye_camera.py │ │ ├── omni_camera.py │ │ └── pinhole_camera.py │ ├── keypoints.py │ └── limbs.py │ ├── io │ └── exr_reader.py │ ├── ops │ ├── __init__.py │ ├── projection │ │ ├── __init__.py │ │ ├── base_projector.py │ │ ├── builder.py │ │ └── opencv_projector.py │ └── triangulation │ │ ├── __init__.py │ │ ├── base_triangulator.py │ │ ├── builder.py │ │ └── opencv_triangulator.py │ ├── transform │ ├── __init__.py │ ├── camera │ │ ├── __init__.py │ │ ├── distortion.py │ │ └── extrinsic.py │ ├── convention │ │ ├── __init__.py │ │ ├── camera │ │ │ ├── __init__.py │ │ │ ├── from_opencv.py │ │ │ ├── intrinsic.py │ │ │ └── to_opencv.py │ │ ├── keypoints_convention │ │ │ ├── __init__.py │ │ │ ├── agora.py │ │ │ ├── campus.py │ │ │ ├── coco.py │ │ │ ├── coco_wholebody.py │ │ │ ├── crowdpose.py │ │ │ ├── face3d.py │ │ │ ├── flame.py │ │ │ ├── gta.py │ │ │ ├── h36m.py │ │ │ ├── human_data.py │ │ │ ├── hybrik.py │ │ │ ├── instavariety.py │ │ │ ├── lsp.py │ │ │ ├── mano.py │ │ │ ├── mediapipe.py │ │ │ ├── mpi_inf_3dhp.py │ │ │ ├── mpii.py │ │ │ ├── openpose.py │ │ │ ├── panoptic.py │ │ │ ├── penn_action.py │ │ │ ├── posetrack.py │ │ │ ├── pw3d.py │ │ │ ├── smpl.py │ │ │ ├── smplx.py │ │ │ ├── spin_smplx.py │ │ │ └── star.py │ │ └── world │ │ │ ├── __init__.py │ │ │ ├── base_world.py │ │ │ ├── plt_world.py │ │ │ └── smc_world.py │ ├── image │ │ ├── __init__.py │ │ └── color.py │ ├── limbs │ │ └── __init__.py │ └── point │ │ ├── __init__.py │ │ └── rotation.py │ ├── utils │ ├── __init__.py │ ├── ffmpeg_utils.py │ ├── log_utils.py │ ├── path_utils.py │ ├── synbody_utils.py │ └── visualization_utils.py │ ├── version.py │ └── visualization │ ├── __init__.py │ ├── keypoints │ ├── __init__.py │ ├── visualize_keypoints2d.py │ └── visualize_keypoints3d.py │ ├── matplotlib │ ├── __init__.py │ ├── plot_frame.py │ └── plot_video.py │ ├── opencv │ ├── __init__.py │ ├── plot_frame.py │ └── plot_video.py │ ├── palette │ ├── __init__.py │ ├── line_palette.py │ └── point_palette.py │ └── presets │ ├── __init__.py │ └── axis.py ├── requirements ├── docs.txt ├── readthedocs.txt ├── runtime.txt ├── synbody.txt ├── test.txt └── visualize.txt ├── resources └── xrprimer-logo.png ├── scripts ├── build_ios.sh └── run_pinhole_camera_calib.sh ├── setup.cfg ├── setup.py └── version.txt /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at openxrlab@pjlab.org.cn. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | For answers to common questions about this code of conduct, see 74 | https://www.contributor-covenant.org/faq 75 | 76 | [homepage]: https://www.contributor-covenant.org 77 | 78 | For answers to common questions about this code of conduct, see 79 | https://www.contributor-covenant.org/faq 80 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to XRPrimer 2 | 3 | All kinds of contributions are welcome, including but not limited to the following. 4 | 5 | - Fixes (typo, bugs) 6 | - New features and components 7 | 8 | ## Workflow 9 | 10 | 1. Fork and pull the latest xrprimer 11 | 1. Checkout a new branch with a meaningful name (do not use master branch for PRs) 12 | 1. Commit your changes 13 | 1. Create a PR 14 | 15 | ```{note} 16 | - If you plan to add some new features that involve large changes, it is encouraged to open an issue for discussion first. 17 | - If you are the author of some papers and would like to include your method to xrprimer, please contact us. We will much appreciate your contribution. 18 | ``` 19 | 20 | ## Code style 21 | 22 | ### Python 23 | 24 | We adopt [PEP8](https://www.python.org/dev/peps/pep-0008/) as the preferred code style. 25 | 26 | We use the following tools for linting and formatting: 27 | 28 | - [flake8](http://flake8.pycqa.org/en/latest/): linter 29 | - [yapf](https://github.com/google/yapf): formatter 30 | - [isort](https://github.com/timothycrosley/isort): sort imports 31 | 32 | Style configurations of yapf and isort can be found in [setup.cfg](../setup.cfg). 33 | 34 | We use [pre-commit hook](https://pre-commit.com/) that checks and formats for `flake8`, `yapf`, `isort`, `trailing whitespaces`, 35 | fixes `end-of-files`, sorts `requirments.txt` automatically on every commit. 36 | The config for a pre-commit hook is stored in [.pre-commit-config](../.pre-commit-config.yaml). 37 | 38 | After you clone the repository, you will need to install initialize pre-commit hook. 39 | 40 | ``` 41 | pip install -U pre-commit 42 | ``` 43 | 44 | From the repository folder 45 | 46 | ``` 47 | pre-commit install 48 | ``` 49 | 50 | If you are facing an issue when installing markdown lint, you may install ruby for markdown lint by 51 | referring to [this repo](https://github.com/innerlee/setup) by following the usage and taking [`zzruby.sh`](https://github.com/innerlee/setup/blob/master/zzruby.sh) 52 | 53 | or by the following steps 54 | 55 | ```shell 56 | # install rvm 57 | curl -L https://get.rvm.io | bash -s -- --autolibs=read-fail 58 | rvm autolibs disable 59 | # install ruby 60 | rvm install 2.7.1 61 | ``` 62 | 63 | After this on every commit check code linters and formatter will be enforced. 64 | 65 | > Before you create a PR, make sure that your code lints and is formatted by yapf. 66 | 67 | ### C++ and CUDA 68 | 69 | We follow the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). 70 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Python 3.8 15 | uses: actions/setup-python@v2 16 | with: 17 | python-version: 3.8 18 | - name: Install pre-commit hook 19 | run: | 20 | sudo apt-add-repository ppa:brightbox/ruby-ng -y 21 | sudo apt-get update 22 | sudo apt-get install -y ruby2.7 23 | pip install pre-commit 24 | pre-commit install 25 | - name: Linting 26 | run: pre-commit run --all-files 27 | - name: Check docstring coverage 28 | run: | 29 | pip install interrogate 30 | interrogate -vinmMI --ignore-init-method --ignore-module --ignore-nested-functions --ignore-regex "__repr__" -f 60 python/xrprimer/ 31 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-pypi.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'publish*' 7 | tags: 8 | - 'v*' 9 | 10 | jobs: 11 | build-n-publish: 12 | runs-on: ubuntu-22.04 13 | container: 14 | image: openxrlab/xrprimer_ci:manylinux2014_x86_64_torch1110_mmcv170 15 | strategy: 16 | matrix: 17 | python-version: [3.6, 3.7, 3.8, 3.9, '3.10'] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Build XRPrimer 21 | run: | 22 | source /opt/miniconda/etc/profile.d/conda.sh && conda activate py-${{ matrix.python-version }} 23 | conan remote add openxrlab http://conan.openxrlab.org.cn/artifactory/api/conan/openxrlab 24 | python setup.py bdist_wheel 25 | - name: Publish distribution to PyPI 26 | run: | 27 | for i in $( ls dist/*-linux_x86_64.whl ); do auditwheel repair $i; done 28 | du -sh wheelhouse/* 29 | for i in $( ls wheelhouse/*.whl ); do auditwheel show $i; done 30 | twine upload wheelhouse/* -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} 31 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: .*/tests/data/|.clang-format|(tests/catch.hpp) 2 | repos: 3 | - repo: https://github.com/pre-commit/mirrors-clang-format 4 | rev: v14.0.1 5 | hooks: 6 | - id: clang-format 7 | exclude: (.*\.json) 8 | - repo: https://github.com/PyCQA/flake8.git 9 | rev: 5.0.4 10 | hooks: 11 | - id: flake8 12 | - repo: https://github.com/asottile/seed-isort-config.git 13 | rev: v2.2.0 14 | hooks: 15 | - id: seed-isort-config 16 | args: [--settings-path, ./] 17 | - repo: https://github.com/PyCQA/isort.git 18 | rev: 5.12.0 19 | hooks: 20 | - id: isort 21 | args: [--settings-file, ./setup.cfg] 22 | - repo: https://github.com/pre-commit/mirrors-yapf.git 23 | rev: v0.30.0 24 | hooks: 25 | - id: yapf 26 | - repo: https://github.com/pre-commit/pre-commit-hooks.git 27 | rev: v3.1.0 28 | hooks: 29 | - id: trailing-whitespace 30 | args: [--markdown-linebreak-ext=md] 31 | exclude: .*/tests/data/ 32 | - id: check-yaml 33 | - id: end-of-file-fixer 34 | - id: requirements-txt-fixer 35 | - id: double-quote-string-fixer 36 | - id: check-merge-conflict 37 | - id: fix-encoding-pragma 38 | args: ["--remove"] 39 | - id: mixed-line-ending 40 | args: ["--fix=lf"] 41 | - repo: https://github.com/codespell-project/codespell 42 | rev: v2.1.0 43 | hooks: 44 | - id: codespell 45 | - repo: https://github.com/myint/docformatter.git 46 | rev: v1.3.1 47 | hooks: 48 | - id: docformatter 49 | args: ["--in-place", "--wrap-descriptions", "79"] 50 | 51 | - repo: https://github.com/cheshirekow/cmake-format-precommit 52 | rev: v0.6.13 53 | hooks: 54 | - id: cmake-format 55 | exclude: conan\.cmake|ios\.toolchain\.cmake 56 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.8" 7 | 8 | formats: 9 | - epub 10 | 11 | python: 12 | install: 13 | - requirements: requirements/docs.txt 14 | - requirements: requirements/readthedocs.txt 15 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - name: "XRPrimer Contributors" 5 | title: "XRPrimer: OpenXRLab foundational library for XR-related algorithms" 6 | date-released: 2022-09-01 7 | url: "https://github.com/openxrlab/xrprimer" 8 | license: Apache-2.0 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | # meta info 4 | # 5 | set(META_PROJECT_NAME "xrprimer") 6 | set(META_PROJECT_DESCRIPTION "OpenXRLab foundational library") 7 | set(META_AUTHOR_ORGANIZATION "OpenXRLab") 8 | set(META_AUTHOR_DOMAIN "OpenXRLab") 9 | set(META_AUTHOR_MAINTAINER "openxrlab@pjlab.org.cn") 10 | 11 | include(cmake/version.cmake) 12 | 13 | project(${META_PROJECT_NAME} VERSION ${META_VERSION} LANGUAGES C CXX) 14 | 15 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 16 | set(_DEFAULT_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install) 17 | set(CMAKE_INSTALL_PREFIX ${_DEFAULT_INSTALL_PREFIX} 18 | CACHE PATH "default install prefix" FORCE 19 | ) 20 | endif() 21 | 22 | if(NOT CMAKE_BUILD_TYPE) 23 | set(CMAKE_BUILD_TYPE Release) 24 | endif() 25 | 26 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 27 | 28 | # 29 | # for project dependencies libraries 30 | # 31 | # Setup dependencies library, when first run 32 | # 33 | option(3RT_FROM_LOCAL 34 | "Dependencies library will be built or find in local host." OFF 35 | ) 36 | option(3RT_FROM_CONAN 37 | "Dependencies library will be download from openxrlab conan remote" OFF 38 | ) 39 | 40 | # set external project install prefix 41 | set(STAGED_INSTALL_PREFIX 42 | ${CMAKE_SOURCE_DIR}/3rdparty 43 | CACHE 44 | STRING 45 | "dependencies install directory, if not exist, need setup You need to get the dependencies first" 46 | ) 47 | 48 | # add cmake prefix path for find external project, find_package() 49 | list(INSERT CMAKE_PREFIX_PATH 0 ${STAGED_INSTALL_PREFIX}) 50 | 51 | if(IOS) 52 | # for find external framework bundle(same as 3rdparty) 53 | set(xrprimer_framework_path ${STAGED_INSTALL_PREFIX}) 54 | endif() 55 | 56 | # install 3rdparty for dist. 57 | install(DIRECTORY ${STAGED_INSTALL_PREFIX}/ DESTINATION 3rdparty/) 58 | 59 | if(NOT EXISTS ${STAGED_INSTALL_PREFIX} AND NOT 3RT_FROM_LOCAL 60 | AND NOT 3RT_FROM_CONAN 61 | ) 62 | message( 63 | FATAL_ERROR 64 | " 65 | --------------------------------------------------------------------------- 66 | You need to get the dependencies first, Configure cmake with `-D3RT_FROM_LOCAL=ON` OR `-D3RT_FROM_CONAN=ON` 67 | and cmake --build 68 | 3RT_FROM_LOCAL: Dependencies library will be built or find in local host 69 | 3RT_FROM_CONAN: Dependencies library will be download from openxrlab conan remote 70 | --------------------------------------------------------------------------- 71 | " 72 | ) 73 | endif() 74 | 75 | if(3RT_FROM_LOCAL OR 3RT_FROM_CONAN) 76 | # Only build deps from source or download prebuilt library 77 | include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/setup_deps.cmake) 78 | 79 | return() 80 | endif() 81 | 82 | # 83 | # for project 84 | # 85 | option(BUILD_SHARED_LIBS "Build shared instead of static libraries." OFF) 86 | option(ENABLE_TEST "Build unit test case." OFF) 87 | option(PYTHON_BINDING "Enable Python binding." ON) 88 | option(CODE_COVERAGE "code coverage" OFF) 89 | 90 | include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/dependencies.cmake) 91 | include(${CMAKE_SOURCE_DIR}/cmake/code-coverage.cmake) 92 | 93 | cmake_policy(SET CMP0042 NEW) # ENABLE CMP0042: MACOSX_RPATH is enabled by 94 | cmake_policy(SET CMP0063 NEW) # ENABLE CMP0063: Honor visibility properties for 95 | cmake_policy(SET CMP0077 NEW) # ENABLE CMP0077: option() honors normal variables 96 | 97 | # 98 | # c++11 99 | # 100 | set(CMAKE_CXX_STANDARD 11) 101 | set(CMAKE_CXX_EXTENSIONS OFF) 102 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 103 | 104 | # 105 | # hidden 106 | # 107 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 108 | set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) 109 | 110 | # include(GNUInstallDirs) 111 | 112 | # 113 | # cpp 114 | # 115 | add_subdirectory(cpp) 116 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/README_CN.md -------------------------------------------------------------------------------- /cmake/XRPrimerConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # =================================================================================== 2 | # The XRPrimer CMake configuration file 3 | # 4 | # ** File generated automatically, do not modify ** 5 | # 6 | # Usage from an external project: 7 | # In your CMakeLists.txt, add these lines: 8 | # 9 | # find_package(XRPrimer REQUIRED) 10 | # target_link_libraries(MY_TARGET_NAME XRPrimer::XRPrimer) 11 | # 12 | # 13 | # =================================================================================== 14 | 15 | ### Initialisation performed by CONFIGURE_PACKAGE_CONFIG_FILE: 16 | @PACKAGE_INIT@ 17 | 18 | include(CMakeFindDependencyMacro) 19 | 20 | list(APPEND CMAKE_PREFIX_PATH "${PACKAGE_PREFIX_DIR}/3rdparty") 21 | if(IOS) 22 | # for find external framework bundle(same as 3rdparty) 23 | set(xrprimer_framework_path "${PACKAGE_PREFIX_DIR}/3rdparty") 24 | endif() 25 | 26 | include(${CMAKE_CURRENT_LIST_DIR}/dependencies.cmake) 27 | 28 | IF(NOT TARGET XRPrimer::xrprimer) 29 | INCLUDE("${CMAKE_CURRENT_LIST_DIR}/xrprimer-targets.cmake") 30 | ENDIF() 31 | -------------------------------------------------------------------------------- /cmake/code-coverage.cmake: -------------------------------------------------------------------------------- 1 | if(CODE_COVERAGE) 2 | if(CMAKE_BUILD_TYPE) 3 | string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type) 4 | if(NOT ${upper_build_type} STREQUAL "DEBUG") 5 | message( 6 | STATUS 7 | "**WARNING** Code coverage need CMAKE_BUILD_TYPE=Debug, FORCE use Debug" 8 | ) 9 | endif() 10 | endif() 11 | 12 | set(CMAKE_BUILD_TYPE Debug) 13 | set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) 14 | set(CMAKE_C_OUTPUT_EXTENSION_REPLACE 1) 15 | 16 | if((CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL 17 | "Clang") 18 | AND (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID 19 | STREQUAL "AppleClang") 20 | ) 21 | set(_GCOV_FLAGS "-fprofile-instr-generate -fcoverage-mapping") 22 | endif() 23 | if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 24 | set(_GCOV_FLAGS "-fprofile-arcs -ftest-coverage") 25 | endif() 26 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${_GCOV_FLAGS}") 27 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${_GCOV_FLAGS}") 28 | endif() 29 | -------------------------------------------------------------------------------- /cmake/compiler_options.cmake: -------------------------------------------------------------------------------- 1 | # MSVC compiler options 2 | if("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") 3 | set(DEFAULT_COMPILE_OPTIONS 4 | ${DEFAULT_COMPILE_OPTIONS} 5 | PRIVATE 6 | /MP # -> build with multiple processes 7 | /W4 # -> warning level 4 8 | # /WX # -> treat warnings as errors $<$: /RTCc # 9 | # -> value is assigned to a smaller data type and results in a data loss 10 | # > 11 | $<$: 12 | /Gw # -> whole program global optimization 13 | /GS- # -> buffer security check: no 14 | /GL # -> whole program optimization: enable link-time code generation 15 | # (disables Zi) 16 | /GF # -> enable string pooling 17 | > 18 | # No manual c++11 enable for MSVC as all supported MSVC versions for 19 | # cmake-init have C++11 implicitly enabled (MSVC >=2013) 20 | PUBLIC 21 | /wd4251 # -> disable warning: 'identifier': class 'type' needs to have 22 | # dll-interface to be used by clients of class 'type2' 23 | /wd4592 # -> disable warning: 'identifier': symbol will be dynamically 24 | # initialized (implementation limitation) 25 | # /wd4201 # -> disable warning: nonstandard extension used: nameless 26 | # struct/union (caused by GLM) /wd4127 # -> disable warning: conditional 27 | # expression is constant (caused by Qt) 28 | ) 29 | endif() 30 | 31 | # GCC and Clang compiler options 32 | if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR "${CMAKE_CXX_COMPILER_ID}" 33 | MATCHES "Clang" 34 | ) 35 | set(DEFAULT_COMPILE_OPTIONS 36 | ${DEFAULT_COMPILE_OPTIONS} 37 | PRIVATE 38 | -Wall 39 | -Wextra 40 | -Wunused 41 | -Wreorder 42 | -Wignored-qualifiers 43 | -Wmissing-braces 44 | -Wreturn-type 45 | -Wswitch 46 | -Wswitch-default 47 | -Wuninitialized 48 | -Wmissing-field-initializers 49 | $<$: 50 | -Wmaybe-uninitialized 51 | $<$,4.8>: 52 | -Wpedantic 53 | -Wreturn-local-addr 54 | > 55 | > 56 | $<$: 57 | -Wpedantic 58 | # -Wreturn-stack-address # gives false positives 59 | > 60 | $<$: 61 | -fprofile-arcs 62 | -ftest-coverage 63 | > 64 | PUBLIC 65 | $<$: 66 | -pthread 67 | > 68 | ) 69 | endif() 70 | -------------------------------------------------------------------------------- /cmake/dependencies.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # find external project and embbed to main project 3 | # 4 | 5 | if(NOT IOS) 6 | find_package(Eigen3 REQUIRED CONFIG) 7 | find_package(Ceres REQUIRED CONFIG) 8 | 9 | if(NOT TARGET Ceres::ceres) 10 | add_library(Ceres::ceres INTERFACE IMPORTED) 11 | set_target_properties( 12 | Ceres::ceres PROPERTIES INTERFACE_LINK_LIBRARIES ceres 13 | ) 14 | endif() 15 | 16 | find_package(OpenCV REQUIRED CONFIG) 17 | 18 | if(OpenCV_FOUND) 19 | add_library(OpenCV::OpenCV INTERFACE IMPORTED GLOBAL) 20 | target_include_directories( 21 | OpenCV::OpenCV INTERFACE ${OpenCV_INCLUDE_DIRS} 22 | ) 23 | target_link_libraries(OpenCV::OpenCV INTERFACE ${OpenCV_LIBS}) 24 | endif() 25 | 26 | find_package(pybind11 REQUIRED CONFIG) 27 | find_package(jsoncpp REQUIRED CONFIG) 28 | find_package(spdlog REQUIRED CONFIG) 29 | find_package(PnpSolver REQUIRED CONFIG) 30 | else() 31 | # 指定opencv_framework_path 32 | if(EXISTS ${xrprimer_framework_path}) 33 | add_library(OpenCV::OpenCV INTERFACE IMPORTED GLOBAL) 34 | target_compile_options( 35 | OpenCV::OpenCV INTERFACE -F${xrprimer_framework_path} 36 | ) 37 | target_link_options(OpenCV::OpenCV INTERFACE -framework opencv2) 38 | else() 39 | message( 40 | FATAL_ERROR 41 | "[XRPrimer] can not found opencv2 framework on IOS, please check xrprimer_framework_path ${xrprimer_framework_path}" 42 | ) 43 | endif() 44 | endif() 45 | -------------------------------------------------------------------------------- /cmake/external/ceres.cmake: -------------------------------------------------------------------------------- 1 | find_package(LAPACK) 2 | 3 | if(NOT LAPACK_FOUND) 4 | message( 5 | FATAL_ERROR 6 | "[LAPACK AND BLAS] is required to build Ceres, 7 | -------------------------------------- 8 | Ubuntu: apt -y install libatlas-base-dev libsuitesparse-dev ibgoogle-glog-dev 9 | Centos7: yum -y install atlas-devel 10 | -------------------------------------- 11 | " 12 | ) 13 | endif() 14 | 15 | include(ExternalProject) 16 | externalproject_add( 17 | ext_ceres 18 | PREFIX ceres 19 | URL https://github.com/ceres-solver/ceres-solver/archive/refs/tags/2.1.0.zip 20 | URL_HASH 21 | SHA256=57149e2018f3cf3fe97bc1df4a36f33319800b3f752ec1d01b28788d21cd496e 22 | CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/ceres 23 | -DBUILD_EXAMPLES=OFF 24 | -DBUILD_TESTING=OFF 25 | -DMINIGLOG=ON 26 | -DMINIGLOG_MAX_LOG_LEVEL=-2 27 | -DGFLAGS=OFF 28 | -DTBB=OFF 29 | -DOPENMP=OFF 30 | -DLAPACK=ON 31 | -DBUILD_BENCHMARKS=OFF 32 | -DPROVIDE_UNINSTALL_TARGET=OFF 33 | -DCMAKE_PREFIX_PATH=${STAGED_INSTALL_PREFIX}/eigen3 34 | ${ExternalProject_CMAKE_ARGS_hidden} 35 | ) 36 | 37 | add_dependencies(ext_ceres ext_eigen) 38 | -------------------------------------------------------------------------------- /cmake/external/common.cmake: -------------------------------------------------------------------------------- 1 | # CMake arguments for configuring ExternalProjects. Use the second _hidden 2 | # version by default. 3 | set(ExternalProject_CMAKE_ARGS 4 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 5 | -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} 6 | -DCMAKE_CUDA_COMPILER=${CMAKE_CUDA_COMPILER} 7 | -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER} 8 | -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER} 9 | -DCMAKE_CUDA_COMPILER_LAUNCHER=${CMAKE_CUDA_COMPILER_LAUNCHER} 10 | -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} 11 | # Always build 3rd party code in Release mode. Ignored by multi-config 12 | # generators (XCode, MSVC). MSVC needs matching config anyway. 13 | -DCMAKE_BUILD_TYPE=Release 14 | -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} 15 | -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=NEW 16 | -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY} 17 | -DCMAKE_POSITION_INDEPENDENT_CODE=ON 18 | ) 19 | 20 | set(ExternalProject_CMAKE_ARGS_hidden 21 | ${ExternalProject_CMAKE_ARGS} 22 | # Apply LANG_VISIBILITY_PRESET to static libraries and archives as well 23 | -DCMAKE_POLICY_DEFAULT_CMP0063:STRING=NEW 24 | -DCMAKE_POLICY_DEFAULT_CMP0135:STRING=NEW 25 | -DCMAKE_CXX_VISIBILITY_PRESET=hidden 26 | -DCMAKE_CUDA_VISIBILITY_PRESET=hidden 27 | -DCMAKE_C_VISIBILITY_PRESET=hidden 28 | -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON 29 | ) 30 | -------------------------------------------------------------------------------- /cmake/external/eigen.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | externalproject_add( 4 | ext_eigen 5 | PREFIX eigen3 6 | URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz 7 | URL_HASH 8 | SHA256=8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72 9 | CMAKE_ARGS ${ExternalProject_CMAKE_ARGS_hidden} 10 | -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/eigen3 11 | ) 12 | -------------------------------------------------------------------------------- /cmake/external/jsoncpp.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | externalproject_add( 3 | ext_jsoncpp 4 | PREFIX jsoncpp 5 | GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp.git 6 | GIT_TAG 5defb4ed1a4293b8e2bf641e16b156fb9de498cc # 1.9.5 7 | CMAKE_ARGS ${ExternalProject_CMAKE_ARGS_hidden} 8 | -DJSONCPP_WITH_TESTS=OFF 9 | -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF 10 | -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF 11 | -DBUILD_OBJECT_LIBS=OFF 12 | -DCMAKE_CXX_FLAGS="-fPIC" 13 | -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/jsoncpp 14 | ) 15 | -------------------------------------------------------------------------------- /cmake/external/opencv.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | if(NOT IOS) 4 | set(OPENCV_URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.zip) 5 | message(STATUS "opencv url: ${OPENCV_URL}") 6 | externalproject_add( 7 | ext_opencv 8 | PREFIX opencv 9 | URL ${OPENCV_URL} 10 | URL_HASH 11 | SHA256=158db5813a891c7eda8644259fc1dbd76b21bd1ffb9854a8b4b8115a4ceec359 12 | CMAKE_ARGS 13 | ${ExternalProject_CMAKE_ARGS_hidden} 14 | -DBUILD_ZLIB=ON 15 | -DWITH_1394=OFF 16 | -DWITH_ADE=OFF 17 | -DWITH_ARAVIS=OFF 18 | -DWITH_CLP=OFF 19 | -DWITH_CUDA=OFF 20 | -DWITH_CUFFT=OFF 21 | -DWITH_NVCUVID=OFF 22 | -DWITH_CUBLAS=OFF 23 | -DWITH_FFMPEG=OFF 24 | -DWITH_FREETYPE=OFF 25 | -DWITH_GDAL=OFF 26 | -DWITH_GDCM=OFF 27 | -DWITH_GPHOTO2=OFF 28 | -DWITH_GSTREAMER=OFF 29 | -DWITH_GTK=OFF 30 | -DWITH_GTK_2_X=OFF 31 | -DWITH_HALIDE=OFF 32 | -DWITH_HPX=OFF 33 | -DWITH_INF_ENGINE=OFF 34 | -DWITH_IPP=OFF 35 | -DWITH_ITT=OFF 36 | -DWITH_QUIRC=OFF 37 | -DWITH_EIGEN=ON 38 | -DWITH_IMGCODEC_HDR=ON 39 | -DWITH_IMGCODEC_PFM=ON 40 | -DWITH_IMGCODEC_PXM=ON 41 | -DWITH_IMGCODEC_SUNRASTER=ON 42 | -DWITH_JASPER=ON 43 | -DWITH_JPEG=ON 44 | -DBUILD_PROTOBUF=OFF 45 | -DBUILD_opencv_apps=OFF 46 | -DBULID_PNG=ON 47 | -DOPENCV_SKIP_PYTHON_WARNING=ON 48 | -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/opencv 49 | -DBUILD_LIST="core,calib3d,features2d,flann,highgui,imgcodecs,imgproc,video,videoio" 50 | -DCMAKE_PREFIX_PATH=${STAGED_INSTALL_PREFIX}/eigen3 51 | ) 52 | 53 | add_dependencies(ext_opencv ext_eigen) 54 | else() 55 | # iOS use framework 56 | set(OPENCV_URL 57 | https://github.com/opencv/opencv/releases/download/4.6.0/opencv-4.6.0-ios-framework.zip 58 | ) 59 | message(STATUS "opencv url: ${OPENCV_URL}") 60 | 61 | # only download 62 | externalproject_add( 63 | ext_opencv 64 | PREFIX opencv 65 | URL ${OPENCV_URL} 66 | SOURCE_DIR "${STAGED_INSTALL_PREFIX}/opencv2.framework" 67 | CONFIGURE_COMMAND "" 68 | BUILD_COMMAND "" 69 | INSTALL_COMMAND "" 70 | ) 71 | endif() 72 | -------------------------------------------------------------------------------- /cmake/external/pnpsolver.cmake: -------------------------------------------------------------------------------- 1 | if(NOT IOS) 2 | include(ExternalProject) 3 | 4 | externalproject_add( 5 | ext_pnpsolver 6 | PREFIX pnpsolver 7 | GIT_REPOSITORY https://github.com/GACLove/pnpsolver.git 8 | GIT_TAG 08182c16424d8730b36fce50168f820fc0733a74 9 | CMAKE_ARGS ${ExternalProject_CMAKE_ARGS_hidden} -DBUILD_SHARED_LIBS=OFF 10 | -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/pnpsolver 11 | -DCMAKE_PREFIX_PATH=${STAGED_INSTALL_PREFIX} 12 | ) 13 | add_dependencies(ext_pnpsolver ext_ceres) 14 | else() 15 | message(STATUS "[XRPrimer] Disable pnpsolver on IOS") 16 | endif() 17 | -------------------------------------------------------------------------------- /cmake/external/pybind11.cmake: -------------------------------------------------------------------------------- 1 | if(NOT IOS) 2 | include(ExternalProject) 3 | 4 | externalproject_add( 5 | ext_pybind11 6 | PREFIX pybind11 7 | URL https://github.com/pybind/pybind11/archive/refs/tags/v2.6.2.tar.gz 8 | URL_HASH 9 | SHA256=8ff2fff22df038f5cd02cea8af56622bc67f5b64534f1b83b9f133b8366acff2 10 | CMAKE_ARGS ${ExternalProject_CMAKE_ARGS_hidden} 11 | -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/pybind11 12 | -DBUILD_TESTING=OFF 13 | ) 14 | else() 15 | message(STATUS "[XRPrimer] Disable pybind11 on IOS") 16 | endif() 17 | -------------------------------------------------------------------------------- /cmake/external/spdlog.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | externalproject_add( 4 | ext_spdlog 5 | PREFIX spdlog 6 | GIT_REPOSITORY https://github.com/gabime/spdlog.git 7 | GIT_TAG 5b4c4f3f770acbd25400d866f3fc2fdf669d5b7e # 1.9.1 8 | CMAKE_ARGS -DSPDLOG_INSTALL=ON ${ExternalProject_CMAKE_ARGS_hidden} 9 | -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/spdlog 10 | ) 11 | -------------------------------------------------------------------------------- /cmake/setup_deps.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # when first config, only configure external project 3 | # 4 | 5 | if(3RT_FROM_LOCAL) 6 | include(${CMAKE_SOURCE_DIR}/cmake/external/common.cmake) 7 | include(${CMAKE_SOURCE_DIR}/cmake/external/eigen.cmake) 8 | include(${CMAKE_SOURCE_DIR}/cmake/external/ceres.cmake) 9 | include(${CMAKE_SOURCE_DIR}/cmake/external/opencv.cmake) 10 | include(${CMAKE_SOURCE_DIR}/cmake/external/pybind11.cmake) 11 | include(${CMAKE_SOURCE_DIR}/cmake/external/spdlog.cmake) 12 | include(${CMAKE_SOURCE_DIR}/cmake/external/jsoncpp.cmake) 13 | include(${CMAKE_SOURCE_DIR}/cmake/external/pnpsolver.cmake) 14 | endif() 15 | 16 | if(3RT_FROM_CONAN) 17 | # download prebuilt binary from conan 18 | include(${CMAKE_SOURCE_DIR}/cmake/conan.cmake) 19 | 20 | conan_add_remote( 21 | NAME openxrlab INDEX 0 URL 22 | http://conan.openxrlab.org.cn/artifactory/api/conan/openxrlab 23 | VERIFY_SSL True 24 | ) 25 | 26 | conan_cmake_autodetect(settings) 27 | conan_cmake_install( 28 | PATH_OR_REFERENCE ${CMAKE_SOURCE_DIR} REMOTE openxrlab SETTINGS 29 | ${settings} UPDATE 30 | ) 31 | endif() 32 | -------------------------------------------------------------------------------- /cmake/unit_test_dep.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeParseArguments) 2 | 3 | function(xr_add_test_catch2 target) 4 | set(options) 5 | set(multiValueArgs LINKS SRCS) 6 | cmake_parse_arguments( 7 | _TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} 8 | ) 9 | add_executable(${target} ${_TEST_SRCS}) 10 | set_target_properties( 11 | ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin 12 | ) 13 | endfunction() 14 | -------------------------------------------------------------------------------- /cmake/version.cmake: -------------------------------------------------------------------------------- 1 | function(source_group_by_path PARENT_PATH REGEX GROUP) 2 | foreach(FILENAME ${ARGN}) 3 | get_filename_component(FILEPATH "${FILENAME}" REALPATH) 4 | file(RELATIVE_PATH FILEPATH ${PARENT_PATH} ${FILEPATH}) 5 | get_filename_component(FILEPATH "${FILEPATH}" DIRECTORY) 6 | string(REPLACE "/" "\\" FILEPATH "${FILEPATH}") 7 | source_group( 8 | "${GROUP}\\${FILEPATH}" REGULAR_EXPRESSION "${REGEX}" 9 | FILES ${FILENAME} 10 | ) 11 | endforeach() 12 | endfunction(source_group_by_path) 13 | 14 | function(get_revison_from_vcs repo_path revision) 15 | # find_package(Git QUIET REQUIRED) 16 | set(abbrev 0) 17 | 18 | if(${ARGC} GREATER 1) 19 | if(${ARGV2}) 20 | set(abbrev ${ARGV2}) 21 | endif() 22 | endif() 23 | 24 | execute_process( 25 | COMMAND git describe --match=NeVeRmAtCh --always --abbrev=${abbrev} 26 | --dirty WORKING_DIRECTORY "${repo_path}" OUTPUT_VARIABLE rev 27 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE 28 | ) 29 | set(${revision} ${rev} PARENT_SCOPE) 30 | endfunction() 31 | 32 | function(get_commit_date_from_vcs repo_path commit_date) 33 | set(date_pattern "%Y-%m-%d %H:%M:%S") 34 | execute_process( 35 | COMMAND git --no-pager log --pretty=format:%cd 36 | --date=format:${date_pattern} HEAD -1 37 | WORKING_DIRECTORY "${repo_path}" 38 | OUTPUT_VARIABLE date 39 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE 40 | ) 41 | set(${commit_date} ${date} PARENT_SCOPE) 42 | endfunction() 43 | 44 | get_revison_from_vcs(${CMAKE_CURRENT_SOURCE_DIR} GIT_REV) 45 | 46 | # 47 | # parse version number 48 | # 49 | if(DEFINED XRPRIMER_VERSION_MAJOR AND DEFINED XRPRIMER_VERSION_MINOR 50 | AND DEFINED XRPRIMER_VERSION_PATCH 51 | ) 52 | set(XRPRIMER_VERSION_MAJOR ${XRPRIMER_VERSION_MAJOR}) 53 | set(XRPRIMER_VERSION_MINOR ${XRPRIMER_VERSION_MINOR}) 54 | set(XRPRIMER_VERSION_PATCH ${XRPRIMER_VERSION_PATCH}) 55 | else() 56 | file(STRINGS "version.txt" XRPRIMER_VERSION_READ) 57 | 58 | foreach(ver ${XRPRIMER_VERSION_READ}) 59 | if(ver MATCHES "XRPRIMER_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") 60 | set(XRPRIMER_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" 61 | CACHE INTERNAL "" 62 | ) 63 | endif() 64 | endforeach() 65 | endif() 66 | 67 | set(META_VERSION_REVISION "${GIT_REV}") 68 | set(META_VERSION 69 | "${XRPRIMER_VERSION_MAJOR}.${XRPRIMER_VERSION_MINOR}.${XRPRIMER_VERSION_PATCH}" 70 | ) 71 | set(META_NAME_VERSION 72 | "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_REVISION})" 73 | ) 74 | -------------------------------------------------------------------------------- /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | ceres/2.1.0@xrlab/stable 3 | eigen3/3.4.0@xrlab/stable 4 | jsoncpp/1.9.5@xrlab/stable 5 | pybind11/2.6.2@xrlab/stable 6 | spdlog/1.9.1@xrlab/stable 7 | pnpsolver/1.0.0@xrlab/stable 8 | opencv/4.6.0@xrlab/stable 9 | 10 | [options] 11 | pnpsolver:shared=False 12 | 13 | [imports] 14 | ., * -> ../3rdparty/eigen3 @ root_package=eigen3 15 | ., * -> ../3rdparty/ceres @ root_package=ceres 16 | ., * -> ../3rdparty/jsoncpp @ root_package=jsoncpp 17 | ., * -> ../3rdparty/opencv @ root_package=opencv 18 | ., * -> ../3rdparty/pybind11 @ root_package=pybind11 19 | ., * -> ../3rdparty/spdlog @ root_package=spdlog 20 | ., * -> ../3rdparty/pnpsolver @ root_package=pnpsolver 21 | -------------------------------------------------------------------------------- /cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # library 3 | # 4 | add_subdirectory(xrprimer) 5 | 6 | # 7 | # python bindding 8 | # 9 | if(PYTHON_BINDING AND NOT IOS) 10 | add_subdirectory(pybind) 11 | endif() 12 | 13 | # 14 | # Test case 15 | # 16 | if(ENABLE_TEST) 17 | add_subdirectory(tests) 18 | endif() 19 | -------------------------------------------------------------------------------- /cpp/pybind/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Python3 COMPONENTS Interpreter Development) 2 | 3 | if(Python3_FOUND) 4 | set(PYTHON_EXECUTABLE 5 | ${Python3_EXECUTABLE} 6 | CACHE STRING 7 | "Deprecated path to the Python executable (for 3rdparty only)" 8 | FORCE 9 | ) 10 | endif() 11 | 12 | if(NOT Python3_EXECUTABLE) 13 | message(FATAL_ERROR "Python 3 not found in top level file") 14 | endif() 15 | 16 | set(PYTHON_VERSION "${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}") 17 | 18 | pybind11_add_module( 19 | xrprimer_cpp xrprimer_pybind.cpp data_structure/camera.cpp 20 | data_structure/pose.cpp calibration/calibrator_api.cpp ops/pnpsolver.cpp 21 | common/version.cpp 22 | ) 23 | 24 | target_link_libraries( 25 | xrprimer_cpp PUBLIC XRPrimer::xrprimer opencv_imgcodecs opencv_calib3d 26 | opencv_core 27 | ) 28 | target_include_directories( 29 | xrprimer_cpp PUBLIC $ 30 | $ 31 | ) 32 | set_target_properties( 33 | xrprimer_cpp PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib 34 | ) 35 | -------------------------------------------------------------------------------- /cpp/pybind/calibration/calibrator_api.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | void pybind_camera_calibrator(py::module &m) { 6 | m.def("CalibrateMultiPinholeCamera", &CalibrateMultiPinholeCamera); 7 | } 8 | 9 | void xrprimer_pybind_calibrator(py::module &m) { 10 | py::module m_submodule = m.def_submodule("calibrator"); 11 | pybind_camera_calibrator(m_submodule); 12 | } 13 | -------------------------------------------------------------------------------- /cpp/pybind/calibration/calibrator_api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pybind/xrprimer_pybind.h" 4 | 5 | void xrprimer_pybind_calibrator(py::module &m); 6 | -------------------------------------------------------------------------------- /cpp/pybind/common/version.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | void pybind_get_version(py::module &m) { 6 | m.def("get_version_major", get_version_major); 7 | m.def("get_version_minor", get_version_minor); 8 | m.def("get_version_patch", get_version_patch); 9 | m.def("get_version_string", get_version_string); 10 | } 11 | 12 | void xrprimer_pybind_version(py::module &m) { 13 | py::module m_submodule = m.def_submodule("common"); 14 | pybind_get_version(m_submodule); 15 | } 16 | -------------------------------------------------------------------------------- /cpp/pybind/common/version.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "pybind/xrprimer_pybind.h" 5 | 6 | void xrprimer_pybind_version(py::module &m); 7 | -------------------------------------------------------------------------------- /cpp/pybind/data_structure/camera.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "pybind/xrprimer_pybind.h" 5 | 6 | void xrprimer_pybind_camera(py::module &m); 7 | -------------------------------------------------------------------------------- /cpp/pybind/data_structure/pose.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind/data_structure/pose.h" 2 | #include 3 | 4 | void xrprimer_pybind_pose(py::module &m) { 5 | py::class_(m, "Pose") 6 | .def(py::init<>(), "Pose constructor") 7 | .def(py::init(), 8 | "quaternion", "position") 9 | .def(py::init(), 10 | "angle_axis", "position") 11 | .def(py::init(), 12 | "rotate_matrix", "position") 13 | .def("quaternion", &Pose::quaternion, "quaternion") 14 | .def("set_quaternion", 15 | py::overload_cast(&Pose::set_quternion)) 16 | .def("set_quaternion", 17 | py::overload_cast(&Pose::set_quternion), 18 | "AngleAxisd") 19 | .def("position", &Pose::position, "position") 20 | .def("set_position", &Pose::set_position) 21 | .def("rotation", &Pose::get_rotation, "rotation") 22 | .def("angle_axis", &Pose::get_angle_axis, "angle_axis") 23 | .def("SetIdentity", &Pose::SetIdentity) 24 | .def(py::self * Eigen::Vector3d()) 25 | .def(py::self * Eigen::Quaterniond()) 26 | .def("InverseMutable", &Pose::InverseMutable) 27 | .def("Inverse", &Pose::Inverse) 28 | .def("Scale", &Pose::Scale) 29 | .def("ScaleMutable", &Pose::ScaleMutable) 30 | .def("PoseMult", &Pose::PoseMult) 31 | .def("__eq__", 32 | py::overload_cast(&operator==)) 33 | .def("__ne__", 34 | py::overload_cast(&operator!=)); 35 | } 36 | -------------------------------------------------------------------------------- /cpp/pybind/data_structure/pose.h: -------------------------------------------------------------------------------- 1 | #include "pybind/xrprimer_pybind.h" 2 | 3 | void xrprimer_pybind_pose(py::module &m); 4 | -------------------------------------------------------------------------------- /cpp/pybind/ops/pnpsolver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | py::dict prior_guided_pnp( 5 | const Eigen::Ref> 6 | points2D, 7 | const Eigen::Ref> 8 | points3D, 9 | const Eigen::Ref> 10 | priors, 11 | const py::dict camera, const py::dict ransac_option) { 12 | 13 | assert(points2D.cols() == points3D.cols()); 14 | assert(points3D.cols() == priors.cols()); 15 | 16 | std::string camera_model_name = camera["model_name"].cast(); 17 | std::vector params = camera["params"].cast>(); 18 | 19 | std::vector point2D_vec(points2D.cols()); 20 | std::vector point3D_vec(points3D.cols()); 21 | std::vector priors_vec(priors.cols()); 22 | for (size_t i = 0; i != point2D_vec.size(); ++i) { 23 | point2D_vec[i][0] = static_cast(points2D(0, i)); 24 | point2D_vec[i][1] = static_cast(points2D(1, i)); 25 | point3D_vec[i][0] = static_cast(points3D(0, i)); 26 | point3D_vec[i][1] = static_cast(points3D(1, i)); 27 | point3D_vec[i][2] = static_cast(points3D(2, i)); 28 | priors_vec[i] = static_cast(priors(0, i)); 29 | } 30 | 31 | Eigen::Vector4d qvec; 32 | Eigen::Vector3d tvec; 33 | double error_thres = ransac_option["error_thres"].cast(); 34 | double inlier_ratio = ransac_option["inlier_ratio"].cast(); 35 | double confidence = ransac_option["confidence"].cast(); 36 | double max_iter = ransac_option["max_iter"].cast(); 37 | std::vector mask; 38 | 39 | colpnp::Robustor robustor = colpnp::RANSAC; 40 | bool lo = ransac_option["local_optimal"].cast(); 41 | if (lo) { 42 | robustor = colpnp::LORANSAC; 43 | } 44 | 45 | py::dict result; 46 | result["ninlier"] = 0; 47 | result["mask"] = mask; 48 | result["qvec"] = qvec; 49 | result["tvec"] = tvec; 50 | 51 | size_t num_inliers = 0; 52 | bool success = colpnp::sovle_pnp_ransac( 53 | point2D_vec, point3D_vec, camera_model_name, params, qvec, tvec, 54 | num_inliers, error_thres, inlier_ratio, confidence, max_iter, &mask, 55 | robustor, colpnp::WEIGHT_SAMPLE, &priors_vec); 56 | if (success) { 57 | result["ninlier"] = num_inliers; 58 | result["mask"] = mask; 59 | result["qvec"] = qvec; 60 | result["tvec"] = tvec; 61 | } 62 | 63 | return result; 64 | } 65 | 66 | void pybind_pnpsolver(py::module &m) { 67 | m.def("prior_guided_pnp", &prior_guided_pnp, py::return_value_policy::copy); 68 | } 69 | 70 | void xrprimer_pybind_pnpsolver(py::module &m) { 71 | py::module m_submodule = m.def_submodule("ops"); 72 | pybind_pnpsolver(m_submodule); 73 | } 74 | -------------------------------------------------------------------------------- /cpp/pybind/ops/pnpsolver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pybind/xrprimer_pybind.h" 4 | 5 | void xrprimer_pybind_pnpsolver(py::module &m); 6 | -------------------------------------------------------------------------------- /cpp/pybind/xrprimer_pybind.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace py = pybind11; 17 | using namespace py::literals; 18 | 19 | PYBIND11_MAKE_OPAQUE(std::vector); 20 | PYBIND11_MAKE_OPAQUE(std::vector); 21 | PYBIND11_MAKE_OPAQUE(std::vector); 22 | PYBIND11_MAKE_OPAQUE(std::vector); 23 | PYBIND11_MAKE_OPAQUE(std::vector); 24 | PYBIND11_MAKE_OPAQUE(std::vector); 25 | -------------------------------------------------------------------------------- /cpp/samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(sample) 4 | 5 | if(NOT XRPrimer_DIR) 6 | # set XRPrimer_DIR pointer to XRPrimerConfig.cmake directory 7 | list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../install/") 8 | message( 9 | STATUS 10 | "**[XRPrimer]** XRPrimer_DIR default path: ${CMAKE_CURRENT_SOURCE_DIR}/../../install/" 11 | ) 12 | endif() 13 | 14 | find_package(XRPrimer REQUIRED) 15 | 16 | add_executable(sample sample.cpp) 17 | target_link_libraries(sample XRPrimer::xrprimer) 18 | -------------------------------------------------------------------------------- /cpp/samples/sample.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main() { 11 | std::cout << "Hello XRPrimer" << std::endl; 12 | FisheyeCameraParameter camera; 13 | Eigen::Matrix3f mat3x3(Eigen::Matrix3f::Identity()); // use Eigen 14 | cv::Mat mat(10, 20, CV_8UC3); // use opencv 15 | auto value = Json::Value(10); // use json 16 | auto image = Image(10, 20, 30, BGR24); // use xrprimer 17 | } 18 | -------------------------------------------------------------------------------- /cpp/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${CMAKE_SOURCE_DIR}/cmake/unit_test_dep.cmake) 2 | 3 | xr_add_test_catch2( 4 | test_calibrator SRCS test_calibrator.cpp filesystem_utils.cpp 5 | ) 6 | target_link_libraries(test_calibrator PRIVATE XRPrimer::xrprimer) 7 | 8 | xr_add_test_catch2(test_image SRCS test_image.cpp) 9 | target_link_libraries(test_image PRIVATE XRPrimer::xrprimer) 10 | 11 | xr_add_test_catch2(test_version SRCS test_version.cpp) 12 | target_link_libraries(test_version PRIVATE XRPrimer::xrprimer) 13 | 14 | xr_add_test_catch2(test_camera SRCS test_camera.cpp) 15 | target_link_libraries(test_camera PRIVATE XRPrimer::xrprimer) 16 | 17 | xr_add_test_catch2(test_pose SRCS test_pose.cpp) 18 | target_link_libraries(test_pose PRIVATE XRPrimer::xrprimer) 19 | -------------------------------------------------------------------------------- /cpp/tests/filesystem_utils.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 The Khronos Group Inc. 2 | // Copyright (c) 2017 Valve Corporation 3 | // Copyright (c) 2017 LunarG, Inc. 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 OR MIT 6 | // 7 | // Initial Author: Mark Young 8 | // 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | // Determine if the path indicates a regular file (not a directory or symbolic 16 | // link) 17 | bool FileSysUtilsIsRegularFile(const std::string &path); 18 | 19 | // Determine if the path indicates a directory 20 | bool FileSysUtilsIsDirectory(const std::string &path); 21 | 22 | // Determine if the provided path exists on the filesystem 23 | bool FileSysUtilsPathExists(const std::string &path); 24 | 25 | // Get the current directory 26 | bool FileSysUtilsGetCurrentPath(std::string &path); 27 | 28 | // Get the parent path of a file 29 | bool FileSysUtilsGetParentPath(const std::string &file_path, 30 | std::string &parent_path); 31 | 32 | // Determine if the provided path is an absolute path 33 | bool FileSysUtilsIsAbsolutePath(const std::string &path); 34 | 35 | // Get the absolute path for a provided file 36 | bool FileSysUtilsGetAbsolutePath(const std::string &path, 37 | std::string &absolute); 38 | 39 | // Get the absolute path for a provided file 40 | bool FileSysUtilsGetCanonicalPath(const std::string &path, 41 | std::string &canonical); 42 | 43 | // Combine a parent and child directory 44 | bool FileSysUtilsCombinePaths(const std::string &parent, 45 | const std::string &child, std::string &combined); 46 | 47 | // Parse out individual paths in a path list 48 | bool FileSysUtilsParsePathList(const std::string &path_list, 49 | std::vector &paths); 50 | 51 | // Record all the filenames for files found in the provided path. 52 | bool FileSysUtilsFindFilesInPath(const std::string &path, 53 | std::vector &files); 54 | -------------------------------------------------------------------------------- /cpp/tests/test_camera.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #define CATCH_CONFIG_MAIN 7 | #include "catch.hpp" 8 | 9 | TEST_CASE("FisheyeCamera", "API") { 10 | auto fishcamera = FisheyeCameraParameter(); 11 | std::cout << fishcamera.ClassName() << std::endl; 12 | fishcamera.SaveFile("test_fisheye_cam.json"); 13 | fishcamera.LoadFile("test_fisheye_cam.json"); 14 | } 15 | 16 | TEST_CASE("OmniCamera", "API") { 17 | auto omnicamera = OmniCameraParameter(); 18 | std::cout << omnicamera.ClassName() << std::endl; 19 | omnicamera.SaveFile("test_omni_cama.json"); 20 | omnicamera.LoadFile("test_omni_cama.json"); 21 | } 22 | 23 | TEST_CASE("PinholeCamera", "API") { 24 | auto pinholecamera = PinholeCameraParameter(); 25 | std::cout << pinholecamera.ClassName() << std::endl; 26 | pinholecamera.SaveFile("test_pinhole_cam.json"); 27 | pinholecamera.LoadFile("test_pinhole_cam.json"); 28 | } 29 | -------------------------------------------------------------------------------- /cpp/tests/test_multi_camera_calibrator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | 5 | import xrprimer_cpp as xr 6 | 7 | calib_config = """ 8 | { 9 | "chessboard_width": 6, 10 | "chessboard_height": 7, 11 | "chessboard_square_size": 100 12 | } 13 | """ 14 | 15 | image_folder = 'tests/data/calib_pinhole_camera/input/images/' 16 | camera_folder = 'tests/data/calib_pinhole_camera/input/config/' 17 | 18 | if __name__ == '__main__': 19 | 20 | if len(sys.argv) >= 3: 21 | image_folder = sys.argv[1] 22 | camera_folder = sys.argv[2] 23 | print('image folder: ', image_folder) 24 | print('camera folder: ', camera_folder) 25 | 26 | pinhole_cam_list = [] 27 | # pinhole_list = [] 28 | for root, dirs, files in os.walk(camera_folder): 29 | camera_cnt = len(list(files)) 30 | pinhole_list = [''] * camera_cnt 31 | pinhole_cam_list = [ 32 | xr.camera.PinholeCameraParameter() for i in range(camera_cnt) 33 | ] 34 | 35 | for file in files: 36 | camera_idx = int(os.path.splitext(file.split('_')[1])[0]) 37 | # pinhole_list[camera_idx] = os.path.join(root, file) 38 | cam_path = os.path.join(root, file) 39 | pinhole_cam_list[camera_idx].LoadFile(cam_path) 40 | # pinhole_cam_list[camera_idx].SaveFile(str(camera_idx) + ".json") 41 | 42 | max_img_idx = 0 43 | frames = [] 44 | for root, dirs, files in os.walk(image_folder): 45 | for file in files: 46 | img = os.path.join(root, file) 47 | matched = re.match( 48 | r'img(?P\d+)_cam(?P\d+).jpg', file) 49 | if max_img_idx < int(matched.group('img_index')): 50 | max_img_idx = int(matched.group('img_index')) 51 | 52 | frames = [[]] * (max_img_idx + 1) 53 | for x in range(max_img_idx + 1): 54 | frames[x] = [''] * 10 55 | 56 | for file in files: 57 | img = os.path.join(root, file) 58 | matched = re.match( 59 | r'img(?P\d+)_cam(?P\d+).jpg', file) 60 | frames[int(matched.group('img_index'))][int( 61 | matched.group('camera_index'))] = img 62 | 63 | vc = xr.VectorPinholeCameraParameter(pinhole_cam_list) 64 | xr.calibrator.CalibrateMultiPinholeCamera(calib_config, frames, vc) 65 | print(vc[1].extrinsic_r) 66 | 67 | for idx, pin in enumerate(vc): 68 | out_path = f'outputs/{idx}.json' 69 | os.makedirs(os.path.dirname(out_path), exist_ok=True) 70 | print(f'save file: {out_path}') 71 | pin.SaveFile(out_path) 72 | -------------------------------------------------------------------------------- /cpp/tests/test_pnpsolver.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from xrprimer_cpp import VectorDouble 4 | from xrprimer_cpp.ops import prior_guided_pnp 5 | 6 | 7 | def pose_estimation(): 8 | m, n = 5, 100 9 | correspondences = np.random.random([m, n]).astype('float32') 10 | point2ds = correspondences[0:2, :].copy() 11 | point3ds = correspondences[2:5, :].copy() 12 | priors = np.ones([1, n], dtype='float32') 13 | params = VectorDouble([1465., 1465., 955., 689.]) 14 | camera_config = {'model_name': 'PINHOLE', 'params': params} 15 | ransac_config = { 16 | 'error_thres': 12, 17 | 'inlier_ratio': 0.01, 18 | 'confidence': 0.9999, 19 | 'max_iter': 100000, 20 | 'local_optimal': True 21 | } 22 | return prior_guided_pnp(point2ds, point3ds, priors, camera_config, 23 | ransac_config) 24 | 25 | 26 | if __name__ == '__main__': 27 | print(pose_estimation()) 28 | -------------------------------------------------------------------------------- /cpp/tests/test_pose.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this 6 | // in one cpp file 7 | #include "catch.hpp" 8 | 9 | TEST_CASE("Pose api test", "xrprimer Pose") { 10 | 11 | Pose pose; 12 | 13 | Eigen::Vector3d vec3d; 14 | 15 | Eigen::Quaterniond quaternion; 16 | Pose pose1(quaternion.setIdentity(), vec3d.setZero()); 17 | 18 | Eigen::AngleAxisd angleAxis(30, Eigen::Vector3d::UnitY()); 19 | Pose pose2(angleAxis, vec3d.setZero()); 20 | 21 | Eigen::Matrix3d mat3d; 22 | Pose pose3(mat3d.setIdentity(), vec3d.setZero()); 23 | 24 | const auto &quat = pose3.quaternion(); 25 | const auto &posi = pose3.position(); 26 | 27 | auto rot = pose3.get_rotation(); 28 | auto angle = pose3.get_angle_axis(); 29 | 30 | pose3.set_quternion(Eigen::Quaterniond(1, 0, 0, 0)); 31 | pose3.set_quternion(1, 0, 0, 0); 32 | pose3.set_quternion(Eigen::Matrix3d().setIdentity()); 33 | pose3.set_quternion(Eigen::AngleAxisd(10.0, Eigen::Vector3d::UnitX())); 34 | pose3.set_position(Eigen::Vector3d(1.0, 3.0, 4.0)); 35 | pose3.SetIdentity(); 36 | 37 | pose3.Inverse(); 38 | pose3.InverseMutable(); 39 | auto p = pose3.Scale(1.2); 40 | pose3.ScaleMutable(1.4); 41 | auto vec = pose3.Center(); 42 | } 43 | -------------------------------------------------------------------------------- /cpp/tests/test_version.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define CATCH_CONFIG_MAIN 5 | #include "catch.hpp" 6 | 7 | TEST_CASE("Meta", "Get Version") { 8 | 9 | int major = get_version_major(); 10 | int minor = get_version_minor(); 11 | int patch = get_version_patch(); 12 | 13 | std::string version = get_version_string(); 14 | 15 | std::cout << "Major: " << major << std::endl; 16 | std::cout << "Minor: " << minor << std::endl; 17 | std::cout << "Patch: " << patch << std::endl; 18 | std::cout << "Version: " << version << std::endl; 19 | } 20 | -------------------------------------------------------------------------------- /cpp/xrprimer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(Headers 2 | common/version.h 3 | calibration/calibrator.h 4 | calibration/calibrator_api.h 5 | data_structure/math_util.h 6 | data_structure/camera/camera.h 7 | data_structure/camera/pinhole_camera.h 8 | data_structure/camera/omni_camera.h 9 | data_structure/camera/fisheye_camera.h 10 | data_structure/image.h 11 | data_structure/pose.h 12 | utils/logging.h 13 | ) 14 | source_group("Headers" FILES ${Headers}) 15 | 16 | set(Sources 17 | common/version.cpp 18 | calibration/calibrator.cpp 19 | calibration/calibrator_api.cpp 20 | data_structure/camera/camera.cpp 21 | data_structure/camera/pinhole_camera.cpp 22 | data_structure/camera/omni_camera.cpp 23 | data_structure/camera/fisheye_camera.cpp 24 | data_structure/image.cpp 25 | data_structure/pose.cpp 26 | utils/logging.cpp 27 | ) 28 | 29 | source_group("Sources" FILES ${Sources}) 30 | 31 | set(target xrprimer) 32 | 33 | add_library(${target} ${Headers} ${Sources}) 34 | add_library(XRPrimer::${target} ALIAS ${target}) 35 | 36 | target_compile_options(${target} PRIVATE -fPIC) 37 | 38 | include(GenerateExportHeader) 39 | generate_export_header( 40 | ${target} EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/xrprimer_export.h 41 | ) 42 | 43 | target_include_directories( 44 | ${target} 45 | PUBLIC $ 46 | $ 47 | $ 48 | ) 49 | 50 | target_link_libraries( 51 | ${target} 52 | PUBLIC $<$>:PnpSolver::pnpsolver> OpenCV::OpenCV 53 | Eigen3::Eigen Ceres::ceres jsoncpp_static spdlog::spdlog_header_only 54 | ) 55 | 56 | install(TARGETS ${target} DESTINATION lib EXPORT ${target}-targets) 57 | install(EXPORT ${target}-targets DESTINATION lib/cmake/xrprimer 58 | NAMESPACE XRPrimer:: 59 | ) 60 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION include FILES_MATCHING 61 | PATTERN "*.h" 62 | ) 63 | 64 | include(CMakePackageConfigHelpers) 65 | configure_package_config_file( 66 | ${CMAKE_SOURCE_DIR}/cmake/XRPrimerConfig.cmake.in 67 | "${CMAKE_CURRENT_BINARY_DIR}/XRPrimerConfig.cmake" 68 | INSTALL_DESTINATION lib/cmake/xrprimer NO_CHECK_REQUIRED_COMPONENTS_MACRO 69 | ) 70 | 71 | file(READ ${CMAKE_SOURCE_DIR}/cmake/dependencies.cmake DEPS_FILE_CONTENTS) 72 | string(REPLACE "find_package" "find_dependency" DEPS_FILE_CONTENTS 73 | ${DEPS_FILE_CONTENTS} 74 | ) 75 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dependencies.cmake ${DEPS_FILE_CONTENTS}) 76 | 77 | write_basic_package_version_file( 78 | "${CMAKE_CURRENT_BINARY_DIR}/XRPrimerConfigVersion.cmake" 79 | VERSION 80 | "${XRPRIMER_VERSION_MAJOR}.${XRPRIMER_VERSION_MINOR}.${XRPRIMER_VERSION_PATCH}" 81 | COMPATIBILITY AnyNewerVersion 82 | ) 83 | 84 | install( 85 | FILES "${CMAKE_CURRENT_BINARY_DIR}/XRPrimerConfig.cmake" 86 | "${CMAKE_CURRENT_BINARY_DIR}/XRPrimerConfigVersion.cmake" 87 | "${CMAKE_CURRENT_BINARY_DIR}/dependencies.cmake" 88 | DESTINATION lib/cmake/xrprimer 89 | ) 90 | 91 | configure_file(common/config.h.in common/config.h) 92 | 93 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/xrprimer_export.h 94 | DESTINATION include/xrprimer 95 | ) 96 | -------------------------------------------------------------------------------- /cpp/xrprimer/calibration/calibrator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "data_structure/camera/pinhole_camera.h" 10 | 11 | struct MultiCalibrator { 12 | MultiCalibrator(std::vector ¶ms) 13 | : pinhole_params(params){}; 14 | cv::Size pattern_size; ///< chess pattern size 15 | cv::Size2f square_size; ///< chess size 16 | std::vector &pinhole_params; 17 | std::vector> found_corners_list; 18 | // frames/camera/points 19 | std::vector>> point2d_lists; 20 | 21 | void Clear() { point2d_lists.clear(); } 22 | bool Push(const std::vector &image_paths); 23 | bool Init(); 24 | void OptimizeExtrinsics(); 25 | void NormalizeCamExtrinsics(); 26 | }; 27 | -------------------------------------------------------------------------------- /cpp/xrprimer/calibration/calibrator_api.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | XRPRIMER_EXPORT 17 | void CalibrateMultiPinholeCamera( 18 | const std::string &calib_config_json, 19 | const std::vector> 20 | &img_groups, // frames/cameras/path 21 | std::vector &pinhole_params) { 22 | 23 | MultiCalibrator calibrator(pinhole_params); 24 | { 25 | // TODO: maybe use constructor 26 | Json::Value calib_config; // will contains the root value after parsing. 27 | Json::Reader reader; 28 | reader.parse(calib_config_json, calib_config, false); 29 | int chessboard_width = calib_config["chessboard_width"].asInt(); 30 | int chessboard_height = calib_config["chessboard_height"].asInt(); 31 | // unit: mm 32 | int chessboard_square_size = 33 | calib_config["chessboard_square_size"].asInt(); 34 | 35 | calibrator.pattern_size = cv::Size(chessboard_width, chessboard_height); 36 | calibrator.square_size = cv::Size2f(1e-3f * chessboard_square_size, 37 | 1e-3f * chessboard_square_size); 38 | } 39 | 40 | for (int gi = 0; gi < (int)img_groups.size(); ++gi) { 41 | std::cout << "Push frame idx: " << gi << std::endl; 42 | if (!calibrator.Push(img_groups[gi])) { 43 | std::cerr << "Invalid frame idx:" << gi << ", less than 2 camera!" 44 | << std::endl; 45 | } 46 | } 47 | if (!calibrator.Init()) { 48 | std::cout 49 | << "ExternalCalibrator: Can't Initialize External Param for All " 50 | "Cameras!" 51 | << std::endl; 52 | return; 53 | } 54 | calibrator.OptimizeExtrinsics(); 55 | calibrator.NormalizeCamExtrinsics(); 56 | } 57 | -------------------------------------------------------------------------------- /cpp/xrprimer/calibration/calibrator_api.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | /** 6 | * @brief Interface to calibrate multiple pinhole camera 7 | * @param calib_config_json Config in json format for calibration 8 | * @param img_groups A vector contains multiple frames, where each frame is a 9 | * vector containing images captured from multiple cameras 10 | * @param pinhole_params A vector of PinholeCameraParamter 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | XRPRIMER_EXPORT 18 | void CalibrateMultiPinholeCamera( 19 | const std::string &calib_config_json, 20 | const std::vector> 21 | &img_groups, // frames/cameras/path 22 | std::vector &pinhole_params); 23 | -------------------------------------------------------------------------------- /cpp/xrprimer/common/config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define XRPRIMER_VERSION_MAJOR @XRPRIMER_VERSION_MAJOR@ 3 | #define XRPRIMER_VERSION_MINOR @XRPRIMER_VERSION_MINOR@ 4 | #define XRPRIMER_VERSION_PATCH @XRPRIMER_VERSION_PATCH@ 5 | -------------------------------------------------------------------------------- /cpp/xrprimer/common/version.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #include 4 | #include 5 | #define FMT_HEADER_ONLY 6 | #include 7 | 8 | std::string get_version_string() { 9 | return fmt::format("{}.{}.{}", get_version_major(), get_version_minor(), 10 | get_version_patch()); 11 | } 12 | 13 | int get_version_major() { return XRPRIMER_VERSION_MAJOR; } 14 | 15 | int get_version_minor() { return XRPRIMER_VERSION_MINOR; } 16 | 17 | int get_version_patch() { return XRPRIMER_VERSION_PATCH; } 18 | -------------------------------------------------------------------------------- /cpp/xrprimer/common/version.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | /** 6 | * @file version.h 7 | * @brief Functions to get xrprimer version 8 | */ 9 | 10 | #include 11 | 12 | /** @brief Get major version 13 | * @return int 14 | */ 15 | int get_version_major(); 16 | 17 | /** @brief Get minor version 18 | * @return int 19 | */ 20 | int get_version_minor(); 21 | 22 | /** @brief Get patch version 23 | * @return int 24 | */ 25 | int get_version_patch(); 26 | 27 | /** @brief Get version as a string 28 | * @return string version string with the format 'major.minor.patch' 29 | */ 30 | std::string get_version_string(); 31 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | #include "data_structure/math_util.h" 3 | #include 4 | 5 | #include 6 | 7 | /* 8 | intrinsics: 9 | perspective: 10 | [fx, 0, px, 0], 11 | [0, fy, py, 0], 12 | [0, 0, 0, 1], 13 | [0, 0, 1, 0] 14 | 15 | orthographics: 16 | [fx, 0, 0, px], 17 | [0, fy, 0, py], 18 | [0, 0, 1, 0], 19 | [0, 0, 0, 1] 20 | 21 | */ 22 | 23 | void BaseCameraParameter::set_intrinsic(int width, int height, double fx, 24 | double fy, double cx, double cy, 25 | bool perspective) { 26 | width_ = width; 27 | height_ = height; 28 | Eigen::Matrix3f insic; 29 | insic.setIdentity(); 30 | insic(0, 0) = fx; 31 | insic(1, 1) = fy; 32 | insic(0, 2) = cx; 33 | insic(1, 2) = cy; 34 | set_intrinsic(insic, perspective); 35 | } 36 | 37 | void BaseCameraParameter::set_intrinsic(const Eigen::Matrix3f &mat, 38 | bool perspective) { 39 | intrinsic_.setZero(); 40 | intrinsic_(0, 0) = mat(0, 0); 41 | intrinsic_(1, 1) = mat(1, 1); 42 | if (perspective) { 43 | intrinsic_(0, 2) = mat(0, 2); 44 | intrinsic_(1, 2) = mat(1, 2); 45 | intrinsic_(2, 3) = 1; 46 | intrinsic_(3, 2) = 1; 47 | } else { 48 | intrinsic_(0, 3) = mat(0, 2); 49 | intrinsic_(1, 3) = mat(1, 2); 50 | intrinsic_(2, 2) = 1; 51 | intrinsic_(3, 3) = 1; 52 | } 53 | } 54 | 55 | Eigen::Matrix3f BaseCameraParameter::intrinsic33() const { 56 | Eigen::Matrix3f mat; 57 | mat.setIdentity(); 58 | 59 | mat(0, 0) = intrinsic_(0, 0); 60 | mat(1, 1) = intrinsic_(1, 1); 61 | 62 | if (IsPerspective()) { 63 | mat(0, 2) = intrinsic_(0, 2); 64 | mat(1, 2) = intrinsic_(1, 2); 65 | } else { 66 | mat(0, 2) = intrinsic_(0, 3); 67 | mat(1, 2) = intrinsic_(1, 3); 68 | } 69 | return mat; 70 | } 71 | 72 | bool BaseCameraParameter::IsPerspective() const { 73 | return intrinsic_(3, 3) == 0; 74 | } 75 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/camera.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | /// \class BaseCameraParameter 14 | /// 15 | /// \brief Contains the base camera parameter. 16 | class XRPRIMER_EXPORT BaseCameraParameter { 17 | public: 18 | virtual ~BaseCameraParameter() = default; 19 | 20 | BaseCameraParameter() 21 | : BaseCameraParameter(Eigen::Matrix4f::Zero(), 22 | Eigen::Matrix3f::Identity(), 23 | Eigen::Vector3f::Zero()) {} 24 | 25 | BaseCameraParameter(const std::string &name, int width, int height, 26 | bool world2cam, const Eigen::Matrix4f &intrinsic, 27 | const Eigen::Matrix3f &extrinsic_r, 28 | const Eigen::Vector3f &extrinsic_t, 29 | const std::string &convention) 30 | : name_(name), width_(width), height_(height), world2cam_(world2cam), 31 | intrinsic_(intrinsic), extrinsic_r_(extrinsic_r), 32 | extrinsic_t_(extrinsic_t), convention_(convention) {} 33 | 34 | BaseCameraParameter(const Eigen::Matrix4f &intrinsic, 35 | const Eigen::Matrix3f &extrinsic_r, 36 | const Eigen::Vector3f &extrinsic_t) 37 | : BaseCameraParameter("default", 1920, 1080, true, intrinsic, 38 | extrinsic_r, extrinsic_t, "opencv") {} 39 | 40 | void set_intrinsic(int width, int height, double fx, double fy, double cx, 41 | double cy, bool perspective = true); 42 | void set_intrinsic(const Eigen::Matrix3f &mat, bool perspective = true); 43 | Eigen::Matrix3f intrinsic33() const; 44 | 45 | // 46 | // interface 47 | // 48 | virtual std::string ClassName() const = 0; 49 | virtual bool SaveFile(const std::string &filename) const = 0; 50 | virtual bool LoadFile(const std::string &filename) = 0; 51 | 52 | // 53 | // properties 54 | // 55 | std::string name_; 56 | Eigen::Matrix4f intrinsic_; 57 | Eigen::Matrix3f extrinsic_r_; 58 | Eigen::Vector3f extrinsic_t_; 59 | int width_; 60 | int height_; 61 | bool world2cam_; 62 | std::string convention_; // opencv or other 63 | 64 | private: 65 | bool IsPerspective() const; 66 | }; 67 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/fisheye_camera.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "json_helper_internal.h" 3 | #include 4 | #include 5 | #include 6 | 7 | FisheyeCameraParameter::FisheyeCameraParameter() 8 | : BaseCameraParameter(), k1_(0), k2_(0), k3_(0), k4_(0), k5_(0), k6_(0), 9 | p1_(0), p2_(0) {} 10 | 11 | std::string FisheyeCameraParameter::ClassName() const { 12 | return "FisheyeCameraParameter"; 13 | } 14 | 15 | bool FisheyeCameraParameter::SaveFile(const std::string &filename) const { 16 | Json::Value obj; 17 | SaveBaseCameraParameter(obj, *this); 18 | obj["k1"] = k1_; 19 | obj["k2"] = k2_; 20 | obj["k3"] = k3_; 21 | obj["k4"] = k4_; 22 | obj["k5"] = k5_; 23 | obj["k6"] = k6_; 24 | obj["p1"] = p1_; 25 | obj["p2"] = p2_; 26 | return JsonToFile(obj, filename); 27 | } 28 | 29 | bool FisheyeCameraParameter::LoadFile(const std::string &filename) { 30 | Json::Value obj; 31 | bool ret = false; 32 | 33 | if (JsonFromFile(obj, filename)) { 34 | if (LoadBaseCameraParameter(obj, *this)) { 35 | ret = check_and_load_float(&k1_, obj, "k1"); 36 | ret &= check_and_load_float(&k2_, obj, "k2"); 37 | ret &= check_and_load_float(&k3_, obj, "k3"); 38 | ret &= check_and_load_float(&k4_, obj, "k4"); 39 | ret &= check_and_load_float(&k5_, obj, "k5"); 40 | ret &= check_and_load_float(&k6_, obj, "k6"); 41 | ret &= check_and_load_float(&p1_, obj, "p1"); 42 | ret &= check_and_load_float(&p2_, obj, "p2"); 43 | return ret; 44 | } 45 | } 46 | return false; 47 | } 48 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/fisheye_camera.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | /// \class FisheyeCameraParameter 8 | /// 9 | /// \brief Contains the fisheye camera parameter. 10 | class XRPRIMER_EXPORT FisheyeCameraParameter : public BaseCameraParameter { 11 | public: 12 | FisheyeCameraParameter(); 13 | ~FisheyeCameraParameter() = default; 14 | 15 | float k1_, k2_, k3_, k4_, k5_, k6_, p1_, p2_; 16 | 17 | std::string ClassName() const override; 18 | bool SaveFile(const std::string &filename) const override; 19 | bool LoadFile(const std::string &filename) override; 20 | }; 21 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/omni_camera.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "json_helper_internal.h" 3 | #include 4 | #include 5 | 6 | OmniCameraParameter::OmniCameraParameter() 7 | : BaseCameraParameter(), k1_(0), k2_(0), k3_(0), k4_(0), k5_(0), k6_(0), 8 | p1_(0), p2_(0), xi_(0) { 9 | D_.setZero(); 10 | } 11 | 12 | std::string OmniCameraParameter::ClassName() const { 13 | return "OmniCameraParameter"; 14 | } 15 | 16 | bool OmniCameraParameter::SaveFile(const std::string &filename) const { 17 | Json::Value obj; 18 | 19 | SaveBaseCameraParameter(obj, *this); 20 | obj["k1"] = k1_; 21 | obj["k2"] = k2_; 22 | obj["k3"] = k3_; 23 | obj["k4"] = k4_; 24 | obj["k5"] = k5_; 25 | obj["k6"] = k6_; 26 | obj["p1"] = p1_; 27 | obj["p2"] = p2_; 28 | obj["xi"] = xi_; 29 | SaveMatrixToJson(obj, "D", D_); 30 | 31 | return JsonToFile(obj, filename); 32 | } 33 | 34 | bool OmniCameraParameter::LoadFile(const std::string &filename) { 35 | Json::Value obj; 36 | bool ret = false; 37 | 38 | if (JsonFromFile(obj, filename)) { 39 | ret = LoadBaseCameraParameter(obj, *this); 40 | ret &= check_and_load_float(&k1_, obj, "k1"); 41 | ret &= check_and_load_float(&k2_, obj, "k2"); 42 | ret &= check_and_load_float(&k3_, obj, "k3"); 43 | ret &= check_and_load_float(&k4_, obj, "k4"); 44 | ret &= check_and_load_float(&k5_, obj, "k5"); 45 | ret &= check_and_load_float(&k6_, obj, "k6"); 46 | ret &= check_and_load_float(&p1_, obj, "p1"); 47 | ret &= check_and_load_float(&p2_, obj, "p2"); 48 | ret &= check_and_load_float(&xi_, obj, "xi"); 49 | ret &= LoadMatrixFromJson(obj, "D", D_); 50 | return ret; 51 | } 52 | return false; 53 | } 54 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/omni_camera.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | /// \class OmniCameraParameter 8 | /// 9 | /// \brief Contains the omni camera parameter. 10 | class XRPRIMER_EXPORT OmniCameraParameter : public BaseCameraParameter { 11 | public: 12 | OmniCameraParameter(); 13 | ~OmniCameraParameter() = default; 14 | 15 | float k1_, k2_, k3_, k4_, k5_, k6_, p1_, p2_, xi_; 16 | Eigen::Vector4f D_; 17 | 18 | std::string ClassName() const override; 19 | bool SaveFile(const std::string &filename) const override; 20 | bool LoadFile(const std::string &filename) override; 21 | }; 22 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/pinhole_camera.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "json_helper_internal.h" 3 | #include 4 | #include 5 | 6 | std::string PinholeCameraParameter::ClassName() const { 7 | return "PinholeCameraParameter"; 8 | } 9 | 10 | bool PinholeCameraParameter::SaveFile(const std::string &filename) const { 11 | Json::Value obj; 12 | SaveBaseCameraParameter(obj, *this); 13 | return JsonToFile(obj, filename); 14 | } 15 | 16 | bool PinholeCameraParameter::LoadFile(const std::string &filename) { 17 | Json::Value obj; 18 | 19 | if (JsonFromFile(obj, filename)) { 20 | return LoadBaseCameraParameter(obj, *this); 21 | } 22 | return false; 23 | } 24 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/camera/pinhole_camera.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | /// \class PinholeCameraParameter 8 | /// 9 | /// \brief Contains the pinhole camera parameter. 10 | class XRPRIMER_EXPORT PinholeCameraParameter : public BaseCameraParameter { 11 | public: 12 | PinholeCameraParameter() = default; 13 | ~PinholeCameraParameter() = default; 14 | 15 | std::string ClassName() const override; 16 | bool SaveFile(const std::string &filename) const override; 17 | bool LoadFile(const std::string &filename) override; 18 | }; 19 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/pose.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #include 4 | 5 | Pose::Pose() { 6 | quaternion_.setIdentity(); 7 | position_.setZero(); 8 | } 9 | 10 | Pose::Pose(const Eigen::Quaterniond &quaternion, 11 | const Eigen::Vector3d &position) { 12 | quaternion_ = quaternion; 13 | position_ = position; 14 | } 15 | 16 | Pose::Pose(const Eigen::AngleAxisd &angle_axis, 17 | const Eigen::Vector3d &position) { 18 | quaternion_ = angle_axis; 19 | position_ = position; 20 | } 21 | 22 | Pose::Pose(const Eigen::Matrix3d &rotate_matrix, 23 | const Eigen::Vector3d &position) { 24 | quaternion_ = rotate_matrix; 25 | position_ = position; 26 | } 27 | 28 | // properties 29 | const Eigen::Quaterniond &Pose::quaternion() const { return quaternion_; } 30 | 31 | const Eigen::Vector3d &Pose::position() const { return position_; } 32 | 33 | void Pose::set_quternion(const Eigen::Quaterniond &quaternion) { 34 | quaternion_ = quaternion; 35 | } 36 | 37 | void Pose::set_quternion(double w, double x, double y, double z) { 38 | quaternion_.w() = w; 39 | quaternion_.x() = x; 40 | quaternion_.y() = y; 41 | quaternion_.z() = z; 42 | } 43 | void Pose::set_quternion(const Eigen::Matrix3d &rotation) { 44 | quaternion_ = rotation; 45 | } 46 | 47 | void Pose::set_quternion(const Eigen::AngleAxisd &angle_axis) { 48 | quaternion_ = angle_axis; 49 | } 50 | 51 | void Pose::set_position(const Eigen::Vector3d &position) { 52 | position_ = position; 53 | } 54 | 55 | Eigen::Matrix3d Pose::get_rotation() const { 56 | return quaternion_.normalized().toRotationMatrix(); 57 | } 58 | 59 | Eigen::AngleAxisd Pose::get_angle_axis() const { 60 | Eigen::AngleAxisd angle_axis(quaternion_.normalized()); 61 | return angle_axis; 62 | } 63 | 64 | void Pose::SetIdentity() { 65 | quaternion_.setIdentity(); 66 | position_.setZero(); 67 | } 68 | 69 | void Pose::InverseMutable() { 70 | this->quaternion_ = this->quaternion_.inverse(); 71 | this->position_ = -(this->quaternion_ * this->position_); 72 | } 73 | 74 | Pose Pose::Inverse() const { 75 | Pose pose = *this; 76 | pose.InverseMutable(); 77 | return pose; 78 | } 79 | 80 | void Pose::ScaleMutable(double s) { this->position_ *= s; } 81 | 82 | Pose Pose::Scale(double s) const { 83 | Pose pose = *this; 84 | pose.ScaleMutable(s); 85 | return pose; 86 | } 87 | 88 | Eigen::Vector3d Pose::Center() const { 89 | return -(this->quaternion_.inverse() * this->position_); 90 | } 91 | 92 | void Pose::PoseMult(const Pose &lhs, const Pose &rhs) { 93 | quaternion_ = lhs.quaternion() * rhs.quaternion(); 94 | position_ = lhs(rhs.position_); 95 | } 96 | 97 | /// @brief the Pose of the vector 98 | Eigen::Vector3d Pose::operator*(const Eigen::Vector3d &vec) const { 99 | return (*this)(vec); 100 | } 101 | 102 | /// @brief the Pose of the Quaternion 103 | Eigen::Quaterniond Pose::operator*(const Eigen::Quaterniond &quaternion) const { 104 | return quaternion_ * quaternion; 105 | } 106 | 107 | /// @brief the Pose of the vector 108 | Eigen::Vector3d Pose::operator()(const Eigen::Vector3d &vec) const { 109 | return quaternion_ * vec + position_; 110 | } 111 | 112 | bool operator!=(const Pose &lhs, const Pose &rhs) { return !(lhs == rhs); } 113 | 114 | bool operator==(const Pose &lhs, const Pose &rhs) { 115 | return lhs.quaternion().coeffs() == rhs.quaternion().coeffs() && 116 | lhs.position() == rhs.position(); 117 | } 118 | -------------------------------------------------------------------------------- /cpp/xrprimer/data_structure/pose.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | class Pose { 8 | 9 | public: 10 | Pose(); 11 | Pose(const Eigen::Quaterniond &quaternion, const Eigen::Vector3d &position); 12 | Pose(const Eigen::AngleAxisd &angle_axis, const Eigen::Vector3d &position); 13 | Pose(const Eigen::Matrix3d &rotate_matrix, const Eigen::Vector3d &position); 14 | 15 | // properties 16 | const Eigen::Quaterniond &quaternion() const; 17 | const Eigen::Vector3d &position() const; 18 | Eigen::Matrix3d get_rotation() const; 19 | Eigen::AngleAxisd get_angle_axis() const; 20 | 21 | void set_quternion(const Eigen::Quaterniond &quaternion); 22 | void set_quternion(double w, double x, double y, double z); 23 | void set_quternion(const Eigen::Matrix3d &rotation); 24 | void set_quternion(const Eigen::AngleAxisd &angle_axis); 25 | 26 | void set_position(const Eigen::Vector3d &position); 27 | 28 | void SetIdentity(); 29 | Pose Inverse() const; 30 | void InverseMutable(); 31 | Pose Scale(double s) const; 32 | void ScaleMutable(double s); 33 | Eigen::Vector3d Center() const; 34 | 35 | /// 36 | /// @brief Set the current Pose as the value of the product of two 37 | /// Pose, This = Pose1 * Pose2 38 | /// 39 | void PoseMult(const Pose &lhs, const Pose &rhs); 40 | 41 | /// 42 | /// @brief the Pose of the vector 43 | /// 44 | Eigen::Vector3d operator*(const Eigen::Vector3d &vec) const; 45 | 46 | /// 47 | /// @brief the Pose of the Quaternion 48 | /// 49 | Eigen::Quaterniond operator*(const Eigen::Quaterniond &quaternion) const; 50 | 51 | /// 52 | /// @brief the Pose of the vector 53 | /// 54 | Eigen::Vector3d operator()(const Eigen::Vector3d &vec) const; 55 | 56 | private: 57 | Eigen::Quaterniond quaternion_; 58 | Eigen::Vector3d position_; 59 | 60 | friend bool operator!=(const Pose &lhs, const Pose &rhs); 61 | friend bool operator==(const Pose &lhs, const Pose &rhs); 62 | }; 63 | 64 | bool operator!=(const Pose &lhs, const Pose &rhs); 65 | bool operator==(const Pose &lhs, const Pose &rhs); 66 | -------------------------------------------------------------------------------- /cpp/xrprimer/utils/logging.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #include "logging.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #define LOG_MSGBUF_INIT 512 9 | 10 | static spdlog::logger *logger() { 11 | static std::unique_ptr s_logger; 12 | if (!s_logger) { 13 | auto console_sink = 14 | std::make_shared(); 15 | console_sink->set_level(spdlog::level::trace); 16 | console_sink->set_pattern("%Y-%m-%d %T.%e - [ROAM][%^%l%$] %v"); 17 | s_logger = std::make_unique( 18 | "roam", spdlog::sinks_init_list{console_sink}); 19 | s_logger->set_level(spdlog::level::trace); 20 | } 21 | return s_logger.get(); 22 | } 23 | 24 | void log_message(XRLogLevel level, const char *format, ...) { 25 | static std::vector msgbuf(LOG_MSGBUF_INIT); 26 | va_list vargs1, vargs2; 27 | va_start(vargs1, format); 28 | va_copy(vargs2, vargs1); 29 | int len = vsnprintf(nullptr, 0, format, vargs1); 30 | va_end(vargs1); 31 | if (msgbuf.size() < len + 1) { 32 | msgbuf.resize(len + 1); 33 | } 34 | vsnprintf(msgbuf.data(), msgbuf.size(), format, vargs2); 35 | va_end(vargs2); 36 | spdlog::level::level_enum lvl; 37 | switch (level) { 38 | case XR_LOG_DEBUG: 39 | lvl = spdlog::level::debug; 40 | break; 41 | case XR_LOG_INFO: 42 | lvl = spdlog::level::info; 43 | break; 44 | case XR_LOG_NOTICE: 45 | lvl = spdlog::level::info; 46 | break; 47 | case XR_LOG_WARNING: 48 | lvl = spdlog::level::warn; 49 | break; 50 | case XR_LOG_ERR: 51 | lvl = spdlog::level::err; 52 | break; 53 | case XR_LOG_CRIT: 54 | lvl = spdlog::level::critical; 55 | break; 56 | case XR_LOG_ALERT: 57 | lvl = spdlog::level::critical; 58 | break; 59 | case XR_LOG_EMERG: 60 | lvl = spdlog::level::critical; 61 | break; 62 | } 63 | logger()->log(lvl, msgbuf.data()); 64 | } 65 | -------------------------------------------------------------------------------- /cpp/xrprimer/utils/logging.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) OpenXRLab. All rights reserved. 2 | 3 | #ifndef XR_LOGGINHT_H_ 4 | #define XR_LOGGINHT_H_ 5 | 6 | enum XRLogLevel { 7 | XR_LOG_DEBUG = -1, /**< debug message **/ 8 | XR_LOG_INFO = 0, /**< informational message **/ 9 | XR_LOG_NOTICE = 1, /**< normal, but significant, condition **/ 10 | XR_LOG_WARNING = 2, /**< warning conditions **/ 11 | XR_LOG_ERR = 3, /**< error conditions **/ 12 | XR_LOG_CRIT = 4, /**< critical conditions **/ 13 | XR_LOG_ALERT = 5, /**< action must be taken immediately **/ 14 | XR_LOG_EMERG = 6 /**< system is unusable **/ 15 | }; 16 | 17 | /** @brief Log message 18 | * @param level Message level defined in XRLogLevel 19 | * @param format Pointer to a string which consists of characters along with 20 | * optional format specifiers starting with % 21 | */ 22 | void log_message(XRLogLevel level, const char *format, ...); 23 | 24 | #define xr_log_emergency(...) log_message(XR_LOG_EMERG, __VA_ARGS__) 25 | #define xr_log_alert(...) log_message(XR_LOG_ALERT, __VA_ARGS__) 26 | #define xr_log_critical(...) log_message(XR_LOG_CRIT, __VA_ARGS__) 27 | #define xr_log_error(...) log_message(XR_LOG_ERR, __VA_ARGS__) 28 | #define xr_log_warning(...) log_message(XR_LOG_WARNING, __VA_ARGS__) 29 | #define xr_log_notice(...) log_message(XR_LOG_NOTICE, __VA_ARGS__) 30 | #define xr_log_info(...) log_message(XR_LOG_INFO, __VA_ARGS__) 31 | #define xr_log_debug(...) log_message(XR_LOG_DEBUG, __VA_ARGS__) 32 | 33 | #define xr_runtime_assert(condition, message) \ 34 | do { \ 35 | if (!(condition)) { \ 36 | log_error("Assertion failed at " __FILE__ \ 37 | ":%d : %s\nWhen testing condition:\n %s", \ 38 | __LINE__, message, #condition); \ 39 | abort(); \ 40 | } \ 41 | } while (0) 42 | 43 | #endif // XR_LOGGINHT_H_ 44 | -------------------------------------------------------------------------------- /dockerfiles/publish_manylinux/Dockerfile: -------------------------------------------------------------------------------- 1 | #Download base image pypa/manylinux2014_x86_64 2 | FROM quay.io/pypa/manylinux2014_x86_64:latest 3 | 4 | # Install wget 5 | RUN yum install wget atlas-devel \ 6 | lapack-devel blas-devel \ 7 | zlib \ 8 | -y && \ 9 | yum clean all 10 | # Install miniconda 11 | RUN wget -q \ 12 | https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \ 13 | && bash Miniconda3-latest-Linux-x86_64.sh -b -p /opt/miniconda \ 14 | && rm -f Miniconda3-latest-Linux-x86_64.sh 15 | 16 | # Prepare shared env 17 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 18 | conda activate base && \ 19 | pip install conan==1.51.1 && \ 20 | pip install twine && \ 21 | pip cache purge 22 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 23 | conda activate base && \ 24 | conda install cmake && \ 25 | conda clean --all 26 | 27 | # Link binaries to somewhere included in PATH 28 | RUN ln -s /opt/miniconda/bin/conan /opt/rh/devtoolset-10/root/usr/bin/conan && \ 29 | ln -s /opt/miniconda/bin/twine /opt/rh/devtoolset-10/root/usr/bin/twine && \ 30 | ln -s /opt/miniconda/bin/cmake /opt/rh/devtoolset-10/root/usr/bin/cmake 31 | 32 | # Update in bashrc 33 | RUN echo "source /opt/miniconda/etc/profile.d/conda.sh" >> /root/.bashrc && \ 34 | echo "conda deactivate" >> /root/.bashrc 35 | 36 | ARG MMCV_VER 37 | ARG TORCH_VER 38 | ARG TORCHV_VER 39 | # Install py-3.6, py-3.7, py-3.8, py-3.9, py-3.10 40 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 41 | for i in 6 7 8 9 10; do \ 42 | conda create -n "py-3.$i" python="3.$i" -y && \ 43 | conda activate "py-3.$i" && \ 44 | pip install --upgrade pip setuptools wheel && \ 45 | pip install torch==$TORCH_VER torchvision==$TORCHV_VER && \ 46 | pip install mmcv==$MMCV_VER && \ 47 | pip cache purge && \ 48 | conda clean --all; \ 49 | done 50 | -------------------------------------------------------------------------------- /dockerfiles/publish_manylinux/build_publish_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MMCV_VER=1.7.0 3 | TORCH_VER=1.11.0 4 | TORCHV_VER=0.12.0 5 | MMCV_VER_DIGIT=${MMCV_VER//./} 6 | TORCH_VER_DIGIT=${TORCH_VER//./} 7 | TAG="openxrlab/xrprimer_ci:manylinux2014_x86_64_torch${TORCH_VER_DIGIT}_mmcv${MMCV_VER_DIGIT}" 8 | echo "tag to build: $TAG" 9 | BUILD_ARGS="--build-arg MMCV_VER=${MMCV_VER} --build-arg TORCH_VER=${TORCH_VER} --build-arg TORCHV_VER=${TORCHV_VER}" 10 | # build according to Dockerfile 11 | docker build -t $TAG -f dockerfiles/publish_manylinux/Dockerfile $BUILD_ARGS --progress=plain . 12 | -------------------------------------------------------------------------------- /dockerfiles/runtime_ubt18/Dockerfile: -------------------------------------------------------------------------------- 1 | # Download base image ubuntu 18.04 2 | FROM ubuntu:18.04 3 | 4 | # Install apt packages 5 | RUN apt-get update && \ 6 | apt-get install -y \ 7 | wget git vim \ 8 | gcc-7 g++-7 make ninja-build \ 9 | libblas-dev liblapack-dev libatlas-base-dev\ 10 | && \ 11 | apt-get autoclean 12 | 13 | # Install miniconda 14 | RUN wget -q \ 15 | https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \ 16 | && mkdir /root/.conda \ 17 | && bash Miniconda3-latest-Linux-x86_64.sh -b -p /opt/miniconda \ 18 | && rm -f Miniconda3-latest-Linux-x86_64.sh 19 | 20 | # Update gcc/g++/conda in bashrc 21 | RUN echo "export CXX=/usr/bin/g++-7" >> /root/.bashrc && \ 22 | echo "export CC=/usr/bin/gcc-7" >> /root/.bashrc && \ 23 | echo "source /opt/miniconda/etc/profile.d/conda.sh" >> /root/.bashrc && \ 24 | echo "conda deactivate" >> /root/.bashrc 25 | 26 | # Source the new env 27 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 28 | conda deactivate 29 | ENV CXX /usr/bin/g++-7 30 | ENV CC /usr/bin/gcc-7 31 | 32 | ARG PY_VER 33 | # Prepare conda env 34 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 35 | conda create -n openxrlab python=$PY_VER -y && \ 36 | conda activate openxrlab && \ 37 | conda install ffmpeg -y && \ 38 | conda install cmake -y && \ 39 | conda clean --all 40 | 41 | # Prepare torch env 42 | ARG TORCH_VER 43 | ARG TORCHV_VER 44 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 45 | conda activate openxrlab && \ 46 | conda install pytorch==$TORCH_VER torchvision==$TORCHV_VER cpuonly -c pytorch && \ 47 | conda clean --all 48 | 49 | ARG MMCV_VER 50 | # Prepare pip env 51 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 52 | conda activate openxrlab && \ 53 | pip install pre-commit interrogate coverage pytest && \ 54 | pip install numpy matplotlib && \ 55 | pip install mmcv==$MMCV_VER && \ 56 | pip install conan==1.51.1 && \ 57 | pip cache purge 58 | 59 | # Install basic requirements 60 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 61 | conda activate openxrlab && \ 62 | pip install -r https://raw.githubusercontent.com/openxrlab/xrprimer/main/requirements/runtime.txt && \ 63 | pip cache purge 64 | 65 | RUN . /opt/miniconda/etc/profile.d/conda.sh && \ 66 | conda activate openxrlab && \ 67 | pip uninstall opencv-python opencv-python-headless -y && \ 68 | pip install opencv-python-headless && \ 69 | pip cache purge 70 | -------------------------------------------------------------------------------- /dockerfiles/runtime_ubt18/build_runtime_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PY_VER=3.8 3 | MMCV_VER=1.7.0 4 | TORCH_VER=1.12.1 5 | TORCHV_VER=0.13.1 6 | PY_VER_DIGIT=${PY_VER//./} 7 | MMCV_VER_DIGIT=${MMCV_VER//./} 8 | TORCH_VER_DIGIT=${TORCH_VER//./} 9 | TAG="openxrlab/xrprimer_runtime:ubuntu1804_x64_gcc7_py${PY_VER_DIGIT}_torch${TORCH_VER_DIGIT}_mmcv${MMCV_VER_DIGIT}" 10 | echo "tag to build: $TAG" 11 | BUILD_ARGS="--build-arg PY_VER=${PY_VER} --build-arg MMCV_VER=${MMCV_VER} --build-arg TORCH_VER=${TORCH_VER} --build-arg TORCHV_VER=${TORCHV_VER}" 12 | # build according to Dockerfile 13 | docker build -t $TAG -f dockerfiles/runtime_ubt18/Dockerfile $BUILD_ARGS --progress=plain . 14 | -------------------------------------------------------------------------------- /docs/en/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/en/_static/xr_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/docs/en/_static/xr_logo.ico -------------------------------------------------------------------------------- /docs/en/api.rst: -------------------------------------------------------------------------------- 1 | xrprimer.data_structure 2 | ----------------------- 3 | 4 | camera 5 | ^^^^^^ 6 | .. automodule:: xrprimer.data_structure.camera 7 | :members: 8 | 9 | 10 | xrprimer.calibration 11 | -------------------- 12 | 13 | .. automodule:: xrprimer.calibration 14 | :members: 15 | 16 | 17 | xrprimer.ops 18 | ------------ 19 | 20 | projection 21 | ^^^^^^^^^^ 22 | .. automodule:: xrprimer.ops.projection 23 | :members: 24 | 25 | triangulation 26 | ^^^^^^^^^^^^^ 27 | .. automodule:: xrprimer.ops.triangulation 28 | :members: 29 | 30 | 31 | xrprimer.transform 32 | ------------------ 33 | 34 | camera 35 | ^^^^^^ 36 | .. automodule:: xrprimer.transform.camera 37 | :members: 38 | 39 | camera convention 40 | ^^^^^^^^^^^^^^^^^ 41 | .. automodule:: xrprimer.transform.convention.camera 42 | :members: 43 | 44 | image 45 | ^^^^^ 46 | .. automodule:: xrprimer.transform.image 47 | :members: 48 | 49 | 50 | xrprimer.utils 51 | -------------- 52 | .. automodule:: xrprimer.utils 53 | :members: 54 | -------------------------------------------------------------------------------- /docs/en/build_docs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import subprocess 4 | import sys 5 | 6 | 7 | def _parse_version(version_file='../../version.txt'): 8 | with open(version_file) as f: 9 | version_lst = [] 10 | for x in f.readlines(): 11 | version_lst += [x.strip().split(' ')[-1]] 12 | version = '.'.join(version_lst) 13 | return version 14 | 15 | 16 | def build_sphinx_docs(temp_dir='_build'): 17 | """Build sphinx docs for python.""" 18 | if os.path.exists(temp_dir): 19 | shutil.rmtree(temp_dir) 20 | os.makedirs(temp_dir, exist_ok=True) 21 | cmd = ['make', 'html'] 22 | subprocess.check_call(cmd, stdout=sys.stdout, stderr=sys.stderr) 23 | 24 | 25 | def build_doxygen_docs(temp_dir='doxygen', cpp_dir='cpp_api'): 26 | """Build sphinx docs for C++""" 27 | cmd = ['doxygen', 'Doxyfile.in'] 28 | subprocess.check_call( 29 | cmd, 30 | stdout=sys.stdout, 31 | stderr=sys.stderr, 32 | env=dict(os.environ, PROJECT_NUMBER=_parse_version())) 33 | # move generated results to _build 34 | doxygen_dir = os.path.join(temp_dir, 'html') 35 | dst_dir = os.path.join('_build', 'html', cpp_dir) 36 | shutil.copytree(doxygen_dir, dst_dir, dirs_exist_ok=True) 37 | if os.path.exists(temp_dir): 38 | shutil.rmtree(temp_dir) 39 | 40 | 41 | if __name__ == '__main__': 42 | """Used for local debugging.""" 43 | build_sphinx_docs() 44 | build_doxygen_docs() 45 | -------------------------------------------------------------------------------- /docs/en/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### v0.7.0 (05/05/2023/) 4 | 5 | **Highlights** 6 | 7 | - Add `synbody_utils.py` and `exr_reader.py` module to help with using synbody dataset. 8 | - Support points and lines visualization by OpenCV and by Matplotlib. 9 | - Improve external dependencies by splitting external and project build. 10 | - Update dockerfiles, build scripts and docker images. 11 | 12 | **New Features** 13 | 14 | - Add points and lines visualization by OpenCV and by Matplotlib, with features such as PointPalette and LinePalette for visualization backends sharing the same definition of data. 15 | - Add fast undistortion in Python. 16 | - Add Keypoints, Limbs and keypoints_convention from XRMoCap. 17 | - Add world convention for 3D spaces. 18 | - Add `VideoReader` to ffmpeg_utils in Python. Together with `VideoWriter`, now we can visualize a really long video on a machine with poor RAM. 19 | 20 | ### v0.6.0 (01/09/2022/) 21 | 22 | **Highlights** 23 | 24 | - Support iOS and Linux compilation 25 | - Support installation via pypi, ranging from python 3.6 to 3.10 26 | - Support various camera models (Pinhole, Fisheye, Omni etc.) 27 | - Support basic 3D operations (Triangulator, Projector etc.) 28 | - Support Multi-camera extrinsic calibration tools 29 | 30 | **New Features** 31 | 32 | - Add pybind to create Python bindings of C++ data structures and switch python backend to C++ code 33 | - Add camera convention convert method 34 | - Add camera calibrator in python to support 3 types of calibration 35 | - Add image class and support the conversion with OpenCV 36 | - Add external deps and use conan manager to accelerate the compilation 37 | - Provide samples to demonstrate linking XRPrimer in other C++ projects 38 | -------------------------------------------------------------------------------- /docs/en/cpp_api.rst: -------------------------------------------------------------------------------- 1 | C++ documentation 2 | ----------------- 3 | 4 | `C++ Doxygen documentation <./cpp_api/index.html>`_ 5 | -------------------------------------------------------------------------------- /docs/en/data_structure/image.md: -------------------------------------------------------------------------------- 1 | # Image 2 | 3 | This file introduces the supported image data structure in C++. 4 | It is an extension of OpenCV Mat, and also provides a way to convert between OpenCV Mat and Image. 5 | 6 | #### Attributes 7 | 8 | Here are attributes of class `Image`. 9 | 10 | | Attribute name | Type | Description | 11 | | --------------- | -------------------------- | ----------------------------------------------- | 12 | | width | int | Image width in pixels | 13 | | height | int | Image height in pixels | 14 | | step\_ | int | Size of aligned image row in bytes | 15 | | ts | int64\_t | Image timestamp | 16 | | stream\_id | int64\_t | Image stream index | 17 | | format\_ | PixelFormat | Image format (e.g., RGB24, BGR24, RGBA, GRAY8) | 18 | | storage\_data\_ | std::shared\_ptr | Pointer to image data | 19 | 20 | Besides the normal attributes for an image, it defines attributes like `timestamp` which is convenient for algorithms like SLAM. 21 | 22 | #### Create an Image 23 | 24 | Note that `Image` follows the order (width, height). 25 | 26 | ```C++ 27 | // create a color image with w=20 and h=10 28 | Image img(20, 10, RGB24); 29 | ``` 30 | 31 | #### From Image to OpenCV 32 | 33 | ```C++ 34 | int width = 20; 35 | int height = 10; 36 | Image img(width, height, BGR24); 37 | 38 | // Image to OpenCV 39 | cv::Mat mat_warpper(img.height(), img.width(), CV_8UC3, img.mutable_data()); 40 | ``` 41 | 42 | #### From OpenCV to Image 43 | 44 | ```C++ 45 | int width = 20; 46 | int height = 10; 47 | cv::Mat black = cv::Mat::zeros(height, width, CV_8UC3); 48 | cv::imwrite("black.bmp", black); 49 | 50 | // OpenCV to Image 51 | Image i_black(black.cols, black.rows, black.step, BGR24, black.data); 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/en/data_structure/limbs.md: -------------------------------------------------------------------------------- 1 | # Limbs 2 | 3 | - [Overview](#overview) 4 | - [Attribute definition](#attribute-definition) 5 | - [Create an instance](#create-an-instance) 6 | 7 | ### Overview 8 | 9 | Limbs is a class for person limbs data, recording connection vectors between keypoints. It accepts either `numpy.ndarray` or `torch.Tensor`, convert them into `numpy.ndarray`, `numpy.int32`. 10 | 11 | ### Attribute definition 12 | 13 | - connections: An ndarray for connections, in shape [n_conn, 2], `connections[:, 0]` are start point indice and `connections[:, 1]` are end point indice. `connections[n, :]` is `[start_index, end_index]` of the No.n connection. 14 | - connection_names: A list of strings, could be None. If not None, length of `connection_names` equals to length of `connections`. 15 | - parts: A nested list, could be None. If not None, `len(parts)` is number of parts, and `len(parts[0])` is number of connections in the first part. `parts[i][j]` is an index of connection. 16 | - part_names: A list of strings, could be None. If not None, length of `part_names` equals to length of `parts`. 17 | - points: An ndarray for points, could be None. If not None, it is in shape [n_point, point_dim]. We could use the index record in `connections` to fetch a point. 18 | - logger: Logger for logging. If None, root logger will be selected. 19 | 20 | ### Create an instance 21 | 22 | a. Create instance with raw data and `__init__()`. 23 | 24 | ```python 25 | from xrprimer.data_structure.limbs import Limbs 26 | 27 | # only connections arg is necessary for Limbs 28 | connections = np.asarray( 29 | [[0, 1], [0, 2], [1, 3]] 30 | ) 31 | limbs = Limbs(connections=connections) 32 | 33 | # split connections into parts 34 | parts = [[0, ], [1, 2]] 35 | part_names = ['head', 'right_arm'] 36 | limbs = Limbs(connections=connections, parts=parts, part_names=part_names) 37 | ``` 38 | 39 | b. Get limbs from a well-defined Keypoints instance. The connections will be searched from a sub-set of `human_data` limbs. 40 | 41 | ```python 42 | from xrprimer.transform.limbs import get_limbs_from_keypoints 43 | 44 | # Get limbs according to keypoints' mask and convention. 45 | limbs = get_limbs_from_keypoints(keypoints=keypoints2d) 46 | # connections, parts and part_names have been set 47 | 48 | # torch type is also accepted 49 | keypoints2d_torch = keypoints2d.to_tensor() 50 | limbs = get_limbs_from_keypoints(keypoints=keypoints2d_torch) 51 | 52 | # If both frame_idx and person_idx have been set, 53 | # limbs are searched from a certain frame 54 | # limbs.points have also been set 55 | limbs = get_limbs_from_keypoints(keypoints=keypoints2d, frame_idx=0, person_idx=0) 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/en/data_structure/pose.md: -------------------------------------------------------------------------------- 1 | # Pose 2 | 3 | This file introduces the supported pose data structure in C++. 4 | Generally, pose consists of a rotation and position. 5 | 6 | #### Attributes 7 | 8 | Here are attributes of class `Pose`. 9 | 10 | | Attribute name | Type | Description | 11 | | --------------- | -------------------------- | ----------------------------------------------- | 12 | | quaternion\_ | Eigen::Quaterniond | Pose rotation | 13 | | position\_ | Eigen::Vector3d | Pose position | 14 | 15 | #### Construct a Pose 16 | 17 | Construct a pose with default value, where rotation is identity matrix and position is zero. 18 | 19 | ```C++ 20 | Pose pose; 21 | ``` 22 | 23 | Construct a pose with rotation and position. Rotation can be represented as quaternion, axis angle or rotation matrix. 24 | 25 | ```C++ 26 | Eigen::Vector3d vec3d; 27 | 28 | Eigen::Quaterniond quaternion; 29 | Pose pose1(quaternion.setIdentity(), vec3d.setZero()); 30 | 31 | Eigen::AngleAxisd angleAxis(30, Eigen::Vector3d::UnitY()); 32 | Pose pose2(angleAxis, vec3d.setZero()); 33 | 34 | Eigen::Matrix3d mat3d; 35 | Pose pose3(mat3d.setIdentity(), vec3d.setZero()); 36 | ``` 37 | 38 | #### Set Pose to identity 39 | 40 | A identity pose denotes to identity rotation and zero position 41 | 42 | ```C++ 43 | pose.SetIdentity(); 44 | ``` 45 | 46 | #### Scale a Pose 47 | 48 | Scale a pose means multiplying scaling factor with the position. 49 | 50 | ```C++ 51 | auto p = pose3.Scale(1.2); 52 | pose3.ScaleMutable(1.4); 53 | ``` 54 | 55 | #### Inverse a Pose 56 | 57 | Inverse a pose is defined as (1) applying rotation inversion and (2) multiplying position with inversed rotation. 58 | 59 | ```C++ 60 | pose.Inverse(); 61 | pose.InverseMutable(); 62 | ``` 63 | -------------------------------------------------------------------------------- /docs/en/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | We list some common troubles faced by many users and their corresponding solutions here. Feel free to enrich the list if you find any frequent issues and have ways to help others to solve them. If the contents here do not cover your issue, do not hesitate to create an issue! 4 | 5 | 6 | ## Installation 7 | 8 | - 'ImportError: libpng16.so.16: cannot open shared object file: No such file or directory' 9 | 10 | 1. If using conda, `conda install -c anaconda libpng` 11 | 2. If sudo is available, `apt update & apt -y install libpng16-16` 12 | 13 | - 'ImportError: liblapack.so.3: cannot open shared object file: No such file or directory' 14 | 15 | 1. If using conda, `conda install -c conda-forge lapack` 16 | 2. If sudo is available, `apt update & apt -y install libatlas-base-dev` 17 | -------------------------------------------------------------------------------- /docs/en/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to XRPrimer's documentation! 2 | ======================================= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Installation 7 | 8 | installation/cpp.md 9 | installation/python.md 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :caption: Data Structure 14 | 15 | data_structure/camera.md 16 | data_structure/image.md 17 | data_structure/pose.md 18 | 19 | .. toctree:: 20 | :maxdepth: 2 21 | :caption: Operations 22 | 23 | ops/triangulator.md 24 | ops/projector.md 25 | 26 | .. toctree:: 27 | :maxdepth: 2 28 | :caption: Transform 29 | 30 | transform/camera_convention.md 31 | 32 | .. toctree:: 33 | :maxdepth: 2 34 | :caption: Tools 35 | 36 | tools/calibrate_multiple_cameras.md 37 | 38 | .. toctree:: 39 | :maxdepth: 2 40 | :caption: Unit tests 41 | 42 | test.md 43 | 44 | .. toctree:: 45 | :maxdepth: 2 46 | :caption: Notes 47 | 48 | faq.md 49 | changelog.md 50 | 51 | .. toctree:: 52 | :maxdepth: 2 53 | :caption: License 54 | 55 | license.rst 56 | 57 | .. toctree:: 58 | :maxdepth: 1 59 | :caption: C++ API 60 | 61 | cpp_api 62 | 63 | .. toctree:: 64 | :maxdepth: 1 65 | :caption: Python API 66 | 67 | api.rst 68 | 69 | Indices and tables 70 | ================== 71 | 72 | * :ref:`genindex` 73 | * :ref:`search` 74 | -------------------------------------------------------------------------------- /docs/en/installation/cpp.md: -------------------------------------------------------------------------------- 1 | # Installation (CPP) 2 | 3 | 4 | 5 | - [Installation (CPP)](#installation-cpp) 6 | - [Requirements](#requirements) 7 | - [Compilation](#compilation) 8 | - [Compilation options](#compilation-options) 9 | - [Compilation on iOS](#compilation-on-ios) 10 | - [Test](#test) 11 | - [How to link in C++ projects](#how-to-link-in-c-projects) 12 | 13 | 14 | 15 | ### Requirements 16 | + C++14 or later compiler 17 | + GCC 7.5+ 18 | + CMake 3.15+ 19 | + LAPACK & BLAS 20 | 1. If using conda, `conda install -c conda-forge lapack` 21 | 2. If sudo is available, `apt update & apt -y install libatlas-base-dev` 22 | 23 | Optional: 24 | + [Conan](https://docs.conan.io/en/1.46/installation.html) (for using pre-built 3rd-party libraries) 25 | ``` bash 26 | # 0. install conan 27 | pip install conan 28 | 29 | # 1. first run 30 | conan profile new --detect --force default 31 | conan profile update settings.compiler.libcxx=libstdc++11 default 32 | 33 | # 2. add conan artifactory 34 | conan remote add openxrlab http://conan.openxrlab.org.cn/artifactory/api/conan/openxrlab 35 | 36 | # 3. check 37 | conan remote list 38 | ``` 39 | 40 | ### Compilation 41 | 42 | ```bash 43 | git clone https://github.com/openxrlab/xrprimer.git 44 | cd xrprimer/ 45 | 46 | # build and install deps 47 | cmake -S. -Bbuild_deps -D3RT_FROM_LOCAL=ON 48 | cmake --build build_deps -j4 49 | 50 | # compiler xrprimer 51 | cmake -S. -Bbuild [Compilation options] 52 | cmake --build build --target install -j4 53 | ``` 54 | 55 | It is currently tested on Linux and iOS. Ideally it can be also compiled on macOS or Windows. 56 | 57 | #### Compilation options 58 | 59 | > Get external dependencies 60 | 61 | - `3RT_FROM_LOCAL` Dependencies library will be built or find in local host. default: `OFF` 62 | - `3RT_FROM_CONAN` Dependencies library will be download from openxrlab conan remote. default: `OFF` 63 | 64 | > Config xrprimer 65 | 66 | - `ENABLE_TEST` Enable unit test. default: `OFF` 67 | - `PYTHON_BINDING` Enable Python binding. default: `ON` 68 | 69 | ```bash 70 | #1. First run, get external dependencies, will install external deps to 3rdparty 71 | cmake -S. -Bbuild_deps <-D3RT_FROM_LOCAL=ON/-D3RT_FROM_CONAN=ON> 72 | cmake --build build_deps 73 | 74 | #2. build xrprimer 75 | cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=install 76 | cmake --build build --target install 77 | 78 | ``` 79 | 80 | #### Compilation on iOS 81 | 82 | Refer to [build_ios.sh](../../../scripts/build_ios.sh) for more details. 83 | 84 | ### Test 85 | 86 | CPP library 87 | 88 | ```bash 89 | # compile (Skip the following two lines if it has been compiled) 90 | cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DENABLE_TEST=ON 91 | cmake --build build -j4 92 | 93 | # run test 94 | cd build 95 | pip install gdown 96 | gdown https://docs.google.com/uc?id=1MJx367I2_ezK3vKdV4eJ9d0cBzgs2jtR && tar -xzf xrprimer.tar.gz && rm xrprimer.tar.gz 97 | ln -sfn xrprimer/test test 98 | ./bin/test_calibrator 99 | ``` 100 | 101 | Python library 102 | 103 | ```bash 104 | # compile (Skip the following two lines if it has been compiled) 105 | cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DENABLE_TEST=ON 106 | cmake --build build -j4 107 | 108 | # run test 109 | cd build 110 | pip install gdown 111 | gdown https://docs.google.com/uc?id=1MJx367I2_ezK3vKdV4eJ9d0cBzgs2jtR && tar -xzf xrprimer.tar.gz && rm xrprimer.tar.gz 112 | PYTHONPATH=./lib/ python ../cpp/tests/test_multi_camera_calibrator.py 113 | ``` 114 | 115 | ### How to link in C++ projects 116 | 117 | see [cpp sample](../../../cpp/samples) 118 | 119 | ```js 120 | cmake_minimum_required(VERSION 3.16) 121 | 122 | project(sample) 123 | 124 | # set path for find XRPrimer package (config mode) 125 | set(XRPrimer_DIR "/lib/cmake") 126 | find_package(XRPrimer REQUIRED) 127 | 128 | add_executable(sample sample.cpp) 129 | 130 | target_link_libraries(sample XRPrimer::xrprimer) 131 | ``` 132 | -------------------------------------------------------------------------------- /docs/en/installation/python.md: -------------------------------------------------------------------------------- 1 | # Installation (Python) 2 | 3 | 4 | 5 | - [Installation (Python)](#installation-python) 6 | - [Requirements](#requirements) 7 | - [Prepare environment](#prepare-environment) 8 | - [Install XRPrimer (python)](#install-xrprimer-python) 9 | - [Install with pip](#install-with-pip) 10 | - [Install by compiling from source](#install-by-compiling-from-source) 11 | 12 | 13 | 14 | ## Requirements 15 | 16 | - Linux 17 | - Conda 18 | - Python 3.6+ 19 | 20 | ## Prepare environment 21 | 22 | a. Create a conda virtual environment and activate it. 23 | 24 | 25 | ## Install XRPrimer (python) 26 | 27 | 28 | ### Install with pip 29 | 30 | ```shell 31 | pip install xrprimer 32 | ``` 33 | 34 | ### Install by compiling from source 35 | 36 | a. Create a conda virtual environment and activate it. 37 | 38 | ```shell 39 | conda create -n openxrlab python=3.8 -y 40 | conda activate openxrlab 41 | ``` 42 | 43 | b. Clone the repo. 44 | 45 | ```shell 46 | git clone https://github.com/openxrlab/xrprimer.git 47 | cd xrprimer/ 48 | ``` 49 | 50 | c. (Optional) Install conan 51 | 52 | ```shell 53 | # compiling with conan accelerates the compilation with pre-built libs, otherwise it builds external libs from source 54 | pip install conan==1.51.1 55 | conan remote add openxrlab http://conan.openxrlab.org.cn/artifactory/api/conan/openxrlab 56 | ``` 57 | 58 | d. Install Opencv 59 | 60 | ```shell 61 | pip install opencv-python 62 | 63 | # or install opencv in headless mode 64 | pip install opencv-python-headless 65 | ``` 66 | 67 | 68 | e. Install PyTorch and MMCV 69 | 70 | Install PyTorch and torchvision following [official instructions](https://pytorch.org/). 71 | 72 | E.g., install PyTorch 1.8.2 & CPU 73 | 74 | ```shell 75 | pip install torch==1.8.2+cpu torchvision==0.9.2+cpu -f https://download.pytorch.org/whl/lts/1.8/torch_lts.html 76 | ``` 77 | 78 | Install mmcv without cuda operations 79 | 80 | ```shell 81 | pip install mmcv 82 | ``` 83 | 84 | e. Install xrprimer in editable mode 85 | 86 | ```shell 87 | pip install -e . # or "python setup.py develop" 88 | python -c "import xrprimer; print(xrprimer.__version__)" 89 | ``` 90 | -------------------------------------------------------------------------------- /docs/en/license.rst: -------------------------------------------------------------------------------- 1 | LICENSE 2 | ------- 3 | 4 | .. include:: ../../LICENSE 5 | -------------------------------------------------------------------------------- /docs/en/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/en/ops/projector.md: -------------------------------------------------------------------------------- 1 | # Projector 2 | 3 | - [Prepare camera parameters](#prepare-camera-parameters) 4 | - [Build a triangulator](#build-a-triangulator) 5 | - [Triangulate points from 2D to 3D](#triangulate-points-from-2d-to-3d) 6 | - [Get reprojection error](#get-reprojection-error) 7 | - [Camera selection](#camera-selection) 8 | 9 | ## Prepare camera parameters 10 | 11 | A multi-view projector requires a list of camera parameters, just like the triangulator. Each camera parameter should be an instance of `PinholeCameraParameter` or it's sub-class. For details, please refer to [triangulator doc](./triangulator.md#prepare-camera-parameters) . 12 | 13 | ## Build a projector 14 | 15 | In XRprimer, we use registry and builder to build a certain projector among multiple alternative classes. 16 | 17 | ```python 18 | import mmcv 19 | 20 | from xrprimer.ops.projection.builder import build_projector 21 | 22 | projector_config = dict( 23 | mmcv.Config.fromfile( 24 | 'config/ops/triangulation/opencv_projector.py')) 25 | projector_config['camera_parameters'] = cam_param_list 26 | projector_config = build_projector(projector_config) 27 | ``` 28 | 29 | ## Set cameras of a projector 30 | 31 | Camera parameters can also be set after building. 32 | 33 | ```python 34 | projector.set_cameras(cam_param_list) 35 | ``` 36 | 37 | ## Project points from 3D to 2D 38 | 39 | If there's only one point in 3D space, we could use `project_single_point()`. 40 | 41 | ```python 42 | # points3d in shape [3, ], in type numpy.ndarray, or list/tuple 43 | mview_point2d = projector.project_single_point(point3d) 44 | # mview_point2d in shape [n_view, 2], in type numpy.ndarray 45 | ``` 46 | 47 | For more than one point, `project()` is recommended. 48 | 49 | ```python 50 | # points3d in shape [n_point, 3], in type numpy.ndarray, or nested list/tuple 51 | points2d = triangulator.triangulate(points3d) 52 | # points2d in shape [n_view, n_point, 2], in type numpy.ndarray 53 | ``` 54 | 55 | In multi-view scenario, if we set the value at `points_mask[p_idx]` to zero, the point will not be projected. 56 | 57 | ```python 58 | points2d = triangulator.project(points3d, points_mask) 59 | ``` 60 | 61 | ## Camera selection 62 | 63 | To select a sub-set of all the cameras, we provide a selection method. For details, please refer to [triangulator doc](./triangulator.md#camera-selection) . 64 | -------------------------------------------------------------------------------- /docs/en/test.md: -------------------------------------------------------------------------------- 1 | # Running Tests 2 | 3 | - [Data Preparation](#data-preparation) 4 | - [Environment Preparation](#environment-preparation) 5 | - [Running tests through pytest](#running-tests-through-pytest) 6 | 7 | ## Data Preparation 8 | 9 | Download data from the file server, and extract files to `python/tests/data`. 10 | 11 | ``` 12 | cd python/tests 13 | pip install gdown 14 | gdown https://docs.google.com/uc?id=1MJx367I2_ezK3vKdV4eJ9d0cBzgs2jtR 15 | tar -xzf xrprimer.tar.gz && rm xrprimer.tar.gz 16 | cp -r xrprimer/tests/data ./ 17 | rm -rf xrprimer && cd ../../ 18 | ``` 19 | 20 | ## Environment Preparation 21 | 22 | Install packages for test. 23 | 24 | ``` 25 | pip install -r requirements/test.txt 26 | ``` 27 | 28 | ## Running tests through pytest 29 | 30 | Running all the tests below `python/tests`. It is a good way to validate whether `XRPrimer` has been correctly installed: 31 | 32 | ``` 33 | cd python 34 | pytest tests/ 35 | cd .. 36 | ``` 37 | 38 | Generate a coverage for the test: 39 | 40 | ``` 41 | cd python 42 | coverage run --source xrprimer -m pytest tests/ 43 | coverage xml 44 | coverage report -m 45 | cd .. 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/en/tools/calibrate_multiple_cameras.md: -------------------------------------------------------------------------------- 1 | # Calibrate Multiple Cameras 2 | 3 | TBA 4 | -------------------------------------------------------------------------------- /docs/en/transform/camera_convention.md: -------------------------------------------------------------------------------- 1 | # Camera convention 2 | 3 | ## Intrinsic convention 4 | 5 | In OpenCV, shape of the intrinsic matrix is 3x3, while in some other system it's 4x4. In XRPrimer data structures, we store intrinsic in 4x4 manner, but you can get 3x3 intrinsic matrix by argument of get method. Here are the differences between intrinsic33 and intrinsic44. 6 | 7 | Intrinsic33, only for perspective camera: 8 | 9 | ```python 10 | [[fx, 0, px], 11 | [0, fy, py], 12 | [0, 0, 1]] 13 | ``` 14 | 15 | Intrinsic44, perspective camera: 16 | 17 | ```python 18 | [[fx, 0, px, 0], 19 | [0, fy, py, 0], 20 | [0, 0, 0, 1], 21 | [0, 0, 1, 0]] 22 | ``` 23 | 24 | Intrinsic44, orthographic camera: 25 | 26 | ```python 27 | [[fx, 0, 0, px], 28 | [0, fy, 0, py], 29 | [0, 0, 1, 0], 30 | [0, 0, 0, 1]] 31 | ``` 32 | 33 | We can convert between intrinsic33 and intrinsic44 by `upgrade_k_3x3()`, `downgrade_k_4x4()`: 34 | 35 | ```python 36 | from xrprimer.transform.convention.camera import downgrade_k_4x4, upgrade_k_3x3 37 | 38 | intrinsic44 = upgrade_k_3x3(intrinsic33, is_perspective=True) # intrinsic33 in shape [3, 3] or [batch_size, 3, 3] 39 | intrinsic33 = downgrade_k_4x4(intrinsic44) # intrinsic44 in shape [4, 4] or [batch_size, 4, 4] 40 | ``` 41 | 42 | ## Extrinsic convention 43 | 44 | In OpenCV camera space, a camera looks at Z+ of , screen right is X+ and screen up is Y-. However, not all the cameras are defined this way. We offer you a conversion method, converting a camera from one system to another. 45 | 46 | For example, in order to convert an OpenCV camera into a Blender camera, call `convert_camera_parameter()`, and the direction of extrinsic (world2cam or cam2world) will not be changed. 47 | 48 | ```python 49 | from xrprimer.transform.convention.camera import convert_camera_parameter 50 | 51 | blender_pinhole_param = convert_camera_parameter(pinhole_param, dst'blender') 52 | ``` 53 | 54 | Here is a sheet of supported camera conventions: 55 | 56 | | Convention name | Forward | Up | Right | 57 | | --------------- | ------ | ---- | ----- | 58 | | opencv | Z+ | Y- | X+ | 59 | | blender | Z- | Y+ | X+ | 60 | | unreal | X+ | Z+ | Y+ | 61 | -------------------------------------------------------------------------------- /docs/zh_cn/installation.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/docs/zh_cn/installation.md -------------------------------------------------------------------------------- /python/config/calibration/mview_fisheye_calibrator.py: -------------------------------------------------------------------------------- 1 | type = 'MviewFisheyeCalibrator' 2 | work_dir = './temp' 3 | chessboard_width = 6 4 | chessboard_height = 7 5 | chessboard_square_size = 100 6 | calibrate_intrinsic = False 7 | calibrate_distortion = False 8 | calibrate_extrinsic = True 9 | -------------------------------------------------------------------------------- /python/config/calibration/mview_pinhole_calibrator.py: -------------------------------------------------------------------------------- 1 | type = 'MviewPinholeCalibrator' 2 | chessboard_width = 6 3 | chessboard_height = 7 4 | chessboard_square_size = 100 5 | calibrate_intrinsic = False 6 | calibrate_extrinsic = True 7 | -------------------------------------------------------------------------------- /python/config/calibration/sview_fisheye_distortion_calibrator.py: -------------------------------------------------------------------------------- 1 | type = 'SviewFisheyeDistortionCalibrator' 2 | chessboard_width = 8 3 | chessboard_height = 11 4 | -------------------------------------------------------------------------------- /python/config/ops/projection/opencv_projector.py: -------------------------------------------------------------------------------- 1 | type = 'OpencvProjector' 2 | camera_parameters = [] 3 | -------------------------------------------------------------------------------- /python/config/ops/triangulation/opencv_triangulator.py: -------------------------------------------------------------------------------- 1 | type = 'OpencvTriangulator' 2 | camera_parameters = [] 3 | multiview_reduction = 'median' 4 | -------------------------------------------------------------------------------- /python/tests/calibration/test_sview_intrinsic_calibrator.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import shutil 4 | 5 | import mmcv 6 | import numpy as np 7 | import pytest 8 | 9 | from xrprimer.calibration.builder import build_calibrator 10 | from xrprimer.data_structure.camera import FisheyeCameraParameter 11 | 12 | input_dir = 'tests/data/calibration/test_sview_intrinsic_calibrator' 13 | output_dir = 'tests/data/output/calibration/test_sview_intrinsic_calibrator' 14 | eps = 1e-4 15 | 16 | 17 | @pytest.fixture(scope='module', autouse=True) 18 | def fixture(): 19 | if os.path.exists(output_dir): 20 | shutil.rmtree(output_dir) 21 | os.makedirs(output_dir, exist_ok=False) 22 | 23 | 24 | def test_sview_fisheye_distortion_calibrator(): 25 | fisheye_param = FisheyeCameraParameter.fromfile( 26 | os.path.join(input_dir, 'init_fisheye_param.json')) 27 | frame_list = sorted(glob.glob(os.path.join(input_dir, 'images', '*.jpg'))) 28 | # test dict config 29 | calibrator_config = dict( 30 | mmcv.Config.fromfile('config/calibration/' + 31 | 'sview_fisheye_distortion_calibrator.py')) 32 | calibrator = build_calibrator(calibrator_config) 33 | calibrated_fisheye_param = calibrator.calibrate(frame_list, fisheye_param) 34 | assert abs(calibrated_fisheye_param.k1 - 0.0) > eps 35 | assert abs(calibrated_fisheye_param.k2 - 0.0) > eps 36 | assert abs(calibrated_fisheye_param.k3 - 0.0) > eps 37 | assert abs(calibrated_fisheye_param.p1 - 0.0) > eps 38 | assert abs(calibrated_fisheye_param.p2 - 0.0) > eps 39 | assert abs(calibrated_fisheye_param.k4 - 0.0) < eps 40 | assert abs(calibrated_fisheye_param.k5 - 0.0) < eps 41 | assert np.all( 42 | np.asarray(calibrated_fisheye_param.get_intrinsic(3)) == np.asarray( 43 | fisheye_param.get_intrinsic(3))) 44 | calibrated_fisheye_param.dump( 45 | os.path.join(output_dir, 'calibrated_fisheye_param.json')) 46 | -------------------------------------------------------------------------------- /python/tests/data_structure/test_pinhole_camera_parameter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | import numpy as np 5 | import pytest 6 | 7 | from xrprimer.data_structure.camera import PinholeCameraParameter # noqa:E501 8 | 9 | input_dir = 'tests/data/data_structure/camera_parameter' 10 | output_dir = 'tests/data/output/data_structure/test_pinhole_camera_parameter' 11 | eps = 1e-4 12 | focal_length_x = 954.5469360351562 13 | 14 | 15 | @pytest.fixture(scope='module', autouse=True) 16 | def fixture(): 17 | if os.path.exists(output_dir): 18 | shutil.rmtree(output_dir) 19 | os.makedirs(output_dir, exist_ok=False) 20 | 21 | 22 | def test_load(): 23 | input_json_path = os.path.join(input_dir, 'pinhole_param_at_origin.json') 24 | camera_parameter = PinholeCameraParameter(name='test_LoadFile') 25 | ret_val = camera_parameter.LoadFile(input_json_path) 26 | assert ret_val is True 27 | assert camera_parameter.get_intrinsic(k_dim=4)[0][0] - focal_length_x < eps 28 | camera_parameter = PinholeCameraParameter(name='test_load') 29 | camera_parameter.load(input_json_path) 30 | assert camera_parameter.get_intrinsic(k_dim=4)[0][0] - focal_length_x < eps 31 | k33_0 = camera_parameter.intrinsic33() 32 | k33_1 = np.asarray(camera_parameter.get_intrinsic(3)) 33 | assert np.sum(k33_1 - k33_0) < eps 34 | with pytest.raises(FileNotFoundError): 35 | camera_parameter.load('/no_file.json') 36 | with pytest.raises(ValueError): 37 | camera_parameter.load( 38 | os.path.join(input_dir, 'omni_param_at_origin.json')) 39 | camera_parameter = PinholeCameraParameter.fromfile(input_json_path) 40 | assert camera_parameter.get_intrinsic(k_dim=4)[0][0] - focal_length_x < eps 41 | 42 | 43 | def test_dump(): 44 | input_json_path = os.path.join(input_dir, 'pinhole_param_at_origin.json') 45 | output_json_path = os.path.join(output_dir, 'pinhole_param_at_origin.json') 46 | camera_parameter = PinholeCameraParameter(name='test_dump') 47 | camera_parameter.load(input_json_path) 48 | camera_parameter.dump(output_json_path) 49 | assert os.path.exists(output_json_path) 50 | os.remove(output_json_path) 51 | camera_parameter = PinholeCameraParameter(name='test_SaveFile') 52 | camera_parameter.load(input_json_path) 53 | camera_parameter.SaveFile(output_json_path) 54 | assert os.path.exists(output_json_path) 55 | os.remove(output_json_path) 56 | 57 | 58 | def test_inverse(): 59 | input_json_path = os.path.join(input_dir, 60 | 'pinhole_param_not_at_origin.json') 61 | camera_parameter = PinholeCameraParameter(name='test_inverse') 62 | camera_parameter.load(input_json_path) 63 | origin_world2cam = camera_parameter.world2cam 64 | origin_intrinsic00 = camera_parameter.get_intrinsic(k_dim=4)[0][0] 65 | origin_r02 = camera_parameter.get_extrinsic_r()[0][2] 66 | camera_parameter.inverse_extrinsic() 67 | assert origin_world2cam is not camera_parameter.world2cam 68 | assert abs(origin_intrinsic00 - 69 | camera_parameter.get_intrinsic(k_dim=4)[0][0]) < eps 70 | assert abs(origin_r02 - camera_parameter.get_extrinsic_r()[0][2]) >= eps 71 | camera_parameter.inverse_extrinsic() 72 | assert origin_world2cam is camera_parameter.world2cam 73 | assert abs(origin_intrinsic00 - 74 | camera_parameter.get_intrinsic(k_dim=4)[0][0]) < eps 75 | assert abs(origin_r02 - camera_parameter.get_extrinsic_r()[0][2]) < eps 76 | 77 | 78 | def test_clone(): 79 | input_json_path = os.path.join(input_dir, 80 | 'pinhole_param_not_at_origin.json') 81 | camera_parameter = PinholeCameraParameter(name='test_clone') 82 | camera_parameter.load(input_json_path) 83 | cloned_camera_parameter = camera_parameter.clone() 84 | assert isinstance(cloned_camera_parameter, PinholeCameraParameter) 85 | cloned_k = np.asarray(cloned_camera_parameter.get_intrinsic(k_dim=4)) 86 | src_k = np.asarray(camera_parameter.get_intrinsic(k_dim=4)) 87 | assert np.allclose(cloned_k, src_k) 88 | assert id(cloned_k) != id(src_k) 89 | -------------------------------------------------------------------------------- /python/tests/ops/test_projection.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import shutil 4 | 5 | import cv2 6 | import mmcv 7 | import numpy as np 8 | import pytest 9 | 10 | from xrprimer.data_structure.camera import PinholeCameraParameter 11 | from xrprimer.ops.projection.builder import build_projector 12 | 13 | input_dir = 'tests/data/ops/test_projection' 14 | output_dir = 'tests/data/output/ops/test_projection' 15 | 16 | 17 | @pytest.fixture(scope='module', autouse=True) 18 | def fixture(): 19 | if os.path.exists(output_dir): 20 | shutil.rmtree(output_dir) 21 | os.makedirs(output_dir, exist_ok=False) 22 | 23 | 24 | def test_opencv_projector(): 25 | keypoints3d = np.load( 26 | os.path.join(input_dir, 'keypoints3d.npz'), allow_pickle=True) 27 | keypoints3d = dict(keypoints3d) 28 | n_view = len(glob.glob(os.path.join(input_dir, '*.json'))) 29 | cam_param_list = [] 30 | for cam_idx in range(n_view): 31 | cam_param_path = os.path.join(input_dir, f'cam_{cam_idx:03d}.json') 32 | cam_param = PinholeCameraParameter() 33 | cam_param.load(cam_param_path) 34 | cam_param_list.append(cam_param) 35 | projector_config = dict( 36 | mmcv.Config.fromfile('config/ops/projection/opencv_projector.py')) 37 | projector_config['camera_parameters'] = cam_param_list 38 | projector = build_projector(projector_config) 39 | point = np.array(keypoints3d['keypoints'][0, 0, 0, :3]) 40 | # test project numpy 41 | projected_points = projector.project_single_point(point) 42 | assert projected_points.shape == (n_view, 2) 43 | # test project list 44 | projected_points = projector.project_single_point(point.tolist()) 45 | assert projected_points.shape == (n_view, 2) 46 | # test project tuple 47 | projected_points = projector.project_single_point((0, 1, 2)) 48 | assert projected_points.shape == (n_view, 2) 49 | points3d = keypoints3d['keypoints'][0, 0, :, :3] 50 | points3d_mask = np.expand_dims(keypoints3d['mask'][0, 0, :], axis=-1) 51 | n_point = points3d.shape[0] 52 | # test project numpy 53 | assert isinstance(points3d, np.ndarray) 54 | projected_points = projector.project(points3d) 55 | assert projected_points.shape == (n_view, n_point, 2) 56 | # test project list 57 | projected_points = projector.project(points3d.tolist()) 58 | assert projected_points.shape == (n_view, n_point, 2) 59 | projected_points = projector.project(points3d, points_mask=points3d_mask) 60 | for cam_idx in range(n_view): 61 | canvas = cv2.imread(os.path.join(input_dir, f'{cam_idx:06}.png')) 62 | valid_idxs = np.where(points3d_mask == 1) 63 | for point_idx in valid_idxs[0]: 64 | cv2.circle( 65 | img=canvas, 66 | center=projected_points[cam_idx, point_idx].astype(np.int32), 67 | radius=2, 68 | color=(0, 0, 255)) 69 | cv2.imwrite( 70 | filename=os.path.join(output_dir, f'projected_kps_{cam_idx}.jpg'), 71 | img=canvas) 72 | -------------------------------------------------------------------------------- /python/tests/transform/convention/test_camera_convention.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | import pytest 5 | 6 | from xrprimer.data_structure.camera import FisheyeCameraParameter 7 | from xrprimer.transform.convention.camera import ( 8 | convert_camera_parameter, 9 | downgrade_k_4x4, 10 | upgrade_k_3x3, 11 | ) 12 | 13 | cam_param_dir = 'tests/data/data_structure/camera_parameter' 14 | 15 | 16 | def test_intrinsic(): 17 | # test one k 18 | intrinsic44 = np.asarray([[954.5469360351562, 0.0, 955.3258056640625, 0.0], 19 | [0.0, 953.5127563476562, 552.7735595703125, 0.0], 20 | [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]]) 21 | intrinsic33 = downgrade_k_4x4(intrinsic44) 22 | assert len(intrinsic33) == 3 23 | assert intrinsic33[2][2] == 1 24 | intrinsic44_back = upgrade_k_3x3(intrinsic33, is_perspective=True) 25 | assert np.allclose(intrinsic44_back, intrinsic44) 26 | # test batched k 27 | intrinsic44 = np.expand_dims(intrinsic44, axis=0).repeat(5, axis=0) 28 | intrinsic33 = downgrade_k_4x4(intrinsic44) 29 | assert len(intrinsic33) == 5 30 | assert len(intrinsic33[0]) == 3 31 | assert intrinsic33[0][2][2] == 1 32 | intrinsic44_back = upgrade_k_3x3(intrinsic33, is_perspective=True) 33 | assert np.allclose(intrinsic44_back, intrinsic44) 34 | 35 | 36 | def test_convert_camera(): 37 | fisheye_path = os.path.join(cam_param_dir, 38 | 'fisheye_param_not_at_origin.json') 39 | fisheye_opencv_param = FisheyeCameraParameter() 40 | fisheye_opencv_param.load(fisheye_path) 41 | # test opencv to opencv 42 | converted_param = convert_camera_parameter( 43 | cam_param=fisheye_opencv_param, dst='opencv') 44 | assert np.allclose( 45 | np.asarray(converted_param.get_extrinsic_r()), 46 | np.asarray(fisheye_opencv_param.get_extrinsic_r())) 47 | # test opencv to blender, world2cam 48 | if not fisheye_opencv_param.world2cam: 49 | fisheye_opencv_param.inverse_extrinsic() 50 | cam_backup = fisheye_opencv_param.clone() 51 | converted_param = convert_camera_parameter( 52 | cam_param=fisheye_opencv_param, dst='blender') 53 | # direction not changed 54 | assert converted_param.world2cam == fisheye_opencv_param.world2cam 55 | # input not changed 56 | assert np.all( 57 | np.asarray(fisheye_opencv_param.get_extrinsic_r()) == np.asarray( 58 | cam_backup.get_extrinsic_r())) 59 | # test opencv to blender, cam2world 60 | if fisheye_opencv_param.world2cam: 61 | fisheye_opencv_param.inverse_extrinsic() 62 | cam_backup = fisheye_opencv_param.clone() 63 | converted_param = convert_camera_parameter( 64 | cam_param=fisheye_opencv_param, dst='blender') 65 | # direction not changed 66 | assert converted_param.world2cam == fisheye_opencv_param.world2cam 67 | # input not changed 68 | assert np.all( 69 | np.asarray(fisheye_opencv_param.get_extrinsic_r()) == np.asarray( 70 | cam_backup.get_extrinsic_r())) 71 | # type not changed 72 | assert type(fisheye_opencv_param) == type(converted_param) 73 | with pytest.raises(NotImplementedError): 74 | converted_param = convert_camera_parameter( 75 | cam_param=fisheye_opencv_param, dst='ue5') 76 | with pytest.raises(NotImplementedError): 77 | converted_param.convention = 'ue5' 78 | converted_param = convert_camera_parameter( 79 | cam_param=converted_param, dst='opencv') 80 | -------------------------------------------------------------------------------- /python/tests/transform/convention/test_keypoints_convention.py: -------------------------------------------------------------------------------- 1 | # yapf: disable 2 | import numpy as np 3 | import torch 4 | 5 | from xrprimer.data_structure.keypoints import Keypoints 6 | from xrprimer.transform.convention.keypoints_convention import ( 7 | convert_keypoints, 8 | get_keypoint_num, 9 | get_mapping_dict, 10 | ) 11 | 12 | # yapf: enable 13 | 14 | 15 | def test_get_mapping_dict(): 16 | map_dict = get_mapping_dict(src='human_data', dst='coco') 17 | assert len(map_dict) == get_keypoint_num(convention='coco') 18 | for k, v in map_dict.items(): 19 | assert k >= 0 and k < 190 20 | assert v >= 0 and v < get_keypoint_num(convention='coco') 21 | 22 | 23 | def test_convert_keypoints(): 24 | # test convert np 25 | kps_np = np.zeros(shape=(2, 3, 25, 3)) 26 | mask_np = np.ones(shape=(2, 3, 25)) 27 | convention = 'openpose_25' 28 | keypoints = Keypoints(kps=kps_np, mask=mask_np, convention=convention) 29 | assert isinstance(keypoints.get_keypoints(), np.ndarray) 30 | hd_keypoints = convert_keypoints(keypoints=keypoints, dst='human_data') 31 | assert isinstance(hd_keypoints.get_keypoints(), np.ndarray) 32 | single_mask = hd_keypoints.get_mask()[0, 0] 33 | assert single_mask.sum() == mask_np.shape[-1] 34 | # test convert torch 35 | keypoints = keypoints.to_tensor() 36 | assert isinstance(keypoints.get_keypoints(), torch.Tensor) 37 | hd_keypoints = convert_keypoints(keypoints=keypoints, dst='human_data') 38 | assert isinstance(hd_keypoints.get_keypoints(), torch.Tensor) 39 | single_mask = hd_keypoints.get_mask()[0, 0] 40 | assert single_mask.sum() == mask_np.shape[-1] 41 | -------------------------------------------------------------------------------- /python/tests/transform/test_image.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from xrprimer.transform.image.color import bgr2rgb 4 | 5 | 6 | def test_bgr2rgb(): 7 | # test numpy 8 | # one img 9 | rgb_image = np.zeros(shape=(3, 640, 480)) 10 | rgb_image[2, ...] = 2 11 | assert rgb_image[2, 0, 0] == 2 12 | bgr_image = bgr2rgb(rgb_image, color_dim=0) 13 | assert bgr_image[0, 0, 0] == 2 14 | assert bgr_image[2, 0, 0] == 0 15 | # pytorch batch like 16 | rgb_image = np.zeros(shape=(2, 3, 640, 480)) 17 | rgb_image[:, 2, ...] = 2 18 | assert rgb_image[0, 2, 0, 0] == 2 19 | bgr_image = bgr2rgb(rgb_image, color_dim=1) 20 | assert bgr_image[0, 0, 0, 0] == 2 21 | assert bgr_image[0, 2, 0, 0] == 0 22 | # opencv video like 23 | rgb_image = np.zeros(shape=(2, 640, 480, 3)) 24 | rgb_image[..., 2] = 2 25 | assert rgb_image[0, 0, 0, 2] == 2 26 | bgr_image = bgr2rgb(rgb_image, color_dim=-1) 27 | assert bgr_image[0, 0, 0, 0] == 2 28 | assert bgr_image[0, 0, 0, 2] == 0 29 | -------------------------------------------------------------------------------- /python/tests/transform/test_limbs_transform.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | import cv2 5 | import numpy as np 6 | import pytest 7 | 8 | from xrprimer.data_structure.keypoints import Keypoints 9 | from xrprimer.transform.limbs import get_limbs_from_keypoints 10 | 11 | input_dir = 'tests/data/transform/test_limbs' 12 | output_dir = 'tests/data/output/transform/test_limbs' 13 | 14 | 15 | @pytest.fixture(scope='module', autouse=True) 16 | def fixture(): 17 | if os.path.exists(output_dir): 18 | shutil.rmtree(output_dir) 19 | os.makedirs(output_dir, exist_ok=False) 20 | 21 | 22 | def test_get_limbs_from_keypoints(): 23 | # test get from numpy 24 | keypoints3d = Keypoints.fromfile( 25 | 'tests/data/ops/test_projection/keypoints3d.npz') 26 | limbs = get_limbs_from_keypoints(keypoints=keypoints3d) 27 | assert len(limbs) > 0 28 | assert limbs.get_points() is None 29 | # test get from torch 30 | keypoints3d_torch = keypoints3d.to_tensor() 31 | limbs = get_limbs_from_keypoints(keypoints=keypoints3d_torch) 32 | assert len(limbs) > 0 33 | assert len(limbs.get_parts()) > 0 34 | assert limbs.get_points() is None 35 | # test get with points 36 | limbs = get_limbs_from_keypoints( 37 | keypoints=keypoints3d, frame_idx=0, person_idx=0) 38 | assert limbs.get_points() is not None 39 | conn = limbs.get_connections() 40 | canvas = np.ones(shape=(1080, 1920, 3), dtype=np.uint8) 41 | points = limbs.get_points() 42 | for start_pt_idx, end_pt_idx in conn: 43 | cv2.line( 44 | img=canvas, 45 | pt1=points[start_pt_idx, :2].astype(np.int32), 46 | pt2=points[end_pt_idx, :2].astype(np.int32), 47 | color=(255, 0, 0), 48 | thickness=2) 49 | cv2.imwrite( 50 | filename=os.path.join(output_dir, 'limbs_from_keypoints.jpg'), 51 | img=canvas) 52 | # test get connection names 53 | limbs = get_limbs_from_keypoints( 54 | keypoints=keypoints3d, frame_idx=0, person_idx=0, fill_limb_names=True) 55 | conn_dict = limbs.get_connections_by_names() 56 | assert len(conn_dict) > 0 57 | -------------------------------------------------------------------------------- /python/tests/utils/test_log_utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import shutil 4 | 5 | import pytest 6 | 7 | from xrprimer.utils.log_utils import get_logger, setup_logger 8 | 9 | input_dir = 'tests/data/utils/test_log_utils' 10 | output_dir = 'tests/data/output/utils/test_log_utils' 11 | 12 | 13 | @pytest.fixture(scope='module', autouse=True) 14 | def fixture(): 15 | if os.path.exists(output_dir): 16 | shutil.rmtree(output_dir) 17 | os.makedirs(output_dir, exist_ok=False) 18 | 19 | 20 | def test_setup_logger(): 21 | # test name 22 | test_logger = setup_logger(logger_name='test_name') 23 | assert get_logger('test_name') == test_logger 24 | # test level 25 | test_logger = setup_logger( 26 | logger_name='test_level', logger_level=logging.DEBUG) 27 | assert test_logger.level == logging.DEBUG 28 | # test level 29 | test_logger = setup_logger( 30 | logger_name='test_path', 31 | logger_path=os.path.join(output_dir, 'log.txt')) 32 | test_logger.info('test msg') 33 | assert os.path.exists(os.path.join(output_dir, 'log.txt')) 34 | # test format 35 | test_logger = setup_logger( 36 | logger_name='test_path', 37 | logger_format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') 38 | test_logger.info('test msg') 39 | 40 | 41 | def test_get_logger(): 42 | # test None 43 | test_logger = get_logger() 44 | assert test_logger.name == 'root' 45 | # test name 46 | test_logger = get_logger('any') 47 | assert test_logger.name == 'any' 48 | # test logger type 49 | test_logger = get_logger(test_logger) 50 | assert test_logger.name == 'any' 51 | -------------------------------------------------------------------------------- /python/tests/utils/test_path_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | import pytest 5 | 6 | from xrprimer.utils.path_utils import ( 7 | Existence, 8 | check_path_existence, 9 | check_path_suffix, 10 | ) 11 | 12 | input_dir = 'tests/data/utils/test_path_utils' 13 | output_dir = 'tests/data/output/utils/test_path_utils' 14 | 15 | 16 | @pytest.fixture(scope='module', autouse=True) 17 | def fixture(): 18 | if os.path.exists(output_dir): 19 | shutil.rmtree(output_dir) 20 | os.makedirs(output_dir, exist_ok=False) 21 | 22 | 23 | def test_check_path_existence(): 24 | # test FileExist 25 | path = os.path.join(output_dir, 'FileExist.txt') 26 | with open(path, 'w') as f_write: 27 | f_write.write('\n') 28 | assert check_path_existence(path) == Existence.FileExist 29 | # test DirectoryExistEmpty 30 | path = os.path.join(output_dir, 'DirectoryExistEmpty') 31 | os.mkdir(path) 32 | assert check_path_existence(path) == Existence.DirectoryExistEmpty 33 | # test DirectoryExistNotEmpty 34 | path = os.path.join(output_dir, 'DirectoryExistNotEmpty') 35 | os.mkdir(path) 36 | with open(os.path.join(path, 'test_file.txt'), 'w') as f_write: 37 | f_write.write('\n') 38 | assert check_path_existence(path) == Existence.DirectoryExistNotEmpty 39 | # test MissingParent 40 | path = os.path.join(output_dir, 'MissingParent', 'test_file.txt') 41 | assert check_path_existence(path) == Existence.MissingParent 42 | # test DirectoryNotExist 43 | path = os.path.join(output_dir, 'DirectoryNotExist') 44 | assert check_path_existence(path) == Existence.DirectoryNotExist 45 | # test DirectoryNotExist 46 | path = os.path.join(output_dir, 'FileNotExist.txt') 47 | assert check_path_existence(path) == Existence.FileNotExist 48 | 49 | 50 | def test_check_path_suffix(): 51 | # test True 52 | path = os.path.join(output_dir, 'check_path_suffix.txt') 53 | with open(path, 'w') as f_write: 54 | f_write.write('\n') 55 | assert check_path_suffix(path, []) 56 | # test single str 57 | assert check_path_suffix(path, '.txt') 58 | # test list 59 | assert check_path_suffix(path, ['.txt']) 60 | assert check_path_suffix(path, ['.bin', '.txt']) 61 | # test upper case 62 | assert check_path_suffix(path, ['.bin', '.TXT']) 63 | # test dir 64 | path = os.path.join(output_dir, 'check_path_suffix_dir') 65 | os.mkdir(path) 66 | assert check_path_suffix(path, '') 67 | assert check_path_suffix(path, ['']) 68 | assert not check_path_suffix(path, ['.txt']) 69 | -------------------------------------------------------------------------------- /python/tests/visualization/matplotlib/test_plot_frame_plt.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | import cv2 5 | import numpy as np 6 | import pytest 7 | 8 | from xrprimer.visualization.matplotlib import plot_frame 9 | from xrprimer.visualization.palette.line_palette import LinePalette 10 | from xrprimer.visualization.palette.point_palette import PointPalette 11 | 12 | input_dir = 'tests/data/visualization/matplotlib/test_plot_frame' 13 | output_dir = 'tests/data/output/visualization/matplotlib/test_plot_frame' 14 | RGB_COLORS = dict( 15 | red=[255, 0, 0], 16 | green=[0, 255, 0], 17 | blue=[0, 0, 255], 18 | ) 19 | 20 | 21 | @pytest.fixture(scope='module', autouse=True) 22 | def fixture() -> None: 23 | if os.path.exists(output_dir): 24 | shutil.rmtree(output_dir) 25 | os.makedirs(output_dir, exist_ok=False) 26 | 27 | 28 | def test_plot_points() -> None: 29 | # test default marksize, R/G/B points 30 | start_locations = np.array([10.0, 10.0, 10.0]) 31 | visual_range = np.array([[0, 150], [0, 150], [0, 150]]) 32 | for color_str, color_list in RGB_COLORS.items(): 33 | location_list = [] 34 | for offset in [1, 5, 10, 20]: 35 | location = start_locations + 5 * np.array([offset, offset, offset]) 36 | location_list.append(location) 37 | point_palette = PointPalette( 38 | point_array=np.asarray(location_list), color_array=color_list) 39 | img_arr = plot_frame( 40 | point_palette=point_palette, visual_range=visual_range) 41 | cv2.imwrite( 42 | os.path.join(output_dir, f'{color_str}_manual_markersize.jpg'), 43 | img_arr) 44 | # test mask 45 | point_palette = PointPalette( 46 | point_array=[[20, 20, 1], [620, 20, 1], [20, 460, 1], [620, 460, 1]], 47 | color_array=[255, 0, 0]) 48 | img_arr = plot_frame(point_palette=point_palette) 49 | cv2.imwrite(os.path.join(output_dir, 'points_before_mask.jpg'), img_arr) 50 | point_palette.set_point_mask([0, 1, 1, 1]) 51 | img_arr = plot_frame(point_palette=point_palette) 52 | cv2.imwrite(os.path.join(output_dir, 'points_after_mask.jpg'), img_arr) 53 | 54 | 55 | def test_plot_lines() -> None: 56 | # test default linewidth, R/G/B lines 57 | for color_str, color_list in RGB_COLORS.items(): 58 | start_location = np.array([10.0, 10.0, 10.0]) 59 | offset = np.array([20, 20, 20]) 60 | point_array = np.array([start_location, start_location + offset]) 61 | line_palette = LinePalette( 62 | conn_array=[0, 1], point_array=point_array, color_array=color_list) 63 | img_arr = plot_frame(line_palette=line_palette) 64 | cv2.imwrite( 65 | os.path.join(output_dir, f'{color_str}_default_linewidth.jpg'), 66 | img_arr) 67 | # test mask 68 | line_palette = LinePalette( 69 | conn_array=[[0, 1], [2, 3]], 70 | point_array=[[20.0, 20.0, 1.0], [620.0, 20.0, 1.0], [20.0, 460.0, 1.0], 71 | [620.0, 460.0, 1.0]], 72 | color_array=[255, 0, 0]) 73 | img_arr = plot_frame(line_palette=line_palette) 74 | cv2.imwrite(os.path.join(output_dir, 'lines_before_mask.jpg'), img_arr) 75 | line_palette.set_conn_mask([0, 1]) 76 | img_arr = plot_frame(line_palette=line_palette) 77 | cv2.imwrite(os.path.join(output_dir, 'lines_after_mask.jpg'), img_arr) 78 | 79 | 80 | def test_points_and_lines() -> None: 81 | point_palette = PointPalette( 82 | point_array=[[300, 300, 300], [200, 400, 200], [400, 400, 400]], 83 | color_array=[255, 0, 0]) 84 | line_palette = LinePalette( 85 | conn_array=[[0, 1], [1, 2], [2, 0]], 86 | point_array=point_palette.point_array, 87 | color_array=[0, 255, 0]) 88 | img_arr = plot_frame( 89 | point_palette=point_palette, line_palette=line_palette) 90 | cv2.imwrite(os.path.join(output_dir, 'points_and_lines.jpg'), img_arr) 91 | -------------------------------------------------------------------------------- /python/xrprimer/__init__.py: -------------------------------------------------------------------------------- 1 | from .version import __version__ 2 | 3 | 4 | def digit_version(version_str): 5 | digit_version = [] 6 | for x in version_str.split('.'): 7 | if x.isdigit(): 8 | digit_version.append(int(x)) 9 | elif x.find('rc') != -1: 10 | patch_version = x.split('rc') 11 | digit_version.append(int(patch_version[0]) - 1) 12 | digit_version.append(int(patch_version[1])) 13 | return digit_version 14 | 15 | 16 | __all__ = ['__version__'] 17 | -------------------------------------------------------------------------------- /python/xrprimer/calibration/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_calibrator import BaseCalibrator 2 | from .mview_fisheye_calibrator import MviewFisheyeCalibrator 3 | from .mview_pinhole_calibrator import MviewPinholeCalibrator 4 | from .sview_fisheye_calibrator import SviewFisheyeDistortionCalibrator 5 | 6 | __all__ = [ 7 | 'BaseCalibrator', 'MviewFisheyeCalibrator', 'MviewPinholeCalibrator', 8 | 'SviewFisheyeDistortionCalibrator' 9 | ] 10 | -------------------------------------------------------------------------------- /python/xrprimer/calibration/base_calibrator.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Union 3 | 4 | from xrprimer.data_structure.camera import PinholeCameraParameter 5 | from xrprimer.utils.log_utils import get_logger 6 | 7 | 8 | class BaseCalibrator(): 9 | """Base class of camera calibrator.""" 10 | 11 | def __init__(self, 12 | work_dir: str = './temp', 13 | logger: Union[None, str, logging.Logger] = None) -> None: 14 | """Initialization for camera calibrator. 15 | 16 | Args: 17 | work_dir (str, optional): 18 | Path to the working dir for this instance. 19 | Defaults to './temp'. 20 | logger (Union[None, str, logging.Logger], optional): 21 | Logger for logging. If None, root logger will be selected. 22 | Defaults to None. 23 | """ 24 | self.logger = get_logger(logger) 25 | self.work_dir = work_dir 26 | 27 | def calibrate(self) -> PinholeCameraParameter: 28 | """Calibrate a camera or several cameras. Input args shall not be 29 | modified and the calibrated camera will be returned. 30 | 31 | Returns: 32 | PinholeCameraParameter: 33 | The calibrated camera. 34 | """ 35 | raise NotImplementedError 36 | -------------------------------------------------------------------------------- /python/xrprimer/calibration/builder.py: -------------------------------------------------------------------------------- 1 | from mmcv.utils import Registry 2 | 3 | from .base_calibrator import BaseCalibrator 4 | from .mview_fisheye_calibrator import MviewFisheyeCalibrator 5 | from .mview_pinhole_calibrator import MviewPinholeCalibrator 6 | from .sview_fisheye_calibrator import SviewFisheyeDistortionCalibrator 7 | 8 | CALIBRATORS = Registry('calibrator') 9 | CALIBRATORS.register_module( 10 | name='MviewPinholeCalibrator', module=MviewPinholeCalibrator) 11 | CALIBRATORS.register_module( 12 | name='MviewFisheyeCalibrator', module=MviewFisheyeCalibrator) 13 | CALIBRATORS.register_module( 14 | name='SviewFisheyeDistortionCalibrator', 15 | module=SviewFisheyeDistortionCalibrator) 16 | 17 | 18 | def build_calibrator(cfg) -> BaseCalibrator: 19 | """Build calibrator.""" 20 | return CALIBRATORS.build(cfg) 21 | -------------------------------------------------------------------------------- /python/xrprimer/data_structure/__init__.py: -------------------------------------------------------------------------------- 1 | from xrprimer_cpp import ( 2 | AngleAxisd, 3 | Pose, 4 | Quaterniond, 5 | VectorDouble, 6 | VectorFloat, 7 | VectorInt, 8 | VectorInt64, 9 | VectorPinholeCameraParameter, 10 | VectorUint8, 11 | ) 12 | from .keypoints import Keypoints 13 | from .limbs import Limbs 14 | 15 | __all__ = [ 16 | 'AngleAxisd', 'Pose', 'Quaterniond', 'VectorDouble', 'VectorFloat', 17 | 'VectorInt', 'VectorInt64', 'VectorPinholeCameraParameter', 'VectorUint8', 18 | 'Keypoints', 'Limbs' 19 | ] 20 | -------------------------------------------------------------------------------- /python/xrprimer/data_structure/camera/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/python/xrprimer/data_structure/camera/.gitkeep -------------------------------------------------------------------------------- /python/xrprimer/data_structure/camera/__init__.py: -------------------------------------------------------------------------------- 1 | from .camera import BaseCameraParameter 2 | from .fisheye_camera import FisheyeCameraParameter 3 | from .omni_camera import OmniCameraParameter 4 | from .pinhole_camera import PinholeCameraParameter 5 | 6 | __all__ = [ 7 | 'FisheyeCameraParameter', 'OmniCameraParameter', 'PinholeCameraParameter', 8 | 'BaseCameraParameter' 9 | ] 10 | -------------------------------------------------------------------------------- /python/xrprimer/data_structure/camera/pinhole_camera.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import logging 3 | from typing import Union 4 | 5 | from xrprimer_cpp.camera import \ 6 | PinholeCameraParameter as PinholeCameraParameter_cpp 7 | from .camera import BaseCameraParameter 8 | 9 | 10 | class PinholeCameraParameter(PinholeCameraParameter_cpp, BaseCameraParameter): 11 | ATTR_NAMES = BaseCameraParameter.ATTR_NAMES.copy() 12 | 13 | def __init__(self, 14 | K: Union[list, None] = None, 15 | R: Union[list, None] = None, 16 | T: Union[list, None] = None, 17 | name: str = 'default', 18 | height: int = 1080, 19 | width: int = 1920, 20 | world2cam: bool = True, 21 | convention: str = 'opencv', 22 | logger: Union[None, str, logging.Logger] = None) -> None: 23 | """A camera parameter class for pinhole camera model. This class has no 24 | distortion attributes and functions. 25 | 26 | Args: 27 | K (Union[list, np.ndarray, None], optional): 28 | Nested list of float32, 4x4 or 3x3 K mat. 29 | Defaults to None, 4x4 zeros. 30 | R (Union[list, np.ndarray, None], optional): 31 | Nested list of float32, 3x3 rotation mat. 32 | Defaults to None, 3x3 identity. 33 | T (Union[list, np.ndarray, None], optional): 34 | List of float32, T vector. 35 | Defaults to None, zero vector. 36 | name (str, optional): 37 | Name of this camera. Defaults to 'default'. 38 | height (int, optional): 39 | Height of the image shot by this camera. 40 | Defaults to 1080. 41 | width (int, optional): 42 | Width of the image shot by this camera. 43 | Defaults to 1920. 44 | world2cam (bool, optional): 45 | Whether the R, T transform points from world space 46 | to camera space. Defaults to True. 47 | convention (str, optional): 48 | Convention name of this camera. 49 | Defaults to 'opencv'. 50 | logger (Union[None, str, logging.Logger], optional): 51 | Logger for logging. If None, root logger will be selected. 52 | Defaults to None. 53 | """ 54 | PinholeCameraParameter_cpp.__init__(self) 55 | BaseCameraParameter.__init__( 56 | self, 57 | K=K, 58 | R=R, 59 | T=T, 60 | name=name, 61 | height=height, 62 | width=width, 63 | world2cam=world2cam, 64 | convention=convention, 65 | logger=logger) 66 | 67 | def clone(self) -> 'PinholeCameraParameter': 68 | """Clone a new CameraParameter instance like self. 69 | 70 | Returns: 71 | PinholeCameraParameter 72 | """ 73 | new_cam_param = self.__class__( 74 | K=copy.deepcopy(self.get_intrinsic(k_dim=4)), 75 | R=copy.deepcopy(self.extrinsic_r), 76 | T=copy.deepcopy(self.extrinsic_t), 77 | name=self.name, 78 | height=self.height, 79 | width=self.width, 80 | world2cam=self.world2cam, 81 | convention=self.convention, 82 | logger=self.logger) 83 | return new_cam_param 84 | 85 | def SaveFile(self, filename: str) -> bool: 86 | """Dump camera name and parameters to a json file. 87 | 88 | Args: 89 | filename (str): 90 | Path to the dumped json file. 91 | 92 | Returns: 93 | bool: True if save succeed. 94 | """ 95 | return PinholeCameraParameter_cpp.SaveFile(self, filename) 96 | 97 | def LoadFile(self, filename: str) -> bool: 98 | """Load camera name and parameters from a dumped json file. 99 | 100 | Args: 101 | filename (str): 102 | Path to the dumped json file. 103 | 104 | Returns: 105 | bool: True if load succeed. 106 | """ 107 | return PinholeCameraParameter_cpp.LoadFile(self, filename) 108 | -------------------------------------------------------------------------------- /python/xrprimer/io/exr_reader.py: -------------------------------------------------------------------------------- 1 | """Utils for exr format data. 2 | 3 | Requirements: requirements/synbody.txt 4 | 5 | If you encounter any problems with openexr installation, 6 | refer to the following link: 7 | https://github.com/AcademySoftwareFoundation/openexr/blob/main/INSTALL.md 8 | """ 9 | from pathlib import Path 10 | from typing import List, Tuple, Union 11 | 12 | import numpy as np 13 | 14 | try: 15 | import Imath 16 | import OpenEXR 17 | has_exr = True 18 | import_exception = '' 19 | except (ImportError, ModuleNotFoundError): 20 | has_exr = False 21 | import traceback 22 | stack_str = '' 23 | for line in traceback.format_stack(): 24 | if 'frozen' not in line: 25 | stack_str += line + '\n' 26 | import_exception = traceback.format_exc() + '\n' 27 | import_exception = stack_str + import_exception 28 | 29 | 30 | class ExrReader: 31 | """Load `.exr` format file.""" 32 | 33 | def __init__(self, exr_path: Union[str, Path]): 34 | """Initialize with a `.exr` format file. 35 | 36 | Args: 37 | exr_path (PathLike): path to `.exr` format file 38 | """ 39 | if not has_exr: 40 | print(import_exception) 41 | print('warning: please install Imath and OpenEXR' 42 | ' in order to read .exr format files.') 43 | raise ImportError 44 | file_ = OpenEXR.InputFile(str(exr_path)) 45 | dw = file_.header()['dataWindow'] 46 | size = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1) 47 | self.file = file_ 48 | self.size: Tuple[int, int] = size 49 | 50 | @property 51 | def channels(self) -> List[str]: 52 | """Get channels in the exr file. 53 | 54 | Returns: 55 | List[str]: list of channel names 56 | """ 57 | return self.file.header()['channels'] 58 | 59 | def read_channel(self, channel: str) -> np.ndarray: 60 | """Read channel's data. 61 | 62 | Args: 63 | channel (str): channel's name 64 | 65 | Returns: 66 | np.ndarray: channel's data in np.ndarray format with shape (H, W) 67 | """ 68 | ChannelType = self.file.header()['channels'][channel] 69 | if ChannelType == Imath.Channel(Imath.PixelType(Imath.PixelType.HALF)): 70 | PixType = Imath.PixelType(Imath.PixelType.HALF) 71 | dtype = np.float16 72 | elif ChannelType == Imath.Channel( 73 | Imath.PixelType(Imath.PixelType.FLOAT)): 74 | PixType = Imath.PixelType(Imath.PixelType.FLOAT) 75 | dtype = np.float32 76 | else: 77 | raise ValueError('please specify PixelType') 78 | 79 | img = np.frombuffer(self.file.channel(channel, PixType), dtype=dtype) 80 | img = np.reshape(img, (self.size[1], self.size[0])).astype(np.float32) 81 | return img 82 | -------------------------------------------------------------------------------- /python/xrprimer/ops/__init__.py: -------------------------------------------------------------------------------- 1 | from xrprimer_cpp.ops import prior_guided_pnp 2 | 3 | __all__ = ['prior_guided_pnp'] 4 | -------------------------------------------------------------------------------- /python/xrprimer/ops/projection/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_projector import BaseProjector 2 | from .opencv_projector import OpencvProjector 3 | 4 | __all__ = ['BaseProjector', 'OpencvProjector'] 5 | -------------------------------------------------------------------------------- /python/xrprimer/ops/projection/builder.py: -------------------------------------------------------------------------------- 1 | from mmcv.utils import Registry 2 | 3 | from .base_projector import BaseProjector 4 | from .opencv_projector import OpencvProjector 5 | 6 | PROJECTORS = Registry('projector') 7 | PROJECTORS.register_module(name='OpencvProjector', module=OpencvProjector) 8 | 9 | 10 | def build_projector(cfg) -> BaseProjector: 11 | """Build projector.""" 12 | return PROJECTORS.build(cfg) 13 | -------------------------------------------------------------------------------- /python/xrprimer/ops/triangulation/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_triangulator import BaseTriangulator 2 | from .opencv_triangulator import OpencvTriangulator 3 | 4 | __all__ = ['BaseTriangulator', 'OpencvTriangulator'] 5 | -------------------------------------------------------------------------------- /python/xrprimer/ops/triangulation/builder.py: -------------------------------------------------------------------------------- 1 | from mmcv.utils import Registry 2 | 3 | from .base_triangulator import BaseTriangulator 4 | from .opencv_triangulator import OpencvTriangulator 5 | 6 | TRIANGULATORS = Registry('triangulator') 7 | TRIANGULATORS.register_module( 8 | name='OpencvTriangulator', module=OpencvTriangulator) 9 | 10 | 11 | def build_triangulator(cfg) -> BaseTriangulator: 12 | """Build triangulator.""" 13 | return TRIANGULATORS.build(cfg) 14 | -------------------------------------------------------------------------------- /python/xrprimer/transform/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/python/xrprimer/transform/__init__.py -------------------------------------------------------------------------------- /python/xrprimer/transform/camera/__init__.py: -------------------------------------------------------------------------------- 1 | from .distortion import undistort_camera, undistort_images, undistort_points 2 | from .extrinsic import rotate_camera, translate_camera 3 | 4 | __all__ = [ 5 | 'undistort_camera', 'undistort_images', 'undistort_points', 6 | 'rotate_camera', 'translate_camera' 7 | ] 8 | -------------------------------------------------------------------------------- /python/xrprimer/transform/camera/extrinsic.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | import numpy as np 4 | 5 | from xrprimer.data_structure.camera import ( 6 | FisheyeCameraParameter, 7 | PinholeCameraParameter, 8 | ) 9 | 10 | 11 | def rotate_camera( 12 | cam_param: Union[PinholeCameraParameter, 13 | FisheyeCameraParameter], rotation_mat: np.ndarray 14 | ) -> Union[PinholeCameraParameter, FisheyeCameraParameter]: 15 | """Apply rotation to a camera parameter. 16 | 17 | Args: 18 | cam_param (Union[PinholeCameraParameter, FisheyeCameraParameter]): 19 | The camera to rotate. 20 | rotation_mat (np.ndarray): 21 | Rotation matrix defined in world space, 22 | shape [3, 3]. 23 | 24 | Returns: 25 | Union[PinholeCameraParameter, FisheyeCameraParameter]: 26 | Rotated camera in same type and extrinsic direction 27 | like the input camera. 28 | """ 29 | input_w2c = cam_param.world2cam 30 | ret_cam_param = cam_param.clone() 31 | if input_w2c: 32 | ret_cam_param.inverse_extrinsic() 33 | cam_loc = np.asarray(ret_cam_param.get_extrinsic_t()) 34 | cam_loc = np.matmul(rotation_mat, cam_loc) 35 | c2w_rot = np.asarray(ret_cam_param.get_extrinsic_r()) 36 | c2w_rot = np.matmul(rotation_mat, c2w_rot) 37 | ret_cam_param.set_KRT(R=c2w_rot, T=cam_loc, world2cam=False) 38 | if ret_cam_param.world2cam != input_w2c: 39 | ret_cam_param.inverse_extrinsic() 40 | return ret_cam_param 41 | 42 | 43 | def translate_camera( 44 | cam_param: Union[PinholeCameraParameter, 45 | FisheyeCameraParameter], translation: np.ndarray 46 | ) -> Union[PinholeCameraParameter, FisheyeCameraParameter]: 47 | """Apply the translation to a camera parameter. 48 | 49 | Args: 50 | cam_param (Union[PinholeCameraParameter, FisheyeCameraParameter]): 51 | The camera to translate. 52 | translation (np.ndarray): 53 | Translation vector defined in world space, 54 | shape [3,]. 55 | 56 | Returns: 57 | Union[PinholeCameraParameter, FisheyeCameraParameter]: 58 | Translated camera in same type and extrinsic direction 59 | like the input camera. 60 | """ 61 | input_w2c = cam_param.world2cam 62 | ret_cam_param = cam_param.clone() 63 | if input_w2c: 64 | ret_cam_param.inverse_extrinsic() 65 | cam_loc = np.asarray(ret_cam_param.get_extrinsic_t()) 66 | cam_loc += translation 67 | ret_cam_param.set_KRT(T=cam_loc, world2cam=False) 68 | if ret_cam_param.world2cam != input_w2c: 69 | ret_cam_param.inverse_extrinsic() 70 | return ret_cam_param 71 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/python/xrprimer/transform/convention/__init__.py -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/camera/__init__.py: -------------------------------------------------------------------------------- 1 | from xrprimer.data_structure.camera.camera import BaseCameraParameter 2 | from .from_opencv import convert_camera_from_opencv 3 | from .intrinsic import downgrade_k_4x4, upgrade_k_3x3 4 | from .to_opencv import convert_camera_to_opencv 5 | 6 | __all__ = ['upgrade_k_3x3', 'downgrade_k_4x4', 'convert_camera_parameter'] 7 | 8 | 9 | def convert_camera_parameter( 10 | cam_param: BaseCameraParameter, 11 | dst: str, 12 | ) -> BaseCameraParameter: 13 | """Convert a camera parameter instance into opencv convention. 14 | 15 | Args: 16 | cam_param (BaseCameraParameter): 17 | The input camera parameter, which is an instance of 18 | BaseCameraParameter subclass. 19 | dst (str): 20 | The name of destination convention. 21 | 22 | Returns: 23 | BaseCameraParameter: 24 | A camera in the same type as input, whose 25 | direction is same as cam_param, and convention 26 | equals to dst. 27 | """ 28 | if cam_param.convention == dst: 29 | return cam_param 30 | else: 31 | opencv_cam = convert_camera_to_opencv(cam_param) 32 | dst_cam = convert_camera_from_opencv(opencv_cam, dst) 33 | if dst_cam.world2cam != cam_param.world2cam: 34 | dst_cam.inverse_extrinsic() 35 | return dst_cam 36 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/camera/from_opencv.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from xrprimer.data_structure.camera.camera import BaseCameraParameter 4 | 5 | 6 | def convert_camera_from_opencv(cam_param: BaseCameraParameter, 7 | dst: str) -> BaseCameraParameter: 8 | """Convert a camera parameter instance from opencv convention into another 9 | convention. 10 | 11 | Args: 12 | cam_param (BaseCameraParameter): 13 | The input camera parameter defined in opencv convention, 14 | which is an instance of BaseCameraParameter subclass. 15 | dst (str): 16 | The name of destination convention. 17 | 18 | Raises: 19 | NotImplementedError: 20 | dst convention has not been supported. 21 | 22 | Returns: 23 | BaseCameraParameter: 24 | A camera in the same type as input, whose 25 | direction is cam2world and and convention 26 | equals to dst. 27 | """ 28 | assert cam_param.convention == 'opencv' 29 | # do not modify cam_param, modify the cloned cam 30 | cam_param_backup = cam_param 31 | cam_param = cam_param_backup.clone() 32 | if cam_param.world2cam is True: 33 | cam_param.inverse_extrinsic() 34 | if dst == 'opencv': 35 | return cam_param 36 | elif dst == 'blender': 37 | # rotation of euler zxy, (0, 180, 0) 38 | rot_mat = np.array([[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], 39 | [0.0, 0.0, -1.0]]) 40 | extrinsic_r = np.asarray(cam_param.get_extrinsic_r()) 41 | extrinsic_r = np.matmul(rot_mat, extrinsic_r) 42 | cam_param.set_KRT(R=extrinsic_r) 43 | cam_param.convention = 'blender' 44 | return cam_param 45 | elif dst == 'unreal': 46 | cam_location = np.asarray(cam_param.get_extrinsic_t()) 47 | # right hand to left hand 48 | cam_location *= np.asarray((-1, 1, 1)) 49 | # rotation of euler zxy, (270, 0, 90) 50 | rot_mat = np.array([[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], 51 | [0.0, -1.0, 0.0]]) 52 | extrinsic_r = np.asarray(cam_param.get_extrinsic_r()) 53 | extrinsic_r = np.matmul(rot_mat, extrinsic_r) 54 | cam_param.set_KRT(R=extrinsic_r, T=cam_location) 55 | cam_param.convention = 'unreal' 56 | return cam_param 57 | else: 58 | cam_param.logger.error(f'Converting a camera from opencv to {dst}' + 59 | ' has not been supported yet.') 60 | raise NotImplementedError 61 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/camera/intrinsic.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def upgrade_k_3x3(k: np.ndarray, is_perspective: bool = True) -> np.ndarray: 5 | """Convert opencv 3x3 intrinsic matrix to 4x4. 6 | 7 | Args: 8 | K (np.ndarray): Input 3x3 intrinsic matrix, left mm defined. 9 | 10 | [[fx, 0, px], 11 | [0, fy, py], 12 | [0, 0, 1]] 13 | 14 | is_perspective (bool, optional): whether is perspective projection. 15 | Defaults to True. 16 | 17 | Returns: 18 | np.ndarray: Output intrinsic matrix. 19 | 20 | for perspective: 21 | [[fx, 0, px, 0], 22 | [0, fy, py, 0], 23 | [0, 0, 0, 1], 24 | [0, 0, 1, 0]] 25 | 26 | for orthographics: 27 | [[fx, 0, 0, px], 28 | [0, fy, 0, py], 29 | [0, 0, 1, 0], 30 | [0, 0, 0, 1]] 31 | """ 32 | k_batch = k.reshape(-1, 3, 3) 33 | if is_perspective: 34 | k_ret = np.zeros((k_batch.shape[0], 4, 4), dtype=k_batch.dtype) 35 | k_ret[:, :2, :3] = k_batch[:, :2, :3] 36 | k_ret[:, 3, 2] = 1 37 | k_ret[:, 2, 3] = 1 38 | else: 39 | k_ret = np.zeros((k_batch.shape[0], 4, 4), dtype=k_batch.dtype) 40 | k_ret[:, :2, :2] = k_batch[:, :2, :2] 41 | k_ret[:, :2, 3:] = k_batch[:, :2, 2:] 42 | k_ret[:, 2, 2] = 1 43 | k_ret[:, 3, 3] = 1 44 | ret_shape = [4, 4] 45 | for dim_index in range(k.ndim - 3, -1, -1): 46 | ret_shape.insert(0, k.shape[dim_index]) 47 | return k_ret.reshape(*ret_shape) 48 | 49 | 50 | def downgrade_k_4x4(k: np.ndarray) -> np.ndarray: 51 | """Convert opencv 4x4 intrinsic matrix to 3x3. 52 | 53 | Args: 54 | K (np.ndarray): 55 | Input 4x4 intrinsic matrix, left mm defined. 56 | 57 | Returns: 58 | np.ndarray: Output 3x3 intrinsic matrix, left mm defined. 59 | 60 | [[fx, 0, px], 61 | [0, fy, py], 62 | [0, 0, 1]] 63 | """ 64 | k_batch = k.reshape(-1, 4, 4) 65 | is_perspective = (k_batch[0, 2, 3] == k_batch[0, 3, 2]) 66 | if is_perspective: 67 | k_ret = np.zeros((k_batch.shape[0], 3, 3), dtype=k_batch.dtype) 68 | k_ret[:, :2, :3] = k_batch[:, :2, :3] 69 | k_ret[:, 2, 2] = 1 70 | else: 71 | k_ret = np.zeros((k_batch.shape[0], 3, 3), dtype=k_batch.dtype) 72 | k_ret[:, :2, :2] = k_batch[:, :2, :2] 73 | k_ret[:, :2, 2:3] = k_batch[:, :2, 3:4] 74 | k_ret[:, 2, 2] = 1 75 | ret_shape = [3, 3] 76 | for dim_index in range(k.ndim - 3, -1, -1): 77 | ret_shape.insert(0, k.shape[dim_index]) 78 | return k_ret.reshape(*ret_shape) 79 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/camera/to_opencv.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from xrprimer.data_structure.camera.camera import BaseCameraParameter 4 | 5 | 6 | def convert_camera_to_opencv( 7 | cam_param: BaseCameraParameter) -> BaseCameraParameter: 8 | """Convert a camera parameter instance into opencv convention. 9 | 10 | Args: 11 | cam_param (BaseCameraParameter): 12 | The input camera parameter, which is an instance of 13 | BaseCameraParameter subclass. 14 | 15 | Raises: 16 | NotImplementedError: 17 | cam_param.convention has not been supported. 18 | 19 | Returns: 20 | BaseCameraParameter: 21 | A camera in the same type as input, whose 22 | direction is cam2world and convention 23 | is 'opencv'. 24 | """ 25 | # do not modify cam_param, modify the cloned cam 26 | cam_param_backup = cam_param 27 | cam_param = cam_param_backup.clone() 28 | if cam_param.world2cam is True: 29 | cam_param.inverse_extrinsic() 30 | if cam_param.convention == 'opencv': 31 | return cam_param 32 | elif cam_param.convention == 'blender': 33 | # rotation of euler zxy, (0, 180, 0) 34 | rot_mat = np.array([[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], 35 | [0.0, 0.0, -1.0]]) 36 | extrinsic_r = np.asarray(cam_param.get_extrinsic_r()) 37 | extrinsic_r = np.matmul(rot_mat, extrinsic_r) 38 | cam_param.set_KRT(R=extrinsic_r) 39 | cam_param.convention = 'opencv' 40 | return cam_param 41 | elif cam_param.convention == 'unreal': 42 | cam_location = np.asarray(cam_param.get_extrinsic_t()) 43 | # left hand to right hand 44 | cam_location *= np.asarray((-1, 1, 1)) 45 | # rotation of euler zxy, (90, 270, 0) 46 | rot_mat = np.array([[0.0, -1.0, 0.0], [0.0, 0.0, 1.0], 47 | [-1.0, 0.0, 0.0]]) 48 | extrinsic_r = np.asarray(cam_param.get_extrinsic_r()) 49 | extrinsic_r = np.matmul(rot_mat, extrinsic_r) 50 | cam_param.set_KRT(R=extrinsic_r, T=cam_location) 51 | cam_param.convention = 'opencv' 52 | return cam_param 53 | else: 54 | cam_param.logger.error( 55 | f'Converting a camera from {cam_param.convention} to opencv' + 56 | ' has not been supported yet.') 57 | raise NotImplementedError 58 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/agora.py: -------------------------------------------------------------------------------- 1 | AGORA_KEYPOINTS = [ 2 | 'pelvis', 3 | 'left_hip', 4 | 'right_hip', 5 | 'spine_1', 6 | 'left_knee', 7 | 'right_knee', 8 | 'spine_2', 9 | 'left_ankle', 10 | 'right_ankle', 11 | 'spine_3', 12 | 'left_foot', 13 | 'right_foot', 14 | 'neck', 15 | 'left_collar', 16 | 'right_collar', 17 | 'head', 18 | 'left_shoulder', 19 | 'right_shoulder', 20 | 'left_elbow', 21 | 'right_elbow', 22 | 'left_wrist', 23 | 'right_wrist', 24 | 'jaw', 25 | 'left_eyeball', 26 | 'right_eyeball', 27 | 'left_index_1', 28 | 'left_index_2', 29 | 'left_index_3', 30 | 'left_middle_1', 31 | 'left_middle_2', 32 | 'left_middle_3', 33 | 'left_pinky_1', 34 | 'left_pinky_2', 35 | 'left_pinky_3', 36 | 'left_ring_1', 37 | 'left_ring_2', 38 | 'left_ring_3', 39 | 'left_thumb_1', 40 | 'left_thumb_2', 41 | 'left_thumb_3', 42 | 'right_index_1', 43 | 'right_index_2', 44 | 'right_index_3', 45 | 'right_middle_1', 46 | 'right_middle_2', 47 | 'right_middle_3', 48 | 'right_pinky_1', 49 | 'right_pinky_2', 50 | 'right_pinky_3', 51 | 'right_ring_1', 52 | 'right_ring_2', 53 | 'right_ring_3', 54 | 'right_thumb_1', 55 | 'right_thumb_2', 56 | 'right_thumb_3', 57 | 'nose', 58 | 'right_eye', 59 | 'left_eye', 60 | 'right_ear', 61 | 'left_ear', 62 | 'left_bigtoe', 63 | 'left_smalltoe', 64 | 'left_heel', 65 | 'right_bigtoe', 66 | 'right_smalltoe', 67 | 'right_heel', 68 | 'left_thumb', 69 | 'left_index', 70 | 'left_middle', 71 | 'left_ring', 72 | 'left_pinky', 73 | 'right_thumb', 74 | 'right_index', 75 | 'right_middle', 76 | 'right_ring', 77 | 'right_pinky', 78 | 'right_eyebrow_1', 79 | 'right_eyebrow_2', 80 | 'right_eyebrow_3', 81 | 'right_eyebrow_4', 82 | 'right_eyebrow_5', 83 | 'left_eyebrow_5', 84 | 'left_eyebrow_4', 85 | 'left_eyebrow_3', 86 | 'left_eyebrow_2', 87 | 'left_eyebrow_1', 88 | 'nosebridge_1', 89 | 'nosebridge_2', 90 | 'nosebridge_3', 91 | 'nosebridge_4', 92 | 'right_nose_2', # original name: nose_1 93 | 'right_nose_1', # original name: nose_2 94 | 'nose_middle', # original name: nose_3 95 | 'left_nose_1', # original name: nose_4 96 | 'left_nose_2', # original name: nose_5 97 | 'right_eye_1', 98 | 'right_eye_2', 99 | 'right_eye_3', 100 | 'right_eye_4', 101 | 'right_eye_5', 102 | 'right_eye_6', 103 | 'left_eye_4', 104 | 'left_eye_3', 105 | 'left_eye_2', 106 | 'left_eye_1', 107 | 'left_eye_6', 108 | 'left_eye_5', 109 | 'right_mouth_1', # original name: mouth_1 110 | 'right_mouth_2', # original name: mouth_2 111 | 'right_mouth_3', # original name: mouth_3 112 | 'mouth_top', # original name: mouth_4 113 | 'left_mouth_3', # original name: mouth_5 114 | 'left_mouth_2', # original name: mouth_6 115 | 'left_mouth_1', # original name: mouth_7 116 | 'left_mouth_5', # original name: mouth_8 117 | 'left_mouth_4', # original name: mouth_9 118 | 'mouth_bottom', # original name: mouth_10 119 | 'right_mouth_4', # original name: mouth_11 120 | 'right_mouth_5', # original name: mouth_12 121 | 'right_lip_1', # original name: lip_1 122 | 'right_lip_2', # original name: lip_2 123 | 'lip_top', # original name: lip_3 124 | 'left_lip_2', # original name: lip_4 125 | 'left_lip_1', # original name: lip_5 126 | 'left_lip_3', # original name: lip_6 127 | 'lip_bottom', # original name: lip_7 128 | 'right_lip_3', # original name: lip_8 129 | ] 130 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/campus.py: -------------------------------------------------------------------------------- 1 | CAMPUS_KEYPOINTS = [ 2 | 'right_ankle', 'right_knee', 'right_hip_extra', 'left_hip_extra', 3 | 'left_knee', 'left_ankle', 'right_wrist', 'right_elbow', 'right_shoulder', 4 | 'left_shoulder', 'left_elbow', 'left_wrist', 'jaw', 'headtop' 5 | ] 6 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/coco.py: -------------------------------------------------------------------------------- 1 | COCO_KEYPOINTS = [ 2 | 'nose', 3 | 'left_eye', 4 | 'right_eye', 5 | 'left_ear', 6 | 'right_ear', 7 | 'left_shoulder', 8 | 'right_shoulder', 9 | 'left_elbow', 10 | 'right_elbow', 11 | 'left_wrist', 12 | 'right_wrist', 13 | 'left_hip_extra', 14 | 'right_hip_extra', 15 | 'left_knee', 16 | 'right_knee', 17 | 'left_ankle', 18 | 'right_ankle', 19 | ] 20 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/crowdpose.py: -------------------------------------------------------------------------------- 1 | CROWDPOSE_KEYPOINTS = [ 2 | 'left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow', 3 | 'left_wrist', 'right_wrist', 'left_hip', 'right_hip', 'left_knee', 4 | 'right_knee', 'left_ankle', 'right_ankle', 'head', 'neck' 5 | ] 6 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/face3d.py: -------------------------------------------------------------------------------- 1 | FACE3D_IND = [ 2 | 'right_eye_1', 'right_eye_4', 'left_eye_4', 'left_eye_1', 'nose_middle', 3 | 'right_mouth_1', 'left_mouth_1' 4 | ] 5 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/flame.py: -------------------------------------------------------------------------------- 1 | FLAME_73_KEYPOINTS = [ 2 | 'head', 3 | 'neck', 4 | 'jaw', 5 | 'left_eye', 6 | 'right_eye', 7 | 'right_eyebrow_1', 8 | 'right_eyebrow_2', 9 | 'right_eyebrow_3', 10 | 'right_eyebrow_4', 11 | 'right_eyebrow_5', 12 | 'left_eyebrow_5', 13 | 'left_eyebrow_4', 14 | 'left_eyebrow_3', 15 | 'left_eyebrow_2', 16 | 'left_eyebrow_1', 17 | 'nosebridge_1', 18 | 'nosebridge_2', 19 | 'nosebridge_3', 20 | 'nosebridge_4', 21 | 'right_nose_2', 22 | 'right_nose_1', 23 | 'nose_middle', 24 | 'left_nose_1', 25 | 'left_nose_2', 26 | 'right_eye_1', 27 | 'right_eye_2', 28 | 'right_eye_3', 29 | 'right_eye_4', 30 | 'right_eye_5', 31 | 'right_eye_6', 32 | 'left_eye_4', 33 | 'left_eye_3', 34 | 'left_eye_2', 35 | 'left_eye_1', 36 | 'left_eye_6', 37 | 'left_eye_5', 38 | 'right_mouth_1', 39 | 'right_mouth_2', 40 | 'right_mouth_3', 41 | 'mouth_top', 42 | 'left_mouth_3', 43 | 'left_mouth_2', 44 | 'left_mouth_1', 45 | 'left_mouth_5', 46 | 'left_mouth_4', 47 | 'mouth_bottom', 48 | 'right_mouth_4', 49 | 'right_mouth_5', 50 | 'right_lip_1', 51 | 'right_lip_2', 52 | 'lip_top', 53 | 'left_lip_2', 54 | 'left_lip_1', 55 | 'left_lip_3', 56 | 'lip_bottom', 57 | 'right_lip_3', 58 | 'right_contour_1', 59 | 'right_contour_2', 60 | 'right_contour_3', 61 | 'right_contour_4', 62 | 'right_contour_5', 63 | 'right_contour_6', 64 | 'right_contour_7', 65 | 'right_contour_8', 66 | 'contour_middle', 67 | 'left_contour_8', 68 | 'left_contour_7', 69 | 'left_contour_6', 70 | 'left_contour_5', 71 | 'left_contour_4', 72 | 'left_contour_3', 73 | 'left_contour_2', 74 | 'left_contour_1', 75 | ] 76 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/h36m.py: -------------------------------------------------------------------------------- 1 | H36M_KEYPOINTS = [ 2 | 'pelvis_extra', 3 | 'left_hip_extra', 4 | 'left_knee', 5 | 'left_ankle', 6 | 'right_hip_extra', 7 | 'right_knee', 8 | 'right_ankle', 9 | 'spine_extra', 10 | 'neck_extra', 11 | 'head_extra', 12 | 'headtop', 13 | 'left_shoulder', 14 | 'left_elbow', 15 | 'left_wrist', 16 | 'right_shoulder', 17 | 'right_elbow', 18 | 'right_wrist', 19 | ] 20 | 21 | H36M_KEYPOINTS_MMPOSE = [ 22 | 'pelvis_extra', 23 | 'right_hip_extra', 24 | 'right_knee', 25 | 'right_ankle', 26 | 'left_hip_extra', 27 | 'left_knee', 28 | 'left_ankle', 29 | 'spine_extra', 30 | 'neck_extra', 31 | 'head_extra', 32 | 'headtop', 33 | 'left_shoulder', 34 | 'left_elbow', 35 | 'left_wrist', 36 | 'right_shoulder', 37 | 'right_elbow', 38 | 'right_wrist', 39 | ] 40 | 41 | H36M_KEYPOINTS_SMPLX = [ 42 | 'pelvis', 43 | 'left_hip', 44 | 'left_knee', 45 | 'left_ankle', 46 | 'right_hip', 47 | 'right_knee', 48 | 'right_ankle', 49 | 'spine', 50 | 'neck', # 'thorax', 51 | 'neck/nose', 52 | 'head', # 'head_h36m', 53 | 'left_shoulder', 54 | 'left_elbow', 55 | 'left_wrist', 56 | 'right_shoulder', 57 | 'right_elbow', 58 | 'right_wrist' 59 | ] 60 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/hybrik.py: -------------------------------------------------------------------------------- 1 | HYBRIK_29_KEYPOINTS = [ 2 | 'pelvis', 3 | 'left_hip', 4 | 'right_hip', # 2 5 | 'spine_1', 6 | 'left_knee', 7 | 'right_knee', # 5 8 | 'spine_2', 9 | 'left_ankle', 10 | 'right_ankle', # 8 11 | 'spine_3', 12 | 'left_foot', 13 | 'right_foot', # 11 14 | 'neck', 15 | 'left_collar', 16 | 'right_collar', # 14 17 | 'jaw', # 15 18 | 'left_shoulder', 19 | 'right_shoulder', # 17 20 | 'left_elbow', 21 | 'right_elbow', # 19 22 | 'left_wrist', 23 | 'right_wrist', # 21 24 | 'left_thumb', 25 | 'right_thumb', # 23 26 | 'head', 27 | 'left_middle', 28 | 'right_middle', # 26 29 | 'left_bigtoe', 30 | 'right_bigtoe' # 28 31 | ] 32 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/instavariety.py: -------------------------------------------------------------------------------- 1 | INSTAVARIETY_KEYPOINTS = [ 2 | 'right_heel_openpose', 3 | 'right_knee_openpose', 4 | 'right_hip_openpose', 5 | 'left_hip_openpose', 6 | 'left_knee_openpose', 7 | 'left_heel_openpose', 8 | 'right_wrist_openpose', 9 | 'right_elbow_openpose', 10 | 'right_shoulder_openpose', 11 | 'left_shoulder_openpose', 12 | 'left_elbow_openpose', 13 | 'left_wrist_openpose', 14 | 'neck_openpose', 15 | 'headtop', 16 | 'nose_openpose', 17 | 'left_eye_openpose', 18 | 'right_eye_openpose', 19 | 'left_ear_openpose', 20 | 'right_ear_openpose', 21 | 'left_bigtoe_openpose', 22 | 'right_bigtoe_openpose', 23 | 'left_smalltoe_openpose', 24 | 'right_smalltoe_openpose', 25 | 'left_ankle_openpose', 26 | 'right_ankle_openpose', 27 | ] 28 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/lsp.py: -------------------------------------------------------------------------------- 1 | LSP_KEYPOINTS = [ 2 | 'right_ankle', 3 | 'right_knee', 4 | 'right_hip_extra', 5 | 'left_hip_extra', 6 | 'left_knee', 7 | 'left_ankle', 8 | 'right_wrist', 9 | 'right_elbow', 10 | 'right_shoulder', 11 | 'left_shoulder', 12 | 'left_elbow', 13 | 'left_wrist', 14 | 'neck_extra', 15 | 'headtop', 16 | ] 17 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/mano.py: -------------------------------------------------------------------------------- 1 | # Original order from MANO J_regressor 2 | MANO_RIGHT_KEYPOINTS = [ 3 | 'right_wrist', 'right_index_1', 'right_index_2', 'right_index_3', 4 | 'right_middle_1', 'right_middle_2', 'right_middle_3', 'right_pinky_1', 5 | 'right_pinky_2', 'right_pinky_3', 'right_ring_1', 'right_ring_2', 6 | 'right_ring_3', 'right_thumb_1', 'right_thumb_2', 'right_thumb_3', 7 | 'right_thumb', 'right_index', 'right_middle', 'right_ring', 'right_pinky' 8 | ] 9 | 10 | MANO_LEFT_KEYPOINTS = [ 11 | x.replace('right_', 'left_') for x in MANO_RIGHT_KEYPOINTS 12 | ] 13 | 14 | # Re-arranged order is compatible with the output of manolayer 15 | # from official [manopth](https://github.com/hassony2/manopth) 16 | MANO_REORDER_MAP = [ 17 | 0, 13, 14, 15, 16, 1, 2, 3, 17, 4, 5, 6, 18, 10, 11, 12, 19, 7, 8, 9, 20 18 | ] 19 | 20 | MANO_RIGHT_REORDER_KEYPOINTS = [ 21 | MANO_RIGHT_KEYPOINTS[i] for i in MANO_REORDER_MAP 22 | ] 23 | MANO_LEFT_REORDER_KEYPOINTS = [ 24 | MANO_LEFT_KEYPOINTS[i] for i in MANO_REORDER_MAP 25 | ] 26 | 27 | # Deprecated: reserved for backward compatibility 28 | MANO_KEYPOINTS = MANO_RIGHT_KEYPOINTS 29 | # Two hands (left + right) 30 | MANO_HANDS_KEYPOINTS = MANO_LEFT_KEYPOINTS + MANO_RIGHT_KEYPOINTS 31 | # Reordered two hands (left + right) 32 | MANO_HANDS_REORDER_KEYPOINTS = \ 33 | MANO_LEFT_REORDER_KEYPOINTS + MANO_RIGHT_REORDER_KEYPOINTS 34 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/mediapipe.py: -------------------------------------------------------------------------------- 1 | from .mano import MANO_HANDS_REORDER_KEYPOINTS 2 | 3 | # mediapipe pose convention defined in 4 | # https://google.github.io/mediapipe/solutions/pose.html 5 | MP_BODY_KEYPOINTS = [ 6 | 'nose', 7 | 'left_eye_4', 8 | 'left_eye', 9 | 'left_eye_1', 10 | 'right_eye_4', 11 | 'right_eye', 12 | 'right_eye_1', 13 | 'left_ear', 14 | 'right_ear', 15 | 'left_mouth_1', 16 | 'right_mouth_1', 17 | 'left_shoulder', 18 | 'right_shoulder', 19 | 'left_elbow', 20 | 'right_elbow', 21 | 'left_wrist', 22 | 'right_wrist', 23 | 'left_pinky_1', 24 | 'right_pinky_1', 25 | 'left_index_1', 26 | 'right_index_1', 27 | 'left_thumb_1', 28 | 'right_thumb_1', 29 | 'left_hip', 30 | 'right_hip', 31 | 'left_knee', 32 | 'right_knee', 33 | 'left_ankle', 34 | 'right_ankle', 35 | 'left_heel', 36 | 'right_heel', 37 | 'left_foot', 38 | 'right_foot', 39 | ] 40 | 41 | # remove hand keypoints in mediapipe pose 42 | MP_WHOLE_BODY_KEYPOINTS = [ 43 | 'nose', 44 | 'left_eye_4', 45 | 'left_eye', 46 | 'left_eye_1', 47 | 'right_eye_4', 48 | 'right_eye', 49 | 'right_eye_1', 50 | 'left_ear', 51 | 'right_ear', 52 | 'left_mouth_1', 53 | 'right_mouth_1', 54 | 'left_shoulder', 55 | 'right_shoulder', 56 | 'left_elbow', 57 | 'right_elbow', 58 | 'left_hip', 59 | 'right_hip', 60 | 'left_knee', 61 | 'right_knee', 62 | 'left_ankle', 63 | 'right_ankle', 64 | 'left_heel', 65 | 'right_heel', 66 | 'left_foot', 67 | 'right_foot', 68 | ] 69 | 70 | # mediapipe keypoint of body and hands 71 | MP_WHOLE_BODY_KEYPOINTS =\ 72 | MP_WHOLE_BODY_KEYPOINTS + MANO_HANDS_REORDER_KEYPOINTS 73 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/mpi_inf_3dhp.py: -------------------------------------------------------------------------------- 1 | MPI_INF_3DHP_KEYPOINTS = [ 2 | 'spine_3', 3 | 'spine_4_3dhp', 4 | 'spine_2', 5 | 'spine_extra', # close to spine2 6 | 'pelvis_extra', 7 | 'neck_extra', # throat 8 | 'head_extra', 9 | 'headtop', 10 | 'left_clavicle_3dhp', 11 | 'left_shoulder', 12 | 'left_elbow', 13 | 'left_wrist', 14 | 'left_hand_3dhp', 15 | 'right_clavicle_3dhp', 16 | 'right_shoulder', 17 | 'right_elbow', 18 | 'right_wrist', 19 | 'right_hand_3dhp', 20 | 'left_hip_extra', 21 | 'left_knee', 22 | 'left_ankle', 23 | 'left_foot', 24 | 'left_toe_3dhp', 25 | 'right_hip_extra', 26 | 'right_knee', 27 | 'right_ankle', 28 | 'right_foot', 29 | 'right_toe_3dhp' 30 | ] 31 | 32 | MPI_INF_3DHP_TEST_KEYPOINTS = [ 33 | 'headtop', 34 | 'neck_extra', 35 | 'right_shoulder', 36 | 'right_elbow', 37 | 'right_wrist', 38 | 'left_shoulder', 39 | 'left_elbow', 40 | 'left_wrist', 41 | 'right_hip_extra', 42 | 'right_knee', 43 | 'right_ankle', 44 | 'left_hip_extra', 45 | 'left_knee', 46 | 'left_ankle', 47 | 'pelvis_extra', 48 | 'spine_extra', # close to spine2 49 | 'head_extra' 50 | ] 51 | 52 | HYBRIK_MPI_INF_3DHP_KEYPOINTS = [ 53 | 'spine_3', 54 | 'spine_4_3dhp', 55 | 'spine_2', 56 | 'spine_extra', # close to spine2 57 | 'pelvis', 58 | 'neck', # throat 59 | 'head_extra', 60 | 'headtop', 61 | 'left_clavicle_3dhp', 62 | 'left_shoulder', 63 | 'left_elbow', 64 | 'left_wrist', 65 | 'left_hand_3dhp', 66 | 'right_clavicle_3dhp', 67 | 'right_shoulder', 68 | 'right_elbow', 69 | 'right_wrist', 70 | 'right_hand_3dhp', 71 | 'left_hip', 72 | 'left_knee', 73 | 'left_ankle', 74 | 'left_foot', 75 | 'left_toe_3dhp', 76 | 'right_hip', 77 | 'right_knee', 78 | 'right_ankle', 79 | 'right_foot', 80 | 'right_toe_3dhp' 81 | ] 82 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/mpii.py: -------------------------------------------------------------------------------- 1 | MPII_KEYPOINTS = [ 2 | 'right_ankle', 3 | 'right_knee', 4 | 'right_hip_extra', 5 | 'left_hip_extra', 6 | 'left_knee', 7 | 'left_ankle', 8 | 'pelvis_extra', 9 | 'thorax_extra', 10 | 'neck_extra', 11 | 'headtop', 12 | 'right_wrist', 13 | 'right_elbow', 14 | 'right_shoulder', 15 | 'left_shoulder', 16 | 'left_elbow', 17 | 'left_wrist', 18 | ] 19 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/panoptic.py: -------------------------------------------------------------------------------- 1 | PANOPTIC_KEYPOINTS = [ 2 | 'neck_openpose', 3 | 'nose_openpose', 4 | 'pelvis_openpose', 5 | 'left_shoulder_openpose', 6 | 'left_elbow_openpose', 7 | 'left_wrist_openpose', 8 | 'left_hip_openpose', 9 | 'left_knee_openpose', 10 | 'left_ankle_openpose', 11 | 'right_shoulder_openpose', 12 | 'right_elbow_openpose', 13 | 'right_wrist_openpose', 14 | 'right_hip_openpose', 15 | 'right_knee_openpose', 16 | 'right_ankle_openpose', 17 | 'left_eye_openpose', 18 | 'left_ear_openpose', 19 | 'right_eye_openpose', 20 | 'right_ear_openpose', 21 | ] 22 | 23 | PANOPTIC15_KEYPOINTS = [ 24 | 'neck_openpose', 25 | 'nose_openpose', 26 | 'pelvis_openpose', 27 | 'left_shoulder_openpose', 28 | 'left_elbow_openpose', 29 | 'left_wrist_openpose', 30 | 'left_hip_openpose', 31 | 'left_knee_openpose', 32 | 'left_ankle_openpose', 33 | 'right_shoulder_openpose', 34 | 'right_elbow_openpose', 35 | 'right_wrist_openpose', 36 | 'right_hip_openpose', 37 | 'right_knee_openpose', 38 | 'right_ankle_openpose', 39 | ] 40 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/penn_action.py: -------------------------------------------------------------------------------- 1 | PENN_ACTION_KEYPOINTS = [ 2 | 'head', 3 | 'left_shoulder', 4 | 'right_shoulder', 5 | 'left_elbow', 6 | 'right_elbow', 7 | 'left_wrist', 8 | 'right_wrist', 9 | 'left_hip', 10 | 'right_hip', 11 | 'left_knee', 12 | 'right_knee', 13 | 'left_ankle', 14 | 'right_ankle', 15 | ] 16 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/posetrack.py: -------------------------------------------------------------------------------- 1 | POSETRACK_KEYPOINTS = [ 2 | 'nose', 'head_bottom_pt', 'headtop', 'left_ear', 'right_ear', 3 | 'left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow', 4 | 'left_wrist', 'right_wrist', 'left_hip', 'right_hip', 'left_knee', 5 | 'right_knee', 'left_ankle', 'right_ankle' 6 | ] 7 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/pw3d.py: -------------------------------------------------------------------------------- 1 | PW3D_KEYPOINTS = [ 2 | 'nose', 3 | 'neck_extra', 4 | 'right_shoulder', 5 | 'right_elbow', 6 | 'right_wrist', 7 | 'left_shoulder', 8 | 'left_elbow', 9 | 'left_wrist', 10 | 'right_hip_extra', 11 | 'right_knee', 12 | 'right_ankle', 13 | 'left_hip_extra', 14 | 'left_knee', 15 | 'left_ankle', 16 | 'right_eye', 17 | 'left_eye', 18 | 'right_ear', 19 | 'left_ear', 20 | ] 21 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/smpl.py: -------------------------------------------------------------------------------- 1 | # the keypoints defined in the SMPL paper 2 | SMPL_KEYPOINTS = [ 3 | 'pelvis', 4 | 'left_hip', 5 | 'right_hip', 6 | 'spine_1', 7 | 'left_knee', 8 | 'right_knee', 9 | 'spine_2', 10 | 'left_ankle', 11 | 'right_ankle', 12 | 'spine_3', 13 | 'left_foot', 14 | 'right_foot', 15 | 'neck', 16 | 'left_collar', 17 | 'right_collar', 18 | 'head', 19 | 'left_shoulder', 20 | 'right_shoulder', 21 | 'left_elbow', 22 | 'right_elbow', 23 | 'left_wrist', 24 | 'right_wrist', 25 | 'left_hand', 26 | 'right_hand', 27 | ] 28 | 29 | # the full keypoints produced by the default SMPL J_regressor 30 | SMPL_45_KEYPOINTS = SMPL_KEYPOINTS + [ 31 | 'nose', 32 | 'right_eye', 33 | 'left_eye', 34 | 'right_ear', 35 | 'left_ear', 36 | 'left_bigtoe', 37 | 'left_smalltoe', 38 | 'left_heel', 39 | 'right_bigtoe', 40 | 'right_smalltoe', 41 | 'right_heel', 42 | 'left_thumb', 43 | 'left_index', 44 | 'left_middle', 45 | 'left_ring', 46 | 'left_pinky', 47 | 'right_thumb', 48 | 'right_index', 49 | 'right_middle', 50 | 'right_ring', 51 | 'right_pinky', 52 | ] 53 | 54 | # the full keypoints produced by the default SMPL J_regressor and 55 | # extra_J_regressor (provided by SPIN) 56 | SMPL_54_KEYPOINTS = SMPL_45_KEYPOINTS + [ 57 | 'right_hip_extra', # LSP 58 | 'left_hip_extra', # LSP 59 | 'neck_extra', # LSP 60 | 'headtop', # LSP 61 | 'pelvis_extra', # MPII 62 | 'thorax_extra', # MPII 63 | 'spine_extra', # H36M 64 | 'jaw_extra', # H36M 65 | 'head_extra', # H36M 66 | ] 67 | 68 | # SMPL keypoint convention used by SPIN, EFT and so on 69 | SMPL_49_KEYPOINTS = [ 70 | # OpenPose 71 | 'nose_openpose', 72 | 'neck_openpose', # 'upper_neck' 73 | 'right_shoulder_openpose', 74 | 'right_elbow_openpose', 75 | 'right_wrist_openpose', 76 | 'left_shoulder_openpose', 77 | 'left_elbow_openpose', 78 | 'left_wrist_openpose', 79 | 'pelvis_openpose', 80 | 'right_hip_openpose', 81 | 'right_knee_openpose', 82 | 'right_ankle_openpose', 83 | 'left_hip_openpose', 84 | 'left_knee_openpose', 85 | 'left_ankle_openpose', 86 | 'right_eye_openpose', 87 | 'left_eye_openpose', 88 | 'right_ear_openpose', 89 | 'left_ear_openpose', 90 | 'left_bigtoe_openpose', 91 | 'left_smalltoe_openpose', 92 | 'left_heel_openpose', 93 | 'right_bigtoe_openpose', 94 | 'right_smalltoe_openpose', 95 | 'right_heel_openpose', 96 | # 24 Keypoints 97 | 'right_ankle', 98 | 'right_knee', 99 | 'right_hip_extra', # LSP 100 | 'left_hip_extra', # LSP 101 | 'left_knee', 102 | 'left_ankle', 103 | 'right_wrist', 104 | 'right_elbow', 105 | 'right_shoulder', 106 | 'left_shoulder', 107 | 'left_elbow', 108 | 'left_wrist', 109 | 'neck_extra', # LSP 110 | 'headtop', # LSP mpii peen_action mpi_inf_3dhp 111 | 'pelvis_extra', # MPII 112 | 'thorax_extra', # MPII 113 | 'spine_extra', # H36M 114 | 'jaw_extra', # H36M 115 | 'head_extra', # H36M 116 | 'nose', 117 | 'left_eye', 118 | 'right_eye', 119 | 'left_ear', 120 | 'right_ear' 121 | ] 122 | 123 | SMPL_24_KEYPOINTS = SMPL_49_KEYPOINTS[-24:] 124 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/spin_smplx.py: -------------------------------------------------------------------------------- 1 | """SPIN in smplx convention. 2 | 3 | SPIN_SMPLX_KEYPOINTS can be found in https://github.com/vchoutas/expose 4 | """ 5 | 6 | # TODO: SMPL_24->HumanData->SMPLX causes hip, spine to be lost. 7 | # SMPL_24: left/right_hip_extra 8 | # SMPLX: left/right_hip 9 | 10 | SPIN_SMPLX_KEYPOINTS = [ 11 | 'right_ankle', 12 | 'right_knee', 13 | 'right_hip', 14 | 'left_hip', 15 | 'left_knee', 16 | 'left_ankle', 17 | 'right_wrist', 18 | 'right_elbow', 19 | 'right_shoulder', 20 | 'left_shoulder', 21 | 'left_elbow', 22 | 'left_wrist', 23 | 'neck', 24 | 'head_top', 25 | 'pelvis', 26 | 'thorax', 27 | 'spine', 28 | 'h36m_jaw', 29 | 'h36m_head', 30 | 'nose', 31 | 'left_eye', 32 | 'right_eye', 33 | 'left_ear', 34 | 'right_ear', 35 | ] 36 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/keypoints_convention/star.py: -------------------------------------------------------------------------------- 1 | STAR_KEYPOINTS = [ 2 | 'pelvis', 3 | 'left_hip', 4 | 'right_hip', 5 | 'spine_1', 6 | 'left_knee', 7 | 'right_knee', 8 | 'spine_2', 9 | 'left_ankle', 10 | 'right_ankle', 11 | 'spine_3', 12 | 'left_foot', 13 | 'right_foot', 14 | 'neck', 15 | 'left_collar', 16 | 'right_collar', 17 | 'head', 18 | 'left_shoulder', 19 | 'right_shoulder', 20 | 'left_elbow', 21 | 'right_elbow', 22 | 'left_wrist', 23 | 'right_wrist', 24 | 'left_hand', 25 | 'right_hand', 26 | ] 27 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/world/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar 2 | 3 | import numpy as np 4 | 5 | from .base_world import BaseWorld 6 | from .plt_world import MatplotlibWorld 7 | from .smc_world import SenseMoCapWorld 8 | 9 | __all__ = ['BaseWorld', 'MatplotlibWorld', 'SenseMoCapWorld'] 10 | 11 | WorldClass = TypeVar('WorldClass') 12 | 13 | 14 | def convert_world(src_world: WorldClass, dst_world: WorldClass) -> np.ndarray: 15 | """Get a rotation matrix converting vectors from src_world to dst_world. 16 | 17 | Args: 18 | src_world (WorldClass): 19 | The source world. 20 | dst_world (WorldClass): 21 | The destination world. 22 | Returns: 23 | np.ndarray: 24 | The rotation matrix from src_world to dst_world. 25 | """ 26 | return dst_world.ROTATION @ src_world.ROTATION.T 27 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/world/base_world.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class BaseWorld: 5 | """A world class for world coordinate convention. 6 | 7 | By using the rotation rotation matrix of this world(left multiplication), 8 | 3D data can be transformed from base world space to the current space. 9 | """ 10 | ROTATION = np.eye(3) 11 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/world/plt_world.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from .base_world import BaseWorld 4 | 5 | 6 | class MatplotlibWorld(BaseWorld): 7 | """A world class for Matplotlib coordinate convention. 8 | 9 | A human subject in this word faces to z- and his head is up towards y-. By 10 | using the rotation rotation matrix of this world(left multiplication), 3D 11 | data can be transformed from base world space to the current space. 12 | """ 13 | # rotation mat of euler_zxy(0, 270, 0) 14 | ROTATION = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]], dtype=np.float32) 15 | -------------------------------------------------------------------------------- /python/xrprimer/transform/convention/world/smc_world.py: -------------------------------------------------------------------------------- 1 | from .base_world import BaseWorld 2 | 3 | 4 | class SenseMoCapWorld(BaseWorld): 5 | """A world class for SenseMoCap coordinate convention. 6 | 7 | A human subject in this word faces to somewhere on xOz plane and his head 8 | is up towards y-. By using the rotation rotation matrix of this world(left 9 | multiplication), 3D data can be transformed from base world space to the 10 | current space. 11 | """ 12 | ROTATION = BaseWorld.ROTATION 13 | -------------------------------------------------------------------------------- /python/xrprimer/transform/image/__init__.py: -------------------------------------------------------------------------------- 1 | from .color import bgr2rgb, rgb2bgr 2 | 3 | __all__ = ['bgr2rgb', 'rgb2bgr'] 4 | -------------------------------------------------------------------------------- /python/xrprimer/transform/point/__init__.py: -------------------------------------------------------------------------------- 1 | from .rotation import Points3DRotation 2 | 3 | __all__ = ['Points3DRotation'] 4 | -------------------------------------------------------------------------------- /python/xrprimer/transform/point/rotation.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | import numpy as np 4 | 5 | from xrprimer.utils.log_utils import get_logger, logging 6 | 7 | try: 8 | import torch 9 | has_torch = True 10 | import_exception = '' 11 | except (ImportError, ModuleNotFoundError): 12 | has_torch = False 13 | import traceback 14 | stack_str = '' 15 | for line in traceback.format_stack(): 16 | if 'frozen' not in line: 17 | stack_str += line + '\n' 18 | import_exception = traceback.format_exc() + '\n' 19 | import_exception = stack_str + import_exception 20 | 21 | 22 | class Points3DRotation: 23 | """Rotation for 3D points or 3D vectors.""" 24 | 25 | def __init__(self, 26 | rotmat: Union[np.ndarray, 'torch.Tensor'], 27 | left_mm: bool = True, 28 | logger: Union[None, str, logging.Logger] = None) -> None: 29 | """ 30 | Args: 31 | rotmat (Union[np.ndarray, torch.Tensor]): 32 | Rotation matrix in shape [3, 3]. 33 | left_mm (bool, optional): 34 | Whether to do left multiplication. 35 | Defaults to True. 36 | logger (Union[None, str, logging.Logger], optional): 37 | Logger for logging. If None, root logger will be selected. 38 | Defaults to None. 39 | 40 | """ 41 | self.rotmat = rotmat 42 | self.left_mm = left_mm 43 | self.logger = get_logger(logger) 44 | if not has_torch: 45 | self.logger.error(import_exception) 46 | raise ImportError 47 | 48 | def __call__( 49 | self, points3d: Union[np.ndarray, 'torch.Tensor'] 50 | ) -> Union[np.ndarray, 'torch.Tensor']: 51 | """Rotate 3D points or 3D vectors. 52 | 53 | Args: 54 | points3d (Union[np.ndarray, torch.Tensor]): 55 | 3D points or 3D vectors in shape [..., 3]. 56 | """ 57 | shape_backup = points3d.shape 58 | if shape_backup[-1] != 3: 59 | self.logger.error('The last dimension of 3D data should be 3.') 60 | raise ValueError 61 | flat_data = _copy_array_tensor(points3d).reshape(-1, 3) 62 | if self.left_mm: 63 | rotated_data = (self.rotmat @ flat_data.T).T 64 | else: 65 | self.logger.error('Right multiplication is not supported yet.') 66 | raise NotImplementedError 67 | rotated_data = rotated_data.reshape(*shape_backup) 68 | return rotated_data 69 | 70 | 71 | def _copy_array_tensor( 72 | data: Union[np.ndarray, 73 | 'torch.Tensor']) -> Union[np.ndarray, 'torch.Tensor']: 74 | if isinstance(data, np.ndarray): 75 | return data.copy() 76 | else: 77 | return data.clone() 78 | -------------------------------------------------------------------------------- /python/xrprimer/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from xrprimer.utils.ffmpeg_utils import ( 2 | VideoInfoReader, 3 | VideoWriter, 4 | array_to_images, 5 | array_to_video, 6 | images_to_array, 7 | images_to_array_opencv, 8 | images_to_sorted_images, 9 | pad_for_libx264, 10 | video_to_array, 11 | ) 12 | from xrprimer.utils.log_utils import get_logger, setup_logger 13 | from xrprimer.utils.path_utils import ( 14 | Existence, 15 | check_path, 16 | check_path_existence, 17 | check_path_suffix, 18 | prepare_output_path, 19 | ) 20 | 21 | __all__ = [ 22 | 'Existence', 'VideoInfoReader', 'VideoWriter', 'array_to_images', 23 | 'array_to_video', 'check_path', 'check_path_existence', 24 | 'check_path_suffix', 'get_logger', 'images_to_array', 25 | 'images_to_array_opencv', 'images_to_sorted_images', 'pad_for_libx264', 26 | 'prepare_output_path', 'setup_logger', 'video_to_array' 27 | ] 28 | -------------------------------------------------------------------------------- /python/xrprimer/utils/log_utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Union 3 | 4 | logging.basicConfig( 5 | level=logging.INFO, 6 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') 7 | 8 | 9 | def setup_logger(logger_name: str = 'root', 10 | file_level: int = logging.INFO, 11 | console_level: int = logging.INFO, 12 | logger_level: Union[int, None] = None, 13 | logger_path: str = None, 14 | logger_format: str = None) -> logging.Logger: 15 | """Set up a logger. 16 | 17 | Args: 18 | logger_name (str, optional): 19 | Name of the logger. Defaults to 'root'. 20 | file_level (int, optional): 21 | Set the logging level of file stream. 22 | Defaults to logging.INFO. 23 | console_level (int, optional): 24 | Set the logging level of console stream. 25 | Defaults to logging.INFO. 26 | logger_level (Union[int, None], optional): 27 | Set the logging level of this logger, 28 | level of every stream will be overwritten 29 | if logger_level is set. 30 | This argument has been deprecated. 31 | Defaults to None. 32 | logger_path (str, optional): 33 | Path to the log file. 34 | Defaults to None, no file will be written, 35 | StreamHandler will be used. 36 | logger_format (str, optional): 37 | The formatter for logger handler. 38 | Defaults to None. 39 | 40 | Returns: 41 | logging.Logger: 42 | A logger with settings above. 43 | """ 44 | logger = logging.getLogger(logger_name) 45 | level_candidates = [file_level, console_level] 46 | if logger_level is not None: 47 | level_candidates = [ 48 | logger_level, 49 | ] 50 | console_level = logger_level 51 | file_level = logger_level 52 | min_level = min(level_candidates) 53 | logger.setLevel(level=min_level) 54 | # prevent logging twice in stdout 55 | logger.propagate = False 56 | stream_handler = logging.StreamHandler() 57 | stream_handler.setLevel(console_level) 58 | handlers = [stream_handler] 59 | if logger_path is not None: 60 | handler = logging.FileHandler(logger_path) 61 | handler.setLevel(file_level) 62 | handlers.append(handler) 63 | if logger_format is not None: 64 | formatter = logging.Formatter(logger_format) 65 | else: 66 | formatter = logging.Formatter( 67 | '%(asctime)s - %(name)s - %(levelname)s - %(message)s') 68 | # assure handlers are not double 69 | while logger.hasHandlers(): 70 | logger.removeHandler(logger.handlers[0]) 71 | for handler in handlers: 72 | handler.setFormatter(formatter) 73 | logger.addHandler(handler) 74 | if logger_level is not None: 75 | logger.warning('UserWarning: logger_level is now deprecated, ' + 76 | 'please specify file_level/console_level instead.') 77 | return logger 78 | 79 | 80 | def get_logger( 81 | logger: Union[None, str, logging.Logger] = None) -> logging.Logger: 82 | """Get logger. 83 | 84 | Args: 85 | logger (Union[None, str, logging.Logger]): 86 | None for root logger. Besides, pass name of the 87 | logger or the logger itself. 88 | Defaults to None. 89 | 90 | Returns: 91 | logging.Logger 92 | """ 93 | if logger is None or isinstance(logger, str): 94 | ret_logger = logging.getLogger(logger) 95 | else: 96 | ret_logger = logger 97 | return ret_logger 98 | -------------------------------------------------------------------------------- /python/xrprimer/version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenXRLab. All rights reserved. 2 | from xrprimer_cpp.common import ( 3 | get_version_major, 4 | get_version_minor, 5 | get_version_patch, 6 | get_version_string, 7 | ) 8 | 9 | 10 | def parse_version_info(version_str): 11 | """Parse a version string into a tuple. 12 | 13 | Args: 14 | version_str (str): The version string. 15 | Returns: 16 | tuple[int | str]: The version info, e.g., "1.3.0" is parsed into 17 | (1, 3, 0), and "2.0.0rc1" is parsed into (2, 0, 0, 'rc1'). 18 | """ 19 | version_info = [] 20 | for x in version_str.split('.'): 21 | if x.isdigit(): 22 | version_info.append(int(x)) 23 | elif x.find('rc') != -1: 24 | patch_version = x.split('rc') 25 | version_info.append(int(patch_version[0])) 26 | version_info.append(f'rc{patch_version[1]}') 27 | return tuple(version_info) 28 | 29 | 30 | __version__ = get_version_string() 31 | version_info = (get_version_major(), get_version_minor(), get_version_patch()) 32 | 33 | __all__ = ['__version__', 'version_info', 'parse_version_info'] 34 | -------------------------------------------------------------------------------- /python/xrprimer/visualization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/python/xrprimer/visualization/__init__.py -------------------------------------------------------------------------------- /python/xrprimer/visualization/keypoints/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/python/xrprimer/visualization/keypoints/__init__.py -------------------------------------------------------------------------------- /python/xrprimer/visualization/matplotlib/__init__.py: -------------------------------------------------------------------------------- 1 | from .plot_frame import plot_frame 2 | from .plot_video import plot_video 3 | 4 | __all__ = ['plot_frame', 'plot_video'] 5 | -------------------------------------------------------------------------------- /python/xrprimer/visualization/opencv/__init__.py: -------------------------------------------------------------------------------- 1 | from .plot_frame import plot_frame 2 | from .plot_video import plot_video 3 | 4 | __all__ = ['plot_frame', 'plot_video'] 5 | -------------------------------------------------------------------------------- /python/xrprimer/visualization/palette/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Union 3 | 4 | import numpy as np 5 | 6 | from xrprimer.utils.log_utils import get_logger 7 | from .line_palette import LinePalette 8 | from .point_palette import PointPalette 9 | 10 | try: 11 | from typing import Literal 12 | except ImportError: 13 | from typing_extensions import Literal 14 | try: 15 | import colorsys 16 | has_colorsys = True 17 | import_exception = '' 18 | except (ImportError, ModuleNotFoundError): 19 | has_colorsys = False 20 | import traceback 21 | stack_str = '' 22 | for line in traceback.format_stack(): 23 | if 'frozen' not in line: 24 | stack_str += line + '\n' 25 | import_exception = traceback.format_exc() + '\n' 26 | import_exception = stack_str + import_exception 27 | 28 | __all__ = ['PointPalette', 'LinePalette', 'get_different_colors'] 29 | 30 | 31 | def get_different_colors( 32 | number_of_colors: int, 33 | enable_random: bool = False, 34 | alpha: float = 1.0, 35 | mode: Literal['rgb', 'rgba', 'bgr', 'bgra'] = 'rgb', 36 | logger: Union[None, str, logging.Logger] = None) -> np.ndarray: 37 | """Get n different colors in one array. The distance between any two colors 38 | will not be too close. 39 | 40 | Args: 41 | number_of_colors (int): 42 | How many colors to get. 43 | enable_random (bool, optional): 44 | Whether to enable random adjustment for base colors. 45 | Defaults to False. 46 | alpha (float, optional): 47 | Value of the alpha. 48 | Defaults to 1.0. 49 | mode (Literal['rgb', 'rgba', 'bgr', 'bgra'], optional): 50 | Color mode in str. 51 | Defaults to 'rgb'. 52 | 53 | Returns: 54 | np.ndarray: 55 | An array of colors in [n_color, 3] or [n_color, 4]. 56 | """ 57 | if not has_colorsys: 58 | logger = get_logger(logger) 59 | logger.error(import_exception) 60 | raise ImportError 61 | mode = mode.lower() 62 | assert set(mode).issubset({'r', 'g', 'b', 'a'}) 63 | colors = [] 64 | for i in np.arange(0.0, 360.0, 360.0 / number_of_colors): 65 | hue = i / 360.0 66 | lightness_offset = np.random.rand() if enable_random else 0 67 | saturation_offset = np.random.rand() if enable_random else 0 68 | lightness = (50 + lightness_offset * 10) / 100. 69 | saturation = (90 + saturation_offset * 10) / 100. 70 | colors.append(colorsys.hls_to_rgb(hue, lightness, saturation)) 71 | colors_bgr = (np.asarray(colors) * 255).astype(np.uint8) 72 | color_dict = {} 73 | if 'a' in mode: 74 | color_dict['a'] = np.ones((number_of_colors, 1)) * alpha 75 | color_dict['b'] = colors_bgr[:, 0:1] 76 | color_dict['g'] = colors_bgr[:, 1:2] 77 | color_dict['r'] = colors_bgr[:, 2:3] 78 | colors_final = [] 79 | for channel in mode: 80 | colors_final.append(color_dict[channel]) 81 | colors_final = np.concatenate(colors_final, -1) 82 | return colors_final 83 | -------------------------------------------------------------------------------- /python/xrprimer/visualization/presets/__init__.py: -------------------------------------------------------------------------------- 1 | from .axis import create_coordinate_axis 2 | 3 | __all__ = ['create_coordinate_axis'] 4 | -------------------------------------------------------------------------------- /python/xrprimer/visualization/presets/axis.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | import numpy as np 4 | 5 | from xrprimer.utils.log_utils import get_logger, logging 6 | from ..palette import LinePalette 7 | 8 | 9 | def create_coordinate_axis( 10 | axis_length: float = 1.0, 11 | location: Union[np.ndarray, None] = None, 12 | logger: Union[None, str, logging.Logger] = None) -> LinePalette: 13 | """Create a coordinate axis at the given location, scaled to the given 14 | size, and return an instance of LinePalette. Colors of each axis are 15 | (red-x, green-y, blue-z) respectively. 16 | 17 | Args: 18 | axis_length (float, optional): 19 | Length of each axis. Defaults to 1.0. 20 | location (Union[np.ndarray, None], optional): 21 | Location of the coordinate axis. Defaults to None, 22 | the axis will be placed at the origin. 23 | logger (Union[None, str, logging.Logger], optional): 24 | Logger for logging. If None, root logger will be selected. 25 | Defaults to None. 26 | 27 | Returns: 28 | LinePalette: 29 | A LinePalette instance of the coordinate axis. 30 | """ 31 | logger = get_logger(logger) 32 | point_array = np.array(((0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)), 33 | dtype=np.float32) 34 | # scale the axis 35 | point_array *= axis_length 36 | # translate the axis according to location 37 | if location is not None: 38 | location = location.reshape(-1) 39 | if location.shape[0] != 3: 40 | logger.error( 41 | f'location should be in shape[3], but got {location.shape}') 42 | raise ValueError 43 | point_array += location 44 | axis_palette = LinePalette( 45 | conn_array=np.array(((0, 1), (0, 2), (0, 3))), 46 | point_array=point_array, 47 | name='coordinate_axis_line_palette', 48 | color_array=np.array(((255, 0, 0), (0, 255, 0), (0, 0, 255))), 49 | logger=logger) 50 | return axis_palette 51 | -------------------------------------------------------------------------------- /requirements/docs.txt: -------------------------------------------------------------------------------- 1 | docutils==0.16.0 2 | myst-parser 3 | sphinx==4.0.2 4 | sphinx-copybutton 5 | sphinx_markdown_tables 6 | sphinx_rtd_theme==0.5.2 7 | -------------------------------------------------------------------------------- /requirements/readthedocs.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | opencv-python 3 | -------------------------------------------------------------------------------- /requirements/runtime.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | -------------------------------------------------------------------------------- /requirements/synbody.txt: -------------------------------------------------------------------------------- 1 | imath 2 | numpy 3 | openexr 4 | scipy 5 | -------------------------------------------------------------------------------- /requirements/test.txt: -------------------------------------------------------------------------------- 1 | coverage 2 | interrogate 3 | numpy 4 | opencv-python-headless 5 | pre-commit 6 | pytest 7 | -------------------------------------------------------------------------------- /requirements/visualize.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | opencv-python 3 | -------------------------------------------------------------------------------- /resources/xrprimer-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openxrlab/xrprimer/4358ca46fd3b38801ea1ed34a6fa801849cc6efb/resources/xrprimer-logo.png -------------------------------------------------------------------------------- /scripts/build_ios.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # ** Unavailable ** 4 | # 5 | # cmake -S. -Bbuild/ios -G Xcode \ 6 | # -DCMAKE_TOOLCHAIN_FILE=cmake/Modules/Platform/ios.toolchain.cmake \ 7 | # -D CMAKE_CONFIGURATION_TYPES=Release \ 8 | # -D BUILD_EXTERNAL=ON \ 9 | # -D IOS_PLATFORM=OS \ 10 | # -D IOS_ARCH=arm64 \ 11 | # -D ENABLE_BITCODE=0 \ 12 | # -D IOS_DEPLOYMENT_TARGET=12.0 \ 13 | # -D ENABLE_VISIBILITY=0 14 | 15 | # if [[ $? == 0 ]] 16 | # then 17 | # cmake --build build/ios --target install -j4 18 | # else 19 | # echo -e "cmake config error!" 20 | # fi 21 | -------------------------------------------------------------------------------- /scripts/run_pinhole_camera_calib.sh: -------------------------------------------------------------------------------- 1 | #/bin/env bash 2 | 3 | # set -x 4 | cmake -S. -Bbuild -DENABLE_TEST=ON 5 | cmake --build build -j$(nproc) 6 | cd build 7 | pip install gdown 8 | gdown https://docs.google.com/uc?id=1MJx367I2_ezK3vKdV4eJ9d0cBzgs2jtR && tar -xzf xrprimer.tar.gz && rm xrprimer.tar.gz 9 | ln -sfn xrprimer/test test 10 | ./bin/test_calibrator 11 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | 4 | [yapf] 5 | based_on_style = pep8 6 | blank_line_before_nested_class_or_def = true 7 | split_before_expression_after_opening_paren = true 8 | 9 | [isort] 10 | line_length = 79 11 | multi_line_output = 3 12 | include_trailing_comma = true 13 | known_first_party = xrprimer,xrprimer_cpp 14 | known_third_party = cv2,mmcv,numpy,pytest,scipy,setuptools,sphinx_rtd_theme,torch,tqdm 15 | no_lines_before = STDLIB,LOCALFOLDER 16 | default_section = THIRDPARTY 17 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | XRPRIMER_VERSION_MAJOR 0 2 | XRPRIMER_VERSION_MINOR 7 3 | XRPRIMER_VERSION_PATCH 1 4 | --------------------------------------------------------------------------------