Last active
November 2, 2020 12:53
-
-
Save Hs293Go/50ced79c1a64cf96379892b49c918c5d to your computer and use it in GitHub Desktop.
Reading MATLAB .mat file in C++, storing data in a std::map keyed by variable names
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* MATLAB API Libraries | |
* | |
* The headers are typically located in ${MATLAB_ROOT}/extern/include | |
* where ${MATLAB_ROOT} is the directory of your matlab installation. | |
* | |
* To use these libraries, your program must link against libmx.so and libmat.so | |
* The shared libraries are typically located in ${MATLAB_ROOT}/bin/glnxa64 | |
* | |
* CMake users add the following lines to your CMakeLists.txt | |
* find_package(Matlab REQUIRED) | |
* set(MATLAB_ROOT /fill/this/path/with/output/of/matlabroot/inside/MATLAB) | |
* target_link_libraries(${Project} ${MATLAB_ROOT}/bin/glnxa64/libmx.so | |
* ${MATLAB_ROOT}/bin/glnxa64/libmat.so) | |
*/ | |
extern "C" { | |
#include "mat.h" | |
#include "matrix.h" | |
} | |
// STL libraries | |
#include <map> | |
#include <memory> | |
#include <string> | |
#include <tuple> | |
#include <vector> | |
/** | |
* @brief Read in data from the .mat file | |
* | |
* @param file A std::string of the .mat file name | |
* @returns A std::map with the names of variables as key and a tuple of: a | |
* std::vector<double> containing the elements, the number of rows, and the | |
* humber of columns, as values. i.e. | |
* { name of variable, {values, number of rows, number of columns}} | |
* | |
* A struct will be much cleaner, but in this demonstration library types | |
* are preferred over introducing new data types | |
*/ | |
std::map<std::string, std::tuple<std::vector<double>, int, int>> | |
read_mat_file(const std::string &file) { | |
std::map<std::string, std::tuple<std::vector<double>, int, int>> matlab_data; | |
// Open the .mat file | |
MATFile *file_ptr = matOpen(file.c_str(), "r"); | |
if (!file_ptr) | |
return matlab_data; | |
int variable_num = 0; | |
// Get the names and number of variables inside the .mat file | |
char **variable_names = matGetDir(file_ptr, &variable_num); | |
for (int i = 0; i < variable_num; ++i) { | |
const std::string name(variable_names[i]); | |
// mxArray is the C type for MATLAB array. It allocates dynamic memory, so | |
// wrap it inside a shared_ptr | |
std::shared_ptr<mxArray> pa(matGetVariable(file_ptr, variable_names[i]), | |
mxDestroyArray); | |
bool is_numeric = mxIsNumeric(pa.get()); | |
bool is_nonempty = !mxIsEmpty(pa.get()); | |
// Only proceed to read data if the variable is numeric and nonempty, and | |
// the mxArray is not null | |
if (pa && is_numeric && is_nonempty) { | |
// Get the number of data elements in the variable | |
const int size = mxGetNumberOfElements(pa.get()); | |
// Get the data elements in the variable casted to a double array | |
const double *value_ptr = static_cast<double *>(mxGetDoubles(pa.get())); | |
const int ndim = mxGetNumberOfDimensions(pa.get()); | |
// Only proceed if to read data if the variable is a 2D matrix. | |
if (value_ptr && ndim <= 2) { | |
// Construct a std::vector<double> from the data elements | |
const std::vector<double> value(value_ptr, value_ptr + size); | |
// Get number of rows and columns | |
const int M = mxGetM(pa.get()); | |
const int N = mxGetN(pa.get()); | |
// Exploit C++11 braced init-list to construct the map of tuple of | |
// vector and ints at the same time as insertion | |
matlab_data.insert({name, {value, M, N}}); | |
} | |
} | |
} | |
// Close the .mat file and return | |
matClose(file_ptr); | |
return matlab_data; | |
} | |
/** Example usage: | |
* | |
* // matlab_data is a std::map keyed by variable names | |
* auto matlab_data = read_mat_file("matrix.mat"); | |
* | |
* // mtrx is a tuple of {std::vector<double>, int, int} | |
* auto mtrx = matlab_data["mtrx"]; | |
* | |
* std::vector<double> elements = std::get<0>(mtrx); // Extract elements | |
* const int rows = std::get<1>(mtrx); // Extract rows | |
* const int cols = std::get<2>(mtrx); // Extract cols | |
* | |
* { // C++17 only: structured binding | |
* auto& [elements, rows, cols] = mtrx; // Extract everything in one line | |
* } | |
* | |
* // Move to Eigen data structure | |
* const Eigen::Map<const Eigen::MatrixXd> eigen_mtrx(elements.data(), rows, cols); | |
* | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment