2010-02-19 8 views
2

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

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); 
} 

И питона я могу играть с ним, как так:

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, который является позором и немного запутанным (я указать, что формат буфера был «я» в конце концов), но я могу с этим справиться. В случае, если 2 и 3 вещи получить действительно странно, хотя: В обоих случаях жиме мне TypeError чтение mismatching item sizes for "my.Image" and "bytes" (Где my.Image, очевидно, мой тип изображения)

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

Благодарим за понимание, которое вы можете дать по этой (правда, расширенной) проблеме!

+0

говорит: «'int' не поддерживает интерфейс буфера» или «объект не поддерживает буферный интерфейс»? –

+0

Он явно говорит «int», точно так же, как я набрал его. – Toji

+0

ОК, просто проверка –

ответ

1

Я нашел это в коде Python (в memoryobject.c в объектах) в функции 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; 
} 

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

Update

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

mv[0] = mv[0] 

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

мв [0] = 0xFFFFFFFF

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

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

Другой Update

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

Заключение

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

+1

Вы уверены, что 'len' должен быть количеством элементов? Это объясняет многое, но также находится в прямом противоречии с документацией Python (http://docs.python.org/3.1/c-api/buffer.html): 'Py_ssize_t len ​​- общая длина памяти в байтах . Я попробую хотя бы попробовать. Благодарю. – Toji

+0

Да, я больше не думаю, что это проблема. –

+0

Спасибо за то, что вы сделали это! Я по-прежнему новичок в API Python C, поэтому мне трудно найти нужные мне в этом биты. Я уже догадался о проблеме int (что немаловажно), но немного разочаровывает то, что itemize> 1 укушен. Учитывая время, которое я могу увидеть, могу ли я заставить это работать самостоятельно, и если так, я обязательно отправлю изменения обратно. Еще раз спасибо! – Toji

 Смежные вопросы

  • Нет связанных вопросов^_^