Передача массивов Numpy в функцию C для ввода и вывода - PullRequest
48 голосов
/ 03 мая 2011

О, мое слово, я дурак. Я просто опускал второй и третий аргументы при вызове функции.Как дурак.Потому что это то, что я есть.Ниже следует оригинальный глупый вопрос:

Похоже, это очень распространенная вещь, но я не могу найти соответствующий учебник, и я слишком неосведомлен о Numpy и ctypes, чтобы понятьЯ сам.

У меня есть функция C в файле ctest.c.

#include <stdio.h>

void cfun(const void * indatav, int rowcount, int colcount, void * outdatav) {
    //void cfun(const double * indata, int rowcount, int colcount, double * outdata) {
    const double * indata = (double *) indatav;
    double * outdata = (double *) outdatav;
    int i;
    puts("Here we go!");
    for (i = 0; i < rowcount * colcount; ++i) {
        outdata[i] = indata[i] * 2;
    }
    puts("Done!");
}

(Как вы можете догадаться, у меня изначально были аргументы как double *, а не void *, ноя не могу понять, что делать на стороне Python. Я бы с удовольствием поменял их обратно, но я не привередлив, если это работает.)

Я делаю из этого общую библиотеку,gcc -fPIC -shared -o ctest.so ctest.c

Затем в Python у меня есть пара массивов numpy, и я хотел бы передать их в функцию C, один для ввода и один для вывода.

indata = numpy.ones((5,6), dtype=numpy.double)
outdata = numpy.zeros((5,6), dtype=numpy.double)
lib = ctypes.cdll.LoadLibrary('./ctest.so')
fun = lib.cfun
# Here comes the fool part.
fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

print 'indata: %s' % indata
print 'outdata: %s' % outdata

Это не сообщает об ошибках, но выводит на печать

>>> Here we go!
Done!
indata: [[ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]]
outdata: [[ 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.]]

Массив outdata не изменяется.И на самом деле, если я вызову функцию снова, я получу ошибку.Что меня не удивляет - я действительно не знаю, что я здесь делаю.Кто-нибудь может указать мне правильное направление?

Ответы [ 2 ]

72 голосов
/ 03 мая 2011

Хотя это и не прямой ответ на исходный вопрос, вот гораздо более удобный способ вызова вашей функции.Во-первых, создайте прототип вашей функции C точно так же, как вы делали бы это на обычном C. Поскольку вам не нужны rowcount и colcount по отдельности, я сверну их в один параметр size:

void cfun(const double *indatav, size_t size, double *outdatav) 
{
    size_t i;
    for (i = 0; i < size; ++i)
        outdatav[i] = indatav[i] * 2.0;
}

Теперь определите прототип ctypes следующим образом:

import ctypes
from numpy.ctypeslib import ndpointer
lib = ctypes.cdll.LoadLibrary("./ctest.so")
fun = lib.cfun
fun.restype = None
fun.argtypes = [ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),
                ctypes.c_size_t,
                ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")]

Теперь вызовы вашей функции будут действительно удобными:

indata = numpy.ones((5,6))
outdata = numpy.empty((5,6))
fun(indata, indata.size, outdata)

Вы также можете определитьОбертка, чтобы сделать это еще удобнее:

def wrap_fun(indata, outdata):
    assert indata.size == outdata.size
    fun(indata, indata.size, outdata)
16 голосов
/ 03 мая 2011

Просто передайте все четыре аргумента в функцию C.Измените свой код Python с:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

На:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_int(5), ctypes.c_int(6),
    ctypes.c_void_p(outdata.ctypes.data))
...