13.3.1.3. Einzelbilder streamen

Ein Skript, das Einzelbilder auf der Cam aufnimmt, kann jedes Einzelbild über USB zurück an den Host streamen. Das Muster besteht aus zwei Aufrufen an der openmv.Camera-Instanz: streaming(), um den Stream ein- oder auszuschalten, und read_frame(), um das nächste Einzelbild aus dem Kanal zu holen.

13.3.1.3.1. Eine minimale Stream-und-Anzeige-Schleife

Das Skript auf der Cam-Seite ist die übliche Schnappschuss-Schleife; neu ist, dass der Host das Streaming öffnet und das Ergebnis zurückliest:

from openmv import Camera

script = """
import csi
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
while True:
    csi0.snapshot()
"""

with Camera('/dev/ttyACM0') as cam:
    cam.stop()
    cam.exec(script)
    cam.streaming(True)

    while True:
        if frame := cam.read_frame():
            print(f"{frame['width']}x{frame['height']}, "
                  f"{frame['raw_size']} bytes")

Die Cam nimmt fortlaufend Einzelbilder auf; der Host holt jedes davon aus dem Stream-Puffer, sobald es eintrifft. Die Cam überschreibt den Stream-Puffer bei jedem neuen Schnappschuss, sodass ein Host, der langsamer abfragt als die Cam aufnimmt, stillschweigend Einzelbilder verwirft – das ist das richtige Verhalten für Anwendungsfälle im Viewer-Stil.

13.3.1.3.2. Das Frame-Dict

read_frame() gibt entweder None (kein Einzelbild wartet) oder ein dict mit fünf Einträgen zurück:

Schlüssel

Bedeutung

width

Breite des Einzelbilds in Pixeln.

height

Höhe des Einzelbilds in Pixeln.

format

Pixelformat-Bezeichner, den die Cam deklariert hat (eine Ganzzahl aus den csi-Konstanten der Cam).

depth

Für komprimierte Formate (JPEG, PNG) die Größe des komprimierten Bildes in Bytes. Wird für unkomprimierte Formate nicht verwendet.

data

Das Einzelbild als bytes-Puffer in RGB888. Jeder Pixel besteht aus drei Bytes (R, G, B); die Gesamtlänge beträgt width * height * 3.

raw_size

Bytes, die die Cam vor dem Decodieren über USB gesendet hat. Nützlich für die tatsächliche Durchsatzberechnung.

Das Paket konvertiert das native Format der Cam (GRAYSCALE, RGB565, JPEG) vor der Rückgabe in RGB888, sodass der Host sich nie selbst mit dem bit-gepackten RGB565 oder dem JPEG-Dekomprimierungspfad befassen muss. Graustufen-Einzelbilder kommen mit dem Luma-Wert zurück, der in alle drei Kanäle repliziert wurde.

Der data-Puffer ist zeilenweise von oben nach unten angeordnet; ihn direkt einer Anzeigebibliothek zuzuführen oder als rohe RGB-Datei zu speichern funktioniert ohne weiteres Umsortieren.

13.3.1.3.3. Roh-Streaming-Modus

Standardmäßig komprimiert die Cam jedes aufgenommene Einzelbild per JPEG, bevor sie es in den Stream-Kanal legt, und read_frame() dekomprimiert es auf dem Host. Auf Cams ohne Hardware-JPEG-Unterstützung ist die Software-Komprimierung der langsamste Schritt in der Schleife. Die Übergabe von raw=True überspringt ihn:

cam.streaming(True, raw=True, resolution=(320, 240))

Die Cam sendet den Pixelpuffer dann unkomprimiert. Unkomprimierte Einzelbilder sind viel größer als ihre JPEG-Entsprechungen, daher skaliert die Cam jedes aufgenommene Einzelbild herunter, damit es in den Stream-Kanal passt, bevor sie es sendet; das Argument resolution=(width, height) legt dieses Ziel fest. Der Host empfängt im data-Feld weiterhin RGB888 – das Paket konvertiert aus dem Pixelformat, das die Cam in format gemeldet hat.

13.3.1.3.4. Die Schleife durch Ereignisse steuern lassen

Eine Abfrageschleife, die read_frame() schneller aufruft, als die Cam Einzelbilder produziert, verbringt die meiste Zeit damit, None zurückzubekommen. Wenn der Host außerdem noch andere Arbeit zu erledigen hat (eine zu aktualisierende UI, weitere abzufragende Kanäle), ist read_status() die günstigere Prüfung: Sie gibt ein Dict zurück, das jeden registrierten Kanalnamen auf einen Boolean von „Daten sind bereit“ abbildet:

while True:
    status = cam.read_status()

    if status.get('stream'):
        frame = cam.read_frame()
        # ... process the frame ...

    if status.get('stdout'):
        text = cam.read_stdout()
        print(text, end='')

    if status.get('my_channel'):
        data = cam.channel_read('my_channel')
        # ... process custom-channel data ...

Dies ist die Schleifenform, die der CLI-Viewer selbst verwendet.

13.3.1.3.5. Den Stream stoppen

Rufen Sie streaming() mit enable=False auf, um zu stoppen. Die Cam führt ihr Skript weiter aus, füllt aber den Stream-Puffer nicht mehr; read_frame() gibt ab diesem Zeitpunkt einfach None zurück. Der Aufruf von stop() bewirkt dasselbe implizit, indem er das Skript anhält.