Как сохранить двоичный файл многомерной матрицы в Matlab и загрузить его в виде структуры C ++? - PullRequest
3 голосов
/ 10 марта 2020

У меня есть многомерная матрица в Matlab , я сохраняю ее в виде двоичного файла, например:

mat = reshape(1:90, 9,5,2);
fileName = 'mat.bin';
fid = fopen(fileName, 'w');
fwrite(fid, mat, 'int32');
fclose(fid);

В C ++ Я загружаю ее как this:

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)?

РЕДАКТИРОВАТЬ:

Текущее решение
Для двумерного случая Transpose of matrix решает его.

Для xD я обнаружил, что могу изменить порядок векторов следующим образом permute(mat, [D D-1 D-2 ... 1]), так что это похоже на распределение векторов (что соответствует отображению памяти в C ++). Я все еще ищу более общий код, который не ' нужно написать вектор размеров (которые иногда меняются)

Спасибо.

Ответы [ 2 ]

1 голос
/ 10 марта 2020

Всегда лучше хранить массивы как простые одномерные массивы и фиксировать способ доступа к ним. Ваш mat[5][2][1] эквивалентен mat[1 + 2*2 + 5*10]. При переходе к динамическому выделению памяти c создание массива с индексом mat[5][2][1] подразумевает выделение массива указателей на массивы указателей на массивы данных. Это приводит к огромным накладным расходам на выделение памяти (что довольно дорого) и вызывает фрагментацию памяти и низкую производительность кэша. Вместо этого выделение одного простого массива, который может содержать данные, и вычисление линейного индекса из ваших трех индексов довольно просто и дешево.

Это тривиальная реализация (без какой-либо формы проверки) для динамически распределенного 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 с соответствующим сокрытием данных и т. Д. c. Или вы можете просто использовать одну из множества существующих библиотек, которые реализуют такие массивы. Для индексации есть перегруженный оператор (): m(5,2,1) преобразуется в соответствующую индексацию в массив 1D m.data. (Я не стал возвращать индексированный элемент по ссылке, но это, конечно, имело бы больше смысла).


Вот полная программа для его тестирования. С входными данными, являющимися целыми числами 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';
}
0 голосов
/ 10 марта 2020

Вы можете использовать matC = permute(mat, ndims(mat):-1:1) перед сохранением из MATLAB:

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

...