将C/C++向量快速转换为Numpy数组

Set*_*son 5 c++ python arrays swig numpy

我正在使用SWIG将一些C++代码粘合到Python(2.6)上,并且该粘合剂的一部分包括一段代码,它将大数据字段(数百万个值)从C++端转换为Numpy数组.我能想出的最好的方法是为类实现一个迭代器,然后提供一个Python方法:

def __array__(self, dtype=float):
    return np.fromiter(self, dtype, self.size())
Run Code Online (Sandbox Code Playgroud)

问题是每个迭代器next调用都非常昂贵,因为它必须通过大约三到四个SWIG包装器.这需要太长时间.我可以保证C++数据是连续存储的(因为它们存在于std :: vector中),并且感觉Numpy应该能够指向该数据的开头以及它包含的值的数量,并且直接阅读.

有没有办法通过一个指针internal_data_[0]和值internal_data_.size(),以numpy的,以便它可以直接,不用所有的Python开销访问或复制的数据?

Set*_*son 0

因此,看起来唯一真正的解决方案是基于pybuffer.i可以从 C++ 复制到现有缓冲区的东西。如果将其添加到 SWIG 包含文件中:

%insert("python") %{
import numpy as np
%}

/*! Templated function to copy contents of a container to an allocated memory
 * buffer
 */
%inline %{
//==== ADDED BY numpy.i
#include <algorithm>

template < typename Container_T >
void copy_to_buffer(
        const Container_T& field,
        typename Container_T::value_type* buffer,
        typename Container_T::size_type length
        )
{
//    ValidateUserInput( length == field.size(),
//            "Destination buffer is the wrong size" );
    // put your own assertion here or BAD THINGS CAN HAPPEN

    if (length == field.size()) {
        std::copy( field.begin(), field.end(), buffer );
    }
}
//====

%}

%define TYPEMAP_COPY_TO_BUFFER(CLASS...)
%typemap(in) (CLASS::value_type* buffer, CLASS::size_type length)
(int res = 0, Py_ssize_t size_ = 0, void *buffer_ = 0) {

    res = PyObject_AsWriteBuffer($input, &buffer_, &size_);
    if ( res < 0 ) {
        PyErr_Clear();
        %argument_fail(res, "(CLASS::value_type*, CLASS::size_type length)",
                $symname, $argnum);
    }
    $1 = ($1_ltype) buffer_;
    $2 = ($2_ltype) (size_/sizeof($*1_type));
}
%enddef


%define ADD_NUMPY_ARRAY_INTERFACE(PYVALUE, PYCLASS, CLASS...)

TYPEMAP_COPY_TO_BUFFER(CLASS)

%template(_copy_to_buffer_ ## PYCLASS) copy_to_buffer< CLASS >;

%extend CLASS {
%insert("python") %{
def __array__(self):
    """Enable access to this data as a numpy array"""
    a = np.ndarray( shape=( len(self), ), dtype=PYVALUE )
    _copy_to_buffer_ ## PYCLASS(self, a)
    return a
%}
}

%enddef
Run Code Online (Sandbox Code Playgroud)

那么你可以制作一个“Numpy”容器

%template(DumbVectorFloat) DumbVector<double>;
ADD_NUMPY_ARRAY_INTERFACE(float, DumbVectorFloat, DumbVector<double>);
Run Code Online (Sandbox Code Playgroud)

然后在 Python 中,只需执行以下操作:

# dvf is an instance of DumbVectorFloat
import numpy as np
my_numpy_array = np.asarray( dvf )
Run Code Online (Sandbox Code Playgroud)

这仅具有单个 Python <--> C++ 转换调用的开销,而不是典型长度为 N 的数组产生的 N 的开销。

此代码的稍微完整的版本是我在 github 上的 PyTRT 项目的一部分。