11

我尝试编写一个通用的序列化函数,该函数采用任何密集矩阵并将其序列化: 其他一些有帮助但没有结束的问题在这里: Question1 Question2

我尝试了以下应该有效的方法:

namespace boost {
namespace serialization {
    template<class Archive, typename Derived> void serialize(Archive & ar,  Eigen::EigenBase<Derived> & g, const unsigned int version)
    {
        ar & boost::serialization::make_array(g.derived().data(), g.size());
    }
    }; // namespace serialization
}; // namespace boost

当我尝试序列化一个Eigen::Matrix<double,4,4>

Eigen::Matrix<double,4,4> a; 
boost::serialize(ar, a);

编译器不能以某种方式与上面的模板不匹配?并给出以下错误:

/usr/local/include/boost/serialization/access.hpp|118|错误:'class Eigen::Matrix' 没有名为'serialize'的成员|

4

3 回答 3

10

I've tested out your code and it also did not work when I tried to compile it. However, based on the documentation for Boost Serialize, I am under the impression that it is intended to be used with the stream operator <<. The following code works fine for me:

namespace boost {
   namespace serialization {
      template <class Archive, typename Derived> 
      void serialize( Archive & ar, Eigen::EigenBase<Derived> & g, const unsigned int version){
          ar & boost::serialization::make_array(g.derived().data(), g.size());
      }
   }
}

int main (int argc, char* argv[]){
    std::ofstream out("my_archive");
    boost::archive::text_oarchive oa (out);

    Eigen::Matrix <double, 4, 4> a;
    out << a;
    return 0;
}

The file my_archive is created in the working folder with non-zero values (just the uninitialized garbage in memory, with the above over-simplified code).

EDIT: I tried using the exact code above in my own application and found that I received the same error as you did. I honestly do not understand why that it, right now. The simplest fix that I found was to replace Eigen::EigenBase<Derived> with the actual Matrix type being used. The current code that I am using is:

namespace boost{
    namespace serialization {
        template <class Archive, typename Scalar>
        void serialize ( Archive & ar, Eigen::Matrix<Scalar, -1, -1, 0, -1, -1> & g, const unsigned int version ){ /* ... */ }
     }
}

The above code works for any scalar type (float, double, int) and for dynamic/runtime sized matrixes. For static sized, check into and update the template parameters accordingly.

EDIT #2 (April 09, 2014):

Despite the appearance of the above code working, when I tried to integrate it fully into my code and exercise it with appropriate unit testing, it stopped working. Unfortunately, the error messages that I was being given -- both from Visual Studio and clang -- were most unhelpful. Fortunately, gcc had buried within the horrible mess of error messages, a reference to CV-mismatch, which seems to have allowed me to fully address this.

The following code now appears to compile and run successfully. I've tried to make the formatting legible (without side-scrolling) -- hopefully the code below is clear:

namespace boost{
    namespace serialization{

        template<   class Archive, 
                    class S, 
                    int Rows_, 
                    int Cols_, 
                    int Ops_, 
                    int MaxRows_, 
                    int MaxCols_>
        inline void save(
            Archive & ar, 
            const Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g, 
            const unsigned int version)
            {
                int rows = g.rows();
                int cols = g.cols();

                ar & rows;
                ar & cols;
                ar & boost::serialization::make_array(g.data(), rows * cols);
            }

        template<   class Archive, 
                    class S, 
                    int Rows_,
                    int Cols_,
                    int Ops_, 
                    int MaxRows_, 
                    int MaxCols_>
        inline void load(
            Archive & ar, 
            Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g, 
            const unsigned int version)
        {
            int rows, cols;
            ar & rows;
            ar & cols;
            g.resize(rows, cols);
            ar & boost::serialization::make_array(g.data(), rows * cols);
        }

        template<   class Archive, 
                    class S, 
                    int Rows_, 
                    int Cols_, 
                    int Ops_, 
                    int MaxRows_, 
                    int MaxCols_>
        inline void serialize(
            Archive & ar, 
            Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g, 
            const unsigned int version)
        {
            split_free(ar, g, version);
        }


    } // namespace serialization
} // namespace boost

A few critical points on the above code:

  • This code now has templated parameters for all of Eigen's Matrix parameters. This should allow it to work with all types of matrices and vectors, whether sized at compile-time or run-time. This is a major enhancement over the above code

  • It is essential that the serialization code be split into separate save and load functions. Otherwise, the deserialization code will not resize the matrix to hold the original data. I believe that Boost::Serialize does provide some additional functions that can be overloaded to perform operations on serialization or deserialization, but this approach was easier to implement.

  • The const qualifier on the save method is essential. This was the source of my troubles before an obscure g++ error keyed me in on this.

  • I cannot say that I have fully stress-tested this code yet. If you (or anyone else) finds any more problems with it, please do let me know and I'll try and follow up with anything else that I find.

Trust that that helps.

Shmuel

于 2014-04-07T03:20:01.270 回答
7

这是一个更通用、更短的版本,具有以下功能:

  • 无论矩阵是行优先还是列优先,都可以正常工作
  • 使用名称-值-对,因此它适用于 xml 档案等
  • 不需要在保存和加载版本之间进行拆分
  • 为 Eigen::Transform (Affine3d, Isometry3f 等) 添加一个包装函数
  • 对各种组合进行了单元测试,例如,当您保存列优先的 2x2 MatrixXd 并在使用 XML 档案时将其加载为行优先的 Eigen::Matrix 时,它可以工作。然而,这显然不适用于二进制或文本档案!在这些情况下,类型必须完全匹配

代码:

namespace boost { namespace serialization {

template<   class Archive,
            class S,
            int Rows_,
            int Cols_,
            int Ops_,
            int MaxRows_,
            int MaxCols_>
inline void serialize(Archive & ar,
                      Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & matrix,
                      const unsigned int version)
{
  int rows = matrix.rows();
  int cols = matrix.cols();
  ar & make_nvp("rows", rows);
  ar & make_nvp("cols", cols);    
  matrix.resize(rows, cols); // no-op if size does not change!

  // always save/load row-major
  for(int r = 0; r < rows; ++r)
    for(int c = 0; c < cols; ++c)
      ar & make_nvp("val", matrix(r,c));
}    

template<   class Archive,
            class S,
            int Dim_,
            int Mode_,
            int Options_>
inline void serialize(Archive & ar,
                      Eigen::Transform<S, Dim_, Mode_, Options_> & transform,
                      const unsigned int version)
{
  serialize(ar, transform.matrix(), version);
}    
}} // namespace boost::serialization
于 2016-01-29T00:22:55.553 回答
6

我使用Eigen 的基于插件的扩展

/**
 * @file EigenDenseBaseAddons.h
 */
#ifndef EIGEN_DENSE_BASE_ADDONS_H_
#define EIGEN_DENSE_BASE_ADDONS_H_

friend class boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
  derived().eval();
  const Index rows = derived().rows(), cols = derived().cols();
  ar & rows;
  ar & cols;
  for (Index j = 0; j < cols; ++j )
    for (Index i = 0; i < rows; ++i )
      ar & derived().coeff(i, j);
}

template<class Archive>
void load(Archive & ar, const unsigned int version) {
  Index rows, cols;
  ar & rows;
  ar & cols;
  if (rows != derived().rows() || cols != derived().cols() )
    derived().resize(rows, cols);
  ar & boost::serialization::make_array(derived().data(), derived().size());
}

template<class Archive>
void serialize(Archive & ar, const unsigned int file_version) {
  boost::serialization::split_member(ar, *this, file_version);
}

#endif // EIGEN_DENSE_BASE_ADDONS_H_

配置 Eigen 以使用此插件:(只需在包含任何 Eigen 标头之前定义宏)

#ifndef EIGEN_CONFIG_H_
#define EIGEN_CONFIG_H_

#include <boost/serialization/array.hpp>
#define EIGEN_DENSEBASE_PLUGIN "EigenDenseBaseAddons.h"

#include <Eigen/Core>

#endif // EIGEN_CONFIG_H_

虽然我还没有真正彻底地测试过,但它运行良好,也可以处理 Array 或任何其他密集的 Eigen 对象。它也适用于像 vec.tail<4>() 这样的表达式,但对于像 mat.topRows<2>() 这样的表达式或块操作可能会失败(没有任何编译错误)。(见更新:现在也适用于子矩阵)

与当前的其他答案相比,这适用于更多的表达方式,并且可能会避免一些暂时的。PlainObjectBase<Derived>通过将对象传递给序列化函数,也可能实现非侵入式版本。


/// Boost Serialization Helper

template <typename T>
bool serialize(const T& data, const std::string& filename) {
  std::ofstream ofs(filename.c_str(), std::ios::out);
  if (!ofs.is_open())
    return false;
  {
    boost::archive::binary_oarchive oa(ofs);
    oa << data;
  }
  ofs.close();
  return true;
}

template <typename T>
bool deSerialize(T& data, const std::string& filename) {
  std::ifstream ifs(filename.c_str(), std::ios::in);
  if (!ifs.is_open())
    return false;
  {
    boost::archive::binary_iarchive ia(ifs);
    ia >> data;
  }
  ifs.close();
  return true;
}

还有一些测试代码:

VectorXf vec(100);
vec.setRandom();
serializeText(vec.tail<5>(), "vec.txt");

MatrixXf vec_in;
deSerialize(vec_in, "vec.bin");
assert(vec_in.isApprox(vec.tail<5>()));

serialize(Vector2f(0.5f,0.5f), "a.bin");
Vector2f a2f;
deSerializeBinary(a2f, "a.bin");
assert(a2f.isApprox(Vector2f(0.5f,0.5f)));
VectorXf axf;
deSerialize(axf, "a.bin");
assert(aXf.isApprox(Vector2f(0.5f,0.5f)));

boost::shared_ptr<Vector4f> b = boost::make_shared<Vector4f>(Vector4f::Random());
serialize(b, "b.tmp");
boost::shared_ptr<Vector4f> b_in;
deSerialize(b_in, "b.tmp");
BOOST_CHECK_EQUAL(*b, *b_in);

Matrix4f m(Matrix4f::Random());
serialize(m.topRows<2>(), "m.bin");
deSerialize(m_in, "m.bin");

更新:我做了一些小的修改,现在子矩阵的序列化也可以了。

于 2014-05-01T11:59:08.843 回答