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[])
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);
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)
{
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:
using glm::vec3;
const float epsilon = 1e-6f;
vec3 v0v1 = v1 - v0;
vec3 v0v2 = v2 - v0;
const vec3 v0v1 = v1 - 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 det is negative, the triangle is back-facing.
......@@ -92,19 +92,19 @@ inline std::pair<bool, boost::optional<float>> ray_triangle_intersect(const glm:
if (std::abs(det) < epsilon)
return { false, boost::none };
}
float inv_det = 1 / det;
const float inv_det = 1 / det;
vec3 tvec = ray_origin - v0;
auto u = glm::dot(tvec, pvec) * inv_det;
const vec3 tvec = ray_origin - v0;
const auto u = glm::dot(tvec, pvec) * inv_det;
if (u < 0 || u > 1)
return { false, boost::none };
vec3 qvec = glm::cross(tvec, v0v1);
auto v = glm::dot(ray_direction, qvec) * inv_det;
const vec3 qvec = glm::cross(tvec, v0v1);
const auto v = glm::dot(ray_direction, qvec) * inv_det;
if (v < 0 || u + v > 1)
return { false, boost::none };
auto t = glm::dot(v0v2, qvec) * inv_det;
const auto t = glm::dot(v0v2, qvec) * inv_det;
return { true, t };
};
......
......@@ -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:
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);
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:
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(
// 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_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 fitting (the inner face landmarks):
......@@ -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:
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:
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);
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;
......
......@@ -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).
* @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;
assert(landmarks.size() == vertex_ids.size());
......@@ -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.
// 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);
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) {
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$
Mat y = Mat::ones(3 * num_landmarks, 1, CV_32FC1);
for (int i = 0; i < num_landmarks; ++i) {
......
......@@ -35,14 +35,15 @@
#include "glm/vec2.hpp"
#include "glm/vec3.hpp"
#include "glm/vec4.hpp"
#include <vector>
#include <array>
#include <cstdint>
// Forward declaration of an internal function:
namespace eos { namespace morphablemodel { namespace detail {
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>());
} } }
// Forward declaration:
namespace eos { namespace morphablemodel {
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 morphablemodel {
......@@ -133,10 +134,10 @@ public:
render::Mesh mesh;
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 {
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;
};
......@@ -159,10 +160,10 @@ public:
render::Mesh mesh;
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 {
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;
};
......@@ -203,10 +204,10 @@ public:
render::Mesh mesh;
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 {
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;
};
......@@ -296,41 +297,39 @@ inline void save_model(MorphableModel model, std::string filename)
output_archive(model);
};
namespace detail { /* eos::morphablemodel::detail */
/**
* Internal helper function that creates a Mesh from given shape and colour
* PCA instances. Needs the vertex index lists as well to assemble the mesh -
* Helper function that creates a Mesh from given shape and colour PCA
* instances. Needs the vertex index lists as well to assemble the mesh -
* and optional texture coordinates.
*
* If \c color is empty, it will create a mesh without vertex colouring.
*
* @param[in] shape PCA shape model instance.
* @param[in] color PCA colour model instance.
* @param[in] shape_instance PCA shape model instance.
* @param[in] color_instance PCA colour model instance.
* @param[in] tvi Triangle vertex indices.
* @param[in] tci Triangle colour indices (usually identical to the vertex indices).
* @param[in] texture_coordinates Optional texture coordinates for each vertex.
* @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;
// Construct the mesh vertices:
mesh.vertices.resize(num_vertices);
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:
if (!color.empty()) {
if (!color_instance.empty()) {
mesh.colors.resize(num_vertices);
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
return mesh;
};
} /* namespace eos::morphablemodel::detail */
} /* namespace morphablemodel */
} /* 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