Pregunta

Estoy tratando de exponer una memoria intermedia de información de la imagen de píxeles (RGBA de 32 bits) a través de la interfaz de Python 3.x amortiguar. Después de un poco de jugar un rato, yo era capaz de conseguir este trabajo, así:

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

Y en Python que puedo jugar con él de esta manera:

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

y que funciona espléndidamente. Sin embargo, sería genial si pudiera trabajar con el valor total de píxeles (int, 4 bytes) en lugar de bytes individuales, por lo que he modificado el búfer de lectura de este 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;
}

En realidad, esto devuelve los datos y puede leer correctamente, pero cualquier intento de asignar un valor en él ahora no!

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)

En el caso 1 informa el error que me 'int' does not support the buffer interface, que es una vergüenza y un poco confuso (I especificó que el formato tampón fue "I" después de todo), pero puedo lidiar con eso. En el caso 2 y 3 cosas se ponen realmente extraño, sin embargo: En ambos casos me Gime un mismatching item sizes for "my.Image" and "bytes" lectura TypeError (Donde my.Image es, obviamente, mi tipo de imagen)

Esto es muy confuso para mí, ya que los datos que estoy pasando es, obviamente, en el mismo tamaño que lo que salga de ese elemento. Parece como si los tampones simplemente dejan que permite la asignación si el ItemSize es mayor que 1. Por supuesto, la documentación de este interfaz es muy escasa y hojeando a través del código Python en realidad no se dará ningún ejemplos de uso, así que estoy bastante atascado. Me estoy perdiendo algo de snippit de la documentación que los estados "buffers se convierten esencialmente inútil cuando ItemSize> 1", estoy haciendo algo mal que no puedo ver, o se trata de un error en Python? (Pruebas contra 3.1.1)

Gracias por cualquier idea se puede dar en este tema (la verdad avanzada)!

¿Fue útil?

Solución

He encontrado esto en el código Python (en memoryobject.c en objetos) en el memory_ass_sub función:

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

que es la fuente de estos dos últimos errores. Parece que la ItemSize para mv incluso [0] todavía no es igual a sí mismo.

Actualizar

Esto es lo que creo que está pasando. Cuando intenta asignar algo en mv, llama memory_ass_sub en Objetos / memoryobject.c, pero esa función sólo toma PyObject como entrada. Este objetivo se cambia entonces en un búfer en el interior mediante la función PyObject_GetBuffer a pesar de que en el caso del vm [0] es ya una memoria intermedia (y la memoria intermedia que usted quiera!). Mi conjetura es que esta función tiene el objeto y lo convierte en un buffer sencilla de ItemSize = 1, independientemente de si ya es un buffer o no. Es por eso que usted consigue el artículo no coincidente tamaños incluso para

mv[0] = mv[0]

El problema con la primera asignación,

mv [0] = 0xFFFFFFFF

tallos (creo) a partir comprobando si el int es capaz de ser utilizado como un amortiguador, que en la actualidad no es puesta a punto de de lo que entiendo.

En otras palabras, el sistema tampón no es actualmente capaz de manejar elemento tamaños más grandes de 1. No se ve como que está tan lejos, pero se necesitaría un poco más de trabajo en su final. Si lo hace conseguir que funcione, debería enviar los cambios a la distribución principal de Python.

Otra actualización

El código de error de su primer intento en la asignación de mv [0] se deriva de la int fallar el PyObject_CheckBuffer cuando PyObject_CheckBuffer se llama en ella. Al parecer, el sistema sólo se encarga de copias de objetos bufferable. Esto parece que se debe cambiar también.

Conclusión

En la actualidad el sistema tampón Python no puede manejar elementos con ItemSize> 1, como lo has adivinado. Además, no puede manejar las asignaciones a una memoria intermedia de la no-bufferable objetos tales como enteros.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top