2010-12-04 33 views
22

Estoy utilizando una biblioteca C envuelta Python (a través de ctypes) para ejecutar una serie de cálculos. En diferentes etapas de la ejecución, quiero obtener datos en Python, y específicamente en las matrices numpy.Obteniendo datos de ctypes array en numpy

La envoltura estoy usando hace dos tipos diferentes de cambio de datos de la matriz (que es de particular interés para mí):

  • ctypes matriz: Cuando hago type(x) (donde x es el ctypes serie, aparece un <class 'module_name.wrapper_class_name.c_double_Array_12000'> a cambio sé que estos datos son una copia de los datos internos de la documentación y soy capaz de conseguirlo en una matriz numpy fácilmente:.

    >>> np.ctypeslib.as_array(x) 
    

Esto devuelve una matriz 1D numpy de los datos.

  • ctype puntero a los datos: En este caso, de la documentación de la biblioteca, entiendo que estoy recibiendo un puntero a los datos almacenados y usados ​​directamente a la biblioteca. Suero que hago type(y) (donde y es el puntero) Obtengo <class 'module_name.wrapper_class_name.LP_c_double'>. Con este caso todavía soy capaz de indexar a través de los datos como y[0][2], pero sólo fue capaz de conseguir en numpy a través de un súper incómoda:

    >>> np.frombuffer(np.core.multiarray.int_asbuffer(
        ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize)) 
    

He encontrado esto en un viejo numpy lista de correo thread from Travis Oliphant , pero no en la documentación numpy. Si en lugar de este enfoque trato que el anterior me sale el siguiente:

>>> np.ctypeslib.as_array(y) 
... 
... BUNCH OF STACK INFORMATION 
... 
AttributeError: 'LP_c_double' object has no attribute '__array_interface__' 

¿Es esta np.frombuffer se aproximan al mejor o la única manera de hacer esto? Estoy abierto a otras sugerencias, pero aún así me gustaría usar numpy ya que tengo muchos otros códigos de postprocesamiento que dependen de la funcionalidad numpy que deseo utilizar con estos datos.

+0

¿tiene el control o ver el C lib? ¿Podrías cambiar la API de la biblioteca? –

+0

Sí, tengo la fuente. No estoy seguro de qué camino tomar, ya que el enfoque del puntero permite a Python actuar directamente sobre los datos, lo que supongo que en algunos casos podría ser una ventaja. Sin embargo, en mi caso, sí, sería una ventaja que todo salga como una matriz 'ctype'. ¿Alguna recomendación? – dtlussier

+1

Sugiero que la biblioteca use una matriz (NumPy-) que asigne en Python y la pase a la biblioteca. De esta forma, puedes actuar en el mismo recuerdo, pero no tienes que molestarte en hacer conversiones torpes. Ya tiene una matriz NumPy, y pasarla a una biblioteca es bien soportada usando ['numpy.ctypeslib.ndpointer'] (http://docs.scipy.org/doc/numpy/reference/routines.ctypeslib.html # numpy.ctypeslib.ndpointer) como tipo de argumento para el contenedor ctypes de su función. (Si esto no está claro, solo pregunte ...) –

Respuesta

23

La creación de matrices NumPy a partir de un objeto puntero ctypes es una operación problemática. No está claro quién posee realmente la memoria a la que apunta el puntero. ¿Cuándo será liberado de nuevo? ¿Cuánto tiempo es válido? Siempre que sea posible, trataré de evitar este tipo de construcción. Es mucho más fácil y más seguro crear matrices en el código Python y pasarlas a la función C que utilizar la memoria asignada por una función C que no tenga conocimiento de Python. Al hacer esto último, niega en cierta medida las ventajas de tener un lenguaje de alto nivel que se ocupa de la administración de la memoria.

Si está realmente seguro de que alguien se ocupa de la memoria, puede crear un objeto exponiendo el "protocolo de búfer" de Python y luego crear una matriz NumPy utilizando este objeto de búfer. Le diste una manera de crear el objeto de búfer en su mensaje, a través de los indocumentados int_asbuffer() función:

buffer = numpy.core.multiarray.int_asbuffer(
    ctypes.addressof(y.contents), 8*array_length) 

(Tenga en cuenta que sustituí 8 para np.dtype(float).itemsize Siempre 8, en cualquier plataforma..) Una manera diferente de crear el objeto de memoria intermedia sería llamar a la función PyBuffer_FromMemory() de la API de Python C a través ctypes:

buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory 
buffer_from_memory.restype = ctypes.py_object 
buffer = buffer_from_memory(y, 8*array_length) 

Por estas dos maneras, se puede crear una matriz NumPy de buffer por

a = numpy.frombuffer(buffer, float) 

(que en realidad no entiendo por qué se utiliza .astype() en lugar de un segundo parámetro a frombuffer, por otro lado, me pregunto por qué se utiliza np.int, mientras que ha dicho antes que la matriz contiene double s.)

Me temo que no será mucho más fácil que esto, pero no es tan malo, ¿no crees? Podrías enterrar todos los detalles desagradables en una función de envoltura y no te preocupes más por eso.

+0

Eso es genial, gracias por la descripción de los pros y los contras. la llamada '.astype()' fue solo una copia accidental y un error pasado. Lo he sacado de mi pregunta ahora. Gracias por aprender sobre eso. – dtlussier

7

Otra posibilidad (que puede requerir versiones más recientes de las bibliotecas que está disponible cuando se escribió la primera respuesta - Probé algo similar con ctypes 1.1.0 y numpy 1.5.0b2) es convertir del puntero a la matriz.

np.ctypeslib.as_array(
    (ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents))) 

Esto parece todavía tienen la semántica de propiedad compartida, por lo que probablemente tendrá que asegurarse de que liberar el búfer subyacente con el tiempo.

+2

O sin soporte especial de numpy: puede convertir un puntero 'y' a un puntero a un tipo de matriz:' ap = ctypes.cast (y, ctypes.POINTER (ArrayType)) 'donde' ArrayType = ctypes.c_double * array_length' y crear una matriz numpy a partir de eso: 'a = np.frombuffer (ap.contents)'. Ver [Cómo convertir el puntero a la matriz c en la matriz de python] (http://stackoverflow.com/questions/7543675/how-to-convert-pointer-to-c-array-to-python-array) – jfs

+0

Estaba intentando esto, pero el objeto ap no tiene un miembro, contenido. –

+0

@TotteKarlsson: el código del enlace funciona como está (lo he probado). Probablemente sea un error en su código (también puede ser una diferencia entre varias versiones de Python, pero es menos probable). Si no lo has arreglado; [crear un ejemplo de código mínimo pero completo] (http: // stackoverflow.com/help/mcve), especifique cuál es su sistema operativo, la versión de Python y [publíquelo como una nueva pregunta de SO] (http://stackoverflow.com/questions/ask) – jfs

5

Ninguno de estos trabajado para mí en Python 3. Como una solución general para convertir un puntero ctypes en un ndarray numpy en Python 2 y 3 me encontré con esto funcionó (a través de la obtención de un búfer de sólo lectura):

def make_nd_array(c_pointer, shape, dtype=np.float64, order='C', own_data=True): 
    arr_size = np.prod(shape[:]) * np.dtype(dtype).itemsize 
    if sys.version_info.major >= 3: 
     buf_from_mem = ctypes.pythonapi.PyMemoryView_FromMemory 
     buf_from_mem.restype = ctypes.py_object 
     buf_from_mem.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int) 
     buffer = buf_from_mem(c_pointer, arr_size, 0x100) 
    else: 
     buf_from_mem = ctypes.pythonapi.PyBuffer_FromMemory 
     buf_from_mem.restype = ctypes.py_object 
     buffer = buf_from_mem(c_pointer, arr_size) 
    arr = np.ndarray(tuple(shape[:]), dtype, buffer, order=order) 
    if own_data and not arr.flags.owndata: 
     return arr.copy() 
    else: 
     return arr 
0

Si estás bien con la creación de matrices en Python, el siguiente ejemplo con matriz 2D trabaja en python3:

import numpy as np 
import ctypes 

OutType = (ctypes.c_float * 4) * 6 
out = OutType() 
YourCfunction = ctypes.CDLL('./yourlib.so').voidreturningfunctionwithweirdname 
YourCfunction.argtypes = [ctypes.POINTER(ctypes.c_float)]*3, ctypes.POINTER(ctypes.c_float)]*5, OutType] 
YourCfunction(input1, input2, out) 
out = np.array(out) # convert it to numpy 

print(out) 

numpy y ctypes son versiones 1.11.1 y 1.1.0