首页 > 编程问答 >Cython:将 2D 数组从 Python 传递到 C 并检索它

Cython:将 2D 数组从 Python 传递到 C 并检索它

时间:2024-07-21 14:19:59浏览次数:7  
标签:python multidimensional-array cython wrapper

我正在尝试使用 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 # 不需要复制,因为我们修改了原始数据

解释:

  1. C 中的二维数组: C 中的二维数组通常表示为指向数组的指针,其中每个数组表示一行。这意味着需要为行分配内存,然后为每一行分配内存,从而有效地创建一个指针数组。

  2. Cython 的内存视图: Cython 提供了一种强大的方式来使用 memoryview 与 C 数组交互。内存视图允许在不复制数据的情况下创建 NumPy 数组的视图,这对于大型数组来说非常高效。

  3. 修改后的代码:

  4. 我们无需手动分配内存。相反,我们获取 NumPy 数组中第一个元素的指针 ( &mat[0, 0] )。由于 NumPy 数组在内存中是连续的,因此该指针可以用来访问所有元素。
  5. 我们将此指针连同行数和列数传递给 C 函数 assign_values2D
  6. C 函数现在接收一个 double* ,它可以用来像访问 C 中的普通二维数组一样访问数组元素。
  7. 无需复制数据或释放内存,因为我们直接在原始 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 代码。

标签:python,multidimensional-array,cython,wrapper
From: 45064370

相关文章