Skip to content

Commit

Permalink
Implemented get_yaw_pitch_roll() and changed most of RenderingParamet…
Browse files Browse the repository at this point in the history
…ers to Eigen

* Updated/improved the docs of RenderingParameters a bit
* Added get_yaw_pitch_roll() to convert from the Eigen rotation quaternion to Euler angles (Tait-Bryan angles, to be precise) that are (almost) equivalent to the angles GLM outputted before.
* Removed GLM code from get_modelview() and get_projection(). I tested the Eigen equivalents carefully.
* Changed the fitting code & apps to use rendering_params.get_yaw_pitch_roll()[0] for the contour fitting.

Some code is still duplicated in RenderingParameters (i.e. containing the GLM and Eigen equivalents), for further testing.
  • Loading branch information
patrikhuber committed Mar 25, 2023
1 parent 7e99ab1 commit a23ee0b
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 45 deletions.
6 changes: 3 additions & 3 deletions examples/fit-model-simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ int main(int argc, char* argv[])
fitting::estimate_orthographic_projection_linear(image_points, model_points, true, image.rows);
fitting::RenderingParameters rendering_params(pose, image.cols, image.rows);

// The 3D head pose can be recovered as follows:
const float yaw_angle = glm::degrees(glm::yaw(rendering_params.get_rotation()));
// and similarly for pitch and roll.
// The 3D head pose can be recovered as follows - the function returns an Eigen::Vector3f with yaw, pitch,
// and roll angles:
const float yaw_angle = rendering_params.get_yaw_pitch_roll()[0];

// Estimate the shape coefficients by fitting the shape to the landmarks:
const Eigen::Matrix<float, 3, 4> affine_from_ortho =
Expand Down
6 changes: 3 additions & 3 deletions examples/fit-model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ int main(int argc, char* argv[])
morphable_model_with_expressions, landmarks, landmark_mapper, image.cols, image.rows, edge_topology,
ibug_contour, model_contour, 5, cpp17::nullopt, 30.0f);

// The 3D head pose can be recovered as follows:
float yaw_angle = glm::degrees(glm::yaw(rendering_params.get_rotation()));
// and similarly for pitch and roll.
// The 3D head pose can be recovered as follows - the function returns an Eigen::Vector3f with yaw, pitch,
// and roll angles:
const float yaw_angle = rendering_params.get_yaw_pitch_roll()[0];

// Extract the texture from the image using given mesh and camera parameters:
const core::Image4u texturemap =
Expand Down
100 changes: 62 additions & 38 deletions include/eos/fitting/RenderingParameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#define EOS_RENDERING_PARAMETERS_HPP

#include "eos/fitting/orthographic_camera_estimation_linear.hpp"
#include "eos/fitting/rotation_angles.hpp"
#include "eos/render/matrix_projection.hpp"
#include "eos/cpp17/optional.hpp"
#include "eos/cpp17/optional_serialization.hpp"
Expand Down Expand Up @@ -85,28 +86,21 @@ enum class CameraType
* camera parameters (viewing frustum).
*
* The estimated rotation and translation transform the model from model-space to camera-space,
* and, if one wishes to use OpenGL, can be used to build the model-view matrix.
* and, if one wishes to use OpenGL, can be used to build the model-view matrix, i.e. the parameter
* representation is OpenGL compliant.
*
* The parameters are the inverse of the camera position in 3D space.
*
* The camera frustum describes the size of the viewing plane of the camera, and
* can be used to build an OpenGL-conformant orthographic projection matrix.
*
* Together, these parameters fully describe the imaging process of a given model instance
* (under an orthographic projection).
*
* The rotation values are given in radians and estimated using the RPY convention.
* Yaw is applied first to the model, then pitch, then roll (R * P * Y * vertex).
* In general, the convention is as follows:
* r_x = Pitch
* r_y = Yaw. Positive means subject is looking left (we see her right cheek).
* r_z = Roll. Positive means the subject's right eye is further down than the other one (he
*tilts his head to the right). However, we're using a quaternion now to represent the rotation, and
*glm::eulerAngles() will give slightly different angles (according to a different (undocumented))
*convention. However, the rotation is exactly the same! (i.e. they are represented by the same
*quaternion / rotation matrix).
* Together, these parameters fully describe the imaging process of a given model instance.
*
* This should always represent all parameters necessary to render the model to an image, and be
*completely OpenGL compliant.
* The rotation is represented using a quaternion. However, the quaternion can be converted to a rotation
* matrix, and subsequently Euler angles can be extracted. In our coordinate system, the x axis points to the
* right, y points upwards, and z into/out of the screen. When extracting Euler angles (or Tait-Bryan angles,
* to be more precise), we assume rotation around the y axis first (yaw), then around x axis (pitch), then
* around z (roll).
*/
class RenderingParameters
{
Expand Down Expand Up @@ -159,10 +153,42 @@ class RenderingParameters
return camera_type;
};

glm::quat get_rotation() const { return rotation; };
glm::quat get_rotation_() const { return rotation; };
Eigen::Quaternionf get_rotation() const
{
return rotation_;
};

void set_rotation(glm::quat rotation_quaternion) { rotation = rotation_quaternion; };

/**
* @brief Returns the intrinsic rotation angles, also called Tait-Bryan angles, in degrees. The returned
* Vector3f contains [yaw, pitch, roll].
*
* The order of rotations is yaw, pitch, roll (in our coordinate system, y, x, z).
*
* - Positive pitch means the head is looking down
* - Positive yaw means the head is looking to the left (from the subject's perspective, i.e. we see their
* right cheek)
* - Positive roll means the head's right eye is further down than the other one (the head is tilted to
* the right, from the subject's perspective).
*/
Eigen::Vector3f get_yaw_pitch_roll()
{
// We use Eigen's Matrix3::eulerAngles() function with axes (1, 0, 2). In our coordinate system, the x
// axis is to the right, y up, and z into/out of the screen. So to get yaw first, our first axis is y
// ('1'), then our x axis ('0') to get pitch, then our z axis ('2') to get roll.
// We are guessing that the function returns intrinsic angles (the unsupported EulerAngles() module
// does, but it's an unrelated module).
// Because eulerAngles() constrains the angles in a different way to what we want, we correct for
// that in tait_bryan_angles() (which calls eulerAngles() and then modifies the solution accordingly).
Eigen::Vector3f ea = fitting::tait_bryan_angles(rotation_.normalized().toRotationMatrix(), 1, 0, 2);
ea(0) = core::degrees(ea(0));
ea(1) = core::degrees(ea(1));
ea(2) = core::degrees(ea(2));
return ea;
};

void set_translation(float t_x, float t_y, cpp17::optional<float> t_z = cpp17::nullopt)
{
this->t_x = t_x;
Expand All @@ -184,48 +210,46 @@ class RenderingParameters
return fov_y;
};

/**
* @brief Construct a model-view matrix from the RenderingParameters' rotation and translation, and return
* it.
*/
Eigen::Matrix4f get_modelview() const
{
if (camera_type == CameraType::Orthographic)
{
Eigen::Matrix4f modelview_ = Eigen::Matrix4f::Identity();
modelview_.block<3, 3>(0, 0) = rotation_.normalized().toRotationMatrix();
modelview_(0, 3) = t_x;
modelview_(1, 3) = t_y;
glm::mat4x4 modelview = glm::mat4_cast(rotation);
modelview[3][0] = t_x;
modelview[3][1] = t_y;
// Checked, identical!
return modelview_;
Eigen::Matrix4f modelview = Eigen::Matrix4f::Identity();
modelview.block<3, 3>(0, 0) = rotation_.normalized().toRotationMatrix();
modelview(0, 3) = t_x;
modelview(1, 3) = t_y;
return modelview;
} else
{
assert(t_z.has_value()); // Might be worth throwing an exception instead.
Eigen::Matrix4f modelview_ = Eigen::Matrix4f::Identity();
modelview_.block<3, 3>(0, 0) = rotation_.normalized().toRotationMatrix();
modelview_(3, 0) = t_x;
modelview_(3, 1) = t_y;
modelview_(3, 2) = t_z.value();
const glm::mat4x4 rot_mtx = glm::mat4_cast(rotation);
const auto t_mtx = glm::translate(glm::vec3(t_x, t_y, t_z.value()));
const glm::mat4x4 modelview = t_mtx * rot_mtx;
return modelview_;
Eigen::Matrix4f modelview = Eigen::Matrix4f::Identity();
modelview.block<3, 3>(0, 0) = rotation_.normalized().toRotationMatrix();
modelview(0, 3) = t_x;
modelview(1, 3) = t_y;
modelview(2, 3) = t_z.value();
return modelview;
}
};

/**
* @brief Construct an orthographic or perspective projection matrix from the RenderingParameters' frustum
* (orthographic) or fov_y and aspect ration (perspective), and return it.
*/
Eigen::Matrix4f get_projection() const
{
if (camera_type == CameraType::Orthographic)
{
glm::mat4x4 ortho_ = glm::ortho<float>(frustum.l, frustum.r, frustum.b, frustum.t);
const auto ortho = render::ortho(frustum.l, frustum.r, frustum.b, frustum.t);
// Checked, identical!
return ortho;
} else
{
assert(fov_y.has_value()); // Might be worth throwing an exception instead.
const float aspect_ratio = static_cast<double>(screen_width) / screen_height;
const auto persp_mtx = render::perspective(fov_y.value(), aspect_ratio, 0.1f, 1000.0f);
const auto persp_mtx_ = glm::perspective(fov_y.value(), aspect_ratio, 0.1f, 1000.0f);
return persp_mtx;
}
};
Expand Down
2 changes: 1 addition & 1 deletion include/eos/fitting/fitting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ inline std::pair<core::Mesh, fitting::RenderingParameters> fit_shape_and_pose(
// Given the current pose, find 2D-3D contour correspondences of the front-facing face contour:
vector<Vector2f> image_points_contour;
vector<int> vertex_indices_contour;
const auto yaw_angle = glm::degrees(glm::eulerAngles(rendering_params.get_rotation())[1]);
const auto yaw_angle = rendering_params.get_yaw_pitch_roll()[0];
// For each 2D contour landmark, get the corresponding 3D vertex point and vertex id:
std::tie(image_points_contour, std::ignore, vertex_indices_contour) =
fitting::get_contour_correspondences(landmarks, contour_landmarks, model_contour, yaw_angle,
Expand Down

0 comments on commit a23ee0b

Please sign in to comment.