使用SWIG编组Python PIL图像 [英] Marshaling a Python PIL Image using SWIG

查看:135
本文介绍了使用SWIG编组Python PIL图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有非常简单的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屋!

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