13.3.1.3. Streaming dei frame

Uno script che cattura frame sulla cam può inviarne ciascuno in streaming all’host tramite USB. Il pattern consiste in due chiamate sull’istanza openmv.Camera: streaming() per attivare o disattivare lo stream e read_frame() per estrarre il frame successivo dal canale.

13.3.1.3.1. Un loop minimale di streaming e visualizzazione

Lo script lato cam è il consueto loop di snapshot; la novità è che l’host apre lo streaming e legge indietro il risultato:

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")

La cam cattura i frame in modo continuo; l’host estrae ciascuno dal buffer dello stream non appena arriva. La cam sovrascrive il buffer dello stream a ogni nuovo snapshot, quindi un host che effettua il polling più lentamente di quanto la cam catturi scarterà silenziosamente dei frame – ed è il comportamento corretto per i casi d’uso di tipo viewer.

13.3.1.3.2. Il dict del frame

read_frame() restituisce None (nessun frame in attesa) oppure un dict con cinque voci:

Chiave

Significato

width

Larghezza del frame in pixel.

height

Altezza del frame in pixel.

format

Identificatore del formato pixel dichiarato dalla cam (un intero tra le costanti csi della cam).

depth

Per i formati compressi (JPEG, PNG), la dimensione dell’immagine compressa in byte. Inutilizzato per i formati non compressi.

data

Il frame come buffer di bytes in RGB888. Ogni pixel è composto da tre byte (R, G, B); la lunghezza totale è width * height * 3.

raw_size

Byte inviati dalla cam tramite USB prima della decodifica. Utile per il calcolo effettivo del throughput.

Il pacchetto converte il formato nativo della cam (GRAYSCALE, RGB565, JPEG) in RGB888 prima di restituirlo, così l’host non deve mai gestire l’RGB565 a bit compressi o il percorso di decompressione JPEG. I frame in scala di grigi vengono restituiti con il valore di luma replicato su tutti e tre i canali.

Il buffer data è disposto riga per riga, dall’alto verso il basso; passarlo direttamente a una libreria di visualizzazione o salvarlo come file RGB grezzo funziona senza ulteriori riordinamenti.

13.3.1.3.3. Modalità streaming grezzo

Per impostazione predefinita la cam comprime in JPEG ogni frame catturato prima di inserirlo nel canale dello stream, e read_frame() lo decomprime sull’host. Sulle cam senza supporto JPEG hardware, la compressione software è il passaggio più lento del loop. Passare raw=True la salta:

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

La cam invia quindi il buffer di pixel non compresso. I frame non compressi sono molto più grandi dei loro equivalenti JPEG, quindi la cam riduce la scala di ogni frame catturato affinché entri nel canale dello stream prima di inviarlo; l’argomento resolution=(width, height) imposta tale destinazione. L’host riceve comunque RGB888 nel campo data – il pacchetto converte da qualunque formato pixel la cam abbia riportato in format.

13.3.1.3.4. Lasciare che gli eventi guidino il loop

Un loop di polling che chiama read_frame() più velocemente di quanto la cam produca i frame trascorre la maggior parte del tempo ottenendo None. Quando l’host ha anche altro lavoro da svolgere (una UI da aggiornare, altri canali su cui effettuare il polling), read_status() è il controllo più economico: restituisce un dict che associa a ogni nome di canale registrato un booleano che indica «i dati sono pronti»:

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 ...

Questa è la forma di loop che usa il viewer della CLI stesso.

13.3.1.3.5. Arrestare lo stream

Chiama streaming() con enable=False per arrestare. La cam continua a eseguire il suo script ma non riempie più il buffer dello stream; read_frame() restituisce semplicemente None da quel punto in poi. Chiamare stop() fa la stessa cosa in modo implicito, fermando lo script.