Commit 482b13b3 authored by Richard Torenvliet's avatar Richard Torenvliet

Add some experiments for using the BFM

parent 5116af65
# todo-list:
# 1. Create obj. file.
# 2. Create isomap of 3D object.
# https://nl.mathworks.com/matlabcentral/fileexchange/5355-toolbox-graph/content/toolbox_graph/toolbox/isomap.m
# keys BFM model:
# 'segbin',
# '__version__',
# 'texEV',
# 'shapeMU',
# 'texMU',
# '__header__',
# '__globals__',
# 'shapeEV',
# 'tl',
# 'segMM',
# 'shapePC',
# 'texPC',
# 'segMB'
import scipy.io as io
def load_bfm_model():
print('BFM: Loading model file...')
return io.loadmat('../01_MorphableModel.mat')
def load_bfm_attributes():
print('BFM: Loading attributes file...')
return io.loadmat('../04_attributes.mat')
def output_shape_and_texture(shape, texture, f):
"""
Write shape in .obj format
Args:
shape(list): [[x_0, y_0, z_0], ..., [x_n, y_n, z_n]]
f(filepointer): pointer to .obj file
"""
shape = shape.T[0]
texture = texture.T[0]
for i in range(0, len(shape), 3):
f.write('v {} {} {} {} {} {}\n'.format(
shape[i], shape[i + 1], shape[i + 2],
int(texture[i]), int(texture[i + 1]), int(texture[i + 2])
)
)
def output_triangles(triangles, f):
"""
Write shape in .obj format
Args:
shape(list): [[x_0, y_0, z_0], ..., [x_n, y_n, z_n]]
f(filepointer): pointer to .obj file
"""
for i in range(0, len(triangles)):
f.write('f {} {} {}\n'.format(
triangles[i][2],
triangles[i][1],
triangles[i][0]
))
def save_to_obj(shape, texture, triangles, filename):
with open(filename, 'w') as f:
output_shape_and_texture(shape, texture, f)
output_triangles(triangles, f)
def main():
dmm = load_bfm_model()
with open('output_model.obj', 'w') as f:
output_shape_and_texture(dmm['shapeMU'], dmm['texMU'], f)
output_triangles(dmm['tl'], f)
if __name__ == '__main__':
main()
import numpy as np
from bfm_to_obj import load_bfm_model, save_to_obj, load_bfm_attributes
def gen_random_shape(mean, Vt, eigenvalues, alpha):
# generate influences eigenvalues, with a random sample
# of a normal distribution.
infl_eigenvalues = alpha * eigenvalues
random_face = mean + np.dot(Vt, infl_eigenvalues)
return random_face
def gen_random_texture(mean, Vt, eigenvalues, betha):
# generate influences eigenvalues, with a random sample
# of a normal distribution.
infl_eigenvalues = betha * eigenvalues
random_face = mean + np.dot(Vt, infl_eigenvalues)
return random_face
def gender_face_tex(attr, betha, n=5):
"""
Influence gender.
Args:
attr(attributes model) - see BFM documentation
alpha(ndarray) - random range of floats generates with np.randn for
example with dim of the eigenvalues of the shape
n(integer) - amount of influence
Notes:
Higher n is more male.
Lower n is more female.
Minus **n** is allowed.
"""
print 'tex', len(betha)
return betha + n * attr['gender_tex'][:len(betha)]
def age_face_tex(attr, betha, n=50):
"""
Influence age.
Args:
attr(attributes model) - see BFM documentation.
alpha(ndarray) - random range of floats generates with np.randn for
example with dim of the eigenvalues of the shape.
n(integer) - amount of influence.
Notes:
Higher n is older.
Lower n is younger.
Minus **n** is allowed.
"""
return betha + n * attr['age_tex'][:199]
def weight_face_tex(attr, betha, n=30):
"""
Influence weight.
Args:
attr(attributes model) - see BFM documentation.
alpha(ndarray) - random range of floats generates with np.randn for
example with dim of the eigenvalues of the shape.
n(integer) - amount of influence
Notes:
Higher n is fatter.
Lower n is thinner.
Minus **n** is allowed.
"""
return betha + n * attr['weight_tex'][:199]
def gender_face_shape(attr, alpha, n=5):
"""
Influence gender.
Args:
attr(attributes model) - see BFM documentation
alpha(ndarray) - random range of floats generates with np.randn for
example with dim of the eigenvalues of the shape
n(integer) - amount of influence
Notes:
Higher n is more male.
Lower n is more female.
Minus **n** is allowed.
"""
return alpha + n * attr['gender_shape'][:199]
def age_face_shape(attr, alpha, n=50):
"""
Influence age.
Args:
attr(attributes model) - see BFM documentation.
alpha(ndarray) - random range of floats generates with np.randn for
example with dim of the eigenvalues of the shape.
n(integer) - amount of influence.
Notes:
Higher n is older.
Lower n is younger.
Minus **n** is allowed.
"""
return alpha + n * attr['age_shape'][:199]
def weight_face_shape(attr, alpha, n=30):
"""
Influence weight.
Args:
attr(attributes model) - see BFM documentation.
alpha(ndarray) - random range of floats generates with np.randn for
example with dim of the eigenvalues of the shape.
n(integer) - amount of influence
Notes:
Higher n is fatter.
Lower n is thinner.
Minus **n** is allowed.
"""
return alpha + n * attr['weight_shape'][:199]
def gen_random_shape_coef(attr, dim):
alpha = np.random.randn(dim, 1)
alpha = age_face_shape(attr, alpha, 1)
alpha = gender_face_shape(attr, alpha, -5)
alpha = weight_face_shape(attr, alpha, -10)
return alpha
def gen_random_tex_coef(attr, dim):
betha = np.random.randn(dim, 1)
betha = age_face_tex(attr, betha, 1)
betha = gender_face_tex(attr, betha, -5)
betha = weight_face_tex(attr, betha, -10)
return betha
def main():
dmm = load_bfm_model()
attr = load_bfm_attributes()
Vt_shape = dmm['shapePC']
mean_shape = dmm['shapeMU']
eigenv_shape = dmm['shapeEV']
alpha = gen_random_shape_coef(attr, eigenv_shape.shape[0])
random_face_shape = gen_random_shape(
mean_shape, Vt_shape, eigenv_shape, alpha
)
Vt_tex = dmm['texPC']
mean_tex = dmm['texMU']
eigenv_tex = dmm['texEV']
betha = gen_random_tex_coef(attr, eigenv_tex.shape[0])
random_face_texture = gen_random_texture(
mean_tex, Vt_tex, eigenv_tex, betha
)
save_to_obj(
random_face_shape,
random_face_texture,
dmm['tl'], 'random_out_1.obj'
)
if __name__ == '__main__':
main()
#include <pybind11/pybind11.h>
namespace py = pybind11;
int add(int i, int j) {
return i + j;
}
/*
* eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: examples/fit-model.cpp
*
* Copyright 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/Landmark.hpp" #include "eos/core/Landmark.hpp"
#include "eos/core/LandmarkMapper.hpp" #include "eos/core/LandmarkMapper.hpp"
#include "eos/fitting/orthographic_camera_estimation_linear.hpp" #include "eos/fitting/orthographic_camera_estimation_linear.hpp"
...@@ -43,7 +15,10 @@ int add(int i, int j) { ...@@ -43,7 +15,10 @@ int add(int i, int j) {
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
using namespace eos; using namespace eos;
namespace po = boost::program_options; namespace po = boost::program_options;
...@@ -60,6 +35,128 @@ using std::endl; ...@@ -60,6 +35,128 @@ using std::endl;
using std::vector; using std::vector;
using std::string; using std::string;
/**
* Print colored image in grayscale
*/
void print_image(uint32_t *image, py::buffer_info *image_info) {
size_t width = image_info->shape[1];
size_t height = image_info->shape[0];
size_t color = image_info->shape[2];
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
auto B = image[y * width + x + 0];
auto G = image[y * width + x + 1];
auto R = image[y * width + x + 2];
std::cout << (int)((B + G + R) / 3.0) << " ";
}
std::cout << std::endl;
}
}
float add(py::array_t<uint32_t> image_input,
py::array_t<float> input_points) {
auto image_info = image_input.request();
auto points_info = input_points.request();
uint32_t *image = reinterpret_cast<uint32_t *>(image_info.ptr);
float *points = reinterpret_cast<float *>(points_info.ptr);
//std::cout << image_info.shape << std::endl;
std::cout << image_info.itemsize << std::endl;
std::cout << image_info.ndim << std::endl;
std::cout << image_info.format << std::endl;
std::cout << image_info.shape[0] << std::endl;
std::cout << image_info.shape[1] << std::endl;
print_image(image, &image_info);
return 1.0;
}
void fit(Mat image, Vec2f landmarks) {
std::cout << image.at<uint32_t>(0,0) << std::endl;
//LandmarkCollection<cv::Vec2f> landmarks;
}
// Mat image = cv::imread(imagefile.string());
// LandmarkCollection<cv::Vec2f> landmarks;
// try {
// landmarks = read_pts_landmarks(landmarksfile.string());
// }
// catch (const std::runtime_error& e) {
// cout << "Error reading the landmarks: " << e.what() << endl;
// return EXIT_FAILURE;
// }
// morphablemodel::MorphableModel morphable_model;
// try {
// morphable_model = morphablemodel::load_model(modelfile.string());
// }
// catch (const std::runtime_error& e) {
// cout << "Error loading the Morphable Model: " << e.what() << endl;
// return EXIT_FAILURE;
// }
// core::LandmarkMapper landmark_mapper = mappingsfile.empty() ? core::LandmarkMapper() : core::LandmarkMapper(mappingsfile);
//
// // Draw the loaded landmarks:
// Mat outimg = image.clone();
// for (auto&& lm : landmarks) {
// cv::rectangle(outimg, cv::Point2f(lm.coordinates[0] - 2.0f, lm.coordinates[1] - 2.0f), cv::Point2f(lm.coordinates[0] + 2.0f, lm.coordinates[1] + 2.0f), { 255, 0, 0 });
// }
//
// // These will be the final 2D and 3D points used for the fitting:
// vector<Vec4f> model_points; // the points in the 3D shape model
// vector<int> vertex_indices; // their vertex indices
// vector<Vec2f> image_points; // the corresponding 2D landmark points
//
// // Sub-select all the landmarks which we have a mapping for (i.e. that are defined in the 3DMM):
// for (int i = 0; i < landmarks.size(); ++i) {
// auto converted_name = landmark_mapper.convert(landmarks[i].name);
// if (!converted_name) { // no mapping defined for the current landmark
// continue;
// }
// int vertex_idx = std::stoi(converted_name.get());
// Vec4f vertex = morphable_model.get_shape_model().get_mean_at_point(vertex_idx);
// model_points.emplace_back(vertex);
// vertex_indices.emplace_back(vertex_idx);
// image_points.emplace_back(landmarks[i].coordinates);
// }
//
// // Estimate the camera (pose) from the 2D - 3D point correspondences
// fitting::ScaledOrthoProjectionParameters pose = 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:
// float yaw_angle = glm::degrees(glm::yaw(rendering_params.get_rotation()));
// // and similarly for pitch and roll.
//
// // Estimate the shape coefficients by fitting the shape to the landmarks:
// Mat affine_from_ortho = fitting::get_3x4_affine_camera_matrix(rendering_params, image.cols, image.rows);
// vector<float> fitted_coeffs = fitting::fit_shape_to_landmarks_linear(morphable_model, affine_from_ortho, image_points, vertex_indices);
//
// // Obtain the full mesh with the estimated coefficients:
// render::Mesh mesh = morphable_model.draw_sample(fitted_coeffs, vector<float>());
//
// // Extract the texture from the image using given mesh and camera parameters:
// Mat isomap = render::extract_texture(mesh, affine_from_ortho, image);
//
// // Save the mesh as textured obj:
// outputfile += fs::path(".obj");
// render::write_textured_obj(mesh, outputfile.string());
//
// // And save the isomap:
// outputfile.replace_extension(".isomap.png");
// cv::imwrite(outputfile.string(), isomap);
//
// cout << "Finished fitting and wrote result mesh and isomap to files with basename " << outputfile.stem().stem() << "." << endl;
//
// return EXIT_SUCCESS;
//}
/** /**
* This app demonstrates estimation of the camera and fitting of the shape * This app demonstrates estimation of the camera and fitting of the shape
......
import cv2 import cv2
import numpy as np
import eos
import pca as pca import pca as pca
from settings import logger from settings import logger
...@@ -49,7 +51,12 @@ def fit_model(): ...@@ -49,7 +51,12 @@ def fit_model():
input_points = dataset_module.factory(filename=image_filename) input_points = dataset_module.factory(filename=image_filename)
input_image = input_points.get_image() input_image = input_points.get_image()
print(fit.add(1, 3)) # scale points to output image shape. We MUST do this.
points = input_points.get_scaled_points(input_image.shape)
print(input_image.shape)
print dir(eos)
#fit.add(input_image, points)
if __name__ == '__main__': if __name__ == '__main__':
fit_model() fit_model()
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