15

C++コードで生成した double のベクトルをpythonnumpy 配列に渡そうとしています。Pythonnumpy 配列に入力したら、いくつかのダウンストリーム処理を行い、いくつかの Python 機能を使用したいと考えています。私がやりたい最大のことの 1 つは、物事をプロットできるようにすることです。これに関しては、C++ は少し不器用です。また、Python の統計力を活用できるようにしたいと考えています。

私はそれを行う方法についてあまり明確ではありませんが。Python C API のドキュメントを読むのに多くの時間を費やしました。どうやらそのトリックを実行できる関数 PyArray_SimpleNewFromData に出くわしました。コードの全体的な設定に関する限り、私はまだ非常に不明確です。このプロセスを理解するのに役立つ、特定の非常に単純なテスト ケースを作成しています。Visual Studio Express 2012 でスタンドアロンの空のプロジェクトとして次のコードを生成しました。このファイルを Project1 と呼びます。

#include <Python.h>
#include "C:/Python27/Lib/site-packages/numpy/core/include/numpy/arrayobject.h"

PyObject * testCreatArray()
{
    float fArray[5] = {0,1,2,3,4};
    npy_intp m = 5;
    PyObject * c = PyArray_SimpleNewFromData(1,&m,PyArray_FLOAT,fArray);
    return c; 
}

私の目標は、Python で PyObject を読み取れるようにすることです。Python でこのモジュールを参照する方法がわからないため、行き詰まっています。特に、このプロジェクトを Python からインポートするにはどうすればよいですか。Python のプロジェクト パスから Project1 をインポートしようとしましたが、失敗しました。この基本的なケースを理解したら、次の目標は、メイン関数で計算したベクトル コンテナーを Python に渡す方法を見つけることです。それを行う方法もわかりません。

これについて私を助けることができる専門家、または単純なC ++ベクトルからnumpy配列を読み込んで設定するいくつかのコードの単純なよく含まれた例を投稿することができる専門家は、感謝します。よろしくお願いします。

4

4 に答える 4

10

私は cpp ヒーローではありませんが、ソリューションに 1D と 2D ベクトルの 2 つのテンプレート関数を提供したいと考えていました。これは l8ter を使用するための 1 つのライナーであり、1D および 2D ベクトルをテンプレート化することにより、コンパイラはベクトル形状に適したバージョンを取得できます。2Dの場合、異形の場合は文字列を投げます。ルーチンはここにデータをコピーしますが、単純な「表現」にするために、入力ベクトルの最初の要素のアドレスを取得するように簡単に変更できます。

使用法は次のようになります。

// Random data
vector<float> some_vector_1D(3,1.f); // 3 entries set to 1
vector< vector<float> > some_vector_2D(3,vector<float>(3,1.f)); // 3 subvectors with 1

// Convert vectors to numpy arrays
PyObject* np_vec_1D = (PyObject*) vector_to_nparray(some_vector_1D);
PyObject* np_vec_2D = (PyObject*) vector_to_nparray(some_vector_2D);

オプションの引数によって、numpy 配列のタイプを変更することもできます。テンプレート関数は次のとおりです。

/** Convert a c++ 2D vector into a numpy array
 *
 * @param const vector< vector<T> >& vec : 2D vector data
 * @return PyArrayObject* array : converted numpy array
 *
 * Transforms an arbitrary 2D C++ vector into a numpy array. Throws in case of
 * unregular shape. The array may contain empty columns or something else, as
 * long as it's shape is square.
 *
 * Warning this routine makes a copy of the memory!
 */
template<typename T>
static PyArrayObject* vector_to_nparray(const vector< vector<T> >& vec, int type_num = PyArray_FLOAT){

   // rows not empty
   if( !vec.empty() ){

      // column not empty
      if( !vec[0].empty() ){

        size_t nRows = vec.size();
        size_t nCols = vec[0].size();
        npy_intp dims[2] = {nRows, nCols};
        PyArrayObject* vec_array = (PyArrayObject *) PyArray_SimpleNew(2, dims, type_num);

        T *vec_array_pointer = (T*) PyArray_DATA(vec_array);

        // copy vector line by line ... maybe could be done at one
        for (size_t iRow=0; iRow < vec.size(); ++iRow){

          if( vec[iRow].size() != nCols){
             Py_DECREF(vec_array); // delete
             throw(string("Can not convert vector<vector<T>> to np.array, since c++ matrix shape is not uniform."));
          }

          copy(vec[iRow].begin(),vec[iRow].end(),vec_array_pointer+iRow*nCols);
        }

        return vec_array;

     // Empty columns
     } else {
        npy_intp dims[2] = {vec.size(), 0};
        return (PyArrayObject*) PyArray_ZEROS(2, dims, PyArray_FLOAT, 0);
     }


   // no data at all
   } else {
      npy_intp dims[2] = {0, 0};
      return (PyArrayObject*) PyArray_ZEROS(2, dims, PyArray_FLOAT, 0);
   }

}


/** Convert a c++ vector into a numpy array
 *
 * @param const vector<T>& vec : 1D vector data
 * @return PyArrayObject* array : converted numpy array
 *
 * Transforms an arbitrary C++ vector into a numpy array. Throws in case of
 * unregular shape. The array may contain empty columns or something else, as
 * long as it's shape is square.
 *
 * Warning this routine makes a copy of the memory!
 */
template<typename T>
static PyArrayObject* vector_to_nparray(const vector<T>& vec, int type_num = PyArray_FLOAT){

   // rows not empty
   if( !vec.empty() ){

       size_t nRows = vec.size();
       npy_intp dims[1] = {nRows};

       PyArrayObject* vec_array = (PyArrayObject *) PyArray_SimpleNew(1, dims, type_num);
       T *vec_array_pointer = (T*) PyArray_DATA(vec_array);

       copy(vec.begin(),vec.end(),vec_array_pointer);
       return vec_array;

   // no data at all
   } else {
      npy_intp dims[1] = {0};
      return (PyArrayObject*) PyArray_ZEROS(1, dims, PyArray_FLOAT, 0);
   }

}
于 2016-09-04T15:27:45.550 に答える
5

この種のものを探しているかもしれない人々にとって実際に役立つこれに対する答えがないので、私は簡単な解決策を考えました.

まず、C++ で python 拡張モジュールを作成する必要があります。これは簡単に行うことができ、すべて python c-api のドキュメントに記載されているため、ここでは説明しません。

C++ std::vector を numpy 配列に変換するのは非常に簡単です。最初に numpy 配列ヘッダーをインポートする必要があります

#include <numpy/arrayobject.h>

初期化関数では、 import_array() が必要です

PyModINIT_FUNC
inittestFunction(void){
   (void) Py_InitModule("testFunction". testFunctionMethods);
   import_array();
}

提供されている numpy 配列関数を使用できるようになりました。OPが数年前にPyArray_SimpleNewFromDataと言ったように、これに必要なものは、愚かなほど簡単に使用できます。必要なのは npy_intp 型の配列だけです。これは、作成される配列の形状です。testVector.size() を使用してベクトルと同じであることを確認します (複数の次元の場合は、testVector[0].size()、testVector[0][0].size() などを実行します。ベクトルは連続していることが保証されていますboolでない限り、c++ 11で)。

//create testVector with data initialised to 0
std::vector<std::vector<uint16_t>> testVector;
testVector.resize(width, std::vector<uint16_t>(height, 0);
//create shape for numpy array
npy_intp dims[2] = {width, height}
//convert testVector to a numpy array
PyArrayObject* numpyArray = (PyArrayObject*)PyArray_SimpleNewFromData(2, dims, NPY_UINT16, (uint16_t*)testVector.data());

パラメータを通過します。まず、それを PyArrayObject にキャストする必要があります。そうしないと、PyObject になり、python に返されたときに numpy 配列にはなりません。2 は配列の次元数です。dims は配列の形状です。これは npy_intp 型である必要があります。NPY_UINT16 は、配列が python になるデータ型です。次に、testVector.data() を使用して配列のデータを取得し、これを void* またはベクターと同じデータ型のポインターにキャストします。

これがこれを必要とする他の誰かに役立つことを願っています。

(また、純粋な速度が必要ない場合は、C-API の使用を避けることをお勧めします。C-API を使用するとかなりの問題が発生するため、cython または swig がおそらく最良の選択です。非常に役立つ c 型もあります。

于 2015-12-18T10:03:57.500 に答える