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

コードをテストしましたが、コンパイルしようとしても機能しませんでした。ただし、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<Derived>、実際に使用されている 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 の Matrix パラメーターのテンプレート パラメーターが含まれています。これにより、コンパイル時または実行時のサイズに関係なく、すべてのタイプの行列とベクトルで動作できるようになります。これは、上記のコードに対する主要な機能強化です。

  • シリアライゼーション コードを個別の保存関数と読み込み関数に分割することが不可欠です。それ以外の場合、逆シリアル化コードは元のデータを保持するためにマトリックスのサイズを変更しません。Boost::Serialize は、シリアライゼーションまたはデシリアライゼーションで操作を実行するためにオーバーロードできるいくつかの追加機能を提供すると思いますが、このアプローチは実装が簡単でした。

  • メソッドのconst修飾子saveは必須です。あいまいなg ++エラーがこれにつながる前に、これが私のトラブルの原因でした。

  • このコードのストレス テストを完全に行ったとは言えません。あなた (または他の誰か) がこれ以上の問題を見つけた場合は、私に知らせてください。

それが役立つと信じてください。

シュムエル

于 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_

この pulgin を使用するように 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 に答える