Search code examples
python-3.6python-cffileptonica

TypeError: initializer for ctype 'unsigned int *' must be a cdata pointer, not bytes


I try to convert PIL image to leptonica PIX. Here is my code python 3.6:

import os, cffi
from PIL import Image

# initialize leptonica
ffi = cffi.FFI()
ffi.cdef("""
    typedef int           l_int32;
    typedef unsigned int  l_uint32;
    struct                Pix;
    typedef struct Pix    PIX;
    PIX * pixCreate       (int width, int height, int depth);
    l_int32 pixSetData    (PIX *pix, l_uint32 *data);
""")
leptonica = ffi.dlopen(os.path.join(os.getcwd(), "leptonica-1.78.0.dll"))

# convert PIL to PIX
im = Image.open("test.png").convert("RGBA")
depth = 32
width, height = im.size
data = im.tobytes("raw", "RGBA")
pixs = leptonica.pixCreate(width, height, depth)
leptonica.pixSetData(pixs, data)

pixSetData failes with message: TypeError: initializer for ctype 'unsigned int *' must be a cdata pointer, not bytes. How to convert bytes object (data) to cdata pointer?


Solution

  • PIL and Leptonica seem not to share exactly the same raw format. At last RGBA vs. ABGR. What worked for me was to use uncompressed TIFF as a fast and dependable data exchange format.

    # Add these to ffi.cdef():
    # 
    # typedef unsigned char l_uint8;
    # PIX * pixReadMem(const l_uint8 *data, size_t size);
    # l_ok pixWriteMem(l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 format);
    
    from io import BytesIO
    import PIL.Image
    
    IFF_TIFF = 4
    
    
    def img_pil_to_lepto(pilimage):
        with BytesIO() as bytesio:
            pilimage.save(bytesio, 'TIFF')
            tiff_bytes = bytesio.getvalue()
    
        cdata = ffi.from_buffer('l_uint8[]', tiff_bytes)
        pix = leptonica.pixReadMem(cdata, len(tiff_bytes))
        return pix
    
    
    def img_lepto_to_pil(pix):
        cdata_ptr = ffi.new('l_uint8**')
        size_ptr = ffi.new('size_t*')
        leptonica.pixWriteMem(cdata_ptr, size_ptr, pix, IFF_TIFF)
        cdata = cdata_ptr[0]
        size = size_ptr[0]
    
        tiff_bytes = bytes(ffi.buffer(cdata, size))
        with BytesIO(tiff_bytes) as bytesio:
            pilimage = PIL.Image.open(bytesio).copy()
            return pilimage