Commit 2550ebd4 authored by Patrik Huber's avatar Patrik Huber

Merge branch 'devel'

parents fa018da8 42d1f43c
...@@ -421,7 +421,7 @@ int main(int argc, char *argv[]) ...@@ -421,7 +421,7 @@ int main(int argc, char *argv[])
return std::vector<float>(std::begin(vec), std::end(vec)); return std::vector<float>(std::begin(vec), std::end(vec));
}; };
auto shape_ceres = morphable_model.get_shape_model().draw_sample(shape_coefficients) + to_matrix(blendshapes) * Mat(vectord_to_vectorf(blendshape_coefficients), true); auto shape_ceres = morphable_model.get_shape_model().draw_sample(shape_coefficients) + to_matrix(blendshapes) * Mat(vectord_to_vectorf(blendshape_coefficients), true);
render::Mesh mesh = morphablemodel::detail::sample_to_mesh(shape_ceres, morphable_model.get_color_model().draw_sample(colour_coefficients), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates()); render::Mesh mesh = morphablemodel::sample_to_mesh(shape_ceres, morphable_model.get_color_model().draw_sample(colour_coefficients), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates());
for (auto&& idx : vertex_indices) for (auto&& idx : vertex_indices)
{ {
glm::dvec3 point_3d(mesh.vertices[idx][0], mesh.vertices[idx][1], mesh.vertices[idx][2]); // The 3D model point glm::dvec3 point_3d(mesh.vertices[idx][0], mesh.vertices[idx][1], mesh.vertices[idx][2]); // The 3D model point
......
...@@ -74,12 +74,12 @@ inline std::pair<bool, boost::optional<float>> ray_triangle_intersect(const glm: ...@@ -74,12 +74,12 @@ inline std::pair<bool, boost::optional<float>> ray_triangle_intersect(const glm:
using glm::vec3; using glm::vec3;
const float epsilon = 1e-6f; const float epsilon = 1e-6f;
vec3 v0v1 = v1 - v0; const vec3 v0v1 = v1 - v0;
vec3 v0v2 = v2 - v0; const vec3 v0v2 = v2 - v0;
vec3 pvec = glm::cross(ray_direction, v0v2); const vec3 pvec = glm::cross(ray_direction, v0v2);
float det = glm::dot(v0v1, pvec); const float det = glm::dot(v0v1, pvec);
if (enable_backculling) if (enable_backculling)
{ {
// If det is negative, the triangle is back-facing. // If det is negative, the triangle is back-facing.
...@@ -92,19 +92,19 @@ inline std::pair<bool, boost::optional<float>> ray_triangle_intersect(const glm: ...@@ -92,19 +92,19 @@ inline std::pair<bool, boost::optional<float>> ray_triangle_intersect(const glm:
if (std::abs(det) < epsilon) if (std::abs(det) < epsilon)
return { false, boost::none }; return { false, boost::none };
} }
float inv_det = 1 / det; const float inv_det = 1 / det;
vec3 tvec = ray_origin - v0; const vec3 tvec = ray_origin - v0;
auto u = glm::dot(tvec, pvec) * inv_det; const auto u = glm::dot(tvec, pvec) * inv_det;
if (u < 0 || u > 1) if (u < 0 || u > 1)
return { false, boost::none }; return { false, boost::none };
vec3 qvec = glm::cross(tvec, v0v1); const vec3 qvec = glm::cross(tvec, v0v1);
auto v = glm::dot(ray_direction, qvec) * inv_det; const auto v = glm::dot(ray_direction, qvec) * inv_det;
if (v < 0 || u + v > 1) if (v < 0 || u + v > 1)
return { false, boost::none }; return { false, boost::none };
auto t = glm::dot(v0v2, qvec) * inv_det; const auto t = glm::dot(v0v2, qvec) * inv_det;
return { true, t }; return { true, t };
}; };
......
...@@ -266,7 +266,7 @@ inline std::pair<render::Mesh, fitting::RenderingParameters> fit_shape_and_pose( ...@@ -266,7 +266,7 @@ inline std::pair<render::Mesh, fitting::RenderingParameters> fit_shape_and_pose(
// Current mesh - either from the given coefficients, or the mean: // Current mesh - either from the given coefficients, or the mean:
Mat current_pca_shape = morphable_model.get_shape_model().draw_sample(pca_shape_coefficients); Mat current_pca_shape = morphable_model.get_shape_model().draw_sample(pca_shape_coefficients);
Mat current_combined_shape = current_pca_shape + blendshapes_as_basis * Mat(blendshape_coefficients); Mat current_combined_shape = current_pca_shape + blendshapes_as_basis * Mat(blendshape_coefficients);
auto current_mesh = morphablemodel::detail::sample_to_mesh(current_combined_shape, morphable_model.get_color_model().get_mean(), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates()); auto current_mesh = morphablemodel::sample_to_mesh(current_combined_shape, morphable_model.get_color_model().get_mean(), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates());
// The 2D and 3D point correspondences used for the fitting: // The 2D and 3D point correspondences used for the fitting:
vector<Vec4f> model_points; // the points in the 3D shape model vector<Vec4f> model_points; // the points in the 3D shape model
...@@ -298,7 +298,7 @@ inline std::pair<render::Mesh, fitting::RenderingParameters> fit_shape_and_pose( ...@@ -298,7 +298,7 @@ inline std::pair<render::Mesh, fitting::RenderingParameters> fit_shape_and_pose(
// Mesh with same PCA coeffs as before, but new expression fit (this is relevant if no initial blendshape coeffs have been given): // Mesh with same PCA coeffs as before, but new expression fit (this is relevant if no initial blendshape coeffs have been given):
current_combined_shape = current_pca_shape + morphablemodel::to_matrix(blendshapes) * Mat(blendshape_coefficients); current_combined_shape = current_pca_shape + morphablemodel::to_matrix(blendshapes) * Mat(blendshape_coefficients);
current_mesh = morphablemodel::detail::sample_to_mesh(current_combined_shape, morphable_model.get_color_model().get_mean(), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates()); current_mesh = morphablemodel::sample_to_mesh(current_combined_shape, morphable_model.get_color_model().get_mean(), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates());
// The static (fixed) landmark correspondences which will stay the same throughout // The static (fixed) landmark correspondences which will stay the same throughout
// the fitting (the inner face landmarks): // the fitting (the inner face landmarks):
...@@ -349,14 +349,14 @@ inline std::pair<render::Mesh, fitting::RenderingParameters> fit_shape_and_pose( ...@@ -349,14 +349,14 @@ inline std::pair<render::Mesh, fitting::RenderingParameters> fit_shape_and_pose(
// Estimate the PCA shape coefficients with the current blendshape coefficients: // Estimate the PCA shape coefficients with the current blendshape coefficients:
Mat mean_plus_blendshapes = morphable_model.get_shape_model().get_mean() + blendshapes_as_basis * Mat(blendshape_coefficients); Mat mean_plus_blendshapes = morphable_model.get_shape_model().get_mean() + blendshapes_as_basis * Mat(blendshape_coefficients);
pca_shape_coefficients = fitting::fit_shape_to_landmarks_linear(morphable_model, affine_from_ortho, image_points, vertex_indices, mean_plus_blendshapes, lambda); pca_shape_coefficients = fitting::fit_shape_to_landmarks_linear(morphable_model, affine_from_ortho, image_points, vertex_indices, mean_plus_blendshapes, lambda, num_shape_coefficients_to_fit);
// Estimate the blendshape coefficients with the current PCA model estimate: // Estimate the blendshape coefficients with the current PCA model estimate:
current_pca_shape = morphable_model.get_shape_model().draw_sample(pca_shape_coefficients); current_pca_shape = morphable_model.get_shape_model().draw_sample(pca_shape_coefficients);
blendshape_coefficients = fitting::fit_blendshapes_to_landmarks_nnls(blendshapes, current_pca_shape, affine_from_ortho, image_points, vertex_indices); blendshape_coefficients = fitting::fit_blendshapes_to_landmarks_nnls(blendshapes, current_pca_shape, affine_from_ortho, image_points, vertex_indices);
current_combined_shape = current_pca_shape + blendshapes_as_basis * Mat(blendshape_coefficients); current_combined_shape = current_pca_shape + blendshapes_as_basis * Mat(blendshape_coefficients);
current_mesh = morphablemodel::detail::sample_to_mesh(current_combined_shape, morphable_model.get_color_model().get_mean(), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates()); current_mesh = morphablemodel::sample_to_mesh(current_combined_shape, morphable_model.get_color_model().get_mean(), morphable_model.get_shape_model().get_triangle_list(), morphable_model.get_color_model().get_triangle_list(), morphable_model.get_texture_coordinates());
} }
fitted_image_points = image_points; fitted_image_points = image_points;
......
...@@ -57,7 +57,7 @@ namespace eos { ...@@ -57,7 +57,7 @@ namespace eos {
* @param[in] model_standard_deviation The standard deviation of the 3D vertex points in the 3D model, projected to 2D (so the value is in pixels). * @param[in] model_standard_deviation The standard deviation of the 3D vertex points in the 3D model, projected to 2D (so the value is in pixels).
* @return The estimated shape-coefficients (alphas). * @return The estimated shape-coefficients (alphas).
*/ */
inline std::vector<float> fit_shape_to_landmarks_linear(morphablemodel::MorphableModel morphable_model, cv::Mat affine_camera_matrix, std::vector<cv::Vec2f> landmarks, std::vector<int> vertex_ids, cv::Mat base_face=cv::Mat(), float lambda=3.0f, boost::optional<int> num_coefficients_to_fit=boost::optional<int>(), boost::optional<float> detector_standard_deviation=boost::optional<float>(), boost::optional<float> model_standard_deviation=boost::optional<float>()) inline std::vector<float> fit_shape_to_landmarks_linear(const morphablemodel::MorphableModel& morphable_model, cv::Mat affine_camera_matrix, std::vector<cv::Vec2f> landmarks, std::vector<int> vertex_ids, cv::Mat base_face=cv::Mat(), float lambda=3.0f, boost::optional<int> num_coefficients_to_fit=boost::optional<int>(), boost::optional<float> detector_standard_deviation=boost::optional<float>(), boost::optional<float> model_standard_deviation=boost::optional<float>())
{ {
using cv::Mat; using cv::Mat;
assert(landmarks.size() == vertex_ids.size()); assert(landmarks.size() == vertex_ids.size());
...@@ -92,11 +92,11 @@ inline std::vector<float> fit_shape_to_landmarks_linear(morphablemodel::Morphabl ...@@ -92,11 +92,11 @@ inline std::vector<float> fit_shape_to_landmarks_linear(morphablemodel::Morphabl
// 3D (model) variance: 0.0f. It only makes sense to set it to something when we have a different variance for different vertices. // 3D (model) variance: 0.0f. It only makes sense to set it to something when we have a different variance for different vertices.
// The 3D variance has to be projected to 2D (for details, see paper [1]) so the units do match up. // The 3D variance has to be projected to 2D (for details, see paper [1]) so the units do match up.
float sigma_squared_2D = std::pow(detector_standard_deviation.get_value_or(std::sqrt(3.0f)), 2) + std::pow(model_standard_deviation.get_value_or(0.0f), 2); float sigma_squared_2D = std::pow(detector_standard_deviation.get_value_or(std::sqrt(3.0f)), 2) + std::pow(model_standard_deviation.get_value_or(0.0f), 2);
Mat Sigma = Mat::zeros(3 * num_landmarks, 3 * num_landmarks, CV_32FC1); Mat Omega = Mat::zeros(3 * num_landmarks, 3 * num_landmarks, CV_32FC1);
for (int i = 0; i < 3 * num_landmarks; ++i) { for (int i = 0; i < 3 * num_landmarks; ++i) {
Sigma.at<float>(i, i) = 1.0f / std::sqrt(sigma_squared_2D); // the higher the sigma_squared_2D, the smaller the diagonal entries of Sigma will be // Sigma(i, i) = sqrt(sigma_squared_2D), but then Omega is Sigma.t() * Sigma (squares the diagonal) - so we just assign 1/sigma_squared_2D to Omega here:
Omega.at<float>(i, i) = 1.0f / sigma_squared_2D; // the higher the sigma_squared_2D, the smaller the diagonal entries of Sigma will be
} }
Mat Omega = Sigma.t() * Sigma; // just squares the diagonal
// The landmarks in matrix notation (in homogeneous coordinates), $3N\times 1$ // The landmarks in matrix notation (in homogeneous coordinates), $3N\times 1$
Mat y = Mat::ones(3 * num_landmarks, 1, CV_32FC1); Mat y = Mat::ones(3 * num_landmarks, 1, CV_32FC1);
for (int i = 0; i < num_landmarks; ++i) { for (int i = 0; i < num_landmarks; ++i) {
......
...@@ -35,14 +35,15 @@ ...@@ -35,14 +35,15 @@
#include "glm/vec2.hpp" #include "glm/vec2.hpp"
#include "glm/vec3.hpp" #include "glm/vec3.hpp"
#include "glm/vec4.hpp" #include "glm/vec4.hpp"
#include <vector> #include <vector>
#include <array> #include <array>
#include <cstdint> #include <cstdint>
// Forward declaration of an internal function: // Forward declaration:
namespace eos { namespace morphablemodel { namespace detail { namespace eos { namespace morphablemodel {
eos::render::Mesh sample_to_mesh(cv::Mat shape, cv::Mat color, std::vector<std::array<int, 3>> tvi, std::vector<std::array<int, 3>> tci, std::vector<cv::Vec2f> texture_coordinates = std::vector<cv::Vec2f>()); eos::render::Mesh sample_to_mesh(cv::Mat shape_instance, cv::Mat color_instance, std::vector<std::array<int, 3>> tvi, std::vector<std::array<int, 3>> tci, std::vector<cv::Vec2f> texture_coordinates = std::vector<cv::Vec2f>());
} } } } }
namespace eos { namespace eos {
namespace morphablemodel { namespace morphablemodel {
...@@ -133,10 +134,10 @@ public: ...@@ -133,10 +134,10 @@ public:
render::Mesh mesh; render::Mesh mesh;
if (has_texture_coordinates()) { if (has_texture_coordinates()) {
mesh = detail::sample_to_mesh(shape, color, shape_model.get_triangle_list(), color_model.get_triangle_list(), texture_coordinates); mesh = sample_to_mesh(shape, color, shape_model.get_triangle_list(), color_model.get_triangle_list(), texture_coordinates);
} }
else { else {
mesh = detail::sample_to_mesh(shape, color, shape_model.get_triangle_list(), color_model.get_triangle_list()); mesh = sample_to_mesh(shape, color, shape_model.get_triangle_list(), color_model.get_triangle_list());
} }
return mesh; return mesh;
}; };
...@@ -159,10 +160,10 @@ public: ...@@ -159,10 +160,10 @@ public:
render::Mesh mesh; render::Mesh mesh;
if (has_texture_coordinates()) { if (has_texture_coordinates()) {
mesh = detail::sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list(), texture_coordinates); mesh = sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list(), texture_coordinates);
} }
else { else {
mesh = detail::sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list()); mesh = sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list());
} }
return mesh; return mesh;
}; };
...@@ -203,10 +204,10 @@ public: ...@@ -203,10 +204,10 @@ public:
render::Mesh mesh; render::Mesh mesh;
if (has_texture_coordinates()) { if (has_texture_coordinates()) {
mesh = detail::sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list(), texture_coordinates); mesh = sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list(), texture_coordinates);
} }
else { else {
mesh = detail::sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list()); mesh = sample_to_mesh(shape_sample, color_sample, shape_model.get_triangle_list(), color_model.get_triangle_list());
} }
return mesh; return mesh;
}; };
...@@ -296,41 +297,39 @@ inline void save_model(MorphableModel model, std::string filename) ...@@ -296,41 +297,39 @@ inline void save_model(MorphableModel model, std::string filename)
output_archive(model); output_archive(model);
}; };
namespace detail { /* eos::morphablemodel::detail */
/** /**
* Internal helper function that creates a Mesh from given shape and colour * Helper function that creates a Mesh from given shape and colour PCA
* PCA instances. Needs the vertex index lists as well to assemble the mesh - * instances. Needs the vertex index lists as well to assemble the mesh -
* and optional texture coordinates. * and optional texture coordinates.
* *
* If \c color is empty, it will create a mesh without vertex colouring. * If \c color is empty, it will create a mesh without vertex colouring.
* *
* @param[in] shape PCA shape model instance. * @param[in] shape_instance PCA shape model instance.
* @param[in] color PCA colour model instance. * @param[in] color_instance PCA colour model instance.
* @param[in] tvi Triangle vertex indices. * @param[in] tvi Triangle vertex indices.
* @param[in] tci Triangle colour indices (usually identical to the vertex indices). * @param[in] tci Triangle colour indices (usually identical to the vertex indices).
* @param[in] texture_coordinates Optional texture coordinates for each vertex. * @param[in] texture_coordinates Optional texture coordinates for each vertex.
* @return A mesh created from given parameters. * @return A mesh created from given parameters.
*/ */
inline render::Mesh sample_to_mesh(cv::Mat shape, cv::Mat color, std::vector<std::array<int, 3>> tvi, std::vector<std::array<int, 3>> tci, std::vector<cv::Vec2f> texture_coordinates /* = std::vector<cv::Vec2f>() */) inline render::Mesh sample_to_mesh(cv::Mat shape_instance, cv::Mat color_instance, std::vector<std::array<int, 3>> tvi, std::vector<std::array<int, 3>> tci, std::vector<cv::Vec2f> texture_coordinates /* = std::vector<cv::Vec2f>() */)
{ {
assert(shape.rows == color.rows || color.empty()); // The number of vertices (= model.getDataDimension() / 3) has to be equal for both models, or, alternatively, it has to be a shape-only model. assert(shape_instance.rows == color_instance.rows || color_instance.empty()); // The number of vertices (= model.getDataDimension() / 3) has to be equal for both models, or, alternatively, it has to be a shape-only model.
auto num_vertices = shape.rows / 3; auto num_vertices = shape_instance.rows / 3;
render::Mesh mesh; render::Mesh mesh;
// Construct the mesh vertices: // Construct the mesh vertices:
mesh.vertices.resize(num_vertices); mesh.vertices.resize(num_vertices);
for (auto i = 0; i < num_vertices; ++i) { for (auto i = 0; i < num_vertices; ++i) {
mesh.vertices[i] = glm::tvec4<float>(shape.at<float>(i * 3 + 0), shape.at<float>(i * 3 + 1), shape.at<float>(i * 3 + 2), 1.0f); mesh.vertices[i] = glm::tvec4<float>(shape_instance.at<float>(i * 3 + 0), shape_instance.at<float>(i * 3 + 1), shape_instance.at<float>(i * 3 + 2), 1.0f);
} }
// Assign the vertex colour information if it's not a shape-only model: // Assign the vertex colour information if it's not a shape-only model:
if (!color.empty()) { if (!color_instance.empty()) {
mesh.colors.resize(num_vertices); mesh.colors.resize(num_vertices);
for (auto i = 0; i < num_vertices; ++i) { for (auto i = 0; i < num_vertices; ++i) {
mesh.colors[i] = glm::tvec3<float>(color.at<float>(i * 3 + 0), color.at<float>(i * 3 + 1), color.at<float>(i * 3 + 2)); // order in hdf5: RGB. Order in OCV: BGR. But order in vertex.color: RGB mesh.colors[i] = glm::tvec3<float>(color_instance.at<float>(i * 3 + 0), color_instance.at<float>(i * 3 + 1), color_instance.at<float>(i * 3 + 2)); // order in hdf5: RGB. Order in OCV: BGR. But order in vertex.color: RGB
} }
} }
...@@ -348,7 +347,6 @@ inline render::Mesh sample_to_mesh(cv::Mat shape, cv::Mat color, std::vector<std ...@@ -348,7 +347,6 @@ inline render::Mesh sample_to_mesh(cv::Mat shape, cv::Mat color, std::vector<std
return mesh; return mesh;
}; };
} /* namespace eos::morphablemodel::detail */
} /* namespace morphablemodel */ } /* namespace morphablemodel */
} /* namespace eos */ } /* namespace eos */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment