C函数(以下代码中的POINTER(c_ubyte)
变量)返回指向image_data
类型缓冲区的指针。我希望这些数据由Python管理,所以我想将其复制到bytearray
。这是函数调用
image_data = stb_image.stbi_load(filename_cstr, byref(width),
byref(height), byref(num_channels),
c_int(expected_num_channels))
我们只能在通话结束后了解图片的width
和height
,因此无法预先分配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
我该怎么办?
答案 0 :(得分:0)
好的,阅读评论中链接的问题的答案(谢谢,@" John Zwinck"和@" eryksun"),有两种存储数据的方式,在bytearray
或numpy.array
中。在所有这些摘录中,image_data
的类型为POINTER(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)
在编写图像时(没有提出这个问题,只是为了完整性而共享),我们可以像这样获得指向bytearray数据的指针并将其提供给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_ptr = np.ctypeslib.as_array(array_type.from_address(address))
但是这仅仅指向C函数返回的内存,不会复制到Python管理的数组对象中。我们可以通过创建一个numpy数组来复制
image_data = np.array(image_data_ptr)
确认我在那里做了assert all(arr_np == arr_bytes)
。 arr_np.dtype
是uint8
。
在写入图像时,我们可以获得指向numpy数组的数据,如下所示
image_data = image_data_numpy.ctypes.data_as(POINTER(c_ubyte))
答案 1 :(得分:0)
您的变量array_type甚至不应该被调用,因为它实际上不是初始化的C数组,也不是任何类型的类型,而是为执行数组初始化而准备的Python对象。 那么,也不应该调用初始化数组。 :d
你应该做的相当于:
unsigned char array[channels*width*height];
C中的然后,数组是指向N *类型的unsigned char的指针,指向数组的第一个字节。 (索引0) cast()应该获得一个指针来查看数据的类型。这样做:
array = (c.c_ubyte*(channels*width*height))()
应该做的伎俩。但是你不需要额外分配的内存。因此,您可以按照评论中的建议创建指针。
但我建议您使用:
image_data = bytearray(c.string_at(image_data))
它应该可以工作,当然,假设返回的图像为空终止。嗯,这也意味着使用签名的字符,但它不一定是。 如果您编写了C部分,只需在内存中分配一个额外的字节,该内存将包含一个声明/强制转换为包含无符号字符的图像,并将最后一项设置为0。 然后让算法像以前一样工作。如果你没有null终止它,你仍然会得到整个图像与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))