tags:

views:

59

answers:

1

I've got code that takes a PIL image and converts it to a ctypes array to pass out to a C function:

w_px, h_px = img.size
pixels = struct.unpack('%dI'%(w_px*h_px), img.convert('RGBA').tostring())
pixels_array = (ctypes.c_int * len(pixels))(*pixels)

But I'm dealing with big images, and unpacking that many items into function arguments seems to be noticeably slow. What's the simplest thing I can do to get a reasonable speedup?

I'm only converting to a tuple as an intermediate step, so if it's unnecessary, all the better.

+3  A: 

You can first build an uninitialized array:

pixarray = (ctypes.c_int * (w_px * h_px))()

and then copy the image's contents into it:

# dylib in MacOSX, cdll.wincrt in Win, libc.so.? in Unix, ...
clib = ctypes.CDLL('libc.dylib')

_ = clib.memcpy(pixarray, im.tostring(), w_px * h_px * 4)

The return value of memcpy is an address you don't care about, so I "swallowed" it by assigning it to name "single underscore" (which by convention means "I don't care about this one";-).

Edit: as @Mu Mind points out in a comment, the latter fragment can usefully be simplified to use ctypes.memmove without the need to go platform-dependent to ferret out clib: just do

_ = ctypes.memmove(pixarray, im.tostring(), w_px * h_px * 4)
Alex Martelli
Looks like ctypes.memmove does the same thing in a platform-independent way: ctypes.memmove(pixarray, im.tostring(), w_px*h_px*4)
Mu Mind
@Mu, excellent, +1 and thanks! Let me edit the answer accordingly.
Alex Martelli
Oops, looks like the output has endianness problems. Any suggestions?
Mu Mind
Unfortunately I don't think `ctypes` has a way to swap bytes around -- the `array` module does so you could use that instead, using either the implied buffer interface of arrays or `array.buffer_info` (though the latter only exists for backwards compatibility).
Alex Martelli