Commit df5f1c75 authored by Patrik Huber's avatar Patrik Huber Committed by GitHub

Merge pull request #75 from patrikhuber/matlab-bindings

Added Matlab bindings for the fitting function
parents dc84b9ae 3375cc06
...@@ -10,3 +10,6 @@ ...@@ -10,3 +10,6 @@
[submodule "3rdparty/eigen3-nnls"] [submodule "3rdparty/eigen3-nnls"]
path = 3rdparty/eigen3-nnls path = 3rdparty/eigen3-nnls
url = https://github.com/hmatuschek/eigen3-nnls.git url = https://github.com/hmatuschek/eigen3-nnls.git
[submodule "3rdparty/mexplus"]
path = 3rdparty/mexplus
url = https://github.com/kyamagu/mexplus.git
Subproject commit 58b8487a00228da9d117770d2964ff67c35f5348
...@@ -62,6 +62,9 @@ option(BUILD_DOCUMENTATION "Build the library documentation." OFF) ...@@ -62,6 +62,9 @@ option(BUILD_DOCUMENTATION "Build the library documentation." OFF)
message(STATUS "BUILD_DOCUMENTATION: ${BUILD_DOCUMENTATION}") message(STATUS "BUILD_DOCUMENTATION: ${BUILD_DOCUMENTATION}")
option(GENERATE_PYTHON_BINDINGS "Build python bindings. Needs BUILD_UTILS enabled too." OFF) option(GENERATE_PYTHON_BINDINGS "Build python bindings. Needs BUILD_UTILS enabled too." OFF)
message(STATUS "GENERATE_PYTHON_BINDINGS: ${GENERATE_PYTHON_BINDINGS}") message(STATUS "GENERATE_PYTHON_BINDINGS: ${GENERATE_PYTHON_BINDINGS}")
option(GENERATE_MATLAB_BINDINGS "Build Matlab bindings. Requires Matlab with the compiler installed or the Matlab Compiler Runtime." OFF)
message(STATUS "GENERATE_MATLAB_BINDINGS: ${GENERATE_MATLAB_BINDINGS}")
# Build a CPack driven installer package: # Build a CPack driven installer package:
include(InstallRequiredSystemLibraries) # This module will include any runtime libraries that are needed by the project for the current platform include(InstallRequiredSystemLibraries) # This module will include any runtime libraries that are needed by the project for the current platform
...@@ -180,3 +183,7 @@ endif() ...@@ -180,3 +183,7 @@ endif()
if(BUILD_DOCUMENTATION) if(BUILD_DOCUMENTATION)
add_subdirectory(doc) add_subdirectory(doc)
endif() endif()
if(GENERATE_MATLAB_BINDINGS)
add_subdirectory(matlab)
endif()
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
# ============================== # ==============================
#set(PYTHON_EXECUTABLE "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python35\\python.exe" CACHE PATH "Path to the python interpreter." FORCE) #set(PYTHON_EXECUTABLE "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python35\\python.exe" CACHE PATH "Path to the python interpreter." FORCE)
# Set the path to the Matlab root directory if you want to build the Matlab bindings and it doesn't find Matlab automatically:
# ==============================
#set(Matlab_ROOT_DIR "/opt/matlab" CACHE PATH "Path to the Matlab installation directory." FORCE)
# Configuration options # Configuration options
# ============================== # ==============================
set(BUILD_EXAMPLES ON CACHE BOOL "Build the example applications." FORCE) set(BUILD_EXAMPLES ON CACHE BOOL "Build the example applications." FORCE)
...@@ -31,3 +35,4 @@ set(BUILD_CERES_EXAMPLE OFF CACHE BOOL "Build the fit-model-ceres example (requi ...@@ -31,3 +35,4 @@ set(BUILD_CERES_EXAMPLE OFF CACHE BOOL "Build the fit-model-ceres example (requi
set(BUILD_UTILS OFF CACHE BOOL "Build utility applications." FORCE) set(BUILD_UTILS OFF CACHE BOOL "Build utility applications." FORCE)
set(BUILD_DOCUMENTATION OFF CACHE BOOL "Build the library documentation." FORCE) set(BUILD_DOCUMENTATION OFF CACHE BOOL "Build the library documentation." FORCE)
set(GENERATE_PYTHON_BINDINGS OFF CACHE BOOL "Build python bindings. Needs BUILD_UTILS enabled too." FORCE) set(GENERATE_PYTHON_BINDINGS OFF CACHE BOOL "Build python bindings. Needs BUILD_UTILS enabled too." FORCE)
set(GENERATE_MATLAB_BINDINGS OFF CACHE BOOL "Build Matlab bindings. Requires Matlab with the compiler installed or the Matlab Compiler Runtime." FORCE)
function [mesh, rendering_parameters] = fit_shape_and_pose(morphable_model, ...
blendshapes, landmarks, landmark_mapper, image_width, image_height, ...
edge_topology, contour_landmarks, model_contour, num_iterations, ...
num_shape_coefficients_to_fit, lambda)
% FIT_SHAPE_AND_POSE Fit a 3DMM shape model to landmarks.
% [ mesh, rendering_parameters ] = FIT_SHAPE_AND_POSE(morphable_model, ...
% blendshapes, landmarks, landmark_mapper, image_width, image_height, ...
% edge_topology, contour_landmarks, model_contour, num_iterations, ...
% num_shape_coefficients_to_fit, lambda)
%
% This function fits a 3D Morphable Model to landmarks in an image.
% It fits the pose (camera), PCA shape model, and expression blendshapes
% in an iterative way.
%
% landmarks must be a 68 x 2 matrix with ibug landmarks, in the order
% from 1 to 68.
%
% Default values for some of the parameters:: num_iterations = 5,
% num_shape_coefficients_to_fit = all (-1), and lambda = 30.0.
%
% Please see the C++ documentation for the description of the parameters:
% http://patrikhuber.github.io/eos/doc/ (TODO: Update to v0.9.1!)
%
% NOTE: In contrast to the C++ function, this Matlab function expects the
% morphable_model, blendshapes, landmark_mapper, edge_topology,
% contour_landmarks and model_contour as *filenames* to the respective
% files in the eos/share/ directory, and not the objects directly.
% We'll use default values to the following arguments, if they're not
% provided:
if (~exist('edge_topology', 'var')), edge_topology = '../share/sfm_3448_edge_topology.json'; end
if (~exist('contour_landmarks', 'var')), contour_landmarks = '../share/ibug2did.txt'; end
if (~exist('model_contour', 'var')), model_contour = '../share/model_contours.json'; end
if (~exist('num_iterations', 'var')), num_iterations = 5; end
if (~exist('num_shape_coefficients_to_fit', 'var')), num_shape_coefficients_to_fit = -1; end
if (~exist('lambda', 'var')), lambda = 30.0; end
[ mesh, rendering_parameters ] = fitting(morphable_model, blendshapes, landmarks, landmark_mapper, image_width, image_height, edge_topology, contour_landmarks, model_contour, num_iterations, num_shape_coefficients_to_fit, lambda);
end
/*
* eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: matlab/+eos/+fitting/private/fitting.cpp
*
* Copyright 2016 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 "eos/morphablemodel/MorphableModel.hpp"
#include "eos/morphablemodel/Blendshape.hpp"
#include "eos/morphablemodel/EdgeTopology.hpp"
#include "eos/fitting/contour_correspondence.hpp"
#include "eos/fitting/fitting.hpp"
#include "eos/fitting/RenderingParameters.hpp"
#include "eos/render/Mesh.hpp"
#include "mexplus_eigen.hpp"
#include "mexplus_eos_types.hpp"
#include "mexplus.h"
#include "Eigen/Core"
#include "opencv2/core/core.hpp"
#include "mex.h"
//#include "matrix.h"
#include <string>
using namespace eos;
using namespace mexplus;
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
using std::string;
// Check for proper number of input and output arguments:
if (nrhs != 12) {
mexErrMsgIdAndTxt("eos:fitting:nargin", "fit_shape_and_pose requires 12 input arguments.");
}
if (nlhs != 2) {
mexErrMsgIdAndTxt("eos:fitting:nargout", "fit_shape_and_pose returns two output arguments.");
}
InputArguments input(nrhs, prhs, 12);
auto morphablemodel_file = input.get<string>(0);
auto blendshapes_file = input.get<string>(1);
auto landmarks_in = input.get<Eigen::MatrixXd>(2);
auto mapper_file = input.get<string>(3);
auto image_width = input.get<int>(4);
auto image_height = input.get<int>(5);
auto edgetopo_file = input.get<string>(6);
auto contour_lms_file = input.get<string>(7);
auto model_cnt_file = input.get<string>(8);
auto num_iterations = input.get<int>(9);
auto num_shape_coeffs = input.get<int>(10);
auto lambda = input.get<double>(11);
if (landmarks_in.rows() != 68) {
mexErrMsgIdAndTxt("eos:fitting:argin", "Given landmarks must be a 68 x 2 vector with ibug landmarks, in the order from 1 to 68.");
}
// Convert the landmarks (given as matrix in Matlab) to a LandmarkCollection:
core::LandmarkCollection<cv::Vec2f> landmarks;
for (int i = 0; i < 68; ++i)
{
landmarks.push_back(core::Landmark<cv::Vec2f>{ std::to_string(i + 1), cv::Vec2f(landmarks_in(i, 0), landmarks_in(i, 1)) });
}
// Load everything:
const auto morphable_model = morphablemodel::load_model(morphablemodel_file);
auto blendshapes = morphablemodel::load_blendshapes(blendshapes_file);
core::LandmarkMapper landmark_mapper(mapper_file);
auto edge_topology = morphablemodel::load_edge_topology(edgetopo_file);
auto contour_landmarks = fitting::ContourLandmarks::load(contour_lms_file);
auto model_contour = fitting::ModelContour::load(model_cnt_file);
boost::optional<int> num_shape_coefficients_to_fit = num_shape_coeffs == -1 ? boost::none : boost::optional<int>(num_shape_coeffs);
// Now do the actual fitting:
render::Mesh mesh;
fitting::RenderingParameters rendering_parameters;
std::tie(mesh, rendering_parameters) = fitting::fit_shape_and_pose(morphable_model, blendshapes, landmarks, landmark_mapper, image_width, image_height, edge_topology, contour_landmarks, model_contour, num_iterations, num_shape_coefficients_to_fit, lambda);
// C++ counts the vertex indices starting at zero, Matlab starts counting
// at one - therefore, add +1 to all triangle indices:
for (auto&& t : mesh.tvi) {
for (auto&& idx : t) {
idx += 1;
}
}
// Return the mesh and the rendering_parameters:
OutputArguments output(nlhs, plhs, 2);
output.set(0, mesh);
output.set(1, rendering_parameters);
};
void func()
{
int x = 4;
};
int func1()
{
return 5;
};
class MyClass
{
public:
MyClass() = default;
int test() {
return 6;
};
};
project(matlab-bindings)
cmake_minimum_required(VERSION 3.7.0)
# If Matlab_ROOT_DIR is set, the Matlab at that location is used.
find_package(Matlab COMPONENTS MX_LIBRARY REQUIRED)
# Dependencies of the modules:
find_package(OpenCV 2.4.3 REQUIRED core)
set_target_properties(${OpenCV_LIBS} PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE)
if(MSVC)
# The standard find_package for boost on Win finds the dynamic libs, so for dynamic linking to boost we need to #define:
add_definitions(-DBOOST_ALL_NO_LIB) # Don't use the automatic library linking by boost with VS (#pragma ...). Instead, we specify everything here in cmake.
add_definitions(-DBOOST_ALL_DYN_LINK) # Link against the dynamic boost lib - needs to match with the version that find_package finds.
endif()
find_package(Boost 1.50.0 COMPONENTS system filesystem REQUIRED) # Why do we need boost for MorphableModel.hpp?
# See: https://cmake.org/cmake/help/v3.7/module/FindMatlab.html?highlight=findmatlab#command:matlab_add_mex
matlab_add_mex(
NAME eos_fitting
#[EXECUTABLE | MODULE | SHARED] # SHARED is the default.
SRC +eos/+fitting/private/fitting.cpp
OUTPUT_NAME fitting #[OUTPUT_NAME output_name]
# DOCUMENTATION +eos/+fitting/fit_shape_and_pose.m # doesn't work - wrong path probably. But it renames the file to fitting.m, so not what we want anyway.
LINK_TO ${OpenCV_LIBS} ${Boost_LIBRARIES} #[LINK_TO target1 target2 ...]
#[...]
)
target_include_directories(eos_fitting PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/mexplus/include ${CMAKE_SOURCE_DIR}/matlab/include)
install(FILES ${CMAKE_SOURCE_DIR}/matlab/demo.m DESTINATION matlab)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/matlab/include DESTINATION matlab)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/matlab/+eos DESTINATION matlab PATTERN "*.cpp" EXCLUDE)
install(TARGETS eos_fitting DESTINATION matlab/+eos/+fitting/private)
%% Demo for running the eos fitting from Matlab
%
%% Set up some required paths to files:
model_file = '../share/sfm_shape_3448.bin';
blendshapes_file = '../share/expression_blendshapes_3448.bin';
landmark_mappings = '../share/ibug2did.txt';
%% Load an image and its landmarks in ibug format:
image = imread('../bin/data/image_0010.png');
landmarks = read_pts_landmarks('../bin/data/image_0010.pts');
image_width = size(image, 2); image_height = size(image, 1);
%% Run the fitting, get back the fitted mesh and pose:
[mesh, render_params] = eos.fitting.fit_shape_and_pose(model_file, blendshapes_file, landmarks, landmark_mappings, image_width, image_height);
% Note: The function actually has a few more arguments to files it
% needs. If you're not running it from within eos/matlab/, you need to
% provide them. See its documentation and .m file.
%% Visualise the fitted mesh using your favourite plot, for example...
figure(1);
plot3(mesh.vertices(:, 1), mesh.vertices(:, 2), mesh.vertices(:, 3), '.');
% or...
FV.vertices = mesh.vertices(:, 1:3);
FV.faces = mesh.tvi;
figure(2);
patch(FV, 'FaceColor', [1 1 1], 'EdgeColor', 'none', 'FaceLighting', 'phong'); light; axis equal; axis off;
%% Visualise the fitting in 2D, on top of the input image:
% Project all vertices to 2D:
points_2d = mesh.vertices * (render_params.viewport*render_params.projection*render_params.modelview)';
% Display the image and plot the projected mesh points on top of it:
figure(3);
imshow(image);
hold on;
plot(points_2d(:, 1), points_2d(:, 2), 'g.');
% We can also plot the landmarks the mesh was fitted to:
plot(landmarks(:, 1), landmarks(:, 2), 'ro');
%% Just a helper function to read ibug .pts landmarks from a file:
function [ landmarks ] = read_pts_landmarks(filename)
file = fopen(filename, 'r');
file_content = textscan(file, '%s');
landmarks = zeros(68, 2);
row_idx = 1;
for i=6:2:141
landmarks(row_idx, 1) = str2double(file_content{1}{i});
landmarks(row_idx, 2) = str2double(file_content{1}{i + 1});
row_idx = row_idx + 1;
end
end
/*
* eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: matlab/include/mexplus_eigen.hpp
*
* Copyright 2016 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.
*/
#pragma once
#ifndef MEXPLUS_EIGEN_HPP_
#define MEXPLUS_EIGEN_HPP_
#include "mexplus/mxarray.h"
#include "Eigen/Core"
#include "mex.h"
namespace mexplus {
/**
* @brief Define a template specialisation for Eigen::MatrixXd for ... .
*
* Todo: Documentation.
*/
template<>
mxArray* MxArray::from(const Eigen::MatrixXd& eigen_matrix) {
const int num_rows = static_cast<int>(eigen_matrix.rows());
const int num_cols = static_cast<int>(eigen_matrix.cols());
MxArray out_array(MxArray::Numeric<double>(num_rows, num_cols));
// This might not copy the data but it's evil and probably really dangerous!!!:
//mxSetData(const_cast<mxArray*>(matrix.get()), (void*)value.data());
// This copies the data. But I suppose it makes sense that we copy the data when we go
// from C++ to Matlab, since Matlab can unload the C++ mex module at any time I think.
// Loop is column-wise
for (int c = 0; c < num_cols; ++c) {
for (int r = 0; r < num_rows; ++r) {
out_array.set(r, c, eigen_matrix(r, c));
}
}
return out_array.release();
};
/**
* @brief Define a template specialisation for Eigen::MatrixXd for ... .
*
* Todo: Documentation.
*/
template<>
void MxArray::to(const mxArray* in_array, Eigen::MatrixXd* eigen_matrix)
{
MxArray array(in_array);
if (array.dimensionSize() > 2)
{
mexErrMsgIdAndTxt("eos:matlab", "Given array has > 2 dimensions. Can only create 2-dimensional matrices (and vectors).");
}
if (array.dimensionSize() == 1 || array.dimensionSize() == 0)
{
mexErrMsgIdAndTxt("eos:matlab", "Given array has 0 or 1 dimensions but we expected a 2-dimensional matrix (or row/column vector).");
// Even when given a single value dimensionSize() is 2, with n=m=1. When does this happen?
}
if (!array.isDouble())
{
mexErrMsgIdAndTxt("eos:matlab", "Trying to create a Eigen::MatrixXd in C++, but the given data is not of type double.");
}
// We can be sure now that the array is 2-dimensional (or 0, but then we're screwed anyway)
auto nrows = array.dimensions()[0]; // or use array.rows()
auto ncols = array.dimensions()[1];
// I think I can just use Eigen::Matrix, not a Map - the Matrix c'tor that we call creates a Map anyway?
Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>> eigen_map(array.getData<double>(), nrows, ncols);
// Not sure that's alright - who owns the data? I think as it is now, everything points to the data in the mxArray owned by Matlab, but I'm not 100% sure.
*eigen_matrix = eigen_map;
};
} /* namespace mexplus */
#endif /* MEXPLUS_EIGEN_HPP_ */
/*
* eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: matlab/include/mexplus_eos_types.hpp
*
* Copyright 2016 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.
*/
#pragma once
#ifndef MEXPLUS_EOS_TYPES_HPP_
#define MEXPLUS_EOS_TYPES_HPP_
#include "eos/render/Mesh.hpp"
#include "eos/fitting/RenderingParameters.hpp"
#include "mexplus/mxarray.h"
#include "glm/vec2.hpp"
#include "glm/vec3.hpp"
#include "glm/vec4.hpp"
#include "glm/gtc/quaternion.hpp"
#include "glm/mat4x4.hpp"
#include "Eigen/Core"
#include "mex.h"
/**
* @file
* @brief Contains mexplus template specialisations to convert eos data
* structures into Matlab.
*
* Note 1: These all copy the data, which I believe might be necessary, since
* Matlab may unload a mex module (with all its allocated data) at any given
* time.
* Note 2: They all return double vectors and matrices, even when the data given
* are floats. We can think about changing that if it's a speed issue, however,
* I think double is Matlab's default data type.
*/
namespace mexplus {
/**
* @brief Converts a glm::tquat<float> to a Matlab vector.
*
* @param[in] quat The quaternion to convert.
* @return An 1x4 Matlab vector.
*/
template<>
mxArray* MxArray::from(const glm::tquat<float>& quat)
{
MxArray out_array(MxArray::Numeric<double>(1, 4));
for (int c = 0; c < 4; ++c) {
out_array.set(c, quat[c]);
}
return out_array.release();
};
/**
* @brief Converts a glm::tmat4x4<float> to a Matlab matrix.
*
* @param[in] mat The matrix to convert.
* @return A 4x4 Matlab matrix.
*/
template<>
mxArray* MxArray::from(const glm::tmat4x4<float>& mat)
{
MxArray out_array(MxArray::Numeric<double>(4, 4));
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
out_array.set(r, c, mat[c][r]);
}
}
return out_array.release();
};
/**
* @brief Converts an std::vector of glm::tvec2<float> to a Matlab matrix.
*
* This function converts a whole vector of vec2's to an n x 2 Matlab matrix,
* where n is data.size(). It is mainly used to pass texture coordinates of
* a Mesh to Matlab.
*
* We specialise for std::vector<glm::tvec2<float>> directly (and not for
* glm::tvec2<float>) because otherwise a cell array of vec2's would be
* generated. Luckily, even if a tvec2 specialisation was to exist too,
* this one would take precedence to convert a vector<tvec2>.
*
* @param[in] data The data to convert.
* @return An n x 2 Matlab matrix.
*/
template<>
mxArray* MxArray::from(const std::vector<glm::tvec2<float>>& data)
{
MxArray out_array(MxArray::Numeric<double>(data.size(), 2));
for (int r = 0; r < data.size(); ++r) {
for (int c = 0; c < 2; ++c) {
out_array.set(r, c, data[r][c]);
}
}
return out_array.release();
};
/**
* @brief Converts an std::vector of glm::tvec3<float> to a Matlab matrix.
*
* This function converts a whole vector of vec3's to an n x 3 Matlab matrix,
* where n is data.size(). It is mainly used to pass vertex colour data of
* a Mesh to Matlab.
*
* See template<> mxArray* MxArray::from(const std::vector<glm::tvec2<float>>&)
* for more details.
*
* @param[in] data The data to convert.
* @return An n x 3 Matlab matrix.
*/
template<>
mxArray* MxArray::from(const std::vector<glm::tvec3<float>>& data)
{
MxArray out_array(MxArray::Numeric<double>(data.size(), 3));
for (int r = 0; r < data.size(); ++r) {
for (int c = 0; c < 3; ++c) {
out_array.set(r, c, data[r][c]);
}
}
return out_array.release();
};
/**
* @brief Converts an std::vector of glm::tvec4<float> to a Matlab matrix.
*
* This function converts a whole vector of vec4's to an n x 4 Matlab matrix,
* where n is data.size(). It is mainly used to pass vertex data of a Mesh
* to Matlab.
*
* See template<> mxArray* MxArray::from(const std::vector<glm::tvec2<float>>&)
* for more details.
*
* @param[in] data The data to convert.
* @return An n x 4 Matlab matrix.
*/
template<>
mxArray* MxArray::from(const std::vector<glm::tvec4<float>>& data)
{
MxArray out_array(MxArray::Numeric<double>(data.size(), 4));
for (int r = 0; r < data.size(); ++r) {
for (int c = 0; c < 4; ++c) {
out_array.set(r, c, data[r][c]);
}
}
return out_array.release();
};
/**
* @brief Converts an std::vector of std::array<int, 3> to a Matlab matrix.
*
* This function converts a whole vector of array<int, 3>'s to an n x 3 Matlab
* matrix, where n is data.size(). It is mainly used to pass triangle indices
* data of a Mesh to Matlab.
*
* We specialise for vector<array<int, 3>> directly (and not for array<int, 3>)
* because otherwise a cell array of arrays would be generated. Luckily, even
* if an array<int, 3> specialisation was to exist too, this one would take
* precedence to convert a vector<array<int, 3>>.
*
* @param[in] data The data to convert.
* @return An n x 3 Matlab matrix.
*/
template<>
mxArray* MxArray::from(const std::vector<std::array<int, 3>>& data)
{
MxArray out_array(MxArray::Numeric<int>(data.size(), 3));
for (int r = 0; r < data.size(); ++r) {
for (int c = 0; c < 3; ++c) {
out_array.set(r, c, data[r][c]);
}
}
return out_array.release();
};
/**
* @brief Define a template specialisation for eos::render::Mesh.
*
* This converts an eos::render::Mesh into a Matlab struct.
*
* @param[in] mesh The Mesh that will be returned to Matlab.
* @return An mxArray containing a Matlab struct with all vertex, colour, texcoords and triangle data.
*/
template<>
mxArray* MxArray::from(const eos::render::Mesh& mesh) {
MxArray out_array(MxArray::Struct());
out_array.set("vertices", mesh.vertices);
out_array.set("colors", mesh.colors);
out_array.set("texcoords", mesh.texcoords);
out_array.set("tvi", mesh.tvi);
out_array.set("tci", mesh.tci);
return out_array.release();
};
/**
* @brief Define a template specialisation for eos::fitting::RenderingParameters.
*
* This converts an eos::fitting::RenderingParameters into a Matlab struct.
*
* @param[in] rendering_parameters The RenderingParameters that will be returned to Matlab.
* @return An mxArray containing a Matlab struct with all required parameters.
*/
template<>
mxArray* MxArray::from(const eos::fitting::RenderingParameters& rendering_parameters) {
MxArray out_array(MxArray::Struct());
const std::string camera_type = [&rendering_parameters]() {
if (rendering_parameters.get_camera_type() == eos::fitting::CameraType::Orthographic)
{
return "Orthographic";
}
else if (rendering_parameters.get_camera_type() == eos::fitting::CameraType::Perspective) {
return "Perspective";
}
else {
return "unknown";
}
}();
// Since we don't expose get_opencv_viewport(), and Matlab doesn't have glm::project()
// anyway, we'll make a 4x4 viewport matrix available. Matlab seems to have the same
// convention than OpenCV (top-left is the image origin).
auto viewport = eos::fitting::get_opencv_viewport(rendering_parameters.get_screen_width(), rendering_parameters.get_screen_height());
glm::mat4x4 viewport_matrix; // Identity matrix
viewport_matrix[0][0] = 0.5f * viewport[2];
viewport_matrix[3][0] = 0.5f * viewport[2] + viewport[0];
viewport_matrix[1][1] = 0.5f * viewport[3];
viewport_matrix[3][1] = 0.5f * viewport[3] + viewport[1];
viewport_matrix[2][2] = 0.5f;
viewport_matrix[3][2] = 0.5f;
out_array.set("camera_type", camera_type);
out_array.set("rotation_quaternion", rendering_parameters.get_rotation());
out_array.set("modelview", rendering_parameters.get_modelview());
out_array.set("projection", rendering_parameters.get_projection());
out_array.set("viewport", viewport_matrix);
out_array.set("screen_width", rendering_parameters.get_screen_width());
out_array.set("screen_height", rendering_parameters.get_screen_height());
return out_array.release();
};
} /* namespace mexplus */
#endif /* MEXPLUS_EOS_TYPES_HPP_ */
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