Python PIL';s Image.frombuffer使用ctypes数组时所需的数据长度

Python PIL';s Image.frombuffer使用ctypes数组时所需的数据长度,python,python-imaging-library,ctypes,Python,Python Imaging Library,Ctypes,我正在使用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 x

我正在使用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")
虽然不漂亮,但效果不错。带着待办事项发表评论,然后继续。在初始管道安装到位后,分析显示此代码块存在严重的性能问题。我想这并不奇怪

这不起作用

与此问题类似,我试图使用
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
工作正常,但使用
fromsbuffer
会导致以下异常:

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
  • 对于每个频带RGBA,缓冲区每像素有4个字节
  • Mac OS X 10.7、python2.7.1、PIL 1.1.7
编辑

根据下面的注释和回答,我将缓冲区分配从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分配的缓冲区不满意,但无论如何,这是更好的内存管理结构。感谢埃里克森和海瑞

请尝试以下代码:

import Image
from ctypes import c_ubyte, cast, POINTER

buf = (c_ubyte * 400)()
pbuf = cast(buf, POINTER(c_ubyte))
pbuf2 = cast(pbuf, POINTER(c_ubyte*400))

img1 = Image.frombuffer("RGBA", (10,10), buf, "raw", "RGBA", 0, 1)
img2 = Image.frombuffer("RGBA", (10,10), pbuf2.contents, "raw", "RGBA", 0, 1)
buf是一个ubyte数组,pbuf是指向ubyte的指针,pbuf2是指向ubyte的指针[400]。img1直接从buf创建,img2从pubf2.contents创建

程序从指向ubyte的指针创建图像,必须将其强制转换为指向数组的指针,并使用contents属性获取缓冲区。因此,请使用以下代码将指针转换为数组:

tmp = cast(result, POINTER(c_ubyte*4*w*h)).contents
Image.frombuffer('RGBA', (w, h), tmp, 'raw', 'RGBA', 0, 1)

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

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

也就是说,使用CType分配内存通常更简单。这为您提供了引用计数内存管理,而不必手动释放内存。下面是一个玩具的例子

假设是动态大小,调用者需要获取图像尺寸以分配数组,因此我添加了一个结构,该结构具有图像的宽度、高度和深度,由C函数
get\u image\u info
填充。函数
get_image
接收指向要复制数据的图像数组的指针

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

你的代码块创建的图像工程,但我不明白你的方向与下半年;
.contents
cast()
的组合不会改变行为。根据您的评论和回答,我将缓冲区分配从C移到Python中,并将指向结果缓冲区的指针传递到C中。这是应该的。这仍然不能解释为什么,但至少我可以继续前进。谢谢你朝正确的方向踢!对匿名评论家来说:在没有提供反馈的情况下记下答案几乎是无用的。如果您提供反馈,我可以尝试澄清或改进答案。为什么
tx.restype
设置为
指针(…)
?您是返回指针还是任何其他指针?@Unapiedra,是-tx函数是C,正在返回指向像素数据缓冲区的无符号char*指针。好的。现在,我很困惑。为什么要将
pix_clr
作为参数传递到函数中?为什么在执行
tx()
后,
char*指针(或其数据)没有被销毁?对不起,你说得对-我已经很久没有查看该代码了。在上面的“编辑”块中,
tx.restype
行是遗留的
pix_clr
变成了一个参数,因此我可以用python进行分配,并将其传递给C库或从C库传递出去,而不是在C中进行malloc'ed。
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);