├── .clang-format
├── .gitattributes
├── .gitignore
├── .gitmodules
├── .natvis
└── eos.natvis
├── .travis.yml
├── CMakeLists.txt
├── LICENSE
├── MANIFEST.in
├── README.md
├── appveyor.yml
├── 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
│ │ └── utils.hpp
│ ├── read_obj.hpp
│ └── read_pts_landmarks.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
│ ├── affine_camera_estimation.hpp
│ ├── blendshape_fitting.hpp
│ ├── ceres_nonlinear.hpp
│ ├── closest_edge_fitting.hpp
│ ├── contour_correspondence.hpp
│ ├── detail
│ │ ├── glm_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
│ ├── 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
│ ├── Rasterizer.hpp
│ ├── SoftwareRenderer.hpp
│ ├── Texture.hpp
│ ├── VertexShader.hpp
│ ├── detail
│ │ ├── TriangleToRasterize.hpp
│ │ ├── Vertex.hpp
│ │ ├── render_affine_detail.hpp
│ │ ├── render_detail.hpp
│ │ ├── render_detail_utils.hpp
│ │ ├── texture_extraction_detail.hpp
│ │ └── texturing.hpp
│ ├── draw_utils.hpp
│ ├── render.hpp
│ ├── render_affine.hpp
│ ├── texture_extraction.hpp
│ └── utils.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
├── sfm_3448_edge_topology.json
├── sfm_model_contours.json
├── sfm_reference.obj
├── sfm_reference_annotated.obj
├── sfm_reference_symmetry.txt
└── sfm_shape_3448.bin
└── utils
├── CMakeLists.txt
└── scm-to-cereal.cpp
/.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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore (optional) configuration files with user-specific paths:
2 | initial_cache.cmake
3 | setup.cfg
4 |
--------------------------------------------------------------------------------
/.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/glm"]
8 | path = 3rdparty/glm
9 | url = https://github.com/g-truc/glm.git
10 | [submodule "3rdparty/eigen3-nnls"]
11 | path = 3rdparty/eigen3-nnls
12 | url = https://github.com/hmatuschek/eigen3-nnls.git
13 | [submodule "3rdparty/mexplus"]
14 | path = 3rdparty/mexplus
15 | url = https://github.com/kyamagu/mexplus.git
16 | [submodule "3rdparty/cereal"]
17 | path = 3rdparty/cereal
18 | url = https://github.com/USCiLab/cereal.git
19 | [submodule "3rdparty/toml11"]
20 | path = 3rdparty/toml11
21 | url = https://github.com/ToruNiina/toml11.git
22 | [submodule "3rdparty/eigen"]
23 | path = 3rdparty/eigen
24 | url = https://github.com/eigenteam/eigen-git-mirror.git
25 |
--------------------------------------------------------------------------------
/.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 | - $T2
58 |
59 | - width_
60 | - height_
61 |
62 | - ($T1*)(&data_[0])
63 |
64 | - row_stride
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty # Use 'trusty' unless specified otherwise
2 | os: linux # Use Linux unless specified otherwise
3 | sudo: false
4 |
5 | language: cpp
6 |
7 | matrix:
8 | fast_finish: true # Don't wait for the builds that are allowed to fail
9 | include:
10 | - env: C_COMPILER=gcc-5 CXX_COMPILER=g++-5 GENERATOR_NAME="Unix Makefiles"
11 | addons: { apt: { packages: ["g++-5"], sources: ["ubuntu-toolchain-r-test"] } }
12 |
13 | - env: C_COMPILER=gcc-6 CXX_COMPILER=g++-6 GENERATOR_NAME="Unix Makefiles"
14 | addons: { apt: { packages: ["g++-6"], sources: ["ubuntu-toolchain-r-test"] } }
15 |
16 | - env: C_COMPILER=gcc-7 CXX_COMPILER=g++-7 GENERATOR_NAME="Unix Makefiles"
17 | addons: { apt: { packages: ["g++-7"], sources: ["ubuntu-toolchain-r-test"] } }
18 |
19 | - env: C_COMPILER=gcc-8 CXX_COMPILER=g++-8 GENERATOR_NAME="Unix Makefiles"
20 | addons: { apt: { packages: ["g++-8"], sources: ["ubuntu-toolchain-r-test"] } }
21 |
22 | - env: C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 GENERATOR_NAME="Unix Makefiles"
23 | addons: { apt: { packages: ["g++-7", "clang-5.0"], sources: ["llvm-toolchain-trusty-5.0", "ubuntu-toolchain-r-test"] } }
24 | # g++-7 is needed here - otherwise g++-4.8 libstdc++ is used.
25 |
26 | - env: C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 GENERATOR_NAME="Unix Makefiles"
27 | addons: { apt: { packages: ["g++-8", "clang-5.0"], sources: ["llvm-toolchain-trusty-5.0", "ubuntu-toolchain-r-test"] } }
28 |
29 | - env: C_COMPILER=clang-6.0 CXX_COMPILER=clang++-6.0 GENERATOR_NAME="Unix Makefiles"
30 | addons: { apt: { packages: ["g++-8", "clang-6.0"], sources: ["llvm-toolchain-trusty-6.0", "ubuntu-toolchain-r-test"] } }
31 |
32 | - os: osx
33 | env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
34 | osx_image: xcode9.2
35 |
36 | - os: osx
37 | env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
38 | osx_image: xcode9.3
39 |
40 | - os: osx
41 | env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
42 | osx_image: xcode9.4
43 |
44 | - os: osx
45 | env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
46 | osx_image: xcode10
47 |
48 | allow_failures:
49 | # These are additional combinations that we test but allow to fail, as they're either older compilers (g++-5) or
50 | # uncommon combinations that would need some workarounds or updates to work.
51 | - env: C_COMPILER=gcc-5 CXX_COMPILER=g++-5 GENERATOR_NAME="Unix Makefiles"
52 | - env: C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 GENERATOR_NAME="Unix Makefiles"
53 | - env: C_COMPILER=clang-6.0 CXX_COMPILER=clang++-6.0 GENERATOR_NAME="Unix Makefiles"
54 | - env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
55 | osx_image: xcode9.2
56 | - env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
57 | osx_image: xcode9.3
58 | - env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
59 | osx_image: xcode9.4
60 | - env: C_COMPILER=clang CXX_COMPILER=clang++ GENERATOR_NAME="Xcode"
61 | osx_image: xcode10
62 |
63 | install:
64 | - |
65 | if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
66 | wget https://cmake.org/files/v3.8/cmake-3.8.2-Linux-x86_64.tar.gz
67 | CMAKE_DIR=${TRAVIS_BUILD_DIR}/cmake && mkdir ${CMAKE_DIR}
68 | tar -xvzf cmake-3.8.2-Linux-x86_64.tar.gz --strip-components=1 -C ${CMAKE_DIR} # extract from inside the top-level directory to CMAKE_DIR
69 | export PATH=${CMAKE_DIR}/bin:${PATH}
70 | pyenv global system 3.6 # Workaround for travis-ci/issues/8363
71 | sudo apt-get update -q
72 | sudo apt-get install libboost-all-dev libopencv-dev -y
73 | else
74 | brew install cmake || brew upgrade cmake
75 | brew install boost
76 | /usr/bin/yes | pip2 uninstall numpy # see: travis-ci/issues/6688
77 | brew install opencv
78 | fi
79 | - cmake --version
80 |
81 | before_script:
82 | - mkdir build
83 | - cd build
84 | - cmake -G "${GENERATOR_NAME}" -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${CXX_COMPILER} -DCMAKE_INSTALL_PREFIX=../install -DEOS_BUILD_EXAMPLES=on -DEOS_BUILD_UTILS=on -DEOS_GENERATE_PYTHON_BINDINGS=on -DPYTHON_EXECUTABLE=`which python3.6` ..
85 |
86 | script:
87 | - cmake --build . --config Release
88 | - cmake --build . --target install --config Release
89 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md LICENSE
2 | global-include CMakeLists.txt *.cmake
3 | exclude initial_cache.cmake
4 | recursive-include 3rdparty/cereal/include *
5 | recursive-include 3rdparty/eigen/Eigen *
6 | recursive-include 3rdparty/eigen3-nnls/src *.h
7 | recursive-include 3rdparty/glm/glm *
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 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | home: C:\projects
3 | cmake: C:\projects\cmake-3.10.1-win64-x64\bin\cmake.exe
4 |
5 | version: '{branch}-{build}'
6 |
7 | os: Visual Studio 2017
8 |
9 | install:
10 | # Get all submodules, since AppVeyor doesn't clone with '--recursive':
11 | - cmd: git submodule update --init --recursive # inside the repository directory
12 | # Get a recent CMake and our dependencies (Eigen, OpenCV, Boost):
13 | - cmd: cd %home%
14 | - ps: wget https://cmake.org/files/v3.10/cmake-3.10.1-win64-x64.zip -OutFile cmake.zip
15 | - cmd: 7z x cmake.zip -o"C:\projects" -y > nul # will extract to cmake-3.10.1-win64-x64\
16 | - cmd: '%cmake% --version'
17 | # Get OpenCV (my pre-built binaries of 3.3.0 for VS2017.3):
18 | - ps: wget "https://drive.google.com/uc?export=download&id=1SZpZ4AnEeTKMqFE-rHWuO6XyrzbaiSIx" -OutFile opencv-3.4.0_vc19.12.25834_release.rar
19 | - cmd: mkdir C:\projects\opencv
20 | - cmd: 7z x opencv-3.4.0_vc19.12.25834_release.rar -o"C:\projects\opencv" -y > nul
21 | # Using Boost 1.64.0 from AppVeyor (C:\Libraries\boost_1_64_0)
22 |
23 | before_build: # We're still in %home%
24 | - cmd: mkdir build
25 | - cmd: cd build
26 | - cmd: '%cmake% -G "Visual Studio 15 Win64" -DOpenCV_DIR=C:\projects\opencv -DBOOST_ROOT=C:\Libraries\boost_1_64_0 -DBOOST_LIBRARYDIR=C:\Libraries\boost_1_64_0\lib64-msvc-14.0 -DCMAKE_INSTALL_PREFIX=..\install -DEOS_BUILD_EXAMPLES=on -DEOS_BUILD_UTILS=on -DEOS_GENERATE_PYTHON_BINDINGS=on -DPYTHON_EXECUTABLE:path=C:\Python36-x64\python.exe ..\eos'
27 |
28 | build:
29 | project: C:\projects\build\eos.sln
30 |
31 | after_build: # We're still in %home%\build
32 | - cmd: '%cmake% --build . --target INSTALL --config Release'
33 |
34 | configuration:
35 | - Release
36 |
--------------------------------------------------------------------------------
/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 - including imgcodecs for compatibility")
11 | find_package(OpenCV 3 REQUIRED core imgproc imgcodecs)
12 | endif()
13 | # This allows us to compile in RelWithDebInfo. It'll use the Release-version of OpenCV:
14 | set_target_properties(${OpenCV_LIBS} PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE)
15 |
16 | if(MSVC)
17 | # The standard find_package for boost on Win finds the dynamic libs, so for dynamic linking to boost we need to #define:
18 | add_definitions(-DBOOST_ALL_NO_LIB) # Don't use the automatic library linking by boost with VS (#pragma ...). Instead, we specify everything here in cmake.
19 | add_definitions(-DBOOST_ALL_DYN_LINK) # Link against the dynamic boost lib - needs to match with the version that find_package finds.
20 | add_definitions(-D_HAS_AUTO_PTR_ETC) # Boost 1.65.1 still does not work with VS C++17 mode, this is the workaround
21 | endif()
22 | find_package(Boost 1.50.0 COMPONENTS system filesystem program_options REQUIRED)
23 | message(STATUS "Boost found at ${Boost_INCLUDE_DIRS}")
24 |
25 | # Simple model fitting (orthographic camera & shape to landmarks) example:
26 | add_executable(fit-model-simple fit-model-simple.cpp)
27 | target_link_libraries(fit-model-simple eos ${OpenCV_LIBS} ${Boost_LIBRARIES})
28 | target_link_libraries(fit-model-simple "$<$:-pthread>$<$:-pthreads>")
29 | target_include_directories(fit-model-simple PUBLIC ${OpenCV_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
30 |
31 | # Model fitting example that fits orthographic camera, shape, blendshapes, and contours:
32 | add_executable(fit-model fit-model.cpp)
33 | target_link_libraries(fit-model eos ${OpenCV_LIBS} ${Boost_LIBRARIES})
34 | target_link_libraries(fit-model "$<$:-pthread>$<$:-pthreads>")
35 | target_include_directories(fit-model PUBLIC ${OpenCV_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
36 |
37 | # Model fitting example that fits orthographic camera, shape, blendshapes, and contours to multiple images:
38 | add_executable(fit-model-multi fit-model-multi.cpp)
39 | target_link_libraries(fit-model-multi eos ${OpenCV_LIBS} ${Boost_LIBRARIES})
40 | target_link_libraries(fit-model-multi "$<$:-pthread>$<$:-pthreads>")
41 | target_include_directories(fit-model-multi PUBLIC ${OpenCV_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
42 |
43 | # Generate random samples from the model:
44 | add_executable(generate-obj generate-obj.cpp)
45 | target_link_libraries(generate-obj eos ${OpenCV_LIBS} ${Boost_LIBRARIES})
46 | target_include_directories(generate-obj PUBLIC ${OpenCV_INCLUDE_DIRS} ${Boost_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 | message(STATUS "Ceres locations: Headers: ${CERES_INCLUDE_DIRS} Library: ${CERES_LIBRARIES}")
60 |
61 | # Single and multi-image non-linear model fitting with Ceres example:
62 | add_executable(fit-model-ceres fit-model-ceres.cpp)
63 | target_link_libraries(fit-model-ceres eos ${CERES_LIBRARIES} ${OpenCV_LIBS} ${Boost_LIBRARIES})
64 | target_include_directories(fit-model-ceres PUBLIC ${CERES_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
65 | install(TARGETS fit-model-ceres DESTINATION bin)
66 | endif()
67 |
--------------------------------------------------------------------------------
/examples/data/image_0010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CVSSP/eos/f8b235bf8da1eef947a62dc09e23ecde2c9c0f3b/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 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/morphablemodel/MorphableModel.hpp"
23 | #include "eos/render/render.hpp"
24 | #include "eos/cpp17/optional.hpp"
25 |
26 | #include "glm/gtc/matrix_transform.hpp"
27 |
28 | #include "boost/filesystem.hpp"
29 | #include "boost/program_options.hpp"
30 |
31 | #include "opencv2/core/core.hpp"
32 | #include "opencv2/highgui/highgui.hpp"
33 |
34 | #include
35 |
36 | using namespace eos;
37 | namespace po = boost::program_options;
38 | namespace fs = boost::filesystem;
39 | using std::cout;
40 | using std::endl;
41 | using std::vector;
42 | using std::string;
43 |
44 | /**
45 | * This app generates random samples from the model and stores them as obj file
46 | * as well as outputs a frontal rendering of the sample.
47 | *
48 | * A list of shape and/or colour coefficients can be specified. Any coefficient
49 | * not specified will be set to zero.
50 | */
51 | int main(int argc, char* argv[])
52 | {
53 | string model_file, output_file;
54 | vector shape_coefficients, color_coefficients;
55 |
56 | try
57 | {
58 | po::options_description desc("Allowed options");
59 | // clang-format off
60 | desc.add_options()
61 | ("help", "produce help message")
62 | ("model", po::value(&model_file)->required(), "an eos .bin Morphable Model file")
63 | ("shape-coeffs", po::value>(&shape_coefficients)->multitoken(),
64 | "optional parameter list of shape coefficients. All not specified will be set to zero. E.g.: "
65 | "'--shape-coeffs 0.0 1.5'. If omitted, the mean is used.")
66 | ("color-coeffs", po::value>(&color_coefficients)->multitoken(),
67 | "optional parameter list of colour coefficients. All not specified will be set to zero. E.g.: "
68 | "'--colour-coeffs 0.0 1.5'. If omitted, the mean is used.")
69 | ("output", po::value(&output_file)->default_value("output.obj"),
70 | "name of the output obj file (including .obj). Can be a full path.");
71 | // clang-format on
72 | po::variables_map vm;
73 | // disabling short options to allow negative values for the coefficients, e.g. '--shape-coeffs 0.0 -1.5'
74 | po::store(
75 | po::parse_command_line(argc, argv, desc,
76 | po::command_line_style::unix_style ^ po::command_line_style::allow_short),
77 | vm);
78 | if (vm.count("help"))
79 | {
80 | cout << "Usage: generate-obj [options]" << endl;
81 | cout << desc;
82 | return EXIT_SUCCESS;
83 | }
84 | po::notify(vm);
85 | } catch (const po::error& e)
86 | {
87 | cout << "Error while parsing command-line arguments: " << e.what() << endl;
88 | cout << "Use --help to display a list of options." << endl;
89 | return EXIT_FAILURE;
90 | }
91 |
92 | morphablemodel::MorphableModel morphable_model = morphablemodel::load_model(model_file);
93 |
94 | if (shape_coefficients.size() < morphable_model.get_shape_model().get_num_principal_components())
95 | {
96 | shape_coefficients.resize(morphable_model.get_shape_model().get_num_principal_components());
97 | }
98 |
99 | if (color_coefficients.size() < morphable_model.get_color_model().get_num_principal_components())
100 | {
101 | color_coefficients.resize(morphable_model.get_color_model().get_num_principal_components());
102 | }
103 |
104 | const core::Mesh sample_mesh = morphable_model.draw_sample(
105 | shape_coefficients, color_coefficients); // if one of the two vectors is empty, it uses get_mean()
106 |
107 | core::write_obj(sample_mesh, output_file);
108 | core::Image4u rendering;
109 | std::tie(rendering, std::ignore) =
110 | render::render(sample_mesh, glm::mat4x4(1.0f), glm::ortho(-130.0f, 130.0f, -130.0f, 130.0f), 512, 512,
111 | cpp17::nullopt, true, false, false);
112 | fs::path filename_rendering(output_file);
113 | filename_rendering.replace_extension(".png");
114 | cv::imwrite(filename_rendering.string(), core::to_mat(rendering));
115 |
116 | cout << "Wrote the generated obj and a rendering to files with basename " << output_file << "." << endl;
117 |
118 | return EXIT_SUCCESS;
119 | }
120 |
--------------------------------------------------------------------------------
/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 |
29 | namespace eos {
30 | namespace core {
31 |
32 | /**
33 | * @brief Representation of a landmark, consisting of a landmark name and
34 | * coordinates of the given type. Usually, the type would be \c Eigen::Vector2f.
35 | */
36 | template
37 | struct Landmark
38 | {
39 | std::string name; ///< Name of the landmark, often used as identifier.
40 | LandmarkType coordinates; ///< The position or coordinates of the landmark.
41 | };
42 |
43 | /**
44 | * @brief A trivial collection of landmarks that belong together.
45 | */
46 | template
47 | using LandmarkCollection = std::vector>;
48 |
49 | /**
50 | * @brief Shorthand for a 2D floating point landmark type.
51 | */
52 | // using Landmark2f = Landmark>;
53 |
54 | /**
55 | * @brief Alias for the 2D landmark point type.
56 | */
57 | // using Point2f = Eigen::Vector2f;
58 | // using Point2f = std::array;
59 |
60 | /**
61 | * @brief Filters the given LandmarkCollection and returns a new LandmarkCollection
62 | * containing all landmarks whose name matches the one given by \p filter.
63 | *
64 | * @param[in] landmarks The input LandmarkCollection to be filtered.
65 | * @param[in] filter A list of landmark names (identifiers) by which the given LandmarkCollection is filtered.
66 | * @return A new, filtered LandmarkCollection.
67 | */
68 | template
69 | LandmarkCollection filter(const LandmarkCollection& landmarks, const std::vector& filter)
70 | {
71 | LandmarkCollection filtered_landmarks;
72 | using std::begin;
73 | using std::end;
74 | std::copy_if(
75 | begin(landmarks), end(landmarks), std::back_inserter(filtered_landmarks),
76 | [&](const Landmark& lm) { return std::find(begin(filter), end(filter), lm.name) != end(filter); });
77 | return filtered_landmarks;
78 | };
79 |
80 | } /* namespace core */
81 | } /* namespace eos */
82 |
83 | #endif /* EOS_LANDMARK_HPP */
84 |
--------------------------------------------------------------------------------
/include/eos/core/LandmarkMapper.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 | *
4 | * File: include/eos/core/LandmarkMapper.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 LANDMARKMAPPER_HPP_
23 | #define LANDMARKMAPPER_HPP_
24 |
25 | #include "eos/cpp17/optional.hpp"
26 |
27 | #include "toml.hpp"
28 |
29 | #include
30 | #include
31 | #include
32 |
33 | namespace eos {
34 | namespace core {
35 |
36 | /**
37 | * @brief Represents a mapping from one kind of landmarks
38 | * to a different format (e.g. model vertices).
39 | *
40 | * When fitting the 3D model to an image, a correspondence must
41 | * be known from the 2D image landmarks to 3D vertex points in
42 | * the Morphable Model. The 3D model defines all its points in
43 | * the form of vertex ids.
44 | * These mappings are stored in a file, see the \c share/ folder for
45 | * an example for mapping 2D ibug landmarks to 3D model vertex indices.
46 | *
47 | * The LandmarkMapper thus has two main use cases:
48 | * - Mapping 2D landmark points to 3D vertices
49 | * - Converting one set of 2D landmarks into another set of 2D
50 | * landmarks with different identifiers.
51 | */
52 | class LandmarkMapper
53 | {
54 | public:
55 | /**
56 | * @brief Constructs a new landmark mapper that performs an identity
57 | * mapping, that is, its output is the same as the input.
58 | */
59 | LandmarkMapper() = default;
60 |
61 | /**
62 | * @brief Constructs a new landmark mapper from a file containing
63 | * mappings from one set of landmark identifiers to another.
64 | *
65 | * In case the file contains no mappings, a landmark mapper
66 | * that performs an identity mapping is constructed.
67 | *
68 | * @param[in] filename A file with landmark mappings.
69 | * @throws runtime_error or toml::exception if there is an error loading the mappings from the file.
70 | */
71 | LandmarkMapper(std::string filename)
72 | {
73 | // parse() as well as extracting the data can throw std::runtime error or toml::exception,
74 | // so ideally you'd want to call this c'tor within a try-catch.
75 | const auto data = toml::parse(filename);
76 |
77 | const auto mappings_table = toml::get(data.at("landmark_mappings"));
78 | // The key in the config is always a string. The value however may be a string or an integer, so we
79 | // check for that and convert to a string.
80 | for (const auto& mapping : mappings_table)
81 | {
82 | std::string f = mapping.first;
83 | std::string value;
84 | switch (mapping.second.type())
85 | {
86 | case toml::value_t::Integer:
87 | value = std::to_string(toml::get(mapping.second));
88 | break;
89 | case toml::value_t::String:
90 | value = toml::get(mapping.second);
91 | break;
92 | default:
93 | throw std::runtime_error("unexpected type : " + toml::stringize(mapping.second.type()));
94 | }
95 |
96 | landmark_mappings.emplace(mapping.first, value);
97 | }
98 | };
99 |
100 | /**
101 | * @brief Constructs a new landmark mapper from a set of existing mappings.
102 | *
103 | * @param[in] mappings A set of landmark mappings.
104 | */
105 | LandmarkMapper(const std::unordered_map& mappings) : landmark_mappings(mappings)
106 | {};
107 |
108 | /**
109 | * @brief Converts the given landmark name to the mapped name.
110 | *
111 | * In the case the mapper is empty (no mappings defined at all), the mapper will perform
112 | * an identity mapping and return the \p landmark_name that was input.
113 | *
114 | * @param[in] landmark_name A landmark name to convert.
115 | * @return The mapped landmark name if a mapping exists, an empty optional otherwise.
116 | */
117 | cpp17::optional convert(std::string landmark_name) const
118 | {
119 | if (landmark_mappings.empty())
120 | {
121 | // perform identity mapping, i.e. return the input
122 | return landmark_name;
123 | }
124 |
125 | const auto& converted_landmark = landmark_mappings.find(landmark_name);
126 | if (converted_landmark != std::end(landmark_mappings))
127 | {
128 | // landmark mapping found, return it
129 | return converted_landmark->second;
130 | } else
131 | { // landmark_name does not match the key of any element in the map
132 | return cpp17::nullopt;
133 | }
134 | };
135 |
136 | /**
137 | * @brief Returns the number of loaded landmark mappings.
138 | *
139 | * @return The number of landmark mappings.
140 | */
141 | auto num_mappings() const {
142 | return landmark_mappings.size();
143 | };
144 |
145 | /**
146 | * @brief Returns the mappings held by this mapper.
147 | *
148 | * @return All mappings contained in the mapper.
149 | */
150 | const auto& get_mappings() const {
151 | return landmark_mappings;
152 | };
153 |
154 | private:
155 | std::unordered_map landmark_mappings; ///< Mapping from one
156 | ///< landmark name to a name
157 | ///< in a different format.
158 | };
159 |
160 | } /* namespace core */
161 | } /* namespace eos */
162 |
163 | #endif /* LANDMARKMAPPER_HPP_ */
164 |
--------------------------------------------------------------------------------
/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 | #include
30 | #include
31 | #include
32 |
33 | namespace eos {
34 | namespace core {
35 |
36 | /**
37 | * @brief This class represents a 3D mesh consisting of vertices, vertex colour
38 | * information and texture coordinates.
39 | *
40 | * Additionally it stores the indices that specify which vertices
41 | * to use to generate the triangle mesh out of the vertices.
42 | */
43 | struct Mesh
44 | {
45 | std::vector vertices; ///< 3D vertex positions.
46 | std::vector colors; ///< Colour information for each vertex. Expected to be in RGB order.
47 | std::vector texcoords; ///< Texture coordinates for each vertex.
48 |
49 | std::vector> tvi; ///< Triangle vertex indices
50 | std::vector> tci; ///< Triangle color indices
51 | std::vector> tti; ///< Triangle texture indices
52 | };
53 |
54 | /**
55 | * @brief Writes the given Mesh to an obj file that for example can be read by MeshLab.
56 | *
57 | * If the mesh contains vertex colour information, it will be written to the obj as well.
58 | *
59 | * @param[in] mesh The mesh to save as obj.
60 | * @param[in] filename Output filename (including ".obj").
61 | */
62 | inline void write_obj(Mesh mesh, std::string filename)
63 | {
64 | assert(mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty());
65 |
66 | std::ofstream obj_file(filename);
67 |
68 | if (mesh.colors.empty())
69 | {
70 | for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
71 | {
72 | obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
73 | << mesh.vertices[i][2] << std::endl;
74 | }
75 | } else
76 | {
77 | for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
78 | {
79 | obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
80 | << mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
81 | << mesh.colors[i][2] << std::endl;
82 | }
83 | }
84 |
85 | if (!mesh.texcoords.empty())
86 | {
87 | for (auto&& tc : mesh.texcoords)
88 | {
89 | obj_file << "vt " << tc[0] << " " << tc[1] << std::endl;
90 | }
91 | }
92 |
93 | for (auto&& v : mesh.tvi)
94 | {
95 | // Add one because obj starts counting triangle indices at 1
96 | obj_file << "f " << v[0] + 1 << " " << v[1] + 1 << " " << v[2] + 1 << std::endl;
97 | }
98 |
99 | return;
100 | }
101 |
102 | /**
103 | * @brief Writes an obj file of the given Mesh, including texture coordinates,
104 | * and an mtl file containing a reference to the isomap.
105 | *
106 | * The obj will contain texture coordinates for the mesh, and the
107 | * mtl file will link to a file named .isomap.png.
108 | * Note that the texture (isomap) has to be saved separately.
109 | *
110 | * @param[in] mesh The mesh to save as obj.
111 | * @param[in] filename Output filename, including .obj.
112 | */
113 | inline void write_textured_obj(Mesh mesh, std::string filename)
114 | {
115 | assert((mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty()) && !mesh.texcoords.empty());
116 |
117 | if (filename.at(filename.size() - 4) != '.')
118 | {
119 | throw std::runtime_error(
120 | "Error in given filename: Expected a dot and a 3-letter extension at the end (i.e. '.obj'). " +
121 | filename);
122 | }
123 |
124 | // Takes a full path to a file and returns only the filename:
125 | const auto get_filename = [](const std::string& path) {
126 | auto last_slash = path.find_last_of("/\\");
127 | if (last_slash == std::string::npos)
128 | {
129 | return path;
130 | }
131 | return path.substr(last_slash + 1, path.size());
132 | };
133 |
134 | std::ofstream obj_file(filename);
135 |
136 | std::string mtl_filename(filename);
137 | // replace '.obj' at the end with '.mtl':
138 | mtl_filename.replace(std::end(mtl_filename) - 4, std::end(mtl_filename), ".mtl");
139 |
140 | obj_file << "mtllib " << get_filename(mtl_filename) << std::endl; // first line of the obj file
141 |
142 | // same as in write_obj():
143 | if (mesh.colors.empty())
144 | {
145 | for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
146 | {
147 | obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
148 | << mesh.vertices[i][2] << " " << std::endl;
149 | }
150 | } else
151 | {
152 | for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
153 | {
154 | obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
155 | << mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
156 | << mesh.colors[i][2] << " " << std::endl;
157 | }
158 | }
159 | // end
160 |
161 | for (std::size_t i = 0; i < mesh.texcoords.size(); ++i)
162 | {
163 | obj_file << "vt " << mesh.texcoords[i][0] << " " << 1.0f - mesh.texcoords[i][1] << std::endl;
164 | // We invert y because Meshlab's uv origin (0, 0) is on the bottom-left
165 | }
166 |
167 | obj_file << "usemtl FaceTexture" << std::endl; // the name of our texture (material) will be 'FaceTexture'
168 |
169 | for (auto&& v : mesh.tvi)
170 | {
171 | // This assumes mesh.texcoords.size() == mesh.vertices.size(). The texture indices could theoretically be different (for example in the cube-mapped 3D scan).
172 | // Add one because obj starts counting triangle indices at 1
173 | obj_file << "f " << v[0] + 1 << "/" << v[0] + 1 << " " << v[1] + 1 << "/" << v[1] + 1 << " "
174 | << v[2] + 1 << "/" << v[2] + 1 << std::endl;
175 | }
176 |
177 | std::ofstream mtl_file(mtl_filename);
178 | std::string texture_filename(filename);
179 | // replace '.obj' at the end with '.isomap.png':
180 | texture_filename.replace(std::end(texture_filename) - 4, std::end(texture_filename), ".isomap.png");
181 |
182 | mtl_file << "newmtl FaceTexture" << std::endl;
183 | mtl_file << "map_Kd " << get_filename(texture_filename) << std::endl;
184 |
185 | return;
186 | };
187 |
188 | } /* namespace core */
189 | } /* namespace eos */
190 |
191 | #endif /* EOS_MESH_HPP_ */
192 |
--------------------------------------------------------------------------------
/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 | inline Image3u from_mat(const cv::Mat& image)
95 | {
96 | if (image.type() != CV_8UC3)
97 | {
98 | throw std::runtime_error("Can only convert a CV_8UC3 cv::Mat to an eos::core::Image3u.");
99 | }
100 |
101 | Image3u converted(image.rows, image.cols);
102 | for (int r = 0; r < image.rows; ++r)
103 | {
104 | for (int c = 0; c < image.cols; ++c)
105 | {
106 | converted(r, c) = {image.at(r, c)[0], image.at(r, c)[1],
107 | image.at(r, c)[2]};
108 | }
109 | }
110 | return converted;
111 | };
112 |
113 | inline Image4u from_mat_with_alpha(const cv::Mat& image)
114 | {
115 | if (image.type() != CV_8UC4)
116 | {
117 | throw std::runtime_error("Can only convert a CV_8UC4 cv::Mat to an eos::core::Image4u.");
118 | }
119 |
120 | Image4u converted(image.rows, image.cols);
121 | for (int r = 0; r < image.rows; ++r)
122 | {
123 | for (int c = 0; c < image.cols; ++c)
124 | {
125 | converted(r, c) = {image.at(r, c)[0], image.at(r, c)[1],
126 | image.at(r, c)[2], image.at(r, c)[3]};
127 | }
128 | }
129 | return converted;
130 | };
131 |
132 | } /* namespace core */
133 | } /* namespace eos */
134 |
135 | #endif /* EOS_IMAGE_OPENCV_INTEROP_HPP_ */
136 |
--------------------------------------------------------------------------------
/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/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 READ_PTS_LANDMARKS_HPP_
23 | #define 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 /* 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 FITTINGRESULT_HPP_
23 | #define 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 /* FITTINGRESULT_HPP_ */
49 |
--------------------------------------------------------------------------------
/include/eos/fitting/closest_edge_fitting.hpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CVSSP/eos/f8b235bf8da1eef947a62dc09e23ecde2c9c0f3b/include/eos/fitting/closest_edge_fitting.hpp
--------------------------------------------------------------------------------
/include/eos/fitting/contour_correspondence.hpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CVSSP/eos/f8b235bf8da1eef947a62dc09e23ecde2c9c0f3b/include/eos/fitting/contour_correspondence.hpp
--------------------------------------------------------------------------------
/include/eos/fitting/detail/glm_cerealisation.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 | *
4 | * File: include/eos/fitting/detail/glm_cerealisation.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 GLMCEREALISATION_HPP_
23 | #define GLMCEREALISATION_HPP_
24 |
25 | #include "cereal/cereal.hpp"
26 |
27 | #include "glm/gtc/quaternion.hpp"
28 |
29 | /**
30 | * @brief Serialisation of GLM \c glm::quat quaternion for the serialisation
31 | * library cereal (http://uscilab.github.io/cereal/index.html).
32 | *
33 | * Contains serialisation for \c glm::quat.
34 | */
35 | namespace glm {
36 |
37 | /**
38 | * @brief Serialisation of a glm::quat using cereal.
39 | *
40 | * @param[in] ar The archive to (de)serialise.
41 | * @param[in] vec The vector to (de)serialise.
42 | */
43 | template
44 | void serialize(Archive& ar, glm::quat& q)
45 | {
46 | ar(q.w, q.x, q.y, q.z);
47 | };
48 |
49 | } /* namespace glm */
50 |
51 | #endif /* GLMCEREALISATION_HPP_ */
52 |
--------------------------------------------------------------------------------
/include/eos/fitting/detail/nonlinear_camera_estimation_detail.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 | *
4 | * File: include/eos/fitting/detail/nonlinear_camera_estimation_detail.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 NONLINEARCAMERAESTIMATION_DETAIL_HPP_
23 | #define NONLINEARCAMERAESTIMATION_DETAIL_HPP_
24 |
25 | #include "glm/gtc/matrix_transform.hpp"
26 |
27 | #include "Eigen/Geometry"
28 |
29 | #include "opencv2/core/core.hpp"
30 |
31 | #include
32 |
33 | namespace eos {
34 | namespace fitting {
35 | namespace detail {
36 |
37 | // ret: 3rd entry is the z
38 | // radians
39 | // expects the landmark points to be in opencv convention, i.e. origin TL
40 | inline glm::vec3 project_ortho(glm::vec3 point, float rot_x_pitch, float rot_y_yaw, float rot_z_roll,
41 | float tx, float ty, float frustum_scale, /* fixed params now: */ int width,
42 | int height)
43 | {
44 | // We could use quaternions in here, to be independent of the RPY... etc convention.
45 | // Then, the user can decompose the quaternion as he wishes to. But then we'd have to estimate 4
46 | // parameters?
47 | // This can of course be optimised, but we keep it this way while we're debugging and as long as
48 | // it's not a performance issue.
49 | auto rot_mtx_x = glm::rotate(glm::mat4(1.0f), rot_x_pitch, glm::vec3{1.0f, 0.0f, 0.0f});
50 | auto rot_mtx_y = glm::rotate(glm::mat4(1.0f), rot_y_yaw, glm::vec3{0.0f, 1.0f, 0.0f});
51 | auto rot_mtx_z = glm::rotate(glm::mat4(1.0f), rot_z_roll, glm::vec3{0.0f, 0.0f, 1.0f});
52 | auto t_mtx = glm::translate(glm::mat4(1.0f),
53 | glm::vec3{tx, ty, 0.0f}); // glm: Col-major memory layout. [] gives the column
54 |
55 | // Note/Todo: Is this the full ortho? n/f missing? or do we need to multiply it with Proj...? See Shirley CG!
56 | // glm::frustum()?
57 | const float aspect = static_cast(width) / height;
58 | auto ortho_mtx = glm::ortho(-1.0f * aspect * frustum_scale, 1.0f * aspect * frustum_scale,
59 | -1.0f * frustum_scale, 1.0f * frustum_scale);
60 |
61 | glm::vec4 viewport(0, height, width, -height); // flips y, origin top-left, like in OpenCV
62 | // P = RPY * P
63 | glm::vec3 res = glm::project(point, t_mtx * rot_mtx_z * rot_mtx_x * rot_mtx_y, ortho_mtx, viewport);
64 | return res;
65 | };
66 |
67 | /**
68 | * @brief Generic functor for Eigen's optimisation algorithms.
69 | */
70 | template
71 | struct Functor
72 | {
73 | typedef _Scalar Scalar;
74 | enum { InputsAtCompileTime = NX, ValuesAtCompileTime = NY };
75 | typedef Eigen::Matrix InputType;
76 | typedef Eigen::Matrix ValueType;
77 | typedef Eigen::Matrix JacobianType;
78 |
79 | const int m_inputs, m_values;
80 |
81 | Functor() : m_inputs(InputsAtCompileTime), m_values(ValuesAtCompileTime) {}
82 | Functor(int inputs, int values) : m_inputs(inputs), m_values(values) {}
83 |
84 | int inputs() const { return m_inputs; }
85 | int values() const { return m_values; }
86 | };
87 |
88 | /**
89 | * @brief LevenbergMarquardt cost function for the orthographic camera estimation.
90 | */
91 | struct OrthographicParameterProjection : Functor
92 | {
93 | public:
94 | // Creates a new OrthographicParameterProjection object with given data.
95 | OrthographicParameterProjection(std::vector image_points, std::vector model_points,
96 | int width, int height)
97 | : Functor(6, image_points.size()), image_points(image_points), model_points(model_points),
98 | width(width), height(height){};
99 |
100 | // x = current params, fvec = the errors/differences of the proj with current params and the GT
101 | // (image_points)
102 | int operator()(const Eigen::VectorXd& x, Eigen::VectorXd& fvec) const
103 | {
104 | const float aspect = static_cast(width) / height;
105 | for (int i = 0; i < values(); i++)
106 | {
107 | // opencv to glm:
108 | glm::vec3 point_3d(model_points[i][0], model_points[i][1], model_points[i][2]);
109 | // projection given current params x:
110 | glm::vec3 proj_with_current_param_esti =
111 | project_ortho(point_3d, x[0], x[1], x[2], x[3], x[4], x[5], width, height);
112 | cv::Vec2f proj_point_2d(proj_with_current_param_esti.x, proj_with_current_param_esti.y);
113 | // diff of current proj to ground truth, our error
114 | auto diff = cv::norm(proj_point_2d, image_points[i]);
115 | // fvec should contain the differences
116 | // don't square it.
117 | fvec[i] = diff;
118 | }
119 | return 0;
120 | };
121 |
122 | private:
123 | std::vector image_points;
124 | std::vector model_points;
125 | int width;
126 | int height;
127 | };
128 |
129 | } /* namespace detail */
130 | } /* namespace fitting */
131 | } /* namespace eos */
132 |
133 | #endif /* NONLINEARCAMERAESTIMATION_DETAIL_HPP_ */
134 |
--------------------------------------------------------------------------------
/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 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 NONLINEARCAMERAESTIMATION_HPP_
23 | #define NONLINEARCAMERAESTIMATION_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 "opencv2/core/core.hpp"
32 |
33 | #include
34 | #include
35 |
36 | namespace eos {
37 | namespace fitting {
38 |
39 | /**
40 | * @brief This algorithm estimates the rotation angles and translation of the model, as
41 | * well as the viewing frustum of the camera, given a set of corresponding 2D-3D points.
42 | *
43 | * It assumes an orthographic camera and estimates 6 parameters,
44 | * [r_x, r_y, r_z, t_x, t_y, frustum_scale], where the first five describe how to transform
45 | * the model, and the last one describes the cameras viewing frustum (see CameraParameters).
46 | * This 2D-3D correspondence problem is solved using Eigen's LevenbergMarquardt algorithm.
47 | *
48 | * The method is slightly inspired by "Computer Vision: Models Learning and Inference",
49 | * Simon J.D. Prince, 2012, but different in a lot of respects.
50 | *
51 | * Eigen's LM implementation requires at least 6 data points, so we require >= 6 corresponding points.
52 | *
53 | * Notes/improvements:
54 | * The algorithm works reliable as it is, however, it could be improved with the following:
55 | * - A better initial guess (see e.g. Prince)
56 | * - We could add a parameter to pass an initial guess
57 | * - Using the analytic derivatives instead of Eigen::NumericalDiff - they're easy to calculate.
58 | *
59 | * @param[in] image_points A list of 2D image points.
60 | * @param[in] model_points Corresponding points of a 3D model.
61 | * @param[in] width Width of the image (or viewport).
62 | * @param[in] height Height of the image (or viewport).
63 | * @return The estimated model and camera parameters.
64 | */
65 | inline RenderingParameters estimate_orthographic_camera(std::vector image_points,
66 | std::vector model_points, int width,
67 | int height)
68 | {
69 | using cv::Mat;
70 | assert(image_points.size() == model_points.size());
71 | assert(image_points.size() >= 6); // Number of correspondence points given needs to be equal to or larger than 6
72 |
73 | const float aspect = static_cast(width) / height;
74 |
75 | // Set up the initial parameter vector and the cost function:
76 | int num_params = 6;
77 | Eigen::VectorXd parameters; // [rot_x_pitch, rot_y_yaw, rot_z_roll, t_x, t_y, frustum_scale]
78 | parameters.setConstant(num_params, 0.0); // Set all 6 values to zero (except frustum_scale, see next line)
79 | parameters[5] = 110.0; // This is just a rough hand-chosen scaling estimate - we could do a lot better. But it works.
80 | detail::OrthographicParameterProjection cost_function(image_points, model_points, width, height);
81 |
82 | // Note: we have analytical derivatives, so we should use them!
83 | Eigen::NumericalDiff cost_function_with_derivative(cost_function,
84 | 0.0001);
85 | // I had to change the default value of epsfcn, it works well around 0.0001. It couldn't produce the
86 | // derivative with the default, I guess the changes in the gradient were too small.
87 |
88 | Eigen::LevenbergMarquardt> lm(
89 | cost_function_with_derivative);
90 | auto info = lm.minimize(parameters); // we could or should use the return value
91 | // 'parameters' contains the solution now.
92 |
93 | Frustum camera_frustum{
94 | -1.0f * aspect * static_cast(parameters[5]), 1.0f * aspect * static_cast(parameters[5]),
95 | -1.0f * static_cast(parameters[5]), 1.0f * static_cast(parameters[5])};
96 | return RenderingParameters(CameraType::Orthographic, camera_frustum, static_cast(parameters[0]),
97 | static_cast(parameters[1]), static_cast(parameters[2]),
98 | static_cast(parameters[3]), static_cast(parameters[4]), width,
99 | height);
100 | };
101 |
102 | } /* namespace fitting */
103 | } /* namespace eos */
104 |
105 | #endif /* NONLINEARCAMERAESTIMATION_HPP_ */
106 |
--------------------------------------------------------------------------------
/include/eos/fitting/orthographic_camera_estimation_linear.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 | *
4 | * File: include/eos/fitting/orthographic_camera_estimation_linear.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 ORTHOGRAPHICCAMERAESTIMATIONLINEAR_HPP_
23 | #define ORTHOGRAPHICCAMERAESTIMATIONLINEAR_HPP_
24 |
25 | #include "eos/cpp17/optional.hpp"
26 |
27 | #include "glm/mat3x3.hpp"
28 |
29 | #include "Eigen/Core"
30 | #include "Eigen/Dense"
31 | #include "Eigen/SVD"
32 | #include "Eigen/Dense"
33 |
34 | #include
35 | #include
36 |
37 | namespace eos {
38 | namespace fitting {
39 |
40 | /**
41 | * Parameters of an estimated scaled orthographic projection.
42 | */
43 | struct ScaledOrthoProjectionParameters
44 | {
45 | glm::mat3x3 R; ///< 3x3 rotation matrix
46 | float tx, ty; ///< x and y translation
47 | float s; ///< Scaling
48 | };
49 |
50 | /**
51 | * Estimates the parameters of a scaled orthographic projection.
52 | *
53 | * Given a set of 2D-3D correspondences, this algorithm estimates rotation,
54 | * translation (in x and y) and a scaling parameters of the scaled orthographic
55 | * projection model using a closed-form solution. It does so by first computing
56 | * an affine camera matrix using algorithm [1], and then finds the closest
57 | * orthonormal matrix to the estimated affine transform using SVD.
58 | * This algorithm follows the original implementation [2] of William Smith,
59 | * University of York.
60 | *
61 | * Requires >= 4 corresponding points.
62 | *
63 | * [1]: Gold Standard Algorithm for estimating an affine camera matrix from
64 | * world to image correspondences, Algorithm 7.2 in Multiple View Geometry,
65 | * Hartley & Zisserman, 2nd Edition, 2003.
66 | * [2]: https://github.com/waps101/3DMM_edges/blob/master/utils/POS.m
67 | *
68 | * @param[in] image_points A list of 2D image points.
69 | * @param[in] model_points Corresponding points of a 3D model.
70 | * @param[in] is_viewport_upsidedown Flag to set whether the viewport of the image points is upside-down (e.g. as in OpenCV).
71 | * @param[in] viewport_height Height of the viewport of the image points (needs to be given if is_viewport_upsidedown == true).
72 | * @return Rotation, translation and scaling of the estimated scaled orthographic projection.
73 | */
74 | inline ScaledOrthoProjectionParameters estimate_orthographic_projection_linear(
75 | std::vector image_points, std::vector model_points,
76 | bool is_viewport_upsidedown, cpp17::optional viewport_height = cpp17::nullopt)
77 | {
78 | using Eigen::Matrix;
79 | assert(image_points.size() == model_points.size());
80 | assert(image_points.size() >= 4); // Number of correspondence points given needs to be equal to or larger than 4
81 |
82 | const int num_correspondences = static_cast(image_points.size());
83 |
84 | if (is_viewport_upsidedown)
85 | {
86 | if (viewport_height == cpp17::nullopt)
87 | {
88 | throw std::runtime_error(
89 | "Error: If is_viewport_upsidedown is set to true, viewport_height needs to be given.");
90 | }
91 | for (auto&& ip : image_points)
92 | {
93 | ip[1] = viewport_height.value() - ip[1];
94 | }
95 | }
96 |
97 | Matrix A = Matrix::Zero(2 * num_correspondences, 8);
98 | for (int i = 0; i < model_points.size(); ++i)
99 | {
100 | A.block<1, 4>(2 * i, 0) = model_points[i]; // even row - copy to left side (first row is row 0)
101 | A.block<1, 4>((2 * i) + 1, 4) = model_points[i]; // odd row - copy to right side
102 | } // 4th coord (homogeneous) is already 1
103 |
104 | Matrix b(2 * num_correspondences, 1);
105 | for (int i = 0; i < image_points.size(); ++i)
106 | {
107 | b.segment<2>(2 * i) = image_points[i];
108 | }
109 |
110 | // The original implementation used SVD here to solve the linear system, but
111 | // QR seems to do the job fine too.
112 | const Matrix k = A.colPivHouseholderQr().solve(b); // resulting affine matrix (8x1)
113 |
114 | // Extract all values from the estimated affine parameters k:
115 | const Eigen::Vector3f R1 = k.segment<3>(0);
116 | const Eigen::Vector3f R2 = k.segment<3>(4);
117 | const float sTx = k(3);
118 | const float sTy = k(7);
119 | const auto s = (R1.norm() + R2.norm()) / 2.0f;
120 | Eigen::Matrix3f R;
121 | Eigen::Vector3f r1 = R1.normalized(); // Not sure why R1.normalize() (in-place) produces a compiler error.
122 | Eigen::Vector3f r2 = R2.normalized();
123 | R.block<1, 3>(0, 0) = r1;
124 | R.block<1, 3>(1, 0) = r2;
125 | R.block<1, 3>(2, 0) = r1.cross(r2);
126 |
127 | // Set R to the closest orthonormal matrix to the estimated affine transform:
128 | Eigen::JacobiSVD svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV);
129 | Eigen::Matrix3f U = svd.matrixU();
130 | const Eigen::Matrix3f V = svd.matrixV();
131 | Eigen::Matrix3f R_ortho = U * V.transpose();
132 |
133 | // The determinant of R must be 1 for it to be a valid rotation matrix
134 | if (R_ortho.determinant() < 0)
135 | {
136 | U.block<1, 3>(2, 0) = -U.block<1, 3>(2, 0); // not tested
137 | R_ortho = U * V.transpose();
138 | }
139 |
140 | // Remove the scale from the translations:
141 | const auto t1 = sTx / s;
142 | const auto t2 = sTy / s;
143 |
144 | // Convert to a glm::mat4x4:
145 | glm::mat3x3 R_glm; // identity
146 | for (int r = 0; r < 3; ++r)
147 | {
148 | for (int c = 0; c < 3; ++c)
149 | {
150 | R_glm[c][r] = R_ortho(r, c);
151 | }
152 | }
153 | return ScaledOrthoProjectionParameters{R_glm, t1, t2, s};
154 | };
155 |
156 | } /* namespace fitting */
157 | } /* namespace eos */
158 |
159 | #endif /* ORTHOGRAPHICCAMERAESTIMATIONLINEAR_HPP_ */
160 |
--------------------------------------------------------------------------------
/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 the 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_coefficients The coefficients used to generate the expression sample.
52 | * @return A model instance with given coefficients.
53 | */
54 | inline Eigen::VectorXf draw_sample(const ExpressionModel& expression_model,
55 | std::vector expression_coefficients)
56 | {
57 | Eigen::VectorXf expression_sample;
58 | // Get a sample of the expression model, depending on whether it's a PcaModel or Blendshapes:
59 | if (cpp17::holds_alternative(expression_model))
60 | {
61 | const auto& pca_expression_model = cpp17::get(expression_model);
62 | if (expression_coefficients.empty())
63 | {
64 | expression_sample = pca_expression_model.get_mean();
65 | } else
66 | {
67 | expression_sample = pca_expression_model.draw_sample(expression_coefficients);
68 | }
69 | } else if (cpp17::holds_alternative(expression_model))
70 | {
71 | const auto& expression_blendshapes = cpp17::get(expression_model);
72 | assert(expression_blendshapes.size() > 0);
73 | if (expression_coefficients.empty())
74 | {
75 | expression_sample.setZero(expression_blendshapes[0].deformation.rows());
76 | } else
77 | {
78 | expression_sample = to_matrix(expression_blendshapes) *
79 | Eigen::Map(expression_coefficients.data(),
80 | expression_coefficients.size());
81 | }
82 | } else
83 | {
84 | throw std::runtime_error("The given ExpressionModel doesn't contain an expression model in the form "
85 | "of a PcaModel or Blendshapes.");
86 | }
87 | return expression_sample;
88 | };
89 |
90 | } /* namespace morphablemodel */
91 | } /* namespace eos */
92 |
93 | #endif /* EOS_EXPRESSION_MODEL_HPP */
94 |
--------------------------------------------------------------------------------
/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 write.
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 EIGENCEREALISATION_HPP_
23 | #define EIGENCEREALISATION_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 /* EIGENCEREALISATION_HPP_ */
90 |
--------------------------------------------------------------------------------
/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 TEXTURE_HPP_
23 | #define TEXTURE_HPP_
24 |
25 | #include "opencv2/core/core.hpp"
26 | #include "opencv2/imgproc/imgproc.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) { return !(x & (x - 1)); };
53 |
54 | /**
55 | * @brief Represents a texture for rendering.
56 | *
57 | * Represents a texture and mipmap levels for use in the renderer.
58 | * Todo: This whole class needs a major overhaul and documentation.
59 | */
60 | class Texture
61 | {
62 | public:
63 | std::vector mipmaps; // make Texture a friend class of renderer, then move this to private?
64 | unsigned char widthLog, heightLog; // log2 of width and height of the base mip-level
65 |
66 | // private:
67 | // std::string filename;
68 | unsigned int mipmaps_num;
69 | };
70 |
71 | // throws: ocv exc, runtime_ex
72 | inline Texture create_mipmapped_texture(cv::Mat image, unsigned int mipmapsNum = 0)
73 | {
74 | assert(image.type() == CV_8UC3 || image.type() == CV_8UC4);
75 |
76 | Texture texture;
77 |
78 | texture.mipmaps_num =
79 | (mipmapsNum == 0 ? get_max_possible_mipmaps_num(image.cols, image.rows) : mipmapsNum);
80 | /*if (mipmapsNum == 0)
81 | {
82 | uchar mmn = render::utils::MatrixUtils::getMaxPossibleMipmapsNum(image.cols, image.rows);
83 | this->mipmapsNum = mmn;
84 | } else
85 | {
86 | this->mipmapsNum = mipmapsNum;
87 | }*/
88 |
89 | if (texture.mipmaps_num > 1)
90 | {
91 | if (!is_power_of_two(image.cols) || !is_power_of_two(image.rows))
92 | {
93 | throw std::runtime_error("Error: Couldn't generate mipmaps, width or height not power of two.");
94 | }
95 | }
96 | if (image.type() == CV_8UC3)
97 | {
98 | image.convertTo(image, CV_8UC4); // Most often, the input img is CV_8UC3. Img is BGR. Add an alpha
99 | // channel. TODO: Actually I think this doesn't do anything. Below
100 | // line adds the 4th channel...
101 | cv::cvtColor(image, image, CV_BGR2BGRA);
102 | }
103 |
104 | int currWidth = image.cols;
105 | int currHeight = image.rows;
106 | std::vector mipmaps;
107 | for (unsigned int i = 0; i < texture.mipmaps_num; i++)
108 | {
109 | if (i == 0)
110 | {
111 | mipmaps.push_back(image);
112 | } else
113 | {
114 | cv::Mat currMipMap(currHeight, currWidth, CV_8UC4);
115 | cv::resize(mipmaps[i - 1], currMipMap, currMipMap.size());
116 | mipmaps.push_back(currMipMap);
117 | }
118 |
119 | if (currWidth > 1)
120 | currWidth >>= 1;
121 | if (currHeight > 1)
122 | currHeight >>= 1;
123 | }
124 | texture.mipmaps = mipmaps;
125 | texture.widthLog = (uchar)(std::log(mipmaps[0].cols) / CV_LOG2 +
126 | 0.0001f); // std::epsilon or something? or why 0.0001f here?
127 | texture.heightLog = (uchar)(
128 | std::log(mipmaps[0].rows) / CV_LOG2 +
129 | 0.0001f); // Changed std::logf to std::log because it doesnt compile in linux (gcc 4.8). CHECK THAT
130 | return texture;
131 | };
132 |
133 | } /* namespace render */
134 | } /* namespace eos */
135 |
136 | #endif /* TEXTURE_HPP_ */
137 |
--------------------------------------------------------------------------------
/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 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 VERTEXSHADER_HPP_
23 | #define VERTEXSHADER_HPP_
24 |
25 | #include "glm/mat4x4.hpp"
26 | #include "glm/vec4.hpp"
27 |
28 | namespace eos {
29 | namespace render {
30 |
31 | /**
32 | * @brief A simple vertex shader that projects the vertex and returns the vertex in clip-space coordinates.
33 | */
34 | class VertexShader
35 | {
36 | public:
37 | /**
38 | * @brief Projects the given vertex into clip-space and returns it.
39 | *
40 | * @param[in] vertex The vertex to project.
41 | * @param[in] model_view_matrix The model-view matrix.
42 | * @param[in] projection_matrix The projection matrix.
43 | * @tparam VT Vertex type.
44 | * @tparam VP Vertex precision.
45 | * @tparam MT Matrix type.
46 | * @tparam MP Matrix precision.
47 | * @return Vertex projected to clip space.
48 | */
49 | template
50 | glm::tvec4 operator()(const glm::tvec4& vertex,
51 | const glm::tmat4x4& model_view_matrix,
52 | const glm::tmat4x4& projection_matrix)
53 | {
54 | return projection_matrix * model_view_matrix * vertex;
55 | };
56 | };
57 |
58 | } /* namespace render */
59 | } /* namespace eos */
60 |
61 | #endif /* VERTEXSHADER_HPP_ */
62 |
--------------------------------------------------------------------------------
/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 TRIANGLETORASTERIZE_HPP_
23 | #define TRIANGLETORASTERIZE_HPP_
24 |
25 | #include "eos/render/detail/Vertex.hpp"
26 |
27 | #include "glm/glm.hpp"
28 |
29 | #include
30 |
31 | /**
32 | * The detail namespace contains implementations of internal functions, not part of the API we expose and not
33 | * meant to be used by a user.
34 | */
35 | namespace eos {
36 | namespace render {
37 | namespace detail {
38 |
39 | // plane should probably go into its own file as well.
40 | class plane
41 | {
42 | public:
43 | plane() {}
44 |
45 | plane(float a, float b, float c, float d)
46 | {
47 | this->a = a;
48 | this->b = b;
49 | this->c = c;
50 | this->d = d;
51 | }
52 |
53 | plane(const glm::vec3& normal, float d = 0.0f)
54 | {
55 | this->a = normal[0];
56 | this->b = normal[1];
57 | this->c = normal[2];
58 | this->d = d;
59 | }
60 |
61 | plane(const glm::vec3& point, const glm::vec3& normal)
62 | {
63 | a = normal[0];
64 | b = normal[1];
65 | c = normal[2];
66 | d = -glm::dot(point, normal);
67 | }
68 |
69 | template
70 | plane(const glm::tvec3& point1, const glm::tvec3& point2, const glm::tvec3& point3)
71 | {
72 | const glm::tvec3 v1 = point2 - point1;
73 | const glm::tvec3 v2 = point3 - point1;
74 | glm::tvec3 normal = glm::cross(v1, v2);
75 | normal = glm::normalize(normal);
76 |
77 | a = normal[0];
78 | b = normal[1];
79 | c = normal[2];
80 | d = -glm::dot(point1, normal);
81 | }
82 |
83 | void normalize()
84 | {
85 | const float length = std::sqrt(a * a + b * b + c * c);
86 |
87 | a /= length;
88 | b /= length;
89 | c /= length;
90 | }
91 |
92 | float getSignedDistanceFromPoint(const glm::vec3& point) const
93 | {
94 | return a * point[0] + b * point[1] + c * point[2] + d;
95 | }
96 |
97 | float getSignedDistanceFromPoint(const glm::vec4& point) const
98 | {
99 | return a * point[0] + b * point[1] + c * point[2] + d;
100 | }
101 |
102 | public:
103 | float a, b, c;
104 | float d;
105 | };
106 |
107 | /**
108 | * A representation for a triangle that is to be rasterised.
109 | * Stores the enclosing bounding box of the triangle that is
110 | * calculated during rendering and used during rasterisation.
111 | *
112 | * Used in render_affine and render.
113 | */
114 | struct TriangleToRasterize
115 | {
116 | Vertex v0, v1, v2;
117 | int min_x;
118 | int max_x;
119 | int min_y;
120 | int max_y;
121 | // Everything below is only used in the "normal" renderer, but not
122 | // in render_affine.
123 | double one_over_z0;
124 | double one_over_z1;
125 | double one_over_z2;
126 | // double one_over_v0ToLine12;
127 | // double one_over_v1ToLine20;
128 | // double one_over_v2ToLine01;
129 | plane alphaPlane;
130 | plane betaPlane;
131 | plane gammaPlane;
132 | double one_over_alpha_c; // those are only used for texturing -> float
133 | double one_over_beta_c;
134 | double one_over_gamma_c;
135 | float alpha_ffx;
136 | float beta_ffx;
137 | float gamma_ffx;
138 | float alpha_ffy;
139 | float beta_ffy;
140 | float gamma_ffy;
141 | };
142 |
143 | } /* namespace detail */
144 | } /* namespace render */
145 | } /* namespace eos */
146 |
147 | #endif /* TRIANGLETORASTERIZE_HPP_ */
148 |
--------------------------------------------------------------------------------
/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 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 VERTEX_HPP_
23 | #define VERTEX_HPP_
24 |
25 | #include "glm/vec2.hpp"
26 | #include "glm/vec3.hpp"
27 | #include "glm/vec4.hpp"
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 | /**
38 | * @brief A representation for a vertex during rendering, used internally.
39 | *
40 | * It's used to build the vertices that will be rendered, and new vertices that are generated by the renderer
41 | * (during clipping).
42 | * I should check at some point that no unnecessary copies of the vertex data is created in some places, but I
43 | * think it's pretty much fine.
44 | *
45 | * FragmentShader and SoftwareRenderer use this.
46 | *
47 | * This is the same as the one in the current render_detail.hpp, except that this is fully templated.
48 | *
49 | */
50 | template
51 | struct Vertex
52 | {
53 | glm::tvec4 position; // XYZW
54 | glm::tvec3 color; // RGB order
55 | glm::tvec2 texcoords; // UV
56 | };
57 |
58 | } /* namespace detail */
59 | } /* namespace render */
60 | } /* namespace eos */
61 |
62 | #endif /* VERTEX_HPP_ */
63 |
--------------------------------------------------------------------------------
/include/eos/render/detail/texture_extraction_detail.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 | *
4 | * File: include/eos/render/detail/texture_extraction_detail.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 TEXTURE_EXTRACTION_DETAIL_HPP_
23 | #define TEXTURE_EXTRACTION_DETAIL_HPP_
24 |
25 | #include "eos/core/Image.hpp"
26 | #include "eos/core/Rect.hpp"
27 | #include "eos/render/detail/render_detail_utils.hpp"
28 |
29 | #include "glm/vec2.hpp"
30 | #include "glm/vec4.hpp"
31 |
32 | #include "Eigen/Core"
33 |
34 | /**
35 | * Implementations of internal functions, not part of the
36 | * API we expose and not meant to be used by a user.
37 | */
38 | namespace eos {
39 | namespace render {
40 | namespace detail {
41 |
42 | /**
43 | * Computes whether the given point is inside (or on the border of) the triangle
44 | * formed out of the given three vertices.
45 | *
46 | * @param[in] point The point to check.
47 | * @param[in] triV0 First vertex.
48 | * @param[in] triV1 Second vertex.
49 | * @param[in] triV2 Third vertex.
50 | * @return Whether the point is inside the triangle.
51 | */
52 | inline bool is_point_in_triangle(Eigen::Vector2f point, Eigen::Vector2f triV0, Eigen::Vector2f triV1,
53 | Eigen::Vector2f triV2)
54 | {
55 | // See http://www.blackpawn.com/texts/pointinpoly/
56 | // Compute vectors
57 | const Eigen::Vector2f v0 = triV2 - triV0;
58 | const Eigen::Vector2f v1 = triV1 - triV0;
59 | const Eigen::Vector2f v2 = point - triV0;
60 |
61 | // Compute dot products
62 | const float dot00 = v0.dot(v0);
63 | const float dot01 = v0.dot(v1);
64 | const float dot02 = v0.dot(v2);
65 | const float dot11 = v1.dot(v1);
66 | const float dot12 = v1.dot(v2);
67 |
68 | // Compute barycentric coordinates
69 | const float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
70 | const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
71 | const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
72 |
73 | // Check if point is in triangle
74 | return (u >= 0) && (v >= 0) && (u + v < 1);
75 | };
76 |
77 | /**
78 | * Checks whether all pixels in the given triangle are visible and
79 | * returns true if and only if the whole triangle is visible.
80 | * The vertices should be given in screen coordinates, but with their
81 | * z-values preserved, so they can be compared against the depthbuffer.
82 | *
83 | * Obviously the depthbuffer given should have been created with the same projection
84 | * matrix than the texture extraction is called with.
85 | *
86 | * Also, we don't do perspective-correct interpolation here I think, so only
87 | * use it with affine and orthographic projection matrices.
88 | *
89 | * @param[in] v0 First vertex, in screen coordinates (but still with their z-value).
90 | * @param[in] v1 Second vertex.
91 | * @param[in] v2 Third vertex.
92 | * @param[in] depthbuffer Pre-calculated depthbuffer.
93 | * @return True if the whole triangle is visible in the image.
94 | */
95 | inline bool is_triangle_visible(const glm::tvec4