10.2. Restituire uno snapshot¶
Un endpoint di stato va bene, ma la ragione per cui la camera esiste è l’obiettivo. Aggiungi un endpoint che restituisca il JPEG di qualunque cosa il sensore stia inquadrando in questo 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'},
)
Visita http://<cam-ip>/snapshot.jpg da un browser e un JPEG della vista corrente riempie la scheda. Aggiorna e ne ottieni uno nuovo.
10.2.1. L’oggetto Response¶
Un handler che restituisce un dict lascia che sia microdot a fare il resto. I byte JPEG richiedono la forma estesa: una microdot.Response costruita esplicitamente. L’argomento body accetta qualsiasi valore di tipo bytes: il buffer image.Image della camera è esposto tramite bytearray(), così lo stesso buffer in cui il sensore ha scritto va dritto al socket.
Content-Type: image/jpeg è ciò che dice al browser di renderizzare il corpo come immagine. Senza, il browser proverebbe a visualizzare i byte JPEG come testo e vedresti uno schermo pieno di caratteri illeggibili.
image.Image.compress() esegue la codifica JPEG sul buffer dell’immagine esistente in place e restituisce la stessa immagine (ora in formato JPEG) così che i suoi byte possano essere inviati così come sono. quality=85 è il valore predefinito abituale: abbastanza alto perché l’immagine sia nitida, abbastanza basso perché il file passi attraverso un collegamento lento.
10.2.2. La cattura blocca il loop¶
csi.CSI.snapshot() attende che il sensore finisca di esporre e trasferire in DMA un frame prima di restituire. All’interno di un handler asincrono ciò significa che l’event loop si ferma per la durata dell’esposizione: dieci, venti, cinquanta millisecondi a seconda dell’illuminazione. Con un solo client che richiede una sola rotta alla volta tutto questo è invisibile; con più client, o con una coroutine di cattura in esecuzione in parallelo, bloccherebbe tutto il resto.
Esiste una variante non bloccante di snapshot() per il caso a più coroutine (blocking=False restituisce il prossimo frame pronto oppure None). Per uno snapshot per richiesta, la chiamata bloccante predefinita va bene.
Il proprietario può ora richiamare un URL e ottenere un frame nuovo.