序列化OpenCV Mat_ <vec3f> </vec3f>

时间:2010-11-13 03:08:56

标签: c++ serialization opencv

我正在开展机器人研究项目,我需要序列化3D点的2D矩阵:基本上每个像素都是3个浮点数矢量。这些像素保存在OpenCV矩阵中,它们需要通过进程间通信发送并保存到文件中以便在多台计算机上进行处理。我想尽快以 endian / architecture-independent,space-efficient 的方式序列化它们。 cv::imencode here是完美的,除了它只适用于8位和16位元素,我们不想失去任何精度。这些文件不需要是人类可读的(尽管我们现在这样做是为了确保数据的可移植性,而且速度非常慢)。是否有最佳实践或优雅的方法?

谢谢!

7 个答案:

答案 0 :(得分:17)

之前的答案很好,但是当你想要序列化感兴趣的区域(以及其他内容)时,它们不适用于非连续矩阵。此外,没有必要序列化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

答案 1 :(得分:16)

编辑:Christoph Heindl对这篇文章发表了评论,并附有他博客的链接,他对此序列化代码进行了改进。强烈推荐!

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

-

对于任何可能受益的人:一些代码来序列化Mat&amp;使用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];
        }
    }

}
}

现在,mat可以序列化和反序列化如下:

    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 :(得分:1)

您可以使用boost::serialization。它经过了大量优化,非常易于集成。

您案例的可能加速包括将每个对象序列化为原始二进制块(请参阅boost::serialization::make_binary)并禁用版本跟踪(BOOST_SERIALIZATION_DISABLE_TRACKING)。

此外,您可以尝试在序列化例程中添加压缩以节省空间(以及在易于压缩的数据的情况下时间)。例如,可以使用boost::iostreams来实现此目的。

答案 3 :(得分:0)

我最近问自己一个类似的问题,不过我特意试图序列化opencv的MatMatND个对象。使用boost::serialize很不错,但需要一些技巧。由于您不想修改OpenCV本身的内部以序列化这些对象,因此您不得不使用所谓的“自由”函数。由于序列化OpenCV对象很复杂,我发现我被迫将序列化操作拆分为保存和加载,每个操作的实现略有不同。您需要使用boost/serialization/split_free.hpp执行此任务。 Boost在此提供了良好的文档:http://www.boost.org/doc/libs/1_45_0/libs/serialization/doc/index.html

祝你好运!

答案 4 :(得分:0)

如何将您的Mat转换为矢量并使用fwrite?

转换为矢量过程可能会影响性能,但它是安全的。我怀疑上面的所有答案,无论是在接受的答案中循环遍历图像数据,还是在Christoph帖子中使用make_array,都假设您的Mat数据是连续的,但情况不一定如此。当您的Mat数据不连续时,这些答案的输出将不正确。

答案 5 :(得分:0)

您也可以使用msgpack 创建适配器https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_adaptor 这是示例代码。可能有用:

template<class Derived>
MatrixXd Math::gradient(const DenseBase<Derived> &y, const Dimension dimension)
{
    ...
    typename Derived::PlainObject v = dimension == COLUMNS ? y.derived().eval() : y.derived().transpose().eval();
    ...
}

答案 6 :(得分:0)

我写了这段代码:

/*
Will save in the file:
cols\n
rows\n
elemSize\n
type\n
DATA
*/
void serializeMatbin(Mat& mat, std::string filename){
    if (!mat.isContinuous()) {
        cout << "Not implemented yet" << endl;
        exit(1);
    }
    int elemSizeInBytes = (int)mat.elemSize();
    int elemType        = (int)mat.type();
    int dataSize        = (int)(mat.cols * mat.rows * mat.elemSize());

    FILE* FP = fopen(filename.c_str(), "wb");
    int sizeImg[4] = {mat.cols, mat.rows, elemSizeInBytes, elemType };
    fwrite(/*buffer*/ sizeImg, /*howmanyelements*/ 4, /* size of each element */ sizeof(int), /*file*/ FP);
    fwrite(mat.data, mat.cols * mat.rows, elemSizeInBytes, FP);
    fclose(FP);
}

Mat deserializeMatbin(std::string filename){
    FILE* fp = fopen(filename.c_str(), "r");
    int header[4];
    fread(header, sizeof(int), 4, fp);
    int cols = header[0]; 
    int rows = header[1];
    int elemSizeInBytes = header[2];
    int elemType = header[3];

    Mat outputMat = Mat(rows, cols, elemType);

    fread(outputMat.data, elemSizeInBytes, cols * rows, fp);
    fclose(fp);
    return outputMat;
}

void testSerializeMatbin(){
    Mat a = Mat::ones(/*cols*/ 10, /* rows */ 5, CV_8U) * 2;
    std::string filename = "test.matbin";
    serializeMatbin(a, filename);
    Mat b = deserializeMatbin(filename);
    cout << "Rows: " << b.rows << " Cols: " << b.cols << " type: " << b.type()<< endl;
}