C++ 如何从 boost::python 返回 numpy.array?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10701514/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:21:41  来源:igfitidea点击:

how to return numpy.array from boost::python?

c++arraysnumpyboost-python

提问by eudoxos

I would like to return some data from c++ code as a numpy.arrayobject. I had a look at boost::python::numeric, but its documentation is very terse. Can I get an example of e.g. returning a (not very large) vector<double>to python? I don't mind doing copies of data.

我想从 C++ 代码中返回一些数据作为numpy.array对象。我看了看boost::python::numeric,但它的文档非常简洁。我能得到一个例子,例如将一个(不是很大)返回vector<double>给 python 吗?我不介意复制数据。

采纳答案by jbosch

UPDATE:the library described in my original answer (https://github.com/ndarray/Boost.NumPy) has been integrated directly into Boost.Python as of Boost 1.63, and hence the standalone version is now deprecated. The text below now corresponds to the new, integrated version (only the namespace has changed).

更新:从Boost 1.63 开始,我的原始答案 ( https://github.com/ndarray/Boost.NumPy) 中描述的库已直接集成到 Boost.Python 中,因此独立版本现已弃用。下面的文本现在对应于新的集成版本(只有命名空间发生了变化)。

Boost.Python now includes a moderately complete wrapper of the NumPy C-API into a Boost.Python interface. It's pretty low-level, and mostly focused on how to address the more difficult problem of how to pass C++ data to and from NumPy without copying, but here's how you'd do a copied std::vector return with that:

Boost.Python 现在将 NumPy C-API 的适度完整包装器包含到 Boost.Python 接口中。这是相当低级的,主要集中在如何解决更困难的问题,即如何在不复制的情况下将 C++ 数据传入和传出 NumPy,但这里是如何使用复制的 std::vector 返回:

#include "boost/python/numpy.hpp"

namespace bp = boost::python;
namespace bn = boost::python::numpy;

std::vector<double> myfunc(...);

bn::ndarray mywrapper(...) {
    std::vector<double> v = myfunc(...);
    Py_intptr_t shape[1] = { v.size() };
    bn::ndarray result = bn::zeros(1, shape, bn::dtype::get_builtin<double>());
    std::copy(v.begin(), v.end(), reinterpret_cast<double*>(result.get_data()));
    return result;
}

BOOST_PYTHON_MODULE(example) {
    bn::initialize();
    bp::def("myfunc", mywrapper);
}

回答by CashCow

A solution that doesn't require you to download any special 3rd party C++ library (but you need numpy).

不需要您下载任何特殊的 3rd 方 C++ 库的解决方案(但您需要 numpy)。

#include <numpy/ndarrayobject.h> // ensure you include this header

boost::python::object stdVecToNumpyArray( std::vector<double> const& vec )
{
      npy_intp size = vec.size();

     /* const_cast is rather horrible but we need a writable pointer
        in C++11, vec.data() will do the trick
        but you will still need to const_cast
      */

      double * data = size ? const_cast<double *>(&vec[0]) 
        : static_cast<double *>(NULL); 

    // create a PyObject * from pointer and data 
      PyObject * pyObj = PyArray_SimpleNewFromData( 1, &size, NPY_DOUBLE, data );
      boost::python::handle<> handle( pyObj );
      boost::python::numeric::array arr( handle );

    /* The problem of returning arr is twofold: firstly the user can modify
      the data which will betray the const-correctness 
      Secondly the lifetime of the data is managed by the C++ API and not the 
      lifetime of the numpy array whatsoever. But we have a simple solution..
     */

       return arr.copy(); // copy the object. numpy owns the copy now.
  }

Of course you might write a function from double * and size, which is generic then invoke that from the vector by extracting this info. You could also write a template but you'd need some kind of mapping from data type to the NPY_TYPESenum.

当然,您可以从 double * 和 size 编写一个函数,这是通用的,然后通过提取此信息从向量中调用该函数。您也可以编写一个模板,但您需要某种从数据类型到NPY_TYPES枚举的映射。

回答by max

It's a bit late, but after many unsuccessful tries I found a way to expose c++ arrays as numpy arrays directly. Here is a short C++11 example using boost::pythonand Eigen:

有点晚了,但经过多次不成功的尝试后,我找到了一种直接将 c++ 数组公开为 numpy 数组的方法。这是一个使用boost::python和 Eigen的简短 C++11 示例:

#include <numpy/ndarrayobject.h>
#include <boost/python.hpp>

#include <Eigen/Core>

// c++ type
struct my_type {
  Eigen::Vector3d position;
};


// wrap c++ array as numpy array
static boost::python::object wrap(double* data, npy_intp size) {
  using namespace boost::python;

  npy_intp shape[1] = { size }; // array size
  PyObject* obj = PyArray_New(&PyArray_Type, 1, shape, NPY_DOUBLE, // data type
                              NULL, data, // data pointer
                              0, NPY_ARRAY_CARRAY, // NPY_ARRAY_CARRAY_RO for readonly
                              NULL);
  handle<> array( obj );
  return object(array);
}



// module definition
BOOST_PYTHON_MODULE(test)
{
  // numpy requires this
  import_array();

  using namespace boost::python;

  // wrapper for my_type
  class_< my_type >("my_type")
    .add_property("position", +[](my_type& self) -> object {
        return wrap(self.position.data(), self.position.size());
      });

}

The example describes a "getter" for the property. For the "setter", the easiest way is to assign the array elements manually from a boost::python::objectusing a boost::python::stl_input_iterator<double>.

该示例描述了属性的“getter”。对于“setter”,最简单的方法是boost::python::object使用 a手动分配数组元素boost::python::stl_input_iterator<double>

回答by dsign

Doing it using the numpy api directly is not necessarily difficult, but I use boost::multiarray regularly for my projects and find it convenient to transfer the shapes of the array between the C++/Python boundary automatically. So, here is my recipe. Use http://code.google.com/p/numpy-boost/, or better yet, thisversion of the numpy_boost.hpp header; which is a better fit for multi-file boost::python projects, although it uses some C++11. Then, from your boost::python code, use something like this:

直接使用 numpy api 做这件事并不一定困难,但我经常在我的项目中使用 boost::multiarray 并且发现在 C++/Python 边界之间自动传输数组的形状很方便。所以,这是我的食谱。使用http://code.google.com/p/numpy-boost/,或者更好的是,这个版本的 numpy_boost.hpp 标头;尽管它使用了一些 C++11,但它更适合多文件 boost::python 项目。然后,从您的 boost::python 代码中,使用如下内容:

PyObject* myfunc(/*....*/)
{
   // If your data is already in a boost::multiarray object:
   // numpy_boost< double, 1 > to_python( numpy_from_boost_array(result_cm) );
   // otherwise:
   numpy_boost< double, 1> to_python( boost::extents[n] );
   std::copy( my_vector.begin(), my_vector.end(), to_python.begin() );

   PyObject* result = to_python.py_ptr();
   Py_INCREF( result );

   return result;
}

回答by sage

I looked at the available answers and thought, "this will be easy". I proceeded to spend hours attempting what seemed like a trivial examples/adaptations of the answers.

我查看了可用的答案并想,“这会很容易”。我继续花几个小时尝试看似微不足道的例子/对答案的改编。

Then I implemented @max's answer exactly (had to install Eigen) and it worked fine, but I still had trouble adapting it. My problems were mostly (by number) silly, syntax mistakes, but additionally I was using a pointer to a copied std::vector's data after the vector seemed to be dropped off the stack.

然后我完全实现了@max 的答案(必须安装 Eigen)并且它运行良好,但我仍然无法适应它。我的问题主要是(按数字)愚蠢的语法错误,但另外,在向量似乎从堆栈中删除之后,我使用了指向复制的 std::vector 数据的指针。

In this example, a pointer to the std::vector is returned, but also you could return the size and data() pointer or use any other implementation that gives your numpy array access to the underlying data in a stable manner (i.e. guaranteed to exist):

在这个例子中,一个指向 std::vector 的指针被返回,但你也可以返回 size 和 data() 指针或使用任何其他实现,让你的 numpy 数组以稳定的方式访问底层数据(即保证存在):

class_<test_wrap>("test_wrap")
    .add_property("values", +[](test_wrap& self) -> object {
            return wrap(self.pvalues()->data(),self.pvalues()->size());
        })
    ;

For test_wrap with a std::vector<double>(normally pvalues() might just return the pointer without populating the vector):

对于带有 a 的 test_wrap std::vector<double>(通常 pvalues() 可能只返回指针而不填充向量):

class test_wrap {
public:
    std::vector<double> mValues;
    std::vector<double>* pvalues() {
        mValues.clear();
        for(double d_ = 0.0; d_ < 4; d_+=0.3)
        {
            mValues.push_back(d_);
        }
        return &mValues;
    }
};

The full example is on Github so you can skip the tedious transcription steps and worry less about build, libs, etc. You should be able to just do the following and get a functioning example (if you have the necessary features installed and your path setup already):

完整的示例在 Github 上,因此您可以跳过繁琐的转录步骤,而不必担心构建、库等。您应该能够执行以下操作并获得一个正常运行的示例(如果您安装了必要的功能并设置了路径)已经):

git clone https://github.com/ransage/boost_numpy_example.git
cd boost_numpy_example
# Install virtualenv, numpy if necessary; update path (see below*)
cd build && cmake .. && make && ./test_np.py

This should give the output:

这应该给出输出:

# cmake/make output
values has type <type 'numpy.ndarray'>
values has len 14
values is [ 0.   0.3  0.6  0.9  1.2  1.5  1.8  2.1  2.4  2.7  3.   3.3  3.6  3.9]

*In my case, I put numpy into a virtualenv as follows - this should be unnecessary if you can execute python -c "import numpy; print numpy.get_include()"as suggested by @max:

*就我而言,我将 numpy 放入 virtualenv 如下 - 如果您可以python -c "import numpy; print numpy.get_include()"按照@max 的建议执行,这应该是不必要的:

# virtualenv, pip, path unnecessary if your Python has numpy
virtualenv venv
./venv/bin/pip install -r requirements.txt 
export PATH="$(pwd)/venv/bin:$PATH"

Have fun! :-)

玩得开心!:-)