├── .clang-format ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── build_wheels.yml │ ├── cibuildwheel.yml │ ├── cmake.yml │ └── msvc.yml ├── .gitignore ├── .gitmodules ├── .natvis └── eos.natvis ├── CITATION.cff ├── CMakeLists.txt ├── LICENSE ├── MANIFEST.in ├── README.md ├── doc ├── CMakeLists.txt ├── Doxyfile.in └── namespaces.doxygen ├── examples ├── CMakeLists.txt ├── data │ ├── image_0010.png │ ├── image_0010.pts │ └── notes.txt ├── fit-model-ceres.cpp ├── fit-model-multi.cpp ├── fit-model-simple.cpp ├── fit-model.cpp └── generate-obj.cpp ├── include └── eos │ ├── core │ ├── Image.hpp │ ├── Landmark.hpp │ ├── LandmarkMapper.hpp │ ├── Mesh.hpp │ ├── Rect.hpp │ ├── image │ │ ├── Pixel.hpp │ │ ├── PixelTraits.hpp │ │ ├── opencv_interop.hpp │ │ ├── resize.hpp │ │ └── utils.hpp │ ├── math.hpp │ ├── read_obj.hpp │ ├── read_pts_landmarks.hpp │ └── write_obj.hpp │ ├── cpp17 │ ├── clamp.hpp │ ├── detail │ │ ├── akrzemi1_optional.hpp │ │ ├── akrzemi1_optional_serialization.hpp │ │ ├── mpark_variant.hpp │ │ └── mpark_variant_serialization.hpp │ ├── optional.hpp │ ├── optional_serialization.hpp │ ├── variant.hpp │ └── variant_serialization.hpp │ ├── fitting │ ├── FittingResult.hpp │ ├── RenderingParameters.hpp │ ├── blendshape_fitting.hpp │ ├── ceres_nonlinear.hpp │ ├── closest_edge_fitting.hpp │ ├── contour_correspondence.hpp │ ├── detail │ │ ├── eigen_quaternion_cerealisation.hpp │ │ └── nonlinear_camera_estimation_detail.hpp │ ├── fitting.hpp │ ├── linear_shape_fitting.hpp │ ├── multi_image_fitting.hpp │ ├── nonlinear_camera_estimation.hpp │ ├── orthographic_camera_estimation_linear.hpp │ └── rotation_angles.hpp │ ├── morphablemodel │ ├── Blendshape.hpp │ ├── EdgeTopology.hpp │ ├── ExpressionModel.hpp │ ├── MorphableModel.hpp │ ├── PcaModel.hpp │ ├── coefficients.hpp │ └── io │ │ ├── cvssp.hpp │ │ ├── eigen_cerealisation.hpp │ │ └── mat_cerealisation.hpp │ ├── pca │ └── pca.hpp │ ├── render │ ├── FragmentShader.hpp │ ├── ProjectionType.hpp │ ├── Rasterizer.hpp │ ├── SoftwareRenderer.hpp │ ├── Texture.hpp │ ├── VertexShader.hpp │ ├── detail │ │ ├── RayDirection.hpp │ │ ├── TriangleToRasterize.hpp │ │ ├── Vertex.hpp │ │ ├── plane.hpp │ │ ├── texturing.hpp │ │ └── utils.hpp │ ├── draw_utils.hpp │ ├── matrix_projection.hpp │ ├── normals.hpp │ ├── opencv │ │ └── draw_utils.hpp │ ├── ray_triangle_intersect.hpp │ ├── render.hpp │ ├── texture_extraction.hpp │ ├── transforms.hpp │ └── vertex_visibility.hpp │ └── video │ ├── Keyframe.hpp │ └── keyframe_merging.hpp ├── initial_cache.cmake.template ├── matlab ├── +eos │ ├── +fitting │ │ ├── fit_shape_and_pose.m │ │ └── private │ │ │ └── fitting.cpp │ └── +render │ │ ├── extract_texture.m │ │ ├── private │ │ └── render.cpp │ │ └── render.m ├── CMakeLists.txt ├── demo.m └── include │ ├── mexplus_eigen.hpp │ ├── mexplus_eos_types.hpp │ └── mexplus_opencv.hpp ├── python ├── CMakeLists.txt ├── demo.py ├── generate-python-bindings.cpp ├── pybind11_Image.hpp ├── pybind11_optional.hpp └── pybind11_variant.hpp ├── setup.py ├── share ├── bfm2009_model_contours.json ├── bfm2017-1_bfm_nomouth_model_contours.json ├── expression_blendshapes_3448.bin ├── ibug_to_bfm2009.txt ├── ibug_to_bfm2017-1_bfm_nomouth.txt ├── ibug_to_sfm.txt ├── readme.txt ├── scripts │ ├── compute_edgestruct.m │ ├── convert-bfm2009-to-eos.py │ ├── convert-bfm2017-to-eos.py │ ├── generate-edgestruct.py │ └── load_lyhm.py ├── sfm_3448_edge_topology.json ├── sfm_model_contours.json ├── sfm_reference.obj ├── sfm_reference_annotated.obj ├── sfm_reference_symmetry.txt └── sfm_shape_3448.bin ├── tests └── test_load_model.py ├── utils ├── CMakeLists.txt └── scm-to-cereal.cpp ├── vcpkg-configuration.json └── vcpkg.json /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: '-4' 4 | AlwaysBreakTemplateDeclarations: 'true' 5 | BreakBeforeBraces: Custom 6 | BraceWrapping: 7 | AfterClass: true 8 | AfterControlStatement: true 9 | AfterEnum: false 10 | AfterFunction: true 11 | AfterNamespace: false 12 | AfterObjCDeclaration: false 13 | AfterStruct: true 14 | AfterUnion: false 15 | BeforeCatch: false 16 | BeforeElse: false 17 | IndentBraces: false 18 | ColumnLimit: '110' 19 | Cpp11BracedListStyle: 'true' 20 | PointerAlignment: Left 21 | AllowShortFunctionsOnASingleLine: Empty 22 | IndentWidth: '4' 23 | Language: Cpp 24 | NamespaceIndentation: None 25 | SortIncludes: false 26 | Standard: Cpp11 27 | UseTab: Never 28 | # http://clang.llvm.org/docs/ClangFormatStyleOptions.html 29 | 30 | ... 31 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Explicitly declare text files we want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.c text 7 | *.cpp text 8 | *.h text 9 | *.hxx text 10 | *.hpp text 11 | 12 | # Denote all files that are truly binary and should not be modified. 13 | *.png binary 14 | *.jpg binary 15 | 16 | # Standard to msysgit 17 | *.doc diff=astextplain 18 | *.DOC diff=astextplain 19 | *.docx diff=astextplain 20 | *.DOCX diff=astextplain 21 | *.dot diff=astextplain 22 | *.DOT diff=astextplain 23 | *.pdf diff=astextplain 24 | *.PDF diff=astextplain 25 | *.rtf diff=astextplain 26 | *.RTF diff=astextplain 27 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [patrikhuber] 2 | -------------------------------------------------------------------------------- /.github/workflows/build_wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build Python wheels 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build_wheels: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [windows-latest, ubuntu-24.04, ubuntu-latest, ubuntu-20.04, macos-latest, macos-13] 15 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] 16 | 17 | name: ${{matrix.os}}, python-${{matrix.python-version}} 18 | 19 | runs-on: ${{matrix.os}} 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | with: 24 | submodules: true 25 | 26 | - name: Set up Python ${{ matrix.python-version }} 27 | uses: actions/setup-python@v4 28 | with: 29 | python-version: ${{ matrix.python-version }} 30 | 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install setuptools wheel 35 | - name: Build Wheel 36 | run: | 37 | python setup.py bdist_wheel 38 | - name: Upload artifact 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: ${{ matrix.os }}-python${{ matrix.python-version }}-whl 42 | path: dist/*.whl 43 | 44 | build_sdist: 45 | name: sdist 46 | runs-on: ubuntu-latest 47 | 48 | steps: 49 | - uses: actions/checkout@v3 50 | with: 51 | submodules: true 52 | 53 | - name: Install dependencies 54 | run: | 55 | python -m pip install --upgrade pip 56 | pip install setuptools wheel 57 | - name: Build sdist 58 | run: | 59 | python setup.py sdist 60 | - name: Upload artifact 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: Source distribution (sdist) 64 | path: dist/*.* 65 | -------------------------------------------------------------------------------- /.github/workflows/cibuildwheel.yml: -------------------------------------------------------------------------------- 1 | name: cibuildwheel build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | release: 9 | types: 10 | - published 11 | 12 | jobs: 13 | make_sdist: 14 | name: Make SDist 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: recursive 20 | 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel 25 | 26 | - name: Build sdist 27 | run: | 28 | python setup.py sdist 29 | 30 | - name: Upload artifact 31 | uses: actions/upload-artifact@v4 32 | with: 33 | path: dist/*.* 34 | 35 | build_wheels: 36 | name: Build wheels on ${{ matrix.os }} 37 | runs-on: ${{ matrix.os }} 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | os: [ubuntu-22.04, windows-2022, macos-13] 42 | 43 | steps: 44 | - uses: actions/checkout@v3 45 | with: 46 | submodules: recursive 47 | 48 | # Used to host cibuildwheel 49 | - uses: actions/setup-python@v3 50 | 51 | - name: Use cmake 52 | run: cmake --version 53 | 54 | - name: Install cibuildwheel 55 | run: python -m pip install cibuildwheel==2.15.0 56 | 57 | - name: Build wheels 58 | run: python -m cibuildwheel --output-dir wheelhouse 59 | # to supply options, put them in 'env', like: 60 | env: 61 | MACOSX_DEPLOYMENT_TARGET: 10.15 62 | CIBW_ARCHS_MACOS: "x86_64 universal2 arm64" 63 | CIBW_SKIP: pp* *i686 cp36-* 64 | CIBW_TEST_REQUIRES: pytest numpy 65 | CIBW_TEST_COMMAND: pytest {package}/tests 66 | CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64" 67 | 68 | - uses: actions/upload-artifact@v4 69 | with: 70 | path: ./wheelhouse/*.whl 71 | 72 | upload_all: 73 | needs: [build_wheels, make_sdist] 74 | environment: pypi 75 | permissions: 76 | id-token: write 77 | runs-on: ubuntu-latest 78 | if: github.event_name == 'release' && github.event.action == 'published' 79 | steps: 80 | - uses: actions/download-artifact@v4 81 | with: 82 | name: artifact 83 | path: dist 84 | 85 | - uses: pypa/gh-action-pypi-publish@release/v1 -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: C++ build 2 | 3 | on: 4 | push: 5 | branches: [ "master", "devel" ] 6 | pull_request: 7 | branches: [ "master", "devel" ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [windows-latest, windows-2019, ubuntu-24.04, ubuntu-latest, ubuntu-20.04, macos-latest, macos-13] 22 | 23 | name: ${{matrix.os}} 24 | 25 | runs-on: ${{matrix.os}} 26 | 27 | env: 28 | # Set cache_path to the vcpkg default Windows directory if the OS is Windows, otherwise set it to the default Linux / macOS directory: 29 | cache_path: ${{ startsWith(matrix.os, 'windows') && 'C:/Users/runneradmin/AppData/Local/vcpkg/archives/' || '~/.cache/vcpkg/archives/' }} 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | with: 34 | submodules: true 35 | 36 | - name: Install vcpkg on macOS if not pre-installed 37 | if: runner.os == 'macOS' && (matrix.os == 'macos-13' || matrix.os == 'macos-latest') 38 | run: | 39 | git clone https://github.com/microsoft/vcpkg.git 40 | ./vcpkg/bootstrap-vcpkg.sh 41 | shell: bash 42 | 43 | - name: Set VCPKG_ROOT environment variable 44 | shell: bash 45 | run: | 46 | if [[ "$RUNNER_OS" == "macOS" ]]; then 47 | echo "VCPKG_ROOT=$GITHUB_WORKSPACE/vcpkg" >> $GITHUB_ENV 48 | else 49 | echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> $GITHUB_ENV 50 | fi 51 | 52 | - name: Cache/restore vcpkg dependencies 53 | uses: actions/cache@v3 54 | with: 55 | path: ${{env.cache_path}} 56 | key: ${{matrix.os}}-vcpkg-cache-${{ hashFiles('vcpkg.json', '.github/workflows/cmake.yml') }} 57 | restore-keys: | 58 | ${{matrix.os}}-vcpkg-cache- 59 | 60 | - name: Configure CMake 61 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 62 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 63 | run: > 64 | cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 65 | -DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake 66 | -DVCPKG_MANIFEST_FEATURES="ceres" 67 | -DEOS_BUILD_CERES_EXAMPLE=ON -DEOS_BUILD_UTILS=ON -DEOS_GENERATE_PYTHON_BINDINGS=ON 68 | 69 | - name: Build 70 | # Build your program with the given configuration 71 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 72 | 73 | - name: Test 74 | working-directory: ${{github.workspace}}/build 75 | # Execute tests defined by the CMake configuration. 76 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 77 | run: ctest -C ${{env.BUILD_TYPE}} 78 | -------------------------------------------------------------------------------- /.github/workflows/msvc.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # 6 | # Find more information at: 7 | # https://github.com/microsoft/msvc-code-analysis-action 8 | 9 | name: Microsoft C++ Code Analysis 10 | 11 | on: 12 | push: 13 | branches: [ "master", devel ] 14 | pull_request: 15 | branches: [ "master", devel ] 16 | schedule: 17 | - cron: '38 1 * * 0' 18 | 19 | env: 20 | # Path to the CMake build directory. 21 | build: '${{ github.workspace }}/build' 22 | BUILD_TYPE: Debug 23 | 24 | permissions: 25 | contents: read 26 | 27 | jobs: 28 | analyze: 29 | permissions: 30 | contents: read # for actions/checkout to fetch code 31 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 32 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 33 | name: Analyze 34 | runs-on: windows-latest 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v3 39 | with: 40 | submodules: true 41 | 42 | - name: "Set VCPKG_ROOT environment variable" 43 | shell: bash 44 | run: | 45 | echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> $GITHUB_ENV 46 | 47 | - name: Cache/restore vcpkg dependencies 48 | uses: actions/cache@v3 49 | with: 50 | path: C:/Users/runneradmin/AppData/Local/vcpkg/archives/ 51 | key: ${{runner.OS}}-vcpkg-cache-${{ hashFiles('vcpkg.json', '.github/workflows/cmake.yml') }} 52 | restore-keys: | 53 | ${{runner.OS}}-vcpkg-cache- 54 | 55 | - name: Configure CMake 56 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 57 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 58 | run: > 59 | cmake -B ${{env.build}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 60 | -DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake 61 | -DVCPKG_MANIFEST_FEATURES="ceres" 62 | -DEOS_BUILD_CERES_EXAMPLE=ON -DEOS_BUILD_UTILS=ON -DEOS_GENERATE_PYTHON_BINDINGS=ON 63 | # Build is not required unless generated source files are used 64 | # - name: Build CMake 65 | # run: cmake --build ${{ env.build }} 66 | 67 | - name: Run MSVC Code Analysis 68 | uses: microsoft/msvc-code-analysis-action@96315324a485db21449515180214ecb78c16a1c5 69 | # Provide a unique ID to access the sarif output path 70 | id: run-analysis 71 | with: 72 | cmakeBuildDirectory: ${{ env.build }} 73 | buildConfiguration: ${{ env.BUILD_TYPE }} 74 | # Ruleset file that will determine what checks will be run 75 | ruleset: NativeRecommendedRules.ruleset 76 | # Paths to ignore analysis of CMake targets and includes 77 | # ignoredPaths: ${{ github.workspace }}/dependencies;${{ github.workspace }}/test 78 | ignoredPaths: ${{ github.workspace }}/3rdparty/eigen/ 79 | 80 | # Upload SARIF file to GitHub Code Scanning Alerts 81 | - name: Upload SARIF to GitHub 82 | uses: github/codeql-action/upload-sarif@v2 83 | with: 84 | sarif_file: ${{ steps.run-analysis.outputs.sarif }} 85 | 86 | # Upload SARIF file as an Artifact to download and view 87 | - name: Upload SARIF as an Artifact 88 | uses: actions/upload-artifact@v4 89 | with: 90 | name: sarif-file 91 | path: ${{ steps.run-analysis.outputs.sarif }} 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore files generated by Visual Studio's CMake: 2 | .vs/ 3 | out/build/ 4 | CMakePresets.json 5 | 6 | # Ignore (optional) configuration files with user-specific paths: 7 | initial_cache.cmake 8 | setup.cfg 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/pybind11"] 2 | path = 3rdparty/pybind11 3 | url = https://github.com/pybind/pybind11.git 4 | [submodule "3rdparty/nanoflann"] 5 | path = 3rdparty/nanoflann 6 | url = https://github.com/jlblancoc/nanoflann.git 7 | [submodule "3rdparty/eigen3-nnls"] 8 | path = 3rdparty/eigen3-nnls 9 | url = https://github.com/hmatuschek/eigen3-nnls.git 10 | [submodule "3rdparty/mexplus"] 11 | path = 3rdparty/mexplus 12 | url = https://github.com/kyamagu/mexplus.git 13 | [submodule "3rdparty/cereal"] 14 | path = 3rdparty/cereal 15 | url = https://github.com/USCiLab/cereal.git 16 | [submodule "3rdparty/toml11"] 17 | path = 3rdparty/toml11 18 | url = https://github.com/ToruNiina/toml11.git 19 | [submodule "3rdparty/eigen"] 20 | path = 3rdparty/eigen 21 | url = https://gitlab.com/libeigen/eigen.git 22 | -------------------------------------------------------------------------------- /.natvis/eos.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | UINT8 17 | 18 | 19 | INT32 20 | 21 | 22 | FLOAT32 23 | 24 | 25 | FLOAT64 26 | 27 | 28 | 29 | 1 30 | 31 | 32 | width_ 33 | height_ 34 | 35 | ($T1*)(&data_[0]) 36 | 37 | row_stride 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | UINT8 46 | 47 | 48 | INT32 49 | 50 | 51 | FLOAT32 52 | 53 | 54 | FLOAT64 55 | 56 | 57 | 58 | RGB 59 | 60 | 61 | RGBA 62 | 63 | 64 | 65 | width_ 66 | height_ 67 | 68 | ($T1*)(&data_[0]) 69 | 70 | row_stride 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Huber" 5 | given-names: "Patrik" 6 | orcid: "https://orcid.org/0000-0002-1474-1040" 7 | title: "eos: A lightweight 3D Morphable Face Model library in modern C++" 8 | version: 1.5.0 9 | date-released: 2024-12-10 10 | url: "https://github.com/patrikhuber/eos" 11 | license: Apache-2.0 12 | preferred-citation: 13 | type: conference-paper 14 | title: "A Multiresolution 3D Morphable Face Model and Fitting Framework" 15 | authors: 16 | - family-names: "Huber" 17 | given-names: "Patrik" 18 | orcid: "https://orcid.org/0000-0002-1474-1040" 19 | - family-names: "Hu" 20 | given-names: "Guosheng" 21 | - family-names: "Tena" 22 | given-names: "Jose Rafael" 23 | - family-names: "Mortazavian" 24 | given-names: "Pouria" 25 | - family-names: "Koppen" 26 | given-names: "Willen P." 27 | - family-names: "Christmas" 28 | given-names: "William J." 29 | - family-names: "Raetsch" 30 | given-names: "Matthias" 31 | - family-names: "Kittler" 32 | given-names: "Josef" 33 | orcid: "https://orcid.org/0000-0002-8110-9205" 34 | doi: "10.5220/0005669500790086" 35 | conference: 36 | name: "11th Joint Conference on Computer Vision, Imaging and Computer Graphics Theory and Applications (VISIGRAPP 2016)" 37 | volume-title: "Proceedings of the 11th Joint Conference on Computer Vision, Imaging and Computer Graphics Theory and Applications (VISIGRAPP 2016)" 38 | start: 79 # First page number 39 | end: 86 # Last page number 40 | volume: 4 41 | year: 2016 42 | month: 2 43 | publisher: 44 | name: "SciTePress" 45 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md LICENSE 2 | global-include CMakeLists.txt *.cmake 3 | exclude initial_cache.cmake 4 | recursive-exclude out * 5 | recursive-include 3rdparty/cereal/include * 6 | recursive-include 3rdparty/eigen/Eigen * 7 | recursive-include 3rdparty/eigen3-nnls/src *.h 8 | recursive-include 3rdparty/nanoflann/include * 9 | recursive-include 3rdparty/toml11 *.hpp 10 | recursive-include 3rdparty/pybind11 * 11 | recursive-include include * 12 | recursive-include python * 13 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Doxygen REQUIRED) 2 | 3 | set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) 4 | set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 5 | 6 | configure_file(${doxyfile_in} ${doxyfile} @ONLY) 7 | 8 | add_custom_target(doc 9 | COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} 10 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 11 | COMMENT "Generating API documentation with Doxygen" 12 | VERBATIM 13 | ) 14 | 15 | install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION doc) 16 | -------------------------------------------------------------------------------- /doc/Doxyfile.in: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = "@CMAKE_PROJECT_NAME@" 2 | PROJECT_NUMBER = @eos_VERSION_MAJOR@.@eos_VERSION_MINOR@.@eos_VERSION_PATCH@ 3 | STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ 4 | INPUT = @PROJECT_SOURCE_DIR@/README.md \ 5 | @PROJECT_SOURCE_DIR@/doc/namespaces.doxygen \ 6 | @PROJECT_SOURCE_DIR@/include 7 | RECURSIVE = YES 8 | EXCLUDE_PATTERNS = */detail/* 9 | 10 | USE_MDFILE_AS_MAINPAGE = README.md 11 | 12 | USE_MATHJAX = YES 13 | MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML 14 | -------------------------------------------------------------------------------- /doc/namespaces.doxygen: -------------------------------------------------------------------------------- 1 | /** 2 | * @namespace eos 3 | * @brief Namespace containing all of eos's 3D model fitting functionality. 4 | */ 5 | 6 | /** 7 | * @namespace eos::core 8 | * @brief Essential functions and classes to work with 3D face models, meshes and landmarks. 9 | */ 10 | 11 | /** 12 | * @namespace eos::fitting 13 | * @brief Pose and shape fitting of a 3D Morphable Model. 14 | */ 15 | 16 | /** 17 | * @namespace eos::morphablemodel 18 | * @brief Functionality to represent a Morphable Model, its PCA models, and functions to load models and blendshapes. 19 | */ 20 | 21 | /** 22 | * @namespace eos::pca 23 | * @brief PCA and functionality to build statistical models. 24 | */ 25 | 26 | /** 27 | * @namespace eos::render 28 | * @brief Software rendering and texture extraction functionality. 29 | */ 30 | 31 | /** 32 | * @namespace eos::video 33 | * @brief Video keyframe extraction and fusion. 34 | */ 35 | 36 | /** 37 | * @namespace eos::cpp17 38 | * @brief Replacement types and functions for C++17 standard library functionality for compilers who don't have them (for example AppleClang). 39 | */ 40 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The examples need a few additional dependencies (e.g. boost filesystem, program_options, and OpenCV highgui): 2 | 3 | # Check installed version in order to include the correct OpenCV libraries: 4 | # First call find_package without a version to find any OpenCV. OpenCV_VERSION_MAJOR is then defined. 5 | find_package(OpenCV REQUIRED core) 6 | if("${OpenCV_VERSION_MAJOR}$" EQUAL 2) 7 | message(STATUS "OpenCV 2.x detected") 8 | find_package(OpenCV 2.4.3 REQUIRED core imgproc highgui) 9 | elseif("${OpenCV_VERSION_MAJOR}$" EQUAL 3) 10 | message(STATUS "OpenCV 3.x detected") 11 | find_package(OpenCV 3 REQUIRED core imgproc imgcodecs) 12 | elseif("${OpenCV_VERSION_MAJOR}$" EQUAL 4) 13 | message(STATUS "OpenCV 4.x detected") 14 | find_package(OpenCV 4 REQUIRED core imgproc imgcodecs) 15 | endif() 16 | # This allows us to compile in RelWithDebInfo. It'll use the Release-version of OpenCV: 17 | set_target_properties(${OpenCV_LIBS} PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE) 18 | 19 | set(Boost_NO_WARN_NEW_VERSIONS ON) # Supress "New Boost version may have incorrect dependencies or import targets" warning 20 | find_package(Boost 1.71.0 REQUIRED COMPONENTS filesystem program_options) 21 | 22 | # Simple model fitting (orthographic camera & shape to landmarks) example: 23 | add_executable(fit-model-simple fit-model-simple.cpp) 24 | target_link_libraries(fit-model-simple PRIVATE eos ${OpenCV_LIBS} Boost::filesystem Boost::program_options) 25 | target_link_libraries(fit-model-simple PRIVATE "$<$:-pthread>$<$:-pthreads>") 26 | target_compile_options(fit-model-simple PRIVATE "$<$:/bigobj>") 27 | target_include_directories(fit-model-simple PRIVATE ${OpenCV_INCLUDE_DIRS}) 28 | 29 | # Model fitting example that fits orthographic camera, shape, blendshapes, and contours: 30 | add_executable(fit-model fit-model.cpp) 31 | target_link_libraries(fit-model PRIVATE eos ${OpenCV_LIBS} Boost::filesystem Boost::program_options) 32 | target_link_libraries(fit-model PRIVATE "$<$:-pthread>$<$:-pthreads>") 33 | target_compile_options(fit-model PRIVATE "$<$:/bigobj>") 34 | target_include_directories(fit-model PRIVATE ${OpenCV_INCLUDE_DIRS}) 35 | 36 | # Model fitting example that fits orthographic camera, shape, blendshapes, and contours to multiple images: 37 | add_executable(fit-model-multi fit-model-multi.cpp) 38 | target_link_libraries(fit-model-multi PRIVATE eos ${OpenCV_LIBS} Boost::filesystem Boost::program_options) 39 | target_link_libraries(fit-model-multi PRIVATE "$<$:-pthread>$<$:-pthreads>") 40 | target_compile_options(fit-model-multi PRIVATE "$<$:/bigobj>") 41 | target_include_directories(fit-model-multi PRIVATE ${OpenCV_INCLUDE_DIRS}) 42 | 43 | # Generate random samples from the model: 44 | add_executable(generate-obj generate-obj.cpp) 45 | target_link_libraries(generate-obj PRIVATE eos ${OpenCV_LIBS} Boost::filesystem Boost::program_options) 46 | target_include_directories(generate-obj PRIVATE ${OpenCV_INCLUDE_DIRS}) 47 | 48 | # Install these targets: 49 | install(TARGETS fit-model-simple DESTINATION bin) 50 | install(TARGETS fit-model DESTINATION bin) 51 | install(TARGETS fit-model-multi DESTINATION bin) 52 | install(TARGETS generate-obj DESTINATION bin) 53 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data DESTINATION bin) 54 | 55 | 56 | if(EOS_BUILD_CERES_EXAMPLE) 57 | # Find Ceres, for the fit-model-ceres app: 58 | find_package(Ceres REQUIRED) 59 | 60 | # Single and multi-image non-linear model fitting with Ceres example: 61 | add_executable(fit-model-ceres fit-model-ceres.cpp) 62 | target_link_libraries(fit-model-ceres PRIVATE eos Ceres::ceres ${OpenCV_LIBS} Boost::filesystem Boost::program_options) 63 | target_compile_options(fit-model-ceres PRIVATE "$<$:/bigobj>") 64 | target_include_directories(fit-model-ceres PRIVATE ${OpenCV_INCLUDE_DIRS}) 65 | install(TARGETS fit-model-ceres DESTINATION bin) 66 | endif() 67 | -------------------------------------------------------------------------------- /examples/data/image_0010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrikhuber/eos/ce984207a51c2edd2e7568a64fec9b98ef61aeed/examples/data/image_0010.png -------------------------------------------------------------------------------- /examples/data/image_0010.pts: -------------------------------------------------------------------------------- 1 | version: 1 2 | n_points: 68 3 | { 4 | 611.284152 272.773913 5 | 607.899810 304.319120 6 | 613.094672 339.255590 7 | 622.808910 372.005502 8 | 632.669412 404.678824 9 | 643.511765 432.387059 10 | 651.944706 460.095294 11 | 665.273062 490.435795 12 | 695.836418 505.268304 13 | 732.882854 507.601124 14 | 771.140919 496.682178 15 | 814.580000 468.528235 16 | 852.378512 442.776782 17 | 879.780380 411.071469 18 | 894.313442 365.675605 19 | 905.385571 315.920470 20 | 912.766990 265.622269 21 | 608.920127 214.000560 22 | 624.612439 205.921886 23 | 641.092550 213.028614 24 | 656.643518 218.496370 25 | 675.118172 229.895972 26 | 714.507903 232.709448 27 | 746.296501 221.279247 28 | 780.463683 216.098001 29 | 814.843700 219.577695 30 | 843.144930 231.216340 31 | 691.116694 258.293884 32 | 686.687460 284.649159 33 | 678.837382 314.952208 34 | 671.575170 341.518858 35 | 662.787059 357.695294 36 | 672.577890 363.872007 37 | 684.099935 368.436761 38 | 701.177972 366.977129 39 | 716.657204 364.432781 40 | 629.308705 253.419965 41 | 641.102353 239.634118 42 | 656.763529 242.043529 43 | 675.896682 261.234513 44 | 657.320778 266.311140 45 | 639.427321 264.662791 46 | 746.963287 268.769913 47 | 763.151112 249.645230 48 | 792.895294 251.681176 49 | 810.965882 264.932941 50 | 793.647370 274.870034 51 | 770.089168 277.392535 52 | 650.466760 402.919129 53 | 662.802218 396.521655 54 | 674.655327 393.178631 55 | 685.548727 399.199281 56 | 699.727862 394.238444 57 | 720.617079 403.327949 58 | 762.033016 412.238881 59 | 727.622851 431.747041 60 | 706.539636 437.383098 61 | 691.346572 438.212545 62 | 679.045609 436.837854 63 | 665.204535 428.934293 64 | 657.444159 406.740225 65 | 677.532790 409.255983 66 | 688.027122 410.142694 67 | 701.031914 411.571227 68 | 750.088005 413.800864 69 | 701.031914 411.571227 70 | 688.027122 410.142694 71 | 677.532790 409.255983 72 | } -------------------------------------------------------------------------------- /examples/data/notes.txt: -------------------------------------------------------------------------------- 1 | The data in this folder was downloaded from http://ibug.doc.ic.ac.uk/resources/300-W/ on 15 December 2014. 2 | -------------------------------------------------------------------------------- /examples/generate-obj.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: examples/generate-obj.cpp 5 | * 6 | * Copyright 2016, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include "eos/core/Image.hpp" 21 | #include "eos/core/image/opencv_interop.hpp" 22 | #include "eos/core/write_obj.hpp" 23 | #include "eos/core/math.hpp" 24 | #include "eos/morphablemodel/MorphableModel.hpp" 25 | #include "eos/render/render.hpp" 26 | #include "eos/render/matrix_projection.hpp" 27 | #include "eos/cpp17/optional.hpp" 28 | 29 | #include "boost/filesystem.hpp" 30 | #include "boost/program_options.hpp" 31 | 32 | #include "opencv2/core/core.hpp" 33 | #include "opencv2/imgcodecs/imgcodecs.hpp" 34 | 35 | #include 36 | 37 | using namespace eos; 38 | namespace po = boost::program_options; 39 | namespace fs = boost::filesystem; 40 | using std::cout; 41 | using std::endl; 42 | using std::string; 43 | using std::vector; 44 | 45 | /** 46 | * This app generates random samples from the model and stores them as obj file 47 | * as well as outputs a frontal rendering of the sample. 48 | * 49 | * A list of shape and/or colour coefficients can be specified. Any coefficient 50 | * not specified will be set to zero. 51 | */ 52 | int main(int argc, char* argv[]) 53 | { 54 | string model_file, output_file; 55 | vector shape_coefficients, color_coefficients; 56 | 57 | try 58 | { 59 | po::options_description desc("Allowed options"); 60 | // clang-format off 61 | desc.add_options() 62 | ("help", "produce help message") 63 | ("model", po::value(&model_file)->required(), "an eos .bin Morphable Model file") 64 | ("shape-coeffs", po::value>(&shape_coefficients)->multitoken(), 65 | "optional parameter list of shape coefficients. All not specified will be set to zero. E.g.: " 66 | "'--shape-coeffs 0.0 1.5'. If omitted, the mean is used.") 67 | ("color-coeffs", po::value>(&color_coefficients)->multitoken(), 68 | "optional parameter list of colour coefficients. All not specified will be set to zero. E.g.: " 69 | "'--colour-coeffs 0.0 1.5'. If omitted, the mean is used.") 70 | ("output", po::value(&output_file)->default_value("output.obj"), 71 | "name of the output obj file (including .obj). Can be a full path."); 72 | // clang-format on 73 | po::variables_map vm; 74 | // disabling short options to allow negative values for the coefficients, e.g. '--shape-coeffs 0.0 -1.5' 75 | po::store( 76 | po::parse_command_line(argc, argv, desc, 77 | po::command_line_style::unix_style ^ po::command_line_style::allow_short), 78 | vm); 79 | if (vm.count("help")) 80 | { 81 | cout << "Usage: generate-obj [options]" << endl; 82 | cout << desc; 83 | return EXIT_SUCCESS; 84 | } 85 | po::notify(vm); 86 | } catch (const po::error& e) 87 | { 88 | cout << "Error while parsing command-line arguments: " << e.what() << endl; 89 | cout << "Use --help to display a list of options." << endl; 90 | return EXIT_FAILURE; 91 | } 92 | 93 | morphablemodel::MorphableModel morphable_model = morphablemodel::load_model(model_file); 94 | 95 | if (shape_coefficients.size() < morphable_model.get_shape_model().get_num_principal_components()) 96 | { 97 | shape_coefficients.resize(morphable_model.get_shape_model().get_num_principal_components()); 98 | } 99 | 100 | if (color_coefficients.size() < morphable_model.get_color_model().get_num_principal_components()) 101 | { 102 | color_coefficients.resize(morphable_model.get_color_model().get_num_principal_components()); 103 | } 104 | 105 | const core::Mesh sample_mesh = morphable_model.draw_sample( 106 | shape_coefficients, color_coefficients); // if one of the two vectors is empty, it uses get_mean() 107 | 108 | core::write_obj(sample_mesh, output_file); 109 | 110 | const auto perspective = render::perspective(core::radians(60.0f), 512.0f / 512.0f, 0.1f, 500.0f); 111 | // Could use orthographic projection accordingly: 112 | // const auto ortho = render::ortho(-130.0f, 130.0f, -130.0f, 130.0f); 113 | // (and set enable_near_clipping and enable_far_clipping to false, or use the ortho() overload with 114 | // appropriate near/far values.) 115 | Eigen::Matrix4f model_view = Eigen::Matrix4f::Identity(); 116 | model_view(2, 3) = -200.0f; // move the model 200 units back along the z-axis 117 | 118 | const core::Image4u rendering = 119 | render::render(sample_mesh, model_view, perspective, 512, 512, true, true, true); 120 | fs::path filename_rendering(output_file); 121 | filename_rendering.replace_extension(".png"); 122 | cv::imwrite(filename_rendering.string(), core::to_mat(rendering)); 123 | 124 | cout << "Wrote the generated obj and a rendering to files with basename " << output_file << "." << endl; 125 | 126 | return EXIT_SUCCESS; 127 | } 128 | -------------------------------------------------------------------------------- /include/eos/core/Image.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/Image.hpp 5 | * 6 | * Copyright 2017, 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_IMAGE_HPP 23 | #define EOS_IMAGE_HPP 24 | 25 | #include "eos/core/image/Pixel.hpp" 26 | #include "eos/core/image/PixelTraits.hpp" 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | namespace eos { 33 | namespace core { 34 | 35 | /** 36 | * @brief Class to represent images. 37 | * 38 | * The class currently uses row-major storage order. 39 | * 40 | * Some of the code is inspired by the Selene library (https://github.com/kmhofmann/selene, MIT license). 41 | */ 42 | template 43 | class Image 44 | { 45 | public: 46 | using pixel_type = PixelType; 47 | 48 | Image() = default; 49 | 50 | // Initialises with all zeros. This is a bit of an overhead. We probably have to use unique_ptr or a raw 51 | // pointer to get rid of this, as a vector can't have "uninitialised" (yet existing) elements. 52 | Image(int height, int width) 53 | : height_(height), width_(width), row_stride(PixelTraits::num_bytes * width) 54 | { 55 | // data_.reserve(row_stride * height); // just reserves, doesn't put any elements into the vector 56 | data_.resize(row_stride * height); 57 | }; 58 | 59 | int height() const noexcept 60 | { 61 | return height_; 62 | }; 63 | 64 | int width() const noexcept 65 | { 66 | return width_; 67 | }; 68 | 69 | PixelType& operator()(int y, int x) noexcept 70 | { 71 | assert(data_.size() > 0); // byte_ptr() checks this but let's put the assert here too, because this 72 | // function is public-facing. 73 | assert(y >= 0 && y < height_); 74 | assert(x >= 0 && x < width_); 75 | return *data(y, x); 76 | }; 77 | const PixelType& operator()(int y, int x) const noexcept 78 | { 79 | assert(data_.size() > 0); // byte_ptr() checks this but let's put the assert here too, because this 80 | // function is public-facing. 81 | assert(y >= 0 && y < height_); 82 | assert(x >= 0 && x < width_); 83 | return *data(y, x); 84 | }; 85 | 86 | private: 87 | int height_ = 0; 88 | int width_ = 0; 89 | 90 | int row_stride = 0; // In bytes. >1 means it's a row-major image, 1 would mean a col-major image. 91 | 92 | std::vector data_; // Storage for the image data as bytes 93 | 94 | // Return a std::ptr_diff? 95 | int compute_data_offset(int y, int x) const noexcept 96 | { 97 | return row_stride * y + PixelTraits::num_bytes * x; 98 | }; 99 | 100 | std::uint8_t* byte_ptr(int y, int x) noexcept 101 | { 102 | assert(data_.size() > 0); 103 | return &data_[0] + compute_data_offset(y, x); 104 | }; 105 | const std::uint8_t* byte_ptr(int y, int x) const noexcept 106 | { 107 | assert(data_.size() > 0); 108 | return &data_[0] + compute_data_offset(y, x); 109 | }; 110 | 111 | PixelType* data(int y, int x) noexcept 112 | { 113 | return reinterpret_cast(byte_ptr(y, x)); 114 | }; 115 | const PixelType* data(int y, int x) const noexcept 116 | { 117 | return reinterpret_cast(byte_ptr(y, x)); 118 | }; 119 | }; 120 | 121 | // Define type aliases for the most commonly used image types: 122 | using Image1u = Image; 123 | using Image3u = Image>; 124 | using Image4u = Image>; 125 | using Image1f = Image; 126 | using Image3f = Image>; 127 | using Image1d = Image; 128 | 129 | } /* namespace core */ 130 | } /* namespace eos */ 131 | 132 | #endif /* EOS_IMAGE_HPP */ 133 | -------------------------------------------------------------------------------- /include/eos/core/Landmark.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/Landmark.hpp 5 | * 6 | * Copyright 2014, 2015 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_LANDMARK_HPP 23 | #define EOS_LANDMARK_HPP 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace eos { 31 | namespace core { 32 | 33 | /** 34 | * @brief Representation of a landmark, consisting of a landmark name and 35 | * coordinates of the given type. Usually, the type would be \c Eigen::Vector2f. 36 | */ 37 | template 38 | struct Landmark 39 | { 40 | std::string name; ///< Name of the landmark, often used as identifier. 41 | LandmarkType coordinates; ///< The position or coordinates of the landmark. 42 | }; 43 | 44 | /** 45 | * @brief A trivial collection of landmarks that belong together. 46 | */ 47 | template 48 | using LandmarkCollection = std::vector>; 49 | 50 | /** 51 | * @brief Shorthand for a 2D floating point landmark type. 52 | */ 53 | // using Landmark2f = Landmark>; 54 | 55 | /** 56 | * @brief Alias for the 2D landmark point type. 57 | */ 58 | // using Point2f = Eigen::Vector2f; 59 | // using Point2f = std::array; 60 | 61 | /** 62 | * @brief Filters the given LandmarkCollection and returns a new LandmarkCollection 63 | * containing all landmarks whose name matches the one given by \p filter. 64 | * 65 | * @param[in] landmarks The input LandmarkCollection to be filtered. 66 | * @param[in] filter A list of landmark names (identifiers) by which the given LandmarkCollection is filtered. 67 | * @return A new, filtered LandmarkCollection. 68 | */ 69 | template 70 | LandmarkCollection filter(const LandmarkCollection& landmarks, const std::vector& filter) 71 | { 72 | LandmarkCollection filtered_landmarks; 73 | using std::begin; 74 | using std::end; 75 | std::copy_if( 76 | begin(landmarks), end(landmarks), std::back_inserter(filtered_landmarks), 77 | [&](const Landmark& lm) { return std::find(begin(filter), end(filter), lm.name) != end(filter); }); 78 | return filtered_landmarks; 79 | }; 80 | 81 | } /* namespace core */ 82 | } /* namespace eos */ 83 | 84 | #endif /* EOS_LANDMARK_HPP */ 85 | -------------------------------------------------------------------------------- /include/eos/core/Mesh.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/Mesh.hpp 5 | * 6 | * Copyright 2014, 2015 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_MESH_HPP 23 | #define EOS_MESH_HPP 24 | 25 | #include "Eigen/Core" 26 | 27 | #include 28 | #include 29 | 30 | namespace eos { 31 | namespace core { 32 | 33 | /** 34 | * @brief This class represents a 3D mesh consisting of vertices, vertex colour 35 | * information and texture coordinates. 36 | * 37 | * Additionally it stores the indices that specify which vertices 38 | * to use to generate the triangle mesh out of the vertices. 39 | * 40 | * \c texcoords should either be the same size as \c vertices (i.e. one set of texture coordinates per 41 | * vertex), or alternatively \c tti can be set, then a separate triangulation for the texture coordinates can 42 | * be used (e.g. for texture maps that contain seams). 43 | */ 44 | struct Mesh 45 | { 46 | std::vector vertices; ///< 3D vertex positions. 47 | std::vector colors; ///< Colour information for each vertex. Expected to be in RGB order. 48 | std::vector texcoords; ///< Texture coordinates. 49 | 50 | std::vector> tvi; ///< Triangle vertex indices 51 | std::vector> tci; ///< Triangle color indices (usually the same as tvi) 52 | std::vector> tti; ///< Triangle texture indices 53 | }; 54 | 55 | } /* namespace core */ 56 | } /* namespace eos */ 57 | 58 | #endif /* EOS_MESH_HPP */ 59 | -------------------------------------------------------------------------------- /include/eos/core/Rect.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/Rect.hpp 5 | * 6 | * Copyright 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RECT_HPP 23 | #define EOS_RECT_HPP 24 | 25 | namespace eos { 26 | namespace core { 27 | 28 | /** 29 | * @brief A simple type representing a rectangle. 30 | */ 31 | template 32 | struct Rect 33 | { 34 | T x, y; ///< Top-left corner x and y position 35 | T width, height; 36 | }; 37 | 38 | } /* namespace core */ 39 | } /* namespace eos */ 40 | 41 | #endif /* EOS_RECT_HPP */ 42 | -------------------------------------------------------------------------------- /include/eos/core/image/Pixel.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/image/Pixel.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_IMAGE_PIXEL_HPP 23 | #define EOS_IMAGE_PIXEL_HPP 24 | 25 | #include 26 | #include 27 | 28 | namespace eos { 29 | namespace core { 30 | 31 | /** 32 | * @brief Represents a pixel with given type and number of channels. 33 | */ 34 | template 35 | class Pixel 36 | { 37 | public: 38 | template > 39 | constexpr Pixel(Args... args) noexcept : data_{{static_cast(args)...}} {}; 40 | 41 | constexpr explicit Pixel(const std::array& arr) noexcept : data_(arr){}; 42 | 43 | constexpr ElementType& operator[](int i) noexcept 44 | { 45 | return data_[i]; 46 | }; 47 | constexpr const ElementType& operator[](int i) const noexcept 48 | { 49 | return data_[i]; 50 | }; 51 | 52 | const std::array& data() const noexcept 53 | { 54 | return data_; 55 | }; 56 | 57 | private: 58 | std::array data_; 59 | }; 60 | 61 | template 62 | constexpr bool operator==(const Pixel& lhs, 63 | const Pixel& rhs) noexcept 64 | { 65 | return lhs.data() == rhs.data(); 66 | }; 67 | 68 | } /* namespace core */ 69 | } /* namespace eos */ 70 | 71 | #endif /* EOS_IMAGE_PIXEL_HPP */ 72 | -------------------------------------------------------------------------------- /include/eos/core/image/PixelTraits.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/image/PixelTraits.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_IMAGE_PIXELTRAITS_HPP 23 | #define EOS_IMAGE_PIXELTRAITS_HPP 24 | 25 | #include "eos/core/image/Pixel.hpp" 26 | 27 | #include 28 | 29 | namespace eos { 30 | namespace core { 31 | 32 | /** 33 | * @brief Todo. 34 | */ 35 | template 36 | struct PixelTraits 37 | { 38 | static constexpr std::uint16_t num_bytes = sizeof(ElementType); 39 | 40 | static constexpr ElementType zero_element = ElementType{0}; 41 | }; 42 | 43 | /** 44 | * @brief Todo. 45 | */ 46 | template 47 | struct PixelTraits> 48 | { 49 | static constexpr std::uint16_t num_bytes = sizeof(Pixel); 50 | 51 | static constexpr Pixel zero_element = 52 | Pixel(std::array()); 53 | }; 54 | 55 | } /* namespace core */ 56 | } /* namespace eos */ 57 | 58 | #endif /* EOS_IMAGE_PIXELTRAITS_HPP */ 59 | -------------------------------------------------------------------------------- /include/eos/core/image/opencv_interop.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/image/opencv_interop.hpp 5 | * 6 | * Copyright 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_IMAGE_OPENCV_INTEROP_HPP 23 | #define EOS_IMAGE_OPENCV_INTEROP_HPP 24 | 25 | #include "eos/core/Image.hpp" 26 | 27 | #include "opencv2/core/core.hpp" 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | namespace eos { 34 | namespace core { 35 | 36 | // We can support different types by making this a template and constexpr if? :-) 37 | inline cv::Mat to_mat(const Image4u& image) 38 | { 39 | cv::Mat opencv_matrix(static_cast(image.height()), static_cast(image.width()), CV_8UC4); 40 | for (int c = 0; c < image.width(); ++c) 41 | { // size_t 42 | for (int r = 0; r < image.height(); ++r) 43 | { 44 | // auto vals = image(r, c); 45 | opencv_matrix.at(r, c) = 46 | cv::Vec4b(image(r, c)[0], image(r, c)[1], image(r, c)[2], image(r, c)[3]); 47 | } 48 | } 49 | return opencv_matrix; 50 | }; 51 | 52 | inline cv::Mat to_mat(const Image3u& image) 53 | { 54 | cv::Mat opencv_matrix(image.height(), image.width(), CV_8UC3); 55 | for (int c = 0; c < image.width(); ++c) 56 | { // size_t 57 | for (int r = 0; r < image.height(); ++r) 58 | { 59 | // auto vals = image(r, c); 60 | opencv_matrix.at(r, c) = cv::Vec3b(image(r, c)[0], image(r, c)[1], image(r, c)[2]); 61 | } 62 | } 63 | return opencv_matrix; 64 | }; 65 | 66 | inline cv::Mat to_mat(const Image1d& image) 67 | { 68 | cv::Mat opencv_matrix(static_cast(image.height()), static_cast(image.width()), CV_64FC1); 69 | for (int c = 0; c < image.width(); ++c) 70 | { // size_t 71 | for (int r = 0; r < image.height(); ++r) 72 | { 73 | // auto vals = image(r, c); 74 | opencv_matrix.at(r, c) = image(r, c); 75 | } 76 | } 77 | return opencv_matrix; 78 | }; 79 | 80 | inline cv::Mat to_mat(const Image1u& image) 81 | { 82 | cv::Mat opencv_matrix(static_cast(image.height()), static_cast(image.width()), CV_8UC1); 83 | for (int c = 0; c < image.width(); ++c) 84 | { // size_t 85 | for (int r = 0; r < image.height(); ++r) 86 | { 87 | // auto vals = image(r, c); 88 | opencv_matrix.at(r, c) = image(r, c); 89 | } 90 | } 91 | return opencv_matrix; 92 | }; 93 | 94 | /** 95 | * Returns an Image3u from a given cv::Mat with type CV_8UC3. The channel order is not changed, i.e. if the 96 | * cv::Mat is BGR, the output Image3u will have BGR channel ordering too. 97 | */ 98 | inline Image3u from_mat(const cv::Mat& image) 99 | { 100 | if (image.type() != CV_8UC3) 101 | { 102 | throw std::runtime_error("Can only convert a CV_8UC3 cv::Mat to an eos::core::Image3u."); 103 | } 104 | 105 | Image3u converted(image.rows, image.cols); 106 | for (int r = 0; r < image.rows; ++r) 107 | { 108 | for (int c = 0; c < image.cols; ++c) 109 | { 110 | converted(r, c) = {image.at(r, c)[0], image.at(r, c)[1], 111 | image.at(r, c)[2]}; 112 | } 113 | } 114 | return converted; 115 | }; 116 | 117 | /** 118 | * Supports both CV_8UC3 and CV_8UC4 cv::Mat as input images. If a CV_8UC3 images is given, then all pixels of 119 | * the alpha channel of the returned image are set to 255. 120 | */ 121 | inline Image4u from_mat_with_alpha(const cv::Mat& image) 122 | { 123 | if (image.type() != CV_8UC3 && image.type() != CV_8UC4) 124 | { 125 | throw std::runtime_error("Can only convert a CV_8UC3 or CV_8UC4 cv::Mat to an eos::core::Image4u."); 126 | } 127 | 128 | Image4u converted(image.rows, image.cols); 129 | if (image.type() == CV_8UC3) 130 | { 131 | for (int r = 0; r < image.rows; ++r) 132 | { 133 | for (int c = 0; c < image.cols; ++c) 134 | { 135 | converted(r, c) = {image.at(r, c)[0], image.at(r, c)[1], 136 | image.at(r, c)[2], 255}; 137 | } 138 | } 139 | } else if (image.type() == CV_8UC4) 140 | { 141 | for (int r = 0; r < image.rows; ++r) 142 | { 143 | for (int c = 0; c < image.cols; ++c) 144 | { 145 | converted(r, c) = {image.at(r, c)[0], image.at(r, c)[1], 146 | image.at(r, c)[2], image.at(r, c)[3]}; 147 | } 148 | } 149 | } 150 | return converted; 151 | }; 152 | 153 | } /* namespace core */ 154 | } /* namespace eos */ 155 | 156 | #endif /* EOS_IMAGE_OPENCV_INTEROP_HPP */ 157 | -------------------------------------------------------------------------------- /include/eos/core/image/utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/image/utils.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_IMAGE_UTILS_HPP 23 | #define EOS_IMAGE_UTILS_HPP 24 | 25 | #include "eos/core/image/Pixel.hpp" 26 | 27 | #include 28 | 29 | namespace eos { 30 | namespace core { 31 | namespace image { 32 | 33 | /** 34 | * @brief Creates an image of given type, height and width, with all zeros. 35 | */ 36 | template 37 | Image zeros(int height, int width) noexcept 38 | { 39 | Image image(height, width); 40 | for (int y = 0; y < height; ++y) 41 | { 42 | for (int x = 0; x < width; ++x) 43 | { 44 | image(y, x) = PixelTraits::zero_element; 45 | } 46 | } 47 | return image; 48 | }; 49 | 50 | /** 51 | * @brief Creates an image of given type, height and width, with a constant value for all pixels. 52 | */ 53 | template 54 | Image constant(int height, int width, PixelType value) noexcept 55 | { 56 | Image image(height, width); 57 | for (int y = 0; y < height; ++y) 58 | { 59 | for (int x = 0; x < width; ++x) 60 | { 61 | image(y, x) = value; 62 | } 63 | } 64 | return image; 65 | }; 66 | 67 | } // namespace image 68 | } // namespace core 69 | } // namespace eos 70 | 71 | #endif /* EOS_IMAGE_UTILS_HPP */ 72 | -------------------------------------------------------------------------------- /include/eos/core/math.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/math.hpp 5 | * 6 | * Copyright 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_MATH_HPP 23 | #define EOS_MATH_HPP 24 | 25 | #include 26 | #include 27 | 28 | namespace eos { 29 | namespace core { 30 | 31 | /** 32 | * @brief Compile-time constant for pi (19 digits). 33 | */ 34 | template 35 | constexpr T pi = T(3.1415926535897932385L); 36 | 37 | /** 38 | * @brief Convert given degrees to radians. 39 | */ 40 | template 41 | T radians(T degrees) 42 | { 43 | // Note: We may want to remove this assert, to allow ceres::Jet as type. 44 | static_assert(std::numeric_limits::is_iec559, "radians() only accepts floating-point inputs."); 45 | return degrees * static_cast(0.01745329251994329576923690768489); 46 | }; 47 | 48 | /** 49 | * @brief Convert given radians to degree. 50 | */ 51 | template 52 | T degrees(T radians) 53 | { 54 | // Note: We may want to remove this assert, to allow ceres::Jet as type. 55 | static_assert(std::numeric_limits::is_iec559, "radians() only accepts floating-point inputs."); 56 | return radians * static_cast(57.295779513082320876798154814105); 57 | }; 58 | 59 | /** 60 | * @brief Returns the sign of the given number (-1, 0 or +1). 61 | * 62 | * According to StackOverflow, the < 0 part of the check triggers GCC's -Wtype-limits warning when 63 | * instantiated for an unsigned type. Thus we use two overloads, for signed and unsigned types. 64 | */ 65 | template 66 | typename std::enable_if::value, int>::type inline constexpr sign(T const x) 67 | { 68 | return T(0) < x; 69 | }; 70 | 71 | /** 72 | * @brief Returns the sign of the given number (-1, 0 or +1). 73 | * 74 | * According to StackOverflow, the < 0 part of the check triggers GCC's -Wtype-limits warning when 75 | * instantiated for an unsigned type. Thus we use two overloads, for signed and unsigned types. 76 | */ 77 | template 78 | typename std::enable_if::value, int>::type inline constexpr sign(T const x) 79 | { 80 | return (T(0) < x) - (x < T(0)); 81 | }; 82 | 83 | } /* namespace core */ 84 | } /* namespace eos */ 85 | 86 | #endif /* EOS_MATH_HPP */ 87 | -------------------------------------------------------------------------------- /include/eos/core/read_pts_landmarks.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/core/read_pts_landmarks.hpp 5 | * 6 | * Copyright 2014, 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_READ_PTS_LANDMARKS_HPP 23 | #define EOS_READ_PTS_LANDMARKS_HPP 24 | 25 | #include "eos/core/Landmark.hpp" 26 | 27 | #include "Eigen/Core" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace eos { 35 | namespace core { 36 | 37 | /** 38 | * Reads an ibug .pts landmark file and returns an ordered vector with 39 | * the 68 2D landmark coordinates. 40 | * 41 | * @param[in] filename Path to a .pts file. 42 | * @return An ordered vector with the 68 ibug landmarks. 43 | */ 44 | inline LandmarkCollection read_pts_landmarks(std::string filename) 45 | { 46 | using Eigen::Vector2f; 47 | using std::getline; 48 | using std::string; 49 | LandmarkCollection landmarks; 50 | landmarks.reserve(68); 51 | 52 | std::ifstream file(filename); 53 | if (!file) 54 | { 55 | throw std::runtime_error(string("Could not open landmark file: " + filename)); 56 | } 57 | 58 | string line; 59 | // Skip the first 3 lines, they're header lines: 60 | getline(file, line); // 'version: 1' 61 | getline(file, line); // 'n_points : 68' 62 | getline(file, line); // '{' 63 | 64 | int ibugId = 1; 65 | while (getline(file, line)) 66 | { 67 | if (line == "}") 68 | { // end of the file 69 | break; 70 | } 71 | std::stringstream lineStream(line); 72 | 73 | Landmark landmark; 74 | landmark.name = std::to_string(ibugId); 75 | if (!(lineStream >> landmark.coordinates[0] >> landmark.coordinates[1])) 76 | { 77 | throw std::runtime_error(string("Landmark format error while parsing the line: " + line)); 78 | } 79 | // From the iBug website: 80 | // "Please note that the re-annotated data for this challenge are saved in the Matlab convention of 1 81 | // being the first index, i.e. the coordinates of the top left pixel in an image are x=1, y=1." 82 | // ==> So we shift every point by 1: 83 | landmark.coordinates[0] -= 1.0f; 84 | landmark.coordinates[1] -= 1.0f; 85 | landmarks.emplace_back(landmark); 86 | ++ibugId; 87 | } 88 | return landmarks; 89 | }; 90 | 91 | } /* namespace core */ 92 | } /* namespace eos */ 93 | 94 | #endif /* EOS_READ_PTS_LANDMARKS_HPP */ 95 | -------------------------------------------------------------------------------- /include/eos/cpp17/clamp.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/cpp17/clamp.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_CLAMP_HPP_ 23 | #define EOS_CLAMP_HPP_ 24 | 25 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 26 | #include 27 | namespace eos { 28 | namespace cpp17 { 29 | using ::std::clamp; 30 | } 31 | } 32 | #else 33 | namespace eos { 34 | namespace cpp17 { 35 | // Returns val constrained to [min, max] 36 | template 37 | constexpr const T& clamp(const T& val, const T& min, const T& max) 38 | { 39 | // this is the implementation that uses: 40 | return ((max < val) ? max : (val < min) ? min : val); 41 | } 42 | } 43 | } 44 | #endif 45 | 46 | #endif /* EOS_CLAMP_HPP_ */ 47 | -------------------------------------------------------------------------------- /include/eos/cpp17/detail/akrzemi1_optional_serialization.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/cpp17/detail/akrzemi1_optional_serialization.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_AKRZEMI1_OPTIONAL_SERIALIZATION_HPP_ 23 | #define EOS_AKRZEMI1_OPTIONAL_SERIALIZATION_HPP_ 24 | 25 | #include "eos/cpp17/detail/akrzemi1_optional.hpp" 26 | 27 | #include "cereal/cereal.hpp" 28 | 29 | namespace cereal { 30 | 31 | //! Saving for akrzemi1::optional 32 | template 33 | inline void CEREAL_SAVE_FUNCTION_NAME(Archive& ar, const akrzemi1::optional& optional) 34 | { 35 | if (!optional) 36 | { 37 | ar(CEREAL_NVP_("nullopt", true)); 38 | } else 39 | { 40 | ar(CEREAL_NVP_("nullopt", false), CEREAL_NVP_("data", *optional)); 41 | } 42 | } 43 | 44 | //! Loading for akrzemi1::optional 45 | template 46 | inline void CEREAL_LOAD_FUNCTION_NAME(Archive& ar, akrzemi1::optional& optional) 47 | { 48 | bool nullopt; 49 | ar(CEREAL_NVP_("nullopt", nullopt)); 50 | 51 | if (nullopt) 52 | { 53 | optional = akrzemi1::nullopt; 54 | } else 55 | { 56 | T value; 57 | ar(CEREAL_NVP_("data", value)); 58 | optional = std::move(value); 59 | } 60 | } 61 | 62 | } // namespace cereal 63 | 64 | #endif /* EOS_AKRZEMI1_OPTIONAL_SERIALIZATION_HPP_ */ 65 | -------------------------------------------------------------------------------- /include/eos/cpp17/detail/mpark_variant_serialization.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/cpp17/detail/mpark_variant_serialization.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_MPARK_VARIANT_SERIALIZATION_HPP_ 23 | #define EOS_MPARK_VARIANT_SERIALIZATION_HPP_ 24 | 25 | #include "eos/cpp17/detail/mpark_variant.hpp" 26 | 27 | #include "cereal/cereal.hpp" 28 | 29 | #include 30 | #include 31 | 32 | namespace cereal { 33 | namespace variant_detail { 34 | 35 | //! @internal 36 | template 37 | struct variant_save_visitor 38 | { 39 | variant_save_visitor(Archive& ar_) : ar(ar_) {} 40 | 41 | template 42 | void operator()(T const& value) const 43 | { 44 | ar(CEREAL_NVP_("data", value)); 45 | } 46 | 47 | Archive& ar; 48 | }; 49 | 50 | //! @internal 51 | template 52 | typename std::enable_if, void>::type 53 | load_variant(Archive& /*ar*/, int /*target*/, Variant& /*variant*/) 54 | { 55 | throw ::cereal::Exception("Error traversing variant during load"); 56 | } 57 | 58 | //! @internal 59 | template 60 | typename std::enable_if < 61 | N, void>::type load_variant(Archive& ar, int target, Variant& variant) 62 | { 63 | if (N == target) 64 | { 65 | H value; 66 | ar(CEREAL_NVP_("data", value)); 67 | variant = std::move(value); 68 | } else 69 | load_variant(ar, target, variant); 70 | } 71 | 72 | } // namespace variant_detail 73 | 74 | //! Saving for mpark::variant 75 | template 76 | inline void CEREAL_SAVE_FUNCTION_NAME(Archive& ar, 77 | mpark::variant const& variant) 78 | { 79 | std::int32_t index = static_cast(variant.index()); 80 | ar(CEREAL_NVP_("index", index)); 81 | variant_detail::variant_save_visitor visitor(ar); 82 | mpark::visit(visitor, variant); 83 | } 84 | 85 | //! Loading for mpark::variant 86 | template 87 | inline void CEREAL_LOAD_FUNCTION_NAME(Archive& ar, mpark::variant& variant) 88 | { 89 | using variant_t = typename mpark::variant; 90 | 91 | std::int32_t index; 92 | ar(CEREAL_NVP_("index", index)); 93 | if (index >= static_cast(mpark::variant_size_v)) 94 | throw Exception("Invalid 'index' selector when deserializing mpark::variant"); 95 | 96 | variant_detail::load_variant<0, variant_t, VariantTypes...>(ar, index, variant); 97 | } 98 | 99 | } // namespace cereal 100 | 101 | #endif /* EOS_MPARK_VARIANT_SERIALIZATION_HPP_ */ 102 | -------------------------------------------------------------------------------- /include/eos/cpp17/optional.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/cpp17/optional.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_OPTIONAL_HPP_ 23 | #define EOS_OPTIONAL_HPP_ 24 | 25 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 26 | #include 27 | namespace eos { 28 | namespace cpp17 { 29 | using ::std::optional; 30 | using ::std::nullopt; 31 | } 32 | } 33 | #else 34 | #include "eos/cpp17/detail/akrzemi1_optional.hpp" 35 | namespace eos { 36 | namespace cpp17 { 37 | using ::akrzemi1::optional; 38 | using ::akrzemi1::nullopt; 39 | } 40 | } 41 | #endif 42 | 43 | #endif /* EOS_OPTIONAL_HPP_ */ 44 | -------------------------------------------------------------------------------- /include/eos/cpp17/optional_serialization.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/cpp17/optional_serialization.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_OPTIONAL_SERIALIZATION_HPP_ 23 | #define EOS_OPTIONAL_SERIALIZATION_HPP_ 24 | 25 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 26 | #include "cereal/types/optional.hpp" 27 | #else 28 | #include "eos/cpp17/detail/akrzemi1_optional_serialization.hpp" 29 | #endif 30 | 31 | #endif /* EOS_OPTIONAL_SERIALIZATION_HPP_ */ 32 | -------------------------------------------------------------------------------- /include/eos/cpp17/variant.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/cpp17/variant.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_VARIANT_HPP_ 23 | #define EOS_VARIANT_HPP_ 24 | 25 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 26 | #include 27 | namespace eos { 28 | namespace cpp17 { 29 | using ::std::variant; 30 | using ::std::holds_alternative; 31 | using ::std::get; 32 | } 33 | } 34 | #else 35 | #include "eos/cpp17/detail/mpark_variant.hpp" 36 | namespace eos { 37 | namespace cpp17 { 38 | using ::mpark::variant; 39 | using ::mpark::holds_alternative; 40 | using ::mpark::get; 41 | } 42 | } 43 | #endif 44 | 45 | #endif /* EOS_VARIANT_HPP_ */ 46 | -------------------------------------------------------------------------------- /include/eos/cpp17/variant_serialization.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/cpp17/variant_serialization.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_VARIANT_SERIALIZATION_HPP_ 23 | #define EOS_VARIANT_SERIALIZATION_HPP_ 24 | 25 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 26 | #include "cereal/types/variant.hpp" 27 | #else 28 | #include "eos/cpp17/detail/mpark_variant_serialization.hpp" 29 | #endif 30 | 31 | #endif /* EOS_VARIANT_SERIALIZATION_HPP_ */ 32 | -------------------------------------------------------------------------------- /include/eos/fitting/FittingResult.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/fitting/FittingResult.hpp 5 | * 6 | * Copyright 2016 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_FITTINGRESULT_HPP 23 | #define EOS_FITTINGRESULT_HPP 24 | 25 | #include "eos/fitting/RenderingParameters.hpp" 26 | 27 | #include 28 | 29 | namespace eos { 30 | namespace fitting { 31 | 32 | /** 33 | * @brief A struct holding the result from a fitting. 34 | * 35 | * Holds the parameters to store and reproduce a shape fitting result: 36 | * Rendering parameters, PCA shape and expression coefficients. 37 | */ 38 | struct FittingResult 39 | { 40 | RenderingParameters rendering_parameters; 41 | std::vector pca_shape_coefficients; 42 | std::vector expression_coefficients; 43 | }; 44 | 45 | } /* namespace fitting */ 46 | } /* namespace eos */ 47 | 48 | #endif /* EOS_FITTINGRESULT_HPP */ 49 | -------------------------------------------------------------------------------- /include/eos/fitting/contour_correspondence.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrikhuber/eos/ce984207a51c2edd2e7568a64fec9b98ef61aeed/include/eos/fitting/contour_correspondence.hpp -------------------------------------------------------------------------------- /include/eos/fitting/detail/eigen_quaternion_cerealisation.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/fitting/detail/eigen_quaternion_cerealisation.hpp 5 | * 6 | * Copyright 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_EIGEN_QUATERNION_CEREALISATION_HPP 23 | #define EOS_EIGEN_QUATERNION_CEREALISATION_HPP 24 | 25 | #include "cereal/cereal.hpp" 26 | 27 | #include "Eigen/Core" 28 | 29 | /** 30 | * @brief Serialisation of Eigen's \c Eigen::Quaternion for the serialisation library cereal 31 | * (http://uscilab.github.io/cereal/index.html). 32 | * 33 | * Contains serialisation for \c Eigen::Quaternion. 34 | */ 35 | namespace Eigen { 36 | 37 | /** 38 | * @brief Serialisation of a Eigen::Quaternion using cereal. 39 | * 40 | * Valid ScalarType's are float and double (Quaternionf and Quaterniond). 41 | * 42 | * @param[in] ar The archive to (de)serialise. 43 | * @param[in] q The quaternion to (de)serialise. 44 | */ 45 | template 46 | void serialize(Archive& ar, Eigen::Quaternion& q) 47 | { 48 | ar(q.w(), q.x(), q.y(), q.z()); 49 | }; 50 | 51 | } /* namespace Eigen */ 52 | 53 | #endif /* EOS_EIGEN_QUATERNION_CEREALISATION_HPP */ 54 | -------------------------------------------------------------------------------- /include/eos/fitting/nonlinear_camera_estimation.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/fitting/nonlinear_camera_estimation.hpp 5 | * 6 | * Copyright 2015, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_NONLINEAR_CAMERA_ESTIMATION_HPP 23 | #define EOS_NONLINEAR_CAMERA_ESTIMATION_HPP 24 | 25 | #include "eos/fitting/detail/nonlinear_camera_estimation_detail.hpp" 26 | #include "eos/fitting/RenderingParameters.hpp" 27 | 28 | #include "Eigen/Geometry" 29 | #include "unsupported/Eigen/NonLinearOptimization" 30 | 31 | #include 32 | #include 33 | 34 | namespace eos { 35 | namespace fitting { 36 | 37 | /** 38 | * @brief This algorithm estimates the rotation angles and translation of the model, as 39 | * well as the viewing frustum of the camera, given a set of corresponding 2D-3D points. 40 | * 41 | * It assumes an orthographic camera and estimates 6 parameters, 42 | * [r_x, r_y, r_z, t_x, t_y, frustum_scale], where the first five describe how to transform 43 | * the model, and the last one describes the cameras viewing frustum (see CameraParameters). 44 | * This 2D-3D correspondence problem is solved using Eigen's LevenbergMarquardt algorithm. 45 | * 46 | * The method is slightly inspired by "Computer Vision: Models Learning and Inference", 47 | * Simon J.D. Prince, 2012, but different in a lot of respects. 48 | * 49 | * Eigen's LM implementation requires at least 6 data points, so we require >= 6 corresponding points. 50 | * 51 | * Notes/improvements: 52 | * The algorithm works reliable as it is, however, it could be improved with the following: 53 | * - A better initial guess (see e.g. Prince) 54 | * - We could add a parameter to pass an initial guess 55 | * - Using the analytic derivatives instead of Eigen::NumericalDiff - they're easy to calculate. 56 | * 57 | * @param[in] image_points A list of 2D image points. 58 | * @param[in] model_points Corresponding points of a 3D model. 59 | * @param[in] width Width of the image (or viewport). 60 | * @param[in] height Height of the image (or viewport). 61 | * @return The estimated model and camera parameters. 62 | */ 63 | inline RenderingParameters estimate_orthographic_camera(std::vector image_points, 64 | std::vector model_points, int width, 65 | int height) 66 | { 67 | assert(image_points.size() == model_points.size()); 68 | assert(image_points.size() >= 6); // Number of correspondence points given needs to be equal to or larger than 6 69 | 70 | const float aspect = static_cast(width) / height; 71 | 72 | // Set up the initial parameter vector and the cost function: 73 | int num_params = 6; 74 | Eigen::VectorXd parameters; // [rot_x_pitch, rot_y_yaw, rot_z_roll, t_x, t_y, frustum_scale] 75 | parameters.setConstant(num_params, 0.0); // Set all 6 values to zero (except frustum_scale, see next line) 76 | parameters[5] = 110.0; // This is just a rough hand-chosen scaling estimate - we could do a lot better. But it works. 77 | detail::OrthographicParameterProjection cost_function(image_points, model_points, width, height); 78 | 79 | // Note: we have analytical derivatives, so we should use them! 80 | Eigen::NumericalDiff cost_function_with_derivative(cost_function, 81 | 0.0001); 82 | // I had to change the default value of epsfcn, it works well around 0.0001. It couldn't produce the 83 | // derivative with the default, I guess the changes in the gradient were too small. 84 | 85 | Eigen::LevenbergMarquardt> lm( 86 | cost_function_with_derivative); 87 | const auto info = lm.minimize(parameters); // we could or should use the return value 88 | // 'parameters' contains the solution now. 89 | 90 | Frustum camera_frustum{ 91 | -1.0f * aspect * static_cast(parameters[5]), 1.0f * aspect * static_cast(parameters[5]), 92 | -1.0f * static_cast(parameters[5]), 1.0f * static_cast(parameters[5])}; 93 | return RenderingParameters(CameraType::Orthographic, camera_frustum, static_cast(parameters[0]), 94 | static_cast(parameters[1]), static_cast(parameters[2]), 95 | static_cast(parameters[3]), static_cast(parameters[4]), width, 96 | height); 97 | }; 98 | 99 | } /* namespace fitting */ 100 | } /* namespace eos */ 101 | 102 | #endif /* EOS_NONLINEAR_CAMERA_ESTIMATION_HPP */ 103 | -------------------------------------------------------------------------------- /include/eos/fitting/rotation_angles.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/fitting/rotation_angles.hpp 5 | * 6 | * Copyright 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_FITTING_ROTATION_ANGLES_HPP 23 | #define EOS_FITTING_ROTATION_ANGLES_HPP 24 | 25 | #include "eos/core/math.hpp" 26 | 27 | #include "Eigen/Core" 28 | 29 | #include 30 | 31 | namespace eos { 32 | namespace fitting { 33 | 34 | /** 35 | * @brief Computes Tait-Bryan angles (in radians) from the given rotation matrix and axes order. 36 | * 37 | * Calls Eigen::Matrix3::eulerAngles(), but then swap the solution for the one where the middle (pitch) axis 38 | * is constrained to -PI/2 to PI/2. 39 | */ 40 | template 41 | inline Eigen::Matrix tait_bryan_angles(Eigen::Matrix rotation_matrix, Eigen::Index axis_0, 42 | Eigen::Index axis_1, Eigen::Index axis_2) 43 | { 44 | Eigen::Matrix euler_angles = rotation_matrix.eulerAngles(axis_0, axis_1, axis_2); 45 | // Eigen::Matrix3X.eulerAngles() returns the solution where the first axis is constrained from 0 to PI. 46 | // This is not what we usually want in robotics; we want the other solution (there are two in the general 47 | // case) where the middle (pitch) axis is constrained to -PI/2 to PI/2. See 48 | // https://gitlab.com/libeigen/eigen/-/issues/2617#note_1298729055 49 | if (std::abs(euler_angles(1)) > T(core::pi / 2.0)) 50 | { 51 | euler_angles.array() -= T(core::pi) * euler_angles.array().sign(); 52 | euler_angles(1) = -euler_angles(1); 53 | } 54 | return euler_angles; 55 | }; 56 | 57 | } /* namespace fitting */ 58 | } /* namespace eos */ 59 | 60 | #endif /* EOS_FITTING_ROTATION_ANGLES_HPP */ 61 | -------------------------------------------------------------------------------- /include/eos/morphablemodel/Blendshape.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/morphablemodel/Blendshape.hpp 5 | * 6 | * Copyright 2015 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_BLENDSHAPE_HPP 23 | #define EOS_BLENDSHAPE_HPP 24 | 25 | #include "cereal/archives/binary.hpp" 26 | #include "cereal/types/string.hpp" 27 | #include "eos/morphablemodel/io/eigen_cerealisation.hpp" 28 | 29 | #include "Eigen/Core" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace eos { 37 | namespace morphablemodel { 38 | 39 | /** 40 | * @brief A class representing a 3D blendshape. 41 | * 42 | * A blendshape is a vector of offsets that transform the vertices of 43 | * a given mesh or shape instance. Usually, a blendshape is associated 44 | * with a deformation like a particular facial expression or a phoneme. 45 | */ 46 | struct Blendshape 47 | { 48 | std::string name; ///< Name of the blendshape. 49 | Eigen::VectorXf deformation; ///< A 3m x 1 col-vector (xyzxyz...)', where m is the number of 50 | ///< model-vertices. Has the same format as PcaModel::mean. 51 | 52 | friend class cereal::access; 53 | /** 54 | * Serialises this class using cereal. 55 | * 56 | * @param[in] archive The archive to serialise to (or to serialise from). 57 | */ 58 | template 59 | void serialize(Archive& archive) 60 | { 61 | archive(CEREAL_NVP(name), CEREAL_NVP(deformation)); 62 | }; 63 | }; 64 | 65 | /** 66 | * Shorthand notation for an std::vector. 67 | */ 68 | using Blendshapes = std::vector; 69 | 70 | /** 71 | * Helper method to load a file with blendshapes from 72 | * a cereal::BinaryInputArchive from the harddisk. 73 | * 74 | * @param[in] filename Filename to a blendshapes-file. 75 | * @return The loaded blendshapes. 76 | * @throw std::runtime_error When the file given in \c filename fails to be opened (most likely because the 77 | * file doesn't exist). 78 | */ 79 | inline std::vector load_blendshapes(std::string filename) 80 | { 81 | std::vector blendshapes; 82 | 83 | std::ifstream file(filename, std::ios::binary); 84 | if (!file) 85 | { 86 | throw std::runtime_error("Error opening given file: " + filename); 87 | } 88 | cereal::BinaryInputArchive input_archive(file); 89 | input_archive(blendshapes); 90 | 91 | return blendshapes; 92 | }; 93 | 94 | /** 95 | * Helper method to save a set of blendshapes to the 96 | * harddisk as a cereal::BinaryOutputArchive. 97 | * 98 | * @param[in] blendshapes The blendshapes to be saved. 99 | * @param[in] filename Filename for the blendshapes. 100 | */ 101 | inline void save_blendshapes(const std::vector& blendshapes, std::string filename) 102 | { 103 | std::ofstream file(filename, std::ios::binary); 104 | if (!file) 105 | { 106 | throw std::runtime_error("Error creating given file: " + filename); 107 | } 108 | cereal::BinaryOutputArchive output_archive(file); 109 | output_archive(blendshapes); 110 | }; 111 | 112 | /** 113 | * @brief Copies the blendshapes into a matrix, with each column being a blendshape. 114 | * 115 | * @param[in] blendshapes Vector of blendshapes. 116 | * @return The resulting matrix. 117 | */ 118 | inline Eigen::MatrixXf to_matrix(const std::vector& blendshapes) 119 | { 120 | assert(blendshapes.size() > 0); 121 | // Todo: Assert all blendshapes have to have the same number of rows, and one col 122 | 123 | Eigen::MatrixXf blendshapes_as_basis(blendshapes[0].deformation.rows(), blendshapes.size()); 124 | for (int i = 0; i < blendshapes.size(); ++i) 125 | { 126 | blendshapes_as_basis.col(i) = blendshapes[i].deformation; 127 | } 128 | return blendshapes_as_basis; 129 | }; 130 | 131 | /** 132 | * @brief Maps an std::vector of coefficients with Eigen::Map, so it can be multiplied 133 | * with a blendshapes matrix. 134 | * 135 | * Note that the resulting Eigen::Map only lives as long as the data given lives and is in scope. 136 | * 137 | * @param[in] coefficients Vector of blendshape coefficients. 138 | * @return An Eigen::Map pointing to the given coefficients data. 139 | */ 140 | inline Eigen::Map to_vector(const std::vector& coefficients) 141 | { 142 | return Eigen::Map(coefficients.data(), coefficients.size()); 143 | }; 144 | 145 | } /* namespace morphablemodel */ 146 | } /* namespace eos */ 147 | 148 | #endif /* EOS_BLENDSHAPE_HPP */ 149 | -------------------------------------------------------------------------------- /include/eos/morphablemodel/EdgeTopology.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/morphablemodel/EdgeTopology.hpp 5 | * 6 | * Copyright 2016 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_EDGETOPOLOGY_HPP 23 | #define EOS_EDGETOPOLOGY_HPP 24 | 25 | #include "cereal/cereal.hpp" 26 | #include "cereal/types/array.hpp" 27 | #include "cereal/types/vector.hpp" 28 | #include "cereal/archives/json.hpp" 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | namespace eos { 35 | namespace morphablemodel { 36 | 37 | /** 38 | * @brief A struct containing a 3D shape model's edge topology. 39 | * 40 | * This struct contains all edges of a 3D mesh, and for each edge, it 41 | * contains the two faces and the two vertices that are adjacent to that 42 | * edge. This is used in the iterated closest edge fitting (ICEF). 43 | * 44 | * Note: The indices are 1-based, so 1 needs to be subtracted before using 45 | * them as mesh indices. An index of 0 as first array element means that 46 | * it's an edge that lies on the mesh boundary, i.e. they are only 47 | * adjacent to one face. 48 | * We should explore a less error-prone way to store this data, but that's 49 | * how it is done in Matlab by the original code. 50 | * 51 | * adjacent_faces.size() is equal to adjacent_vertices.size(). 52 | */ 53 | struct EdgeTopology 54 | { 55 | std::vector> 56 | adjacent_faces; ///< num_edges x 2 matrix storing faces adjacent to each edge 57 | std::vector> 58 | adjacent_vertices; ///< num_edges x 2 matrix storing vertices adjacent to each edge 59 | 60 | friend class cereal::access; 61 | /** 62 | * Serialises this class using cereal. 63 | * 64 | * @param[in] archive The archive to serialise to (or to serialise from). 65 | */ 66 | template 67 | void serialize(Archive& archive) 68 | { 69 | archive(CEREAL_NVP(adjacent_faces), CEREAL_NVP(adjacent_vertices)); 70 | }; 71 | }; 72 | 73 | /** 74 | * Saves a 3DMM edge topology file to a json file. 75 | * 76 | * @param[in] edge_topology A model's edge topology. 77 | * @param[in] filename The file to write. 78 | * @throws std::runtime_error if unable to open the given file for writing. 79 | */ 80 | inline void save_edge_topology(EdgeTopology edge_topology, std::string filename) 81 | { 82 | std::ofstream file(filename); 83 | if (!file) 84 | { 85 | throw std::runtime_error("Error creating given file: " + filename); 86 | } 87 | cereal::JSONOutputArchive output_archive(file); 88 | output_archive(cereal::make_nvp("edge_topology", edge_topology)); 89 | }; 90 | 91 | /** 92 | * Load a 3DMM edge topology file from a json file. 93 | * 94 | * @param[in] filename The file to load the edge topology from. 95 | * @return A struct containing the edge topology. 96 | * @throws std::runtime_error if unable to open the given file for writing. 97 | */ 98 | inline EdgeTopology load_edge_topology(std::string filename) 99 | { 100 | EdgeTopology edge_topology; 101 | std::ifstream file(filename); 102 | if (!file) 103 | { 104 | throw std::runtime_error("Error opening file for reading: " + filename); 105 | } 106 | cereal::JSONInputArchive output_archive(file); 107 | output_archive(cereal::make_nvp("edge_topology", edge_topology)); 108 | 109 | return edge_topology; 110 | }; 111 | 112 | } /* namespace morphablemodel */ 113 | } /* namespace eos */ 114 | 115 | #endif /* EOS_EDGETOPOLOGY_HPP */ 116 | -------------------------------------------------------------------------------- /include/eos/morphablemodel/ExpressionModel.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/morphablemodel/ExpressionModel.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_EXPRESSION_MODEL_HPP 23 | #define EOS_EXPRESSION_MODEL_HPP 24 | 25 | #include "eos/morphablemodel/PcaModel.hpp" 26 | #include "eos/morphablemodel/Blendshape.hpp" 27 | #include "eos/cpp17/variant.hpp" 28 | 29 | #include "Eigen/Core" 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | namespace eos { 36 | namespace morphablemodel { 37 | 38 | /** 39 | * @brief Type alias to represent an expression model, which can either consist of blendshapes or a PCA model. 40 | * 41 | * Defining a type alias so that we don't have to spell out the type everywhere. 42 | */ 43 | using ExpressionModel = cpp17::variant; 44 | 45 | /** 46 | * Returns a sample from an expression model with the given expression coefficients. 47 | * 48 | * The underlying expression model can be both a PcaModel as well as a Blendshapes model. 49 | * If a partial coefficient vector is given, it is filled with zeros up to the end. 50 | * 51 | * @param[in] expression_model The expression model to draw a sample from. 52 | * @param[in] expression_coefficients The coefficients used to generate the expression sample. 53 | * @return A model instance with given coefficients. 54 | */ 55 | inline Eigen::VectorXf draw_sample(const ExpressionModel& expression_model, 56 | std::vector expression_coefficients) 57 | { 58 | Eigen::VectorXf expression_sample; 59 | // Get a sample of the expression model, depending on whether it's a PcaModel or Blendshapes: 60 | if (cpp17::holds_alternative(expression_model)) 61 | { 62 | const auto& pca_expression_model = cpp17::get(expression_model); 63 | if (expression_coefficients.empty()) 64 | { 65 | expression_sample = pca_expression_model.get_mean(); 66 | } else 67 | { 68 | expression_sample = pca_expression_model.draw_sample(expression_coefficients); 69 | } 70 | } else if (cpp17::holds_alternative(expression_model)) 71 | { 72 | const auto& expression_blendshapes = cpp17::get(expression_model); 73 | assert(expression_blendshapes.size() > 0); 74 | if (expression_coefficients.empty()) 75 | { 76 | expression_sample.setZero(expression_blendshapes[0].deformation.rows()); 77 | } else 78 | { 79 | expression_sample = to_matrix(expression_blendshapes) * 80 | Eigen::Map(expression_coefficients.data(), 81 | expression_coefficients.size()); 82 | } 83 | } else 84 | { 85 | throw std::runtime_error("The given ExpressionModel doesn't contain an expression model in the form " 86 | "of a PcaModel or Blendshapes."); 87 | } 88 | return expression_sample; 89 | }; 90 | 91 | } /* namespace morphablemodel */ 92 | } /* namespace eos */ 93 | 94 | #endif /* EOS_EXPRESSION_MODEL_HPP */ 95 | -------------------------------------------------------------------------------- /include/eos/morphablemodel/coefficients.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/morphablemodel/coefficients.hpp 5 | * 6 | * Copyright 2016 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_COEFFICIENTS_HPP 23 | #define EOS_COEFFICIENTS_HPP 24 | 25 | #include "cereal/cereal.hpp" 26 | #include "cereal/archives/json.hpp" 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | namespace eos { 33 | namespace morphablemodel { 34 | 35 | /** 36 | * Saves coefficients (for example PCA shape coefficients) to a json file. 37 | * 38 | * @param[in] coefficients A vector of coefficients. 39 | * @param[in] filename The file to write. 40 | * @throws std::runtime_error if unable to open the given file for writing. 41 | */ 42 | inline void save_coefficients(std::vector coefficients, std::string filename) 43 | { 44 | std::ofstream file(filename); 45 | if (!file) 46 | { 47 | throw std::runtime_error("Error opening file for writing: " + filename); 48 | } 49 | cereal::JSONOutputArchive output_archive(file); 50 | output_archive(cereal::make_nvp("shape_coefficients", coefficients)); 51 | }; 52 | 53 | /** 54 | * Loads coefficients (for example PCA shape coefficients) from a json file. 55 | * 56 | * @param[in] filename The file to read. 57 | * @return Returns vector of floats. 58 | * @throws std::runtime_error if unable to open the given file for reading. 59 | */ 60 | inline std::vector load_coefficients(std::string filename) 61 | { 62 | std::vector coefficients; 63 | std::ifstream file(filename); 64 | if (!file) 65 | { 66 | throw std::runtime_error("Error opening file for reading: " + filename); 67 | } 68 | cereal::JSONInputArchive input_archive(file); 69 | input_archive(cereal::make_nvp("shape_coefficients", coefficients)); 70 | return coefficients; 71 | }; 72 | 73 | } /* namespace morphablemodel */ 74 | } /* namespace eos */ 75 | 76 | #endif /* EOS_COEFFICIENTS_HPP */ 77 | -------------------------------------------------------------------------------- /include/eos/morphablemodel/io/eigen_cerealisation.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/morphablemodel/io/eigen_cerealisation.hpp 5 | * 6 | * Copyright 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_EIGEN_MATRIX_BINARY_CEREALISATION_HPP 23 | #define EOS_EIGEN_MATRIX_BINARY_CEREALISATION_HPP 24 | 25 | #include "cereal/cereal.hpp" 26 | 27 | #include "Eigen/Core" 28 | 29 | #include 30 | 31 | /** 32 | * @brief Serialisation of Eigen matrices for the serialisation 33 | * library cereal (http://uscilab.github.io/cereal/index.html). 34 | * 35 | * Contains serialisation for Eigen matrices to binary archives, i.e. matrices like 36 | * \c Eigen::MatrixXf, \c Eigen::Matrix4d, or \c Eigen::Vector3f. 37 | * 38 | * Todo: Add serialisation to and from JSON. Need to find out how to combine the two 39 | * variants of SFINAE that are used. 40 | */ 41 | namespace cereal { 42 | 43 | /** 44 | * @brief Serialise an Eigen::Matrix using cereal. 45 | * 46 | * Note: Writes the binary data from Matrix::data(), so not sure what happens if a matrix ever has 47 | * non-contiguous data (if that can ever happen with Eigen). 48 | * 49 | * @param[in] ar The archive to serialise to. 50 | * @param[in] matrix The matrix to serialise. 51 | */ 52 | template 53 | inline 54 | typename std::enable_if, Archive>::value, void>::type 55 | save(Archive& ar, const Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>& matrix) 56 | { 57 | const std::int32_t rows = static_cast(matrix.rows()); 58 | const std::int32_t cols = static_cast(matrix.cols()); 59 | ar(rows); 60 | ar(cols); 61 | ar(binary_data(matrix.data(), rows * cols * sizeof(_Scalar))); 62 | }; 63 | 64 | /** 65 | * @brief De-serialise an Eigen::Matrix using cereal. 66 | * 67 | * Reads the block of binary data back from a cereal archive into the Eigen::Matrix. 68 | * 69 | * @param[in] ar The archive to deserialise from. 70 | * @param[in] matrix The matrix to deserialise into. 71 | */ 72 | template 73 | inline 74 | typename std::enable_if, Archive>::value, void>::type 75 | load(Archive& ar, Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>& matrix) 76 | { 77 | std::int32_t rows; 78 | std::int32_t cols; 79 | ar(rows); 80 | ar(cols); 81 | 82 | matrix.resize(rows, cols); 83 | 84 | ar(binary_data(matrix.data(), static_cast(rows * cols * sizeof(_Scalar)))); 85 | }; 86 | 87 | } /* namespace cereal */ 88 | 89 | #endif /* EOS_EIGEN_MATRIX_BINARY_CEREALISATION_HPP */ 90 | -------------------------------------------------------------------------------- /include/eos/render/ProjectionType.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/ProjectionType.hpp 5 | * 6 | * Copyright 2019, 2020 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_PROJECTION_TYPE_HPP 23 | #define EOS_RENDER_PROJECTION_TYPE_HPP 24 | 25 | namespace eos { 26 | namespace render { 27 | 28 | /** 29 | * Specifies whether orthographic or perspective projection shall be used. 30 | */ 31 | enum class ProjectionType { Orthographic, Perspective }; 32 | 33 | } // namespace render 34 | } // namespace eos 35 | 36 | #endif /* EOS_RENDER_PROJECTION_TYPE_HPP */ 37 | -------------------------------------------------------------------------------- /include/eos/render/Texture.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/Texture.hpp 5 | * 6 | * Copyright 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_TEXTURE_HPP 23 | #define EOS_TEXTURE_HPP 24 | 25 | #include "eos/core/Image.hpp" 26 | #include "eos/core/image/resize.hpp" 27 | 28 | #include 29 | #include 30 | 31 | namespace eos { 32 | namespace render { 33 | 34 | // TODO: Should go to detail:: namespace, or texturing/utils or whatever. 35 | inline unsigned int get_max_possible_mipmaps_num(unsigned int width, unsigned int height) 36 | { 37 | unsigned int mipmapsNum = 1; 38 | unsigned int size = std::max(width, height); 39 | 40 | if (size == 1) 41 | return 1; 42 | 43 | do 44 | { 45 | size >>= 1; 46 | mipmapsNum++; 47 | } while (size != 1); 48 | 49 | return mipmapsNum; 50 | }; 51 | 52 | inline bool is_power_of_two(int x) 53 | { 54 | return !(x & (x - 1)); 55 | }; 56 | 57 | /** 58 | * @brief Represents a texture for rendering. 59 | * 60 | * Represents a texture and mipmap levels for use in the renderer. 61 | * Todo: This whole class needs a major overhaul and documentation. 62 | */ 63 | class Texture 64 | { 65 | public: 66 | std::vector 67 | mipmaps; // make Texture a friend class of renderer, then move this to private? 68 | unsigned char widthLog, heightLog; // log2 of width and height of the base mip-level 69 | 70 | // private: 71 | // std::string filename; 72 | unsigned int mipmaps_num; 73 | }; 74 | 75 | // throws: ocv exc, runtime_ex 76 | inline Texture create_mipmapped_texture(const eos::core::Image4u& image, unsigned int mipmapsNum = 0) 77 | { 78 | Texture texture; 79 | 80 | texture.mipmaps_num = 81 | (mipmapsNum == 0 ? get_max_possible_mipmaps_num(image.width(), image.height()) : mipmapsNum); 82 | /*if (mipmapsNum == 0) 83 | { 84 | uchar mmn = render::utils::MatrixUtils::getMaxPossibleMipmapsNum(image.cols, image.rows); 85 | this->mipmapsNum = mmn; 86 | } else 87 | { 88 | this->mipmapsNum = mipmapsNum; 89 | }*/ 90 | 91 | if (texture.mipmaps_num > 1) 92 | { 93 | if (!is_power_of_two(image.width()) || !is_power_of_two(image.height())) 94 | { 95 | throw std::runtime_error("Error: Couldn't generate mipmaps, width or height not power of two."); 96 | } 97 | } 98 | 99 | int currWidth = image.width(); 100 | int currHeight = image.height(); 101 | std::vector mipmaps; 102 | for (unsigned int i = 0; i < texture.mipmaps_num; i++) 103 | { 104 | if (i == 0) 105 | { 106 | mipmaps.push_back(image); 107 | } else 108 | { 109 | const eos::core::Image4u currMipMap = 110 | eos::core::image::resize(mipmaps[i - 1], currWidth, currHeight); 111 | mipmaps.push_back(currMipMap); 112 | } 113 | 114 | if (currWidth > 1) 115 | currWidth >>= 1; 116 | if (currHeight > 1) 117 | currHeight >>= 1; 118 | } 119 | texture.mipmaps = mipmaps; 120 | constexpr double ln2 = 0.69314718056; 121 | texture.widthLog = (unsigned char)(std::log(mipmaps[0].width()) / ln2 + 122 | 0.0001f); // std::epsilon or something? or why 0.0001f here? 123 | texture.heightLog = (unsigned char)(std::log(mipmaps[0].height()) / ln2 + 124 | 0.0001f); // Changed std::logf to std::log because it doesnt compile 125 | // in linux (gcc 4.8). CHECK THAT 126 | return texture; 127 | }; 128 | 129 | } /* namespace render */ 130 | } /* namespace eos */ 131 | 132 | #endif /* EOS_TEXTURE_HPP */ 133 | -------------------------------------------------------------------------------- /include/eos/render/VertexShader.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/VertexShader.hpp 5 | * 6 | * Copyright 2017, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_VERTEX_SHADER_HPP 23 | #define EOS_VERTEX_SHADER_HPP 24 | 25 | #include "Eigen/Core" 26 | 27 | namespace eos { 28 | namespace render { 29 | 30 | /** 31 | * @brief A simple vertex shader that projects the vertex and returns the vertex in clip-space coordinates. 32 | */ 33 | class VertexShader 34 | { 35 | public: 36 | /** 37 | * @brief Projects the given vertex into clip-space and returns it. 38 | * 39 | * @param[in] vertex The vertex to project. 40 | * @param[in] model_view_matrix The model-view matrix. 41 | * @param[in] projection_matrix The projection matrix. 42 | * @tparam VertexType Vertex type. 43 | * @tparam MatrixType Matrix type. 44 | * @return Vertex projected to clip space. 45 | */ 46 | template 47 | Eigen::Vector4 operator()(const Eigen::Vector4& vertex, 48 | const Eigen::Matrix4& model_view_matrix, 49 | const Eigen::Matrix4& projection_matrix) 50 | { 51 | return projection_matrix * model_view_matrix * vertex; 52 | }; 53 | }; 54 | 55 | } /* namespace render */ 56 | } /* namespace eos */ 57 | 58 | #endif /* EOS_VERTEX_SHADER_HPP */ 59 | -------------------------------------------------------------------------------- /include/eos/render/detail/RayDirection.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/detail/RayDirection.hpp 5 | * 6 | * Copyright 2019, 2020 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_DETAIL_RAY_DIRECTION_HPP 23 | #define EOS_RENDER_DETAIL_RAY_DIRECTION_HPP 24 | 25 | namespace eos { 26 | namespace render { 27 | namespace detail { 28 | 29 | /** 30 | * Specifies whether to cast rays parallel or towards the origin while computing vertex/mesh self-occlusion. 31 | * 32 | * Note: This is best as an internal-only type I think. But it's needed in the public interface of the 33 | * functions in vertex_visibility.hpp - so perhaps think about that. 34 | */ 35 | enum class RayDirection { Parallel, TowardsOrigin }; 36 | 37 | } // namespace detail 38 | } // namespace render 39 | } // namespace eos 40 | 41 | #endif /* EOS_RENDER_DETAIL_RAY_DIRECTION_HPP */ 42 | -------------------------------------------------------------------------------- /include/eos/render/detail/TriangleToRasterize.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/detail/TriangleToRasterize.hpp 5 | * 6 | * Copyright 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_TRIANGLE_TO_RASTERIZE_HPP 23 | #define EOS_TRIANGLE_TO_RASTERIZE_HPP 24 | 25 | #include "eos/render/detail/Vertex.hpp" 26 | #include "eos/render/detail/plane.hpp" 27 | 28 | /** 29 | * The detail namespace contains implementations of internal functions, not part of the API we expose and not 30 | * meant to be used by a user. 31 | */ 32 | namespace eos { 33 | namespace render { 34 | namespace detail { 35 | 36 | /** 37 | * A representation for a triangle that is to be rasterised. 38 | * Stores the enclosing bounding box of the triangle that is 39 | * calculated during rendering and used during rasterisation. 40 | * 41 | * Used in render_affine and render. 42 | */ 43 | struct TriangleToRasterize 44 | { 45 | Vertex v0, v1, v2; 46 | int min_x; 47 | int max_x; 48 | int min_y; 49 | int max_y; 50 | // Everything below is only used in the "normal" renderer, but not 51 | // in render_affine. 52 | double one_over_z0; 53 | double one_over_z1; 54 | double one_over_z2; 55 | // double one_over_v0ToLine12; 56 | // double one_over_v1ToLine20; 57 | // double one_over_v2ToLine01; 58 | plane alphaPlane; 59 | plane betaPlane; 60 | plane gammaPlane; 61 | double one_over_alpha_c; // those are only used for texturing -> float 62 | double one_over_beta_c; 63 | double one_over_gamma_c; 64 | float alpha_ffx; 65 | float beta_ffx; 66 | float gamma_ffx; 67 | float alpha_ffy; 68 | float beta_ffy; 69 | float gamma_ffy; 70 | }; 71 | 72 | } /* namespace detail */ 73 | } /* namespace render */ 74 | } /* namespace eos */ 75 | 76 | #endif /* EOS_TRIANGLE_TO_RASTERIZE_HPP */ 77 | -------------------------------------------------------------------------------- /include/eos/render/detail/Vertex.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/detail/Vertex.hpp 5 | * 6 | * Copyright 2017, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_VERTEX_HPP 23 | #define EOS_VERTEX_HPP 24 | 25 | #include "Eigen/Core" 26 | 27 | /** 28 | * The detail namespace contains implementations of internal functions, not part of the API we expose and not 29 | * meant to be used by a user. 30 | */ 31 | namespace eos { 32 | namespace render { 33 | namespace detail { 34 | 35 | /** 36 | * @brief A representation for a vertex during rendering, used internally. 37 | * 38 | * It's used to build the vertices that will be rendered, and new vertices that are generated by the renderer 39 | * (during clipping). 40 | * I should check at some point that no unnecessary copies of the vertex data is created in some places, but I 41 | * think it's pretty much fine. 42 | * 43 | * FragmentShader and SoftwareRenderer use this. 44 | * 45 | * This is the same as the one in the current render_detail.hpp, except that this is fully templated. 46 | * 47 | */ 48 | template 49 | struct Vertex 50 | { 51 | Eigen::Vector4 position; // XYZW 52 | Eigen::Vector3 color; // RGB order 53 | Eigen::Vector2 texcoords; // UV 54 | }; 55 | 56 | } /* namespace detail */ 57 | } /* namespace render */ 58 | } /* namespace eos */ 59 | 60 | #endif /* EOS_VERTEX_HPP */ 61 | -------------------------------------------------------------------------------- /include/eos/render/detail/plane.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/detail/plane.hpp 5 | * 6 | * Copyright 2017, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_DETAIL_PLANE_HPP 23 | #define EOS_RENDER_DETAIL_PLANE_HPP 24 | 25 | #include "Eigen/Core" 26 | 27 | #include 28 | 29 | /** 30 | * The detail namespace contains implementations of internal functions, not part of the API we expose and not 31 | * meant to be used by a user. 32 | */ 33 | namespace eos { 34 | namespace render { 35 | namespace detail { 36 | 37 | class plane 38 | { 39 | public: 40 | plane() {} 41 | 42 | plane(float a, float b, float c, float d) 43 | { 44 | this->a = a; 45 | this->b = b; 46 | this->c = c; 47 | this->d = d; 48 | } 49 | 50 | plane(const Eigen::Vector3f& normal, float d = 0.0f) 51 | { 52 | this->a = normal[0]; 53 | this->b = normal[1]; 54 | this->c = normal[2]; 55 | this->d = d; 56 | } 57 | 58 | plane(const Eigen::Vector3f& point, const Eigen::Vector3f& normal) 59 | { 60 | a = normal[0]; 61 | b = normal[1]; 62 | c = normal[2]; 63 | d = -point.dot(normal); 64 | } 65 | 66 | template 67 | plane(const Eigen::Vector3& point1, const Eigen::Vector3& point2, const Eigen::Vector3& point3) 68 | { 69 | const Eigen::Vector3 v1 = point2 - point1; 70 | const Eigen::Vector3 v2 = point3 - point1; 71 | Eigen::Vector3 normal = v1.cross(v2); 72 | 73 | normal = normal.normalized(); 74 | 75 | a = normal[0]; 76 | b = normal[1]; 77 | c = normal[2]; 78 | d = -point1.dot(normal); 79 | } 80 | 81 | void normalize() 82 | { 83 | const float length = std::sqrt(a * a + b * b + c * c); 84 | 85 | a /= length; 86 | b /= length; 87 | c /= length; 88 | } 89 | 90 | float getSignedDistanceFromPoint(const Eigen::Vector3f& point) const 91 | { 92 | return a * point[0] + b * point[1] + c * point[2] + d; 93 | } 94 | 95 | float getSignedDistanceFromPoint(const Eigen::Vector4f& point) const 96 | { 97 | return a * point[0] + b * point[1] + c * point[2] + d; 98 | } 99 | 100 | public: 101 | float a, b, c; 102 | float d; 103 | }; 104 | 105 | } /* namespace detail */ 106 | } /* namespace render */ 107 | } /* namespace eos */ 108 | 109 | #endif /* EOS_RENDER_DETAIL_PLANE_HPP */ 110 | -------------------------------------------------------------------------------- /include/eos/render/draw_utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/draw_utils.hpp 5 | * 6 | * Copyright 2017-2019, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_DRAW_UTILS_HPP 23 | #define EOS_RENDER_DRAW_UTILS_HPP 24 | 25 | #include "eos/core/Mesh.hpp" 26 | #include "eos/render/matrix_projection.hpp" 27 | #include "eos/render/detail/utils.hpp" 28 | 29 | #include "Eigen/Core" 30 | 31 | namespace eos { 32 | namespace render { 33 | 34 | /** 35 | * Draws a line using the Bresenham algorithm. 36 | * 37 | * From: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm 38 | * I also tried this: https://www.thecrazyprogrammer.com/2017/01/bresenhams-line-drawing-algorithm-c-c.html 39 | * which looks awesome, but it drew weird lines. 40 | * 41 | * @param[in] image An image to draw into. 42 | * @param[in] x0 X coordinate of the start point. 43 | * @param[in] y0 Y coordinate of the start point. 44 | * @param[in] x1 X coordinate of the start point. 45 | * @param[in] y1 Y coordinate of the end point. 46 | * @param[in] color RGB colour of the line to be drawn. 47 | */ 48 | 49 | inline void draw_line(core::Image3u& image, float x0, float y0, float x1, float y1, Eigen::Vector3f color) 50 | { 51 | auto plot_line_low = [&image, &color](float x0, float y0, float x1, float y1) { 52 | float dx = x1 - x0; 53 | float dy = y1 - y0; 54 | int yi = 1; 55 | if (dy < 0) 56 | { 57 | yi = -1; 58 | dy = -dy; 59 | } 60 | 61 | float D = 2 * dy - dx; 62 | float y = y0; 63 | 64 | for (int x = x0; x <= x1; ++x) // for x from x0 to x1 65 | { 66 | image(y, x) = {color[0], color[1], color[2]}; // plot(x, y); 67 | if (D > 0) 68 | { 69 | y = y + yi; 70 | D = D - 2 * dx; 71 | } 72 | D = D + 2 * dy; 73 | } 74 | }; 75 | 76 | auto plot_line_high = [&image, &color](float x0, float y0, float x1, float y1) { 77 | float dx = x1 - x0; 78 | float dy = y1 - y0; 79 | int xi = 1; 80 | if (dx < 0) 81 | { 82 | xi = -1; 83 | dx = -dx; 84 | } 85 | 86 | float D = 2 * dx - dy; 87 | float x = x0; 88 | 89 | for (int y = y0; y <= y1; ++y) // for y from y0 to y1 90 | { 91 | image(y, x) = {color[0], color[1], color[2]}; // plot(x, y); 92 | if (D > 0) 93 | { 94 | x = x + xi; 95 | D = D - 2 * dy; 96 | } 97 | D = D + 2 * dx; 98 | } 99 | }; 100 | 101 | if (abs(y1 - y0) < abs(x1 - x0)) 102 | { 103 | if (x0 > x1) 104 | { 105 | plot_line_low(x1, y1, x0, y0); 106 | } else 107 | { 108 | plot_line_low(x0, y0, x1, y1); 109 | } 110 | } else 111 | { 112 | if (y0 > y1) 113 | { 114 | plot_line_high(x1, y1, x0, y0); 115 | } else 116 | { 117 | plot_line_high(x0, y0, x1, y1); 118 | } 119 | } 120 | }; 121 | 122 | /** 123 | * Draws the given mesh as wireframe into the image. 124 | * 125 | * It does backface culling, i.e. draws only vertices in CCW order. 126 | * 127 | * @param[in] image An image to draw into. 128 | * @param[in] mesh The mesh to draw. 129 | * @param[in] modelview Model-view matrix to draw the mesh. 130 | * @param[in] projection Projection matrix to draw the mesh. 131 | * @param[in] viewport Viewport to draw the mesh. 132 | * @param[in] color Colour of the mesh to be drawn, in RGB. 133 | */ 134 | inline void draw_wireframe(core::Image3u& image, const core::Mesh& mesh, Eigen::Matrix4f modelview, 135 | Eigen::Matrix4f projection, Eigen::Vector4f viewport, 136 | Eigen::Vector3f color = Eigen::Vector3f(0, 255, 0)) 137 | { 138 | for (const auto& triangle : mesh.tvi) 139 | { 140 | const auto p1 = project(mesh.vertices[triangle[0]], modelview, projection, viewport); 141 | const auto p2 = project(mesh.vertices[triangle[1]], modelview, projection, viewport); 142 | const auto p3 = project(mesh.vertices[triangle[2]], modelview, projection, viewport); 143 | if (render::detail::are_vertices_ccw_in_screen_space(p1.head<2>(), p2.head<2>(), p3.head<2>())) 144 | { 145 | draw_line(image, p1.x(), p1.y(), p2.x(), p2.y(), color); 146 | draw_line(image, p2.x(), p2.y(), p3.x(), p3.y(), color); 147 | draw_line(image, p3.x(), p3.y(), p1.x(), p1.y(), color); 148 | } 149 | } 150 | }; 151 | 152 | } /* namespace render */ 153 | } /* namespace eos */ 154 | 155 | #endif /* EOS_RENDER_DRAW_UTILS_HPP */ 156 | -------------------------------------------------------------------------------- /include/eos/render/normals.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/normals.hpp 5 | * 6 | * Copyright 2014-2019 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_NORMALS_HPP 23 | #define EOS_RENDER_NORMALS_HPP 24 | 25 | #include "Eigen/Core" 26 | 27 | #include 28 | 29 | namespace eos { 30 | namespace render { 31 | 32 | /** 33 | * Computes the normal of a face (triangle), i.e. the per-face normal. Returned normal will be unit length. 34 | * 35 | * Assumes the triangle is given in CCW order, i.e. vertices in counterclockwise order on the screen are 36 | * front-facing. 37 | * 38 | * @param[in] v0 First vertex. 39 | * @param[in] v1 Second vertex. 40 | * @param[in] v2 Third vertex. 41 | * @return The unit-length normal of the given triangle. 42 | */ 43 | inline Eigen::Vector3f compute_face_normal(const Eigen::Vector3f& v0, const Eigen::Vector3f& v1, 44 | const Eigen::Vector3f& v2) 45 | { 46 | Eigen::Vector3f n = (v1 - v0).cross(v2 - v0); // v0-to-v1 x v0-to-v2 47 | return n.normalized(); 48 | }; 49 | 50 | /** 51 | * Computes the normal of a face (triangle), i.e. the per-face normal. Returned normal will be unit length. 52 | * 53 | * Assumes the triangle is given in CCW order, i.e. vertices in counterclockwise order on the screen are 54 | * front-facing. 55 | * 56 | * @param[in] v0 First vertex. 57 | * @param[in] v1 Second vertex. 58 | * @param[in] v2 Third vertex. 59 | * @return The unit-length normal of the given triangle. 60 | */ 61 | inline Eigen::Vector3f compute_face_normal(const Eigen::Vector4f& v0, const Eigen::Vector4f& v1, 62 | const Eigen::Vector4f& v2) 63 | { 64 | Eigen::Vector4f n = (v1 - v0).cross3(v2 - v0); // v0-to-v1 x v0-to-v2 65 | return n.head<3>().normalized(); 66 | }; 67 | 68 | /** 69 | * Computes the per-face (per-triangle) normals of all triangles of the given mesh. Returned normals will be unit length. 70 | * 71 | * Assumes triangles are given in CCW order, i.e. vertices in counterclockwise order on the screen are front-facing. 72 | * 73 | * @param[in] vertices A list of vertices. 74 | * @param[in] triangle_vertex_indices Triangle list for the given vertices. 75 | * @return The unit-length per-face normals. 76 | */ 77 | inline std::vector 78 | compute_face_normals(const std::vector& vertices, 79 | const std::vector>& triangle_vertex_indices) 80 | { 81 | std::vector face_normals; 82 | for (const auto& tvi : triangle_vertex_indices) 83 | { 84 | const auto face_normal = compute_face_normal(vertices[tvi[0]], vertices[tvi[1]], vertices[tvi[2]]); 85 | face_normals.push_back(face_normal); 86 | } 87 | return face_normals; 88 | }; 89 | 90 | /** 91 | * Computes the per-vertex normals of all vertices of the given mesh. Returned normals will be unit length. 92 | * 93 | * Assumes triangles are given in CCW order, i.e. vertices in counterclockwise order on the screen are 94 | * front-facing. 95 | * 96 | * @param[in] vertices A list of vertices. 97 | * @param[in] triangle_vertex_indices Triangle list for the given vertices. 98 | * @param[in] face_normals Per-face normals for all triangles. 99 | * @return The unit-length per-vertex normals. 100 | */ 101 | inline std::vector 102 | compute_vertex_normals(const std::vector& vertices, 103 | const std::vector>& triangle_vertex_indices, 104 | const std::vector& face_normals) 105 | { 106 | std::vector per_vertex_normals; 107 | // Initialise with zeros: 108 | for (int i = 0; i < vertices.size(); ++i) 109 | { 110 | per_vertex_normals.emplace_back(Eigen::Vector3f(0.0f, 0.0f, 0.0f)); 111 | } 112 | 113 | // Loop over the faces again: 114 | for (int i = 0; i < triangle_vertex_indices.size(); ++i) 115 | { 116 | const auto& tvi = triangle_vertex_indices[i]; 117 | // Throw normal at each corner: 118 | per_vertex_normals[tvi[0]] += face_normals[i]; 119 | per_vertex_normals[tvi[1]] += face_normals[i]; 120 | per_vertex_normals[tvi[2]] += face_normals[i]; 121 | } 122 | 123 | // Take average via normalization: 124 | for (auto& n : per_vertex_normals) 125 | { 126 | n.normalize(); 127 | } 128 | return per_vertex_normals; 129 | }; 130 | 131 | } /* namespace render */ 132 | } /* namespace eos */ 133 | 134 | #endif /* EOS_RENDER_NORMALS_HPP */ 135 | -------------------------------------------------------------------------------- /include/eos/render/opencv/draw_utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/opencv/draw_utils.hpp 5 | * 6 | * Copyright 2017-2019, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_OPENCV_DRAW_UTILS_HPP 23 | #define EOS_RENDER_OPENCV_DRAW_UTILS_HPP 24 | 25 | #include "eos/core/Mesh.hpp" 26 | #include "eos/render/matrix_projection.hpp" 27 | #include "eos/render/detail/utils.hpp" 28 | 29 | #include "Eigen/Core" 30 | 31 | #include "opencv2/core/core.hpp" 32 | #include "opencv2/imgproc/imgproc.hpp" 33 | 34 | namespace eos { 35 | namespace render { 36 | 37 | /** 38 | * Draws the given mesh as wireframe into the image. 39 | * 40 | * It does backface culling, i.e. draws only vertices in CCW order. 41 | * 42 | * Note: This function might be deprecated in the future, in favour of draw_wireframe(...) in 43 | * render/draw_utils.hpp, which doesn't depend on OpenCV anymore. 44 | * 45 | * @param[in] image An image to draw into. 46 | * @param[in] mesh The mesh to draw. 47 | * @param[in] modelview Model-view matrix to draw the mesh. 48 | * @param[in] projection Projection matrix to draw the mesh. 49 | * @param[in] viewport Viewport to draw the mesh. 50 | * @param[in] color Colour of the mesh to be drawn. 51 | */ 52 | inline void draw_wireframe(cv::Mat image, const core::Mesh& mesh, Eigen::Matrix4f modelview, 53 | Eigen::Matrix4f projection, Eigen::Vector4f viewport, 54 | cv::Scalar color = cv::Scalar(0, 255, 0, 255)) 55 | { 56 | for (const auto& triangle : mesh.tvi) 57 | { 58 | const auto p1 = project(mesh.vertices[triangle[0]], modelview, projection, viewport); 59 | const auto p2 = project(mesh.vertices[triangle[1]], modelview, projection, viewport); 60 | const auto p3 = project(mesh.vertices[triangle[2]], modelview, projection, viewport); 61 | if (render::detail::are_vertices_ccw_in_screen_space(p1.head<2>(), p2.head<2>(), p3.head<2>())) 62 | { 63 | cv::line(image, cv::Point(p1.x(), p1.y()), cv::Point(p2.x(), p2.y()), color); 64 | cv::line(image, cv::Point(p2.x(), p2.y()), cv::Point(p3.x(), p3.y()), color); 65 | cv::line(image, cv::Point(p3.x(), p3.y()), cv::Point(p1.x(), p1.y()), color); 66 | } 67 | } 68 | }; 69 | 70 | /** 71 | * Draws the texture coordinates (uv-coords) of the given mesh 72 | * into an image by looping over the triangles and drawing each 73 | * triangle's texcoords. 74 | * 75 | * Note/Todo: This function has a slight problems, the lines do not actually get 76 | * drawn blue, if the image is 8UC4. Well if I save a PNG, it is blue. Not sure. 77 | * 78 | * @param[in] mesh A mesh with texture coordinates. 79 | * @param[in] image An optional image to draw onto. 80 | * @return An image with the texture coordinate triangles drawn in it, 512x512 if no image is given. 81 | */ 82 | inline cv::Mat draw_texcoords(core::Mesh mesh, cv::Mat image = cv::Mat()) 83 | { 84 | using cv::Point2f; 85 | using cv::Scalar; 86 | if (image.empty()) 87 | { 88 | image = cv::Mat(512, 512, CV_8UC4, Scalar(0.0f, 0.0f, 0.0f, 255.0f)); 89 | } 90 | 91 | for (const auto& triIdx : mesh.tvi) 92 | { 93 | cv::line( 94 | image, 95 | Point2f(mesh.texcoords[triIdx[0]][0] * image.cols, mesh.texcoords[triIdx[0]][1] * image.rows), 96 | Point2f(mesh.texcoords[triIdx[1]][0] * image.cols, mesh.texcoords[triIdx[1]][1] * image.rows), 97 | Scalar(255.0f, 0.0f, 0.0f)); 98 | cv::line( 99 | image, 100 | Point2f(mesh.texcoords[triIdx[1]][0] * image.cols, mesh.texcoords[triIdx[1]][1] * image.rows), 101 | Point2f(mesh.texcoords[triIdx[2]][0] * image.cols, mesh.texcoords[triIdx[2]][1] * image.rows), 102 | Scalar(255.0f, 0.0f, 0.0f)); 103 | cv::line( 104 | image, 105 | Point2f(mesh.texcoords[triIdx[2]][0] * image.cols, mesh.texcoords[triIdx[2]][1] * image.rows), 106 | Point2f(mesh.texcoords[triIdx[0]][0] * image.cols, mesh.texcoords[triIdx[0]][1] * image.rows), 107 | Scalar(255.0f, 0.0f, 0.0f)); 108 | } 109 | return image; 110 | }; 111 | 112 | } /* namespace render */ 113 | } /* namespace eos */ 114 | 115 | #endif /* EOS_RENDER_OPENCV_DRAW_UTILS_HPP */ 116 | -------------------------------------------------------------------------------- /include/eos/render/ray_triangle_intersect.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/ray_triangle_intersect.hpp 5 | * 6 | * Copyright 2016-2019 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RAY_TRIANGLE_INTERSECT_HPP 23 | #define EOS_RAY_TRIANGLE_INTERSECT_HPP 24 | 25 | #include "eos/cpp17/optional.hpp" 26 | 27 | #include "Eigen/Core" 28 | 29 | #include 30 | 31 | namespace eos { 32 | namespace render { 33 | 34 | /** 35 | * @brief Computes the intersection of the given ray with the given triangle. 36 | * 37 | * Uses the Möller-Trumbore algorithm "Fast Minimum Storage Ray/Triangle Intersection". 38 | * Independent implementation, inspired by: 39 | * http://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection 40 | * The default eps (1e-6f) is from the paper. 41 | * When culling is on, rays intersecting triangles from the back will be discarded - 42 | * otherwise, the triangles normal direction w.r.t. the ray direction is just ignored. 43 | * 44 | * Todo: We don't need the pair<> here, we could just return optional. Also, I hope optional wouldn't 45 | * be a performance problem here, as this function is called loads of times. 46 | * 47 | * @param[in] ray_origin Ray origin. 48 | * @param[in] ray_direction Ray direction. 49 | * @param[in] v0 First vertex of a triangle. 50 | * @param[in] v1 Second vertex of a triangle. 51 | * @param[in] v2 Third vertex of a triangle. 52 | * @param[in] enable_backculling When culling is on, rays intersecting triangles from the back will be 53 | * discarded. 54 | * @return Whether the ray intersects the triangle, and if yes, including the distance. 55 | */ 56 | inline std::pair> 57 | ray_triangle_intersect(const Eigen::Vector3f& ray_origin, const Eigen::Vector3f& ray_direction, 58 | const Eigen::Vector3f& v0, const Eigen::Vector3f& v1, const Eigen::Vector3f& v2, 59 | bool enable_backculling) 60 | { 61 | using Eigen::Vector3f; 62 | const float epsilon = 1e-6f; 63 | 64 | const Vector3f v0v1 = v1 - v0; 65 | const Vector3f v0v2 = v2 - v0; 66 | 67 | const Vector3f pvec = ray_direction.cross(v0v2); 68 | 69 | const float det = v0v1.dot(pvec); 70 | 71 | if (enable_backculling) 72 | { 73 | // If det is negative, the triangle is back-facing. 74 | // If det is close to 0, the ray misses the triangle. 75 | if (det < epsilon) 76 | return {false, cpp17::nullopt}; 77 | } else 78 | { 79 | // If det is close to 0, the ray and triangle are parallel. 80 | if (std::abs(det) < epsilon) 81 | return {false, cpp17::nullopt}; 82 | } 83 | const float inv_det = 1 / det; 84 | 85 | const Vector3f tvec = ray_origin - v0; 86 | const auto u = tvec.dot(pvec) * inv_det; 87 | if (u < 0 || u > 1) 88 | return {false, cpp17::nullopt}; 89 | 90 | const Vector3f qvec = tvec.cross(v0v1); 91 | const auto v = ray_direction.dot(qvec) * inv_det; 92 | if (v < 0 || u + v > 1) 93 | return {false, cpp17::nullopt}; 94 | 95 | const auto t = v0v2.dot(qvec) * inv_det; 96 | 97 | return {true, t}; 98 | }; 99 | 100 | } /* namespace render */ 101 | } /* namespace eos */ 102 | 103 | #endif /* EOS_RAY_TRIANGLE_INTERSECT_HPP */ 104 | -------------------------------------------------------------------------------- /include/eos/render/render.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/render.hpp 5 | * 6 | * Copyright 2014-2020, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_HPP 23 | #define EOS_RENDER_HPP 24 | 25 | #include "eos/core/Image.hpp" 26 | #include "eos/core/Mesh.hpp" 27 | #include "eos/render/SoftwareRenderer.hpp" 28 | #include "eos/render/VertexShader.hpp" 29 | #include "eos/render/FragmentShader.hpp" 30 | 31 | #include "Eigen/Core" 32 | 33 | namespace eos { 34 | namespace render { 35 | 36 | /** 37 | * Convenience function that renders the given mesh onto a 2D image using \c SoftwareRenderer. Conforms to 38 | * OpenGL conventions. 39 | * 40 | * Renders using per-vertex colouring, without texturing. 41 | * 42 | * @param[in] mesh A 3D mesh. 43 | * @param[in] model_view_matrix A 4x4 OpenGL model-view matrix. 44 | * @param[in] projection_matrix A 4x4 orthographic or perspective OpenGL projection matrix. 45 | * @param[in] viewport_width Screen width. 46 | * @param[in] viewport_height Screen height. 47 | * @param[in] enable_backface_culling Whether the renderer should perform backface culling. If true, only draw triangles with vertices ordered CCW in screen-space. 48 | * @param[in] enable_near_clipping Whether vertices should be clipped against the near plane. 49 | * @param[in] enable_far_clipping Whether vertices should be clipped against the far plane. 50 | * @return Framebuffer (colourbuffer) with the rendered image. 51 | */ 52 | core::Image4u render(const core::Mesh& mesh, const Eigen::Matrix4f& model_view_matrix, 53 | const Eigen::Matrix4f& projection_matrix, int viewport_width, int viewport_height, 54 | bool enable_backface_culling = false, bool enable_near_clipping = true, 55 | bool enable_far_clipping = true) 56 | { 57 | SoftwareRenderer software_renderer(viewport_width, 58 | viewport_height); 59 | software_renderer.enable_backface_culling = enable_backface_culling; 60 | software_renderer.enable_near_clipping = enable_near_clipping; 61 | software_renderer.rasterizer.enable_far_clipping = enable_far_clipping; 62 | 63 | return software_renderer.render(mesh, model_view_matrix, projection_matrix); 64 | }; 65 | 66 | /** 67 | * Convenience function that renders the given mesh onto a 2D image using \c SoftwareRenderer. Conforms to 68 | * OpenGL conventions. 69 | * 70 | * Performs texturing using the given \p texture. 71 | * 72 | * @param[in] mesh A 3D mesh. 73 | * @param[in] model_view_matrix A 4x4 OpenGL model-view matrix. 74 | * @param[in] projection_matrix A 4x4 orthographic or perspective OpenGL projection matrix. 75 | * @param[in] viewport_width Screen width. 76 | * @param[in] viewport_height Screen height. 77 | * @param[in] texture A texture map to texture the model. 78 | * @param[in] enable_backface_culling Whether the renderer should perform backface culling. If true, only draw 79 | * triangles with vertices ordered CCW in screen-space. 80 | * @param[in] enable_near_clipping Whether vertices should be clipped against the near plane. 81 | * @param[in] enable_far_clipping Whether vertices should be clipped against the far plane. 82 | * @return Framebuffer (colourbuffer) with the rendered image. 83 | */ 84 | core::Image4u render(const core::Mesh& mesh, const Eigen::Matrix4f& model_view_matrix, 85 | const Eigen::Matrix4f& projection_matrix, int viewport_width, int viewport_height, 86 | Texture texture, bool enable_backface_culling = false, bool enable_near_clipping = true, 87 | bool enable_far_clipping = true) 88 | { 89 | SoftwareRenderer software_renderer(viewport_width, 90 | viewport_height); 91 | software_renderer.enable_backface_culling = enable_backface_culling; 92 | software_renderer.enable_near_clipping = enable_near_clipping; 93 | software_renderer.rasterizer.enable_far_clipping = enable_far_clipping; 94 | 95 | return software_renderer.render(mesh, model_view_matrix, projection_matrix, texture); 96 | }; 97 | 98 | } /* namespace render */ 99 | } /* namespace eos */ 100 | 101 | #endif /* EOS_RENDER_HPP */ 102 | -------------------------------------------------------------------------------- /include/eos/render/transforms.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: include/eos/render/transforms.hpp 5 | * 6 | * Copyright 2014, 2015, 2023 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_RENDER_TRANSFORMS_HPP 23 | #define EOS_RENDER_TRANSFORMS_HPP 24 | 25 | #include "Eigen/Core" 26 | 27 | namespace eos { 28 | namespace render { 29 | 30 | /** 31 | * Transforms a point from clip space ([-1, 1] x [-1, 1]) to 32 | * image (screen) coordinates, i.e. the window transform. 33 | * Note that the y-coordinate is flipped because the image origin 34 | * is top-left while in clip space top is +1 and bottom is -1. 35 | * No z-division is performed. 36 | * Note: It should rather be called from NDC to screen space? 37 | * 38 | * Exactly conforming to the OpenGL viewport transform, except that 39 | * we flip y at the end. 40 | * Qt: Origin top-left. OpenGL: bottom-left. OCV: top-left. 41 | * 42 | * @param[in] clip_coordinates A point in clip coordinates. 43 | * @param[in] screen_width Width of the screen or window. 44 | * @param[in] screen_height Height of the screen or window. 45 | * @return A vector with x and y coordinates transformed to screen space. 46 | */ 47 | inline Eigen::Vector2f clip_to_screen_space(const Eigen::Vector2f& clip_coordinates, int screen_width, 48 | int screen_height) 49 | { 50 | // Window transform: 51 | const float x_ss = (clip_coordinates[0] + 1.0f) * (screen_width / 2.0f); 52 | const float y_ss = 53 | screen_height - (clip_coordinates[1] + 1.0f) * 54 | (screen_height / 2.0f); // also flip y; Qt: Origin top-left. OpenGL: bottom-left. 55 | return Eigen::Vector2f(x_ss, y_ss); 56 | /* Note: What we do here is equivalent to 57 | x_w = (x * vW/2) + vW/2; 58 | However, Shirley says we should do: 59 | x_w = (x * vW/2) + (vW-1)/2; 60 | (analogous for y) 61 | Todo: Check the consequences. 62 | */ 63 | }; 64 | 65 | // Equivalent to the above, but templated, and taking clip_coord_x and _y separately. 66 | // Todo: Can merge this back into one function I think? 67 | template 68 | Eigen::Vector2 clip_to_screen_space(const T clip_coord_x, const T clip_coord_y, int screen_width, 69 | int screen_height) 70 | { 71 | // Window transform: 72 | const T x_ss = (clip_coord_x + T(1)) * (screen_width / 2.0); 73 | const T y_ss = 74 | screen_height - (clip_coord_y + T(1)) * 75 | (screen_height / 2.0); // also flip y; Qt: Origin top-left. OpenGL: bottom-left. 76 | return Eigen::Vector2(x_ss, y_ss); 77 | }; 78 | 79 | /** 80 | * Transforms a point from image (screen) coordinates to 81 | * clip space ([-1, 1] x [-1, 1]). 82 | * Note that the y-coordinate is flipped because the image origin 83 | * is top-left while in clip space top is +1 and bottom is -1. 84 | * 85 | * @param[in] screen_coordinates A point in screen coordinates. 86 | * @param[in] screen_width Width of the screen or window. 87 | * @param[in] screen_height Height of the screen or window. 88 | * @return A vector with x and y coordinates transformed to clip space. 89 | */ 90 | /*inline cv::Vec2f screen_to_clip_space(const cv::Vec2f& screen_coordinates, int screen_width, int screen_height) 91 | { 92 | const float x_cs = screen_coordinates[0] / (screen_width / 2.0f) - 1.0f; 93 | float y_cs = screen_coordinates[1] / (screen_height / 2.0f) - 1.0f; 94 | y_cs *= -1.0f; 95 | return cv::Vec2f(x_cs, y_cs); 96 | };*/ 97 | 98 | } /* namespace render */ 99 | } /* namespace eos */ 100 | 101 | #endif /* EOS_RENDER_TRANSFORMS_HPP */ 102 | -------------------------------------------------------------------------------- /include/eos/video/Keyframe.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrikhuber/eos/ce984207a51c2edd2e7568a64fec9b98ef61aeed/include/eos/video/Keyframe.hpp -------------------------------------------------------------------------------- /initial_cache.cmake.template: -------------------------------------------------------------------------------- 1 | # NOTE: It is recommended to use vcpkg (https://github.com/Microsoft/vcpkg/) to install the dependencies for eos. 2 | # However, for users who wish to install the dependencies manually, this file might be helpful to set the library paths explicitly. 3 | 4 | # Mechanism via FindLIB.cmake 5 | # ============================== 6 | # Boost: 7 | # ------- 8 | # Windows: Download the pre-built binaries from http://sourceforge.net/projects/boost/files/boost-binaries/ for VS2017 (msvc-14.1 64bit). 9 | # Either set the windows PATH variable to "\lib64-msvc-14.1" and CMake will find it, or, set: 10 | #set(BOOST_ROOT "C:/boost" CACHE PATH "Boost search location" FORCE) 11 | # Linux: Boost can usually be installed via a package manager (e.g. apt-get install boost-all-dev) and this variable can be left uncommented. 12 | #set(BOOST_ROOT "/home/user/boost/install" CACHE PATH "Boost search location" FORCE) 13 | 14 | 15 | # Mechanism via ConfigLIB.cmake in 3rd party library directory 16 | # ============================== 17 | # OpenCV: 18 | # ------- 19 | # Windows: Download the package from opencv.org, use 2.4.13 or newer (e.g. 3.x), which it include binaries for VS2017. Set this path accordingly. 20 | #set(OpenCV_DIR "C:/opencv/install" CACHE PATH "Location of OpenCVConfig.cmake" FORCE) 21 | # Linux: Usually can be left blank but it can be used if OpenCV is not found. 22 | #set(OpenCV_DIR "/home/user/opencv/install/share/OpenCV" CACHE PATH "Location of OpenCVConfig.cmake" FORCE) 23 | # 24 | # Ceres: 25 | # ------- 26 | #set(Ceres_DIR "C:/ceres/install-vs2017/CMake" CACHE PATH "Location of CeresConfig.cmake" FORCE) 27 | 28 | # Set the path to the python interpreter if you want to build the python bindings and it doesn't find it automatically: 29 | # ============================== 30 | #set(PYTHON_EXECUTABLE "C:/Users/user/AppData/Local/Programs/Python/Python36/python.exe" CACHE PATH "Path to the python interpreter." FORCE) 31 | 32 | # Set the path to the Matlab root directory if you want to build the Matlab bindings and it doesn't find Matlab automatically: 33 | # ============================== 34 | #set(Matlab_ROOT_DIR "/opt/matlab" CACHE PATH "Path to the Matlab installation directory." FORCE) 35 | 36 | # Configuration options 37 | # ============================== 38 | set(EOS_BUILD_EXAMPLES ON CACHE BOOL "Build the example applications." FORCE) 39 | set(EOS_BUILD_CERES_EXAMPLE OFF CACHE BOOL "Build the fit-model-ceres example (requires Ceres)." FORCE) 40 | set(EOS_BUILD_UTILS OFF CACHE BOOL "Build utility applications." FORCE) 41 | set(EOS_BUILD_DOCUMENTATION OFF CACHE BOOL "Build the library documentation." FORCE) 42 | set(EOS_GENERATE_PYTHON_BINDINGS OFF CACHE BOOL "Build python bindings. Requires python to be installed." FORCE) 43 | set(EOS_GENERATE_MATLAB_BINDINGS OFF CACHE BOOL "Build Matlab bindings. Requires Matlab with the compiler installed or the Matlab Compiler Runtime." FORCE) 44 | -------------------------------------------------------------------------------- /matlab/+eos/+fitting/fit_shape_and_pose.m: -------------------------------------------------------------------------------- 1 | function [mesh, rendering_parameters] = fit_shape_and_pose(morphable_model, ... 2 | blendshapes, landmarks, landmark_mapper, image_width, image_height, ... 3 | edge_topology, contour_landmarks, model_contour, num_iterations, ... 4 | num_shape_coefficients_to_fit, lambda) 5 | % FIT_SHAPE_AND_POSE Fit a 3DMM shape model to landmarks. 6 | % [ mesh, rendering_parameters ] = FIT_SHAPE_AND_POSE(morphable_model, ... 7 | % blendshapes, landmarks, landmark_mapper, image_width, image_height, ... 8 | % edge_topology, contour_landmarks, model_contour, num_iterations, ... 9 | % num_shape_coefficients_to_fit, lambda) 10 | % 11 | % This function fits a 3D Morphable Model to landmarks in an image. 12 | % It fits the pose (camera), PCA shape model, and expression blendshapes 13 | % in an iterative way. 14 | % 15 | % landmarks must be a 68 x 2 matrix with ibug landmarks, in the order 16 | % from 1 to 68. 17 | % 18 | % Default values for some of the parameters: num_iterations = 5, 19 | % num_shape_coefficients_to_fit = all (-1), and lambda = 30.0. 20 | % 21 | % Please see the C++ documentation for the description of the parameters: 22 | % http://patrikhuber.github.io/eos/doc/ (TODO: Update to v0.9.1!) 23 | % 24 | % NOTE: In contrast to the C++ function, this Matlab function expects the 25 | % morphable_model, blendshapes, landmark_mapper, edge_topology, 26 | % contour_landmarks and model_contour as *filenames* to the respective 27 | % files in the eos/share/ directory, and not the objects directly. 28 | 29 | if(~isa(landmarks,'double')) 30 | error('Please specify the landmarks as type double.'); 31 | end 32 | 33 | % We'll use default values to the following arguments, if they're not 34 | % provided: 35 | if (~exist('edge_topology', 'var')), edge_topology = '../share/sfm_3448_edge_topology.json'; end 36 | if (~exist('contour_landmarks', 'var')), contour_landmarks = '../share/ibug_to_sfm.txt'; end 37 | if (~exist('model_contour', 'var')), model_contour = '../share/sfm_model_contours.json'; end 38 | if (~exist('num_iterations', 'var')), num_iterations = 5; end 39 | if (~exist('num_shape_coefficients_to_fit', 'var')), num_shape_coefficients_to_fit = -1; end 40 | if (~exist('lambda', 'var')), lambda = 30.0; end 41 | 42 | [ mesh, rendering_parameters ] = fitting(morphable_model, blendshapes, landmarks, landmark_mapper, image_width, image_height, edge_topology, contour_landmarks, model_contour, num_iterations, num_shape_coefficients_to_fit, lambda); 43 | 44 | end 45 | -------------------------------------------------------------------------------- /matlab/+eos/+fitting/private/fitting.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: matlab/+eos/+fitting/private/fitting.cpp 5 | * 6 | * Copyright 2016-2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include "eos/fitting/fitting.hpp" 21 | #include "eos/core/LandmarkMapper.hpp" 22 | #include "eos/core/Mesh.hpp" 23 | #include "eos/fitting/RenderingParameters.hpp" 24 | #include "eos/fitting/contour_correspondence.hpp" 25 | #include "eos/morphablemodel/Blendshape.hpp" 26 | #include "eos/morphablemodel/EdgeTopology.hpp" 27 | #include "eos/morphablemodel/MorphableModel.hpp" 28 | #include "eos/cpp17/optional.hpp" 29 | 30 | #include "mexplus_eigen.hpp" 31 | #include "mexplus_eos_types.hpp" 32 | 33 | #include "mexplus.h" 34 | 35 | #include "Eigen/Core" 36 | 37 | #include "mex.h" 38 | 39 | #include 40 | 41 | void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) 42 | { 43 | using std::string; 44 | using namespace eos; 45 | using namespace mexplus; 46 | 47 | // Check for proper number of input and output arguments: 48 | int num_input_args = 12; 49 | int num_output_args = 2; 50 | if (nrhs != num_input_args) 51 | { 52 | mexErrMsgIdAndTxt("eos:fitting:nargin", "fit_shape_and_pose requires 12 input arguments."); 53 | } 54 | if (nlhs != num_output_args) 55 | { 56 | mexErrMsgIdAndTxt("eos:fitting:nargout", "fit_shape_and_pose returns two output arguments."); 57 | } 58 | 59 | InputArguments input(nrhs, prhs, num_input_args); 60 | const auto morphablemodel_file = input.get(0); 61 | const auto blendshapes_file = input.get(1); 62 | const auto landmarks_in = input.get(2); 63 | const auto mapper_file = input.get(3); 64 | const auto image_width = input.get(4); 65 | const auto image_height = input.get(5); 66 | const auto edgetopo_file = input.get(6); 67 | const auto contour_lms_file = input.get(7); 68 | const auto model_cnt_file = input.get(8); 69 | const auto num_iterations = input.get(9); 70 | const auto num_shape_coeffs = input.get(10); 71 | const auto lambda = input.get(11); 72 | 73 | if (landmarks_in.rows() != 68 || landmarks_in.cols() != 2) 74 | { 75 | mexErrMsgIdAndTxt( 76 | "eos:fitting:argin", 77 | "Given landmarks must be a 68 x 2 vector with ibug landmarks, in the order from 1 to 68."); 78 | } 79 | // Convert the landmarks (given as matrix in Matlab) to a LandmarkCollection: 80 | core::LandmarkCollection landmarks; 81 | for (int i = 0; i < 68; ++i) 82 | { 83 | landmarks.push_back(core::Landmark{ 84 | std::to_string(i + 1), Eigen::Vector2f(landmarks_in(i, 0), landmarks_in(i, 1))}); 85 | } 86 | 87 | // Load everything: 88 | morphablemodel::MorphableModel morphable_model_with_expressions; 89 | { 90 | const auto morphable_model = morphablemodel::load_model(morphablemodel_file); 91 | const auto blendshapes = morphablemodel::load_blendshapes(blendshapes_file); 92 | morphable_model_with_expressions = morphablemodel::MorphableModel( 93 | morphable_model.get_shape_model(), blendshapes, morphable_model.get_color_model(), cpp17::nullopt, 94 | morphable_model.get_texture_coordinates()); 95 | } 96 | const core::LandmarkMapper landmark_mapper(mapper_file); 97 | const auto edge_topology = morphablemodel::load_edge_topology(edgetopo_file); 98 | const auto contour_landmarks = fitting::ContourLandmarks::load(contour_lms_file); 99 | const auto model_contour = fitting::ModelContour::load(model_cnt_file); 100 | const cpp17::optional num_shape_coefficients_to_fit = 101 | num_shape_coeffs == -1 ? cpp17::nullopt : cpp17::optional(num_shape_coeffs); 102 | 103 | // Now do the actual fitting: 104 | core::Mesh mesh; 105 | fitting::RenderingParameters rendering_parameters; 106 | std::tie(mesh, rendering_parameters) = 107 | fitting::fit_shape_and_pose(morphable_model_with_expressions, landmarks, landmark_mapper, image_width, 108 | image_height, edge_topology, contour_landmarks, model_contour, 109 | num_iterations, num_shape_coefficients_to_fit, lambda); 110 | 111 | // Return the mesh and the rendering_parameters: 112 | OutputArguments output(nlhs, plhs, num_output_args); 113 | output.set(0, mesh); 114 | output.set(1, rendering_parameters); 115 | }; 116 | -------------------------------------------------------------------------------- /matlab/+eos/+render/extract_texture.m: -------------------------------------------------------------------------------- 1 | function [ texturemap ] = extract_texture(mesh, rendering_params, image, ... 2 | compute_view_angle, isomap_resolution) 3 | % EXTRACT_TEXTURE Extracts the texture from an image and returns the texture map. 4 | % [ texturemap ] = EXTRACT_TEXTURE(mesh, rendering_params, image, ... 5 | % compute_view_angle, isomap_resolution) 6 | % 7 | % Extracts the texture of the face from the given image and stores it as 8 | % isomap (a rectangular texture map). 9 | % 10 | % Default values for the parameters: compute_view_angle = false, 11 | % isomap_resolution = 512. 12 | % 13 | % Please see the C++ documentation for the full description: 14 | % http://patrikhuber.github.io/eos/doc/ (TODO: Update to v0.10.1!) 15 | 16 | % We'll use default values to the following arguments, if they're not 17 | % provided: 18 | if (~exist('compute_view_angle', 'var')), compute_view_angle = false; end 19 | if (~exist('isomap_resolution', 'var')), isomap_resolution = 512; end 20 | 21 | [ texturemap ] = render('extract_texture', mesh, rendering_params, image, compute_view_angle, isomap_resolution); 22 | 23 | end 24 | -------------------------------------------------------------------------------- /matlab/+eos/+render/private/render.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: matlab/+eos/+render/private/render.cpp 5 | * 6 | * Copyright 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include "eos/render/render.hpp" 21 | #include "eos/core/Image_opencv_interop.hpp" 22 | #include "eos/core/Mesh.hpp" 23 | #include "eos/fitting/RenderingParameters.hpp" 24 | #include "eos/render/Texture.hpp" 25 | #include "eos/render/texture_extraction.hpp" 26 | 27 | #include "mexplus_eos_types.hpp" 28 | #include "mexplus_opencv.hpp" 29 | 30 | #include "mexplus.h" 31 | #include "mexplus/dispatch.h" 32 | 33 | #include "opencv2/core/core.hpp" 34 | 35 | #include "mex.h" 36 | 37 | using namespace eos; 38 | using namespace mexplus; 39 | 40 | MEX_DEFINE(extract_texture)(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) 41 | { 42 | // Check for proper number of input and output arguments: 43 | if (nrhs != 5) 44 | { 45 | mexErrMsgIdAndTxt("eos:render:nargin", "extract_texture requires 5 input arguments."); 46 | } 47 | if (nlhs != 1) 48 | { 49 | mexErrMsgIdAndTxt("eos:render:nargout", "extract_texture returns one output argument."); 50 | } 51 | 52 | InputArguments input(nrhs, prhs, 5); 53 | const auto mesh = input.get(0); 54 | const auto rendering_params = input.get(1); 55 | const auto image = input.get(2); 56 | const auto compute_view_angle = input.get(3); // default: false 57 | const auto isomap_resolution = input.get(4); // default: 512 58 | 59 | // We expect to be given a RGB image. Let's convert it to BGR for OpenCV. 60 | // Actually, it doesn't matter at all for the texture extraction - just keep it! 61 | // cv::Mat image_as_bgr;// = image.clone(); 62 | // cv::cvtColor(image, image_as_bgr, cv::COLOR_RGB2BGR); 63 | 64 | // Now do the actual extraction: 65 | const auto affine_from_ortho = 66 | fitting::get_3x4_affine_camera_matrix(rendering_params, image.cols, image.rows); 67 | const auto isomap = 68 | render::extract_texture(mesh, affine_from_ortho, core::from_mat(image), compute_view_angle, 69 | render::TextureInterpolation::NearestNeighbour, isomap_resolution); 70 | const auto isomap_mat = core::to_mat(isomap); 71 | 72 | // Return the extracted texture map: 73 | OutputArguments output(nlhs, plhs, 1); 74 | output.set(0, isomap_mat); 75 | }; 76 | 77 | MEX_DEFINE(render)(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) 78 | { 79 | // Check for proper number of input and output arguments: 80 | if (nrhs != 6) 81 | { 82 | mexErrMsgIdAndTxt("eos:render:nargin", "render requires 6 input arguments."); 83 | } 84 | if (nlhs != 2) 85 | { 86 | mexErrMsgIdAndTxt("eos:render:nargout", "render returns two output arguments."); 87 | } 88 | 89 | InputArguments input(nrhs, prhs, 6); 90 | const auto mesh = input.get(0); 91 | const auto modelview_matrix = input.get(1); 92 | const auto projection_matrix = input.get(2); 93 | const auto image_width = input.get(3); 94 | const auto image_height = input.get(4); 95 | const auto texture = input.get(5); 96 | 97 | core::Image4u colorbuffer; 98 | core::Image1d depthbuffer; 99 | std::tie(colorbuffer, depthbuffer) = 100 | render::render(mesh, modelview_matrix, projection_matrix, image_width, image_height, 101 | render::create_mipmapped_texture(texture), true, false, 102 | false); // backface culling = true, near & far plane clipping = false 103 | 104 | const cv::Mat colorbuffer_mat = core::to_mat(colorbuffer); 105 | const cv::Mat depthbuffer_mat = core::to_mat(depthbuffer); 106 | 107 | OutputArguments output(nlhs, plhs, 2); 108 | output.set(0, colorbuffer_mat); 109 | output.set(1, depthbuffer_mat); 110 | }; 111 | 112 | MEX_DISPATCH; 113 | -------------------------------------------------------------------------------- /matlab/+eos/+render/render.m: -------------------------------------------------------------------------------- 1 | function [ framebuffer, depthbuffer ] = render(mesh, modelview_matrix, ... 2 | projection_matrix, image_width, image_height, texture) 3 | % RENDER Renders the given mesh in the given pose. 4 | % [ framebuffer, depthbuffer ] = RENDER(mesh, modelview_matrix, ... 5 | % projection_matrix, image_width, image_height, texture) 6 | % 7 | % Renders the mesh with given model-view and projection matrix, and given 8 | % texture, and returns the rendered framebuffer as well as the depthbuffer. 9 | % 10 | % Please see the C++ documentation for the full description: 11 | % http://patrikhuber.github.io/eos/doc/ (TODO: Update to v0.10.1!) 12 | 13 | [ framebuffer, depthbuffer ] = render('render', mesh, modelview_matrix, ... 14 | projection_matrix, image_width, image_height, texture); 15 | 16 | end 17 | -------------------------------------------------------------------------------- /matlab/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.0) 2 | 3 | # If Matlab_ROOT_DIR is set, the Matlab at that location is used. 4 | find_package(Matlab COMPONENTS MX_LIBRARY REQUIRED) 5 | 6 | # Our helper-headers, and Matlab-specific headers - to make them show up in IDEs: 7 | set(EOS_MATLAB_HEADERS 8 | ${CMAKE_CURRENT_SOURCE_DIR}/include/mexplus_eigen.hpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/include/mexplus_eos_types.hpp 10 | ) 11 | add_custom_target(eos-matlab-headers SOURCES ${EOS_MATLAB_HEADERS}) 12 | 13 | # See: https://cmake.org/cmake/help/latest/module/FindMatlab.html?#command:matlab_add_mex 14 | matlab_add_mex( 15 | NAME eos-matlab-fitting 16 | #[EXECUTABLE | MODULE | SHARED] # SHARED is the default. 17 | SRC +eos/+fitting/private/fitting.cpp 18 | OUTPUT_NAME fitting 19 | # DOCUMENTATION +eos/+fitting/fit_shape_and_pose.m # doesn't work - wrong path probably. But it renames the file to fitting.m, so not what we want anyway. 20 | LINK_TO eos 21 | #[...] 22 | ) 23 | 24 | #matlab_add_mex( 25 | # NAME eos-matlab-render 26 | # SRC +eos/+render/private/render.cpp 27 | # OUTPUT_NAME render 28 | # LINK_TO eos 29 | #) 30 | 31 | # Group the matlab bindings targets into one folder (in IDEs): 32 | set_target_properties(eos-matlab-headers eos-matlab-fitting PROPERTIES FOLDER "matlab-bindings") # eos-matlab-render 33 | 34 | target_include_directories(eos-matlab-fitting PRIVATE ${eos_3RDPARTY_DIR}/mexplus/include ${CMAKE_CURRENT_SOURCE_DIR}/include) # the latter one we can use the eos-matlab-headers target? 35 | # target_include_directories(eos-matlab-render PRIVATE ${eos_3RDPARTY_DIR}/mexplus/include ${CMAKE_CURRENT_SOURCE_DIR}/include) 36 | 37 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/demo.m DESTINATION matlab) 38 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include DESTINATION matlab) 39 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/+eos DESTINATION matlab PATTERN "*.cpp" EXCLUDE) 40 | install(TARGETS eos-matlab-fitting DESTINATION matlab/+eos/+fitting/private) 41 | # install(TARGETS eos-matlab-render DESTINATION matlab/+eos/+render/private) 42 | -------------------------------------------------------------------------------- /matlab/demo.m: -------------------------------------------------------------------------------- 1 | %% Demo for running the eos fitting from Matlab 2 | % 3 | %% Set up some required paths to files: 4 | model_file = '../share/sfm_shape_3448.bin'; 5 | blendshapes_file = '../share/expression_blendshapes_3448.bin'; 6 | landmark_mappings = '../share/ibug_to_sfm.txt'; 7 | 8 | %% Load an image and its landmarks in ibug format: 9 | image = imread('../bin/data/image_0010.png'); 10 | landmarks = read_pts_landmarks('../bin/data/image_0010.pts'); 11 | image_width = size(image, 2); image_height = size(image, 1); 12 | 13 | %% Run the fitting, get back the fitted mesh and pose: 14 | [mesh, render_params] = eos.fitting.fit_shape_and_pose(model_file, blendshapes_file, landmarks, landmark_mappings, image_width, image_height); 15 | % Note: The function actually has a few more arguments to files it 16 | % needs. If you're not running it from within eos/matlab/, you need to 17 | % provide them. See its documentation and .m file. 18 | 19 | %% Visualise the fitted mesh using your favourite plot, for example... 20 | figure(1); 21 | plot3(mesh.vertices(:, 1), mesh.vertices(:, 2), mesh.vertices(:, 3), '.'); 22 | % or... 23 | FV.vertices = mesh.vertices(:, 1:3); 24 | FV.faces = mesh.tvi; 25 | figure(2); 26 | patch(FV, 'FaceColor', [1 1 1], 'EdgeColor', 'none', 'FaceLighting', 'phong'); light; axis equal; axis off; 27 | 28 | %% Visualise the fitting in 2D, on top of the input image: 29 | % Project all vertices to 2D (we extend mesh.vertices to homogeneous coordinates and multiply with 4x4 matrices): 30 | points_2d = [mesh.vertices, ones(size(mesh.vertices, 1), 1)] * (render_params.viewport*render_params.projection*render_params.modelview)'; 31 | % Display the image and plot the projected mesh points on top of it: 32 | figure(3); 33 | imshow(image); 34 | hold on; 35 | plot(points_2d(:, 1), points_2d(:, 2), 'g.'); 36 | % We can also plot the landmarks the mesh was fitted to: 37 | plot(landmarks(:, 1), landmarks(:, 2), 'ro'); 38 | 39 | 40 | %% Just a helper function to read ibug .pts landmarks from a file: 41 | function [ landmarks ] = read_pts_landmarks(filename) 42 | 43 | file = fopen(filename, 'r'); 44 | file_content = textscan(file, '%s'); 45 | 46 | landmarks = zeros(68, 2); 47 | 48 | row_idx = 1; 49 | for i=6:2:141 50 | landmarks(row_idx, 1) = str2double(file_content{1}{i}); 51 | landmarks(row_idx, 2) = str2double(file_content{1}{i + 1}); 52 | row_idx = row_idx + 1; 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /matlab/include/mexplus_eigen.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: matlab/include/mexplus_eigen.hpp 5 | * 6 | * Copyright 2016-2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_MEXPLUS_EIGEN_HPP 23 | #define EOS_MEXPLUS_EIGEN_HPP 24 | 25 | #include "mexplus/mxarray.h" 26 | 27 | #include "Eigen/Core" 28 | 29 | #include "mex.h" 30 | 31 | namespace mexplus { 32 | 33 | /** 34 | * @brief Define a template specialisation for Eigen::MatrixXd for ... . 35 | * 36 | * The default precision in Matlab is double, but most matrices in eos (for example the PCA basis matrices 37 | * are stored as float values, so this defines conversion from these matrices to Matlab. 38 | * 39 | * Todo: Documentation. 40 | */ 41 | template <> 42 | mxArray* MxArray::from(const Eigen::MatrixXf& eigen_matrix) 43 | { 44 | const int num_rows = static_cast(eigen_matrix.rows()); 45 | const int num_cols = static_cast(eigen_matrix.cols()); 46 | MxArray out_array(MxArray::Numeric(num_rows, num_cols)); 47 | 48 | // This might not copy the data but it's evil and probably really dangerous!!!: 49 | // mxSetData(const_cast(matrix.get()), (void*)value.data()); 50 | 51 | // This copies the data. But I suppose it makes sense that we copy the data when we go 52 | // from C++ to Matlab, since Matlab can unload the C++ mex module at any time I think. 53 | // Loop is column-wise 54 | for (int c = 0; c < num_cols; ++c) 55 | { 56 | for (int r = 0; r < num_rows; ++r) 57 | { 58 | out_array.set(r, c, eigen_matrix(r, c)); 59 | } 60 | } 61 | return out_array.release(); 62 | }; 63 | 64 | /** 65 | * @brief Define a template specialisation for Eigen::MatrixXd for ... . 66 | * 67 | * Todo: Documentation. 68 | * TODO: Maybe provide this one as MatrixXf as well as MatrixXd? Matlab's default is double? 69 | */ 70 | template <> 71 | void MxArray::to(const mxArray* in_array, Eigen::MatrixXd* eigen_matrix) 72 | { 73 | MxArray array(in_array); 74 | 75 | if (array.dimensionSize() > 2) 76 | { 77 | mexErrMsgIdAndTxt( 78 | "eos:matlab", 79 | "Given array has > 2 dimensions. Can only create 2-dimensional matrices (and vectors)."); 80 | } 81 | 82 | if (array.dimensionSize() == 1 || array.dimensionSize() == 0) 83 | { 84 | mexErrMsgIdAndTxt("eos:matlab", "Given array has 0 or 1 dimensions but we expected a 2-dimensional " 85 | "matrix (or row/column vector)."); 86 | // Even when given a single value dimensionSize() is 2, with n=m=1. When does this happen? 87 | } 88 | 89 | if (!array.isDouble()) 90 | { 91 | mexErrMsgIdAndTxt( 92 | "eos:matlab", 93 | "Trying to create an Eigen::MatrixXd in C++, but the given data is not of type double."); 94 | } 95 | 96 | // We can be sure now that the array is 2-dimensional (or 0, but then we're screwed anyway) 97 | const auto nrows = array.dimensions()[0]; // or use array.rows() 98 | const auto ncols = array.dimensions()[1]; 99 | 100 | // I think I can just use Eigen::Matrix, not a Map - the Matrix c'tor that we call creates a Map anyway? 101 | Eigen::Map> eigen_map( 102 | array.getData(), nrows, ncols); 103 | // Not sure that's alright - who owns the data? I think as it is now, everything points to the data in the 104 | // mxArray owned by Matlab, but I'm not 100% sure. 105 | // Actually, doesn't eigen_map go out of scope and get destroyed? This might be trouble? But this 106 | // assignment should (or might) copy, then it's fine? Check if it invokes the copy c'tor. 107 | // 2 May 2018: Yes this copies. 108 | *eigen_matrix = eigen_map; 109 | }; 110 | 111 | } /* namespace mexplus */ 112 | 113 | #endif /* EOS_MEXPLUS_EIGEN_HPP */ 114 | -------------------------------------------------------------------------------- /matlab/include/mexplus_opencv.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: matlab/include/mexplus_opencv.hpp 5 | * 6 | * Copyright 2017 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef MEXPLUS_OPENCV_HPP_ 23 | #define MEXPLUS_OPENCV_HPP_ 24 | 25 | #include "mexplus/mxarray.h" 26 | 27 | #include "opencv2/core/core.hpp" 28 | 29 | #include "mex.h" 30 | 31 | #include 32 | #include 33 | 34 | namespace mexplus { 35 | 36 | /** 37 | * @brief Convert a cv::Mat to a Matlab matrix. 38 | * 39 | * Conversion should work for matrices of any type, with any number of channels. 40 | * The function creates a new MxArray of the same data type and copies the data over. 41 | * 42 | * Note: Even non-standard step/stride sizes should work, but that is not tested. 43 | */ 44 | template 45 | void deepcopy_and_transpose(const cv::Mat& in, MxArray& out) 46 | { 47 | const std::size_t num_rows = in.rows; 48 | const std::size_t num_cols = in.cols; 49 | const std::size_t num_chans = in.channels(); 50 | out = MxArray::Numeric({num_rows, num_cols, num_chans}); 51 | 52 | for (std::size_t c = 0; c < num_cols; ++c) 53 | { // outer loop over rows would be faster if OpenCV stores data row-major? 54 | for (std::size_t r = 0; r < num_rows; ++r) 55 | { 56 | for (std::size_t chan = 0; chan < num_chans; ++chan) 57 | { 58 | out.set(std::vector{r, c, chan}, in.ptr(r, c)[chan]); 59 | } 60 | } 61 | } 62 | }; 63 | 64 | // Note/Todo: Currently only works for 3-chan and 4-chan U8 images 65 | // Matlab stores matrices in col - major order in memory, OpenCV stores them in row - major.Thus, we copy & transpose... 66 | template 67 | void deepcopy_and_transpose(const MxArray& in, cv::Mat& out) 68 | { 69 | auto r = in.rows(); 70 | auto c = in.cols(); // if dimensionSize() == 3, this returns cols * third_dim 71 | auto d = in.dimensions(); 72 | auto ds = in.dimensionSize(); 73 | auto e = in.elementSize(); // Number of bytes required to store each data element 74 | auto s = in.size(); 75 | auto n = in.isNumeric(); 76 | 77 | const auto num_channels = [ num_dims = in.dimensionSize(), dims = in.dimensions() ]() 78 | { 79 | if (num_dims == 1 || num_dims == 2) 80 | { 81 | return std::size_t(1); 82 | } else if (num_dims == 3) 83 | { // the dims vector has 3 entries, the 3rd entry tell us if there are 3 or 4 channels. 84 | return dims[2]; 85 | } 86 | } 87 | (); 88 | 89 | const auto actual_cols = [&num_channels, num_cols = in.cols() ]() 90 | { 91 | if (num_channels == 3 || num_channels == 4) // aargh... simplify all this... just use in.dimensions()? 92 | { 93 | return num_cols / num_channels; 94 | } else 95 | { 96 | return num_cols; 97 | } 98 | } 99 | (); 100 | 101 | std::vector channels; 102 | for (size_t c = 0; c < num_channels; ++c) 103 | { 104 | cv::Mat outmat; 105 | // Note: I think this doesn't actually copy the data, does it? 106 | // We construct with (cols, rows) because it'll later get transposed. 107 | cv::Mat inmat(actual_cols, in.rows(), cv::DataType::type, 108 | static_cast( 109 | const_cast(in.getData() + actual_cols * in.rows() * c))); 110 | inmat.convertTo(outmat, cv::DataType::type); 111 | cv::transpose(outmat, outmat); 112 | channels.push_back(outmat); 113 | } 114 | cv::merge(channels, out); 115 | }; 116 | 117 | /** 118 | * @brief Convert a cv::Mat to a Matlab matrix. 119 | * 120 | * Conversion should work for matrices of type CV_8U, CV_32F and CV_64F, with any number of channels. 121 | */ 122 | template <> 123 | mxArray* MxArray::from(const cv::Mat& opencv_matrix) 124 | { 125 | MxArray out_array; 126 | if (opencv_matrix.depth() == CV_8U) 127 | { 128 | deepcopy_and_transpose(opencv_matrix, out_array); 129 | } else if (opencv_matrix.depth() == CV_32F) 130 | { 131 | deepcopy_and_transpose(opencv_matrix, out_array); 132 | } else if (opencv_matrix.depth() == CV_64F) 133 | { 134 | deepcopy_and_transpose(opencv_matrix, out_array); 135 | } else 136 | { 137 | mexErrMsgIdAndTxt("eos:matlab", "Can only convert CV_8U, CV_32F and CV_64F matrices at this point."); 138 | } 139 | return out_array.release(); 140 | }; 141 | 142 | /** 143 | * @brief Convert a 3-channel or 4-channel uint8 Matlab matrix to a cv::Mat. 144 | * 145 | * Currently only works for 3-channel and 4-channel uint8 Matlab matrices (i.e. images and isomaps). 146 | * Todo: Add some error detection, it will silently fail or crash when the type/channels are 147 | * not correct. 148 | */ 149 | template <> 150 | void MxArray::to(const mxArray* in_array, cv::Mat* opencv_matrix) 151 | { 152 | MxArray array(in_array); 153 | deepcopy_and_transpose(array, *opencv_matrix); 154 | }; 155 | 156 | } /* namespace mexplus */ 157 | 158 | #endif /* MEXPLUS_OPENCV_HPP_ */ 159 | -------------------------------------------------------------------------------- /python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | pybind11_add_module(python-bindings generate-python-bindings.cpp pybind11_Image.hpp pybind11_optional.hpp pybind11_variant.hpp) 2 | target_link_libraries(python-bindings PRIVATE eos) 3 | set_target_properties(python-bindings PROPERTIES OUTPUT_NAME eos) 4 | 5 | install(TARGETS python-bindings DESTINATION python) 6 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/demo.py DESTINATION python) 7 | -------------------------------------------------------------------------------- /python/demo.py: -------------------------------------------------------------------------------- 1 | import eos 2 | import numpy as np 3 | 4 | def main(): 5 | """Demo for running the eos fitting from Python.""" 6 | landmarks = read_pts('../bin/data/image_0010.pts') 7 | image_width = 1280 # Make sure to adjust these when using your own images! 8 | image_height = 1024 9 | 10 | model = eos.morphablemodel.load_model("../share/sfm_shape_3448.bin") 11 | blendshapes = eos.morphablemodel.load_blendshapes("../share/expression_blendshapes_3448.bin") 12 | # Create a MorphableModel with expressions from the loaded neutral model and blendshapes: 13 | morphablemodel_with_expressions = eos.morphablemodel.MorphableModel(model.get_shape_model(), blendshapes, 14 | color_model=eos.morphablemodel.PcaModel(), 15 | vertex_definitions=None, 16 | texture_coordinates=model.get_texture_coordinates()) 17 | landmark_mapper = eos.core.LandmarkMapper('../share/ibug_to_sfm.txt') 18 | edge_topology = eos.morphablemodel.load_edge_topology('../share/sfm_3448_edge_topology.json') 19 | contour_landmarks = eos.fitting.ContourLandmarks.load('../share/ibug_to_sfm.txt') 20 | model_contour = eos.fitting.ModelContour.load('../share/sfm_model_contours.json') 21 | 22 | (mesh, pose, shape_coeffs, blendshape_coeffs) = eos.fitting.fit_shape_and_pose(morphablemodel_with_expressions, 23 | landmarks, landmark_mapper, image_width, image_height, edge_topology, contour_landmarks, model_contour) 24 | 25 | # Now you can use your favourite plotting/rendering library to display the fitted mesh, using the rendering 26 | # parameters in the 'pose' variable. 27 | 28 | # Or for example extract the texture map, like this: 29 | # import cv2 30 | # image = cv2.imread('../bin/data/image_0010.png') 31 | # image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA, 4) # extract_texture(...) expects a 4-channel image 32 | # texturemap = eos.render.extract_texture(mesh, pose, image) 33 | 34 | 35 | def read_pts(filename): 36 | """A helper function to read the 68 ibug landmarks from a .pts file.""" 37 | lines = open(filename).read().splitlines() 38 | lines = lines[3:71] 39 | 40 | landmarks = [] 41 | ibug_index = 1 # count from 1 to 68 for all ibug landmarks 42 | for l in lines: 43 | coords = l.split() 44 | landmarks.append(eos.core.Landmark(str(ibug_index), [float(coords[0]), float(coords[1])])) 45 | ibug_index = ibug_index + 1 46 | 47 | return landmarks 48 | 49 | if __name__ == "__main__": 50 | main() 51 | -------------------------------------------------------------------------------- /python/pybind11_optional.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: python/pybind11_optional.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_PYBIND11_OPTIONAL_HPP 23 | #define EOS_PYBIND11_OPTIONAL_HPP 24 | 25 | /** 26 | * @file python/pybind11_optional.hpp 27 | * @brief Define a type_caster for akrzemi1::optional, which is used when the compiler doesn't have 28 | * (e.g. on Apple). 29 | */ 30 | 31 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 32 | 33 | #include "pybind11/stl.h" 34 | 35 | #else 36 | 37 | #include "eos/cpp17/optional.hpp" 38 | #include "pybind11/stl.h" 39 | 40 | namespace pybind11 { 41 | namespace detail { 42 | 43 | /** 44 | * @brief Type caster for akrzemi1::optional, which is used when the compiler doesn't have (e.g. on 45 | * Apple). 46 | */ 47 | template 48 | struct type_caster> : optional_caster> 49 | { 50 | }; 51 | 52 | } /* namespace detail */ 53 | } /* namespace pybind11 */ 54 | 55 | #endif 56 | 57 | #endif /* EOS_PYBIND11_OPTIONAL_HPP */ 58 | -------------------------------------------------------------------------------- /python/pybind11_variant.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: python/pybind11_variant.hpp 5 | * 6 | * Copyright 2018 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #pragma once 21 | 22 | #ifndef EOS_PYBIND11_VARIANT_HPP 23 | #define EOS_PYBIND11_VARIANT_HPP 24 | 25 | /** 26 | * @file python/pybind11_variant.hpp 27 | * @brief Define a type_caster for mpark::variant, which is used when the compiler doesn't have 28 | * (e.g. on Apple). 29 | */ 30 | 31 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 32 | 33 | #include "pybind11/stl.h" 34 | 35 | #else 36 | 37 | #include "eos/cpp17/variant.hpp" 38 | #include "pybind11/stl.h" 39 | 40 | namespace pybind11 { 41 | namespace detail { 42 | 43 | /** 44 | * @brief Type caster for mpark::variant, which is used when the compiler doesn't have (e.g. on 45 | * Apple). 46 | */ 47 | template 48 | struct type_caster> : variant_caster> 49 | { 50 | }; 51 | 52 | } /* namespace detail */ 53 | } /* namespace pybind11 */ 54 | 55 | #endif 56 | 57 | #endif /* EOS_PYBIND11_VARIANT_HPP */ 58 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import platform 5 | import subprocess 6 | 7 | from setuptools import setup, Extension 8 | from setuptools.command.build_ext import build_ext 9 | from distutils.version import LooseVersion 10 | 11 | 12 | class CMakeExtension(Extension): 13 | def __init__(self, name, sourcedir=''): 14 | Extension.__init__(self, name, sources=[]) 15 | self.sourcedir = os.path.abspath(sourcedir) 16 | 17 | 18 | class CMakeBuild(build_ext): 19 | 20 | def run(self): 21 | try: 22 | out = subprocess.check_output(['cmake', '--version']) 23 | except OSError: 24 | raise RuntimeError("CMake must be installed to build the following extensions: " + 25 | ", ".join(e.name for e in self.extensions)) 26 | 27 | if platform.system() == "Windows": 28 | cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) 29 | if cmake_version < '3.10.0': 30 | raise RuntimeError("CMake >= 3.10.0 is required on Windows") 31 | 32 | for ext in self.extensions: 33 | self.build_extension(ext) 34 | 35 | def build_extension(self, ext): 36 | extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) 37 | cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, 38 | '-DPYTHON_EXECUTABLE=' + sys.executable, 39 | '-DEOS_BUILD_EXAMPLES=OFF', 40 | '-DEOS_GENERATE_PYTHON_BINDINGS=ON' 41 | ] 42 | 43 | cfg = 'Debug' if self.debug else 'Release' 44 | build_args = ['--config', cfg] 45 | 46 | if platform.system() == "Windows": 47 | if platform.architecture()[0] == '32bit': 48 | cmake_args += ['-A', 'Win32'] 49 | else: 50 | cmake_args += ['-A', 'x64'] 51 | cmake_args += ['-G', 'Visual Studio 17 2022'] 52 | cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] 53 | build_args += ['--', '/m'] 54 | else: 55 | cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] 56 | build_args += ['--', '-j2'] 57 | 58 | env = os.environ.copy() 59 | env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), 60 | self.distribution.get_version()) 61 | if not os.path.exists(self.build_temp): 62 | os.makedirs(self.build_temp) 63 | subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) 64 | subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp) 65 | 66 | 67 | this_directory = os.path.abspath(os.path.dirname(__file__)) 68 | with open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f: 69 | long_description = f.read() 70 | 71 | setup( 72 | name='eos-py', 73 | version='1.5.0', 74 | author='Patrik Huber', 75 | author_email='patrikhuber@gmail.com', 76 | description='Python bindings for eos - A lightweight 3D Morphable Face Model fitting library in modern C++11/14', 77 | long_description=long_description, 78 | long_description_content_type='text/markdown', 79 | url='https://github.com/patrikhuber/eos', 80 | license='Apache-2.0', 81 | ext_modules=[CMakeExtension('eos')], 82 | cmdclass=dict(build_ext=CMakeBuild), 83 | zip_safe=False, 84 | ) 85 | -------------------------------------------------------------------------------- /share/bfm2009_model_contours.json: -------------------------------------------------------------------------------- 1 | { 2 | "model_contour": { 3 | "right_contour": [ 4 | 27302, 5 | 46587, 6 | 45866, 7 | 2445, 8 | 1260, 9 | 721, 10 | 316, 11 | 27431 12 | ], 13 | "left_contour": [ 14 | 27689, 15 | 16312, 16 | 15943, 17 | 15450, 18 | 14313, 19 | 50480, 20 | 49788, 21 | 27818 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /share/bfm2017-1_bfm_nomouth_model_contours.json: -------------------------------------------------------------------------------- 1 | { 2 | "model_contour": { 3 | "right_contour": [ 4 | 22451, 5 | 22463, 6 | 22231, 7 | 21961, 8 | 21591, 9 | 21737, 10 | 23037, 11 | 43151, 12 | 44368, 13 | 45617, 14 | 46999 15 | ], 16 | "left_contour": [ 17 | 31991, 18 | 32002, 19 | 32272, 20 | 32795, 21 | 33069, 22 | 33088, 23 | 32329, 24 | 52325, 25 | 50233, 26 | 49299, 27 | 48560 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /share/expression_blendshapes_3448.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrikhuber/eos/ce984207a51c2edd2e7568a64fec9b98ef61aeed/share/expression_blendshapes_3448.bin -------------------------------------------------------------------------------- /share/ibug_to_bfm2009.txt: -------------------------------------------------------------------------------- 1 | # Mapping from the 68-point ibug annotations to the BFM (2009) (3DMM vertex indices). 2 | # The numbers in brackets are MPEG-4 facial feature point numbers. 3 | 4 | [landmark_mappings] # A mapping from input landmarks (ibug, lhs) to output landmarks (BFM, rhs) 5 | # 1 to 8 are the right contour landmarks 6 | # chin bottom (2.1, MPEG point not marked in the BFM) 7 | # 10 to 17 are the left contour landmarks 8 | 18 = 38792 # right eyebrow outer-corner (4.6) 9 | 20 = 40087 # right eyebrow middle, vertical middle (4.4, the MPEG point is on top of the brow though) 10 | 22 = 40514 # right eyebrow inner-corner (4.2) 11 | 23 = 41091 # left eyebrow inner-corner (4.1) 12 | 25 = 41511 # left eyebrow middle (4.3, the MPEG point is on top of the brow though) 13 | 27 = 42825 # left eyebrow outer-corner (4.5) 14 | 31 = 8319 # nose-tip (9.3) 15 | 34 = 8334 # nose-lip junction (9.15) 16 | 37 = 2088 # right eye outer-corner (3.12) 17 | 40 = 5959 # right eye inner-corner (3.8) 18 | 43 = 10603 # left eye inner-corner (3.11) 19 | 46 = 14472 # left eye outer-corner (3.7) 20 | 49 = 5006 # right mouth corner (8.4) 21 | 52 = 8344 # upper lip middle top (8.1) 22 | 55 = 11714 # left mouth corner (8.3) 23 | 58 = 8374 # lower lip middle bottom (8.2) 24 | #61 # right inner corner of the mouth (2.5) 25 | #62 # upper lip right bottom outer (2.7) 26 | 63 = 8354 # upper lip middle bottom (2.2) 27 | #64 # upper lip left bottom outer (2.6) 28 | #65 # left inner corner of the mouth (2.4) 29 | #66 # lower lip left top outer (2.8) 30 | 67 = 8366 # lower lip middle top (2.3) 31 | #68 # lower lip right top outer (2.9) 32 | 33 | 34 | # Definitions of which 2D landmarks make up the right and left face contours: 35 | [contour_landmarks] 36 | right = [ 1, 37 | 2, 38 | 3, 39 | 4, 40 | 5, 41 | 6, 42 | 7, 43 | 8 44 | ] 45 | left = [ 10, 46 | 11, 47 | 12, 48 | 13, 49 | 14, 50 | 15, 51 | 16, 52 | 17 53 | ] 54 | -------------------------------------------------------------------------------- /share/ibug_to_bfm2017-1_bfm_nomouth.txt: -------------------------------------------------------------------------------- 1 | # Mapping from the 68-point ibug annotations to the BFM2017 head model (vertex indices). 2 | 3 | [landmark_mappings] # A mapping from input landmarks (ibug, lhs) to output landmarks (BFM, rhs) 4 | # 1 to 8 are the right contour landmarks 5 | 9 = 47846 # chin bottom 6 | # 10 to 17 are the left contour landmarks 7 | #18 = # right eyebrow outer-corner 8 | #20 = # right eyebrow middle, vertical middle 9 | #22 = # right eyebrow inner-corner 10 | #23 = # left eyebrow inner-corner 11 | #25 = # left eyebrow middle 12 | #27 = # left eyebrow outer-corner 13 | 31 = 8156 # nose-tip 14 | #34 = # nose-lip junction 15 | 37 = 2602 # right eye outer-corner 16 | 40 = 5830 # right eye inner-corner 17 | 43 = 10390 # left eye inner-corner 18 | 46 = 13481 # left eye outer-corner 19 | 49 = 5522 # right mouth corner 20 | 50 = 6026 # upper lip right-right top 21 | 51 = 7355 # upper lip middle-right top 22 | 52 = 8181 # upper lip middle top 23 | 53 = 9007 # upper lip middle-left top 24 | 54 = 10329 # upper lip left-left top 25 | 55 = 10857 # left mouth corner 26 | 56 = 9730 # 27 | 57 = 8670 # 28 | 58 = 8199 # lower lip middle bottom 29 | 59 = 7726 # 30 | 60 = 6898 # 31 | 61 = 6291 # right inner corner of the mouth 32 | 62 = 7364 # upper lip right bottom outer 33 | 63 = 8190 # upper lip middle bottom 34 | 64 = 9016 # upper lip left bottom outer 35 | 65 = 10088 # left inner corner of the mouth 36 | 66 = 8663 # lower lip left top outer 37 | 67 = 8191 # lower lip middle top 38 | 68 = 7719 # lower lip right top outer 39 | 40 | 41 | # Definitions of which 2D landmarks make up the right and left face contours: 42 | [contour_landmarks] 43 | right = [ 1, 44 | 2, 45 | 3, 46 | 4, 47 | 5, 48 | 6, 49 | 7, 50 | 8 51 | ] 52 | left = [ 10, 53 | 11, 54 | 12, 55 | 13, 56 | 14, 57 | 15, 58 | 16, 59 | 17 60 | ] 61 | -------------------------------------------------------------------------------- /share/ibug_to_sfm.txt: -------------------------------------------------------------------------------- 1 | # Mapping from the 68-point ibug annotations to the Surrey Face Model (SFM) mesh vertex indices. 2 | # Note: Points above vertex id 845 are not defined on the reference and thus not available in all model resolutions. 3 | # This file uses TOML syntax (https://github.com/toml-lang/toml). 4 | 5 | # Mappings from input landmarks (ibug, lhs) to output landmarks (SFM, rhs): 6 | [landmark_mappings] 7 | # 1 to 8 are the right contour landmarks 8 | 9 = 33 # chin bottom 9 | # 10 to 17 are the left contour landmarks 10 | 18 = 225 # right eyebrow outer-corner (18) 11 | 19 = 229 # right eyebrow between middle and outer corner 12 | 20 = 233 # right eyebrow middle, vertical middle (20) 13 | 21 = 2086 # right eyebrow between middle and inner corner 14 | 22 = 157 # right eyebrow inner-corner (19) 15 | 23 = 590 # left eyebrow inner-corner (23) 16 | 24 = 2091 # left eyebrow between inner corner and middle 17 | 25 = 666 # left eyebrow middle (24) 18 | 26 = 662 # left eyebrow between middle and outer corner 19 | 27 = 658 # left eyebrow outer-corner (22) 20 | 28 = 2842 # bridge of the nose (parallel to upper eye lids) 21 | 29 = 379 # middle of the nose, a bit below the lower eye lids 22 | 30 = 272 # above nose-tip (1cm or so) 23 | 31 = 114 # nose-tip (3) 24 | 32 = 100 # right nostril, below nose, nose-lip junction 25 | 33 = 2794 # nose-lip junction 26 | 34 = 270 # nose-lip junction (28) 27 | 35 = 2797 # nose-lip junction 28 | 36 = 537 # left nostril, below nose, nose-lip junction 29 | 37 = 177 # right eye outer-corner (1) 30 | 38 = 172 # right eye pupil top right (from subject's perspective) 31 | 39 = 191 # right eye pupil top left 32 | 40 = 181 # right eye inner-corner (5) 33 | 41 = 173 # right eye pupil bottom left 34 | 42 = 174 # right eye pupil bottom right 35 | 43 = 614 # left eye inner-corner (8) 36 | 44 = 624 # left eye pupil top right 37 | 45 = 605 # left eye pupil top left 38 | 46 = 610 # left eye outer-corner (2) 39 | 47 = 607 # left eye pupil bottom left 40 | 48 = 606 # left eye pupil bottom right 41 | 49 = 398 # right mouth corner (12) 42 | 50 = 315 # upper lip right top outer 43 | 51 = 413 # upper lip middle top right 44 | 52 = 329 # upper lip middle top (14) 45 | 53 = 825 # upper lip middle top left 46 | 54 = 736 # upper lip left top outer 47 | 55 = 812 # left mouth corner (13) 48 | 56 = 841 # lower lip left bottom outer 49 | 57 = 693 # lower lip middle bottom left 50 | 58 = 411 # lower lip middle bottom (17) 51 | 59 = 264 # lower lip middle bottom right 52 | 60 = 431 # lower lip right bottom outer 53 | # 61 not defined - would be right inner corner of the mouth 54 | 62 = 416 # upper lip right bottom outer 55 | 63 = 423 # upper lip middle bottom 56 | 64 = 828 # upper lip left bottom outer 57 | # 65 not defined - would be left inner corner of the mouth 58 | 66 = 817 # lower lip left top outer 59 | 67 = 442 # lower lip middle top 60 | 68 = 404 # lower lip right top outer 61 | 62 | 63 | # Definitions of which 2D landmarks make up the right and left face contours: 64 | [contour_landmarks] 65 | right = [ 1, 66 | 2, 67 | 3, 68 | 4, 69 | 5, 70 | 6, 71 | 7, 72 | 8 73 | ] 74 | left = [ 10, 75 | 11, 76 | 12, 77 | 13, 78 | 14, 79 | 15, 80 | 16, 81 | 17 82 | ] 83 | -------------------------------------------------------------------------------- /share/readme.txt: -------------------------------------------------------------------------------- 1 | eos: A lightweight header-only 3D Morphable Model fitting library in modern C++11/14 2 | ========= 3 | 4 | Files in this directory: 5 | 6 | - ibug_to_sfm.txt: 7 | Mappings from the popular ibug 68-point 2D facial landmarks markup to 8 | Surrey Face Model indices. 9 | 10 | - sfm_shape_3448.bin: 11 | The public shape-only Surrey 3D Morphable Face Model. 12 | To obtain a full 3DMM and higher resolution levels, follow the instructions 13 | at cvssp.org/facemodel. 14 | Details about the different models can be found in: 15 | "A Multiresolution 3D Morphable Face Model and Fitting Framework", 16 | P. Huber, G. Hu, R. Tena, P. Mortazavian, W. Koppen, W. Christmas, M. Rätsch, J. Kittler, 17 | VISAPP 2016, Rome, Italy. 18 | 19 | - expression_blendshapes_3448.bin: 20 | 6 expression blendshapes for the sfm_shape_3448 model. Contains the expressions anger, 21 | disgust, fear, happiness, sadness and surprise. 22 | 23 | - sfm_3448_edge_topology.json: 24 | Contains a precomputed list of the model's edges, and the two faces and vertices that are 25 | adjacent to each edge. Uses 1-based indexing ("0" has a special meaning of "no adjacent 26 | vertex/edge") - this may change to 0-based in the future to be consistent with the rest of 27 | the library. The file is used in the edge-fitting. 28 | 29 | - sfm_model_contours.json: 30 | Definition of the SFM's contour vertices of the right and left side of the face. 31 | 32 | - sfm_reference.obj: 33 | The reference 3D shape used to built the Surrey Face Model. We make it available so 34 | that new user-defined landmark points can be marked in this lowest-resolution 35 | model, if the points exist here. 36 | 37 | - sfm_reference_annotated.obj: 38 | Visualisation of the landmark points defined in the ibug_to_sfm.txt mapping file. 39 | * Red: Annotated ibug points that are defined on the reference shape. 40 | * Green: Contour vertices from the file model_contours.json. 41 | The file ibug_to_sfm.txt contains a few more mappings of landmarks that are not present 42 | in the reference, for example the middle-inner eyebrow points - they are not visualised. 43 | 44 | - sfm_reference_symmetry.txt: 45 | Contains a list of vertex symmetries of the reference shape, i.e. each 46 | vertex's symmetric counterpart. See the top of the file for more information. 47 | -------------------------------------------------------------------------------- /share/scripts/compute_edgestruct.m: -------------------------------------------------------------------------------- 1 | %% The code in this file is largely copied and modified from 2 | % https://github.com/waps101/3DMM_edges: 3 | % A. Bas, W.A.P. Smith, T. Bolkart and S. Wuhrer, "Fitting a 3D Morphable 4 | % Model to Edges: A Comparison Between Hard and Soft Correspondences", 5 | % ACCV Workshop 2016. 6 | % The code is licensed under the Apache-2.0 license. 7 | 8 | %% Read the instructions in share/generate-edgestruct.py for how to use this script. 9 | 10 | function [] = compute_edgestruct(trianglelist_file) 11 | load(trianglelist_file); % loads 'triangle_list' from the file 12 | num_vertices = max(max(triangle_list)); % we assume that the largest triangle 13 | % index that we're going to find is the 14 | % number of vertices of the model. 15 | % Get the edge list: 16 | TR = triangulation(double(triangle_list), ones(num_vertices, 1), ones(num_vertices, 1), ones(num_vertices, 1)); 17 | Ev = TR.edges; % This should be a list of all the edges. 18 | clear TR; 19 | Ef = meshFaceEdges(triangle_list, Ev); 20 | save('edgestruct.mat', 'Ef', 'Ev'); % Load this file back into generate-edgestruct.py. 21 | end 22 | 23 | % This function is copied from: 24 | % https://github.com/waps101/3DMM_edges/blob/master/utils/meshFaceEdges.m, 25 | % on 3 Oct 2016. 26 | function Ef = meshFaceEdges(faces, edges) 27 | %MESHFACEEDGES Compute faces adjacent to each edge in the mesh 28 | % faces - nverts by 3 matrix of mesh faces 29 | % edges - nedges by 2 matrix containing vertices adjacent to each edge 30 | % 31 | % This function is slow! But it only needs to be run once for a morphable 32 | % model and the edge-face list can then be saved 33 | 34 | nedges = size(edges, 1); 35 | 36 | faces = sort(faces, 2); 37 | edges = sort(edges, 2); 38 | 39 | disp(' '); 40 | for i=1:nedges 41 | idx = find(((faces(:,1)==edges(i,1)) & ( (faces(:,2)==edges(i,2)) | (faces(:,3)==edges(i,2)) )) | ((faces(:,2)==edges(i,1)) & (faces(:,3)==edges(i,2)))); 42 | if length(idx)==1 43 | idx = [0 idx]; 44 | end 45 | Ef(i,:)=[idx(1) idx(2)]; 46 | fprintf('\b\b\b\b\b\b%05.2f%%', i/nedges*100); 47 | end 48 | 49 | end 50 | -------------------------------------------------------------------------------- /share/scripts/convert-bfm2009-to-eos.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import eos 3 | import scipy.io 4 | 5 | # This script converts the Basel Face Model 2009 (BFM2009, [1]) to the eos model format, 6 | # specifically the file PublicMM1/01_MorphableModel.mat from the BFM2009 distribution. 7 | # 8 | # The script does not use or convert the segments of the BFM2009, just the global PCA. 9 | # The BFM2009 also does not come with texture (uv-) coordinates. If you have texture coordinates for the BFM, they can 10 | # be added to the eos.morphablemodel.MorphableModel(...) constructor. 11 | # 12 | # [1]: A 3D Face Model for Pose and Illumination Invariant Face Recognition, 13 | # P. Paysan, R. Knothe, B. Amberg, S. Romdhani, and T. Vetter, 14 | # AVSS 2009. 15 | # http://faces.cs.unibas.ch/bfm/main.php?nav=1-0&id=basel_face_model 16 | 17 | # Set this to the path of the PublicMM1/01_MorphableModel.mat file from the BFM2009 distribution: 18 | bfm2009_path = r"./PublicMM1/01_MorphableModel.mat" 19 | bfm2009 = scipy.io.loadmat(bfm2009_path) 20 | 21 | # The PCA shape model: 22 | # Note: All the matrices are of type float32, so we're good and don't need to convert anything. 23 | shape_mean = bfm2009['shapeMU'] 24 | shape_orthogonal_pca_basis = bfm2009['shapePC'] 25 | # Their basis is unit norm: np.linalg.norm(shape_pca_basis[:,0]) == 1.0 26 | # And the basis vectors are orthogonal: np.dot(shape_pca_basis[:,0], shape_pca_basis[:,0]) == 1.0 27 | # np.dot(shape_pca_basis[:,0], shape_pca_basis[:,1]) == 1e-08 28 | shape_pca_standard_deviations = bfm2009['shapeEV'] # These are standard deviations, not eigenvalues! 29 | shape_pca_eigenvalues = np.square(shape_pca_standard_deviations) 30 | triangle_list = bfm2009['tl'] - 1 # Convert from 1-based Matlab indexing to 0-based C++ indexing 31 | # The BFM has front-facing triangles defined the wrong way round (not in accordance with OpenGL) - we swap the indices: 32 | for t in triangle_list: 33 | t[1], t[2] = t[2], t[1] 34 | shape_model = eos.morphablemodel.PcaModel(shape_mean, shape_orthogonal_pca_basis, shape_pca_eigenvalues, 35 | triangle_list.tolist()) 36 | 37 | # PCA colour model: 38 | color_mean = bfm2009['texMU'] 39 | # The BFM2009's colour data is in the range [0, 255], while eos generally expects [0, 1], so we divide by 255: 40 | color_mean /= 255 41 | color_orthogonal_pca_basis = bfm2009['texPC'] 42 | color_pca_standard_deviations = bfm2009['texEV'] # Again, these are standard deviations, not eigenvalues 43 | color_pca_standard_deviations /= 255 # Divide the standard deviations by the same amount as the mean 44 | color_pca_eigenvalues = np.square(color_pca_standard_deviations) 45 | 46 | color_model = eos.morphablemodel.PcaModel(color_mean, color_orthogonal_pca_basis, color_pca_eigenvalues, 47 | triangle_list.tolist()) 48 | 49 | # Construct and save the BFM2009 model in the eos format: 50 | model = eos.morphablemodel.MorphableModel(shape_model, color_model, vertex_definitions=None, 51 | texture_coordinates=[], 52 | texture_triangle_indices=[]) # uv-coordinates can be added here 53 | eos.morphablemodel.save_model(model, "bfm2009.bin") 54 | print("Converted and saved model as bfm2009.bin.") 55 | -------------------------------------------------------------------------------- /share/scripts/convert-bfm2017-to-eos.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import eos 3 | import h5py 4 | 5 | # This script converts the Basel Face Model 2017 (BFM2017, [1]) to the eos model format, 6 | # specifically the files model2017-1_face12_nomouth.h5 and model2017-1_bfm_nomouth.h5 from the BFM2017 download. 7 | # 8 | # The BFM2017 does not come with texture (uv-) coordinates. If you have texture coordinates for the BFM, they can be 9 | # added to the eos.morphablemodel.MorphableModel(...) constructor. 10 | # 11 | # [1]: Morphable Face Models - An Open Framework, 12 | # T. Gerig, A. Morel-Forster, C. Blumer, B. Egger, M. Lüthi, S. Schönborn and T. Vetter, 13 | # arXiv preprint, 2017. 14 | # http://faces.cs.unibas.ch/bfm/bfm2017.html 15 | 16 | # Set this to the path of the model2017-1_bfm_nomouth.h5 or model2017-1_face12_nomouth.h5 file from the BFM2017 download: 17 | bfm2017_file = r"./model2017-1_bfm_nomouth.h5" 18 | 19 | with h5py.File(bfm2017_file, 'r') as hf: 20 | # The PCA shape model: 21 | shape_mean = np.array(hf['shape/model/mean']) 22 | shape_orthogonal_pca_basis = np.array(hf['shape/model/pcaBasis']) 23 | # Their basis is unit norm: np.linalg.norm(shape_pca_basis[:,0]) == ~1.0 24 | # And the basis vectors are orthogonal: np.dot(shape_pca_basis[:,0], shape_pca_basis[:,0]) == 1.0 25 | # np.dot(shape_pca_basis[:,0], shape_pca_basis[:,1]) == 1e-10 26 | shape_pca_variance = np.array(hf['shape/model/pcaVariance']) # the PCA variances are the eigenvectors 27 | 28 | triangle_list = np.array(hf['shape/representer/cells']) 29 | 30 | shape_model = eos.morphablemodel.PcaModel(shape_mean, shape_orthogonal_pca_basis, shape_pca_variance, 31 | triangle_list.transpose().tolist()) 32 | 33 | # PCA colour model: 34 | color_mean = np.array(hf['color/model/mean']) 35 | color_orthogonal_pca_basis = np.array(hf['color/model/pcaBasis']) 36 | color_pca_variance = np.array(hf['color/model/pcaVariance']) 37 | 38 | color_model = eos.morphablemodel.PcaModel(color_mean, color_orthogonal_pca_basis, color_pca_variance, 39 | triangle_list.transpose().tolist()) 40 | 41 | # PCA expression model: 42 | expression_mean = np.array(hf['expression/model/mean']) 43 | expression_pca_basis = np.array(hf['expression/model/pcaBasis']) 44 | expression_pca_variance = np.array(hf['expression/model/pcaVariance']) 45 | 46 | expression_model = eos.morphablemodel.PcaModel(expression_mean, expression_pca_basis, expression_pca_variance, 47 | triangle_list.transpose().tolist()) 48 | 49 | # Construct and save an eos model from the BFM data: 50 | model = eos.morphablemodel.MorphableModel(shape_model, expression_model, color_model, vertex_definitions=None, 51 | texture_coordinates=[], 52 | texture_triangle_indices=[]) # uv-coordinates can be added here 53 | eos.morphablemodel.save_model(model, "bfm2017-1_bfm_nomouth.bin") 54 | print("Converted and saved model as bfm2017-1_bfm_nomouth.bin.") 55 | -------------------------------------------------------------------------------- /share/scripts/generate-edgestruct.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import eos 3 | import scipy.io 4 | 5 | # This script computes an edge_topology.json file for a given model, which is used in eos's contour fitting. 6 | # The script can be used for any Morphable Model, for example the SFM, BFM2009, BFM2017, and others. 7 | 8 | # Set this to the path of the model that you want to generate an edgestruct from: 9 | model_path = "bfm2017-1_bfm_nomouth.bin" 10 | 11 | # Step 1: 12 | # Save the triangle list of the model to Matlab (to be read by Matlab in Step 2): 13 | model = eos.morphablemodel.load_model(model_path) 14 | triangle_list = np.array(model.get_shape_model().get_triangle_list()) + 1 # add 1 to make 1-based indices for Matlab 15 | scipy.io.savemat("bfm2017-1_bfm_nomouth_trianglelist.mat", {'triangle_list': triangle_list}) 16 | 17 | # Step 2: 18 | # Open Matlab and run compute_edgestruct.m on the generated triangle-list .mat file. 19 | # Matlab will save an edgestruct.mat file with face and vertex adjacency information. 20 | 21 | # Step 3: 22 | # Load the generated edgestruct.mat from Matlab and save it as an eos EdgeTopology in json format: 23 | edgestruct_path = r"edgestruct.mat" 24 | edge_info = scipy.io.loadmat(edgestruct_path) 25 | Ef = edge_info['Ef'] 26 | Ev = edge_info['Ev'] 27 | edge_topology = eos.morphablemodel.EdgeTopology(Ef.tolist(), Ev.tolist()) 28 | eos.morphablemodel.save_edge_topology(edge_topology, "bfm2017-1_bfm_nomouth_edge_topology.json") 29 | 30 | print("Finished generating edge-topology file and saved it as bfm2017-1_bfm_nomouth_edge_topology.json.") 31 | -------------------------------------------------------------------------------- /share/scripts/load_lyhm.py: -------------------------------------------------------------------------------- 1 | import eos 2 | import numpy as np 3 | from scipy.io import loadmat 4 | 5 | # This script loads the Liverpool-York Head Model (LYHM, [1]) from one of their Matlab .mat files into the eos model 6 | # format, and returns an eos.morphablemodel.MorphableModel. 7 | # 8 | # Note: The LYHM does not come with texture (uv-) coordinates. If you have texture coordinates for the model, they can 9 | # be added to the eos.morphablemodel.MorphableModel(...) constructor as a parameter. 10 | # 11 | # [1]: Statistical Modeling of Craniofacial Shape and Texture, 12 | # H. Dai, N. E. Pears, W. Smith and C. Duncan, 13 | # International Journal of Computer Vision (2019). 14 | # https://www-users.cs.york.ac.uk/~nep/research/LYHM/ 15 | 16 | 17 | def load_lyhm(matlab_model_path): 18 | lyhm = loadmat(matlab_model_path) 19 | triangle_list = lyhm['tri']['faces'][0][0] - 1 # Convert from 1-based Matlab indexing to 0-based C++ indexing 20 | # The LYHM has front-facing triangles defined the wrong way round (not in accordance with OpenGL) - we swap the indices: 21 | for t in triangle_list: 22 | t[1], t[2] = t[2], t[1] 23 | 24 | # The LYHM .mat files contain the orthonormal basis vectors, so we don't need to convert anything: 25 | shape_mean = lyhm['shp']['mu'][0][0][0] 26 | shape_orthonormal_pca_basis = lyhm['shp']['eigVec'][0][0] 27 | shape_pca_eigenvalues = lyhm['shp']['eigVal'][0][0] 28 | 29 | # The color values are in [0, 1] 30 | color_mean = lyhm['tex']['mu'][0][0][0] 31 | color_orthonormal_pca_basis = lyhm['tex']['eigVec'][0][0] 32 | color_pca_eigenvalues = lyhm['tex']['eigVal'][0][0] 33 | 34 | # Construct and return the LYHM as eos MorphableModel: 35 | shape_model = eos.morphablemodel.PcaModel(shape_mean, shape_orthonormal_pca_basis, shape_pca_eigenvalues, triangle_list) 36 | color_model = eos.morphablemodel.PcaModel(color_mean, color_orthonormal_pca_basis, color_pca_eigenvalues, triangle_list) 37 | model = eos.morphablemodel.MorphableModel(shape_model, color_model) 38 | 39 | return model 40 | -------------------------------------------------------------------------------- /share/sfm_model_contours.json: -------------------------------------------------------------------------------- 1 | { 2 | "model_contour": { 3 | "right_contour": [ 4 | 380, 5 | 373, 6 | 356, 7 | 358, 8 | 359, 9 | 360, 10 | 365, 11 | 363, 12 | 364, 13 | 388, 14 | 391, 15 | 392, 16 | 393, 17 | 11, 18 | 21, 19 | 25, 20 | 22 21 | ], 22 | "left_contour": [ 23 | 795, 24 | 790, 25 | 773, 26 | 775, 27 | 776, 28 | 777, 29 | 782, 30 | 780, 31 | 781, 32 | 802, 33 | 805, 34 | 806, 35 | 807, 36 | 454, 37 | 464, 38 | 466, 39 | 465 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /share/sfm_shape_3448.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrikhuber/eos/ce984207a51c2edd2e7568a64fec9b98ef61aeed/share/sfm_shape_3448.bin -------------------------------------------------------------------------------- /tests/test_load_model.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import eos 3 | import numpy as np 4 | from pathlib import Path 5 | 6 | def test_load_model(): 7 | model = eos.morphablemodel.load_model(str(Path(__file__).resolve().parent.parent / 'share' / 'sfm_shape_3448.bin')) 8 | assert model is not None -------------------------------------------------------------------------------- /utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(Boost_NO_WARN_NEW_VERSIONS ON) # Supress "New Boost version may have incorrect dependencies or import targets" warning 2 | find_package(Boost 1.71.0 REQUIRED COMPONENTS program_options) 3 | 4 | # Converts a CVSSP .scm Morphable Model to a cereal binary file: 5 | add_executable(scm-to-cereal scm-to-cereal.cpp) 6 | target_link_libraries(scm-to-cereal PRIVATE eos Boost::program_options) 7 | 8 | # Install targets: 9 | install(TARGETS scm-to-cereal DESTINATION bin) 10 | -------------------------------------------------------------------------------- /utils/scm-to-cereal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14. 3 | * 4 | * File: utils/scm-to-cereal.cpp 5 | * 6 | * Copyright 2015 Patrik Huber 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include "eos/morphablemodel/MorphableModel.hpp" 21 | #include "eos/morphablemodel/io/cvssp.hpp" 22 | #include "eos/cpp17/optional.hpp" 23 | 24 | #include "boost/program_options.hpp" 25 | 26 | #include 27 | #include 28 | 29 | using namespace eos; 30 | namespace po = boost::program_options; 31 | using std::cout; 32 | using std::endl; 33 | 34 | /** 35 | * Reads a CVSSP .scm Morphable Model file and converts it 36 | * to a cereal binary file. 37 | */ 38 | int main(int argc, char* argv[]) 39 | { 40 | std::string scmmodelfile, isomapfile, outputfile; 41 | bool save_shape_only; 42 | try 43 | { 44 | po::options_description desc("Allowed options"); 45 | // clang-format off 46 | desc.add_options() 47 | ("help,h", "display the help message") 48 | ("model,m", po::value(&scmmodelfile)->required(), 49 | "a CVSSP .scm Morphable Model file") 50 | ("isomap,t", po::value(&isomapfile), 51 | "optional text file containing CVSSP texture mapping coordinates") 52 | ("shape-only,s", po::value(&save_shape_only)->default_value(false)->implicit_value(true), 53 | "save only the shape-model part of the full 3DMM") 54 | ("output,o", po::value(&outputfile)->required()->default_value("converted_model.bin"), 55 | "output filename for the Morphable Model in cereal binary format"); 56 | // clang-format on 57 | po::variables_map vm; 58 | po::store(po::command_line_parser(argc, argv).options(desc).run(), vm); 59 | if (vm.count("help")) 60 | { 61 | cout << "Usage: scm-to-cereal [options]" << endl; 62 | cout << desc; 63 | return EXIT_SUCCESS; 64 | } 65 | po::notify(vm); 66 | } catch (const po::error& e) 67 | { 68 | cout << "Error while parsing command-line arguments: " << e.what() << endl; 69 | cout << "Use --help to display a list of options." << endl; 70 | return EXIT_FAILURE; 71 | } 72 | 73 | cpp17::optional isomapfile_optional = 74 | isomapfile.empty() ? cpp17::nullopt : cpp17::optional(isomapfile); 75 | 76 | // Load the .scm Morphable Model and save it as cereal model: 77 | morphablemodel::MorphableModel morphable_model = 78 | morphablemodel::load_scm_model(scmmodelfile, isomapfile_optional); 79 | 80 | if (save_shape_only) 81 | { 82 | // Save only the shape model - to generate the public sfm_shape_3448.bin 83 | const morphablemodel::MorphableModel shape_only_model(morphable_model.get_shape_model(), 84 | morphablemodel::PcaModel(), cpp17::nullopt, 85 | morphable_model.get_texture_coordinates()); 86 | morphablemodel::save_model(shape_only_model, outputfile); 87 | } else 88 | { 89 | morphablemodel::save_model(morphable_model, outputfile); 90 | } 91 | 92 | cout << "Saved converted model as " << outputfile << "." << endl; 93 | return EXIT_SUCCESS; 94 | } 95 | -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "b2cb0da531c2f1f740045bfe7c4dac59f0b2b69c", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "artifact", 10 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 11 | "name": "microsoft" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", 3 | "name": "eos", 4 | "version": "1.5.0", 5 | "dependencies": [ 6 | "boost-filesystem", 7 | "boost-program-options", 8 | { 9 | "name": "opencv4", 10 | "features": [ "jpeg", "png" ], 11 | "default-features": false 12 | } 13 | ], 14 | "features": { 15 | "ceres": { 16 | "description": "Required for non-linear fitting", 17 | "dependencies": [ "ceres" ] 18 | } 19 | } 20 | } 21 | --------------------------------------------------------------------------------