如何在 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';
}