初学者用Python(特别是Numpy)扩展C

6 投票
1 回答
3913 浏览
提问于 2025-04-15 19:26

我正在开发一个实时音频处理的动态链接库,其中有一个二维的C数组,里面存储的是浮点数据,这些数据代表了音频缓冲区。这个数组的一个维度是时间(样本),另一个维度是通道。我想把这个数组传递给一个Python脚本,作为numpy数组进行数字信号处理(DSP),然后再把处理后的数据传回C语言,以便继续在C中进行后续处理。C++中负责处理的成员函数大概是这样的:

void myEffect::process (float** inputs, float** outputs, int buffersize)
{
    //Some processing stuff
}

这里的数组inputsoutputs大小是相等的。整数buffersize表示inputsoutputs数组中的列数。在Python这边,我希望处理是通过一个类似下面的函数来完成的:

class myPyEffect
    ...
    ...
    def process(self,inBuff):
    #inBuff and outBuff should be numpy arrays
        outBuff = inBuff * self.whatever # some DSP stuff
        return outBuff
    ...
    ...

现在,我的问题是,怎样才能以最有效的方式将数据在C和Python之间传递(避免不必要的内存复制等)?到目前为止,对于简单的参数更改,我一直在使用类似下面的C API调用:

pValue = PyObject_CallMethod(pInstance, "setParameter", "(f)", value);

我是否可以对我的numpy数组使用类似的方法,还是有更好的方式?感谢你的阅读。

1 个回答

7

你可能可以完全避免使用NumPy的C API。Python可以通过ctypes模块来调用C语言的代码,而且你可以通过数组的ctypes属性来访问NumPy数据中的指针。

下面是一个简单的例子,展示了如何实现一个一维的平方和函数。

ctsquare.c

#include <stdlib.h>

float mysumsquares(float * array, size_t size) {
    float total = 0.0f;
    size_t idx;
    for (idx = 0; idx < size; ++idx) {
        total += array[idx]*array[idx];
    }
    return total;
}

编译成 ctsquare.so

这些命令行是针对OS X的,你的操作系统可能会有所不同。

$ gcc -O3 -fPIC -c ctsquare.c -o ctsquare.o
$ ld -dylib -o ctsquare.so -lc ctsquare.o

ctsquare.py

import numpy
import ctypes

# pointer to float type, for convenience
c_float_p = ctypes.POINTER(ctypes.c_float)

# load the library
ctsquarelib = ctypes.cdll.LoadLibrary("ctsquare.so")

# define the return type and arguments of the function
ctsquarelib.mysumsquares.restype = ctypes.c_float
ctsquarelib.mysumsquares.argtypes = [c_float_p, ctypes.c_size_t]

# python front-end function, takes care of the ctypes interface
def myssq(arr):
    # make sure that the array is contiguous and the right data type
    arr = numpy.ascontiguousarray(arr, dtype='float32')

    # grab a pointer to the array's data
    dataptr = arr.ctypes.data_as(c_float_p)

    # this assumes that the array is 1-dimensional. 2d is more complex.
    datasize = arr.ctypes.shape[0]

    # call the C function
    ret = ctsquarelib.mysumsquares(dataptr, datasize)

    return ret

if __name__ == '__main__':
    a = numpy.array([1,2,3,4])
    print 'sum of squares of [1,2,3,4] =', myssq(a)

撰写回答