Импорт функции C с помощью Cython, как передать указатель на функцию python - PullRequest
0 голосов
/ 27 мая 2020

Я пытаюсь создать функцию, которая определена в библиотеке C, вызываемой из python. Я начинаю с cython n C

Короче говоря, у меня возникла следующая проблема:

#.pyx file
cdef extern from "lsd.h":
    double *lsd_scale(int *n_out, double *img, int X, int Y, double scale)

def detector(int n_out, double  img, int  X, int  Y, double scale):
    return lsd_scale(n_out, img, X, Y, scale)

Определение из файла library.h

    @param n_out       Pointer to an int where LSD will store the number of
                       line segments detected.

    @param img         Pointer to input image data. It must be an array of
                       doubles of size X x Y, and the pixel at coordinates
                       (x,y) is obtained by img[x+y*X].

    @param X           X size of the image: the number of columns.

    @param Y           Y size of the image: the number of rows.

    @return            A double array of size 7 x n_out, containing the list
                       of line segments detected. The array contains first
                       7 values of line segment number 1, then the 7 values
                       of line segment number 2, and so on, and it finish
                       by the 7 values of line segment number n_out.
                       The seven values are:
                       - x1,y1,x2,y2,width,p,-log10(NFA)
                       .
                       for a line segment from coordinates (x1,y1) to (x2,y2),
                       a width 'width', an angle precision of p in (0,1) given
                       by angle_tolerance/180 degree, and NFA value 'NFA'.
                       If 'out' is the returned pointer, the 7 values of
                       line segment number 'n+1' are obtained with
                       'out[7*n+0]' to 'out[7*n+6]'.

Вкратце, вопрос в том, как указать в функции python, что n_out является указателем на целое число, а img - указателем на массив. Я пробовал следующее

ctypedef int*p_int
ctypedef double*p_double

cdef extern from "lsd.h":
    double *lsd_scale(int *n_out, double *img, int X, int Y, double scale)

def detector(p_int n_out, p_double  img, int  X, int  Y, double scale):
    return lsd_scale(n_out, img, X, Y, scale)

Но выдает ту же ошибку

Error compiling Cython file:
------------------------------------------------------------
...

cdef extern from "lsd.h":
    double *lsd_scale(int *n_out, double *img, int X, int Y, double scale)

def detector(int n_out, double  img, int  X, int  Y, double scale):
    return lsd_scale(n_out, img, X, Y, scale)
                    ^
------------------------------------------------------------

detector.pyx:6:21: Cannot assign type 'int' to 'int *'

Error compiling Cython file:
------------------------------------------------------------
...

cdef extern from "lsd.h":
    double *lsd_scale(int *n_out, double *img, int X, int Y, double scale)

def detector(int n_out, double  img, int  X, int  Y, double scale):
    return lsd_scale(n_out, img, X, Y, scale)
                           ^
------------------------------------------------------------

detector.pyx:6:28: Cannot assign type 'double' to 'double *'

Error compiling Cython file:
------------------------------------------------------------
...

cdef extern from "lsd.h":
    double *lsd_scale(int *n_out, double *img, int X, int Y, double scale)

def detector(int n_out, double  img, int  X, int  Y, double scale):
    return lsd_scale(n_out, img, X, Y, scale)
                   ^
------------------------------------------------------------

Спасибо за помощь

1 Ответ

1 голос
/ 27 мая 2020

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

double *img, int X, int Y все описывают входной массив. По сути, это требование - «2D, непрерывный блок памяти». В Cython это можно выразить с помощью типизированного представления памяти:

def detector(..., double[:,::1] img, ...):
    ... = lsd_scale(..., &img[0,0], # address of first element
                         img.shape[0], img.shape[1], # (it's possible these are the wrong way round, test!)
                         ...)

Возвращаемое значение и n_out объединяются, чтобы описать выходной массив, который равен (7xn_out). Вы можете получить Numpy, чтобы стать владельцем этих данных , или вы можете скопировать данные. Я рекомендую последнее. Следовательно, вы на самом деле не хотите, чтобы n_out входил в Python. Вместо:

def detector(...):
    cdef double[:,::1] result_memview # useful for later

    cdef int n_out
    cdef double* result = lsd_scale(&n_out, ...)
    try:
        # copy the data to an output array.
        # The following is an untested outline. Making it work is an exercise for you

        result_memview = <double[:7,:n_out:1]>result # make a temporary memoryview of the data
        return np.array(result_memview) # should make a copy, I think
    finally:
        free(result) # cimported.
...