eos  0.7.1
PcaModel.hpp
1 /*
2  * Eos - A 3D Morphable Model fitting library written in modern C++11/14.
3  *
4  * File: include/eos/morphablemodel/PcaModel.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 PCAMODEL_HPP_
23 #define PCAMODEL_HPP_
24 
25 #include "eos/morphablemodel/io/mat_cerealisation.hpp"
26 #include "cereal/access.hpp"
27 #include "cereal/types/array.hpp"
28 #include "cereal/types/vector.hpp"
29 
30 #include "opencv2/core/core.hpp"
31 
32 #include <string>
33 #include <vector>
34 #include <array>
35 #include <random>
36 #include <cassert>
37 
38 namespace eos {
39  namespace morphablemodel {
40 
41 // Forward declarations of free functions
42 cv::Mat normalise_pca_basis(cv::Mat unnormalised_basis, cv::Mat eigenvalues);
43 cv::Mat unnormalise_pca_basis(cv::Mat normalised_basis, cv::Mat eigenvalues);
44 
55 class PcaModel
56 {
57 public:
58  PcaModel() {}; // workaround for a VS2015 RC bug. Change to '=default' in RTM.
59 
72  PcaModel(cv::Mat mean, cv::Mat pca_basis, cv::Mat eigenvalues, std::vector<std::array<int, 3>> triangle_list) : mean(mean), normalised_pca_basis(pca_basis), eigenvalues(eigenvalues), triangle_list(triangle_list)
73  {
74  const auto seed = std::random_device()();
75  engine.seed(seed);
76  unnormalised_pca_basis = unnormalise_pca_basis(normalised_pca_basis, eigenvalues);
77  };
78 
85  {
86  // Note: we could assert(normalised_pca_basis.cols==unnormalised_pca_basis.cols)
87  return normalised_pca_basis.cols;
88  };
89 
98  int get_data_dimension() const
99  {
100  // Note: we could assert(normalised_pca_basis.rows==unnormalised_pca_basis.rows)
101  return normalised_pca_basis.rows;
102  };
103 
109  std::vector<std::array<int, 3>> get_triangle_list() const
110  {
111  return triangle_list;
112  };
113 
119  cv::Mat get_mean() const
120  {
121  return mean;
122  };
123 
130  cv::Vec4f get_mean_at_point(int vertex_index) const
131  {
132  vertex_index *= 3;
133  return cv::Vec4f(mean.at<float>(vertex_index), mean.at<float>(vertex_index + 1), mean.at<float>(vertex_index + 2), 1.0f);
134  };
135 
143  cv::Mat draw_sample(float sigma = 1.0f)
144  {
145  std::normal_distribution<float> distribution(0.0f, sigma); // this constructor takes the stddev
146 
147  std::vector<float> alphas(get_num_principal_components());
148 
149  for (auto&& a : alphas) {
150  a = distribution(engine);
151  }
152 
153  return draw_sample(alphas);
154  };
155 
164  cv::Mat draw_sample(std::vector<float> coefficients) const
165  {
166  // Fill the rest with zeros if not all coefficients are given:
167  if (coefficients.size() < get_num_principal_components()) {
168  coefficients.resize(get_num_principal_components());
169  }
170  cv::Mat alphas(coefficients);
171 
172  cv::Mat model_sample = mean + normalised_pca_basis * alphas;
173 
174  return model_sample;
175  };
176 
188  cv::Mat get_normalised_pca_basis() const
189  {
190  return normalised_pca_basis.clone();
191  };
192 
201  cv::Mat get_normalised_pca_basis(int vertex_id) const
202  {
203  vertex_id *= 3; // the basis is stored in the format [x y z x y z ...]
204  assert(vertex_id < get_data_dimension()); // Make sure the given vertex index isn't larger than the number of model vertices.
205  return normalised_pca_basis.rowRange(vertex_id, vertex_id + 3);
206  };
207 
219  {
220  return unnormalised_pca_basis.clone();
221  };
222 
230  cv::Mat get_unnormalised_pca_basis(int vertex_id) const
231  {
232  vertex_id *= 3; // the basis is stored in the format [x y z x y z ...]
233  assert(vertex_id < get_data_dimension()); // Make sure the given vertex index isn't larger than the number of model vertices.
234  return unnormalised_pca_basis.rowRange(vertex_id, vertex_id + 3);
235  };
236 
243  float get_eigenvalue(int index) const
244  {
245  // no assert - OpenCV checks ::at in debug builds
246  return eigenvalues.at<float>(index);
247  };
248 
249 private:
250  std::mt19937 engine;
251 
252  cv::Mat mean;
253  cv::Mat normalised_pca_basis;
254  cv::Mat unnormalised_pca_basis;
255  cv::Mat eigenvalues;
256 
257  std::vector<std::array<int, 3>> triangle_list;
258 
259  friend class cereal::access;
265  template<class Archive>
266  void serialize(Archive& archive)
267  {
268  archive(mean, normalised_pca_basis, unnormalised_pca_basis, eigenvalues, triangle_list);
269  // Note: If the files are too big, We could split this in save/load, only
270  // store one of the bases, and calculate the other one when loading.
271  };
272 };
273 
274 
285 inline cv::Mat normalise_pca_basis(cv::Mat unnormalised_basis, cv::Mat eigenvalues)
286 {
287  using cv::Mat;
288  Mat normalised_basis(unnormalised_basis.size(), unnormalised_basis.type()); // empty matrix with the same dimensions
289  Mat sqrt_of_eigenvalues = eigenvalues.clone();
290  for (int i = 0; i < eigenvalues.rows; ++i) {
291  sqrt_of_eigenvalues.at<float>(i) = std::sqrt(eigenvalues.at<float>(i));
292  }
293  // Normalise the basis: We multiply each eigenvector (i.e. each column) with the square root of its corresponding eigenvalue
294  for (int basis = 0; basis < unnormalised_basis.cols; ++basis) {
295  Mat normalised_eigenvector = unnormalised_basis.col(basis).mul(sqrt_of_eigenvalues.at<float>(basis));
296  normalised_eigenvector.copyTo(normalised_basis.col(basis));
297  }
298 
299  return normalised_basis;
300 };
301 
312 inline cv::Mat unnormalise_pca_basis(cv::Mat normalised_basis, cv::Mat eigenvalues)
313 {
314  using cv::Mat;
315  Mat unnormalised_basis(normalised_basis.size(), normalised_basis.type()); // empty matrix with the same dimensions
316  Mat one_over_sqrt_of_eigenvalues = eigenvalues.clone();
317  for (int i = 0; i < eigenvalues.rows; ++i) {
318  one_over_sqrt_of_eigenvalues.at<float>(i) = 1.0f / std::sqrt(eigenvalues.at<float>(i));
319  }
320  // De-normalise the basis: We multiply each eigenvector (i.e. each column) with 1 over the square root of its corresponding eigenvalue
321  for (int basis = 0; basis < normalised_basis.cols; ++basis) {
322  Mat unnormalised_eigenvector = normalised_basis.col(basis).mul(one_over_sqrt_of_eigenvalues.at<float>(basis));
323  unnormalised_eigenvector.copyTo(unnormalised_basis.col(basis));
324  }
325 
326  return unnormalised_basis;
327 };
328 
329  } /* namespace morphablemodel */
330 } /* namespace eos */
331 
332 #endif /* PCAMODEL_HPP_ */
This class represents a PCA-model that consists of:
Definition: PcaModel.hpp:55
cv::Mat get_normalised_pca_basis() const
Definition: PcaModel.hpp:188
cv::Mat draw_sample(float sigma=1.0f)
Definition: PcaModel.hpp:143
int get_num_principal_components() const
Definition: PcaModel.hpp:84
cv::Mat unnormalise_pca_basis(cv::Mat normalised_basis, cv::Mat eigenvalues)
Definition: PcaModel.hpp:312
int get_data_dimension() const
Definition: PcaModel.hpp:98
cv::Mat draw_sample(std::vector< float > coefficients) const
Definition: PcaModel.hpp:164
cv::Mat get_unnormalised_pca_basis(int vertex_id) const
Definition: PcaModel.hpp:230
std::vector< std::array< int, 3 > > get_triangle_list() const
Definition: PcaModel.hpp:109
float get_eigenvalue(int index) const
Definition: PcaModel.hpp:243
Namespace containing all of eos&#39;s 3D model fitting functionality.
cv::Mat normalise_pca_basis(cv::Mat unnormalised_basis, cv::Mat eigenvalues)
Definition: PcaModel.hpp:285
cv::Mat get_normalised_pca_basis(int vertex_id) const
Definition: PcaModel.hpp:201
cv::Mat get_unnormalised_pca_basis() const
Definition: PcaModel.hpp:218
PcaModel(cv::Mat mean, cv::Mat pca_basis, cv::Mat eigenvalues, std::vector< std::array< int, 3 >> triangle_list)
Definition: PcaModel.hpp:72
cv::Mat get_mean() const
Definition: PcaModel.hpp:119
cv::Vec4f get_mean_at_point(int vertex_index) const
Definition: PcaModel.hpp:130