使用ctypes数组时PIL的Image.frombuffer预期数据长度
我正在使用 Python、PIL 和 ctypes 进行图像处理。当我将一些东西组合在一起时,我使用 PIL 的 fromstring
函数将像素缓冲区从 ctypes 获取到 PIL 对象中。我只是迭代数组,构建 python 字符串。
这有效
tx = foo.tx
tx.restype = POINTER(c_ubyte)
result = tx(...args...)
#TODO there must also be a better way to do this
pystr = ""
for i in xrange(w*h*4):
pystr += result[i]
i = Image.fromstring("RGBA", (w, h), pystr)
i.save("out.png")
虽然不太漂亮,但它有效。评论了 TODO 并继续。初始管道到位后,分析显示此代码块存在重大性能问题。我想这并不奇怪。
这不起作用
与此问题类似:像素操作使用 PIL.Image 和 ctypes,我尝试使用 frombuffer()
更有效地将像素数据获取到 PIL 对象中:
tx = foo.tx
tx.restype = POINTER(c_ubyte)
result = tx(...args...)
i = Image.frombuffer('RGBA', (w, h), result, 'raw', 'RGBA', 0, 1)
i.save("out.png")
尽管 fromstring
工作,但使用frombuffer
会导致以下异常:
Traceback (most recent call last):
File "test.py", line 53, in <module>
i = Image.frombuffer('RGBA', (w, h), res, 'raw', 'RGBA', 0, 1)
File "/Library/Python/2.7/site-packages/PIL/Image.py", line 1853, in frombuffer
core.map_buffer(data, size, decoder_name, None, 0, args)
ValueError: buffer is not large enough
环境
缓冲区在 C 中进行 malloc
处理:
unsigned char *pixels_color = (unsigned char*)malloc((WIDTH*HEIGHT*4)*sizeof(unsigned char*));
- 缓冲区的每个像素每个像素有 4 个字节波段 RGBA。
- Mac OS X 10.7、python2.7.1、PIL 1.1.7
编辑
基于 eryksun 的在下面的评论和回答中,我将缓冲区分配从 C 库中的 malloc
移动到 Python 中,并将指针传递到 C 中:
tx = foo.tx
tx.restype = POINTER(c_ubyte)
pix_clr = (c_ubyte*(w*h*4))()
tx(...args..., pix_clr)
i = Image.frombuffer('RGBA', (w, h), pix_clr, 'raw', 'RGBA', 0, 1)
i.save("out.png")
这按预期工作。它仍然没有解释为什么 PIL 对 C 分配的缓冲区不满意,但无论如何,这是更好的内存管理结构。感谢 ErykSun 和 HYRY!
I am using Python, PIL and ctypes to do image manipulation. As I hacked stuff together, I used PIL's fromstring
function to get the pixel buffer from ctypes into a PIL object. I simply iterated over the array, building the python string.
This works
tx = foo.tx
tx.restype = POINTER(c_ubyte)
result = tx(...args...)
#TODO there must also be a better way to do this
pystr = ""
for i in xrange(w*h*4):
pystr += result[i]
i = Image.fromstring("RGBA", (w, h), pystr)
i.save("out.png")
It wasn't pretty, but it worked. Commented with a TODO and moved on. After getting the initial plumbing in place, profiling showed significant performance issues with this code block. Not surprising, I guess.
This does not work
Similar to this question: Pixel manipulation with PIL.Image and ctypes, I am trying to use frombuffer()
to get the pixel data into the PIL object more efficiently:
tx = foo.tx
tx.restype = POINTER(c_ubyte)
result = tx(...args...)
i = Image.frombuffer('RGBA', (w, h), result, 'raw', 'RGBA', 0, 1)
i.save("out.png")
Despite fromstring
working, using frombuffer
results in the following exception:
Traceback (most recent call last):
File "test.py", line 53, in <module>
i = Image.frombuffer('RGBA', (w, h), res, 'raw', 'RGBA', 0, 1)
File "/Library/Python/2.7/site-packages/PIL/Image.py", line 1853, in frombuffer
core.map_buffer(data, size, decoder_name, None, 0, args)
ValueError: buffer is not large enough
Environment
The buffer is malloc
'ed in C as:
unsigned char *pixels_color = (unsigned char*)malloc((WIDTH*HEIGHT*4)*sizeof(unsigned char*));
- The buffer has 4 bytes per pixel for each of the bands RGBA.
- Mac OS X 10.7, python2.7.1, PIL 1.1.7
Edit
Based on eryksun's comment and answer below, I moved the buffer allocation from the malloc
in the C library into Python and passed the pointer into C:
tx = foo.tx
tx.restype = POINTER(c_ubyte)
pix_clr = (c_ubyte*(w*h*4))()
tx(...args..., pix_clr)
i = Image.frombuffer('RGBA', (w, h), pix_clr, 'raw', 'RGBA', 0, 1)
i.save("out.png")
This works as expected. It still doesn't explain why PIL was unhappy with the C-allocated buffer, but this is the better structure for memory management anyway. Thanks to both ErykSun and HYRY!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当您设置 tx.restype = POINTER(c_ubyte) 时,生成的 ctypes 指针对象是图像缓冲区地址的 4 或 8 字节缓冲区。您需要从此地址创建一个 ctypes 数组。
如果你提前知道尺寸,可以设置SIZE = WIDTH * HEIGHT * 4; tx.restype = POINTER(c_ubyte * SIZE)。否则设置tx.restype = POINTER(c_ubyte),并转换为动态大小,即size = w * h * 4; pbuf = 转换(结果,POINTER(c_ubyte * 大小))。无论哪种方式,您都需要取消引用指针才能获取数组。使用
buf = pbuf[0]
或buf = pbuf.contents
。然后您可以将buf
传递给Image.frombuffer
。也就是说,使用 ctypes 分配内存通常更简单。这为您提供了引用计数内存管理,而不必手动释放内存。以下是一个玩具示例。
假设动态大小,调用者需要获取图像尺寸才能分配数组,因此我添加了一个包含图像宽度、高度和深度的结构体,该结构体由 C 函数填充>获取图像信息。函数
get_image
接收指向图像数组的指针以复制数据。C 声明:
When you set
tx.restype = POINTER(c_ubyte)
, the resulting ctypes pointer object is a buffer of either 4 or 8 bytes for the address of the image buffer. You need to create a ctypes array from this address.If you know the size ahead of time, you can set
SIZE = WIDTH * HEIGHT * 4; tx.restype = POINTER(c_ubyte * SIZE)
. Otherwise settx.restype = POINTER(c_ubyte)
, and cast to the dynamic size, i.e.size = w * h * 4; pbuf = cast(result, POINTER(c_ubyte * size))
. Either way, you need to dereference the pointer to get at the array. Use eitherbuf = pbuf[0]
orbuf = pbuf.contents
. Then you can passbuf
toImage.frombuffer
.That said, it's usually simpler to allocate memory using ctypes. This gives you reference-counted memory management instead of having to manually free memory. The following is a toy example.
Assuming a dynamic size, the caller needs to get the image dimensions in order to allocate the array, so I've added a struct that has the width, height, and depth of the image, which gets filled in by the C function
get_image_info
. The functionget_image
receives a pointer to the image array to copy in data.C declarations:
尝试以下代码:
buf是一个ubyte数组,pbuf是一个指向ubyte的指针,pbuf2是一个指向ubyte[400]的指针。 img1是直接从buf创建的,img2是从pubf2.contents创建的。
您的程序从指向 ubyte 的指针创建图像,您必须将其转换为指向数组的指针,并使用内容属性来获取缓冲区。因此使用以下代码将指针转换为数组:
try the following code:
buf is an ubyte array, pbuf is a pointer to ubyte, pbuf2 is a pointer to ubyte[400]. img1 is created from buf directly, img2 is created from pubf2.contents.
your program create image from an pointer to ubyte, you must cast it to pointer to array, and use contents attribute to get the buffer. So use the following code to convert pointer to array: