我正在尝试使用 Cython 用 C 语言构建相机驱动程序的包装器。我是 Cython 的新手(两周前开始)。经过一番努力,我可以成功开发结构体、一维数组的包装器,但现在我陷入了二维数组的困境。
相机的 C API 之一采用 2D 数组指针作为输入,并将捕获的图像分配给它。该函数需要从 Python 调用,并且输出图像需要在 Python 中处理/显示。在浏览了 Cython 文档和有关堆栈溢出的各种帖子后,我最终感到更加困惑。我不知道如何在 Python 和 C 之间传递 2D 数组。驱动程序 api 看起来(有点)像这样:
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
编译时,给出错误:
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
事实上,上面的代码是从堆栈溢出中获取的 post 我尝试了其他几种方法,但没有一个有效。请告诉我如何将 2D 图像导入 Python。提前致谢。
以下是如何在 Cython 中将二维数组从 Python 传递到 C 并取回的方法。关键是理解 C 如何以及 Cython 如何处理指向数组的指针。以下是一个分步指南,以及说明:
# 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
@cython.boundscheck(False)
@cython.wraparound(False)
def assignValues2D(self, np.ndarray[np.double_t,ndim=2,mode='c'] mat):
cdef int row_size = mat.shape[0]
cdef int column_size = mat.shape[1]
cdef double* mat_pointer = &mat[0, 0] # 获取指向第一个元素的指针
assign_values2D(mat_pointer, row_size, column_size)
return mat # 不需要复制,因为我们修改了原始数据
解释:
-
C 中的二维数组: C 中的二维数组通常表示为指向数组的指针,其中每个数组表示一行。这意味着需要为行分配内存,然后为每一行分配内存,从而有效地创建一个指针数组。
-
Cython 的内存视图: Cython 提供了一种强大的方式来使用
memoryview
与 C 数组交互。内存视图允许在不复制数据的情况下创建 NumPy 数组的视图,这对于大型数组来说非常高效。 -
修改后的代码:
-
我们无需手动分配内存。相反,我们获取 NumPy 数组中第一个元素的指针 (
&mat[0, 0]
)。由于 NumPy 数组在内存中是连续的,因此该指针可以用来访问所有元素。 -
我们将此指针连同行数和列数传递给 C 函数
assign_values2D
。 -
C 函数现在接收一个
double*
,它可以用来像访问 C 中的普通二维数组一样访问数组元素。 - 无需复制数据或释放内存,因为我们直接在原始 NumPy 数组上操作。
driver.h 中的更改:
需要更新 C 函数
assign_values2D
以接受指向双精度型的单个指针,如下所示:
void assign_values2D(double *matrix, unsigned int row_size, unsigned int column_size);
在的 C 函数中,可以使用以下方法访问矩阵中的元素:
void assign_values2D(double *matrix, unsigned int row_size, unsigned int column_size) {
for (int i = 0; i < row_size; ++i) {
for (int j = 0; j < column_size; ++j) {
matrix[i * column_size + j] = // 设置值;
}
}
}
通过这些更改,现在应该能够将二维 NumPy 数组有效地传递给的 C 函数,并让 C 函数直接修改数组的内容。这避免了不必要的复制并提高了性能,尤其是在处理大型图像时。请记住在运行 Cython 代码之前使用
setup.py
文件编译的 Cython 代码。