Ошибка типа: инициализатором для ctype ‘unsigned int *’ должен быть указатель cdata, а не байты

#python-3.6 #python-cffi #leptonica

#python-3.6 #python-cffi #leptonica

Вопрос:

Я пытаюсь преобразовать PIL-изображение в leptonica PIX. Вот мой код на 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 сбой с сообщением: TypeError: initializer for ctype 'unsigned int *' must be a cdata pointer, not bytes .
Как преобразовать объект bytes ( data ) в указатель cdata?

Ответ №1:

Я получил ответ от Армина Риго на форуме python-cffi:

Предполагая, что у вас есть недавний cffi 1.12, вы можете сделать:

    leptonica.pixSetData(pixs, ffi.from_buffer("l_uint32[]", data))
  

Способ обратной совместимости сложнее, потому что нам нужно
убедитесь, что промежуточный объект остается активным:

    p = ffi.from_buffer(data)
   leptonica.pixSetData(pixs, ffi.cast("l_uint32 *", p))
   # 'p' must still be alive here after the call, so put it in a variable above!
  

Ответ №2:

PIL и Leptonica, похоже, используют не совсем одинаковый формат raw. Наконец, RGBA против ABGR. Что сработало для меня, так это использование несжатого TIFF в качестве быстрого и надежного формата обмена данными.

 # 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