将 C++ 数组发送到 Python 并返回(使用 Numpy 扩展 C++)
Posted
技术标签:
【中文标题】将 C++ 数组发送到 Python 并返回(使用 Numpy 扩展 C++)【英文标题】:Sending a C++ array to Python and back (Extending C++ with Numpy) 【发布时间】:2015-08-03 23:21:15 【问题描述】:我要将c++
数组作为numpy array
发送到python 函数并返回另一个numpy array
。在咨询了numpy
文档和其他一些线程并调整了代码后,代码终于可以正常工作了,但我想知道这段代码是否写得最好,考虑到:
c++
和numpy (python)
之间不必要地复制数组。
正确取消引用变量。
简单直接的方法。
C++ 代码:
// python_embed.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "Python.h"
#include "numpy/arrayobject.h"
#include<iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
Py_SetProgramName(argv[0]);
Py_Initialize();
import_array()
// Build the 2D array
PyObject *pArgs, *pReturn, *pModule, *pFunc;
PyArrayObject *np_ret, *np_arg;
const int SIZE 10 ;
npy_intp dims[2]SIZE, SIZE;
const int ND 2 ;
long double(*c_arr)[SIZE] new long double[SIZE][SIZE] ;
long double* c_out;
for (int i; i < SIZE; i++)
for (int j; j < SIZE; j++)
c_arr[i][j] = i * SIZE + j;
np_arg = reinterpret_cast<PyArrayObject*>(PyArray_SimpleNewFromData(ND, dims, NPY_LONGDOUBLE,
reinterpret_cast<void*>(c_arr)));
// Calling array_tutorial from mymodule
PyObject *pName = PyUnicode_FromString("mymodule");
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (!pModule)
cout << "mymodule can not be imported" << endl;
Py_DECREF(np_arg);
delete[] c_arr;
return 1;
pFunc = PyObject_GetAttrString(pModule, "array_tutorial");
if (!pFunc || !PyCallable_Check(pFunc))
Py_DECREF(pModule);
Py_XDECREF(pFunc);
Py_DECREF(np_arg);
delete[] c_arr;
cout << "array_tutorial is null or not callable" << endl;
return 1;
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, reinterpret_cast<PyObject*>(np_arg));
pReturn = PyObject_CallObject(pFunc, pArgs);
np_ret = reinterpret_cast<PyArrayObject*>(pReturn);
if (PyArray_NDIM(np_ret) != ND - 1) // row[0] is returned
cout << "Function returned with wrong dimension" << endl;
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(np_arg);
Py_DECREF(np_ret);
delete[] c_arr;
return 1;
int len PyArray_SHAPE(np_ret)[0] ;
c_out = reinterpret_cast<long double*>(PyArray_DATA(np_ret));
cout << "Printing output array" << endl;
for (int i; i < len; i++)
cout << c_out[i] << ' ';
cout << endl;
// Finalizing
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(np_arg);
Py_DECREF(np_ret);
delete[] c_arr;
Py_Finalize();
return 0;
在 CodeReview 中,有一个绝妙的答案:Link...
【问题讨论】:
你看过 Boost.Numpy 吗?看这个非常简单的例子:github.com/ndarray/Boost.NumPy/blob/master/libs/numpy/example/… 或使用 Cython? ***.com/a/18176741/2230844 @denfromufa,我不想在这里使用 boost,cython 也不是一个选项,因为我正在使用 python/numpy 扩展 C++。 你看过 SWIG (docs.scipy.org/doc/numpy/reference/swig.interface-file.html) 吗?它允许在 C++ 数组上的 numpy 中拥有内存视图,反之亦然。它会自动处理包装。虽然我从未在 C++ 到 Python 的方向上使用过它。 @Dietrich,谢谢,SWIG 也不错,但在这里我想纯粹使用 Python/C++ API。我感谢提供的代码上的任何 cmets。 【参考方案1】:试用xtensor 和xtensor-python python 绑定。
xtensor 是一个 C++ 库,用于使用多维数组表达式进行数值分析。
xtensor 提供
一个可扩展的表达系统,支持 numpy 风格的广播(请参阅 numpy 到 xtensor 备忘单)。 一个遵循 C++ 标准库习惯用法的 API。 用于操作数组表达式并基于 xtensor 构建的工具。 Python 的绑定,还有 R 和 Julia。使用示例
初始化一个二维数组并计算其中一行与一维数组的总和。
#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
xt::xarray<double> arr1
1.0, 2.0, 3.0,
2.0, 5.0, 7.0,
2.0, 5.0, 7.0;
xt::xarray<double> arr2
5.0, 6.0, 7.0;
xt::xarray<double> res = xt::view(arr1, 1) + arr2;
std::cout << res;
输出
7, 11, 14
在 C++ 中创建一个 Numpy 风格的通用函数。
#include "pybind11/pybind11.h"
#include "xtensor-python/pyvectorize.hpp"
#include <numeric>
#include <cmath>
namespace py = pybind11;
double scalar_func(double i, double j)
return std::sin(i) - std::cos(j);
PYBIND11_PLUGIN(xtensor_python_test)
py::module m("xtensor_python_test", "Test module for xtensor python bindings");
m.def("vectorized_func", xt::pyvectorize(scalar_func), "");
return m.ptr();
Python 代码:
import numpy as np
import xtensor_python_test as xt
x = np.arange(15).reshape(3, 5)
y = [1, 2, 3, 4, 5]
z = xt.vectorized_func(x, y)
z
输出
[[-0.540302, 1.257618, 1.89929 , 0.794764, -1.040465],
[-1.499227, 0.136731, 1.646979, 1.643002, 0.128456],
[-1.084323, -0.583843, 0.45342 , 1.073811, 0.706945]]
【讨论】:
看起来很有趣,但是你是用C++扩展python,我需要用python扩展C++。 好吧,这实际上是在 xtensor 的测试中完成的,我们从 c++ 实例化一个 python 解释器并开始创建数组。【参考方案2】:我们将把二维数组传递给写在文件pyCode.py
中的python函数:
def pyArray (a):
print ("Contents of a :")
print (a)
c = 0
return c
-
对于 C++ 到 Python:
档案:
c_code.cpp
#include <Python.h>
#include <stdio.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
float Array [] = 1.2, 3.4, 5.6, 7.8;
int main (int argc, char *argv[])
float *ptr = Array;
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs;
npy_intp dims[1] = 4 ;
PyObject *py_array;
setenv("PYTHONPATH",".",1);
Py_Initialize ();
pName = PyUnicode_FromString ("pyCode");
pModule = PyImport_Import(pName);
pDict = PyModule_GetDict(pModule);
import_array ();
py_array = PyArray_SimpleNewFromData(1, dims, NPY_FLOAT, ptr);
pArgs = PyTuple_New (1);
PyTuple_SetItem (pArgs, 0, py_array);
pFunc = PyDict_GetItemString (pDict, (char*)"pyArray");
if (PyCallable_Check (pFunc))
PyObject_CallObject(pFunc, pArgs);
else
cout << "Function is not callable !" << endl;
Py_DECREF(pName);
Py_DECREF (py_array);
Py_DECREF (pModule);
Py_DECREF (pDict);
Py_DECREF (pFunc);
Py_Finalize ();
return 0;
编译代码:
g++ -g -fPIC c_code.cpp -o runMe -lpython3.5m -I/usr/include/python3.5m/
-
从 OpenCV Mat 到 Python:
文件:cv_mat_code.cpp
#include <iostream>
#include <Python.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main (int argc, char *argv[])
float data[10] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
Mat mat1 (cv::Size (5, 2), CV_32F, data, Mat::AUTO_STEP);
int row = 0;
float *p = mat1.ptr<float>(row);
cout << "Mat" << mat1 <<endl;
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs;
npy_intp dims[2] = 2, 5 ;
PyObject *py_array;
setenv("PYTHONPATH",".",1);
Py_Initialize ();
pName = PyUnicode_FromString ("pyCode");
pModule = PyImport_Import(pName);
pDict = PyModule_GetDict(pModule);
// Required for the C-API : http://docs.scipy.org/doc/numpy/reference/c-api.array.html#importing-the-api
import_array ();
py_array = PyArray_SimpleNewFromData(2, dims, NPY_FLOAT, p);
pArgs = PyTuple_New (1);
PyTuple_SetItem (pArgs, 0, py_array);
pFunc = PyDict_GetItemString (pDict, (char*)"pyArray");
if (PyCallable_Check (pFunc))
PyObject_CallObject(pFunc, pArgs);
else
cout << "Function is not callable !" << endl;
Py_DECREF(pName);
Py_DECREF (py_array);
Py_DECREF (pModule);
Py_DECREF (pDict);
Py_DECREF (pFunc);
Py_Finalize ();
return 0;
编译代码:
g++ -g -fPIC cv_mat_code.cpp -o runMe -lpython3.5m -I/usr/include/python3.5m/ -I/usr/include/ -lopencv_core -lopencv_imgproc -lopencv_highgui
【讨论】:
为什么你的数据array
中有 42 个 float
但在你的 Mat
中只分配了 10 个元素?
@simplename 谢谢!进行了更改。
如果里面有 cv2 调用,PyObject_callObject 会被阻塞。这是 opencv for python 2.7 的一个已知问题吗
它被称为python代码embedding,因此它们被阻塞了。了解主进程如何调用解释器here。
我说错了,我的意思是它挂在 cv2 调用中并且没有继续。【参考方案3】:
作为另一种方式,无需直接接触 Python C API,可以使用pybind11(仅标头库):
CPP:
#include <pybind11/embed.h> // everything needed for embedding
#include <iostream>
#include <Eigen/Dense>
#include<pybind11/eigen.h>
using Eigen::MatrixXd;
namespace py = pybind11;
int main()
try
Py_SetProgramName("PYTHON");
py::scoped_interpreter guard;
py::module py_test = py::module::import("py_test");
MatrixXd m(2,2);
m(0,0) = 1;
m(1,0) = 2;
m(0,1) = 3;
m(1,1) = 4;
py::object result = py_test.attr("test_mat")(m);
MatrixXd res = result.cast<MatrixXd>();
std::cout << "In c++ \n" << res << std::endl;
catch (std::exception ex)
std::cout << "ERROR : " << ex.what() << std::endl;
return 1;
在py_test.py
:
def test_mat(m):
print ("Inside python m = \n ",m )
m[0,0] = 10
m[1,1] = 99
return m
输出:
Inside python m =
[[ 1. 3.]
[ 2. 4.]]
In c++
10 3
2 99
请参阅official documentation。
ps:我在 C++ 矩阵中使用Eigen。
【讨论】:
感谢你的好例子;我刚刚复制了它。发现这行需要是:std::cout << "In c++ \n" << res << std::endl;
【参考方案4】:
根据我的经验,这似乎非常有效。 要从中获得更高的效率,请尝试以下操作: http://ubuntuforums.org/showthread.php?t=1266059
使用 weave,您可以在 Python 中内联 C/C++ 代码,这样会很有用。
http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.weave.inline.html
这是一个关于如何使用 Python 在许多不同语言之间进行交互的链接以及示例。
http://docs.scipy.org/doc/numpy/user/c-info.python-as-glue.html
这是一个快速简单的示例,说明如何使用 Cython 将 numpy 数组传递给 c++:
http://www.birving.com/blog/2014/05/13/passing-numpy-arrays-between-python-and/
【讨论】:
我正在扩展c++
使用 python
而不是相反,所以 cython
, weave
不相关。只有您的第一个链接可能是相关的。如果您可以对提供的代码进行任何可能的改进,我将不胜感激。
Cython 仍然相关,因为您可以使用 public 关键字从 C++/C 中调用它。
@BeRecursive,是的,它也可以工作。现在我主要关心问题中给出的代码的性能。
如果你想提高 Python 位的速度,我也可以建议使用 pypy.org 使用 PyPy 可以大大提高速度。
Weave 现在已经被 Cython 弃用了。独立的weave package 建议仅在您有依赖于尚未移植的 weave 的现有代码时使用它。以上是关于将 C++ 数组发送到 Python 并返回(使用 Numpy 扩展 C++)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ctypes 将数组从 C++ 函数返回到 Python