10.2. Returnarea unui instantaneu

Un punct final de stare este în regulă, dar motivul pentru care camera există este obiectivul. Adăugați un punct final care returnează fișierul JPEG cu ceea ce privește senzorul chiar acum.

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

Accesați http://<cam-ip>/snapshot.jpg dintr-un browser și un JPEG cu vizualizarea curentă umple fila. Reîmprospătați și obțineți unul nou.

10.2.1. Obiectul Response

Un handler care returnează un dict lasă microdot să facă restul. Octeții JPEG necesită forma lungă: un microdot.Response construit explicit. Argumentul body acceptă orice valoare de tip bytes – tamponul (buffer) image.Image al camerei este expus prin bytearray(), astfel încât același tampon în care a scris senzorul ajunge direct în socket.

Content-Type: image/jpeg este ceea ce îi spune browserului să redea corpul ca imagine. Fără el, browserul ar încerca să afișeze octeții JPEG ca text și ați vedea un ecran plin de mizerie.

image.Image.compress() rulează codificarea JPEG pe tamponul de imagine existent, pe loc, și returnează aceeași imagine (acum în format JPEG), astfel încât octeții ei să poată fi trimiși ca atare. quality=85 este valoarea implicită obișnuită – suficient de ridicată pentru ca imaginea să fie clară, suficient de scăzută pentru ca fișierul să încapă printr-o legătură lentă.

10.2.2. Capturarea blochează bucla

csi.CSI.snapshot() așteaptă ca senzorul să termine expunerea și transferul DMA al unui cadru înainte de a returna. În interiorul unui handler asincron, asta înseamnă că bucla de evenimente se blochează pe durata expunerii – zece, douăzeci, cincizeci de milisecunde, în funcție de iluminare. Cu un singur client care solicită o singură rută la un moment dat, acest lucru este invizibil; cu mai mulți clienți sau cu o corutină de capturare care rulează în paralel, ar bloca tot restul.

Există o variantă neblocantă a snapshot() pentru cazul cu mai multe corutine (blocking=False returnează următorul cadru pregătit sau None). Pentru un singur instantaneu per cerere, apelul blocant implicit este suficient.

Proprietarul poate acum accesa o adresă URL și obține un cadru nou.