4.16. Vista previa de la imagen

El conjunto de framebuffers es donde la aplicación lee sus fotogramas. Mientras la aplicación trabaja con esos fotogramas, lo que esté conectado a la cámara para previsualizarlos también necesita una copia de cada fotograma. La cámara dispone de un segundo búfer dedicado para ese fin, y de una única regla sobre cuándo se llena: cada vez que la aplicación llama a snapshot(), el fotograma capturado anterior se copia en el búfer de vista previa antes de devolver el nuevo fotograma.

La aplicación y el previsualizador nunca compiten por la misma memoria. La aplicación lee su fotograma del conjunto; el previsualizador lee su fotograma del búfer de vista previa. Ambas cosas ocurren en paralelo.

4.16.1. El framebuffer de transmisión

El búfer de vista previa (el framebuffer de transmisión) es una única región de RAM de tamaño fijo, separada del conjunto de framebuffers. Su tamaño se establece en el momento de compilar el firmware y no cambia con framesize() ni con pixformat(). Lo típico en las OpenMV Cams recientes es alrededor de un megabyte, lo bastante grande para alojar una vista previa de resolución moderada, mucho más pequeño que un fotograma de resolución completa en los mayores tamaños de sensor.

El código de la aplicación no lee ni escribe directamente este búfer; el controlador de la cámara lo llena como efecto secundario de snapshot().

4.16.2. Qué hace snapshot para la vista previa

En cada llamada a snapshot(), antes de que el controlador libere el framebuffer anterior de la aplicación de vuelta al conjunto y entregue el nuevo, copia el fotograma anterior al búfer de vista previa, con todo lo que la aplicación dibujó sobre él durante el procesamiento aún presente en la imagen. Son posibles dos ramas. Cuál de ellas se ejecuta lo elige el previsualizador, no la cámara: el consumidor que abrió la vista previa indica al controlador si quiere la imagen sin procesar o un JPEG, y qué tamaño de ventana sin procesar puede aceptar.

  • Copia sin procesar reducida. Cuando el previsualizador ha solicitado fotogramas sin procesar, el controlador copia el fotograma anterior en su formato de píxel nativo (RGB565, escala de grises, etc.). Si el fotograma es mayor que la ventana sin procesar que solicitó el previsualizador, el controlador lo reduce con filtrado bilineal hasta que encaja; de lo contrario, los píxeles pasan sin cambios. Sin artefactos de compresión; el previsualizador ve los mismos píxeles con los que estaba trabajando la aplicación.

  • Compresión JPEG. Cuando el previsualizador ha solicitado JPEG, o cuando la copia sin procesar no cabría en absoluto en el búfer de transmisión, el controlador comprime en JPEG el fotograma anterior a su resolución completa dentro del búfer de transmisión. La calidad se ajusta de forma adaptativa por cada fotograma para que la salida comprimida se mantenga dentro de la capacidad del búfer de transmisión. Cuando un fotograma cabe, el controlador sube la calidad poco a poco un paso hacia un techo que depende del tamaño en píxeles del fotograma capturado (a los fotogramas más pequeños se les permite mayor calidad; los más grandes se limitan a un valor inferior para que no puedan desbordarse ante un pequeño cambio de contenido). Cuando un fotograma no cabe, el controlador reduce a la mitad la calidad actual, la mantiene en el nivel reducido durante las siguientes varias decenas de fotogramas para que el nuevo ajuste tenga tiempo de estabilizarse, y descarta de la vista previa el fotograma que se desbordó. El bucle de la aplicación sigue ejecutándose sin verse afectado; solo el previsualizador se pierde el fotograma descartado.

Los fotogramas que la cámara produce en un formato que ya está comprimido (el formato de píxel JPEG en los sensores que emiten JPEG directamente) omiten ambas ramas: el flujo de bits codificado se copia directamente al búfer de vista previa tal cual.

El previsualizador sondea según su propia planificación, por lo general mucho más lenta que la velocidad de captura de la cámara, de modo que submuestrea la velocidad de captura sin procesar: solo se muestran las capturas que llega a leer a tiempo. Si una nueva snapshot() llega al búfer de vista previa antes de que el previsualizador haya leído el fotograma anterior, el búfer sigue bloqueado por el previsualizador y la nueva actualización de la vista previa se omite: esa captura se pierde del flujo de vista previa. El propio conjunto de framebuffers de la aplicación no se ve afectado; el fotograma capturado sigue llegando a la aplicación con normalidad.

4.16.3. Enviar el último fotograma manualmente

Dado que la vista previa se actualiza como efecto secundario de snapshot(), un script que termina sin volver a llamar nunca a snapshot deja lo último que envió a la vista previa en el previsualizador de forma indefinida, lo que, en el caso de un script que hace su trabajo antes de la primera captura y luego sale, es una vista previa vacía. image.Image.flush() (o el equivalente flush() en el objeto CSI) copia el contenido actual del framebuffer de la aplicación al búfer de transmisión bajo demanda, sin capturar un nuevo fotograma:

img = csi0.snapshot()
# process the image and draw on it
img.flush()                               # previewer sees the annotated frame

La misma llamada también es útil cuando una operación de larga duración se intercala entre capturas y, de lo contrario, el previsualizador mostraría una vista previa obsoleta todo ese tiempo.

Nota

La aplicación de vista previa tiene que leer el fotograma del búfer de transmisión antes de que el script salga. Un flush al final de un script corto solo prepara el fotograma; si el script luego devuelve el control a la cámara antes de que el previsualizador haya sondeado, el búfer se reutiliza en la siguiente ejecución y ese fotograma final se pierde. Para las vistas previas de fin de script, dele al previsualizador un momento para recoger el fotograma (una breve pausa tras el flush, o simplemente no salir de inmediato) antes de que el script termine.