eos 1.4.0
Loading...
Searching...
No Matches
cvssp.hpp
1/*
2 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 *
4 * File: include/eos/morphablemodel/io/cvssp.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 EOS_IO_CVSSP_HPP
23#define EOS_IO_CVSSP_HPP
24
25#include "eos/morphablemodel/MorphableModel.hpp"
26#include "eos/cpp17/optional.hpp"
27
28#include "Eigen/Core"
29
30#include <array>
31#include <iostream>
32#include <fstream>
33#include <string>
34#include <sstream>
35#include <vector>
36
37namespace eos {
38namespace morphablemodel {
39
40// Forward declaration
41std::vector<std::array<double, 2>> load_isomap(std::string isomap_file);
42
64inline MorphableModel load_scm_model(std::string model_filename,
65 cpp17::optional<std::string> isomap_file = cpp17::nullopt)
66{
67 using Eigen::MatrixXf;
68 using Eigen::VectorXf;
69
70 if (sizeof(unsigned int) != 4) // Note: maybe use uint32 or similar instead? Yep, but still we could
71 // encounter endianness-trouble.
72 {
73 std::cout << "Warning: We're reading 4 Bytes from the file but sizeof(unsigned int) != 4. Check the "
74 "code/behaviour."
75 << std::endl;
76 }
77 if (sizeof(double) != 8)
78 {
79 std::cout << "Warning: We're reading 8 Bytes from the file but sizeof(double) != 8. Check the "
80 "code/behaviour."
81 << std::endl;
82 }
83
84 std::ifstream model_file(model_filename, std::ios::binary);
85 if (!model_file)
86 {
87 const std::string msg("Unable to open model file: " + model_filename);
88 std::cout << msg << std::endl;
89 throw std::runtime_error(msg);
90 }
91
92 // Reading the shape model
93 // Read (reference?) num triangles and vertices
94 unsigned int num_vertices = 0;
95 unsigned int num_triangles = 0;
96 model_file.read(reinterpret_cast<char*>(&num_vertices),
97 4); // 1 char = 1 byte. uint32=4bytes. float64=8bytes.
98 model_file.read(reinterpret_cast<char*>(&num_triangles), 4);
99
100 // Read triangles
101 std::vector<std::array<int, 3>> triangle_list;
102
103 triangle_list.resize(num_triangles);
104 unsigned int v0, v1, v2;
105 for (unsigned int i = 0; i < num_triangles; ++i)
106 {
107 v0 = v1 = v2 = 0;
108 model_file.read(reinterpret_cast<char*>(&v0), 4); // would be nice to pass a &vector and do it in one
109 model_file.read(reinterpret_cast<char*>(&v1), 4); // go, but didn't work.
110 model_file.read(reinterpret_cast<char*>(&v2), 4);
111 triangle_list[i][0] = v0;
112 triangle_list[i][1] = v1;
113 triangle_list[i][2] = v2;
114 }
115
116 // Read number of rows and columns of the shape projection matrix (pcaBasis)
117 unsigned int num_shape_pca_coeffs = 0;
118 unsigned int num_shape_dims = 0; // dimension of the shape vector (3*num_vertices)
119 model_file.read(reinterpret_cast<char*>(&num_shape_pca_coeffs), 4);
120 model_file.read(reinterpret_cast<char*>(&num_shape_dims), 4);
121
122 if (3 * num_vertices != num_shape_dims)
123 {
124 std::cout << "Warning: Number of shape dimensions is not equal to three times the number of "
125 "vertices. Something will probably go wrong during the loading."
126 << std::endl;
127 }
128
129 // Read shape projection matrix
130 MatrixXf orthonormal_pca_basis_shape(
131 num_shape_dims, num_shape_pca_coeffs); // m x n (rows x cols) = num_shape_dims x num_shape_pca_coeffs
132 std::cout << "Loading shape PCA basis matrix with " << orthonormal_pca_basis_shape.rows() << " rows and "
133 << orthonormal_pca_basis_shape.cols() << " cols." << std::endl;
134 for (unsigned int col = 0; col < num_shape_pca_coeffs; ++col)
135 {
136 for (unsigned int row = 0; row < num_shape_dims; ++row)
137 {
138 double var = 0.0;
139 model_file.read(reinterpret_cast<char*>(&var), 8);
140 orthonormal_pca_basis_shape(row, col) = static_cast<float>(var);
141 }
142 }
143
144 // Read mean shape vector
145 unsigned int mean_dims = 0; // dimension of the mean (3*num_vertices)
146 model_file.read(reinterpret_cast<char*>(&mean_dims), 4);
147 if (mean_dims != num_shape_dims)
148 {
149 std::cout << "Warning: Number of shape dimensions is not equal to the number of dimensions of the "
150 "mean. Something will probably go wrong during the loading."
151 << std::endl;
152 }
153 VectorXf mean_shape(mean_dims);
154 unsigned int counter = 0;
155 double vd0, vd1, vd2;
156 for (unsigned int i = 0; i < mean_dims / 3; ++i)
157 {
158 vd0 = vd1 = vd2 = 0.0;
159 model_file.read(reinterpret_cast<char*>(&vd0), 8);
160 model_file.read(reinterpret_cast<char*>(&vd1), 8);
161 model_file.read(reinterpret_cast<char*>(&vd2), 8);
162 mean_shape(counter) = static_cast<float>(vd0);
163 ++counter;
164 mean_shape(counter) = static_cast<float>(vd1);
165 ++counter;
166 mean_shape(counter) = static_cast<float>(vd2);
167 ++counter;
168 }
169
170 // Read shape eigenvalues
171 unsigned int num_eigenvals_shape = 0;
172 model_file.read(reinterpret_cast<char*>(&num_eigenvals_shape), 4);
173 if (num_eigenvals_shape != num_shape_pca_coeffs)
174 {
175 std::cout << "Warning: Number of coefficients in the PCA basis matrix is not equal to the number of "
176 "eigenvalues. Something will probably go wrong during the loading."
177 << std::endl;
178 }
179 VectorXf eigenvalues_shape(num_eigenvals_shape);
180 for (unsigned int i = 0; i < num_eigenvals_shape; ++i)
181 {
182 double var = 0.0;
183 model_file.read(reinterpret_cast<char*>(&var), 8);
184 eigenvalues_shape(i) = static_cast<float>(var);
185 }
186
187 const PcaModel shape_model(mean_shape, orthonormal_pca_basis_shape, eigenvalues_shape, triangle_list);
188
189 // Reading the color model
190 // Read number of rows and columns of projection matrix
191 unsigned int num_color_pca_coeffs = 0;
192 unsigned int num_color_dims = 0;
193 model_file.read(reinterpret_cast<char*>(&num_color_pca_coeffs), 4);
194 model_file.read(reinterpret_cast<char*>(&num_color_dims), 4);
195 // Read color projection matrix
196 MatrixXf orthonormal_pca_basis_color(num_color_dims, num_color_pca_coeffs);
197 std::cout << "Loading color PCA basis matrix with " << orthonormal_pca_basis_color.rows() << " rows and "
198 << orthonormal_pca_basis_color.cols() << " cols." << std::endl;
199 for (unsigned int col = 0; col < num_color_pca_coeffs; ++col)
200 {
201 for (unsigned int row = 0; row < num_color_dims; ++row)
202 {
203 double var = 0.0;
204 model_file.read(reinterpret_cast<char*>(&var), 8);
205 orthonormal_pca_basis_color(row, col) = static_cast<float>(var);
206 }
207 }
208
209 // Read mean color vector
210 unsigned int color_mean_dims = 0; // dimension of the mean (3*num_vertices)
211 model_file.read(reinterpret_cast<char*>(&color_mean_dims), 4);
212 VectorXf mean_color(color_mean_dims);
213 counter = 0;
214 for (unsigned int i = 0; i < color_mean_dims / 3; ++i)
215 {
216 vd0 = vd1 = vd2 = 0.0;
217 model_file.read(reinterpret_cast<char*>(&vd0),
218 8); // order in hdf5: RGB. Order in OCV: BGR. But order in vertex.color: RGB
219 model_file.read(reinterpret_cast<char*>(&vd1), 8);
220 model_file.read(reinterpret_cast<char*>(&vd2), 8);
221 mean_color(counter) = static_cast<float>(vd0);
222 ++counter;
223 mean_color(counter) = static_cast<float>(vd1);
224 ++counter;
225 mean_color(counter) = static_cast<float>(vd2);
226 ++counter;
227 }
228
229 // Read color eigenvalues
230 unsigned int num_eigenvals_color = 0;
231 model_file.read(reinterpret_cast<char*>(&num_eigenvals_color), 4);
232 VectorXf eigenvalues_color(num_eigenvals_color);
233 for (unsigned int i = 0; i < num_eigenvals_color; ++i)
234 {
235 double var = 0.0;
236 model_file.read(reinterpret_cast<char*>(&var), 8);
237 eigenvalues_color(i) = static_cast<float>(var);
238 }
239
240 const PcaModel color_model(mean_color, orthonormal_pca_basis_color, eigenvalues_color, triangle_list);
241
242 model_file.close();
243
244 // Load the isomap with texture coordinates if a filename has been given:
245 std::vector<std::array<double, 2>> tex_coords;
246 if (isomap_file)
247 {
248 tex_coords = load_isomap(isomap_file.value());
249 if (shape_model.get_data_dimension() / 3.0f != tex_coords.size())
250 {
251 const std::string error_msg("Error, wrong number of texture coordinates. Don't have the same "
252 "number of texcoords than the shape model has vertices.");
253 std::cout << error_msg << std::endl;
254 throw std::runtime_error(error_msg);
255 }
256 }
257
258 return MorphableModel(shape_model, color_model, cpp17::nullopt, tex_coords);
259};
260
269inline std::vector<std::array<double, 2>> load_isomap(std::string isomap_file)
270{
271 using std::string;
272 std::vector<float> x_coords, y_coords;
273 string line;
274 std::ifstream file(isomap_file);
275 if (!file)
276 {
277 const string error_msg("The isomap file could not be opened. Did you specify a correct filename? " +
278 isomap_file);
279 throw std::runtime_error(error_msg);
280 } else
281 {
282 while (getline(file, line))
283 {
284 std::istringstream iss(line);
285 string x, y;
286 iss >> x >> y;
287 x_coords.push_back(std::stof(x));
288 y_coords.push_back(std::stof(y));
289 }
290 file.close();
291 }
292 // Process the coordinates: Find the min/max and rescale to [0, 1] x [0, 1]
293 const auto min_max_x =
294 std::minmax_element(begin(x_coords), end(x_coords)); // min_max_x is a pair, first=min, second=max
295 const auto min_max_y = std::minmax_element(begin(y_coords), end(y_coords));
296
297 std::vector<std::array<double, 2>> tex_coords;
298 const float divisor_x = *min_max_x.second - *min_max_x.first;
299 const float divisor_y = *min_max_y.second - *min_max_y.first;
300 for (int i = 0; i < x_coords.size(); ++i)
301 {
302 tex_coords.push_back(std::array<double, 2>{
303 (x_coords[i] - *min_max_x.first) / divisor_x,
304 1.0f - (y_coords[i] - *min_max_y.first) /
305 divisor_y}); // We rescale to [0, 1] and at the same time flip the y-coords (because in
306 // the isomap, the coordinates are stored upside-down).
307 }
308
309 return tex_coords;
310};
311
312} /* namespace morphablemodel */
313} /* namespace eos */
314
315#endif /* EOS_IO_CVSSP_HPP */
A class representing a 3D Morphable Model, consisting of a shape- and colour (albedo) PCA model.
Definition: MorphableModel.hpp:73
This class represents a PCA-model that consists of:
Definition: PcaModel.hpp:59
int get_data_dimension() const
Definition: PcaModel.hpp:102
std::vector< std::array< double, 2 > > load_isomap(std::string isomap_file)
Definition: cvssp.hpp:269
MorphableModel load_scm_model(std::string model_filename, cpp17::optional< std::string > isomap_file=cpp17::nullopt)
Definition: cvssp.hpp:64
Namespace containing all of eos's 3D model fitting functionality.