使用SWIG编组Python PIL图像 [英] Marshaling a Python PIL Image using SWIG
问题描述
我有一个带有非常简单的C图像结构的库:
I've got a library that takes in a very simple C image structure:
// Represents a one-channel 8-bit image
typedef struct simple_image_t {
uint32 rows;
uint32 cols;
uint8 *imgdata;
} simple_image;
我没有创建这个库,也没有这个结构,所以我无法改变它。我负责使用SWIG为python包装这个库。 Python包装器需要能够接收PIL图像并将其转换为此结构。以下是我现在正在做的事情(使用SWIG %inline%
):
I didn't create this library, nor this structure, so I can't change it. I'm responsible for wrapping this library for python using SWIG. The Python wrapper needs to be able to take in a PIL Image and convert it into this structure. Here's how I'm doing it right now (using a SWIG %inline%
):
// Allows python to easily create and initialize this structure
simple_image* py_make_simple_image(uint32 width, uint32 height)
{
simple_image* img = new simple_image();
img->rows = height;
img->cols = width;
img->imgdata = new uint8[height * width];
return img;
}
// Allows python to set a particular pixel value
void py_set_simple_image(simple_image* img, uint32 pos, uint8 val)
{
img->imgdata[pos] = val;
}
然后在python包装器端,这就是现在的情况:
And then on the python wrapper side here's how things look right now:
# Make sure it's an 8-bit image
if pil_image.mode != "L":
pil_image = pil_image.convert("L")
# Create the simple image structure
(width, height) = pil_image.size
img = swig_wrapper.py_make_simple_image(width, height)
try:
# Copy the image data into the simple image structure
pos = 0
for pixel in pil_image.getdata():
swig_wrapper.py_set_simple_image(img, pos, pixel)
pos += 1
# Call some library method that accepts a simple_image*
return swig_wrapper.some_image_method(img)
finally:
# Clean up the simple image structure
swig_wrapper.py_destroy_simple_image(img)
令人惊讶的是,这可行,但是你可能已经猜到了工作时难以置信慢即使是中等大小的图像。我知道SWIG的正确的方式是使用类型映射,但是这意味着要深入研究PIL的C API,而我现在没有时间去做。
Amazingly this works, however as you may have guessed it's incredibly slow when working with even moderately large images. I know with SWIG the proper way to do things is to use a typemap, however that would mean digging in to the C API of PIL, and I just didn't have time to do that at the moment.
在速度方面我有哪些选择?是否有更快的方法将像素数据从PIL图像封送到这个简单的图像结构?有人已经这样做了,我的谷歌技能那么糟糕吗?我刚刚结账并很快需要学习PIL的内部结构吗?
What are my options in terms of speed? Are there quicker ways of marshaling the pixel data from a PIL image to this simple image structure? Has someone already done this and my Google skills are just that bad? Am I just boned and soon will need to learn the internals of PIL?
谢谢。
推荐答案
PIL的 Image.tostring()
返回 imgdata
所需的确切数据字符串。我使用的类型映射相当简单,但并不完美,我将在下面说明。以下是我在Windows上为我创建的示例代码:
PIL's Image.tostring()
returns a string of the exact data you need for imgdata
. The typemap I used is fairly simple, but not perfect, which I'll note below. Here is the sample code I created on Windows that worked for me:
typedef unsigned int uint32;
typedef unsigned char uint8;
typedef struct simple_image_t {
uint32 rows;
uint32 cols;
uint8 *imgdata;
} simple_image;
#ifdef SAMPLE_EXPORT
# define SAMPLE_API __declspec(dllexport)
#else
# define SAMPLE_API __declspec(dllimport)
#endif
SAMPLE_API void some_func(const simple_image* si);
sample.c
sample.c
#include <stdio.h>
#define SAMPLE_EXPORT
#include "sample.h"
void some_func(const simple_image* si)
{
uint32 i,j;
printf(
"rows = %d\n"
"cols = %d\n",
si->rows,si->cols);
/* Dump a simple map of the image data */
for(i = 0; i < si->rows; i++)
{
for(j = 0; j < si->cols; j++)
{
if(si->imgdata[i * si->rows + j] < 0x80)
printf(" ");
else
printf("*");
}
printf("\n");
}
}
sample.i
sample.i
%module sample
%begin %{
#pragma warning(disable:4100 4127 4706)
%}
%{
#include "sample.h"
%}
%include <windows.i>
%typemap(in) uint8* (char* buffer, Py_ssize_t length) {
PyString_AsStringAndSize($input,&buffer,&length);
$1 = (uint8*)buffer;
}
%include "sample.h"
makefile
makefile
all: _sample.pyd
sample.dll: sample.c sample.h
cl /nologo /W4 /LD /MD sample.c
sample_wrap.c: sample.i
@echo sample.i
swig -python sample.i
_sample.pyd: sample_wrap.c sample.dll
cl /nologo /W4 /LD /MD /Fe_sample.pyd sample_wrap.c /Ic:\Python27\include -link /LIBPATH:c:\Python27\libs python27.lib sample.lib
example.py
$来自PIL导入的b$ b
example.py
from PIL import Image
import sample
im = Image.open('sample.gif')
im = im.convert('L')
si = sample.simple_image()
si.rows,si.cols = im.size
s = im.tostring() # Must keep a reference
si.imgdata = s
sample.some_func(si)
使用这个快速示例,我还没有确定typemap应该如何正确地增加引用计数字符串对象。请注意,如果使用以下代码,上述代码可能会崩溃:
With this quick example I haven't determined how the typemap should correctly increment the reference count of the string object. Note that the above code could crash if the following code were used:
si.imgdata = im.tostring()
当前的typemap的 PyString_AsStringAndSize
返回一个指向PyString的直接指针对象的缓冲区,但不会增加对象的引用计数。它可以在 some_func
执行之前进行垃圾收集(对我来说是崩溃的Python)。分配给 s
保留对字符串的引用并防止出现问题。 typemap应该复制缓冲区,但你一直在寻找速度,所以这个hack可能就是你想要的。
The current typemap's PyString_AsStringAndSize
returns a direct pointer to the PyString object's buffer, but doesn't increment the reference count for the object. It can be garbage collected before some_func
executes (and was for me, crashing Python). Assigning to s
keeps a reference to the string and prevents problems. The typemap should copy the buffer, but you were looking for speed so this hack may be what you want.
这篇关于使用SWIG编组Python PIL图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!