Python PIL';s Image.frombuffer使用ctypes数组时所需的数据长度
我正在使用Python、PIL和ctypes进行图像处理。当我把东西拼凑在一起时,我使用了PIL的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
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
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);