Eigen and boost::serialize

Eigen and boost::serialize

本文关键字:serialize boost and Eigen      更新时间:2023-10-16

我试图编写一个通用的序列化函数,它接受任何密集矩阵并对其进行序列化:以下是其他一些有帮助但并非最终的问题:问题1问题2

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

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|error:"class Eigen::Matrix"没有名为"serialize"的成员|

我已经测试了您的代码,当我试图编译它时它也不起作用。但是,根据boost serialize的文档,我认为它是用来与流运算符一起使用的<lt;。以下代码对我来说很好:

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;
}

文件my_archive是在具有非零值的工作文件夹中创建的(只是内存中未初始化的垃圾,上面的代码过于简化)。

编辑:我尝试在自己的应用程序中使用上面的代码,发现我收到了和您相同的错误。老实说,我现在不明白为什么。我发现的最简单的修复方法是用实际使用的矩阵类型替换Eigen::EigenBase<Derived>。我目前使用的代码是:

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 ){ /* ... */ }
     }
}

上面的代码适用于任何标量类型(float、double、int)和动态/运行时大小的矩阵。对于静态大小,请检查并相应地更新模板参数。

第2版(2014年4月9日):

尽管上面的代码看起来可以工作,但当我试图将它完全集成到我的代码中,并通过适当的单元测试进行练习时,它停止了工作。不幸的是,我收到的错误消息——来自VisualStudio和clang——都是毫无帮助的。幸运的是,gcc已经隐藏在错误消息的可怕混乱中,这是对CV不匹配的引用,这似乎让我能够完全解决这个问题。

下面的代码现在显示为已成功编译并运行。我试着让格式清晰易读(没有侧滚动)——希望下面的代码是清晰的:

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

以上代码的几个关键点:

  • 这个代码现在已经为所有的特征矩阵参数模板化了参数。这应该允许它处理所有类型的矩阵和向量,无论是在编译时还是在运行时调整大小。这是对上述代码的一个重大增强

  • 序列化代码必须拆分为单独的saveload函数。否则,反序列化代码将不会调整矩阵的大小以容纳原始数据。我相信Boost::Serialize确实提供了一些额外的函数,这些函数可以重载以执行序列化或反序列化操作,但这种方法更容易实现。

  • save方法上的const限定符是必不可少的。在一个不明显的g++错误让我陷入困境之前,这就是我麻烦的根源。

  • 我不能说我已经对这个代码进行了充分的压力测试。如果你(或其他任何人)发现它还有任何问题,请告诉我,我会尽力跟进我发现的其他问题。

相信这会有所帮助。

Shmuel

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

  • 无论矩阵是行还是列,都能正确工作
  • 使用名称-值对,因此可以与xml档案等一起使用
  • 不需要在保存和加载版本之间进行拆分
  • 为Eigen::Transform(Affine3d、Isometry3f等)添加了一个包装函数
  • 单元针对各种组合进行了测试,例如,当您保存列major 2x2 MatrixXd并在使用XML归档时将其加载为行major 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

我使用了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以使用此pulgin:(只需在包含任何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");

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