numpy 如何使用ctypes转换Python浮点数组?

vbopmzt1  于 2023-01-09  发布在  Python
关注(0)|答案(2)|浏览(347)

我既没有C++的经验,也没有ctypes库的经验。
我有一个用C语言编写的程序,其中包含一个名为image_lib的DLL。

int Generate_Hologram(unsigned char *Array, unsigned char* WFC, float *x_spots, float *y_spots, float *z_spots, float *I_spots, int N_spots, int ApplyAffine);

在Python中,我有一个名为x_spots的数组和其他一些数组:

# coordinate locations
x_spots = np.arange(-1, 2, 1, dtype=float)
x_spots = np.arange(-1, 2, 1, dtype=float)
z_spots = np.arange([0] * num_tweezers.value, dtype=float)

# Intensities
int_spots = np.arange([1] * num_tweezers.value, dtype=float)

# Image for the GPU to compute the hologram on
Image = np.empty([width.value*height.value*bytpesPerPixel], np.uint8, 'C');

# Create a blank vector to hold the wavefront correction
WFC = np.empty([width.value*height.value*bytpesPerPixel], np.uint8, 'C');

我有(基于制造商给出的单一例子)

image_lib.Generate_Hologram(Image.ctpyes.data_as(POINTER(c_ubyte)), WFC.ctypes.data_as(POINTER(c_ubyte)), ....
                           )

我怎么把x_floats传递给这个函数呢?

x_spots.ctypes.data_as(POINTER(c_float))
rekjcdws

rekjcdws1#

我能在代码中发现的一个问题是浮点数的大小。
注意,作为一个指针,你传递给函数的numpy数据的指针并不重要,比如我,我习惯于以arr.ctypes.data_as(ctypes.c_void_p)的形式传递数据。
这是动态的,所以C编译器不可能检查任何东西,而且,至少在这个例子中,没有动态检查,也没有意义把这个numpy数组数据当作指向这个类型的指针。
因此,正确地或不正确地说,作为POINTER,你想传递一个numpy数组给一个函数,期望一个浮点数 *,并不能保护你免受错误。
这也不意味着任何转换,指针只是传递给C函数,由它来正确解释,这是静态完成的,当你输入参数float *时。
因此,在代码中(你似乎很想知道),你不能确保C函数得到它所期望的一堆float,不管怎样,C函数将得到一个指向numpy数据的指针。
代码中的问题是x_spots中的数据不是float(如单词float的C含义;即32位浮点数)。它们很可能是双精度数
我说“最有可能”是因为我不是不同python解释器的Maven,我知道,根据解释器的不同,原生float类型的python可能是float32,或者float64,甚至是其他类型的,在最经典的CPython中,它们是float64。我也不确定当dtypes是原生类型float时,对numpy意味着什么。但是,使用我的cpython,一个np.array([1,2,3], dtype=float)是一个由float64组成的numpy数组。所以,不是100%确定这是否是一个确定的事情,或者是否存在一个python解释器,在其中该数组将由float32组成。因为我不确定,无论如何,我从来不用float作为dtype的参数,我用np.float32np.float64
但是,最有可能(甚至可以肯定)您的x_spots是由float64组成的。
然后,当你传递x_spots.ctypes.data_as(POINTER(...))时,你所说的指针是什么并不重要,指针将是指向那些float64的指针。也就是说,用C语言来说,指向double。你的C函数将把它当作指向float的指针(没有任何警告:警告发生在编译时,我们已经过去了)。
说来话长但结论是要么你

  • 修改你的C函数,接受double *作为参数,对于你的,可能是64位的计算机,这可能是最好的方法,但是你好像说你不能修改C函数
  • 因此,您也可以确保数据由float(C语言的意思)组成,即使用dtype=np.float32创建x_spots

请注意,即使您选择第一种情况(double *),也最好将dtype更改为显式设置为np.float64
一旦你这样做了,不管你是以x_spots.ctypes.data_as(c_void_p)x_spots.ctypes.data_as(POINTER(c_float))x_spots.ctypes.data_as(POINTER(c_double)),甚至x_spots.ctypes.data_as(POINTER(c_char))的形式传递参数。
(当然,我强烈建议您不要使用错误类型POINTER() arg,但这是为了代码的可读性;从执行变更的Angular 来看,它不会改变结果)
所以

  • 将C函数的参数类型从float *更改为double *
  • 或者,将numpy数组的dtypefloat更改为np.float32
mec1mxoz

mec1mxoz2#

下面是一个使用numpy将C float作为参数传递的最小示例。这是OP将其应用于其函数的练习。注意,C函数必须假定为固定大小,或者以某种形式传递数组的长度。
还要注意,dtype被声明为ct.c_float,因为Python float通常是64位的,而C float通常是32位的。

    • 测试c**
#include <stdio.h>

__declspec(dllexport) // for Windows
void func(float* p, size_t size) {
    for(size_t i = 0; i < size; ++i)
        printf("p[%zu] = %f\n", i, p[i]);
}
    • 测试. py**
import ctypes as ct
import numpy as np

dll = ct.CDLL('./test')
# The helper function "ndpointer" can declare the expected type
# and either number of dimensions expected or the shape of the
# numpy array.  ctypes will then require that array and type check
# the parameter. 
dll.func.argtypes = np.ctypeslib.ndpointer(dtype=ct.c_float, ndim=1),
dll.func.restype = None

x_spots = np.arange(-1, 2, 1, dtype=ct.c_float)
dll.func(x_spots, len(x_spots))

输出:

p[0] = -1.000000
p[1] = 0.000000
p[2] = 1.000000

相关问题