eos 1.4.0
Loading...
Searching...
No Matches
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, 2023 Patrik Huber
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20#pragma once
21
22#ifndef EOS_ORTHOGRAPHIC_CAMERA_ESTIMATION_LINEAR_HPP
23#define EOS_ORTHOGRAPHIC_CAMERA_ESTIMATION_LINEAR_HPP
24
25#include "eos/cpp17/optional.hpp"
26
27#include "Eigen/Core"
28#include "Eigen/Dense"
29#include "Eigen/SVD"
30#include "Eigen/Dense"
31
32#include <vector>
33#include <cassert>
34
35namespace eos {
36namespace fitting {
37
42{
43 Eigen::Matrix3f R;
44 float tx, ty;
45 float s;
46};
47
73 std::vector<Eigen::Vector2f> image_points, std::vector<Eigen::Vector4f> model_points,
74 bool is_viewport_upsidedown, cpp17::optional<int> viewport_height = cpp17::nullopt)
75{
76 using Eigen::Matrix;
77 assert(image_points.size() == model_points.size());
78 assert(image_points.size() >= 4); // Number of correspondence points given needs to be equal to or larger than 4
79
80 const int num_correspondences = static_cast<int>(image_points.size());
81
82 if (is_viewport_upsidedown)
83 {
84 if (viewport_height == cpp17::nullopt)
85 {
86 throw std::runtime_error(
87 "Error: If is_viewport_upsidedown is set to true, viewport_height needs to be given.");
88 }
89 for (auto&& ip : image_points)
90 {
91 ip[1] = viewport_height.value() - ip[1];
92 }
93 }
94
95 Matrix<float, Eigen::Dynamic, 8> A = Matrix<float, Eigen::Dynamic, 8>::Zero(2 * num_correspondences, 8);
96 for (int i = 0; i < model_points.size(); ++i)
97 {
98 A.block<1, 4>(2 * i, 0) = model_points[i]; // even row - copy to left side (first row is row 0)
99 A.block<1, 4>((2 * i) + 1, 4) = model_points[i]; // odd row - copy to right side
100 } // 4th coord (homogeneous) is already 1
101
102 Matrix<float, Eigen::Dynamic, 1> b(2 * num_correspondences, 1);
103 for (int i = 0; i < image_points.size(); ++i)
104 {
105 b.segment<2>(2 * i) = image_points[i];
106 }
107
108 // The original implementation used SVD here to solve the linear system, but
109 // QR seems to do the job fine too.
110 const Matrix<float, 8, 1> k = A.colPivHouseholderQr().solve(b); // resulting affine matrix (8x1)
111
112 // Extract all values from the estimated affine parameters k:
113 const Eigen::Vector3f R1 = k.segment<3>(0);
114 const Eigen::Vector3f R2 = k.segment<3>(4);
115 const float sTx = k(3);
116 const float sTy = k(7);
117 const auto s = (R1.norm() + R2.norm()) / 2.0f;
118 Eigen::Matrix3f R;
119 Eigen::Vector3f r1 = R1.normalized(); // Not sure why R1.normalize() (in-place) produces a compiler error.
120 Eigen::Vector3f r2 = R2.normalized();
121 R.block<1, 3>(0, 0) = r1;
122 R.block<1, 3>(1, 0) = r2;
123 R.block<1, 3>(2, 0) = r1.cross(r2);
124
125 // Set R to the closest orthonormal matrix to the estimated affine transform:
126 Eigen::JacobiSVD<Eigen::Matrix3f, Eigen::NoQRPreconditioner> svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV);
127 Eigen::Matrix3f U = svd.matrixU();
128 const Eigen::Matrix3f V = svd.matrixV();
129 Eigen::Matrix3f R_ortho = U * V.transpose();
130
131 // The determinant of R must be 1 for it to be a valid rotation matrix
132 if (R_ortho.determinant() < 0)
133 {
134 U.block<1, 3>(2, 0) = -U.block<1, 3>(2, 0); // not tested
135 R_ortho = U * V.transpose();
136 }
137
138 // Remove the scale from the translations:
139 const auto t1 = sTx / s;
140 const auto t2 = sTy / s;
141
142 return ScaledOrthoProjectionParameters{R_ortho, t1, t2, s};
143};
144
145} /* namespace fitting */
146} /* namespace eos */
147
148#endif /* EOS_ORTHOGRAPHIC_CAMERA_ESTIMATION_LINEAR_HPP */
ScaledOrthoProjectionParameters estimate_orthographic_projection_linear(std::vector< Eigen::Vector2f > image_points, std::vector< Eigen::Vector4f > model_points, bool is_viewport_upsidedown, cpp17::optional< int > viewport_height=cpp17::nullopt)
Definition: orthographic_camera_estimation_linear.hpp:72
Namespace containing all of eos's 3D model fitting functionality.
Definition: orthographic_camera_estimation_linear.hpp:42
Eigen::Matrix3f R
3x3 rotation matrix
Definition: orthographic_camera_estimation_linear.hpp:43
float ty
x and y translation
Definition: orthographic_camera_estimation_linear.hpp:44
float s
Scaling.
Definition: orthographic_camera_estimation_linear.hpp:45