10.2. Een snapshot teruggeven

Een statuseindpunt is prima, maar de reden dat de cam bestaat is de lens. Voeg een eindpunt toe dat de JPEG teruggeeft van waar de sensor op dit moment naar kijkt.

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'},
    )

Roep http://<cam-ip>/snapshot.jpg aan vanuit een browser en een JPEG van het huidige beeld vult het tabblad. Vernieuw en je krijgt een nieuwe.

10.2.1. Het Response-object

Bij een handler die een dict teruggeeft, doet microdot de rest. JPEG-bytes vereisen de lange vorm: een microdot.Response die expliciet wordt geconstrueerd. Het argument body accepteert elke bytes-achtige waarde – de image.Image-buffer van de camera wordt blootgesteld via bytearray(), zodat dezelfde buffer waarin de sensor schreef rechtstreeks naar de socket gaat.

Content-Type: image/jpeg is wat de browser vertelt om de body als afbeelding weer te geven. Zonder dit zou de browser de JPEG-bytes als tekst proberen weer te geven en zou je een scherm vol rommel zien.

image.Image.compress() voert JPEG-codering uit op de bestaande afbeeldingsbuffer ter plaatse en geeft dezelfde afbeelding terug (nu in JPEG-formaat) zodat de bytes ervan ongewijzigd kunnen worden verzonden. quality=85 is de gebruikelijke standaard – hoog genoeg dat het beeld scherp is, laag genoeg dat het bestand door een trage verbinding past.

10.2.2. Vastleggen blokkeert de loop

csi.CSI.snapshot() wacht tot de sensor klaar is met het belichten en via DMA overdragen van een frame voordat de functie terugkeert. Binnen een async-handler betekent dat dat de event loop stilvalt voor de duur van de belichting – tien, twintig, vijftig milliseconden afhankelijk van de verlichting. Met één client die één route tegelijk opvraagt, is dit onzichtbaar; met meerdere clients, of een capture-coroutine die er naast draait, zou het al het andere blokkeren.

Er bestaat een niet-blokkerende variant van snapshot() voor het geval met meerdere coroutines (blocking=False geeft het volgende gereede frame terug of None). Voor één snapshot per verzoek is de standaard blokkerende aanroep prima.

De eigenaar kan nu een URL aanroepen en een vers frame krijgen.