Sending a C++ array to Python and back (Extending C++ with Numpy) Sending a C++ array to Python and back (Extending C++ with Numpy) numpy numpy

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
  1. 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/

  1. 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.