0

I'm trying to make a function which is defined in a C library callable from python. I'm begginer with cython n C

Sooo in short I ended up having the following problem:

#.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)

The definition from library.h file

    @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]'.

Soo in short the question is how to indicate in the python function that n_out is a pointer to an integer and img is a pointer to an array. I tried the following

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)

But throws same error

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)
                   ^
------------------------------------------------------------

Thanks for helping

1 Answer 1

1

The idea should be to create a useful Python interface, not directly reproduce the C interface. Some of these arguments are pointless to the Python interface because they're already stored in (say) a Numpy array.

double *img, int X, int Y all describe the input array. Essentially the requirement is "2D, contiguous block of memory". In Cython this can be expressed using a typed memoryview:

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!)
                         ...)

The return value and n_out combine to describe the output array, which is (7xn_out). You could get Numpy to take ownership of this data, or you could copy the data. I recommend the latter. Therefore, you don't actually want n_out to be an input from Python. Instead:

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.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.