Commit 99152f22 authored by Patrik Huber's avatar Patrik Huber

Moved all remaining functions to headers

parent 2b975f86
...@@ -42,12 +42,7 @@ else(Boost_FOUND) ...@@ -42,12 +42,7 @@ else(Boost_FOUND)
message(FATAL_ERROR "Boost not found") message(FATAL_ERROR "Boost not found")
endif() endif()
# Source and header files: # Header files:
set(SOURCES
src/eos/core/LandmarkMapper.cpp
src/eos/morphablemodel/io/cvssp.cpp
src/eos/render/utils.cpp
)
set(HEADERS set(HEADERS
include/eos/core/LandmarkMapper.hpp include/eos/core/LandmarkMapper.hpp
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#define BOOST_ALL_NO_LIB // Don't use the automatic library linking by boost with VS2010 (#pragma ...). Instead, we specify everything in cmake. #define BOOST_ALL_NO_LIB // Don't use the automatic library linking by boost with VS2010 (#pragma ...). Instead, we specify everything in cmake.
#endif #endif
#include "boost/filesystem/path.hpp" #include "boost/filesystem/path.hpp"
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/info_parser.hpp"
#include <string> #include <string>
#include <map> #include <map>
...@@ -52,8 +54,34 @@ public: ...@@ -52,8 +54,34 @@ public:
* Constructs a new landmark mapper from a mappings-file. * Constructs a new landmark mapper from a mappings-file.
* *
* @param[in] filename A file with landmark mappings. * @param[in] filename A file with landmark mappings.
* @throws runtime_error exception if there is an error
* loading the mappings from the file.
*/ */
LandmarkMapper(boost::filesystem::path filename); LandmarkMapper(boost::filesystem::path filename)
{
using std::string;
using boost::property_tree::ptree;
ptree configTree;
try {
boost::property_tree::info_parser::read_info(filename.string(), configTree);
}
catch (const boost::property_tree::ptree_error& error) {
throw std::runtime_error(string("LandmarkMapper: Error reading landmark-mappings file: ") + error.what());
}
try {
ptree ptLandmarkMappings = configTree.get_child("landmarkMappings");
for (auto&& mapping : ptLandmarkMappings) {
landmarkMappings.insert(make_pair(mapping.first, mapping.second.get_value<string>()));
}
}
catch (const boost::property_tree::ptree_error& error) {
throw std::runtime_error(string("LandmarkMapper: Error while parsing the mappings file: ") + error.what());
}
catch (const std::runtime_error& error) {
throw std::runtime_error(string("LandmarkMapper: Error while parsing the mappings file: ") + error.what());
}
};
/** /**
* Converts the given landmark name to the mapped name. * Converts the given landmark name to the mapped name.
...@@ -63,7 +91,26 @@ public: ...@@ -63,7 +91,26 @@ public:
* @throws out_of_range exception if there is no mapping * @throws out_of_range exception if there is no mapping
* for the given landmarkName. * for the given landmarkName.
*/ */
std::string convert(std::string landmarkName); std::string convert(std::string landmarkName) const
{
if (landmarkMappings.empty()) {
// perform identity mapping, i.e. return the input
return landmarkName;
}
else {
return landmarkMappings.at(landmarkName); // throws an out_of_range exception if landmarkName does not match the key of any element in the map
}
};
/**
* Returns the number of loaded landmark mappings.
*
* @return The number of landmark mappings.
*/
auto size() const
{
return landmarkMappings.size();
};
private: private:
std::map<std::string, std::string> landmarkMappings; ///< Mapping from one landmark name to a name in a different format. std::map<std::string, std::string> landmarkMappings; ///< Mapping from one landmark name to a name in a different format.
......
...@@ -33,13 +33,21 @@ ...@@ -33,13 +33,21 @@
#include "boost/filesystem/path.hpp" #include "boost/filesystem/path.hpp"
#include <vector> #include <vector>
#include <iostream>
namespace eos { namespace eos {
namespace morphablemodel { namespace morphablemodel {
/**
* Forward declarations
*/
std::vector<cv::Vec2f> loadIsomap(boost::filesystem::path isomapFile);
/** /**
* Load a shape or color model from a .scm file containing * Load a shape or color model from a .scm file containing
* a Morphable Model in the Surrey format. * a Morphable Model in the Surrey format. CVSSP's software
* internally trains and stores the model in this custom binary
* format and this class provides means to load them.
* *
* Note on multi-resolution models: The landmarks to vertex-id mapping is * Note on multi-resolution models: The landmarks to vertex-id mapping is
* always the same. The lowest resolution model has all the landmarks defined * always the same. The lowest resolution model has all the landmarks defined
...@@ -52,8 +60,171 @@ namespace eos { ...@@ -52,8 +60,171 @@ namespace eos {
* @param[in] modelFilename A binary .scm-file containing the model. * @param[in] modelFilename A binary .scm-file containing the model.
* @param[in] isomapFile An optional path to an isomap containing texture coordinates. * @param[in] isomapFile An optional path to an isomap containing texture coordinates.
* @return The Morphable Model loaded from the file. * @return The Morphable Model loaded from the file.
* @throws ...
*/ */
MorphableModel loadScmModel(boost::filesystem::path modelFilename, boost::filesystem::path isomapFile = boost::filesystem::path()); MorphableModel loadScmModel(boost::filesystem::path modelFilename, boost::filesystem::path isomapFile = boost::filesystem::path())
{
using cv::Mat;
if (sizeof(unsigned int) != 4) { // note/todo: maybe use uint32 or similar instead? Yep, but still we could encounter endianness-trouble.
std::cout << "Warning: We're reading 4 Bytes from the file but sizeof(unsigned int) != 4. Check the code/behaviour." << std::endl;
}
if (sizeof(double) != 8) {
std::cout << "Warning: We're reading 8 Bytes from the file but sizeof(double) != 8. Check the code/behaviour." << std::endl;
}
std::ifstream modelFile(modelFilename.string(), std::ios::binary);
if (!modelFile.is_open()) {
std::string msg("Unable to open model file: " + modelFilename.string());
std::cout << msg << std::endl;
throw std::runtime_error(msg);
}
// Reading the shape model
// Read (reference?) num triangles and vertices
unsigned int numVertices = 0;
unsigned int numTriangles = 0;
modelFile.read(reinterpret_cast<char*>(&numVertices), 4); // 1 char = 1 byte. uint32=4bytes. float64=8bytes.
modelFile.read(reinterpret_cast<char*>(&numTriangles), 4);
// Read triangles
std::vector<std::array<int, 3>> triangleList;
triangleList.resize(numTriangles);
unsigned int v0, v1, v2;
for (unsigned int i = 0; i < numTriangles; ++i) {
v0 = v1 = v2 = 0;
modelFile.read(reinterpret_cast<char*>(&v0), 4); // would be nice to pass a &vector and do it in one
modelFile.read(reinterpret_cast<char*>(&v1), 4); // go, but didn't work. Maybe a cv::Mat would work?
modelFile.read(reinterpret_cast<char*>(&v2), 4);
triangleList[i][0] = v0;
triangleList[i][1] = v1;
triangleList[i][2] = v2;
}
// Read number of rows and columns of the shape projection matrix (pcaBasis)
unsigned int numShapePcaCoeffs = 0;
unsigned int numShapeDims = 0; // dimension of the shape vector (3*numVertices)
modelFile.read(reinterpret_cast<char*>(&numShapePcaCoeffs), 4);
modelFile.read(reinterpret_cast<char*>(&numShapeDims), 4);
if (3 * numVertices != numShapeDims) {
std::cout << "Warning: Number of shape dimensions is not equal to three times the number of vertices. Something will probably go wrong during the loading." << std::endl;
}
// Read shape projection matrix
Mat unnormalisedPcaBasisShape(numShapeDims, numShapePcaCoeffs, CV_32FC1); // m x n (rows x cols) = numShapeDims x numShapePcaCoeffs
std::cout << "Loading shape PCA basis matrix with " << unnormalisedPcaBasisShape.rows << " rows and " << unnormalisedPcaBasisShape.cols << " cols." << std::endl;
for (unsigned int col = 0; col < numShapePcaCoeffs; ++col) {
for (unsigned int row = 0; row < numShapeDims; ++row) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
unnormalisedPcaBasisShape.at<float>(row, col) = static_cast<float>(var);
}
}
// Read mean shape vector
unsigned int numMean = 0; // dimension of the mean (3*numVertices)
modelFile.read(reinterpret_cast<char*>(&numMean), 4);
if (numMean != numShapeDims) {
std::cout << "Warning: Number of shape dimensions is not equal to the number of dimensions of the mean. Something will probably go wrong during the loading." << std::endl;
}
Mat meanShape(numMean, 1, CV_32FC1);
unsigned int counter = 0;
double vd0, vd1, vd2;
for (unsigned int i = 0; i < numMean / 3; ++i) {
vd0 = vd1 = vd2 = 0.0;
modelFile.read(reinterpret_cast<char*>(&vd0), 8);
modelFile.read(reinterpret_cast<char*>(&vd1), 8);
modelFile.read(reinterpret_cast<char*>(&vd2), 8);
meanShape.at<float>(counter, 0) = static_cast<float>(vd0);
++counter;
meanShape.at<float>(counter, 0) = static_cast<float>(vd1);
++counter;
meanShape.at<float>(counter, 0) = static_cast<float>(vd2);
++counter;
}
// Read shape eigenvalues
unsigned int numEigenValsShape = 0;
modelFile.read(reinterpret_cast<char*>(&numEigenValsShape), 4);
if (numEigenValsShape != numShapePcaCoeffs) {
std::cout << "Warning: Number of coefficients in the PCA basis matrix is not equal to the number of eigenvalues. Something will probably go wrong during the loading." << std::endl;
}
Mat eigenvaluesShape(numEigenValsShape, 1, CV_32FC1);
for (unsigned int i = 0; i < numEigenValsShape; ++i) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
eigenvaluesShape.at<float>(i, 0) = static_cast<float>(var);
}
// We read the unnormalised basis from the file. Now let's normalise it and store the normalised basis separately.
Mat normalisedPcaBasisShape = normalisePcaBasis(unnormalisedPcaBasisShape, eigenvaluesShape);
PcaModel shapeModel(meanShape, normalisedPcaBasisShape, eigenvaluesShape, triangleList);
// Reading the color model
// Read number of rows and columns of projection matrix
unsigned int numTexturePcaCoeffs = 0;
unsigned int numTextureDims = 0;
modelFile.read(reinterpret_cast<char*>(&numTexturePcaCoeffs), 4);
modelFile.read(reinterpret_cast<char*>(&numTextureDims), 4);
// Read color projection matrix
Mat unnormalisedPcaBasisColor(numTextureDims, numTexturePcaCoeffs, CV_32FC1);
std::cout << "Loading color PCA basis matrix with " << unnormalisedPcaBasisColor.rows << " rows and " << unnormalisedPcaBasisColor.cols << " cols." << std::endl;
for (unsigned int col = 0; col < numTexturePcaCoeffs; ++col) {
for (unsigned int row = 0; row < numTextureDims; ++row) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
unnormalisedPcaBasisColor.at<float>(row, col) = static_cast<float>(var);
}
}
// Read mean color vector
unsigned int numMeanColor = 0; // dimension of the mean (3*numVertices)
modelFile.read(reinterpret_cast<char*>(&numMeanColor), 4);
Mat meanColor(numMeanColor, 1, CV_32FC1);
counter = 0;
for (unsigned int i = 0; i < numMeanColor / 3; ++i) {
vd0 = vd1 = vd2 = 0.0;
modelFile.read(reinterpret_cast<char*>(&vd0), 8); // order in hdf5: RGB. Order in OCV: BGR. But order in vertex.color: RGB
modelFile.read(reinterpret_cast<char*>(&vd1), 8);
modelFile.read(reinterpret_cast<char*>(&vd2), 8);
meanColor.at<float>(counter, 0) = static_cast<float>(vd0);
++counter;
meanColor.at<float>(counter, 0) = static_cast<float>(vd1);
++counter;
meanColor.at<float>(counter, 0) = static_cast<float>(vd2);
++counter;
}
// Read color eigenvalues
unsigned int numEigenValsColor = 0;
modelFile.read(reinterpret_cast<char*>(&numEigenValsColor), 4);
Mat eigenvaluesColor(numEigenValsColor, 1, CV_32FC1);
for (unsigned int i = 0; i < numEigenValsColor; ++i) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
eigenvaluesColor.at<float>(i, 0) = static_cast<float>(var);
}
// We read the unnormalised basis from the file. Now let's normalise it and store the normalised basis separately.
Mat normalisedPcaBasisColor = normalisePcaBasis(unnormalisedPcaBasisColor, eigenvaluesColor);
PcaModel colorModel(meanColor, normalisedPcaBasisColor, eigenvaluesColor, triangleList);
modelFile.close();
// Load the isomap with texture coordinates if a filename has been given:
std::vector<cv::Vec2f> texCoords;
if (!isomapFile.empty()) {
texCoords = loadIsomap(isomapFile);
if (shapeModel.getDataDimension() / 3.0f != texCoords.size()) {
std::string errorMessage("Error, wrong number of texture coordinates. Don't have the same number of texcoords than the shape model has vertices.");
std::cout << errorMessage << std::endl;
throw std::runtime_error(errorMessage);
}
}
return MorphableModel(shapeModel, colorModel, texCoords);
};
/** /**
* Load a set of 2D texture coordinates pre-generated by the isomap algorithm. * Load a set of 2D texture coordinates pre-generated by the isomap algorithm.
...@@ -61,8 +232,42 @@ MorphableModel loadScmModel(boost::filesystem::path modelFilename, boost::filesy ...@@ -61,8 +232,42 @@ MorphableModel loadScmModel(boost::filesystem::path modelFilename, boost::filesy
* *
* @param[in] isomapFile Path to an isomap file containing texture coordinates. * @param[in] isomapFile Path to an isomap file containing texture coordinates.
* @return The 2D texture coordinates for every vertex. * @return The 2D texture coordinates for every vertex.
* @throws ...
*/ */
std::vector<cv::Vec2f> loadIsomap(boost::filesystem::path isomapFile); std::vector<cv::Vec2f> loadIsomap(boost::filesystem::path isomapFile)
{
using std::string;
std::vector<float> xCoords, yCoords;
string line;
std::ifstream myfile(isomapFile.string());
if (!myfile.is_open()) {
string logMessage("The isomap file could not be opened. Did you specify a correct filename? " + isomapFile.string());
throw std::runtime_error(logMessage);
}
else {
while (getline(myfile, line))
{
std::istringstream iss(line);
string x, y;
iss >> x >> y;
xCoords.push_back(std::stof(x));
yCoords.push_back(std::stof(y));
}
myfile.close();
}
// Process the coordinates: Find the min/max and rescale to [0, 1] x [0, 1]
auto minMaxX = std::minmax_element(begin(xCoords), end(xCoords)); // minMaxX is a pair, first=min, second=max
auto minMaxY = std::minmax_element(begin(yCoords), end(yCoords));
std::vector<cv::Vec2f> texCoords;
float divisorX = *minMaxX.second - *minMaxX.first;
float divisorY = *minMaxY.second - *minMaxY.first;
for (int i = 0; i < xCoords.size(); ++i) {
texCoords.push_back(cv::Vec2f((xCoords[i] - *minMaxX.first) / divisorX, 1.0f - (yCoords[i] - *minMaxY.first) / divisorY)); // We rescale to [0, 1] and at the same time flip the y-coords (because in the isomap, the coordinates are stored upside-down).
}
return texCoords;
};
} /* namespace morphablemodel */ } /* namespace morphablemodel */
} /* namespace eos */ } /* namespace eos */
......
...@@ -70,7 +70,13 @@ inline cv::Vec2f clip_to_screen_space(cv::Vec2f clip_coordinates, int screen_wid ...@@ -70,7 +70,13 @@ inline cv::Vec2f clip_to_screen_space(cv::Vec2f clip_coordinates, int screen_wid
* @param[in] screenHeight Height of the screen or window. * @param[in] screenHeight Height of the screen or window.
* @return A vector with x and y coordinates transformed to clip space. * @return A vector with x and y coordinates transformed to clip space.
*/ */
cv::Vec2f screenToClipSpace(cv::Vec2f screenCoordinates, int screenWidth, int screenHeight); inline cv::Vec2f screenToClipSpace(cv::Vec2f screenCoordinates, int screenWidth, int screenHeight)
{
float x_cs = screenCoordinates[0] / (screenWidth / 2.0f) - 1.0f;
float y_cs = screenCoordinates[1] / (screenHeight / 2.0f) - 1.0f;
y_cs *= -1.0f;
return cv::Vec2f(x_cs, y_cs);
};
} /* namespace render */ } /* namespace render */
} /* namespace eos */ } /* namespace eos */
......
/*
* Eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: src/eos/core/LandmarkMapper.cpp
*
* Copyright 2014, 2015 Patrik Huber
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "eos/core/LandmarkMapper.hpp"
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/info_parser.hpp"
#include <iostream>
using boost::property_tree::ptree;
using std::string;
namespace eos {
namespace core {
LandmarkMapper::LandmarkMapper(boost::filesystem::path filename)
{
ptree configTree;
try {
boost::property_tree::info_parser::read_info(filename.string(), configTree);
}
catch (const boost::property_tree::ptree_error& error) {
throw std::runtime_error(string("LandmarkMapper: Error reading landmark-mappings file: ") + error.what());
}
try {
ptree ptLandmarkMappings = configTree.get_child("landmarkMappings");
for (auto&& mapping : ptLandmarkMappings) {
landmarkMappings.insert(make_pair(mapping.first, mapping.second.get_value<string>()));
}
std::cout << "Loaded a list of " << landmarkMappings.size() << " landmark mappings." << std::endl;
}
catch (const boost::property_tree::ptree_error& error) {
throw std::runtime_error(string("LandmarkMapper: Error while parsing the mappings file: ") + error.what());
}
catch (const std::runtime_error& error) {
throw std::runtime_error(string("LandmarkMapper: Error while parsing the mappings file: ") + error.what());
}
}
string LandmarkMapper::convert(string landmarkName)
{
if (landmarkMappings.empty()) {
// perform identity mapping, i.e. return the input
return landmarkName;
}
else {
return landmarkMappings.at(landmarkName); // throws an out_of_range exception if landmarkName does not match the key of any element in the map
}
}
} /* namespace core */
} /* namespace eos */
/*
* Eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: src/eos/morphablemodel/io/cvssp.cpp
*
* Copyright 2014, 2015 Patrik Huber
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "eos/morphablemodel/io/cvssp.hpp"
#include "boost/lexical_cast.hpp"
#include "boost/algorithm/string.hpp"
#include <fstream>
#include <iostream>
namespace fs = boost::filesystem;
using cv::Mat;
using cv::Vec2f;
using cv::Vec3f;
using boost::lexical_cast;
using std::string;
using std::vector;
using std::array;
namespace eos {
namespace morphablemodel {
MorphableModel loadScmModel(fs::path modelFilename, fs::path isomapFile)
{
if (sizeof(unsigned int) != 4) { // note/todo: maybe use uint32 or similar instead? Yep, but still we could encounter endianness-trouble.
std::cout << "Warning: We're reading 4 Bytes from the file but sizeof(unsigned int) != 4. Check the code/behaviour." << std::endl;
}
if (sizeof(double) != 8) {
std::cout << "Warning: We're reading 8 Bytes from the file but sizeof(double) != 8. Check the code/behaviour." << std::endl;
}
std::ifstream modelFile(modelFilename.string(), std::ios::binary);
if (!modelFile.is_open()) {
std::string msg("Unable to open model file: " + modelFilename.string());
std::cout << msg << std::endl;
throw std::runtime_error(msg);
}
// Reading the shape model
// Read (reference?) num triangles and vertices
unsigned int numVertices = 0;
unsigned int numTriangles = 0;
modelFile.read(reinterpret_cast<char*>(&numVertices), 4); // 1 char = 1 byte. uint32=4bytes. float64=8bytes.
modelFile.read(reinterpret_cast<char*>(&numTriangles), 4);
// Read triangles
vector<array<int, 3>> triangleList;
triangleList.resize(numTriangles);
unsigned int v0, v1, v2;
for (unsigned int i=0; i < numTriangles; ++i) {
v0 = v1 = v2 = 0;
modelFile.read(reinterpret_cast<char*>(&v0), 4); // would be nice to pass a &vector and do it in one
modelFile.read(reinterpret_cast<char*>(&v1), 4); // go, but didn't work. Maybe a cv::Mat would work?
modelFile.read(reinterpret_cast<char*>(&v2), 4);
triangleList[i][0] = v0;
triangleList[i][1] = v1;
triangleList[i][2] = v2;
}
// Read number of rows and columns of the shape projection matrix (pcaBasis)
unsigned int numShapePcaCoeffs = 0;
unsigned int numShapeDims = 0; // dimension of the shape vector (3*numVertices)
modelFile.read(reinterpret_cast<char*>(&numShapePcaCoeffs), 4);
modelFile.read(reinterpret_cast<char*>(&numShapeDims), 4);
if (3 * numVertices != numShapeDims) {
std::cout << "Warning: Number of shape dimensions is not equal to three times the number of vertices. Something will probably go wrong during the loading." << std::endl;
}
// Read shape projection matrix
Mat unnormalisedPcaBasisShape(numShapeDims, numShapePcaCoeffs, CV_32FC1); // m x n (rows x cols) = numShapeDims x numShapePcaCoeffs
std::cout << "Loading shape PCA basis matrix with " << unnormalisedPcaBasisShape.rows << " rows and " << unnormalisedPcaBasisShape.cols << " cols." << std::endl;
for (unsigned int col = 0; col < numShapePcaCoeffs; ++col) {
for (unsigned int row = 0; row < numShapeDims; ++row) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
unnormalisedPcaBasisShape.at<float>(row, col) = static_cast<float>(var);
}
}
// Read mean shape vector
unsigned int numMean = 0; // dimension of the mean (3*numVertices)
modelFile.read(reinterpret_cast<char*>(&numMean), 4);
if (numMean != numShapeDims) {
std::cout << "Warning: Number of shape dimensions is not equal to the number of dimensions of the mean. Something will probably go wrong during the loading." << std::endl;
}
Mat meanShape(numMean, 1, CV_32FC1);
unsigned int counter = 0;
double vd0, vd1, vd2;
for (unsigned int i=0; i < numMean/3; ++i) {
vd0 = vd1 = vd2 = 0.0;
modelFile.read(reinterpret_cast<char*>(&vd0), 8);
modelFile.read(reinterpret_cast<char*>(&vd1), 8);
modelFile.read(reinterpret_cast<char*>(&vd2), 8);
meanShape.at<float>(counter, 0) = static_cast<float>(vd0);
++counter;
meanShape.at<float>(counter, 0) = static_cast<float>(vd1);
++counter;
meanShape.at<float>(counter, 0) = static_cast<float>(vd2);
++counter;
}
// Read shape eigenvalues
unsigned int numEigenValsShape = 0;
modelFile.read(reinterpret_cast<char*>(&numEigenValsShape), 4);
if (numEigenValsShape != numShapePcaCoeffs) {
std::cout << "Warning: Number of coefficients in the PCA basis matrix is not equal to the number of eigenvalues. Something will probably go wrong during the loading." << std::endl;
}
Mat eigenvaluesShape(numEigenValsShape, 1, CV_32FC1);
for (unsigned int i=0; i < numEigenValsShape; ++i) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
eigenvaluesShape.at<float>(i, 0) = static_cast<float>(var);
}
// We read the unnormalised basis from the file. Now let's normalise it and store the normalised basis separately.
Mat normalisedPcaBasisShape = normalisePcaBasis(unnormalisedPcaBasisShape, eigenvaluesShape);
PcaModel shapeModel(meanShape, normalisedPcaBasisShape, eigenvaluesShape, triangleList);
// Reading the color model
// Read number of rows and columns of projection matrix
unsigned int numTexturePcaCoeffs = 0;
unsigned int numTextureDims = 0;
modelFile.read(reinterpret_cast<char*>(&numTexturePcaCoeffs), 4);
modelFile.read(reinterpret_cast<char*>(&numTextureDims), 4);
// Read color projection matrix
Mat unnormalisedPcaBasisColor(numTextureDims, numTexturePcaCoeffs, CV_32FC1);
std::cout << "Loading color PCA basis matrix with " << unnormalisedPcaBasisColor.rows << " rows and " << unnormalisedPcaBasisColor.cols << " cols." << std::endl;
for (unsigned int col = 0; col < numTexturePcaCoeffs; ++col) {
for (unsigned int row = 0; row < numTextureDims; ++row) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
unnormalisedPcaBasisColor.at<float>(row, col) = static_cast<float>(var);
}
}
// Read mean color vector
unsigned int numMeanColor = 0; // dimension of the mean (3*numVertices)
modelFile.read(reinterpret_cast<char*>(&numMeanColor), 4);
Mat meanColor(numMeanColor, 1, CV_32FC1);
counter = 0;
for (unsigned int i=0; i < numMeanColor/3; ++i) {
vd0 = vd1 = vd2 = 0.0;
modelFile.read(reinterpret_cast<char*>(&vd0), 8); // order in hdf5: RGB. Order in OCV: BGR. But order in vertex.color: RGB
modelFile.read(reinterpret_cast<char*>(&vd1), 8);
modelFile.read(reinterpret_cast<char*>(&vd2), 8);
meanColor.at<float>(counter, 0) = static_cast<float>(vd0);
++counter;
meanColor.at<float>(counter, 0) = static_cast<float>(vd1);
++counter;
meanColor.at<float>(counter, 0) = static_cast<float>(vd2);
++counter;
}
// Read color eigenvalues
unsigned int numEigenValsColor = 0;
modelFile.read(reinterpret_cast<char*>(&numEigenValsColor), 4);
Mat eigenvaluesColor(numEigenValsColor, 1, CV_32FC1);
for (unsigned int i=0; i < numEigenValsColor; ++i) {
double var = 0.0;
modelFile.read(reinterpret_cast<char*>(&var), 8);
eigenvaluesColor.at<float>(i, 0) = static_cast<float>(var);
}
// We read the unnormalised basis from the file. Now let's normalise it and store the normalised basis separately.
Mat normalisedPcaBasisColor = normalisePcaBasis(unnormalisedPcaBasisColor, eigenvaluesColor);
PcaModel colorModel(meanColor, normalisedPcaBasisColor, eigenvaluesColor, triangleList);
modelFile.close();
// Load the isomap with texture coordinates if a filename has been given:
vector<Vec2f> texCoords;
if (!isomapFile.empty()) {
texCoords = loadIsomap(isomapFile);
if (shapeModel.getDataDimension() / 3.0f != texCoords.size()) {
std::string errorMessage("Error, wrong number of texture coordinates. Don't have the same number of texcoords than the shape model has vertices.");
std::cout << errorMessage << std::endl;
throw std::runtime_error(errorMessage);
}
}
return MorphableModel(shapeModel, colorModel, texCoords);
}
vector<Vec2f> loadIsomap(fs::path isomapFile)
{
vector<float> xCoords, yCoords;
string line;
std::ifstream myfile(isomapFile.string());
if (!myfile.is_open()) {
string logMessage("The Isomap file could not be opened. Did you specify a correct filename? " + isomapFile.string());
std::cout << logMessage << std::endl;
throw std::runtime_error(logMessage);
}
else {
while (getline(myfile, line))
{
std::istringstream iss(line);
string x, y;
iss >> x >> y;
xCoords.push_back(lexical_cast<float>(x));
yCoords.push_back(lexical_cast<float>(y));
}
myfile.close();
}
// Process the coordinates: Find the min/max and rescale to [0, 1] x [0, 1]
auto minMaxX = std::minmax_element(begin(xCoords), end(xCoords)); // minMaxX is a pair, first=min, second=max
auto minMaxY = std::minmax_element(begin(yCoords), end(yCoords));
vector<Vec2f> texCoords;
float divisorX = *minMaxX.second - *minMaxX.first;
float divisorY = *minMaxY.second - *minMaxY.first;
for (int i = 0; i < xCoords.size(); ++i) {
texCoords.push_back(Vec2f((xCoords[i] - *minMaxX.first) / divisorX, 1.0f - (yCoords[i] - *minMaxY.first) / divisorY)); // We rescale to [0, 1] and at the same time flip the y-coords (because in the isomap, the coordinates are stored upside-down).
}
return texCoords;
}
} /* namespace morphablemodel */
} /* namespace eos */
/*
* Eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: src/eos/render/Mesh.cpp
*
* Copyright 2014, 2015 Patrik Huber
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: src/eos/render/utils.cpp
*
* Copyright 2014, 2015 Patrik Huber
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "eos/render/utils.hpp"
using cv::Vec2f;
namespace eos {
namespace render {
Vec2f screenToClipSpace(Vec2f screenCoordinates, int screenWidth, int screenHeight)
{
float x_cs = screenCoordinates[0] / (screenWidth / 2.0f) - 1.0f;
float y_cs = screenCoordinates[1] / (screenHeight / 2.0f) - 1.0f;
y_cs *= -1.0f;
return Vec2f(x_cs, y_cs);
}
} /* namespace render */
} /* 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