【问题标题】:Eigen and boost::serialize特征和提升::序列化
【发布时间】:2013-08-25 07:15:39
【问题描述】:

我尝试编写一个通用的序列化函数,它采用任何密集矩阵并将其序列化: 其他一些有帮助但不是最后的问题在这里: Question1Question2

我尝试了以下应该可以工作的方法:

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&lt;double,4,4&gt;

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

编译器无法匹配上面的模板? 并给出以下错误:

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

【问题讨论】:

    标签: c++ templates serialization boost eigen


    【解决方案1】:

    我已经测试了你的代码,当我尝试编译它时它也没有工作。但是,根据 Boost Serialize 的文档,我的印象是它旨在与流运算符

    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&lt;Derived&gt; 替换为实际使用的 Matrix 类型。我正在使用的当前代码是:

    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 日):

    尽管上述代码看起来可以正常工作,但当我尝试将其完全集成到我的代码中并通过适当的单元测试对其进行练习时,它停止了工作。不幸的是,我收到的错误消息——来自 Visual Studio 和 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
    

    上面代码的几个关键点:

    • 此代码现在具有所有 Eigen 矩阵参数的模板化参数。这应该允许它与所有类型的矩阵和向量一起工作,无论是在编译时还是在运行时调整大小。这是对上述代码的重大改进

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

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

    • 我不能说我已经对这段代码进行了全面的压力测试。如果您(或其他任何人)发现任何其他问题,请告诉我,我会尝试跟进我发现的任何其他问题。

    相信这会有所帮助。

    施穆尔

    【讨论】:

    • 好评:-)!:我还是没有答案,为什么编译器不能匹配它... :-)
    • @Gabriel,我不确定您的编译器到底提供了什么错误消息。从我自己(非常令人沮丧)的尝试中,我会注意到我收到的错误消息非常无益。请再看看我的答案,因为我已经用我自己尝试使它工作的结果再次更新了它。
    • @Gabriel,您能告诉我答案中的代码更新版本是否解决了您的问题吗?我目前在我自己的应用程序中使用上面的确切代码(带有差异命名空间),它现在可以完美运行。如果它对您有用,如果您能接受这个答案,我将不胜感激。谢谢!
    • 谢谢,是的,这段代码到目前为止还不错:对我来说主要的缺点是它不能处理 Eigen::Base 类型和其他唯一的 Eigen:.Matrix 类型,如果允许其他类型来代替会很酷只有 Matrix 类,假设我们要序列化 ​​m.tail&lt;4&gt;()。但我认为这行不通,因为我认为 ADL ......?我很快就会在我的框架中测试你的通用代码!
    • 如果您要使用后端 data() 缓冲区进行序列化,您是否还需要担心步幅和存储顺序(又名标志)?如果不是,那么您可以序列化一个行序矩阵并反序列化一个列序矩阵?
    【解决方案2】:

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

    • 无论矩阵是行优先还是列优先,都能正常工作
    • 使用名称-值-对,因此它适用于 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
    

    【讨论】:

      【解决方案3】:

      我使用Eigen's plugin based extension:

      /**
       * @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() 这样的表达式,但对于像 mat.topRows() 这样的表达式或块操作可能会失败(没有任何编译错误)。 (参见更新:现在也适用于子矩阵)

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


      /// 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");
      

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

      【讨论】:

        猜你喜欢
        • 2011-10-17
        • 2012-09-24
        • 2011-09-07
        • 2015-04-22
        • 2018-12-22
        • 2012-11-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多