10.2. Einen Schnappschuss zurückgeben¶
Ein Status-Endpunkt ist gut und schön, aber der Grund, warum die Kamera existiert, ist das Objektiv. Fügen Sie einen Endpunkt hinzu, der das JPEG dessen zurückgibt, was der Sensor gerade betrachtet.
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'},
)
Rufen Sie http://<cam-ip>/snapshot.jpg aus einem Browser auf, und ein JPEG der aktuellen Ansicht füllt den Tab. Aktualisieren Sie, und Sie erhalten ein neues.
10.2.1. Das Response-Objekt¶
Ein Handler, der ein dict zurückgibt, lässt microdot den Rest erledigen. JPEG-Bytes benötigen die ausführliche Form: ein explizit konstruiertes microdot.Response. Das Argument body nimmt jeden Bytes-ähnlichen Wert entgegen – der image.Image-Puffer der Kamera wird über bytearray() zugänglich gemacht, sodass derselbe Puffer, in den der Sensor geschrieben hat, direkt an den Socket geht.
Content-Type: image/jpeg ist das, was dem Browser sagt, den Body als Bild darzustellen. Ohne dieses würde der Browser versuchen, die JPEG-Bytes als Text anzuzeigen, und Sie würden einen Bildschirm voller Datenmüll sehen.
image.Image.compress() führt die JPEG-Kodierung auf dem vorhandenen Bildpuffer an Ort und Stelle aus und gibt dasselbe Bild zurück (nun im JPEG-Format), sodass dessen Bytes unverändert gesendet werden können. quality=85 ist der übliche Standardwert – hoch genug, dass das Bild scharf ist, niedrig genug, dass die Datei durch eine langsame Verbindung passt.
10.2.2. Die Aufnahme blockiert die Schleife¶
csi.CSI.snapshot() wartet, bis der Sensor die Belichtung und das DMA eines Einzelbilds abgeschlossen hat, bevor es zurückkehrt. Innerhalb eines async-Handlers bedeutet das, dass die Ereignisschleife für die Dauer der Belichtung stillsteht – zehn, zwanzig, fünfzig Millisekunden, je nach Beleuchtung. Wenn ein Client eine Route nach der anderen anfragt, ist das unsichtbar; bei mehreren Clients oder einer parallel laufenden Aufnahme-Coroutine würde es alles andere blockieren.
Für den Fall mehrerer Coroutinen existiert eine nicht-blockierende Variante von snapshot() (blocking=False gibt das nächste fertige Einzelbild oder None zurück). Für einen Schnappschuss pro Anfrage ist der blockierende Standardaufruf in Ordnung.
Der Besitzer kann nun eine URL anstoßen und ein frisches Einzelbild erhalten.