web-dev-qa-db-fra.com

Envoi d'un tableau C ++ à Python et retour (extension C ++ avec Numpy)

Je vais envoyer un tableau c++ À un python comme numpy array Et récupérer un autre numpy array. Après avoir consulté numpy documentation et quelques autres threads et peaufinage du code, enfin le code fonctionne mais je voudrais savoir si ce code est écrit de manière optimale compte tenu de:

  • Copie inutile du tableau entre c++ Et numpy (python).
  • Déréférencement correct des variables.
  • Approche simple et directe.

Code 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;
}

Dans CodeReview, il y a une réponse fantastique: Link ...

28
rowman

Essayez xtensor et les liaisons xtensor-python python).

xtensor est une bibliothèque C++ destinée à l'analyse numérique avec des expressions de tableau multidimensionnelles.

xtensor fournit

  • un système d'expression extensible permettant la diffusion de style numpy (voir la feuille de triche numpy à xtensor).
  • une API suivant les idiomes de la bibliothèque standard C++.
  • des outils pour manipuler les expressions du tableau et s'appuyer sur xtensor.
  • liaisons pour Python, mais aussi R et Julia.

Exemple d'utilisation

Initialisez un tableau 2D et calculez la somme de l'une de ses lignes et d'un tableau 1D.

#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;

Les sorties

{7, 11, 14}

Création d'une fonction universelle de style Numpy en 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();
}

Code 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

Les sorties

[[-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]]
5
Quant

D'après mon expérience, cela semble être assez efficace. Pour obtenir encore plus d'efficacité, essayez ceci: http://ubuntuforums.org/showthread.php?t=1266059

En utilisant weave, vous pouvez incorporer du code C/C++ dans Python afin que cela puisse être utile.

http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.weave.inline.html

Voici un lien sur la façon dont Python peut être utilisé pour interfacer entre de nombreux langages différents avec des exemples.

http://docs.scipy.org/doc/numpy/user/c-info.python-as-glue.html

Voici un exemple rapide et simple de comment passer des tableaux numpy à c ++ en utilisant Cython:

http://www.birving.com/blog/2014/05/13/passing-numpy-arrays-between-python-and/

3
Meghdeep Ray

Comme moyen supplémentaire, sans toucher directement à l'API Python C, il est possible d'utiliser pybind11 (bibliothèque d'en-tête uniquement):

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;
}

Dans py_test.py:

def test_mat(m):
    print ("Inside python m = \n ",m )
    m[0,0] = 10
    m[1,1] = 99 
    return m

Sortie:

Inside python m =
  [[ 1.  3.]
  [ 2.  4.]]
In c++
10  3
 2 99

Voir documentation officielle .

ps: j'utilise Eigen pour la matrice C++.

3
Malick

Nous passerons un tableau 2D à python écrite dans le fichier pyCode.py:

def pyArray (a):
    print ("Contents of a :")
    print (a)
    c = 0
    return c
  1. Pour C++ à Python: Fichier: 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, *pValue;
    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;
}

compiler le code: g++ -g -fPIC c_code.cpp -o runMe -lpython3.5m -I/usr/include/python3.5m/

  1. De OpenCV Mat à Python:

fichier: 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[42] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
                     31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 };

    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, *pValue;
    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;
}

Compilez le 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

1
Milind Deore