使用ctypes数组时,PIL的Image.frombuffer预期数据长度 [英] PIL's Image.frombuffer expected data length when using ctypes array

查看:231
本文介绍了使用ctypes数组时,PIL的Image.frombuffer预期数据长度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Python,PIL和ctypes进行图像处理.当我一起砍东西时,我使用了PIL的fromstring函数将像素缓冲区从ctypes获取到PIL对象中.我只是简单地遍历数组,构建python字符串.

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.

这有效

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.

这不起作用

类似于此问题:使用PIL.Image和ctypes进行像素处理,我正在尝试使用frombuffer()来将像素数据更有效地获取到PIL对象中:

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")

尽管fromstring运行,但使用frombuffer会导致以下异常:

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

环境

缓冲区在C中以malloc的形式存储:

The buffer is malloc'ed in C as:

unsigned char *pixels_color = (unsigned char*)malloc((WIDTH*HEIGHT*4)*sizeof(unsigned char*));

  • 对于每个RGBA波段,缓冲区每个像素有4个字节.
  • Mac OS X 10.7,python2.7.1,PIL 1.1.7
  • 修改

    基于下面的 eryksun 的评论和答案,我将缓冲区分配从C中的malloc中移出了库放入Python,并将指针传递到C:

    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")
    

    这按预期工作.仍然不能解释为什么 PIL对C分配的缓冲区不满意,但是无论如何,这是更好的内存管理结构.多亏了ErykSun和HYRY!

    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!

    推荐答案

    设置tx.restype = POINTER(c_ubyte)时,生成的ctypes指针对象是缓冲区的地址的4或8个字节的缓冲区.图像缓冲区.您需要从该地址创建一个ctypes数组.

    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.

    如果您提前知道尺寸,则可以设置SIZE = WIDTH * HEIGHT * 4; tx.restype = POINTER(c_ubyte * SIZE).否则设置tx.restype = POINTER(c_ubyte),并强制转换为动态尺寸,即size = w * h * 4; pbuf = cast(result, POINTER(c_ubyte * size)).无论哪种方式,您都需要取消引用指针以获取数组.使用buf = pbuf[0]buf = pbuf.contents.然后,您可以将buf传递给Image.frombuffer.

    If you know the size ahead of time, you can set SIZE = WIDTH * HEIGHT * 4; tx.restype = POINTER(c_ubyte * SIZE). Otherwise set tx.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 either buf = pbuf[0] or buf = pbuf.contents. Then you can pass buf to Image.frombuffer.

    也就是说,使用ctypes分配内存通常更简单.这使您可以进行参考计数的内存管理,而不必手动释放内存.以下是一个玩具示例.

    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.

    假设动态尺寸,调用方需要获取图像尺寸才能分配数组,因此我添加了一个结构,该结构具有图像的宽度,高度和深度,并由C填充功能get_image_info.函数get_image接收指向图像数组的指针以复制数据.

    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 function get_image receives a pointer to the image array to copy in data.

    import ctypes
    
    lib = ctypes.CDLL('imgtest.dll')
    
    class ImageInfo(ctypes.Structure):
        _fields_ = (
            ('width', ctypes.c_int),
            ('height', ctypes.c_int),
            ('depth', ctypes.c_int),
        )
    
    pImageInfo = ctypes.POINTER(ImageInfo)
    pImage = ctypes.POINTER(ctypes.c_ubyte)
    
    lib.get_image_info.argtypes = [pImageInfo]
    lib.get_image_info.restype = ctypes.c_int
    
    lib.get_image.argtypes = [pImage]
    lib.get_image.restype = ctypes.c_int
    
    imgnfo = ImageInfo()
    lib.get_image_info(ctypes.byref(imgnfo))
    w, h, d = imgnfo.width, imgnfo.height, imgnfo.depth
    
    imgdata = (w * h * d * ctypes.c_ubyte)()
    lib.get_image(imgdata)
    
    from PIL import Image
    img = Image.frombuffer('RGBA', (w, h), imgdata, 'raw', 'RGBA', 0, 1)
    

    C声明:

    typedef struct _ImageInfo {
        int width;
        int height;
        int depth;
    } ImageInfo, *pImageInfo;
    
    typedef unsigned char *pImage;
    
    int get_image_info(pImageInfo imgnfo);
    int get_image(pImage img);
    

    这篇关于使用ctypes数组时,PIL的Image.frombuffer预期数据长度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆