10.2. Devolver una captura¶
Un endpoint de estado está bien, pero la razón de ser de la cámara es la lente. Añade un endpoint que devuelva el JPEG de lo que el sensor esté mirando en este momento.
import csi
from microdot import Response
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
@app.get('/snapshot.jpg')
async def snapshot(request):
img = csi0.snapshot().compress(quality=85)
return Response(
body=img.bytearray(),
headers={'Content-Type': 'image/jpeg'},
)
Accede a http://<cam-ip>/snapshot.jpg desde un navegador y un JPEG de la vista actual llenará la pestaña. Actualiza y obtendrás uno nuevo.
10.2.1. El objeto Response¶
Un manejador que devuelve un diccionario deja que microdot haga el resto. Los bytes JPEG necesitan la forma extendida: una microdot.Response construida explícitamente. El argumento body acepta cualquier valor de tipo bytes; el búfer de image.Image de la cámara se expone a través de bytearray(), de modo que el mismo búfer en el que escribió el sensor va directamente al socket.
Content-Type: image/jpeg es lo que indica al navegador que renderice el cuerpo como una imagen. Sin él, el navegador intentaría mostrar los bytes JPEG como texto y verías una pantalla llena de basura.
image.Image.compress() ejecuta la codificación JPEG sobre el búfer de imagen existente in situ y devuelve la misma imagen (ahora en formato JPEG) para que sus bytes puedan enviarse tal cual. quality=85 es el valor predeterminado habitual: lo bastante alto como para que la imagen sea nítida y lo bastante bajo como para que el archivo quepa a través de un enlace lento.
10.2.2. La captura bloquea el bucle¶
csi.CSI.snapshot() espera a que el sensor termine de exponer y transferir por DMA un fotograma antes de devolver el control. Dentro de un manejador asíncrono, eso significa que el bucle de eventos se detiene durante la exposición: diez, veinte o cincuenta milisegundos según la iluminación. Con un solo cliente solicitando una sola ruta a la vez esto es imperceptible; con varios clientes, o con una corrutina de captura ejecutándose en paralelo, bloquearía todo lo demás.
Existe una variante no bloqueante de snapshot() para el caso de múltiples corrutinas (blocking=False devuelve el siguiente fotograma listo o None). Para una captura por solicitud, la llamada bloqueante predeterminada es adecuada.
El propietario ahora puede consultar una URL y obtener un fotograma nuevo.