Вопрос

Я пытаюсь предоставить буфер пиксельной информации изображения (32-разрядный RGBA) через интерфейс буфера Python 3.x.После довольно продолжительной игры я смог заставить это работать следующим образом:

int Image_get_buffer(PyObject* self, Py_buffer* view, int flags)
{
    int img_len;
    void* img_bytes;

    // Do my image fetch magic
    get_image_pixel_data(self, &img_bytes, &img_len);

    // Let python fill my buffer
    PyBuffer_FillInfo(view, self, img_bytes, img_len, 0, flags); 
}

И в python я могу играть с этим вот так:

mv = memoryview(image)
print(mv[0]) # prints b'\x00'
mv[0] = b'\xFF' # set the first pixels red component to full
mx[0:4] = b'\xFF\xFF\xFF\xFF' # set the first pixel to white

И это работает великолепно.Однако было бы здорово, если бы я мог работать с полным значением пикселя (int, 4 байта) вместо отдельных байтов, поэтому я изменил выборку буфера следующим образом:

int Image_get_buffer(PyObject* self, Py_buffer* view, int flags)
{
    int img_len;
    void* img_bytes;

    // Do my image fetch magic
    get_image_pixel_data(self, &img_bytes, &img_len);

    // Fill my buffer manually (derived from the PyBuffer_FillInfo source)
    Py_INCREF(self);
    view->readonly = 0;
    view->obj = self;
    view->buf = img_bytes;
    view->itemsize = 4;
    view->ndim = 1;
    view->len = img_len;
    view->suboffsets = NULL;

    view->format = NULL;
    if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
        view->format = "I";

    view->shape = NULL;
    if ((flags & PyBUF_ND) == PyBUF_ND)
    {
        Py_ssize_t shape[] = { (int)(img_len/4) };
        view->shape = shape;
    }

    view->strides = NULL;
    if((flags & PyBUF_STRIDED) == PyBUF_STRIDED)
    {
        Py_ssize_t strides[] = { 4 };
        view->strides = strides;
    }

    return 0;
}

Это фактически возвращает данные, и я могу прочитать их правильно, но любая попытка присвоить им значение теперь терпит неудачу!

mv = memoryview(image)
print(mv[0]) # prints b'\x00\x00\x00\x00'
mv[0] = 0xFFFFFFFF # ERROR (1)
mv[0] = b'\xFF\xFF\xFF\xFF' # ERROR! (2)
mv[0] = mv[0] # ERROR?!? (3)

В случае 1 ошибка сообщает мне, что 'int' does not support the buffer interface, что досадно и немного сбивает с толку (в конце концов, я указал, что формат буфера был "I"), но я могу с этим справиться.Однако в случае 2 и 3 все становится действительно странным:В обоих случаях дайте мне сообщение об ошибке типа mismatching item sizes for "my.Image" and "bytes" (Где my.Image это, очевидно, мой тип изображения)

Это очень сбивает меня с толку, поскольку данные, которые я передаю, очевидно, того же размера, что и то, что я получаю из этого элемента.Кажется, что буферы просто перестают разрешать присваивание, если itemsize больше 1.Конечно, документация для этого интерфейса действительно скудная, и просмотр кода python на самом деле не дает никаких примеров использования, так что я довольно сильно застрял.Я пропускаю какой-то фрагмент документации, в котором говорится "буферы становятся практически бесполезными, когда размер элемента > 1", я делаю что-то неправильно, чего не вижу, или это ошибка в Python?(Тестирование в соответствии с пунктом 3.1.1)

Спасибо за любую информацию, которую вы можете дать по этому (по общему признанию, продвинутому) вопросу!

Это было полезно?

Решение

Я нашел это в коде python (в memoryobject.c в Objects) в функции memory_ass_sub:

/* XXX should we allow assignment of different item sizes
   as long as the byte length is the same?
   (e.g. assign 2 shorts to a 4-byte slice) */
if (srcview.itemsize != view->itemsize) {
    PyErr_Format(PyExc_TypeError,
        "mismatching item sizes for \"%.200s\" and \"%.200s\"", 
        view->obj->ob_type->tp_name, srcview.obj->ob_type->tp_name);
    goto _error;
}

это источник последних двух ошибок.Похоже, что itemsize даже для mv[0] все еще не равен самому себе.

Обновить

Вот что, я думаю, происходит.Когда вы пытаетесь назначить что-то в mv, она вызывает memory_ass_sub в Objects/memoryobject.c , но эта функция принимает только PyObject в качестве входных данных.Затем этот объект преобразуется в буфер внутри с помощью функции PyObject_GetBuffer, хотя в случае mv[0] это уже буфер (и буфер, который вы хотите!).Я предполагаю, что эта функция берет объект и превращает его в простой буфер itemsize = 1 независимо от того, является ли это уже буфером или нет.Вот почему вы получаете несоответствующие размеры товара даже для

mv[0] = mv[0]

Проблема с первым заданием,

mv[0] = 0xFFFFFFFF

проистекает (я думаю) из проверки, можно ли использовать int в качестве буфера, для чего в настоящее время он не настроен, насколько я понимаю.

Другими словами, буферная система в настоящее время не способна обрабатывать элементы размером больше 1.Не похоже, что это так уж далеко, но с вашей стороны потребовалось бы немного больше работы.Если у вас все получится, вам, вероятно, следует отправить изменения обратно в основной дистрибутив Python.

Еще одно Обновление

Код ошибки при вашей первой попытке присвоения mv[0] связан с тем, что int не выполняет PyObject_CheckBuffer при вызове PyObject_CheckBuffer.Очевидно, что система обрабатывает копии только из буферизуемых объектов.Это, похоже, тоже должно быть изменено.

Заключение

В настоящее время буферная система Python не может обрабатывать элементы с itemsize > 1, как вы догадались.Кроме того, он не может обрабатывать назначения буферу из небуферизуемых объектов, таких как целые числа.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top