Доступ к данным в типизированных, ячеистых и структурных массивах

MATLAB® Data API, используемый функциями C++ MEX, обеспечивает ту же семантику копирования при записи, используемую функциями, написанными непосредственно в MATLAB. MEX-функция может дублировать переданный ей массив данных, но этот дубликат является общей копией исходной переменной. Отдельная копия данных создается, когда MEX-функция изменяет значения в массиве.

Когда вы вызываете MEX-функцию, matlab::data::Array входы являются общими копиями переменного MATLAB, переданными в MEX-функцию. Общие копии обеспечивают преимущество, когда MEX-функция:

  • Не изменяет большой массив, переданный в MEX-функцию.

  • Изменяет определенные поля структуры, которая передается как вход, не создавая копию всей структуры.

  • Изменяет камеру в массиве ячеек, который передается как вход, не вызывая глубокой копии массива ячеек

  • Изменяет свойство объекта, которое передается как вход, не делая глубокой копии объекта.

Общие копии

Этот пример кода выигрывает от использования общих копий при вычислении среднего значения массива изображений. Копирование массива входа в const matlab::data::TypedArray<T>, который поддерживает итераторы, позволяет использовать основанную на диапазоне for цикл для выполнения вычисления, но гарантирует, что массив не будет скопирован.

#include "mex.hpp"
#include "mexAdapter.hpp"

using matlab::mex::ArgumentList;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
    ArrayFactory factory;
public:
    void operator()(ArgumentList outputs, ArgumentList inputs) {
        double sm = 0;
        const TypedArray<uint8_t> inArray = inputs[0];
        for (auto& elem : inArray) {
            sm += elem;
        }
        outputs[0] = factory.createScalar(sm / inArray.getNumberOfElements());
    }
};

Сохраните исходный код MEX-функции в файле с именем aveImage.cpp. Можно использовать эту функцию, чтобы найти среднее значение массива топографических данных. Загрузите topo.mat файл в рабочее пространство MATLAB и передайте массив данных в MEX-функцию.

mex aveImage.cpp
load topo
d = uint8(topo); % convert data to use aveImage
m = aveImage(d)
m = 73.5487

Изменение массива ячеек в MEX-функция

Когда вы передаете массив ячеек в MEX-функцию, его представление MATLAB Data API, matlab::data::CellArray, является по существу matlab::data::Array который содержит matlab::data::Array в каждой камере. Этот проект позволяет каждому содержащемуся в массиве быть общей копией до изменения.

Когда вы изменяете камеру массива ячеек в MEX-функция, только массив, содержащийся в этой камере, не разделяется, а не весь массив ячеек. Например, предположим, что вы копируете массив ячеек A в массив ячеек B. Значение каждой камеры является общим.

Если вы изменяете Cell1 в массиве ячеек B массив в этой камере больше не является общим с Cell1 в массиве ячеек А. Однако значения всех других камер остаются общими, так что весь массив ячеек не должен быть скопирован.

Этот пример показывает, как передать массив ячеек в MEX-функцию и изменить одну камеру в массиве ячеек, не вызывая копий данных в других камерах. Пример загружает данные изображения в рабочее пространство MATLAB и создает массив ячеек, который содержит данные изображения, подпись и палитру.

load durer
whos
  Name           Size               Bytes  Class 

  X            648x509            2638656  double
  caption        2x28                 112  char  
  map          128x3                 3072  double 

Назначьте каждую переменную другой камере.

durerCell{1} = X;
durerCell{2} = caption;
durerCell{3} = map;

Эта MEX-функция использует std::move для перемещения входного массива ячеек в matlab::data::CellArray, который обеспечивает доступ к отдельным камерам. Единственными измененными данными является подпись, которая хранится в одной камере массива ячеек. Чтобы изменить эту камеру:

  • Получите matlab::data::Reference<TypedArray<T>> ссылка на массив в камере, содержащем заголовок.

  • Присвойте новое значение ссылке как matlab::data::CharArray поскольку возвращенное значение является вектором символов MATLAB.

#include "mex.hpp"
#include "mexAdapter.hpp"

using matlab::mex::ArgumentList;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
    ArrayFactory factory;
public:
    void operator()(ArgumentList outputs, ArgumentList inputs) {
        checkArguments(inputs);
        CellArray imageCell = std::move(inputs[0]);
        TypedArrayRef<char16_t> cellRef = imageCell[1];
        cellRef = factory.createCharArray("Albrecht Durer's Melancolia with a magic square" );
        outputs[0] = imageCell;
    }

    void checkArguments(ArgumentList inputs) {
        std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine();
        if (inputs[0].getType() != ArrayType::CELL) {
            matlabPtr->feval(u"error", 0, std::vector<Array>
                ({ factory.createScalar("Input must be cell array") }));
        }
    }
};

Сохраните этот код в файле с именем modifyCellArray.cppсоздайте файл MEX и вызовите функцию.

mex modifyCellArray.cpp
durerCell = modifyCellArray(durerCell);

Возвращенный массив ячеек содержит новый символьный массив, назначенный в MEX-функция.

durerCell{2}
ans =

    'Albrecht Durer's Melancolia with a magic square'

Для получения дополнительной информации о работе с массивами ячеек см. Раздел «Массивы ячеек C++».

Изменение структуры в MEX-функция

MATLAB Data API задает matlab::data::StructArray для представления данных MATLAB struct массивы. Каждое поле в StructArray сам содержит массив. Поэтому можно изменить поле StructArray передается в MEX-функцию, не вызывая копирования всего массива. Поля, не измененные, остаются общими с полями входного массива.

The StructArray класс предоставляет представителя функции для доступа к полям структуры:

  • getFieldNames возвращает начальный и конечный итераторы, обеспечивающие доступ к именам полей.

  • getNumberOfFields возвращает количество полей в структуре.

Например, можно сгенерировать вектор имен полей и использовать эти имена для доступа к данным в структуре. Этот код принимает, что поле содержит массив типа double.

auto fields = myStruct.getFieldNames();
std::vector<matlab::data::MATLABFieldIdentifier> fieldNames(fields.begin(), fields.end());
// Get the data from one field
matlab::data::TypedArray<double> field1 = myStruct[0][fieldNames[0]]

Присвойте новые значения полю путем создания массива правильного типа.

myStruct[0][fieldNames[0]] = factory.createArray<double>({ 1,5 }, { 1, 2, 3, 4, 5 });

Создайте ссылку на поле структуры с помощью matlab::data::Reference<T>, который можно передать функциям, которые получают доступ к значениям в поле. Ссылки позволяют вам получить значение поля, назначить новое значение полю и создать другой массив, который ссылается на то же значение. Для примера этот код создает ссылку на поле, содержащий массив типа double.

auto fields = myStruct.getFieldNames();
std::vector<matlab::data::MATLABFieldIdentifier> fieldNames(fields.begin(), fields.end());
matlab::data::TypedArrayRef<double> field1Reference = myStruct[0][fieldNames[0]]

Присвойте новые значения полю с помощью этой ссылки.

field1Reference = factory.createArray<double>({ 1,5 }, { 1, 2, 3, 4, 5 });

Присвоение структурному полю

Этот пример передает структуру MATLAB в MEX-функцию. Структура содержит два больших поля данных и два поля, содержащие скалярные значения, присвоенные MEX-функцией. MEX-функция вычисляет среднее значение для чисел в каждом массиве и присваивает эти значения Average поле соответствующей структуры.

Этот код MATLAB создает массив структур и передает его в MEX-функцию, созданную из modifyStruct.cpp файл, описанный здесь.

s = struct('Average',{[],[]},...
   'Data',{rand(1,1000),randi([1,9],1,1000)});
s = modifyStruct(s);

Вот MexFunction::operator() функция. Он выполняет следующие операции:

  • Вызовите checkArgument функция для проверки входов и выходов на размер и тип.

  • Назначьте вход matlab::data::StructArray переменная.

  • Вызовите calcMean функция для вычисления средних значений.

  • Присвойте обновленный массив структур выходным данным MEX-функции.

#include "mex.hpp"
#include "mexAdapter.hpp"

using matlab::mex::ArgumentList;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
public:
    void operator()(ArgumentList outputs, ArgumentList inputs) {
        checkArguments(outputs, inputs);
        StructArray inStruct(inputs[0]);
        calcMean(inStruct);
        outputs[0] = inStruct;
    }

The checkArguments функция выполняет следующие проверки:

  • Количество входов равняется единице.

  • Количество выходов не больше единицы.

  • Входной вход является массивом структур MATLAB.

    void checkArguments(ArgumentList outputs, ArgumentList inputs) {
        std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine();
        ArrayFactory factory;
        if (inputs.size() != 1) {
            matlabPtr->feval(u"error", 0,
                std::vector<Array>({ factory.createScalar("One input required") }));
        }
        if (outputs.size() > 1) {
            matlabPtr->feval(u"error", 0,
                std::vector<Array>({ factory.createScalar("Too many outputs specified") }));
        }
        if (inputs[0].getType() != ArrayType::STRUCT) {
            matlabPtr->feval(u"error", 0,
                std::vector<Array>({ factory.createScalar("Input must be structure") }));
        }
    }

The calcMean функция вычисляет среднее значение для каждого значения чисел в Data и присваивает эти значения Average поле соответствующей структуры.

    void calcMean(StructArray& inStruct) {  
        ArrayFactory factory;
        auto fields = inStruct.getFieldNames();
        std::vector<MATLABFieldIdentifier> fieldNames(fields.begin(), fields.end());
        double sm = 0;
        for (auto i = 0; i < 2; i++) {
            const TypedArray<double> data = inStruct[i][fieldNames[1]];
            for (auto& elem : data) {
                sm += elem;
            }
            inStruct[i][fieldNames[0]] = factory.createScalar(sm / data.getNumberOfElements());
        }
    }
};

Пример Камеры и структурных массивов

Для связанного примера, который использует массивы камер и структур, откройте этот исходный файл в редакторе MATLAB phonebook.cpp.

См. также

Похожие темы