Сериализация OpenCV Mat_ <Vec3f>

Я работаю над исследовательским проектом по робототехнике, где мне нужно сериализовать 2D-матрицы трехмерных точек: в основном каждый пиксель представляет собой 3-векторный поплавок. Эти пиксели сохраняются в матрице OpenCV, и их необходимо отправлять по межпроцессной связи и сохранять в файлах, которые будут обрабатываться на нескольких компьютерах. Я бы хотел как можно быстрее сериализовать их в независимом от endian/architecture-space, экономичном пространстве. cv::imencode здесь будет идеальным, за исключением того, что он работает только на 8-битных и 16-битных элементах, и мы не хотим терять любой точности. Файлы не должны быть удобочитаемыми (хотя мы делаем это сейчас, чтобы обеспечить переносимость данных, и это невероятно медленно). Есть ли лучшие методы для этого, или изящные способы сделать это?

Спасибо!

Ответы

Ответ 1

Редактировать: Christoph Heindl прокомментировал это сообщение со ссылкой на свой блог, где он улучшил этот код сериализации. Очень рекомендуется!

http://cheind.wordpress.com/2011/12/06/serialization-of-cvmat-objects-using-boost/

-

Для тех, кому это может пригодиться: некоторый код для сериализации Mat & с boost:: serialization
Я не тестировал многоканальные данные, но все должно работать нормально.

#include <iostream>
#include <fstream>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/vector.hpp>

BOOST_SERIALIZATION_SPLIT_FREE(Mat)
namespace boost {
namespace serialization {

    /*** Mat ***/
    template<class Archive>
    void save(Archive & ar, const Mat& m, const unsigned int version)
    {
      size_t elemSize = m.elemSize(), elemType = m.type();

      ar & m.cols;
      ar & m.rows;
      ar & elemSize;
      ar & elemType; // element type.
      size_t dataSize = m.cols * m.rows * m.elemSize();

      //cout << "Writing matrix data rows, cols, elemSize, type, datasize: (" << m.rows << "," << m.cols << "," << m.elemSize() << "," << m.type() << "," << dataSize << ")" << endl;

      for (size_t dc = 0; dc < dataSize; ++dc) {
          ar & m.data[dc];
      }
    }

    template<class Archive>
    void load(Archive & ar, Mat& m, const unsigned int version)
    {
        int cols, rows;
        size_t elemSize, elemType;

        ar & cols;
        ar & rows;
        ar & elemSize;
        ar & elemType;

        m.create(rows, cols, elemType);
        size_t dataSize = m.cols * m.rows * elemSize;

        //cout << "reading matrix data rows, cols, elemSize, type, datasize: (" << m.rows << "," << m.cols << "," << m.elemSize() << "," << m.type() << "," << dataSize << ")" << endl;

        for (size_t dc = 0; dc < dataSize; ++dc) {
                  ar & m.data[dc];
        }
    }

}
}

Теперь мат можно сериализовать и десериализовать следующим образом:

    void saveMat(Mat& m, string filename) {
            ofstream ofs(filename.c_str());
            boost::archive::binary_oarchive oa(ofs);
            //boost::archive::text_oarchive oa(ofs);
            oa << m;
    }

    void loadMat(Mat& m, string filename) {
            std::ifstream ifs(filename.c_str());
            boost::archive::binary_iarchive ia(ifs);
            //boost::archive::text_iarchive ia(ifs);
            ia >> m;
    }

Я использовал здесь binary_oarchive и binary_iarchive, чтобы сохранить использование памяти. Бинарный формат не обеспечивает переносимости между платформами, но при желании можно использовать text_oarchive/iarchive.

Ответ 2

Более ранние ответы хороши, но они не будут работать для не-непрерывных матриц, которые возникают, когда вы хотите сериализовать интересующие регионы (между прочим). Кроме того, нет необходимости сериализовывать elemSize(), потому что это происходит из значения type.

Здесь некоторый код, который будет работать независимо от непрерывности (с включением/пространством имен)

#pragma once

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/utility.hpp>
#include <opencv2/opencv.hpp>

namespace boost {
namespace serialization {

template<class Archive>
void serialize(Archive &ar, cv::Mat& mat, const unsigned int)
{
    int cols, rows, type;
    bool continuous;

    if (Archive::is_saving::value) {
        cols = mat.cols; rows = mat.rows; type = mat.type();
        continuous = mat.isContinuous();
    }

    ar & cols & rows & type & continuous;

    if (Archive::is_loading::value)
        mat.create(rows, cols, type);

    if (continuous) {
        const unsigned int data_size = rows * cols * mat.elemSize();
        ar & boost::serialization::make_array(mat.ptr(), data_size);
    } else {
        const unsigned int row_size = cols*mat.elemSize();
        for (int i = 0; i < rows; i++) {
            ar & boost::serialization::make_array(mat.ptr(i), row_size);
        }
    }

}

} // namespace serialization
} // namespace boost

Ответ 3

Вы можете использовать boost::serialization для этого. Он сильно оптимизирован и довольно легко интегрируется.

Возможные ускорения для вашего случая включают сериализацию каждого объекта в виде необработанного двоичного блока (см. boost::serialization::make_binary) и отключение отслеживания версий (BOOST_SERIALIZATION_DISABLE_TRACKING).

Кроме того, вы можете поэкспериментировать с добавлением сжатия в свои процедуры сериализации для экономии места (и времени в случае легко сжимаемых данных). Это может быть реализовано, например, с помощью boost::iostreams.

Ответ 4

Недавно я задавал себе аналогичный вопрос, хотя я специально пытался сериализовать opencv Mat и MatND объекты. Использование boost::serialize приятно, но требует нескольких трюков. Поскольку вы не хотите самостоятельно модифицировать внутренние компоненты OpenCV для сериализации этих объектов, вы вынуждены использовать то, что называется "свободной" функцией. Поскольку сложно сериализовать объекты OpenCV, я обнаружил, что мне пришлось разделить операцию сериализации на сохранение и загрузку, каждая из которых имеет немного другую реализацию. Для этой задачи вам нужно использовать boost/serialization/split_free.hpp. Boost предоставляет хорошую документацию для этого здесь: http://www.boost.org/doc/libs/1_45_0/libs/serialization/doc/index.html.

Удачи!

Ответ 5

Как насчет того, чтобы просто преобразовать свой Mat в вектор и использовать fwrite?

Преобразование в векторный процесс может повлиять на производительность, но это безопасно. Я подозреваю, что все приведенные выше ответы, либо обход данных изображения, как в принятом ответе, либо использование make_array, как и в столбце Christoph, предполагают, что ваши данные Mat являются смежными, что не обязательно так. Когда ваши данные Mat не будут смежными, результат этих ответов будет неправильным.