Подтвердить что ты не робот

Получение данных из массива ctypes в numpy

Я использую Python (через ctypes) завернутую библиотеку C для запуска серии вычислений. На разных этапах работы я хочу получить данные в Python и, в частности, numpy массивы.

Используемая обертка использует два разных типа возврата для данных массива (что меня особенно интересует):

  • ctypes Массив. Когда я делаю type(x) (где x - массив ctypes, я возвращаю <class 'module_name.wrapper_class_name.c_double_Array_12000'>. Я знаю, что эти данные являются копия внутренних данных из документации, и я могу легко получить ее в массив numpy:

    >>> np.ctypeslib.as_array(x)
    

Это возвращает массив данных 1D numpy.

  • ctype указатель на данные. В этом случае из документации библиотеки я понимаю, что получаю указатель на данные, хранящиеся и используемые непосредственно в библиотеке. Whey я do type(y) (где y - указатель) Я получаю <class 'module_name.wrapper_class_name.LP_c_double'>. В этом случае я все еще могу индексировать данные, такие как y[0][2], но я мог только получить его в numpy через супер неудобно:

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

Я нашел это в старом numpy списке рассылки от Travis Oliphant, но не в документации numpy. Если вместо этого подхода я попробую, как указано выше, я получаю следующее:

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

Подходит ли этот np.frombuffer лучший или единственный способ сделать это? Я открыт для других предложений, но мне все равно хотелось бы использовать numpy, поскольку у меня есть много других постобработок, которые полагаются на функциональность numpy, которую я хочу использовать с этими данными.

4b9b3361

Ответ 1

Создание массивов NumPy из объекта указателя ctypes является проблематичной операцией. Неясно, кто на самом деле владеет памятью, на которую указывает указатель. Когда он будет освобожден снова? Как долго это действует? По возможности я старался избежать такой конструкции. Гораздо проще и безопаснее создавать массивы в коде Python и передавать их функции C, чем использовать память, выделенную функцией Python-unaware C. Выполняя последнее, вы в какой-то степени отрицаете преимущества использования языка высокого уровня, который заботится о управлении памятью.

Если вы действительно уверены, что кто-то позаботится о памяти, вы можете создать объект, подвергая "буферный протокол" Python, а затем создать массив NumPy, используя этот буферный объект. Вы предоставили один способ создания объекта-буфера в своем сообщении через недокументированную функцию int_asbuffer():

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

(Обратите внимание, что я заменил 8 на np.dtype(float).itemsize. Он всегда 8 на любой платформе.) Другим способом создания объекта буфера будет вызов функции PyBuffer_FromMemory() из API Python C через ctypes:

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

Для обоих способов вы можете создать массив NumPy из buffer на

a = numpy.frombuffer(buffer, float)

(на самом деле я не понимаю, почему вы используете .astype() вместо второго параметра frombuffer; кроме того, мне интересно, почему вы используете np.int, а вы сказали ранее, что массив содержит double s.)

Я боюсь, что это не будет намного легче, чем это, но это не так уж плохо, не так ли? Вы можете похоронить все уродливые детали в функции обертки и больше не беспокоиться об этом.

Ответ 2

Другая возможность (которая может потребовать более поздних версий библиотек, чем доступно, когда был написан первый ответ - я протестировал что-то подобное с ctypes 1.1.0 и numpy 1.5.0b2) для преобразования из указателя в массив.

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

Кажется, что у вас все еще есть семантика совместного использования, поэтому вам, вероятно, необходимо убедиться, что вы освободите базовый буфер в конце концов.

Ответ 3

Ни один из них не работал у меня на Python 3. Как общее решение для преобразования указателя ctypes в numpy ndarray в python 2 и 3, я нашел, что это сработало (через получение только для чтения):

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

Ответ 4

Если вы в порядке с созданием массивов в python, следующий пример с 2d-массивом работает в 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 и ctypes версии: 1.11.1 и 1.1.0