如何在 Matlab 中保存多维矩阵的二进制并将其作为 C++ 结构加载?
How to save binary of multi-dimensional matrix in Matlab and load it to as a C++ struct?
我在Matlab中有一个多维矩阵,我将它保存为二进制文件,如下所示:
mat = reshape(1:90, 9,5,2);
fileName = 'mat.bin';
fid = fopen(fileName, 'w');
fwrite(fid, mat, 'int32');
fclose(fid);
在C++中我这样加载它:
struct Mat {
int32_t arr[9][5][2];
};
std:ifstream input("mat.bin");
Mat* m = new Mat();
input.read(reinterpret_cast<char*>(m), sizeof(Mat));
input.close();
问题是加载的数据在内存中的排列方式不一样
例如:
访问arr[1][1][1]
将产生Matlab索引数组的值mat(5,2,1)
(当然考虑到 Matlab 的索引从 1 开始)
在将矩阵(和任何其他多维矩阵)写入二进制文件之前,如何排列矩阵,使 arr[i][j][k]
与 mat(i+1, j+1, k+1)
相同?
编辑:
Current solution
For the 2-D case, Transpose of matrix solves it.
For x-D I found that I can change the order of vectors like this
permute(mat, [D D-1 D-2 ... 1])
so it's like spreading the vectors
(which matches how memory is mapped in C++) I still look for a more
general code that doesn't have to write a vector of dimensions (which
sometimes change)
谢谢。
在从 MATLAB 保存之前,您可以使用 matC = permute(mat, ndims(mat):-1:1)
:
permute(mat, ndims(mat):-1:1)
,在您的示例中将维度从 (9,5,2) 更改为 (2,5,9)。
这基本上是您的解决方案,但将 permute(mat, [D D-1 D-2 ... 1])
替换为 ndims(mat):-1:1
。
MATLAB 代码示例:
mat = ones(9,5,2);
% Fill some data for testing:
mat(:, :, 1) = meshgrid(1:5, 1:9);
mat(:, :, 2) = meshgrid(101:105, 101:109);
matC = permute(mat, ndims(mat):-1:1);
fileName = 'mat.bin';
fid = fopen(fileName, 'w');
fwrite(fid, matC, 'int32');
fclose(fid);
C++ 代码示例:
#include <stdint.h>
#include <iostream>
#include <fstream>
int main()
{
struct Mat {
int32_t arr[9][5][2];
};
std::ifstream input("mat.bin", std::ios_base::binary);
Mat* m = new Mat();
input.read(reinterpret_cast<char*>(m), sizeof(Mat));
input.close();
delete m;
return 0;
}
测试样本:
在 C++ 中:m->arr[3][4][1] = 105
在 MATLAB 中:mat(4,5,2) = 105
最好将数组存储为普通一维数组并固定访问它的方式。您的 mat[5][2][1]
等同于 mat[1 + 2*2 + 5*10]
。当继续进行动态内存分配时,创建一个索引为 mat[5][2][1]
的数组意味着将一个指针数组分配给指向数据数组的指针数组。这涉及内存分配的巨大开销(这是相当昂贵的)并导致内存碎片和缓存性能差。相反,分配一个可以保存数据的普通数组,并从 3 个索引计算线性索引非常简单和便宜。
这是动态分配的 3D 数组的简单实现(没有任何形式的检查):
struct Mat {
using value_type = int32_t;
std::vector<value_type> data;
int size[3] = {0, 0, 0};
Mat(int s1, int s2, int s3) {
size[0] = s1; size[1] = s2; size[2] = s3;
data.resize(s1 * s2 * s3);
}
value_type operator()(int i, int j, int k) {
return data[(k * size[1] + j) * size[0] + i];
}
};
当然,您可以将其变成 class
并适当隐藏数据等。或者您可以只使用现有的实现此类数组的众多库之一。有一个用于索引的重载运算符 ()
:m(5,2,1)
被转换为一维数组 m.data
中的适当索引。 (我没有在这里通过引用 return 索引元素,但这当然更有意义)。
这里有一个完整的程序来测试它。输入数据是按列优先存储顺序排列的整数 1:90,在 MATLAB 中我看到了:
>> mat(6,3,2)
ans = 69
C++ 程序输出如下:
m(5,2,1) = 69
源代码:
#include <vector>
#include <fstream>
#include <iostream>
struct Mat {
using value_type = int32_t;
std::vector<value_type> data;
int size[3] = {0, 0, 0};
Mat(int s1, int s2, int s3) {
size[0] = s1; size[1] = s2; size[2] = s3;
data.resize(s1 * s2 * s3);
}
value_type operator()(int i, int j, int k) {
return data[(k * size[1] + j) * size[0] + i];
}
};
int main() {
std::ifstream input("mat.bin");
Mat m(9, 5, 2);
input.read(reinterpret_cast<char*>(m.data.data()), m.data.size() * sizeof(Mat::value_type));
input.close();
std::cout << "m(5,2,1) = " << m(5,2,1) << '\n';
}
我在Matlab中有一个多维矩阵,我将它保存为二进制文件,如下所示:
mat = reshape(1:90, 9,5,2);
fileName = 'mat.bin';
fid = fopen(fileName, 'w');
fwrite(fid, mat, 'int32');
fclose(fid);
在C++中我这样加载它:
struct Mat {
int32_t arr[9][5][2];
};
std:ifstream input("mat.bin");
Mat* m = new Mat();
input.read(reinterpret_cast<char*>(m), sizeof(Mat));
input.close();
问题是加载的数据在内存中的排列方式不一样 例如:
访问arr[1][1][1]
将产生Matlab索引数组的值mat(5,2,1)
(当然考虑到 Matlab 的索引从 1 开始)
在将矩阵(和任何其他多维矩阵)写入二进制文件之前,如何排列矩阵,使 arr[i][j][k]
与 mat(i+1, j+1, k+1)
相同?
编辑:
Current solution
For the 2-D case, Transpose of matrix solves it.For x-D I found that I can change the order of vectors like this
permute(mat, [D D-1 D-2 ... 1])
so it's like spreading the vectors (which matches how memory is mapped in C++) I still look for a more general code that doesn't have to write a vector of dimensions (which sometimes change)
谢谢。
在从 MATLAB 保存之前,您可以使用 matC = permute(mat, ndims(mat):-1:1)
:
permute(mat, ndims(mat):-1:1)
,在您的示例中将维度从 (9,5,2) 更改为 (2,5,9)。
这基本上是您的解决方案,但将 permute(mat, [D D-1 D-2 ... 1])
替换为 ndims(mat):-1:1
。
MATLAB 代码示例:
mat = ones(9,5,2);
% Fill some data for testing:
mat(:, :, 1) = meshgrid(1:5, 1:9);
mat(:, :, 2) = meshgrid(101:105, 101:109);
matC = permute(mat, ndims(mat):-1:1);
fileName = 'mat.bin';
fid = fopen(fileName, 'w');
fwrite(fid, matC, 'int32');
fclose(fid);
C++ 代码示例:
#include <stdint.h>
#include <iostream>
#include <fstream>
int main()
{
struct Mat {
int32_t arr[9][5][2];
};
std::ifstream input("mat.bin", std::ios_base::binary);
Mat* m = new Mat();
input.read(reinterpret_cast<char*>(m), sizeof(Mat));
input.close();
delete m;
return 0;
}
测试样本:
在 C++ 中:m->arr[3][4][1] = 105
在 MATLAB 中:mat(4,5,2) = 105
最好将数组存储为普通一维数组并固定访问它的方式。您的 mat[5][2][1]
等同于 mat[1 + 2*2 + 5*10]
。当继续进行动态内存分配时,创建一个索引为 mat[5][2][1]
的数组意味着将一个指针数组分配给指向数据数组的指针数组。这涉及内存分配的巨大开销(这是相当昂贵的)并导致内存碎片和缓存性能差。相反,分配一个可以保存数据的普通数组,并从 3 个索引计算线性索引非常简单和便宜。
这是动态分配的 3D 数组的简单实现(没有任何形式的检查):
struct Mat {
using value_type = int32_t;
std::vector<value_type> data;
int size[3] = {0, 0, 0};
Mat(int s1, int s2, int s3) {
size[0] = s1; size[1] = s2; size[2] = s3;
data.resize(s1 * s2 * s3);
}
value_type operator()(int i, int j, int k) {
return data[(k * size[1] + j) * size[0] + i];
}
};
当然,您可以将其变成 class
并适当隐藏数据等。或者您可以只使用现有的实现此类数组的众多库之一。有一个用于索引的重载运算符 ()
:m(5,2,1)
被转换为一维数组 m.data
中的适当索引。 (我没有在这里通过引用 return 索引元素,但这当然更有意义)。
这里有一个完整的程序来测试它。输入数据是按列优先存储顺序排列的整数 1:90,在 MATLAB 中我看到了:
>> mat(6,3,2)
ans = 69
C++ 程序输出如下:
m(5,2,1) = 69
源代码:
#include <vector>
#include <fstream>
#include <iostream>
struct Mat {
using value_type = int32_t;
std::vector<value_type> data;
int size[3] = {0, 0, 0};
Mat(int s1, int s2, int s3) {
size[0] = s1; size[1] = s2; size[2] = s3;
data.resize(s1 * s2 * s3);
}
value_type operator()(int i, int j, int k) {
return data[(k * size[1] + j) * size[0] + i];
}
};
int main() {
std::ifstream input("mat.bin");
Mat m(9, 5, 2);
input.read(reinterpret_cast<char*>(m.data.data()), m.data.size() * sizeof(Mat::value_type));
input.close();
std::cout << "m(5,2,1) = " << m(5,2,1) << '\n';
}