Domanda

Sto cercando di esporre un buffer di informazioni di pixel (32 bit RGBA) attraverso l'interfaccia del buffer 3.x di Python. Dopo un po 'di suonare in giro, sono stato in grado di ottenere questo lavoro in questo modo:

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

E in python posso giocare con essa in questo modo:

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

e che funziona splendidamente. Tuttavia, sarebbe grande se ho potuto lavorare con il valore totale dei pixel (int, 4 byte) invece di singoli byte, così modificato buffer di recupero in questo modo:

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

Questo in realtà restituisce i dati e posso leggerlo correttamente, ma ogni tentativo di assegnare un valore in esso ora fallisce!

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)

Nel caso 1 l'errore mi informa che 'int' does not support the buffer interface, che è una vergogna e un po 'di confusione (ho specificato che il formato del buffer è stato "I", dopo tutto), ma posso fare con questo. Nel caso 2 e 3 le cose si fanno davvero strano, però: In entrambi i casi mi Gime un mismatching item sizes for "my.Image" and "bytes" lettura TypeError (Dove my.Image è, ovviamente, il mio tipo di immagine)

Questo è molto confuso per me, dal momento che i dati che sto passando, ovviamente, è la stessa dimensione di quello che esco di tale elemento. Sembra come se i buffer semplicemente smettere di consentire l'assegnazione se l'ItemSize è maggiore di 1. Naturalmente, la documentazione per questa interfaccia è molto scarsa e sfogliando attraverso il codice Python in realtà non dà alcun esempi di utilizzo, quindi sono abbastanza bloccato. Mi sto perdendo un po 'di snippit documentazione che indica "buffer diventano sostanzialmente inutile quando ItemSize> 1", sto facendo qualcosa di sbagliato che non riesco a vedere, o si tratta di un bug in Python? (Testing contro 3.1.1)

Grazie per tutta la comprensione che si può dare su questo tema (certamente avanzato)!

È stato utile?

Soluzione

Ho trovato questo nel codice Python (in memoryobject.c in oggetti) nella funzione 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;
}

che è la fonte di questi ultimi due errori. Sembra che l'ItemSize anche per mv [0] non è ancora uguale a se stesso.

Aggiorna

Ecco quello che penso sta succedendo. Quando si tenta di assegnare qualcosa in mv, chiama memory_ass_sub di oggetti / memoryobject.c, ma che la funzione richiede solo un PyObject come input. Questo oggetto viene poi cambiato in un buffer interno utilizzando la funzione di PyObject_GetBuffer anche se nel caso di mv [0] è già un tampone (e il buffer che volete!). La mia ipotesi è che questa funzione prende l'oggetto e lo rende in un buffer di semplice itemsize = 1 indipendentemente dal fatto che è già un tampone oppure no. Questo è il motivo che ottenete l'articolo mismatching dimensioni anche per

mv[0] = mv[0]

Il problema con la prima assegnazione,

mv [0] = 0xFFFFFFFF

steli (Credo) da controllare se l'int è in grado di essere usato come un tampone, che attualmente non è predisposta per da quanto ho capito.

In altre parole, il sistema tampone non è attualmente in grado di gestire voce formati più grandi da 1. Non sembra che sia così lontano, ma ci vorrebbe un po 'più di lavoro da parte vostra. Se non farlo funzionare, probabilmente si dovrebbe rinviare le modifiche alla distribuzione Python principale.

Un altro aggiornamento

Il codice di errore dal primo tentativo di assegnazione mv [0] deriva dalla mancanza di int il PyObject_CheckBuffer quando PyObject_CheckBuffer viene chiamato su di esso. A quanto pare il sistema gestisce solo copie di oggetti bufferable. Questo sembra che dovrebbe essere cambiato troppo.

Conclusione

Attualmente il sistema di buffer Python non può gestire oggetti con ItemSize> 1 come avete indovinato. Inoltre, non è in grado di gestire le assegnazioni a un buffer da oggetti non bufferable come int.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top