2

I am trying to build a wrapper to a camera driver in written C using Cython. I am new to Cython (started 2 weeks ago). After a lot of struggle, I could successfully develop wrappers for structures, 1D arrays but now I am stuck with 2D arrays.

One of the camera's C APIs takes a 2D array pointer as input and assigns the captured image to it. This function needs to be called from Python and the output image needs to be processed/displayed in Python. After going through the Cython docs and various posts on stack-overflow, I ended up with more confusion. I could not figure out how to pass 2D arrays between Python and the C. The driver api looks (somewhat) like this:

driver.h

void assign_values2D(double **matrix, unsigned int row_size, unsigned int column_size);

c_driver.pyd

cdef extern from "driver.h":
    void assign_values2D(double **matrix, unsigned int row_size, unsigned int column_size)

test.pyx

from c_driver import assign_values2D
import numpy as np
cimport numpy as np
cimport cython
from libc.stdlib cimport malloc, free
import ctypes

@cython.boundscheck(False)
@cython.wraparound(False)
def assignValues2D(self, np.ndarray[np.double_t,ndim=2,mode='c']mat):
    row_size,column_size = np.shape(mat)
    cdef np.ndarray[double, ndim=2, mode="c"] temp_mat = np.ascontiguousarray(mat, dtype = ctypes.c_double)
    cdef double ** mat_pointer = <double **>malloc(column_size * sizeof(double*))
    if not mat_pointer:
        raise MemoryError
    try:
        for i in range(row_size):
            mat_pointer[i] = &temp_mat[i, 0]

        assign_values2D(<double **> &mat_pointer[0], row_size, column_size)
        return np.array(mat)
    finally:
        free(mat_pointer)

test_camera.py

b = np.zeros((5,5), dtype=np.float) # sample code
print "B Before = "
print b
assignValues2D(b)
print "B After = "
print b

When compiled, it gives the error:

Error compiling Cython file:
------------------------------------------------------------
...
    if not mat_pointer:
        raise MemoryError
    try:
        for i in range(row_size):
            mat_pointer[i] = &temp_mat[i, 0]
                ^
 ------------------------------------------------------------
 test.pyx:120:21: Cannot take address of Python variable

In fact, the above code was taken from a stack-overflow post. I have tried several other ways but none of them are working. Please let me know how I can get the 2D image into Python. Thanks in advance.

DVS
  • 43
  • 10
  • My guess is that this may not be the first error message the compilation gives and that the earlier error messages may be more informative. – DavidW Jul 12 '17 at 18:04
  • But probably the issue is that `tmp_mat` is defined as `np.ndarray[double...` rather than `np.ndarray[np.double_t...` – DavidW Jul 12 '17 at 18:05
  • Thanks @DavidW for your quick response.There are no earlier compiler errors. – DVS Jul 12 '17 at 18:16
  • I have tried `np.ndarray[np.double_t...]` and it gives the same compiler error message – DVS Jul 12 '17 at 18:17
  • My guesses (without trying the code) were wrong. Found the real issue now... – DavidW Jul 12 '17 at 20:42

1 Answers1

2

You need to type i:

cdef int i

(Alternatively you can type row_size and it also works)

Once it knows that i is an int then it can work out the type that indexing the tmp_map gives and so the & operator works.


Normally it's pretty good about figuring out the type of loop variables like i, but I think the issue is that it can't deduce the type of row_size so it decided it can't deduce the type of i since it is deduced from range(row_size). Because of that it can't deduce the type of temp_mat[i,0].


I suspect you also you also want to change the return statement to return np.array(temp_mat) - the code you have will likely work most of the time but occasionally np.ascontinuousarray will have to make a copy and mat won't be changed.

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Awesome!!! It is working!!!. I just defined `row_size` as `int` and it started working. I am struggling with this for the past 1 week. Probably, with the current level of experience that I have with Cython, it would have taken years for me to figure out this issue. Thanks a lot @DavidW for the solution and the explanation. – DVS Jul 13 '17 at 05:13
  • Based on your explanation, dont you think the Cython compiler should point out that the variable needs to be defined? Had the compiler pointed it out, this would not have become an issue at all. – DVS Jul 13 '17 at 05:27
  • It would certainly have been helpful. There are times when you do want to index with a Python variable (e.g. a tuple) so it's not 100% obvious how to generate a warning. Let me explain my logic though so you can hopefully use it in future: we know from the error message that `temp_mat[i,0]` is giving a Python object. Initially I thought you'd failed to type `temp_mat`. Once I'd eliminated that, `i` was the only other option on that line. – DavidW Jul 13 '17 at 06:06