2017-06-04 16 views
1

C函数(以下代码中的image_data变量)返回指向类型为POINTER(c_ubyte)的缓冲区的指针。我希望这些数据由Python管理,因此我想将它复制到bytearray。这里的函数调用Python`ctypes` - 如何将C函数返回的缓冲区复制到字节数组中

image_data = stb_image.stbi_load(filename_cstr, byref(width), 
           byref(height), byref(num_channels), 
           c_int(expected_num_channels)) 

我们得到了只有电话后知道图像的widthheight,所以不能预先分配bytearray

我会用

array_type = c.c_ubyte * (num_channels.value * width.value * height.value) 

image_data_bytearray = bytearray(cast(image_data, array_type)) 

但类型cast到必须是一个指针,而不是数组,所以我得到一个错误。

TypeError: cast() argument 2 must be a pointer type, not c_ubyte_Array_262144 

我该怎么办?

+1

你会发现这个有用:https://stackoverflow.com/questions/4355524/getting-data-from-ctypes-array-into-numpy - NumPy的是一个很好的图书馆数据处理的类型。 –

+1

您可以有一个指向数组的指针,例如'ptype = POINTER(c_ubyte *(num_channels.value * width.value * height.value))'。通过使用'from_buffer'复制源指针的地址来获取实例,然后解引用以获取可传递给'bytearray'的数组,例如'image_data_bytearray = bytearray(ptype.from_buffer(image_data)[0])''。 – eryksun

回答

0

好的,阅读评论中链接到的问题的答案(谢谢,@“John Zwinck”和@“eryksun”),有两种存储数据的方式,可以是bytearraynumpy.array。在所有这些片段,image_dataPOINTER(c_ubyte)型的,我们有array_type定义为 -

array_type = c_ubyte * num_channels * width * height 

我们可以先创建循环的ByteArray上,并设置字节

arr_bytes = bytearray(array_size) 
for i in range(array_size): 
    arr_bytes[i] = image_data[i] 

-

或者更好的方法是使用from_address创建一个C数组实例,然后用它初始化一个bytearray -

image_data_carray = array_type.from_address(addressof(image_data.contents)) 

# Copy into bytearray 
image_data_bytearray = bytearray(image_data_carray) 

和写入图像(没有问这个问题,只是分享的完整性),我们可以得到指针这样的字节组数据,并给它stbi_write_png

image_data_carray = array_type.from_buffer(image_data_bytearray) 
image_data = cast(image_data_carray, POINTER(c_ubyte)) 

中 -

做的numpy基于方式是回答在链接的问题

address = addressof(image_data.contents) 
image_data = np.ctypeslib.as_array(array_type.from_address(address)) 

仅此一项但只有PO整数到由C函数返回的内存。我们可以通过创建一个numpy的数组作为

image_data_numpy = np.array(image_data_numpy) 

复制要确认我曾经做过一个assert all(arr_np == arr_bytes)那里。并且arr_np.dtypeuint8

和写入图像期间,我们可以得到一个指向numpy的阵列的数据是这样

image_data = image_data_numpy.ctypes.data_as(POINTER(c_ubyte)) 
0

你的变量ARRAY_TYPE甚至不应该这样叫,因为它其实不是一个初始化数组c也不任何类型的类型,但为做数组初始化准备的Python对象。 那么,初始化数组也不应该这样调用。 :d

你应该在做有的等效:

unsigned char array[channels*width*height]; 

在C.然后阵列是一个指向N *类型无符号字符指向阵列的第一个字节。 (index 0) cast()应该得到一个指针来查看数据的类型。这样做:

array = (c.c_ubyte*(channels*width*height))() 

应该做的伎俩。但是你不需要额外分配的内存。所以你可以按照评论中的建议创建一个指针。

但我建议你使用:

image_data = bytearray(c.string_at(image_data)) 

它应该工作,假设,当然,这返回的图像是空值终止。那么,这也意味着使用签名的字符,但它不一定是。 如果您编写了C部分,只需将一个额外的字节分配给将包含声明/转换为包含无符号字符并将最后一项设置为0的映像的内存。 然后让算法像以前一样工作。如果你不终止它,你仍然会得到整个图像string_at(),但会有3个字节或更多的内存泄漏。非常不受欢迎。

我在C模块中使用了这个技巧进行色彩空间转换。它的工作速度非常快,因为没有循环,没有任何额外的。 string_at()只是拉入缓冲区并在其周围创建Python字符串包装。 然后,您可以使用numpy.fromstring(...)或array.array(“B”,image_data)或使用像上面的bytearray()等

否则,我刚才看到你的答案。你也可以这样写,但我认为我的诡计更好(当然,如果你可以改变C代码)。

P.S.哎呦!我刚刚在doc字符串中看到string_at()可以具有可选的参数大小。也许使用它会完全忽略终止,并且不会有任何泄漏。我现在在问自己,为什么我没有在我的项目中使用它,却与空终止相混淆。 也许出于懒惰。使用大小不应该要求对C代码进行任何修改。因此,这将是:

image_data = bytearray(c.string_at(image_data, channels*width*height)) 
相关问题