Как передать битовую карту байтов в функцию Cython? Я получаю значения мусора

Я делаю поиск субизображения в python, и он явно слишком медленный. Итак, в процессе конвертации в Cython я обнаружил, что это не так просто. Питонская сторона вещей показывает байты как правильные (при выполнении debugPrint), и поиск субизображения был протестирован и работает отлично. Однако, выполнив свое первое преобразование py в Cy, я столкнулся с камнем преткновения. Единственное, что я делаю, чтобы преобразовать некоторые байты Python (я думаю, что они являются байтами....: o) в указатель на char * - это присваивание. Я как-то теряю право собственности на блок данных?

Вот соответствующий код:

cdef struct PixelsBMP:
    char* data
    int width, height, bands


# Here's how I create the bytes:


cpdef grabPixelsBMP(img=None):
    if img is None:
        img = ImageGrab.grab()
    elif isinstance(img, str):
        img = Image.open(img)

    with io.BytesIO() as bytes_io:
        img.save(bytes_io, 'BMP')
        data = bytes_io.getvalue() 
        offset = int.from_bytes(data[10:14], byteorder='little', signed=False)
        data = data[offset:]        # pixels start here

    cdef PixelsBMP px;
    px.data = data
    px.width = img.width
    px.height = img.height
    px.bands = 3 if img.mode == 'RGB' else 4

    return px


# Here's how I access the bytes:
cpdef debugPrintPixels(PixelsBMP px):
    import sys
    cdef char* d = px.data
    print('width:', px.width)
    print('height:', px.height)
    print('bands (alpha=>4):', px.bands)

    cdef:
        int pad_sum = 0
        int pad = nextMult4Pad(px.width * px.bands)
        int x, y, offs

    for y in range(0, px.height):
        for x in range(0, px.width):
            offs = px.width * px.bands * y + px.bands * x + pad_sum
            sys.stdout.write('(' + str(hex(d[offs])) + ',' + str(hex(d[offs + 1])) + ',' + \
                str(hex(d[offs + 2])) + ((',' + str(hex(d[offs + 3]))) if px.bands == 4 else '') + ')')
        print()
        pad_sum += pad

Что это печатает:

('width:', 7)
('height:', 3)
('bands (alpha=>4):', 4)
(0x0,0x0,0x0,0x0)(0x0,0x0,0x0,0x0)(0x1,0x0,0x0,0x0)(0x30,-0x3f,0x4a,0x5c)(0x2,0x0,0x0,0x0)(-0x1,-0x1,-0x1,-0x1)(0x0,0x7,0x0,0x0)()
(0x0,0x0,0x0,0x0)(0x1,0x0,0x0,0x0)(0x30,-0x3f,0x4a,0x5c)(0x2,0x0,0x0,0x0)(-0x1,-0x1,-0x1,-0x1)(0x0,0x4,0x0,0x0)(0x0,0x0,0x0,0x0)()
(0x1,0x0,0x0,0x0)(0x30,-0x3f,0x4a,0x5c)(0x4,0x0,0x0,0x0)(-0x1,-0x1,-0x1,-0x1)(0x64,0x1,0x0,0x53)(0x0,0x0,0x0,0x0)(0x1,0x0,0x0,0x0)()

Где должно быть изображение черных пикселей с альфа-каналом = 255.

Правильно ли я получаю доступ к байту с помощью Cython?

1 ответ

Решение

Это проблема владения памятью. Ваш bytes_io владеет данными, которые px_data указывает на. Вам нужно выделить немного памяти и скопировать данные:

# at the top of your file
from libc.stdlib cimport malloc
from libc.string cimport memcpy

#replacing px.data = data
cdef char* data_str = data
px.data = <char*>malloc(sizeof(char)*len(data))
memcpy(px.data,data_str, len(data))

Когда вы закончите со структурой, вам нужно освободить данные. Обратите внимание, что если вы передаете структуру в Python (например, путем вызова cpdef grabPixelsBMP из Python) он генерирует его словарную копию. Вы должны освободить данные, хранящиеся в структуре, а не в словаре. Управление памятью в С может быть сложным...

Другие вопросы по тегам