Cómo convertir el puntero a la matriz c a la matriz python

Tengo una función de callback de C ++ que llama a Python usando ctypes. Los parámetros de esta función son un puntero a una matriz de doble y el número de elementos.

Hay muchos elementos, aproximadamente 2,000,000. Necesito enviar esto a funciones scipy.

El prototipo de C ++ es:

bool (*ptsetDataSource)(double*, long long); 

que es el siguiente código de python:

 CPF_setDataSource = CFUNCTYPE(c_bool, POINTER(c_double),c_longlong) CPF_setSelection= CFUNCTYPE(c_bool,c_char_p, c_longlong,c_longlong) CPF_ResetSequence = CFUNCTYPE(c_bool) def setDataSource(Data, DataLength): Datalist=[0.0]*100 for i in range(0,100): Datalist[i]=Data[i] print Datalist return True 

El problema es que el datalist de impresión devuelve:

 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 

lo cual no es correcto (los datos se llenan con muchos otros números cuando se verifican en el lado de c ++).

Además, si uso este código para convertir los datos a una lista de python, bloquea la computadora en el paso de asignación.

¿Hay alguna forma de cargar los datos de la matriz C ++ y luego convertirlos en una matriz apta para scipy?

Si los Data fueran una (c_double*DataLength.value) entonces usted podría:

 a = np.frombuffer(Data) # no copy. Changes in `a` are reflected in `Data` 

Si los Data son un POINTER(c_double) podría obtener una matriz numpy.fromiter() utilizando numpy.fromiter() . Es el mismo bucle que en tu pregunta pero más rápido:

 a = np.fromiter(Data, dtype=np.float, count=DataLength.value) # copy 

Para crear una matriz numpy desde la instancia de POINTER(c_double) sin copiar, puede usar el método .from_address() :

 ArrayType = ctypes.c_double*DataLength.value addr = ctypes.addressof(Data.contents) a = np.frombuffer(ArrayType.from_address(addr)) 

O

 array_pointer = ctypes.cast(Data, ctypes.POINTER(ArrayType)) a = np.frombuffer(array_pointer.contents) 

Ambos métodos convierten la instancia de POINTER(c_double) en (c_double*DataLength) antes de pasarla a numpy.frombuffer() .

Solución basada en Cython

¿Hay alguna forma de cargar los datos de la matriz C ++ y luego convertirlos en una matriz apta para scipy?

Aquí está el módulo de extensión C para Python (escrito en Cython) que proporciona a la API de C la función de conversión:

 cimport numpy as np np.import_array() # initialize C API to call PyArray_SimpleNewFromData cdef public api tonumpyarray(double* data, long long size) with gil: if not (data and size >= 0): raise ValueError cdef np.npy_intp dims = size #NOTE: it doesn't take ownership of `data`. You must free `data` yourself return np.PyArray_SimpleNewFromData(1, &dims, np.NPY_DOUBLE, data) 

Podría ser utilizado con ctypes siguiente manera:

 from ctypes import (PYFUNCTYPE, py_object, POINTER, c_double, c_longlong, pydll, CFUNCTYPE, c_bool, cdll) import pointer2ndarray tonumpyarray = PYFUNCTYPE(py_object, POINTER(c_double), c_longlong)( ("tonumpyarray", pydll.LoadLibrary(pointer2ndarray.__file__))) @CFUNCTYPE(c_bool, POINTER(c_double), c_longlong) def callback(data, size): a = tonumpyarray(data, size) # call scipy functions on the `a` array here return True cpplib = cdll.LoadLibrary("call_callback.so") # your C++ lib goes here cpplib.call_callback(callback) 

Donde call_callback es: void call_callback(bool (*)(double *, long long)) .