Sending a C++ array to Python and back (Extending C++ with Numpy)
Try out xtensor and the xtensor-python python bindings.
xtensor is a C++ library meant for numerical analysis with multi-dimensional array expressions.
xtensor provides
- an extensible expression system enabling numpy-style broadcasting (see the numpy to xtensor cheat sheet).
- an API following the idioms of the C++ standard library.
- tools to manipulate array expressions and build upon xtensor.
- bindings for Python, but also R and Julia.
Example of usage
Initialize a 2-D array and compute the sum of one of its rows and a 1-D array.
#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;
Outputs
{7, 11, 14}
Creating a Numpy-style universal function in C++.
#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 code:
import numpy as npimport xtensor_python_test as xtx = np.arange(15).reshape(3, 5)y = [1, 2, 3, 4, 5]z = xt.vectorized_func(x, y)z
Outputs
[[-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]]
We will be passing 2D array to python function written in file pyCode.py
:
def pyArray (a): print ("Contents of a :") print (a) c = 0 return c
- For C++ to Python:File:
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;}
compile the code:g++ -g -fPIC c_code.cpp -o runMe -lpython3.5m -I/usr/include/python3.5m/
- From OpenCV Mat to Python:
file: 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;}
Compile the code: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
As an additional way, without touching directly to the Python C API, it is possible to use pybind11 ( header-only library) :
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;}
In py_test.py
:
def test_mat(m): print ("Inside python m = \n ",m ) m[0,0] = 10 m[1,1] = 99 return m
Output :
Inside python m = [[ 1. 3.] [ 2. 4.]]In c++10 3 2 99
See the official documentation.
ps: I'm using Eigen for the C++ Matrix.